mirror of
https://gitlab.com/Mr_Goldberg/goldberg_emulator.git
synced 2025-12-06 12:14:54 +01:00
Update 128 files
- /dll/base.cpp - /dll/base.h - /dll/common_includes.h - /dll/dll.cpp - /dll/dll.h - /dll/flat.cpp - /dll/local_storage.cpp - /dll/local_storage.h - /dll/net.proto - /dll/network.cpp - /dll/network.h - /dll/rtlgenrandom.c - /dll/rtlgenrandom.def - /dll/settings.cpp - /dll/settings.h - /dll/settings_parser.cpp - /dll/settings_parser.h - /dll/steam_HTMLsurface.h - /dll/steam_applist.cpp - /dll/steam_applist.h - /dll/steam_apps.cpp - /dll/steam_apps.h - /dll/steam_client.cpp - /dll/steam_client.h - /dll/steam_controller.h - /dll/steam_friends.h - /dll/steam_game_coordinator.h - /dll/steam_gamesearch.h - /dll/steam_gameserver.cpp - /dll/steam_gameserver.h - /dll/steam_gameserverstats.cpp - /dll/steam_gameserverstats.h - /dll/steam_http.cpp - /dll/steam_http.h - /dll/steam_inventory.h - /dll/steam_masterserver_updater.h - /dll/steam_matchmaking.h - /dll/steam_matchmaking_servers.cpp - /dll/steam_matchmaking_servers.h - /dll/steam_music.cpp - /dll/steam_music.h - /dll/steam_musicremote.cpp - /dll/steam_musicremote.h - /dll/steam_networking.h - /dll/steam_networking_messages.h - /dll/steam_networking_sockets.h - /dll/steam_networking_socketsserialized.h - /dll/steam_networking_utils.h - /dll/steam_parental.cpp - /dll/steam_parental.h - /dll/steam_parties.h - /dll/steam_remote_storage.h - /dll/steam_remoteplay.h - /dll/steam_screenshots.cpp - /dll/steam_screenshots.h - /dll/steam_tv.h - /dll/steam_ugc.h - /dll/steam_unified_messages.h - /dll/steam_user.h - /dll/steam_user_stats.h - /dll/steam_utils.h - /dll/steam_video.cpp - /dll/steam_video.h - /dll/wrap.cpp - /all/network.h - /all/network.cpp - /all/dll.cpp - /all/base.cpp - /all/base.h - /all/common_includes.h - /all/local_storage.cpp - /all/local_storage.h - /all/rtlgenrandom.c - /all/settings_parser.cpp - /all/settings.cpp - /all/steam_HTMLsurface.h - /all/settings_parser.h - /all/steam_client.h - /all/steam_apps.h - /all/steam_gameserver.h - /all/steam_client.cpp - /all/steam_applist.cpp - /all/steam_gameserver.cpp - /all/steam_controller.h - /all/dll.h - /all/steam_apps.cpp - /all/steam_friends.h - /all/steam_game_coordinator.h - /all/net.proto - /all/rtlgenrandom.def - /all/steam_matchmaking_servers.h - /all/settings.h - /all/steam_applist.h - /all/flat.cpp - /all/steam_gamesearch.h - /all/steam_music.cpp - /all/steam_remoteplay.h - /all/steam_screenshots.cpp - /all/steam_parental.cpp - /all/steam_musicremote.h - /all/steam_networking.h - /all/steam_networking_messages.h - /all/steam_networking_sockets.h - /all/steam_networking_socketsserialized.h - /all/steam_networking_utils.h - /all/steam_parental.h - /all/steam_parties.h - /all/steam_remote_storage.h - /all/steam_screenshots.h - /all/steam_tv.h - /all/steam_ugc.h - /all/steam_unified_messages.h - /all/steam_user.h - /all/steam_user_stats.h - /all/steam_http.cpp - /all/steam_http.h - /all/steam_inventory.h - /all/steam_masterserver_updater.h - /all/steam_matchmaking.h - /all/steam_matchmaking_servers.cpp - /all/steam_music.h - /all/steam_musicremote.cpp - /all/steam_utils.h - /all/steam_video.cpp - /all/wrap.cpp - /all/steam_gameserverstats.cpp - /all/steam_gameserverstats.h - /all/steam_video.h
This commit is contained in:
parent
1c0bdceaac
commit
25c00530f3
64 changed files with 0 additions and 0 deletions
867
all/base.cpp
Normal file
867
all/base.cpp
Normal file
|
|
@ -0,0 +1,867 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
static void
|
||||
randombytes(char * const buf, const size_t size)
|
||||
{
|
||||
while (!RtlGenRandom((PVOID) buf, (ULONG) size)) {
|
||||
PRINT_DEBUG("RtlGenRandom ERROR\n");
|
||||
Sleep(100);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string get_env_variable(std::string name)
|
||||
{
|
||||
wchar_t env_variable[1024];
|
||||
DWORD ret = GetEnvironmentVariableW(utf8_decode(name).c_str(), env_variable, _countof(env_variable));
|
||||
if (ret <= 0) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
env_variable[ret] = 0;
|
||||
return utf8_encode(env_variable);
|
||||
}
|
||||
|
||||
bool set_env_variable(std::string name, std::string value)
|
||||
{
|
||||
return SetEnvironmentVariableW(utf8_decode(name).c_str(), utf8_decode(value).c_str());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int fd = -1;
|
||||
|
||||
static void randombytes(char *buf, size_t size)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (fd == -1) {
|
||||
for (;;) {
|
||||
fd = open("/dev/urandom",O_RDONLY);
|
||||
if (fd != -1) break;
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
while (size > 0) {
|
||||
if (size < 1048576) i = size; else i = 1048576;
|
||||
|
||||
i = read(fd,buf,i);
|
||||
if (i < 1) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
buf += i;
|
||||
size -= i;
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_env_variable(std::string name)
|
||||
{
|
||||
char *env = getenv(name.c_str());
|
||||
if (!env) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
return std::string(env);
|
||||
}
|
||||
|
||||
bool set_env_variable(std::string name, std::string value)
|
||||
{
|
||||
return setenv(name.c_str(), value.c_str(), 1) == 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::recursive_mutex global_mutex;
|
||||
|
||||
SteamAPICall_t generate_steam_api_call_id() {
|
||||
static SteamAPICall_t a;
|
||||
randombytes((char *)&a, sizeof(a));
|
||||
++a;
|
||||
if (a == 0) ++a;
|
||||
return a;
|
||||
}
|
||||
|
||||
int generate_random_int() {
|
||||
int a;
|
||||
randombytes((char *)&a, sizeof(a));
|
||||
return a;
|
||||
}
|
||||
|
||||
static uint32 generate_steam_ticket_id() {
|
||||
/* not random starts with 2? */
|
||||
static uint32 a = 1;
|
||||
++a;
|
||||
if (a == 0) ++a;
|
||||
return a;
|
||||
}
|
||||
|
||||
static unsigned generate_account_id()
|
||||
{
|
||||
int a;
|
||||
randombytes((char *)&a, sizeof(a));
|
||||
a = abs(a);
|
||||
if (!a) ++a;
|
||||
return a;
|
||||
}
|
||||
|
||||
CSteamID generate_steam_id_user()
|
||||
{
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeIndividual);
|
||||
}
|
||||
|
||||
static CSteamID generate_steam_anon_user()
|
||||
{
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeAnonUser);
|
||||
}
|
||||
|
||||
CSteamID generate_steam_id_server()
|
||||
{
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeGameServer);
|
||||
}
|
||||
|
||||
CSteamID generate_steam_id_anonserver()
|
||||
{
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeAnonGameServer);
|
||||
}
|
||||
|
||||
CSteamID generate_steam_id_lobby()
|
||||
{
|
||||
return CSteamID(generate_account_id(), k_EChatInstanceFlagLobby | k_EChatInstanceFlagMMSLobby, k_EUniversePublic, k_EAccountTypeChat);
|
||||
}
|
||||
|
||||
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout)
|
||||
{
|
||||
if (timeout == 0.0) return true;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::duration<double>>(now - old).count() > timeout) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __LINUX__
|
||||
std::string get_lib_path() {
|
||||
std::string dir = "/proc/self/map_files";
|
||||
DIR *dp;
|
||||
int i = 0;
|
||||
struct dirent *ep;
|
||||
dp = opendir (dir.c_str());
|
||||
uintptr_t p = (uintptr_t)&get_lib_path;
|
||||
|
||||
if (dp != NULL)
|
||||
{
|
||||
while ((ep = readdir (dp))) {
|
||||
if (memcmp(ep->d_name, ".", 2) != 0 && memcmp(ep->d_name, "..", 3) != 0) {
|
||||
char *upper = NULL;
|
||||
uintptr_t lower_bound = strtoull(ep->d_name, &upper, 16);
|
||||
if (lower_bound) {
|
||||
++upper;
|
||||
uintptr_t upper_bound = strtoull(upper, &upper, 16);
|
||||
if (upper_bound && (lower_bound < p && p < upper_bound)) {
|
||||
std::string path = dir + PATH_SEPARATOR + ep->d_name;
|
||||
char link[PATH_MAX] = {};
|
||||
if (readlink(path.c_str(), link, sizeof(link)) > 0) {
|
||||
std::string lib_path = link;
|
||||
(void) closedir (dp);
|
||||
return link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
(void) closedir (dp);
|
||||
}
|
||||
|
||||
return ".";
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string get_full_lib_path()
|
||||
{
|
||||
std::string program_path;
|
||||
#if defined(__WINDOWS__)
|
||||
wchar_t DllPath[2048] = {0};
|
||||
GetModuleFileNameW((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));
|
||||
program_path = utf8_encode(DllPath);
|
||||
#else
|
||||
program_path = get_lib_path();
|
||||
#endif
|
||||
return program_path;
|
||||
}
|
||||
|
||||
std::string get_full_program_path()
|
||||
{
|
||||
std::string env_program_path = get_env_variable("SteamAppPath");
|
||||
if (env_program_path.length()) {
|
||||
if (env_program_path.back() != PATH_SEPARATOR[0]) {
|
||||
env_program_path = env_program_path.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
return env_program_path;
|
||||
}
|
||||
|
||||
std::string program_path;
|
||||
program_path = get_full_lib_path();
|
||||
return program_path.substr(0, program_path.rfind(PATH_SEPARATOR)).append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
std::string get_current_path()
|
||||
{
|
||||
std::string path;
|
||||
#if defined(STEAM_WIN32)
|
||||
char *buffer = _getcwd( NULL, 0 );
|
||||
#else
|
||||
char *buffer = get_current_dir_name();
|
||||
#endif
|
||||
if (buffer) {
|
||||
path = buffer;
|
||||
path.append(PATH_SEPARATOR);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string canonical_path(std::string path)
|
||||
{
|
||||
std::string output;
|
||||
#if defined(STEAM_WIN32)
|
||||
wchar_t *buffer = _wfullpath(NULL, utf8_decode(path).c_str(), 0);
|
||||
if (buffer) {
|
||||
output = utf8_encode(buffer);
|
||||
free(buffer);
|
||||
}
|
||||
#else
|
||||
char *buffer = canonicalize_file_name(path.c_str());
|
||||
if (buffer) {
|
||||
output = buffer;
|
||||
free(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
bool file_exists_(std::string full_path)
|
||||
{
|
||||
#if defined(STEAM_WIN32)
|
||||
struct _stat buffer;
|
||||
if (_wstat(utf8_decode(full_path).c_str(), &buffer) != 0)
|
||||
return false;
|
||||
|
||||
if ( buffer.st_mode & _S_IFDIR)
|
||||
return false;
|
||||
#else
|
||||
struct stat buffer;
|
||||
if (stat(full_path.c_str(), &buffer) != 0)
|
||||
return false;
|
||||
|
||||
if (S_ISDIR(buffer.st_mode))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int file_size_(std::string full_path)
|
||||
{
|
||||
#if defined(STEAM_WIN32)
|
||||
struct _stat buffer = {};
|
||||
if (_wstat(utf8_decode(full_path).c_str(), &buffer) != 0) return 0;
|
||||
#else
|
||||
struct stat buffer = {};
|
||||
if (stat (full_path.c_str(), &buffer) != 0) return 0;
|
||||
#endif
|
||||
return buffer.st_size;
|
||||
}
|
||||
|
||||
static void steam_auth_ticket_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_auth_ticket_callback\n");
|
||||
|
||||
Auth_Ticket_Manager *auth_ticket_manager = (Auth_Ticket_Manager *)object;
|
||||
auth_ticket_manager->Callback(msg);
|
||||
}
|
||||
|
||||
Auth_Ticket_Manager::Auth_Ticket_Manager(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks) {
|
||||
this->network = network;
|
||||
this->settings = settings;
|
||||
this->callbacks = callbacks;
|
||||
|
||||
this->network->setCallback(CALLBACK_ID_AUTH_TICKET, settings->get_local_steam_id(), &steam_auth_ticket_callback, this);
|
||||
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &steam_auth_ticket_callback, this);
|
||||
}
|
||||
|
||||
#define STEAM_TICKET_PROCESS_TIME 0.03
|
||||
|
||||
void Auth_Ticket_Manager::launch_callback(CSteamID id, EAuthSessionResponse resp, double delay)
|
||||
{
|
||||
ValidateAuthTicketResponse_t data;
|
||||
data.m_SteamID = id;
|
||||
data.m_eAuthSessionResponse = resp;
|
||||
data.m_OwnerSteamID = id;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), delay);
|
||||
}
|
||||
|
||||
void Auth_Ticket_Manager::launch_callback_gs(CSteamID id, bool approved)
|
||||
{
|
||||
if (approved) {
|
||||
GSClientApprove_t data;
|
||||
data.m_SteamID = data.m_OwnerSteamID = id;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
} else {
|
||||
GSClientDeny_t data;
|
||||
data.m_SteamID = id;
|
||||
data.m_eDenyReason = k_EDenyNotLoggedOn; //TODO: other reasons?
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
}
|
||||
|
||||
#define STEAM_ID_OFFSET_TICKET (4 + 8)
|
||||
#define STEAM_TICKET_MIN_SIZE (4 + 8 + 8)
|
||||
Auth_Ticket_Data Auth_Ticket_Manager::getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
|
||||
{
|
||||
uint64 steam_id = settings->get_local_steam_id().ConvertToUint64();
|
||||
memset(pTicket, 123, cbMaxTicket);
|
||||
((char *)pTicket)[0] = 0x14;
|
||||
((char *)pTicket)[1] = 0;
|
||||
((char *)pTicket)[2] = 0;
|
||||
((char *)pTicket)[3] = 0;
|
||||
memcpy((char *)pTicket + STEAM_ID_OFFSET_TICKET, &steam_id, sizeof(steam_id));
|
||||
*pcbTicket = cbMaxTicket;
|
||||
|
||||
Auth_Ticket_Data ticket_data;
|
||||
ticket_data.id = settings->get_local_steam_id();
|
||||
ticket_data.number = generate_steam_ticket_id();
|
||||
uint32 ttt = ticket_data.number;
|
||||
|
||||
memcpy(((char *)pTicket) + sizeof(uint64), &ttt, sizeof(ttt));
|
||||
return ticket_data;
|
||||
}
|
||||
//Conan Exiles doesn't work with 512 or 128, 256 seems to be the good size
|
||||
//Steam returns 234
|
||||
#define STEAM_AUTH_TICKET_SIZE 234
|
||||
|
||||
uint32 Auth_Ticket_Manager::getTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
|
||||
{
|
||||
if (cbMaxTicket < STEAM_TICKET_MIN_SIZE) return 0;
|
||||
if (cbMaxTicket > STEAM_AUTH_TICKET_SIZE) cbMaxTicket = STEAM_AUTH_TICKET_SIZE;
|
||||
|
||||
Auth_Ticket_Data ticket_data = getTicketData(pTicket, cbMaxTicket, pcbTicket );
|
||||
uint32 ttt = ticket_data.number;
|
||||
GetAuthSessionTicketResponse_t data;
|
||||
data.m_hAuthTicket = ttt;
|
||||
data.m_eResult = k_EResultOK;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), STEAM_TICKET_PROCESS_TIME);
|
||||
|
||||
outbound.push_back(ticket_data);
|
||||
|
||||
return ttt;
|
||||
}
|
||||
|
||||
CSteamID Auth_Ticket_Manager::fakeUser()
|
||||
{
|
||||
Auth_Ticket_Data data = {};
|
||||
data.id = generate_steam_anon_user();
|
||||
inbound.push_back(data);
|
||||
return data.id;
|
||||
}
|
||||
|
||||
void Auth_Ticket_Manager::cancelTicket(uint32 number)
|
||||
{
|
||||
auto ticket = std::find_if(outbound.begin(), outbound.end(), [&number](Auth_Ticket_Data const& item) { return item.number == number; });
|
||||
if (outbound.end() == ticket)
|
||||
return;
|
||||
|
||||
Auth_Ticket *auth_ticket = new Auth_Ticket();
|
||||
auth_ticket->set_number(number);
|
||||
auth_ticket->set_type(Auth_Ticket::CANCEL);
|
||||
Common_Message msg;
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_allocated_auth_ticket(auth_ticket);
|
||||
network->sendToAll(&msg, true);
|
||||
|
||||
outbound.erase(ticket);
|
||||
}
|
||||
|
||||
bool Auth_Ticket_Manager::SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser )
|
||||
{
|
||||
if (cubAuthBlobSize < STEAM_TICKET_MIN_SIZE) return false;
|
||||
|
||||
Auth_Ticket_Data data;
|
||||
uint64 id;
|
||||
memcpy(&id, (char *)pvAuthBlob + STEAM_ID_OFFSET_TICKET, sizeof(id));
|
||||
uint32 number;
|
||||
memcpy(&number, ((char *)pvAuthBlob) + sizeof(uint64), sizeof(number));
|
||||
data.id = CSteamID(id);
|
||||
data.number = number;
|
||||
if (pSteamIDUser) *pSteamIDUser = data.id;
|
||||
|
||||
for (auto & t : inbound) {
|
||||
if (t.id == data.id) {
|
||||
//Should this return false?
|
||||
launch_callback_gs(id, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
inbound.push_back(data);
|
||||
launch_callback_gs(id, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
EBeginAuthSessionResult Auth_Ticket_Manager::beginAuth(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID )
|
||||
{
|
||||
if (cbAuthTicket < STEAM_TICKET_MIN_SIZE) return k_EBeginAuthSessionResultInvalidTicket;
|
||||
|
||||
Auth_Ticket_Data data;
|
||||
uint64 id;
|
||||
memcpy(&id, (char *)pAuthTicket + STEAM_ID_OFFSET_TICKET, sizeof(id));
|
||||
uint32 number;
|
||||
memcpy(&number, ((char *)pAuthTicket) + sizeof(uint64), sizeof(number));
|
||||
data.id = CSteamID(id);
|
||||
data.number = number;
|
||||
data.created = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (auto & t : inbound) {
|
||||
if (t.id == data.id && !check_timedout(t.created, STEAM_TICKET_PROCESS_TIME)) {
|
||||
return k_EBeginAuthSessionResultDuplicateRequest;
|
||||
}
|
||||
}
|
||||
|
||||
inbound.push_back(data);
|
||||
launch_callback(steamID, k_EAuthSessionResponseOK, STEAM_TICKET_PROCESS_TIME);
|
||||
return k_EBeginAuthSessionResultOK;
|
||||
}
|
||||
|
||||
uint32 Auth_Ticket_Manager::countInboundAuth()
|
||||
{
|
||||
return inbound.size();
|
||||
}
|
||||
|
||||
bool Auth_Ticket_Manager::endAuth(CSteamID id)
|
||||
{
|
||||
bool erased = false;
|
||||
auto t = std::begin(inbound);
|
||||
while (t != std::end(inbound)) {
|
||||
if (t->id == id) {
|
||||
erased = true;
|
||||
t = inbound.erase(t);
|
||||
} else {
|
||||
++t;
|
||||
}
|
||||
}
|
||||
|
||||
return erased;
|
||||
}
|
||||
|
||||
void Auth_Ticket_Manager::Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
PRINT_DEBUG("TICKET DISCONNECT\n");
|
||||
auto t = std::begin(inbound);
|
||||
while (t != std::end(inbound)) {
|
||||
if (t->id.ConvertToUint64() == msg->source_id()) {
|
||||
launch_callback(t->id, k_EAuthSessionResponseUserNotConnectedToSteam);
|
||||
t = inbound.erase(t);
|
||||
} else {
|
||||
++t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->has_auth_ticket()) {
|
||||
if (msg->auth_ticket().type() == Auth_Ticket::CANCEL) {
|
||||
PRINT_DEBUG("TICKET CANCEL %llu\n", msg->source_id());
|
||||
uint32 number = msg->auth_ticket().number();
|
||||
auto t = std::begin(inbound);
|
||||
while (t != std::end(inbound)) {
|
||||
if (t->id.ConvertToUint64() == msg->source_id() && t->number == number) {
|
||||
PRINT_DEBUG("TICKET CANCELED\n");
|
||||
launch_callback(t->id, k_EAuthSessionResponseAuthTicketCanceled);
|
||||
t = inbound.erase(t);
|
||||
} else {
|
||||
++t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EMU_EXPERIMENTAL_BUILD
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
struct ips_test {
|
||||
uint32_t ip_from;
|
||||
uint32_t ip_to;
|
||||
};
|
||||
|
||||
static std::vector<struct ips_test> whitelist_ips;
|
||||
|
||||
void set_whitelist_ips(uint32_t *from, uint32_t *to, unsigned num_ips)
|
||||
{
|
||||
whitelist_ips.clear();
|
||||
for (unsigned i = 0; i < num_ips; ++i) {
|
||||
struct ips_test ip_a;
|
||||
PRINT_DEBUG("from: %hhu.%hhu.%hhu.%hhu\n", ((unsigned char *)&from[i])[0], ((unsigned char *)&from[i])[1], ((unsigned char *)&from[i])[2], ((unsigned char *)&from[i])[3]);
|
||||
PRINT_DEBUG("to: %hhu.%hhu.%hhu.%hhu\n", ((unsigned char *)&to[i])[0], ((unsigned char *)&to[i])[1], ((unsigned char *)&to[i])[2], ((unsigned char *)&to[i])[3]);
|
||||
ip_a.ip_from = ntohl(from[i]);
|
||||
ip_a.ip_to = ntohl(to[i]);
|
||||
if (ip_a.ip_to < ip_a.ip_from) continue;
|
||||
if ((ip_a.ip_to - ip_a.ip_from) > (1 << 25)) continue;
|
||||
PRINT_DEBUG("added\n");
|
||||
whitelist_ips.push_back(ip_a);
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_whitelist_ip(unsigned char *ip)
|
||||
{
|
||||
uint32_t ip_temp = 0;
|
||||
memcpy(&ip_temp, ip, sizeof(ip_temp));
|
||||
ip_temp = ntohl(ip_temp);
|
||||
|
||||
for (auto &i : whitelist_ips) {
|
||||
if (i.ip_from <= ip_temp && ip_temp <= i.ip_to) {
|
||||
PRINT_DEBUG("WHITELISTED IP %hhu.%hhu.%hhu.%hhu\n", ip[0], ip[1], ip[2], ip[3]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_lan_ip(const sockaddr *addr, int namelen)
|
||||
{
|
||||
if (!namelen) return false;
|
||||
|
||||
if (addr->sa_family == AF_INET) {
|
||||
struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
|
||||
unsigned char ip[4];
|
||||
memcpy(ip, &addr_in->sin_addr, sizeof(ip));
|
||||
PRINT_DEBUG("CHECK LAN IP %hhu.%hhu.%hhu.%hhu:%u\n", ip[0], ip[1], ip[2], ip[3], ntohs(addr_in->sin_port));
|
||||
if (is_whitelist_ip(ip)) return true;
|
||||
if (ip[0] == 127) return true;
|
||||
if (ip[0] == 10) return true;
|
||||
if (ip[0] == 192 && ip[1] == 168) return true;
|
||||
if (ip[0] == 169 && ip[1] == 254 && ip[2] != 0) return true;
|
||||
if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) return true;
|
||||
if ((ip[0] == 100) && ((ip[1] & 0xC0) == 0x40)) return true;
|
||||
if (ip[0] == 239) return true; //multicast
|
||||
if (ip[0] == 0) return true; //Current network
|
||||
if (ip[0] == 192 && (ip[1] == 18 || ip[1] == 19)) return true; //Used for benchmark testing of inter-network communications between two separate subnets.
|
||||
if (ip[0] >= 224) return true; //ip multicast (224 - 239) future use (240.0.0.0–255.255.255.254) broadcast (255.255.255.255)
|
||||
} else if (addr->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;
|
||||
unsigned char ip[16];
|
||||
unsigned char zeroes[16] = {};
|
||||
memcpy(ip, &addr_in6->sin6_addr, sizeof(ip));
|
||||
PRINT_DEBUG("CHECK LAN IP6 %hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu...%hhu\n", ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[15]);
|
||||
if (((ip[0] == 0xFF) && (ip[1] < 3) && (ip[15] == 1)) ||
|
||||
((ip[0] == 0xFE) && ((ip[1] & 0xC0) == 0x80))) return true;
|
||||
if (memcmp(zeroes, ip, sizeof(ip)) == 0) return true;
|
||||
if (memcmp(zeroes, ip, sizeof(ip) - 1) == 0 && ip[15] == 1) return true;
|
||||
if (ip[0] == 0xff) return true; //multicast
|
||||
if (ip[0] == 0xfc) return true; //unique local
|
||||
if (ip[0] == 0xfd) return true; //unique local
|
||||
//TODO: ipv4 mapped?
|
||||
}
|
||||
|
||||
PRINT_DEBUG("NOT LAN IP\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int ( WINAPI *Real_SendTo )( SOCKET s, const char *buf, int len, int flags, const sockaddr *to, int tolen) = sendto;
|
||||
int ( WINAPI *Real_Connect )( SOCKET s, const sockaddr *addr, int namelen ) = connect;
|
||||
int ( WINAPI *Real_WSAConnect )( SOCKET s, const sockaddr *addr, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS) = WSAConnect;
|
||||
|
||||
static int WINAPI Mine_SendTo( SOCKET s, const char *buf, int len, int flags, const sockaddr *to, int tolen) {
|
||||
PRINT_DEBUG("Mine_SendTo\n");
|
||||
if (is_lan_ip(to, tolen)) {
|
||||
return Real_SendTo( s, buf, len, flags, to, tolen );
|
||||
} else {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
static int WINAPI Mine_Connect( SOCKET s, const sockaddr *addr, int namelen )
|
||||
{
|
||||
PRINT_DEBUG("Mine_Connect\n");
|
||||
if (is_lan_ip(addr, namelen)) {
|
||||
return Real_Connect(s, addr, namelen);
|
||||
} else {
|
||||
WSASetLastError(WSAECONNREFUSED);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static int WINAPI Mine_WSAConnect( SOCKET s, const sockaddr *addr, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS)
|
||||
{
|
||||
PRINT_DEBUG("Mine_WSAConnect\n");
|
||||
if (is_lan_ip(addr, namelen)) {
|
||||
return Real_WSAConnect(s, addr, namelen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS);
|
||||
} else {
|
||||
WSASetLastError(WSAECONNREFUSED);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool file_exists (const std::string& name) {
|
||||
struct stat buffer;
|
||||
return (stat (name.c_str(), &buffer) == 0);
|
||||
}
|
||||
|
||||
#ifdef DETOURS_64BIT
|
||||
#define DLL_NAME "steam_api64.dll"
|
||||
#else
|
||||
#define DLL_NAME "steam_api.dll"
|
||||
#endif
|
||||
|
||||
HMODULE (WINAPI *Real_GetModuleHandleA)(LPCSTR lpModuleName) = GetModuleHandleA;
|
||||
HMODULE WINAPI Mine_GetModuleHandleA(LPCSTR lpModuleName)
|
||||
{
|
||||
PRINT_DEBUG("Mine_GetModuleHandleA %s\n", lpModuleName);
|
||||
if (!lpModuleName) return Real_GetModuleHandleA(lpModuleName);
|
||||
std::string in(lpModuleName);
|
||||
if (in == std::string(DLL_NAME)) {
|
||||
in = std::string("crack") + in;
|
||||
}
|
||||
|
||||
return Real_GetModuleHandleA(in.c_str());
|
||||
}
|
||||
|
||||
static void redirect_crackdll()
|
||||
{
|
||||
DetourTransactionBegin();
|
||||
DetourUpdateThread( GetCurrentThread() );
|
||||
DetourAttach( &(PVOID &)Real_GetModuleHandleA, Mine_GetModuleHandleA );
|
||||
DetourTransactionCommit();
|
||||
}
|
||||
|
||||
static void unredirect_crackdll()
|
||||
{
|
||||
DetourTransactionBegin();
|
||||
DetourUpdateThread( GetCurrentThread() );
|
||||
DetourDetach( &(PVOID &)Real_GetModuleHandleA, Mine_GetModuleHandleA );
|
||||
DetourTransactionCommit();
|
||||
}
|
||||
|
||||
HMODULE crack_dll_handle;
|
||||
static void load_dll()
|
||||
{
|
||||
std::string path = get_full_program_path();
|
||||
path += "crack";
|
||||
//path += PATH_SEPARATOR;
|
||||
path += DLL_NAME;
|
||||
PRINT_DEBUG("Crack file %s\n", path.c_str());
|
||||
if (file_exists(path)) {
|
||||
redirect_crackdll();
|
||||
crack_dll_handle = LoadLibraryW(utf8_decode(path).c_str());
|
||||
unredirect_crackdll();
|
||||
PRINT_DEBUG("Loaded crack file\n");
|
||||
}
|
||||
}
|
||||
|
||||
#include "local_storage.h"
|
||||
static void load_dlls()
|
||||
{
|
||||
std::string path = Local_Storage::get_game_settings_path();
|
||||
path += "load_dlls";
|
||||
path += PATH_SEPARATOR;
|
||||
|
||||
std::vector<std::string> paths = Local_Storage::get_filenames_path(path);
|
||||
for (auto & p: paths) {
|
||||
std::string full_path = path + p;
|
||||
size_t length = full_path.length();
|
||||
if (length < 4) continue;
|
||||
if (std::toupper(full_path[length - 1]) != 'L') continue;
|
||||
if (std::toupper(full_path[length - 2]) != 'L') continue;
|
||||
if (std::toupper(full_path[length - 3]) != 'D') continue;
|
||||
if (full_path[length - 4] != '.') continue;
|
||||
|
||||
PRINT_DEBUG("Trying to load %s\n", full_path.c_str());
|
||||
if (LoadLibraryW(utf8_decode(full_path).c_str())) {
|
||||
PRINT_DEBUG("LOADED %s\n", full_path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//For some reason when this function is optimized it breaks the shogun 2 prophet (reloaded) crack.
|
||||
#pragma optimize( "", off )
|
||||
bool crack_SteamAPI_RestartAppIfNecessary(uint32 unOwnAppID)
|
||||
{
|
||||
if (crack_dll_handle) {
|
||||
bool (__stdcall* restart_app)(uint32) = (bool (__stdcall *)(uint32))GetProcAddress(crack_dll_handle, "SteamAPI_RestartAppIfNecessary");
|
||||
if (restart_app) {
|
||||
PRINT_DEBUG("Call crack SteamAPI_RestartAppIfNecessary\n");
|
||||
redirect_crackdll();
|
||||
bool ret = restart_app(unOwnAppID);
|
||||
unredirect_crackdll();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#pragma optimize( "", on )
|
||||
|
||||
bool crack_SteamAPI_Init()
|
||||
{
|
||||
if (crack_dll_handle) {
|
||||
bool (__stdcall* init_app)() = (bool (__stdcall *)())GetProcAddress(crack_dll_handle, "SteamAPI_Init");
|
||||
if (init_app) {
|
||||
PRINT_DEBUG("Call crack SteamAPI_Init\n");
|
||||
redirect_crackdll();
|
||||
bool ret = init_app();
|
||||
unredirect_crackdll();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HINTERNET (WINAPI *Real_WinHttpConnect)(
|
||||
IN HINTERNET hSession,
|
||||
IN LPCWSTR pswzServerName,
|
||||
IN INTERNET_PORT nServerPort,
|
||||
IN DWORD dwReserved
|
||||
);
|
||||
|
||||
HINTERNET WINAPI Mine_WinHttpConnect(
|
||||
IN HINTERNET hSession,
|
||||
IN LPCWSTR pswzServerName,
|
||||
IN INTERNET_PORT nServerPort,
|
||||
IN DWORD dwReserved
|
||||
) {
|
||||
PRINT_DEBUG("Mine_WinHttpConnect %ls %u\n", pswzServerName, nServerPort);
|
||||
struct sockaddr_in ip4;
|
||||
struct sockaddr_in6 ip6;
|
||||
ip4.sin_family = AF_INET;
|
||||
ip6.sin6_family = AF_INET6;
|
||||
|
||||
if ((InetPtonW(AF_INET, pswzServerName, &(ip4.sin_addr)) && is_lan_ip((sockaddr *)&ip4, sizeof(ip4))) || (InetPtonW(AF_INET6, pswzServerName, &(ip6.sin6_addr)) && is_lan_ip((sockaddr *)&ip6, sizeof(ip6)))) {
|
||||
return Real_WinHttpConnect(hSession, pswzServerName, nServerPort, dwReserved);
|
||||
} else {
|
||||
return Real_WinHttpConnect(hSession, L"127.1.33.7", nServerPort, dwReserved);
|
||||
}
|
||||
}
|
||||
|
||||
HINTERNET (WINAPI *Real_WinHttpOpenRequest)(
|
||||
IN HINTERNET hConnect,
|
||||
IN LPCWSTR pwszVerb,
|
||||
IN LPCWSTR pwszObjectName,
|
||||
IN LPCWSTR pwszVersion,
|
||||
IN LPCWSTR pwszReferrer,
|
||||
IN LPCWSTR *ppwszAcceptTypes,
|
||||
IN DWORD dwFlags
|
||||
);
|
||||
|
||||
HINTERNET WINAPI Mine_WinHttpOpenRequest(
|
||||
IN HINTERNET hConnect,
|
||||
IN LPCWSTR pwszVerb,
|
||||
IN LPCWSTR pwszObjectName,
|
||||
IN LPCWSTR pwszVersion,
|
||||
IN LPCWSTR pwszReferrer,
|
||||
IN LPCWSTR *ppwszAcceptTypes,
|
||||
IN DWORD dwFlags
|
||||
) {
|
||||
PRINT_DEBUG("Mine_WinHttpOpenRequest %ls %ls %ls %ls %i\n", pwszVerb, pwszObjectName, pwszVersion, pwszReferrer, dwFlags);
|
||||
if (dwFlags & WINHTTP_FLAG_SECURE) {
|
||||
dwFlags ^= WINHTTP_FLAG_SECURE;
|
||||
}
|
||||
|
||||
return Real_WinHttpOpenRequest(hConnect, pwszVerb, pwszObjectName, pwszVersion, pwszReferrer, ppwszAcceptTypes, dwFlags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool network_functions_attached = false;
|
||||
BOOL WINAPI DllMain( HINSTANCE, DWORD dwReason, LPVOID ) {
|
||||
switch ( dwReason ) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
if (!file_exists(get_full_program_path() + "disable_lan_only.txt")) {
|
||||
PRINT_DEBUG("Hooking lan only functions\n");
|
||||
DetourTransactionBegin();
|
||||
DetourUpdateThread( GetCurrentThread() );
|
||||
DetourAttach( &(PVOID &)Real_SendTo, Mine_SendTo );
|
||||
DetourAttach( &(PVOID &)Real_Connect, Mine_Connect );
|
||||
DetourAttach( &(PVOID &)Real_WSAConnect, Mine_WSAConnect );
|
||||
|
||||
HMODULE winhttp = GetModuleHandle("winhttp.dll");
|
||||
if (winhttp) {
|
||||
Real_WinHttpConnect = (decltype(Real_WinHttpConnect))GetProcAddress(winhttp, "WinHttpConnect");
|
||||
DetourAttach( &(PVOID &)Real_WinHttpConnect, Mine_WinHttpConnect );
|
||||
// Real_WinHttpOpenRequest = (decltype(Real_WinHttpOpenRequest))GetProcAddress(winhttp, "WinHttpOpenRequest");
|
||||
// DetourAttach( &(PVOID &)Real_WinHttpOpenRequest, Mine_WinHttpOpenRequest );
|
||||
}
|
||||
|
||||
DetourTransactionCommit();
|
||||
network_functions_attached = true;
|
||||
}
|
||||
load_dll();
|
||||
load_dlls();
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
if (network_functions_attached) {
|
||||
DetourTransactionBegin();
|
||||
DetourUpdateThread( GetCurrentThread() );
|
||||
DetourDetach( &(PVOID &)Real_SendTo, Mine_SendTo );
|
||||
DetourDetach( &(PVOID &)Real_Connect, Mine_Connect );
|
||||
DetourDetach( &(PVOID &)Real_WSAConnect, Mine_WSAConnect );
|
||||
if (Real_WinHttpConnect) {
|
||||
DetourDetach( &(PVOID &)Real_WinHttpConnect, Mine_WinHttpConnect );
|
||||
// DetourDetach( &(PVOID &)Real_WinHttpOpenRequest, Mine_WinHttpOpenRequest );
|
||||
}
|
||||
DetourTransactionCommit();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
void set_whitelist_ips(uint32_t *from, uint32_t *to, unsigned num_ips)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
void set_whitelist_ips(uint32_t *from, uint32_t *to, unsigned num_ips)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
466
all/base.h
Normal file
466
all/base.h
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef BASE_INCLUDE
|
||||
#define BASE_INCLUDE
|
||||
|
||||
#include "common_includes.h"
|
||||
|
||||
#define PUSH_BACK_IF_NOT_IN(vector, element) { if(std::find(vector.begin(), vector.end(), element) == vector.end()) vector.push_back(element); }
|
||||
|
||||
extern std::recursive_mutex global_mutex;
|
||||
|
||||
std::string get_env_variable(std::string name);
|
||||
bool set_env_variable(std::string name, std::string value);
|
||||
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout);
|
||||
|
||||
class CCallbackMgr
|
||||
{
|
||||
public:
|
||||
static void SetRegister(class CCallbackBase *pCallback, int iCallback) {
|
||||
pCallback->m_nCallbackFlags |= CCallbackBase::k_ECallbackFlagsRegistered;
|
||||
pCallback->m_iCallback = iCallback;
|
||||
};
|
||||
|
||||
static void SetUnregister(class CCallbackBase *pCallback) {
|
||||
if (pCallback)
|
||||
pCallback->m_nCallbackFlags &= !CCallbackBase::k_ECallbackFlagsRegistered;
|
||||
};
|
||||
|
||||
static bool isServer(class CCallbackBase *pCallback) {
|
||||
return (pCallback->m_nCallbackFlags & CCallbackBase::k_ECallbackFlagsGameServer) != 0;
|
||||
};
|
||||
};
|
||||
|
||||
#define STEAM_CALLRESULT_TIMEOUT 120.0
|
||||
#define STEAM_CALLRESULT_WAIT_FOR_CB 0.01
|
||||
struct Steam_Call_Result {
|
||||
Steam_Call_Result(SteamAPICall_t a, int icb, void *r, unsigned int s, double r_in, bool run_cc_cb) {
|
||||
api_call = a;
|
||||
result.resize(s);
|
||||
if (s > 0 && r != NULL)
|
||||
memcpy(&(result[0]), r, s);
|
||||
created = std::chrono::high_resolution_clock::now();
|
||||
run_in = r_in;
|
||||
run_call_completed_cb = run_cc_cb;
|
||||
iCallback = icb;
|
||||
}
|
||||
|
||||
bool operator==(const struct Steam_Call_Result& a)
|
||||
{
|
||||
return a.api_call == api_call && a.callbacks == callbacks;
|
||||
}
|
||||
|
||||
bool timed_out() {
|
||||
return check_timedout(created, STEAM_CALLRESULT_TIMEOUT);
|
||||
}
|
||||
|
||||
bool call_completed() {
|
||||
return (!reserved) && check_timedout(created, run_in);
|
||||
}
|
||||
|
||||
bool can_execute() {
|
||||
return (!to_delete) && call_completed() && (has_cb() || check_timedout(created, STEAM_CALLRESULT_WAIT_FOR_CB));
|
||||
}
|
||||
|
||||
bool has_cb() {
|
||||
return callbacks.size() > 0;
|
||||
}
|
||||
|
||||
SteamAPICall_t api_call;
|
||||
std::vector<class CCallbackBase *> callbacks;
|
||||
std::vector<char> result;
|
||||
bool to_delete = false;
|
||||
bool reserved = false;
|
||||
std::chrono::high_resolution_clock::time_point created;
|
||||
double run_in;
|
||||
bool run_call_completed_cb;
|
||||
int iCallback;
|
||||
};
|
||||
|
||||
int generate_random_int();
|
||||
SteamAPICall_t generate_steam_api_call_id();
|
||||
CSteamID generate_steam_id_user();
|
||||
CSteamID generate_steam_id_server();
|
||||
CSteamID generate_steam_id_anonserver();
|
||||
CSteamID generate_steam_id_lobby();
|
||||
std::string get_full_lib_path();
|
||||
std::string get_full_program_path();
|
||||
std::string get_current_path();
|
||||
std::string canonical_path(std::string path);
|
||||
bool file_exists_(std::string full_path);
|
||||
unsigned int file_size_(std::string full_path);
|
||||
|
||||
#define DEFAULT_CB_TIMEOUT 0.002
|
||||
|
||||
class SteamCallResults {
|
||||
std::vector<struct Steam_Call_Result> callresults;
|
||||
std::vector<class CCallbackBase *> completed_callbacks;
|
||||
void (*cb_all)(std::vector<char> result, int callback) = nullptr;
|
||||
|
||||
public:
|
||||
void addCallCompleted(class CCallbackBase *cb) {
|
||||
if (std::find(completed_callbacks.begin(), completed_callbacks.end(), cb) == completed_callbacks.end()) {
|
||||
completed_callbacks.push_back(cb);
|
||||
}
|
||||
}
|
||||
|
||||
void rmCallCompleted(class CCallbackBase *cb) {
|
||||
auto c = std::find(completed_callbacks.begin(), completed_callbacks.end(), cb);
|
||||
if (c != completed_callbacks.end()) {
|
||||
completed_callbacks.erase(c);
|
||||
}
|
||||
}
|
||||
|
||||
void addCallBack(SteamAPICall_t api_call, class CCallbackBase *cb) {
|
||||
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
|
||||
if (cb_result != callresults.end()) {
|
||||
cb_result->callbacks.push_back(cb);
|
||||
CCallbackMgr::SetRegister(cb, cb->GetICallback());
|
||||
}
|
||||
}
|
||||
|
||||
bool exists(SteamAPICall_t api_call) {
|
||||
auto cr = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
|
||||
if (cr == callresults.end()) return false;
|
||||
if (!cr->call_completed()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool callback_result(SteamAPICall_t api_call, void *copy_to, unsigned int size) {
|
||||
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
|
||||
if (cb_result != callresults.end()) {
|
||||
if (!cb_result->call_completed()) return false;
|
||||
if (cb_result->result.size() > size) return false;
|
||||
|
||||
memcpy(copy_to, &(cb_result->result[0]), cb_result->result.size());
|
||||
cb_result->to_delete = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void rmCallBack(SteamAPICall_t api_call, class CCallbackBase *cb) {
|
||||
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
|
||||
if (cb_result != callresults.end()) {
|
||||
auto it = std::find(cb_result->callbacks.begin(), cb_result->callbacks.end(), cb);
|
||||
if (it != cb_result->callbacks.end()) {
|
||||
cb_result->callbacks.erase(it);
|
||||
CCallbackMgr::SetUnregister(cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rmCallBack(class CCallbackBase *cb) {
|
||||
//TODO: check if callback is callback or call result?
|
||||
for (auto & cr: callresults) {
|
||||
auto it = std::find(cr.callbacks.begin(), cr.callbacks.end(), cb);
|
||||
if (it != cr.callbacks.end()) {
|
||||
cr.callbacks.erase(it);
|
||||
}
|
||||
|
||||
if (cr.callbacks.size() == 0) {
|
||||
cr.to_delete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SteamAPICall_t addCallResult(SteamAPICall_t api_call, int iCallback, void *result, unsigned int size, double timeout=DEFAULT_CB_TIMEOUT, bool run_call_completed_cb=true) {
|
||||
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
|
||||
if (cb_result != callresults.end()) {
|
||||
if (cb_result->reserved) {
|
||||
std::chrono::high_resolution_clock::time_point created = cb_result->created;
|
||||
std::vector<class CCallbackBase *> temp_cbs = cb_result->callbacks;
|
||||
*cb_result = Steam_Call_Result(api_call, iCallback, result, size, timeout, run_call_completed_cb);
|
||||
cb_result->callbacks = temp_cbs;
|
||||
cb_result->created = created;
|
||||
return cb_result->api_call;
|
||||
}
|
||||
} else {
|
||||
struct Steam_Call_Result res = Steam_Call_Result(api_call, iCallback, result, size, timeout, run_call_completed_cb);
|
||||
callresults.push_back(res);
|
||||
return callresults.back().api_call;
|
||||
}
|
||||
|
||||
PRINT_DEBUG("addCallResult ERROR\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SteamAPICall_t reserveCallResult() {
|
||||
struct Steam_Call_Result res = Steam_Call_Result(generate_steam_api_call_id(), 0, NULL, 0, 0.0, true);
|
||||
res.reserved = true;
|
||||
callresults.push_back(res);
|
||||
return callresults.back().api_call;
|
||||
}
|
||||
|
||||
SteamAPICall_t addCallResult(int iCallback, void *result, unsigned int size, double timeout=DEFAULT_CB_TIMEOUT, bool run_call_completed_cb=true) {
|
||||
return addCallResult(generate_steam_api_call_id(), iCallback, result, size, timeout, run_call_completed_cb);
|
||||
}
|
||||
|
||||
void setCbAll(void (*cb_all)(std::vector<char> result, int callback)) {
|
||||
this->cb_all = cb_all;
|
||||
}
|
||||
|
||||
void runCallResults() {
|
||||
unsigned long current_size = callresults.size();
|
||||
for (unsigned i = 0; i < current_size; ++i) {
|
||||
unsigned index = i;
|
||||
|
||||
if (!callresults[index].to_delete) {
|
||||
if (callresults[index].can_execute()) {
|
||||
std::vector<char> result = callresults[index].result;
|
||||
SteamAPICall_t api_call = callresults[index].api_call;
|
||||
bool run_call_completed_cb = callresults[index].run_call_completed_cb;
|
||||
int iCallback = callresults[index].iCallback;
|
||||
if (run_call_completed_cb) {
|
||||
callresults[index].run_call_completed_cb = false;
|
||||
}
|
||||
|
||||
callresults[index].to_delete = true;
|
||||
if (callresults[index].has_cb()) {
|
||||
std::vector<class CCallbackBase *> temp_cbs = callresults[index].callbacks;
|
||||
for (auto & cb : temp_cbs) {
|
||||
PRINT_DEBUG("Calling callresult %p %i\n", cb, cb->GetICallback());
|
||||
global_mutex.unlock();
|
||||
//TODO: unlock relock doesn't work if mutex was locked more than once.
|
||||
if (run_call_completed_cb) { //run the right function depending on if it's a callback or a call result.
|
||||
cb->Run(&(result[0]), false, api_call);
|
||||
} else {
|
||||
cb->Run(&(result[0]));
|
||||
}
|
||||
//COULD BE DELETED SO DON'T TOUCH CB
|
||||
global_mutex.lock();
|
||||
PRINT_DEBUG("callresult done\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (run_call_completed_cb) {
|
||||
//can it happen that one is removed during the callback?
|
||||
std::vector<class CCallbackBase *> callbacks = completed_callbacks;
|
||||
SteamAPICallCompleted_t data;
|
||||
data.m_hAsyncCall = api_call;
|
||||
data.m_iCallback = iCallback;
|
||||
data.m_cubParam = result.size();
|
||||
|
||||
for (auto & cb: callbacks) {
|
||||
PRINT_DEBUG("Call complete cb %i %p %llu\n", iCallback, cb, api_call);
|
||||
//TODO: check if this is a problem or not.
|
||||
SteamAPICallCompleted_t temp = data;
|
||||
global_mutex.unlock();
|
||||
cb->Run(&temp);
|
||||
global_mutex.lock();
|
||||
}
|
||||
|
||||
if (cb_all) {
|
||||
std::vector<char> res;
|
||||
res.resize(sizeof(data));
|
||||
memcpy(&(res[0]), &data, sizeof(data));
|
||||
cb_all(res, data.k_iCallback);
|
||||
}
|
||||
} else {
|
||||
if (cb_all) {
|
||||
cb_all(result, iCallback);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (callresults[index].timed_out()) {
|
||||
callresults[index].to_delete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_DEBUG("runCallResults erase to_delete\n");
|
||||
auto c = std::begin(callresults);
|
||||
while (c != std::end(callresults)) {
|
||||
if (c->to_delete) {
|
||||
if (c->timed_out()) {
|
||||
c = callresults.erase(c);
|
||||
} else {
|
||||
++c;
|
||||
}
|
||||
} else {
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Steam_Call_Back {
|
||||
std::vector<class CCallbackBase *> callbacks;
|
||||
std::vector<std::vector<char>> results;
|
||||
};
|
||||
|
||||
class SteamCallBacks {
|
||||
std::map<int, struct Steam_Call_Back> callbacks;
|
||||
SteamCallResults *results;
|
||||
public:
|
||||
SteamCallBacks(SteamCallResults *results) {
|
||||
this->results = results;
|
||||
}
|
||||
|
||||
void addCallBack(int iCallback, class CCallbackBase *cb) {
|
||||
PRINT_DEBUG("addCallBack %i\n", iCallback);
|
||||
if (iCallback == SteamAPICallCompleted_t::k_iCallback) {
|
||||
results->addCallCompleted(cb);
|
||||
CCallbackMgr::SetRegister(cb, iCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::find(callbacks[iCallback].callbacks.begin(), callbacks[iCallback].callbacks.end(), cb) == callbacks[iCallback].callbacks.end()) {
|
||||
callbacks[iCallback].callbacks.push_back(cb);
|
||||
CCallbackMgr::SetRegister(cb, iCallback);
|
||||
for (auto & res: callbacks[iCallback].results) {
|
||||
//TODO: timeout?
|
||||
SteamAPICall_t api_id = results->addCallResult(iCallback, &(res[0]), res.size(), 0.0, false);
|
||||
results->addCallBack(api_id, cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addCBResult(int iCallback, void *result, unsigned int size, double timeout, bool dont_post_if_already) {
|
||||
if (dont_post_if_already) {
|
||||
for (auto & r : callbacks[iCallback].results) {
|
||||
if (r.size() == size) {
|
||||
if (memcmp(&(r[0]), result, size) == 0) {
|
||||
//cb already posted
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char> temp;
|
||||
temp.resize(size);
|
||||
memcpy(&(temp[0]), result, size);
|
||||
callbacks[iCallback].results.push_back(temp);
|
||||
for (auto cb: callbacks[iCallback].callbacks) {
|
||||
SteamAPICall_t api_id = results->addCallResult(iCallback, result, size, timeout, false);
|
||||
results->addCallBack(api_id, cb);
|
||||
}
|
||||
|
||||
if (callbacks[iCallback].callbacks.empty()) {
|
||||
results->addCallResult(iCallback, result, size, timeout, false);
|
||||
}
|
||||
}
|
||||
|
||||
void addCBResult(int iCallback, void *result, unsigned int size) {
|
||||
addCBResult(iCallback, result, size, DEFAULT_CB_TIMEOUT, false);
|
||||
}
|
||||
|
||||
void addCBResult(int iCallback, void *result, unsigned int size, bool dont_post_if_already) {
|
||||
addCBResult(iCallback, result, size, DEFAULT_CB_TIMEOUT, dont_post_if_already);
|
||||
}
|
||||
|
||||
void addCBResult(int iCallback, void *result, unsigned int size, double timeout) {
|
||||
addCBResult(iCallback, result, size, timeout, false);
|
||||
}
|
||||
|
||||
void rmCallBack(int iCallback, class CCallbackBase *cb) {
|
||||
if (iCallback == SteamAPICallCompleted_t::k_iCallback) {
|
||||
results->rmCallCompleted(cb);
|
||||
CCallbackMgr::SetUnregister(cb);
|
||||
return;
|
||||
}
|
||||
|
||||
auto c = std::find(callbacks[iCallback].callbacks.begin(), callbacks[iCallback].callbacks.end(), cb);
|
||||
if (c != callbacks[iCallback].callbacks.end()) {
|
||||
callbacks[iCallback].callbacks.erase(c);
|
||||
CCallbackMgr::SetUnregister(cb);
|
||||
results->rmCallBack(cb);
|
||||
}
|
||||
}
|
||||
|
||||
void runCallBacks() {
|
||||
for (auto & c : callbacks) {
|
||||
c.second.results.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Auth_Ticket_Data {
|
||||
CSteamID id;
|
||||
uint64 number;
|
||||
std::chrono::high_resolution_clock::time_point created;
|
||||
};
|
||||
|
||||
class Auth_Ticket_Manager {
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallBacks *callbacks;
|
||||
|
||||
void launch_callback(CSteamID id, EAuthSessionResponse resp, double delay=0);
|
||||
void launch_callback_gs(CSteamID id, bool approved);
|
||||
std::vector<struct Auth_Ticket_Data> inbound, outbound;
|
||||
public:
|
||||
Auth_Ticket_Manager(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks);
|
||||
|
||||
void Callback(Common_Message *msg);
|
||||
uint32 getTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket );
|
||||
void cancelTicket(uint32 number);
|
||||
EBeginAuthSessionResult beginAuth(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID);
|
||||
bool endAuth(CSteamID id);
|
||||
uint32 countInboundAuth();
|
||||
bool SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser );
|
||||
CSteamID fakeUser();
|
||||
Auth_Ticket_Data getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket );
|
||||
};
|
||||
|
||||
struct RunCBs {
|
||||
void (*function)(void *object);
|
||||
void *object;
|
||||
};
|
||||
|
||||
class RunEveryRunCB {
|
||||
std::vector<struct RunCBs> cbs;
|
||||
public:
|
||||
void add(void (*cb)(void *object), void *object) {
|
||||
remove(cb, object);
|
||||
RunCBs rcb;
|
||||
rcb.function = cb;
|
||||
rcb.object = object;
|
||||
cbs.push_back(rcb);
|
||||
}
|
||||
|
||||
void remove(void (*cb)(void *object), void *object) {
|
||||
auto c = std::begin(cbs);
|
||||
while (c != std::end(cbs)) {
|
||||
if (c->function == cb && c->object == object) {
|
||||
c = cbs.erase(c);
|
||||
} else {
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
std::vector<struct RunCBs> temp_cbs = cbs;
|
||||
for (auto c : temp_cbs) {
|
||||
c.function(c.object);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void set_whitelist_ips(uint32_t *from, uint32_t *to, unsigned num_ips);
|
||||
#ifdef EMU_EXPERIMENTAL_BUILD
|
||||
bool crack_SteamAPI_RestartAppIfNecessary(uint32 unOwnAppID);
|
||||
bool crack_SteamAPI_Init();
|
||||
#endif
|
||||
|
||||
#endif
|
||||
201
all/common_includes.h
Normal file
201
all/common_includes.h
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef __INCLUDED_COMMON_INCLUDES__
|
||||
#define __INCLUDED_COMMON_INCLUDES__
|
||||
|
||||
#if defined(WIN64) || defined(_WIN64) || defined(__MINGW64__)
|
||||
#define __WINDOWS_64__
|
||||
#elif defined(WIN32) || defined(_WIN32) || defined(__MINGW32__)
|
||||
#define __WINDOWS_32__
|
||||
#endif
|
||||
|
||||
#if defined(__WINDOWS_32__) || defined(__WINDOWS_64__)
|
||||
#define __WINDOWS__
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(linux)
|
||||
#if defined(__x86_64__)
|
||||
#define __LINUX_64__
|
||||
#else
|
||||
#define __LINUX_32__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__LINUX_32__) || defined(__LINUX_64__)
|
||||
#define __LINUX__
|
||||
#endif
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#define STEAM_WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define STEAM_API_EXPORTS
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <processthreadsapi.h>
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#include <iphlpapi.h> // Include winsock2 before this, or winsock2 iphlpapi will be unavailable
|
||||
#include <shlobj.h>
|
||||
|
||||
#define MSG_NOSIGNAL 0
|
||||
|
||||
#define SystemFunction036 NTAPI SystemFunction036
|
||||
#include <ntsecapi.h>
|
||||
#undef SystemFunction036
|
||||
|
||||
#ifndef EMU_RELEASE_BUILD
|
||||
#define PRINT_DEBUG(a, ...) do {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, "%u " a, GetCurrentThreadId(), __VA_ARGS__); fclose(t); WSASetLastError(0);} while (0)
|
||||
#endif
|
||||
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
#define PATH_SEPARATOR "\\"
|
||||
|
||||
#ifdef EMU_EXPERIMENTAL_BUILD
|
||||
#include <winhttp.h>
|
||||
|
||||
#include "../detours/detours.h"
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
// Convert a wide Unicode string to an UTF8 string
|
||||
inline std::string utf8_encode(const std::wstring &wstr)
|
||||
{
|
||||
if( wstr.empty() ) return std::string();
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
|
||||
std::string strTo( size_needed, 0 );
|
||||
WideCharToMultiByte (CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
|
||||
return strTo;
|
||||
}
|
||||
|
||||
// Convert an UTF8 string to a wide Unicode String
|
||||
inline std::wstring utf8_decode(const std::string &str)
|
||||
{
|
||||
if( str.empty() ) return std::wstring();
|
||||
int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
|
||||
std::wstring wstrTo( size_needed, 0 );
|
||||
MultiByteToWideChar (CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
|
||||
return wstrTo;
|
||||
}
|
||||
|
||||
#elif defined(__LINUX__)
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <netdb.h>
|
||||
#include <dlfcn.h>
|
||||
#include <utime.h>
|
||||
|
||||
#define PATH_MAX_STRING_SIZE 512
|
||||
|
||||
#ifndef EMU_RELEASE_BUILD
|
||||
#define PRINT_DEBUG(...) {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, __VA_ARGS__); fclose(t);}
|
||||
#endif
|
||||
#define PATH_SEPARATOR "/"
|
||||
|
||||
#define utf8_decode(a) a
|
||||
#endif
|
||||
//#define PRINT_DEBUG(...) fprintf(stdout, __VA_ARGS__)
|
||||
#ifdef EMU_RELEASE_BUILD
|
||||
#define PRINT_DEBUG(...)
|
||||
#endif
|
||||
|
||||
// C/C++ includes
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <list>
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
inline std::string ascii_to_lowercase(std::string data) {
|
||||
std::transform(data.begin(), data.end(), data.begin(),
|
||||
[](unsigned char c){ return std::tolower(c); });
|
||||
return data;
|
||||
}
|
||||
|
||||
// Other libs includes
|
||||
#include "../json/json.hpp"
|
||||
#include "../controller/gamepad.h"
|
||||
|
||||
// Steamsdk includes
|
||||
#include "../sdk_includes/steam_api.h"
|
||||
#include "../sdk_includes/steam_gameserver.h"
|
||||
#include "../sdk_includes/steamdatagram_tickets.h"
|
||||
|
||||
// Emulator includes
|
||||
#include "net.pb.h"
|
||||
#include "settings.h"
|
||||
#include "local_storage.h"
|
||||
#include "network.h"
|
||||
|
||||
// Emulator defines
|
||||
#define CLIENT_HSTEAMUSER 1
|
||||
#define SERVER_HSTEAMUSER 1
|
||||
|
||||
#define DEFAULT_NAME "Noob"
|
||||
#define PROGRAM_NAME_1 "Go"
|
||||
#define PROGRAM_NAME_2 "ld"
|
||||
#define PROGRAM_NAME_3 "be"
|
||||
#define PROGRAM_NAME_4 "rg "
|
||||
#define PROGRAM_NAME_5 "St"
|
||||
#define PROGRAM_NAME_6 "ea"
|
||||
#define PROGRAM_NAME_7 "mE"
|
||||
#define PROGRAM_NAME_8 "mu"
|
||||
|
||||
#define DEFAULT_LANGUAGE "english"
|
||||
|
||||
#define LOBBY_CONNECT_APPID ((uint32)-2)
|
||||
|
||||
#endif//__INCLUDED_COMMON_INCLUDES__
|
||||
1193
all/dll.cpp
Normal file
1193
all/dll.cpp
Normal file
File diff suppressed because it is too large
Load diff
34
all/dll.h
Normal file
34
all/dll.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_client.h"
|
||||
|
||||
#ifdef STEAMCLIENT_DLL
|
||||
#define STEAMAPI_API static
|
||||
#define STEAMCLIENT_API S_API_EXPORT
|
||||
#else
|
||||
#define STEAMAPI_API S_API_EXPORT
|
||||
#define STEAMCLIENT_API static
|
||||
#endif
|
||||
|
||||
Steam_Client *get_steam_client();
|
||||
bool steamclient_has_ipv6_functions();
|
||||
|
||||
HSteamUser flat_hsteamuser();
|
||||
HSteamPipe flat_hsteampipe();
|
||||
HSteamUser flat_gs_hsteamuser();
|
||||
HSteamPipe flat_gs_hsteampipe();
|
||||
7080
all/flat.cpp
Normal file
7080
all/flat.cpp
Normal file
File diff suppressed because it is too large
Load diff
788
all/local_storage.cpp
Normal file
788
all/local_storage.cpp
Normal file
|
|
@ -0,0 +1,788 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "local_storage.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_STATIC
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_ONLY_JPEG
|
||||
#if defined(__WINDOWS__)
|
||||
#define STBI_WINDOWS_UTF8
|
||||
#endif
|
||||
#include "../stb/stb_image.h"
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_STATIC
|
||||
#include "../stb/stb_image_write.h"
|
||||
|
||||
struct File_Data {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
#ifdef NO_DISK_WRITES
|
||||
std::string Local_Storage::get_program_path()
|
||||
{
|
||||
return " ";
|
||||
}
|
||||
|
||||
|
||||
std::string Local_Storage::get_user_appdata_path()
|
||||
{
|
||||
return " ";
|
||||
}
|
||||
|
||||
std::string Local_Storage::get_game_settings_path()
|
||||
{
|
||||
return " ";
|
||||
}
|
||||
|
||||
std::string Local_Storage::get_path(std::string folder)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string Local_Storage::get_global_settings_path()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
Local_Storage::Local_Storage(std::string save_directory)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Local_Storage::setAppId(uint32 appid)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int Local_Storage::store_file_data(std::string folder, std::string file, char *data, unsigned int length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Local_Storage::store_data(std::string folder, std::string file, char *data, unsigned int length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Local_Storage::store_data_settings(std::string file, char *data, unsigned int length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length, unsigned int offset)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Local_Storage::get_data(std::string folder, std::string file, char *data, unsigned int max_length, unsigned int offset)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Local_Storage::get_data_settings(std::string file, char *data, unsigned int max_length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Local_Storage::count_files(std::string folder)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Local_Storage::file_exists(std::string folder, std::string file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int Local_Storage::file_size(std::string folder, std::string file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Local_Storage::file_delete(std::string folder, std::string file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t Local_Storage::file_timestamp(std::string folder, std::string file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Local_Storage::iterate_file(std::string folder, int index, char *output_filename, int32 *output_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Local_Storage::update_save_filenames(std::string folder)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Local_Storage::load_json(std::string full_path, nlohmann::json& json)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
|
||||
{
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::vector<image_pixel_t> Local_Storage::load_image(std::string const& image_path)
|
||||
{
|
||||
return std::vector<image_pixel_t>();
|
||||
}
|
||||
|
||||
bool Local_Storage::save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
#if defined(__WINDOWS__)
|
||||
|
||||
static BOOL DirectoryExists(LPCWSTR szPath)
|
||||
{
|
||||
DWORD dwAttrib = GetFileAttributesW(szPath);
|
||||
|
||||
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
||||
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
|
||||
static void createDirectoryRecursively(std::wstring path)
|
||||
{
|
||||
unsigned long long pos = 0;
|
||||
do
|
||||
{
|
||||
pos = path.find_first_of(L"\\/", pos + 1);
|
||||
CreateDirectoryW(path.substr(0, pos).c_str(), NULL);
|
||||
} while (pos != std::string::npos);
|
||||
}
|
||||
|
||||
static void create_directory(std::string in_path)
|
||||
{
|
||||
std::wstring strPath = utf8_decode(in_path);
|
||||
if (DirectoryExists(strPath.c_str()) == FALSE)
|
||||
createDirectoryRecursively(strPath);
|
||||
}
|
||||
|
||||
static std::vector<struct File_Data> get_filenames(std::string in_path)
|
||||
{
|
||||
std::vector<struct File_Data> output;
|
||||
in_path = in_path.append("\\*");
|
||||
WIN32_FIND_DATAW ffd;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
|
||||
std::wstring strPath = utf8_decode(in_path);
|
||||
// Start iterating over the files in the path directory.
|
||||
hFind = ::FindFirstFileW (strPath.c_str(), &ffd);
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do // Managed to locate and create an handle to that folder.
|
||||
{
|
||||
if (wcscmp(L".", ffd.cFileName) == 0) continue;
|
||||
if (wcscmp(L"..", ffd.cFileName) == 0) continue;
|
||||
struct File_Data f_data;
|
||||
f_data.name = utf8_encode(ffd.cFileName);
|
||||
output.push_back(f_data);
|
||||
} while (::FindNextFileW(hFind, &ffd) == TRUE);
|
||||
::FindClose(hFind);
|
||||
} else {
|
||||
//printf("Failed to find path: %s", strPath.c_str());
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static std::vector<struct File_Data> get_filenames_recursive_w(std::wstring base_path)
|
||||
{
|
||||
if (base_path.back() == *L"\\")
|
||||
base_path.pop_back();
|
||||
std::vector<struct File_Data> output;
|
||||
std::wstring strPath = base_path;
|
||||
strPath = strPath.append(L"\\*");
|
||||
WIN32_FIND_DATAW ffd;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
|
||||
// Start iterating over the files in the path directory.
|
||||
hFind = ::FindFirstFileW (strPath.c_str(), &ffd);
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do // Managed to locate and create an handle to that folder.
|
||||
{
|
||||
if (wcscmp(L".", ffd.cFileName) == 0) continue;
|
||||
if (wcscmp(L"..", ffd.cFileName) == 0) continue;
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
// Construct new path from our base path
|
||||
std::wstring dir_name = ffd.cFileName;
|
||||
|
||||
std::wstring path = base_path;
|
||||
path += L"\\";
|
||||
path += dir_name;
|
||||
|
||||
std::vector<struct File_Data> lower = get_filenames_recursive_w(path);
|
||||
std::transform(lower.begin(), lower.end(), std::back_inserter(output), [&dir_name](File_Data f) {f.name = utf8_encode(dir_name) + "\\" + f.name; return f;});
|
||||
} else {
|
||||
File_Data f;
|
||||
f.name = utf8_encode(ffd.cFileName);
|
||||
output.push_back(f);
|
||||
}
|
||||
} while (::FindNextFileW(hFind, &ffd) == TRUE);
|
||||
::FindClose(hFind);
|
||||
} else {
|
||||
//printf("Failed to find path: %s", strPath.c_str());
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static std::vector<struct File_Data> get_filenames_recursive(std::string base_path)
|
||||
{
|
||||
return get_filenames_recursive_w(utf8_decode(base_path));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* recursive mkdir */
|
||||
static int mkdir_p(const char *dir, const mode_t mode) {
|
||||
char tmp[PATH_MAX_STRING_SIZE];
|
||||
char *p = NULL;
|
||||
struct stat sb;
|
||||
size_t len;
|
||||
|
||||
/* copy path */
|
||||
len = strnlen (dir, PATH_MAX_STRING_SIZE);
|
||||
if (len == 0 || len == PATH_MAX_STRING_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
memcpy (tmp, dir, len);
|
||||
tmp[len] = '\0';
|
||||
|
||||
/* remove trailing slash */
|
||||
if(tmp[len - 1] == '/') {
|
||||
tmp[len - 1] = '\0';
|
||||
}
|
||||
|
||||
/* check if path exists and is a directory */
|
||||
if (stat (tmp, &sb) == 0) {
|
||||
if (S_ISDIR (sb.st_mode)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* recursive mkdir */
|
||||
for(p = tmp + 1; *p; p++) {
|
||||
if(*p == '/') {
|
||||
*p = 0;
|
||||
/* test path */
|
||||
if (stat(tmp, &sb) != 0) {
|
||||
/* path does not exist - create directory */
|
||||
if (mkdir(tmp, mode) < 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if (!S_ISDIR(sb.st_mode)) {
|
||||
/* not a directory */
|
||||
return -1;
|
||||
}
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
/* test path */
|
||||
if (stat(tmp, &sb) != 0) {
|
||||
/* path does not exist - create directory */
|
||||
if (mkdir(tmp, mode) < 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if (!S_ISDIR(sb.st_mode)) {
|
||||
/* not a directory */
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void create_directory(std::string strPath)
|
||||
{
|
||||
mkdir_p(strPath.c_str(), 0777);
|
||||
}
|
||||
|
||||
static std::vector<struct File_Data> get_filenames(std::string strPath)
|
||||
{
|
||||
DIR *dp;
|
||||
int i = 0;
|
||||
struct dirent *ep;
|
||||
std::vector<struct File_Data> output;
|
||||
dp = opendir (strPath.c_str());
|
||||
|
||||
if (dp != NULL)
|
||||
{
|
||||
while ((ep = readdir (dp))) {
|
||||
if (memcmp(ep->d_name, ".", 2) != 0 && memcmp(ep->d_name, "..", 3) != 0) {
|
||||
struct File_Data f_data;
|
||||
f_data.name = ep->d_name;
|
||||
output.push_back(f_data);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
(void) closedir (dp);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static std::vector<struct File_Data> get_filenames_recursive(std::string base_path)
|
||||
{
|
||||
std::vector<struct File_Data> output;
|
||||
std::string path;
|
||||
struct dirent *dp;
|
||||
DIR *dir = opendir(base_path.c_str());
|
||||
|
||||
// Unable to open directory stream
|
||||
if (!dir)
|
||||
return output;
|
||||
|
||||
while ((dp = readdir(dir)) != NULL)
|
||||
{
|
||||
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
|
||||
{
|
||||
if (dp->d_type == DT_REG) {
|
||||
File_Data f;
|
||||
f.name = dp->d_name;
|
||||
output.push_back(f);
|
||||
} else if (dp->d_type == DT_DIR) {
|
||||
// Construct new path from our base path
|
||||
std::string dir_name = dp->d_name;
|
||||
|
||||
path = base_path;
|
||||
path += "/";
|
||||
path += dir_name;
|
||||
|
||||
std::vector<struct File_Data> lower = get_filenames_recursive(path);
|
||||
std::transform(lower.begin(), lower.end(), std::back_inserter(output), [&dir_name](File_Data f) {f.name = dir_name + "/" + f.name; return f;});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
std::string Local_Storage::get_program_path()
|
||||
{
|
||||
return get_full_program_path();
|
||||
}
|
||||
|
||||
std::string Local_Storage::get_game_settings_path()
|
||||
{
|
||||
return get_program_path().append(game_settings_folder).append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
std::string Local_Storage::get_user_appdata_path()
|
||||
{
|
||||
std::string user_appdata_path = "SAVE";
|
||||
#if defined(STEAM_WIN32)
|
||||
WCHAR szPath[MAX_PATH] = {};
|
||||
|
||||
HRESULT hr = SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, szPath);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
user_appdata_path = utf8_encode(szPath);
|
||||
}
|
||||
|
||||
#else
|
||||
/* $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
|
||||
If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. */
|
||||
char *datadir = getenv("XDG_DATA_HOME");
|
||||
if (datadir) {
|
||||
user_appdata_path = datadir;
|
||||
} else {
|
||||
char *homedir = getenv("HOME");
|
||||
if (homedir) {
|
||||
user_appdata_path = std::string(homedir) + "/.local/share";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return user_appdata_path.append(PATH_SEPARATOR).append(PROGRAM_NAME_1).append(PROGRAM_NAME_2).append(PROGRAM_NAME_3).append(PROGRAM_NAME_4).append(PROGRAM_NAME_5).append(PROGRAM_NAME_6).append(PROGRAM_NAME_7).append(PROGRAM_NAME_8).append(" Saves");
|
||||
}
|
||||
|
||||
static std::string replace_with(std::string s, std::string const &old, const char *new_str)
|
||||
{
|
||||
int pos;
|
||||
while ((pos = s.find(old)) != std::string::npos)
|
||||
s.replace(pos, old.length(), new_str);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::string sanitize_file_name(std::string name)
|
||||
{
|
||||
//I'm not sure all of these are necessary but just to be sure
|
||||
if (name[0] == '.' && name.size() > 2 && (name[1] == '\\' || name[1] == '/')) name.erase(0, 2);
|
||||
|
||||
#if defined(STEAM_WIN32)
|
||||
name = replace_with(name, "/", PATH_SEPARATOR);
|
||||
#else
|
||||
//On linux does using "\\" in a remote storage file name create a directory?
|
||||
//I didn't test but I'm going to say yes
|
||||
name = replace_with(name, "\\", PATH_SEPARATOR);
|
||||
#endif
|
||||
name = replace_with(name, "|", ".V_SLASH.");
|
||||
name = replace_with(name, ":", ".COLON.");
|
||||
name = replace_with(name, "*", ".ASTERISK.");
|
||||
name = replace_with(name, "\"", ".QUOTE.");
|
||||
name = replace_with(name, "?", ".Q_MARK.");
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static std::string desanitize_file_name(std::string name)
|
||||
{
|
||||
//I'm not sure all of these are necessary but just to be sure
|
||||
name = replace_with(name, ".SLASH.", "/");
|
||||
name = replace_with(name, ".B_SLASH.", "\\");
|
||||
name = replace_with(name, ".F_SLASH.", "/");
|
||||
name = replace_with(name, ".V_SLASH.", "|");
|
||||
name = replace_with(name, ".COLON.", ":");
|
||||
name = replace_with(name, ".ASTERISK.", "*");
|
||||
name = replace_with(name, ".QUOTE.", "\"");
|
||||
name = replace_with(name, ".Q_MARK.", "?");
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
Local_Storage::Local_Storage(std::string save_directory)
|
||||
{
|
||||
this->save_directory = save_directory;
|
||||
|
||||
if (this->save_directory.back() != *PATH_SEPARATOR) {
|
||||
this->save_directory.append(PATH_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
void Local_Storage::setAppId(uint32 appid)
|
||||
{
|
||||
this->appid = std::to_string(appid) + PATH_SEPARATOR;
|
||||
}
|
||||
|
||||
int Local_Storage::store_file_data(std::string folder, std::string file, char *data, unsigned int length)
|
||||
{
|
||||
if (folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
file = sanitize_file_name(file);
|
||||
std::string::size_type pos = file.rfind(PATH_SEPARATOR);
|
||||
|
||||
std::string file_folder;
|
||||
if (pos == 0 || pos == std::string::npos) {
|
||||
file_folder = "";
|
||||
} else {
|
||||
file_folder = file.substr(0,pos);
|
||||
}
|
||||
|
||||
create_directory(folder + file_folder);
|
||||
std::ofstream myfile;
|
||||
myfile.open(utf8_decode(folder + file), std::ios::binary | std::ios::out);
|
||||
if (!myfile.is_open()) return -1;
|
||||
myfile.write(data, length);
|
||||
int position = myfile.tellp();
|
||||
myfile.close();
|
||||
return position;
|
||||
}
|
||||
|
||||
std::string Local_Storage::get_path(std::string folder)
|
||||
{
|
||||
std::string path = save_directory + appid + folder;
|
||||
create_directory(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string Local_Storage::get_global_settings_path()
|
||||
{
|
||||
return save_directory + settings_storage_folder + PATH_SEPARATOR;
|
||||
}
|
||||
|
||||
std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
|
||||
{
|
||||
if (path.back() != *PATH_SEPARATOR) {
|
||||
path.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
std::vector<struct File_Data> filenames = get_filenames(path);
|
||||
std::vector<std::string> output;
|
||||
std::transform(filenames.begin(), filenames.end(), std::back_inserter(output), [](struct File_Data d) { return d.name;});
|
||||
return output;
|
||||
}
|
||||
|
||||
int Local_Storage::store_data(std::string folder, std::string file, char *data, unsigned int length)
|
||||
{
|
||||
if (folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
return store_file_data(save_directory + appid + folder, file, data, length);
|
||||
}
|
||||
|
||||
int Local_Storage::store_data_settings(std::string file, char *data, unsigned int length)
|
||||
{
|
||||
return store_file_data(get_global_settings_path(), file, data, length);
|
||||
}
|
||||
|
||||
int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length, unsigned int offset)
|
||||
{
|
||||
std::ifstream myfile;
|
||||
myfile.open(utf8_decode(full_path), std::ios::binary | std::ios::in);
|
||||
if (!myfile.is_open()) return -1;
|
||||
|
||||
myfile.seekg (offset, std::ios::beg);
|
||||
myfile.read (data, max_length);
|
||||
myfile.close();
|
||||
return myfile.gcount();
|
||||
}
|
||||
|
||||
int Local_Storage::get_data(std::string folder, std::string file, char *data, unsigned int max_length, unsigned int offset)
|
||||
{
|
||||
file = sanitize_file_name(file);
|
||||
if (folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
std::string full_path = save_directory + appid + folder + file;
|
||||
return get_file_data(full_path, data, max_length, offset);
|
||||
}
|
||||
|
||||
int Local_Storage::get_data_settings(std::string file, char *data, unsigned int max_length)
|
||||
{
|
||||
file = sanitize_file_name(file);
|
||||
std::string full_path = get_global_settings_path() + file;
|
||||
return get_file_data(full_path, data, max_length);
|
||||
}
|
||||
|
||||
int Local_Storage::count_files(std::string folder)
|
||||
{
|
||||
if (folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
return get_filenames_recursive(save_directory + appid + folder).size();
|
||||
}
|
||||
|
||||
bool Local_Storage::file_exists(std::string folder, std::string file)
|
||||
{
|
||||
file = sanitize_file_name(file);
|
||||
if (folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
std::string full_path = save_directory + appid + folder + file;
|
||||
return file_exists_(full_path);
|
||||
}
|
||||
|
||||
unsigned int Local_Storage::file_size(std::string folder, std::string file)
|
||||
{
|
||||
file = sanitize_file_name(file);
|
||||
if (folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
std::string full_path = save_directory + appid + folder + file;
|
||||
return file_size_(full_path);
|
||||
}
|
||||
|
||||
bool Local_Storage::file_delete(std::string folder, std::string file)
|
||||
{
|
||||
file = sanitize_file_name(file);
|
||||
if (folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
std::string full_path = save_directory + appid + folder + file;
|
||||
#if defined(STEAM_WIN32)
|
||||
return _wremove(utf8_decode(full_path).c_str()) == 0;
|
||||
#else
|
||||
return remove(full_path.c_str()) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t Local_Storage::file_timestamp(std::string folder, std::string file)
|
||||
{
|
||||
file = sanitize_file_name(file);
|
||||
if (folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
std::string full_path = save_directory + appid + folder + file;
|
||||
|
||||
#if defined(STEAM_WIN32)
|
||||
struct _stat buffer = {};
|
||||
if (_wstat(utf8_decode(full_path).c_str(), &buffer) != 0) return 0;
|
||||
#else
|
||||
struct stat buffer = {};
|
||||
if (stat (full_path.c_str(), &buffer) != 0) return 0;
|
||||
#endif
|
||||
return buffer.st_mtime;
|
||||
}
|
||||
|
||||
bool Local_Storage::iterate_file(std::string folder, int index, char *output_filename, int32 *output_size)
|
||||
{
|
||||
if (folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
std::vector<struct File_Data> files = get_filenames_recursive(save_directory + appid + folder);
|
||||
if (index < 0 || index >= files.size()) return false;
|
||||
|
||||
std::string name = desanitize_file_name(files[index].name);
|
||||
if (output_size) *output_size = file_size(folder, name);
|
||||
#if defined(STEAM_WIN32)
|
||||
name = replace_with(name, PATH_SEPARATOR, "/");
|
||||
#endif
|
||||
strcpy(output_filename, name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Local_Storage::update_save_filenames(std::string folder)
|
||||
{
|
||||
std::vector<struct File_Data> files = get_filenames_recursive(save_directory + appid + folder);
|
||||
|
||||
for (auto &f : files) {
|
||||
std::string path = f.name;
|
||||
PRINT_DEBUG("Local_Storage:: remote file %s\n", path.c_str());
|
||||
std::string to = sanitize_file_name(desanitize_file_name(path));
|
||||
if (path != to && !file_exists(folder, to)) {
|
||||
//create the folder
|
||||
store_data(folder, to, (char *)"", 0);
|
||||
file_delete(folder, to);
|
||||
|
||||
std::string from = (save_directory + appid + folder + PATH_SEPARATOR + path);
|
||||
to = (save_directory + appid + folder + PATH_SEPARATOR + to);
|
||||
PRINT_DEBUG("Local_Storage::update_save_filenames renaming %s to %s\n", from.c_str(), to.c_str());
|
||||
if (std::rename(from.c_str(), to.c_str()) < 0) {
|
||||
PRINT_DEBUG("ERROR RENAMING\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Local_Storage::load_json(std::string full_path, nlohmann::json& json)
|
||||
{
|
||||
std::ifstream inventory_file(utf8_decode(full_path));
|
||||
// If there is a file and we opened it
|
||||
if (inventory_file)
|
||||
{
|
||||
inventory_file.seekg(0, std::ios::end);
|
||||
size_t size = inventory_file.tellg();
|
||||
std::string buffer(size, '\0');
|
||||
inventory_file.seekg(0);
|
||||
// Read it entirely, if the .json file gets too big,
|
||||
// I should look into this and split reads into smaller parts.
|
||||
inventory_file.read(&buffer[0], size);
|
||||
inventory_file.close();
|
||||
|
||||
try {
|
||||
json = std::move(nlohmann::json::parse(buffer));
|
||||
PRINT_DEBUG("Loaded json \"%s\". Loaded %u items.\n", full_path.c_str(), json.size());
|
||||
return true;
|
||||
} catch (std::exception& e) {
|
||||
PRINT_DEBUG("Error while parsing \"%s\" json: %s\n", full_path.c_str(), e.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PRINT_DEBUG("Couldn't open file \"%s\" to read json\n", full_path.c_str());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json)
|
||||
{
|
||||
if (!folder.empty() && folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
std::string inv_path = std::move(save_directory + appid + folder);
|
||||
std::string full_path = inv_path + file;
|
||||
|
||||
return load_json(full_path, json);
|
||||
}
|
||||
|
||||
bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json)
|
||||
{
|
||||
if (!folder.empty() && folder.back() != *PATH_SEPARATOR) {
|
||||
folder.append(PATH_SEPARATOR);
|
||||
}
|
||||
std::string inv_path = std::move(save_directory + appid + folder);
|
||||
std::string full_path = inv_path + file;
|
||||
|
||||
create_directory(inv_path);
|
||||
|
||||
std::ofstream inventory_file(utf8_decode(full_path), std::ios::trunc | std::ios::out);
|
||||
if (inventory_file)
|
||||
{
|
||||
inventory_file << std::setw(2) << json;
|
||||
return true;
|
||||
}
|
||||
|
||||
PRINT_DEBUG("Couldn't open file \"%s\" to write json\n", full_path.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<image_pixel_t> Local_Storage::load_image(std::string const& image_path)
|
||||
{
|
||||
std::vector<image_pixel_t> res;
|
||||
int width, height;
|
||||
image_pixel_t* img = (image_pixel_t*)stbi_load(image_path.c_str(), &width, &height, nullptr, 4);
|
||||
if (img != nullptr)
|
||||
{
|
||||
res.resize(width*height);
|
||||
std::copy(img, img + width * height, res.begin());
|
||||
|
||||
stbi_image_free(img);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Local_Storage::save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels)
|
||||
{
|
||||
std::string screenshot_path = std::move(save_directory + appid + screenshots_folder + PATH_SEPARATOR);
|
||||
create_directory(screenshot_path);
|
||||
screenshot_path += image_path;
|
||||
return stbi_write_png(screenshot_path.c_str(), width, height, channels, img_ptr, 0) == 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
90
all/local_storage.h
Normal file
90
all/local_storage.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef LOCAL_STORAGE_INCLUDE
|
||||
#define LOCAL_STORAGE_INCLUDE
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define MAX_FILENAME_LENGTH 300
|
||||
|
||||
union image_pixel_t
|
||||
{
|
||||
uint32_t pixel;
|
||||
struct pixel_channels_t
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
} channels;
|
||||
};
|
||||
|
||||
struct image_t
|
||||
{
|
||||
size_t width;
|
||||
size_t height;
|
||||
std::vector<image_pixel_t> pix_map;
|
||||
};
|
||||
|
||||
class Local_Storage {
|
||||
public:
|
||||
static constexpr auto inventory_storage_folder = "inventory";
|
||||
static constexpr auto settings_storage_folder = "settings";
|
||||
static constexpr auto remote_storage_folder = "remote";
|
||||
static constexpr auto stats_storage_folder = "stats";
|
||||
static constexpr auto user_data_storage = "local";
|
||||
static constexpr auto screenshots_folder = "screenshots";
|
||||
static constexpr auto game_settings_folder = "steam_settings";
|
||||
|
||||
private:
|
||||
std::string save_directory;
|
||||
std::string appid;
|
||||
public:
|
||||
static std::string get_program_path();
|
||||
static std::string get_game_settings_path();
|
||||
static std::string get_user_appdata_path();
|
||||
Local_Storage(std::string save_directory);
|
||||
static int get_file_data(std::string full_path, char *data, unsigned int max_length, unsigned int offset=0);
|
||||
void setAppId(uint32 appid);
|
||||
static int store_file_data(std::string folder, std::string file, char *data, unsigned int length);
|
||||
static std::vector<std::string> get_filenames_path(std::string path);
|
||||
|
||||
int store_data(std::string folder, std::string file, char *data, unsigned int length);
|
||||
int store_data_settings(std::string file, char *data, unsigned int length);
|
||||
int get_data(std::string folder, std::string file, char *data, unsigned int max_length, unsigned int offset=0);
|
||||
int get_data_settings(std::string file, char *data, unsigned int max_length);
|
||||
int count_files(std::string folder);
|
||||
bool iterate_file(std::string folder, int index, char *output_filename, int32 *output_size);
|
||||
bool file_exists(std::string folder, std::string file);
|
||||
unsigned int file_size(std::string folder, std::string file);
|
||||
bool file_delete(std::string folder, std::string file);
|
||||
uint64_t file_timestamp(std::string folder, std::string file);
|
||||
std::string get_global_settings_path();
|
||||
std::string get_path(std::string folder);
|
||||
|
||||
bool update_save_filenames(std::string folder);
|
||||
|
||||
bool load_json(std::string full_path, nlohmann::json& json);
|
||||
bool load_json_file(std::string folder, std::string const& file, nlohmann::json& json);
|
||||
bool write_json_file(std::string folder, std::string const& file, nlohmann::json const& json);
|
||||
|
||||
std::vector<image_pixel_t> load_image(std::string const& image_path);
|
||||
bool save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels);
|
||||
};
|
||||
|
||||
#endif
|
||||
237
all/net.proto
Normal file
237
all/net.proto
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
syntax = "proto3";
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message Announce {
|
||||
enum Types {
|
||||
PING = 0;
|
||||
PONG = 1;
|
||||
}
|
||||
|
||||
Types type = 1;
|
||||
repeated uint64 ids = 2;
|
||||
|
||||
message Other_Peers {
|
||||
uint64 id = 1;
|
||||
uint32 ip = 2;
|
||||
uint32 udp_port = 3;
|
||||
uint32 appid = 4;
|
||||
}
|
||||
|
||||
uint32 tcp_port = 3;
|
||||
repeated Other_Peers peers = 4;
|
||||
uint32 appid = 5;
|
||||
}
|
||||
|
||||
message Lobby {
|
||||
uint64 room_id = 1;
|
||||
uint64 owner = 2;
|
||||
|
||||
map<string, bytes> values = 3;
|
||||
|
||||
message Member {
|
||||
uint64 id = 1;
|
||||
map<string, bytes> values = 2;
|
||||
}
|
||||
|
||||
repeated Member members = 4;
|
||||
|
||||
message Gameserver {
|
||||
uint64 id = 1;
|
||||
uint32 ip = 2;
|
||||
uint32 port = 3;
|
||||
uint32 num_update = 4;
|
||||
}
|
||||
|
||||
Gameserver gameserver = 5;
|
||||
|
||||
uint32 member_limit = 6;
|
||||
uint32 type = 7; //ELobbyType
|
||||
bool joinable = 8;
|
||||
uint32 appid = 9;
|
||||
bool deleted = 32;
|
||||
uint64 time_deleted = 33;
|
||||
}
|
||||
|
||||
message Lobby_Messages {
|
||||
uint64 id = 1;
|
||||
|
||||
enum Types {
|
||||
JOIN = 0;
|
||||
LEAVE = 1;
|
||||
CHANGE_OWNER = 2;
|
||||
MEMBER_DATA = 3;
|
||||
CHAT_MESSAGE = 4;
|
||||
}
|
||||
|
||||
Types type = 2;
|
||||
uint64 idata = 3;
|
||||
bytes bdata = 4;
|
||||
map<string, bytes> map = 5;
|
||||
}
|
||||
|
||||
message Low_Level {
|
||||
enum Types {
|
||||
HEARTBEAT = 0;
|
||||
CONNECT = 1;
|
||||
DISCONNECT = 2;
|
||||
}
|
||||
|
||||
Types type = 1;
|
||||
}
|
||||
|
||||
message Network_pb {
|
||||
uint32 channel = 1;
|
||||
bytes data = 2;
|
||||
|
||||
enum Types {
|
||||
DATA = 0;
|
||||
NEW_CONNECTION = 1;
|
||||
}
|
||||
|
||||
Types type = 3;
|
||||
|
||||
bool processed = 128;
|
||||
uint64 time_processed = 129;
|
||||
}
|
||||
|
||||
message Network_Old {
|
||||
enum Types {
|
||||
CONNECTION_REQUEST_IP = 0;
|
||||
CONNECTION_REQUEST_STEAMID = 1;
|
||||
CONNECTION_ACCEPTED = 2;
|
||||
CONNECTION_END = 3;
|
||||
DATA = 4;
|
||||
}
|
||||
|
||||
Types type = 1;
|
||||
uint64 connection_id = 2;
|
||||
uint64 connection_id_from = 3;
|
||||
uint32 port = 4;
|
||||
bytes data = 5;
|
||||
}
|
||||
|
||||
message Networking_Sockets {
|
||||
enum Types {
|
||||
CONNECTION_REQUEST = 0;
|
||||
CONNECTION_ACCEPTED = 2;
|
||||
CONNECTION_END = 3;
|
||||
DATA = 4;
|
||||
}
|
||||
|
||||
Types type = 1;
|
||||
int32 virtual_port = 2;
|
||||
int32 real_port = 6;
|
||||
uint64 connection_id = 3;
|
||||
uint64 connection_id_from = 4;
|
||||
bytes data = 5;
|
||||
uint64 message_number = 7;
|
||||
}
|
||||
|
||||
message Networking_Messages {
|
||||
enum Types {
|
||||
CONNECTION_NEW = 0;
|
||||
CONNECTION_ACCEPT = 1;
|
||||
CONNECTION_END = 2;
|
||||
DATA = 3;
|
||||
}
|
||||
|
||||
Types type = 1;
|
||||
uint32 channel = 2;
|
||||
uint32 id_from = 3;
|
||||
bytes data = 5;
|
||||
}
|
||||
|
||||
message Gameserver {
|
||||
uint64 id = 1;
|
||||
bytes game_description = 2;
|
||||
bytes mod_dir = 3;
|
||||
bool dedicated_server = 4;
|
||||
uint32 max_player_count = 5;
|
||||
uint32 bot_player_count = 6;
|
||||
bytes server_name = 7;
|
||||
bytes map_name = 8;
|
||||
bool password_protected = 9;
|
||||
uint32 spectator_port = 10;
|
||||
bytes spectator_server_name = 11;
|
||||
map<string, bytes> values = 12;
|
||||
bytes tags = 13;
|
||||
bytes gamedata = 14;
|
||||
bytes region = 15;
|
||||
bytes product = 16;
|
||||
bool secure = 17;
|
||||
uint32 num_players = 18;
|
||||
uint32 version = 19;
|
||||
|
||||
uint32 ip = 32;
|
||||
uint32 port = 33;
|
||||
uint32 query_port = 34;
|
||||
uint32 appid = 35;
|
||||
|
||||
bool offline = 48;
|
||||
}
|
||||
|
||||
message Friend {
|
||||
uint64 id = 1;
|
||||
bytes name = 2;
|
||||
map<string, bytes> rich_presence = 3;
|
||||
uint32 appid = 4;
|
||||
uint64 lobby_id = 5;
|
||||
}
|
||||
|
||||
message Auth_Ticket {
|
||||
uint32 number = 1;
|
||||
|
||||
enum Types {
|
||||
CANCEL = 0;
|
||||
}
|
||||
|
||||
Types type = 2;
|
||||
}
|
||||
|
||||
message Friend_Messages {
|
||||
enum Types {
|
||||
LOBBY_INVITE = 0;
|
||||
GAME_INVITE = 1;
|
||||
}
|
||||
|
||||
Types type = 1;
|
||||
oneof invite_data {
|
||||
uint64 lobby_id = 2;
|
||||
bytes connect_str = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message Steam_Messages {
|
||||
enum Types {
|
||||
FRIEND_CHAT = 0;
|
||||
}
|
||||
|
||||
Types type = 1;
|
||||
oneof message_data {
|
||||
bytes message = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message Common_Message {
|
||||
uint64 source_id = 1;
|
||||
uint64 dest_id = 2;
|
||||
oneof messages {
|
||||
Announce announce = 3;
|
||||
Low_Level low_level = 4;
|
||||
Lobby lobby = 5;
|
||||
Lobby_Messages lobby_messages = 6;
|
||||
Network_pb network = 7;
|
||||
Gameserver gameserver = 8;
|
||||
Friend friend = 9;
|
||||
Auth_Ticket auth_ticket = 10;
|
||||
Friend_Messages friend_messages = 11;
|
||||
Network_Old network_old = 12;
|
||||
Networking_Sockets networking_sockets = 13;
|
||||
Steam_Messages steam_messages = 14;
|
||||
Networking_Messages networking_messages = 15;
|
||||
}
|
||||
|
||||
uint32 source_ip = 128;
|
||||
uint32 source_port = 129;
|
||||
}
|
||||
1277
all/network.cpp
Normal file
1277
all/network.cpp
Normal file
File diff suppressed because it is too large
Load diff
145
all/network.h
Normal file
145
all/network.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef NETWORK_INCLUDE
|
||||
#define NETWORK_INCLUDE
|
||||
|
||||
#include "base.h"
|
||||
|
||||
inline bool protobuf_message_equal(const google::protobuf::MessageLite& msg_a,
|
||||
const google::protobuf::MessageLite& msg_b) {
|
||||
return (msg_a.GetTypeName() == msg_b.GetTypeName()) &&
|
||||
(msg_a.SerializeAsString() == msg_b.SerializeAsString());
|
||||
}
|
||||
|
||||
|
||||
#define DEFAULT_PORT 47584
|
||||
|
||||
#if defined(STEAM_WIN32)
|
||||
typedef unsigned int sock_t;
|
||||
#else
|
||||
typedef int sock_t;
|
||||
#endif
|
||||
|
||||
struct IP_PORT {
|
||||
uint32 ip;
|
||||
uint16 port;
|
||||
bool operator <(const IP_PORT& other) const
|
||||
{
|
||||
return (ip < other.ip) || (ip == other.ip && port < other.port);
|
||||
}
|
||||
};
|
||||
|
||||
struct Network_Callback {
|
||||
void (*message_callback)(void *object, Common_Message *msg);
|
||||
void *object;
|
||||
CSteamID steam_id;
|
||||
};
|
||||
|
||||
enum Callback_Ids {
|
||||
CALLBACK_ID_USER_STATUS,
|
||||
CALLBACK_ID_LOBBY,
|
||||
CALLBACK_ID_NETWORKING,
|
||||
CALLBACK_ID_GAMESERVER,
|
||||
CALLBACK_ID_FRIEND,
|
||||
CALLBACK_ID_AUTH_TICKET,
|
||||
CALLBACK_ID_FRIEND_MESSAGES,
|
||||
CALLBACK_ID_NETWORKING_SOCKETS,
|
||||
CALLBACK_ID_STEAM_MESSAGES,
|
||||
CALLBACK_ID_NETWORKING_MESSAGES,
|
||||
|
||||
CALLBACK_IDS_MAX
|
||||
};
|
||||
|
||||
struct Network_Callback_Container {
|
||||
std::vector<struct Network_Callback> callbacks;
|
||||
};
|
||||
|
||||
struct TCP_Socket {
|
||||
sock_t sock = ~0;
|
||||
bool received_data = false;
|
||||
std::vector<char> recv_buffer;
|
||||
std::vector<char> send_buffer;
|
||||
std::chrono::high_resolution_clock::time_point last_heartbeat_sent, last_heartbeat_received;
|
||||
};
|
||||
|
||||
struct Connection {
|
||||
struct TCP_Socket tcp_socket_outgoing, tcp_socket_incoming;
|
||||
bool connected = false;
|
||||
IP_PORT udp_ip_port;
|
||||
bool udp_pinged = false;
|
||||
IP_PORT tcp_ip_port;
|
||||
std::vector<CSteamID> ids;
|
||||
uint32 appid;
|
||||
std::chrono::high_resolution_clock::time_point last_received;
|
||||
};
|
||||
|
||||
class Networking {
|
||||
bool enabled = false;
|
||||
bool alive;
|
||||
std::chrono::high_resolution_clock::time_point last_run;
|
||||
sock_t udp_socket, tcp_socket;
|
||||
uint16 udp_port, tcp_port;
|
||||
uint32 own_ip;
|
||||
std::vector<struct Connection> connections;
|
||||
struct Connection *find_connection(CSteamID id, uint32 appid = 0);
|
||||
struct Connection *new_connection(CSteamID id, uint32 appid);
|
||||
|
||||
bool handle_announce(Common_Message *msg, IP_PORT ip_port);
|
||||
bool handle_low_level_udp(Common_Message *msg, IP_PORT ip_port);
|
||||
bool handle_tcp(Common_Message *msg, struct TCP_Socket &socket);
|
||||
void send_announce_broadcasts();
|
||||
|
||||
std::vector<CSteamID> ids;
|
||||
uint32 appid;
|
||||
std::chrono::high_resolution_clock::time_point last_broadcast;
|
||||
std::vector<IP_PORT> custom_broadcasts;
|
||||
|
||||
std::vector<struct TCP_Socket> accepted;
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
struct Network_Callback_Container callbacks[CALLBACK_IDS_MAX];
|
||||
std::vector<Common_Message> local_send;
|
||||
|
||||
bool add_id_connection(struct Connection *connection, CSteamID steam_id);
|
||||
void run_callbacks(Callback_Ids id, Common_Message *msg);
|
||||
void run_callback_user(CSteamID steam_id, bool online, uint32 appid);
|
||||
void do_callbacks_message(Common_Message *msg);
|
||||
|
||||
Common_Message create_announce(bool request);
|
||||
public:
|
||||
//NOTE: for all functions ips/ports are passed/returned in host byte order
|
||||
//ex: 127.0.0.1 should be passed as 0x7F000001
|
||||
static std::set<IP_PORT> resolve_ip(std::string dns);
|
||||
Networking(CSteamID id, uint32 appid, uint16 port, std::set<IP_PORT> *custom_broadcasts, bool disable_sockets);
|
||||
void addListenId(CSteamID id);
|
||||
void setAppID(uint32 appid);
|
||||
void Run();
|
||||
bool sendTo(Common_Message *msg, bool reliable, Connection *conn = NULL);
|
||||
bool sendToAllIndividuals(Common_Message *msg, bool reliable);
|
||||
bool sendToAll(Common_Message *msg, bool reliable);
|
||||
bool sendToIPPort(Common_Message *msg, uint32 ip, uint16 port, bool reliable);
|
||||
|
||||
bool setCallback(Callback_Ids id, CSteamID steam_id, void (*message_callback)(void *object, Common_Message *msg), void *object);
|
||||
uint32 getIP(CSteamID id);
|
||||
uint32 getOwnIP();
|
||||
|
||||
void shutDown();
|
||||
bool isAlive();
|
||||
};
|
||||
|
||||
#endif
|
||||
4
all/rtlgenrandom.c
Normal file
4
all/rtlgenrandom.c
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#include <windows.h>
|
||||
#define RtlGenRandom SystemFunction036
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
DLLEXPORT BOOLEAN WINAPI RtlGenRandom(PVOID in, ULONG len) {}
|
||||
3
all/rtlgenrandom.def
Normal file
3
all/rtlgenrandom.def
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
LIBRARY advapi32.dll
|
||||
EXPORTS
|
||||
SystemFunction036
|
||||
226
all/settings.cpp
Normal file
226
all/settings.cpp
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
|
||||
std::string Settings::sanitize(std::string name)
|
||||
{
|
||||
name.erase(std::remove(name.begin(), name.end(), '\n'), name.end());
|
||||
name.erase(std::remove(name.begin(), name.end(), '\r'), name.end());
|
||||
|
||||
for (auto& i : name)
|
||||
{
|
||||
if (!isprint(i))
|
||||
i = ' ';
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
Settings::Settings(CSteamID steam_id, CGameID game_id, std::string name, std::string language, bool offline)
|
||||
{
|
||||
this->steam_id = steam_id;
|
||||
this->game_id = game_id;
|
||||
this->name = sanitize(name);
|
||||
if (this->name.size() == 0) {
|
||||
this->name = " ";
|
||||
}
|
||||
|
||||
if (this->name.size() == 1) {
|
||||
this->name = this->name + " ";
|
||||
}
|
||||
|
||||
auto lang = sanitize(language);
|
||||
std::transform(lang.begin(), lang.end(), lang.begin(), ::tolower);
|
||||
lang.erase(std::remove(lang.begin(), lang.end(), ' '), lang.end());
|
||||
this->language = lang;
|
||||
this->lobby_id = k_steamIDNil;
|
||||
this->unlockAllDLCs = true;
|
||||
|
||||
this->offline = offline;
|
||||
this->create_unknown_leaderboards = true;
|
||||
}
|
||||
|
||||
CSteamID Settings::get_local_steam_id()
|
||||
{
|
||||
return steam_id;
|
||||
}
|
||||
|
||||
CGameID Settings::get_local_game_id()
|
||||
{
|
||||
return game_id;
|
||||
}
|
||||
|
||||
const char *Settings::get_local_name()
|
||||
{
|
||||
return name.c_str();
|
||||
}
|
||||
|
||||
const char *Settings::get_language()
|
||||
{
|
||||
return language.c_str();
|
||||
}
|
||||
|
||||
void Settings::set_local_name(char *name)
|
||||
{
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
void Settings::set_language(char *language)
|
||||
{
|
||||
this->language = language;
|
||||
}
|
||||
|
||||
void Settings::set_game_id(CGameID game_id)
|
||||
{
|
||||
this->game_id = game_id;
|
||||
}
|
||||
|
||||
void Settings::set_lobby(CSteamID lobby_id)
|
||||
{
|
||||
this->lobby_id = lobby_id;
|
||||
}
|
||||
|
||||
CSteamID Settings::get_lobby()
|
||||
{
|
||||
return this->lobby_id;
|
||||
}
|
||||
|
||||
void Settings::unlockAllDLC(bool value)
|
||||
{
|
||||
this->unlockAllDLCs = value;
|
||||
}
|
||||
|
||||
void Settings::addDLC(AppId_t appID, std::string name, bool available)
|
||||
{
|
||||
auto f = std::find_if(DLCs.begin(), DLCs.end(), [&appID](DLC_entry const& item) { return item.appID == appID; });
|
||||
if (DLCs.end() != f) {
|
||||
f->name = name;
|
||||
f->available = available;
|
||||
return;
|
||||
}
|
||||
|
||||
DLC_entry new_entry;
|
||||
new_entry.appID = appID;
|
||||
new_entry.name = name;
|
||||
new_entry.available = available;
|
||||
DLCs.push_back(new_entry);
|
||||
}
|
||||
|
||||
void Settings::addMod(PublishedFileId_t id, std::string title, std::string path)
|
||||
{
|
||||
auto f = std::find_if(mods.begin(), mods.end(), [&id](Mod_entry const& item) { return item.id == id; });
|
||||
if (mods.end() != f) {
|
||||
f->title = title;
|
||||
f->path = path;
|
||||
return;
|
||||
}
|
||||
|
||||
Mod_entry new_entry;
|
||||
new_entry.id = id;
|
||||
new_entry.title = title;
|
||||
new_entry.path = path;
|
||||
mods.push_back(new_entry);
|
||||
}
|
||||
|
||||
Mod_entry Settings::getMod(PublishedFileId_t id)
|
||||
{
|
||||
auto f = std::find_if(mods.begin(), mods.end(), [&id](Mod_entry const& item) { return item.id == id; });
|
||||
if (mods.end() != f) {
|
||||
return *f;
|
||||
}
|
||||
|
||||
return Mod_entry();
|
||||
}
|
||||
|
||||
bool Settings::isModInstalled(PublishedFileId_t id)
|
||||
{
|
||||
auto f = std::find_if(mods.begin(), mods.end(), [&id](Mod_entry const& item) { return item.id == id; });
|
||||
if (mods.end() != f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::set<PublishedFileId_t> Settings::modSet()
|
||||
{
|
||||
std::set<PublishedFileId_t> ret_set;
|
||||
|
||||
for (auto & m: mods) {
|
||||
ret_set.insert(m.id);
|
||||
}
|
||||
|
||||
return ret_set;
|
||||
}
|
||||
|
||||
unsigned int Settings::DLCCount()
|
||||
{
|
||||
return this->DLCs.size();
|
||||
}
|
||||
|
||||
bool Settings::hasDLC(AppId_t appID)
|
||||
{
|
||||
if (this->unlockAllDLCs) return true;
|
||||
|
||||
auto f = std::find_if(DLCs.begin(), DLCs.end(), [&appID](DLC_entry const& item) { return item.appID == appID; });
|
||||
if (DLCs.end() == f)
|
||||
return false;
|
||||
|
||||
return f->available;
|
||||
}
|
||||
|
||||
bool Settings::getDLC(unsigned int index, AppId_t &appID, bool &available, std::string &name)
|
||||
{
|
||||
if (index >= DLCs.size()) return false;
|
||||
|
||||
appID = DLCs[index].appID;
|
||||
available = DLCs[index].available;
|
||||
name = DLCs[index].name;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Settings::setAppInstallPath(AppId_t appID, std::string path)
|
||||
{
|
||||
app_paths[appID] = path;
|
||||
}
|
||||
|
||||
std::string Settings::getAppInstallPath(AppId_t appID)
|
||||
{
|
||||
return app_paths[appID];
|
||||
}
|
||||
|
||||
void Settings::setLeaderboard(std::string leaderboard, enum ELeaderboardSortMethod sort_method, enum ELeaderboardDisplayType display_type)
|
||||
{
|
||||
Leaderboard_config leader;
|
||||
leader.sort_method = sort_method;
|
||||
leader.display_type = display_type;
|
||||
|
||||
leaderboards[leaderboard] = leader;
|
||||
}
|
||||
|
||||
int Settings::add_image(std::string data, uint32 width, uint32 height)
|
||||
{
|
||||
int last = images.size() + 1;
|
||||
struct Image_Data dt;
|
||||
dt.width = width;
|
||||
dt.height = height;
|
||||
dt.data = data;
|
||||
images[last] = dt;
|
||||
return last;
|
||||
}
|
||||
169
all/settings.h
Normal file
169
all/settings.h
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef SETTINGS_INCLUDE
|
||||
#define SETTINGS_INCLUDE
|
||||
|
||||
#include "base.h"
|
||||
|
||||
struct IP_PORT;
|
||||
|
||||
struct DLC_entry {
|
||||
AppId_t appID;
|
||||
std::string name;
|
||||
bool available;
|
||||
};
|
||||
|
||||
struct Mod_entry {
|
||||
PublishedFileId_t id;
|
||||
std::string title;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
struct Leaderboard_config {
|
||||
enum ELeaderboardSortMethod sort_method;
|
||||
enum ELeaderboardDisplayType display_type;
|
||||
};
|
||||
|
||||
enum Stat_Type {
|
||||
STAT_TYPE_INT,
|
||||
STAT_TYPE_FLOAT,
|
||||
STAT_TYPE_AVGRATE
|
||||
};
|
||||
|
||||
struct Stat_config {
|
||||
enum Stat_Type type;
|
||||
union {
|
||||
float default_value_float;
|
||||
uint32 default_value_int;
|
||||
};
|
||||
};
|
||||
|
||||
struct Image_Data {
|
||||
uint32 width;
|
||||
uint32 height;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
struct Controller_Settings {
|
||||
std::map<std::string, std::map<std::string, std::pair<std::set<std::string>, std::string>>> action_sets;
|
||||
std::map<std::string, std::string> action_set_layer_parents;
|
||||
std::map<std::string, std::map<std::string, std::pair<std::set<std::string>, std::string>>> action_set_layers;
|
||||
};
|
||||
|
||||
class Settings {
|
||||
CSteamID steam_id;
|
||||
CGameID game_id;
|
||||
std::string name, language;
|
||||
CSteamID lobby_id;
|
||||
|
||||
bool unlockAllDLCs;
|
||||
bool offline;
|
||||
std::vector<struct DLC_entry> DLCs;
|
||||
std::vector<struct Mod_entry> mods;
|
||||
std::map<AppId_t, std::string> app_paths;
|
||||
std::map<std::string, Leaderboard_config> leaderboards;
|
||||
std::map<std::string, Stat_config> stats;
|
||||
bool create_unknown_leaderboards;
|
||||
uint16 port;
|
||||
|
||||
public:
|
||||
#ifdef LOBBY_CONNECT
|
||||
static const bool is_lobby_connect = true;
|
||||
#else
|
||||
static const bool is_lobby_connect = false;
|
||||
#endif
|
||||
static std::string sanitize(std::string name);
|
||||
Settings(CSteamID steam_id, CGameID game_id, std::string name, std::string language, bool offline);
|
||||
CSteamID get_local_steam_id();
|
||||
CGameID get_local_game_id();
|
||||
const char *get_local_name();
|
||||
void set_local_name(char *name);
|
||||
const char *get_language();
|
||||
void set_language(char *language);
|
||||
|
||||
void set_game_id(CGameID game_id);
|
||||
void set_lobby(CSteamID lobby_id);
|
||||
CSteamID get_lobby();
|
||||
bool is_offline() {return offline; }
|
||||
uint16 get_port() {return port;}
|
||||
void set_port(uint16 port) { this->port = port;}
|
||||
|
||||
//DLC stuff
|
||||
void unlockAllDLC(bool value);
|
||||
void addDLC(AppId_t appID, std::string name, bool available);
|
||||
unsigned int DLCCount();
|
||||
bool hasDLC(AppId_t appID);
|
||||
bool getDLC(unsigned int index, AppId_t &appID, bool &available, std::string &name);
|
||||
|
||||
//Depots
|
||||
std::vector<DepotId_t> depots;
|
||||
|
||||
//App Install paths
|
||||
void setAppInstallPath(AppId_t appID, std::string path);
|
||||
std::string getAppInstallPath(AppId_t appID);
|
||||
|
||||
//mod stuff
|
||||
void addMod(PublishedFileId_t id, std::string title, std::string path);
|
||||
Mod_entry getMod(PublishedFileId_t id);
|
||||
bool isModInstalled(PublishedFileId_t id);
|
||||
std::set<PublishedFileId_t> modSet();
|
||||
|
||||
//leaderboards
|
||||
void setLeaderboard(std::string leaderboard, enum ELeaderboardSortMethod sort_method, enum ELeaderboardDisplayType display_type);
|
||||
std::map<std::string, Leaderboard_config> getLeaderboards() { return leaderboards; }
|
||||
void setCreateUnknownLeaderboards(bool enable) {create_unknown_leaderboards = enable;}
|
||||
bool createUnknownLeaderboards() { return create_unknown_leaderboards; }
|
||||
|
||||
//custom broadcasts
|
||||
std::set<IP_PORT> custom_broadcasts;
|
||||
|
||||
//stats
|
||||
std::map<std::string, Stat_config> getStats() { return stats; }
|
||||
void setStatDefiniton(std::string name, struct Stat_config stat_config) {stats[ascii_to_lowercase(name)] = stat_config; }
|
||||
|
||||
//subscribed lobby/group ids
|
||||
std::set<uint64> subscribed_groups;
|
||||
|
||||
//images
|
||||
std::map<int, struct Image_Data> images;
|
||||
int add_image(std::string data, uint32 width, uint32 height);
|
||||
|
||||
//controller
|
||||
struct Controller_Settings controller_settings;
|
||||
std::string glyphs_directory;
|
||||
|
||||
//networking
|
||||
bool disable_networking = false;
|
||||
|
||||
//overlay
|
||||
bool disable_overlay = false;
|
||||
|
||||
//app build id
|
||||
int build_id = 10;
|
||||
|
||||
//make lobby creation fail in the matchmaking interface
|
||||
bool disable_lobby_creation = false;
|
||||
|
||||
//warn people who use force_ settings
|
||||
bool warn_forced = false;
|
||||
|
||||
//warn people who use local save
|
||||
bool warn_local_save = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
598
all/settings_parser.cpp
Normal file
598
all/settings_parser.cpp
Normal file
|
|
@ -0,0 +1,598 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "settings_parser.h"
|
||||
|
||||
static void consume_bom(std::ifstream &input)
|
||||
{
|
||||
int bom[3];
|
||||
bom[0] = input.get();
|
||||
bom[1] = input.get();
|
||||
bom[2] = input.get();
|
||||
if (bom[0] != 0xEF || bom[1] != 0xBB || bom[2] != 0xBF) {
|
||||
input.seekg(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void load_custom_broadcasts(std::string broadcasts_filepath, std::set<IP_PORT> &custom_broadcasts)
|
||||
{
|
||||
PRINT_DEBUG("Broadcasts file path: %s\n", broadcasts_filepath.c_str());
|
||||
std::ifstream broadcasts_file(utf8_decode(broadcasts_filepath));
|
||||
consume_bom(broadcasts_file);
|
||||
if (broadcasts_file.is_open()) {
|
||||
std::string line;
|
||||
while (std::getline(broadcasts_file, line)) {
|
||||
std::set<IP_PORT> ips = Networking::resolve_ip(line);
|
||||
custom_broadcasts.insert(ips.begin(), ips.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Out>
|
||||
static void split_string(const std::string &s, char delim, Out result) {
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim)) {
|
||||
*(result++) = item;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_gamecontroller_settings(Settings *settings)
|
||||
{
|
||||
std::string path = Local_Storage::get_game_settings_path() + "controller";
|
||||
std::vector<std::string> paths = Local_Storage::get_filenames_path(path);
|
||||
|
||||
for (auto & p: paths) {
|
||||
size_t length = p.length();
|
||||
if (length < 4) continue;
|
||||
if ( std::toupper(p.back()) != 'T') continue;
|
||||
if ( std::toupper(p[length - 2]) != 'X') continue;
|
||||
if ( std::toupper(p[length - 3]) != 'T') continue;
|
||||
if (p[length - 4] != '.') continue;
|
||||
|
||||
PRINT_DEBUG("controller config %s\n", p.c_str());
|
||||
std::string action_set_name = p.substr(0, length - 4);
|
||||
std::transform(action_set_name.begin(), action_set_name.end(), action_set_name.begin(),[](unsigned char c){ return std::toupper(c); });
|
||||
|
||||
std::string controller_config_path = path + PATH_SEPARATOR + p;
|
||||
std::ifstream input( utf8_decode(controller_config_path) );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
std::map<std::string, std::pair<std::set<std::string>, std::string>> button_pairs;
|
||||
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
if (!line.empty() && line[line.length()-1] == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
if (!line.empty() && line[line.length()-1] == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
std::string action_name;
|
||||
std::string button_name;
|
||||
std::string source_mode;
|
||||
|
||||
std::size_t deliminator = line.find("=");
|
||||
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
|
||||
action_name = line.substr(0, deliminator);
|
||||
std::size_t deliminator2 = line.find("=", deliminator + 1);
|
||||
|
||||
if (deliminator2 != std::string::npos && deliminator2 != line.size()) {
|
||||
button_name = line.substr(deliminator + 1, deliminator2 - (deliminator + 1));
|
||||
source_mode = line.substr(deliminator2 + 1);
|
||||
} else {
|
||||
button_name = line.substr(deliminator + 1);
|
||||
source_mode = "";
|
||||
}
|
||||
}
|
||||
|
||||
std::transform(action_name.begin(), action_name.end(), action_name.begin(),[](unsigned char c){ return std::toupper(c); });
|
||||
std::transform(button_name.begin(), button_name.end(), button_name.begin(),[](unsigned char c){ return std::toupper(c); });
|
||||
std::pair<std::set<std::string>, std::string> button_config = {{}, source_mode};
|
||||
split_string(button_name, ',', std::inserter(button_config.first, button_config.first.begin()));
|
||||
button_pairs[action_name] = button_config;
|
||||
PRINT_DEBUG("Added %s %s %s\n", action_name.c_str(), button_name.c_str(), source_mode.c_str());
|
||||
}
|
||||
|
||||
settings->controller_settings.action_sets[action_set_name] = button_pairs;
|
||||
PRINT_DEBUG("Added %u action names to %s\n", button_pairs.size(), action_set_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
settings->glyphs_directory = path + (PATH_SEPARATOR "glyphs" PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out)
|
||||
{
|
||||
std::string program_path = Local_Storage::get_program_path(), save_path = Local_Storage::get_user_appdata_path();;
|
||||
|
||||
PRINT_DEBUG("Current Path %s save_path: %s\n", program_path.c_str(), save_path.c_str());
|
||||
|
||||
char array[10] = {};
|
||||
array[0] = '0';
|
||||
Local_Storage::get_file_data(Local_Storage::get_game_settings_path() + "steam_appid.txt", array, sizeof(array) - 1);
|
||||
uint32 appid = 0;
|
||||
try {
|
||||
appid = std::stoi(array);
|
||||
} catch (...) {}
|
||||
if (!appid) {
|
||||
memset(array, 0, sizeof(array));
|
||||
array[0] = '0';
|
||||
Local_Storage::get_file_data("steam_appid.txt", array, sizeof(array) - 1);
|
||||
try {
|
||||
appid = std::stoi(array);
|
||||
} catch (...) {}
|
||||
if (!appid) {
|
||||
memset(array, 0, sizeof(array));
|
||||
array[0] = '0';
|
||||
Local_Storage::get_file_data(program_path + "steam_appid.txt", array, sizeof(array) - 1);
|
||||
try {
|
||||
appid = std::stoi(array);
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (!appid) {
|
||||
std::string str_appid = get_env_variable("SteamAppId");
|
||||
std::string str_gameid = get_env_variable("SteamGameId");
|
||||
PRINT_DEBUG("str_appid %s str_gameid: %s\n", str_appid.c_str(), str_gameid.c_str());
|
||||
uint32 appid_env = 0;
|
||||
uint32 gameid_env = 0;
|
||||
|
||||
if (str_appid.size() > 0) {
|
||||
try {
|
||||
appid_env = std::stoul(str_appid);
|
||||
} catch (...) {
|
||||
appid_env = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (str_gameid.size() > 0) {
|
||||
try {
|
||||
gameid_env = std::stoul(str_gameid);
|
||||
} catch (...) {
|
||||
gameid_env = 0;
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_DEBUG("appid_env %u gameid_env: %u\n", appid_env, gameid_env);
|
||||
if (appid_env) {
|
||||
appid = appid_env;
|
||||
}
|
||||
|
||||
if (gameid_env) {
|
||||
appid = gameid_env;
|
||||
}
|
||||
}
|
||||
|
||||
bool local_save = false;
|
||||
|
||||
{
|
||||
char array[33] = {};
|
||||
if (Local_Storage::get_file_data(program_path + "local_save.txt", array, sizeof(array) - 1) != -1) {
|
||||
save_path = program_path + Settings::sanitize(array);
|
||||
local_save = true;
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_DEBUG("Set save_path: %s\n", save_path.c_str());
|
||||
Local_Storage *local_storage = new Local_Storage(save_path);
|
||||
local_storage->setAppId(appid);
|
||||
|
||||
// Listen port
|
||||
char array_port[10] = {};
|
||||
array_port[0] = '0';
|
||||
local_storage->get_data_settings("listen_port.txt", array_port, sizeof(array_port) - 1);
|
||||
uint16 port = std::stoi(array_port);
|
||||
if (port == 0) {
|
||||
port = DEFAULT_PORT;
|
||||
snprintf(array_port, sizeof(array_port), "%hu", port);
|
||||
local_storage->store_data_settings("listen_port.txt", array_port, strlen(array_port));
|
||||
}
|
||||
|
||||
|
||||
// Custom broadcasts
|
||||
std::set<IP_PORT> custom_broadcasts;
|
||||
load_custom_broadcasts(local_storage->get_global_settings_path() + "custom_broadcasts.txt", custom_broadcasts);
|
||||
load_custom_broadcasts(Local_Storage::get_game_settings_path() + "custom_broadcasts.txt", custom_broadcasts);
|
||||
|
||||
// Acount name
|
||||
char name[32] = {};
|
||||
if (local_storage->get_data_settings("account_name.txt", name, sizeof(name) - 1) <= 0) {
|
||||
strcpy(name, DEFAULT_NAME);
|
||||
local_storage->store_data_settings("account_name.txt", name, strlen(name));
|
||||
}
|
||||
|
||||
// Language
|
||||
char language[32] = {};
|
||||
if (local_storage->get_data_settings("language.txt", language, sizeof(language) - 1) <= 0) {
|
||||
strcpy(language, DEFAULT_LANGUAGE);
|
||||
local_storage->store_data_settings("language.txt", language, strlen(language));
|
||||
}
|
||||
|
||||
// Steam ID
|
||||
char array_steam_id[32] = {};
|
||||
CSteamID user_id;
|
||||
uint64 steam_id = 0;
|
||||
bool generate_new = false;
|
||||
//try to load steam id from game specific settings folder first
|
||||
if (local_storage->get_data(Local_Storage::settings_storage_folder, "user_steam_id.txt", array_steam_id, sizeof(array_steam_id) - 1) > 0) {
|
||||
user_id = CSteamID((uint64)std::atoll(array_steam_id));
|
||||
if (!user_id.IsValid()) {
|
||||
generate_new = true;
|
||||
}
|
||||
} else {
|
||||
generate_new = true;
|
||||
}
|
||||
|
||||
if (generate_new) {
|
||||
generate_new = false;
|
||||
if (local_storage->get_data_settings("user_steam_id.txt", array_steam_id, sizeof(array_steam_id) - 1) > 0) {
|
||||
user_id = CSteamID((uint64)std::atoll(array_steam_id));
|
||||
if (!user_id.IsValid()) {
|
||||
generate_new = true;
|
||||
}
|
||||
} else {
|
||||
generate_new = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (generate_new) {
|
||||
user_id = generate_steam_id_user();
|
||||
uint64 steam_id = user_id.ConvertToUint64();
|
||||
char temp_text[32] = {};
|
||||
snprintf(temp_text, sizeof(temp_text), "%llu", steam_id);
|
||||
local_storage->store_data_settings("user_steam_id.txt", temp_text, strlen(temp_text));
|
||||
}
|
||||
|
||||
bool steam_offline_mode = false;
|
||||
bool disable_networking = false;
|
||||
bool disable_overlay = false;
|
||||
bool disable_lobby_creation = false;
|
||||
int build_id = 10;
|
||||
|
||||
bool warn_forced = false;
|
||||
|
||||
{
|
||||
std::string steam_settings_path = Local_Storage::get_game_settings_path();
|
||||
|
||||
std::vector<std::string> paths = Local_Storage::get_filenames_path(steam_settings_path);
|
||||
for (auto & p: paths) {
|
||||
PRINT_DEBUG("steam settings path %s\n", p.c_str());
|
||||
if (p == "offline.txt") {
|
||||
steam_offline_mode = true;
|
||||
} else if (p == "disable_networking.txt") {
|
||||
disable_networking = true;
|
||||
} else if (p == "disable_overlay.txt") {
|
||||
disable_overlay = true;
|
||||
} else if (p == "disable_lobby_creation.txt") {
|
||||
disable_lobby_creation = true;
|
||||
} else if (p == "force_language.txt") {
|
||||
int len = Local_Storage::get_file_data(steam_settings_path + "force_language.txt", language, sizeof(language) - 1);
|
||||
if (len > 0) {
|
||||
language[len] = 0;
|
||||
warn_forced = true;
|
||||
}
|
||||
} else if (p == "force_steamid.txt") {
|
||||
char steam_id_text[32] = {};
|
||||
if (Local_Storage::get_file_data(steam_settings_path + "force_steamid.txt", steam_id_text, sizeof(steam_id_text) - 1) > 0) {
|
||||
CSteamID temp_id = CSteamID((uint64)std::atoll(steam_id_text));
|
||||
if (temp_id.IsValid()) {
|
||||
user_id = temp_id;
|
||||
warn_forced = true;
|
||||
}
|
||||
}
|
||||
} else if (p == "force_account_name.txt") {
|
||||
int len = Local_Storage::get_file_data(steam_settings_path + "force_account_name.txt", name, sizeof(name) - 1);
|
||||
if (len > 0) {
|
||||
name[len] = 0;
|
||||
warn_forced = true;
|
||||
}
|
||||
} else if (p == "force_listen_port.txt") {
|
||||
char array_port[10] = {};
|
||||
int len = Local_Storage::get_file_data(steam_settings_path + "force_listen_port.txt", array_port, sizeof(array_port) - 1);
|
||||
if (len > 0) {
|
||||
port = std::stoi(array_port);
|
||||
warn_forced = true;
|
||||
}
|
||||
} else if (p == "build_id.txt") {
|
||||
char array_id[10] = {};
|
||||
int len = Local_Storage::get_file_data(steam_settings_path + "build_id.txt", array_id, sizeof(array_id) - 1);
|
||||
if (len > 0) build_id = std::stoi(array_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Settings *settings_client = new Settings(user_id, CGameID(appid), name, language, steam_offline_mode);
|
||||
Settings *settings_server = new Settings(generate_steam_id_server(), CGameID(appid), name, language, steam_offline_mode);
|
||||
settings_client->set_port(port);
|
||||
settings_server->set_port(port);
|
||||
settings_client->custom_broadcasts = custom_broadcasts;
|
||||
settings_server->custom_broadcasts = custom_broadcasts;
|
||||
settings_client->disable_networking = disable_networking;
|
||||
settings_server->disable_networking = disable_networking;
|
||||
settings_client->disable_overlay = disable_overlay;
|
||||
settings_server->disable_overlay = disable_overlay;
|
||||
settings_client->disable_lobby_creation = disable_lobby_creation;
|
||||
settings_server->disable_lobby_creation = disable_lobby_creation;
|
||||
settings_client->build_id = build_id;
|
||||
settings_server->build_id = build_id;
|
||||
settings_client->warn_forced = warn_forced;
|
||||
settings_server->warn_forced = warn_forced;
|
||||
settings_client->warn_local_save = local_save;
|
||||
settings_server->warn_local_save = local_save;
|
||||
|
||||
{
|
||||
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "DLC.txt";
|
||||
std::ifstream input( utf8_decode(dlc_config_path) );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
settings_client->unlockAllDLC(false);
|
||||
settings_server->unlockAllDLC(false);
|
||||
PRINT_DEBUG("Locking all DLC\n");
|
||||
|
||||
for( std::string line; std::getline( input, line ); ) {
|
||||
if (!line.empty() && line.front() == '#') {
|
||||
continue;
|
||||
}
|
||||
if (!line.empty() && line.back() == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
if (!line.empty() && line.back() == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
std::size_t deliminator = line.find("=");
|
||||
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
|
||||
AppId_t appid = stol(line.substr(0, deliminator));
|
||||
std::string name = line.substr(deliminator + 1);
|
||||
bool available = true;
|
||||
|
||||
if (appid) {
|
||||
PRINT_DEBUG("Adding DLC: %u|%s| %u\n", appid, name.c_str(), available);
|
||||
settings_client->addDLC(appid, name, available);
|
||||
settings_server->addDLC(appid, name, available);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//unlock all DLC
|
||||
PRINT_DEBUG("Unlocking all DLC\n");
|
||||
settings_client->unlockAllDLC(true);
|
||||
settings_server->unlockAllDLC(true);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "app_paths.txt";
|
||||
std::ifstream input( utf8_decode(dlc_config_path) );
|
||||
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
if (!line.empty() && line[line.length()-1] == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
if (!line.empty() && line[line.length()-1] == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
std::size_t deliminator = line.find("=");
|
||||
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
|
||||
AppId_t appid = stol(line.substr(0, deliminator));
|
||||
std::string rel_path = line.substr(deliminator + 1);
|
||||
std::string path = canonical_path(program_path + rel_path);
|
||||
|
||||
if (appid) {
|
||||
if (path.size()) {
|
||||
PRINT_DEBUG("Adding app path: %u|%s|\n", appid, path.c_str());
|
||||
settings_client->setAppInstallPath(appid, path);
|
||||
settings_server->setAppInstallPath(appid, path);
|
||||
} else {
|
||||
PRINT_DEBUG("Error adding app path for: %u does this path exist? |%s|\n", appid, rel_path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "leaderboards.txt";
|
||||
std::ifstream input( utf8_decode(dlc_config_path) );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
settings_client->setCreateUnknownLeaderboards(false);
|
||||
settings_server->setCreateUnknownLeaderboards(false);
|
||||
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
if (!line.empty() && line[line.length()-1] == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
if (!line.empty() && line[line.length()-1] == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
std::string leaderboard;
|
||||
unsigned int sort_method = 0;
|
||||
unsigned int display_type = 0;
|
||||
|
||||
std::size_t deliminator = line.find("=");
|
||||
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
|
||||
leaderboard = line.substr(0, deliminator);
|
||||
std::size_t deliminator2 = line.find("=", deliminator + 1);
|
||||
if (deliminator2 != std::string::npos && deliminator2 != line.size()) {
|
||||
sort_method = stol(line.substr(deliminator + 1, deliminator2 - (deliminator + 1)));
|
||||
display_type = stol(line.substr(deliminator2 + 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (leaderboard.size() && sort_method <= k_ELeaderboardSortMethodDescending && display_type <= k_ELeaderboardDisplayTypeTimeMilliSeconds) {
|
||||
PRINT_DEBUG("Adding leaderboard: %s|%u|%u\n", leaderboard.c_str(), sort_method, display_type);
|
||||
settings_client->setLeaderboard(leaderboard, (ELeaderboardSortMethod)sort_method, (ELeaderboardDisplayType)display_type);
|
||||
settings_server->setLeaderboard(leaderboard, (ELeaderboardSortMethod)sort_method, (ELeaderboardDisplayType)display_type);
|
||||
} else {
|
||||
PRINT_DEBUG("Error adding leaderboard for: %s, are sort method %u or display type %u valid?\n", leaderboard.c_str(), sort_method, display_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string stats_config_path = Local_Storage::get_game_settings_path() + "stats.txt";
|
||||
std::ifstream input( utf8_decode(stats_config_path) );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
if (!line.empty() && line[line.length()-1] == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
if (!line.empty() && line[line.length()-1] == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
std::string stat_name;
|
||||
std::string stat_type;
|
||||
std::string stat_default_value;
|
||||
|
||||
std::size_t deliminator = line.find("=");
|
||||
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
|
||||
stat_name = line.substr(0, deliminator);
|
||||
std::size_t deliminator2 = line.find("=", deliminator + 1);
|
||||
|
||||
if (deliminator2 != std::string::npos && deliminator2 != line.size()) {
|
||||
stat_type = line.substr(deliminator + 1, deliminator2 - (deliminator + 1));
|
||||
stat_default_value = line.substr(deliminator2 + 1);
|
||||
} else {
|
||||
stat_type = line.substr(deliminator + 1);
|
||||
stat_default_value = "0";
|
||||
}
|
||||
}
|
||||
|
||||
std::transform(stat_type.begin(), stat_type.end(), stat_type.begin(),[](unsigned char c){ return std::tolower(c); });
|
||||
struct Stat_config config = {};
|
||||
|
||||
try {
|
||||
if (stat_type == "float") {
|
||||
config.type = Stat_Type::STAT_TYPE_FLOAT;
|
||||
config.default_value_float = std::stof(stat_default_value);
|
||||
} else if (stat_type == "int") {
|
||||
config.type = Stat_Type::STAT_TYPE_INT;
|
||||
config.default_value_int = std::stol(stat_default_value);
|
||||
} else if (stat_type == "avgrate") {
|
||||
config.type = Stat_Type::STAT_TYPE_AVGRATE;
|
||||
config.default_value_float = std::stof(stat_default_value);
|
||||
} else {
|
||||
PRINT_DEBUG("Error adding stat %s, type %s isn't valid\n", stat_name.c_str(), stat_type.c_str());
|
||||
continue;
|
||||
}
|
||||
} catch (...) {
|
||||
PRINT_DEBUG("Error adding stat %s, default value %s isn't valid\n", stat_name.c_str(), stat_default_value.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stat_name.size()) {
|
||||
PRINT_DEBUG("Adding stat type: %s|%u|%f|%u\n", stat_name.c_str(), config.type, config.default_value_float, config.default_value_int);
|
||||
settings_client->setStatDefiniton(stat_name, config);
|
||||
settings_server->setStatDefiniton(stat_name, config);
|
||||
} else {
|
||||
PRINT_DEBUG("Error adding stat for: %s, empty name\n", stat_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string depots_config_path = Local_Storage::get_game_settings_path() + "depots.txt";
|
||||
std::ifstream input( utf8_decode(depots_config_path) );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
if (!line.empty() && line[line.length()-1] == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
if (!line.empty() && line[line.length()-1] == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
try {
|
||||
DepotId_t depot_id = std::stoul(line);
|
||||
settings_client->depots.push_back(depot_id);
|
||||
settings_server->depots.push_back(depot_id);
|
||||
PRINT_DEBUG("Added depot %u\n", depot_id);
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string depots_config_path = Local_Storage::get_game_settings_path() + "subscribed_groups.txt";
|
||||
std::ifstream input( utf8_decode(depots_config_path) );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
if (!line.empty() && line[line.length()-1] == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
if (!line.empty() && line[line.length()-1] == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
try {
|
||||
uint64 source_id = std::stoull(line);
|
||||
settings_client->subscribed_groups.insert(source_id);
|
||||
settings_server->subscribed_groups.insert(source_id);
|
||||
PRINT_DEBUG("Added source %llu\n", source_id);
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string mod_path = Local_Storage::get_game_settings_path() + "mods";
|
||||
std::vector<std::string> paths = Local_Storage::get_filenames_path(mod_path);
|
||||
for (auto & p: paths) {
|
||||
PRINT_DEBUG("mod directory %s\n", p.c_str());
|
||||
try {
|
||||
PublishedFileId_t id = std::stoull(p);
|
||||
settings_client->addMod(id, p, mod_path + PATH_SEPARATOR + p);
|
||||
settings_server->addMod(id, p, mod_path + PATH_SEPARATOR + p);
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
load_gamecontroller_settings(settings_client);
|
||||
|
||||
*settings_client_out = settings_client;
|
||||
*settings_server_out = settings_server;
|
||||
*local_storage_out = local_storage;
|
||||
|
||||
return appid;
|
||||
}
|
||||
|
||||
void save_global_settings(Local_Storage *local_storage, char *name, char *language)
|
||||
{
|
||||
local_storage->store_data_settings("account_name.txt", name, strlen(name));
|
||||
local_storage->store_data_settings("language.txt", language, strlen(language));
|
||||
}
|
||||
27
all/settings_parser.h
Normal file
27
all/settings_parser.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#ifndef SETTINGS_PARSER_INCLUDE
|
||||
#define SETTINGS_PARSER_INCLUDE
|
||||
|
||||
//returns appid
|
||||
uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out);
|
||||
void save_global_settings(Local_Storage *local_storage, char *name, char *language);
|
||||
|
||||
#endif
|
||||
339
all/steam_HTMLsurface.h
Normal file
339
all/steam_HTMLsurface.h
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_HTMLsurface :
|
||||
public ISteamHTMLSurface001,
|
||||
public ISteamHTMLSurface002,
|
||||
public ISteamHTMLSurface003,
|
||||
public ISteamHTMLSurface004,
|
||||
public ISteamHTMLSurface
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
|
||||
public:
|
||||
Steam_HTMLsurface(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
// Must call init and shutdown when starting/ending use of the interface
|
||||
bool Init()
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::Init\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Shutdown()
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::Shutdown\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Create a browser object for display of a html page, when creation is complete the call handle
|
||||
// will return a HTML_BrowserReady_t callback for the HHTMLBrowser of your new browser.
|
||||
// The user agent string is a substring to be added to the general user agent string so you can
|
||||
// identify your client on web servers.
|
||||
// The userCSS string lets you apply a CSS style sheet to every displayed page, leave null if
|
||||
// you do not require this functionality.
|
||||
//
|
||||
// YOU MUST HAVE IMPLEMENTED HANDLERS FOR HTML_BrowserReady_t, HTML_StartRequest_t,
|
||||
// HTML_JSAlert_t, HTML_JSConfirm_t, and HTML_FileOpenDialog_t! See the CALLBACKS
|
||||
// section of this interface (AllowStartRequest, etc) for more details. If you do
|
||||
// not implement these callback handlers, the browser may appear to hang instead of
|
||||
// navigating to new pages or triggering javascript popups.
|
||||
//
|
||||
STEAM_CALL_RESULT( HTML_BrowserReady_t )
|
||||
SteamAPICall_t CreateBrowser( const char *pchUserAgent, const char *pchUserCSS )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::CreateBrowser\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
HTML_BrowserReady_t data;
|
||||
data.unBrowserHandle = 1234869;
|
||||
//callback too?
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));;
|
||||
}
|
||||
|
||||
|
||||
// Call this when you are done with a html surface, this lets us free the resources being used by it
|
||||
void RemoveBrowser( HHTMLBrowser unBrowserHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::RemoveBrowser\n");
|
||||
}
|
||||
|
||||
|
||||
// Navigate to this URL, results in a HTML_StartRequest_t as the request commences
|
||||
void LoadURL( HHTMLBrowser unBrowserHandle, const char *pchURL, const char *pchPostData )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::LoadURL %s %s\n", pchURL, pchPostData);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
static char url[256];
|
||||
strncpy(url, pchURL, sizeof(url));
|
||||
static char target[] = "_self";
|
||||
static char title[] = "title";
|
||||
|
||||
{
|
||||
HTML_StartRequest_t data;
|
||||
data.unBrowserHandle = unBrowserHandle;
|
||||
data.pchURL = url;
|
||||
data.pchTarget = target;
|
||||
data.pchPostData = "";
|
||||
data.bIsRedirect = false;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
|
||||
}
|
||||
|
||||
{
|
||||
HTML_FinishedRequest_t data;
|
||||
data.unBrowserHandle = unBrowserHandle;
|
||||
data.pchURL = url;
|
||||
data.pchPageTitle = title;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.8);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Tells the surface the size in pixels to display the surface
|
||||
void SetSize( HHTMLBrowser unBrowserHandle, uint32 unWidth, uint32 unHeight )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::SetSize\n");
|
||||
}
|
||||
|
||||
|
||||
// Stop the load of the current html page
|
||||
void StopLoad( HHTMLBrowser unBrowserHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::StopLoad\n");
|
||||
}
|
||||
|
||||
// Reload (most likely from local cache) the current page
|
||||
void Reload( HHTMLBrowser unBrowserHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::Reload\n");
|
||||
}
|
||||
|
||||
// navigate back in the page history
|
||||
void GoBack( HHTMLBrowser unBrowserHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::GoBack\n");
|
||||
}
|
||||
|
||||
// navigate forward in the page history
|
||||
void GoForward( HHTMLBrowser unBrowserHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::GoForward\n");
|
||||
}
|
||||
|
||||
|
||||
// add this header to any url requests from this browser
|
||||
void AddHeader( HHTMLBrowser unBrowserHandle, const char *pchKey, const char *pchValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::AddHeader\n");
|
||||
}
|
||||
|
||||
// run this javascript script in the currently loaded page
|
||||
void ExecuteJavascript( HHTMLBrowser unBrowserHandle, const char *pchScript )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::ExecuteJavascript\n");
|
||||
}
|
||||
|
||||
// Mouse click and mouse movement commands
|
||||
void MouseUp( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::MouseUp\n");
|
||||
}
|
||||
|
||||
void MouseDown( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::MouseDown\n");
|
||||
}
|
||||
|
||||
void MouseDoubleClick( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::MouseDoubleClick\n");
|
||||
}
|
||||
|
||||
// x and y are relative to the HTML bounds
|
||||
void MouseMove( HHTMLBrowser unBrowserHandle, int x, int y )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::MouseMove\n");
|
||||
}
|
||||
|
||||
// nDelta is pixels of scroll
|
||||
void MouseWheel( HHTMLBrowser unBrowserHandle, int32 nDelta )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::MouseWheel\n");
|
||||
}
|
||||
|
||||
// keyboard interactions, native keycode is the key code value from your OS
|
||||
void KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers, bool bIsSystemKey = false )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::KeyDown\n");
|
||||
}
|
||||
|
||||
void KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers)
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::KeyDown old\n");
|
||||
KeyDown(unBrowserHandle, nNativeKeyCode, eHTMLKeyModifiers, false);
|
||||
}
|
||||
|
||||
|
||||
void KeyUp( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::KeyUp\n");
|
||||
}
|
||||
|
||||
// cUnicodeChar is the unicode character point for this keypress (and potentially multiple chars per press)
|
||||
void KeyChar( HHTMLBrowser unBrowserHandle, uint32 cUnicodeChar, EHTMLKeyModifiers eHTMLKeyModifiers )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::KeyChar\n");
|
||||
}
|
||||
|
||||
|
||||
// programmatically scroll this many pixels on the page
|
||||
void SetHorizontalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::SetHorizontalScroll\n");
|
||||
}
|
||||
|
||||
void SetVerticalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::SetVerticalScroll\n");
|
||||
}
|
||||
|
||||
|
||||
// tell the html control if it has key focus currently, controls showing the I-beam cursor in text controls amongst other things
|
||||
void SetKeyFocus( HHTMLBrowser unBrowserHandle, bool bHasKeyFocus )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::SetKeyFocus\n");
|
||||
}
|
||||
|
||||
|
||||
// open the current pages html code in the local editor of choice, used for debugging
|
||||
void ViewSource( HHTMLBrowser unBrowserHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::ViewSource\n");
|
||||
}
|
||||
|
||||
// copy the currently selected text on the html page to the local clipboard
|
||||
void CopyToClipboard( HHTMLBrowser unBrowserHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::CopyToClipboard\n");
|
||||
}
|
||||
|
||||
// paste from the local clipboard to the current html page
|
||||
void PasteFromClipboard( HHTMLBrowser unBrowserHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::PasteFromClipboard\n");
|
||||
}
|
||||
|
||||
|
||||
// find this string in the browser, if bCurrentlyInFind is true then instead cycle to the next matching element
|
||||
void Find( HHTMLBrowser unBrowserHandle, const char *pchSearchStr, bool bCurrentlyInFind, bool bReverse )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::Find\n");
|
||||
}
|
||||
|
||||
// cancel a currently running find
|
||||
void StopFind( HHTMLBrowser unBrowserHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::StopFind\n");
|
||||
}
|
||||
|
||||
|
||||
// return details about the link at position x,y on the current page
|
||||
void GetLinkAtPosition( HHTMLBrowser unBrowserHandle, int x, int y )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::GetLinkAtPosition\n");
|
||||
}
|
||||
|
||||
|
||||
// set a webcookie for the hostname in question
|
||||
void SetCookie( const char *pchHostname, const char *pchKey, const char *pchValue, const char *pchPath, RTime32 nExpires, bool bSecure, bool bHTTPOnly )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::SetCookie\n");
|
||||
}
|
||||
|
||||
|
||||
// Zoom the current page by flZoom ( from 0.0 to 2.0, so to zoom to 120% use 1.2 ), zooming around point X,Y in the page (use 0,0 if you don't care)
|
||||
void SetPageScaleFactor( HHTMLBrowser unBrowserHandle, float flZoom, int nPointX, int nPointY )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::SetPageScaleFactor\n");
|
||||
}
|
||||
|
||||
|
||||
// Enable/disable low-resource background mode, where javascript and repaint timers are throttled, resources are
|
||||
// more aggressively purged from memory, and audio/video elements are paused. When background mode is enabled,
|
||||
// all HTML5 video and audio objects will execute ".pause()" and gain the property "._steam_background_paused = 1".
|
||||
// When background mode is disabled, any video or audio objects with that property will resume with ".play()".
|
||||
void SetBackgroundMode( HHTMLBrowser unBrowserHandle, bool bBackgroundMode )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::SetBackgroundMode\n");
|
||||
}
|
||||
|
||||
|
||||
// Scale the output display space by this factor, this is useful when displaying content on high dpi devices.
|
||||
// Specifies the ratio between physical and logical pixels.
|
||||
void SetDPIScalingFactor( HHTMLBrowser unBrowserHandle, float flDPIScaling )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::SetDPIScalingFactor\n");
|
||||
}
|
||||
|
||||
void OpenDeveloperTools( HHTMLBrowser unBrowserHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::OpenDeveloperTools\n");
|
||||
}
|
||||
|
||||
// CALLBACKS
|
||||
//
|
||||
// These set of functions are used as responses to callback requests
|
||||
//
|
||||
|
||||
// You MUST call this in response to a HTML_StartRequest_t callback
|
||||
// Set bAllowed to true to allow this navigation, false to cancel it and stay
|
||||
// on the current page. You can use this feature to limit the valid pages
|
||||
// allowed in your HTML surface.
|
||||
void AllowStartRequest( HHTMLBrowser unBrowserHandle, bool bAllowed )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::AllowStartRequest\n");
|
||||
}
|
||||
|
||||
|
||||
// You MUST call this in response to a HTML_JSAlert_t or HTML_JSConfirm_t callback
|
||||
// Set bResult to true for the OK option of a confirm, use false otherwise
|
||||
void JSDialogResponse( HHTMLBrowser unBrowserHandle, bool bResult )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::JSDialogResponse\n");
|
||||
}
|
||||
|
||||
|
||||
// You MUST call this in response to a HTML_FileOpenDialog_t callback
|
||||
STEAM_IGNOREATTR()
|
||||
void FileLoadDialogResponse( HHTMLBrowser unBrowserHandle, const char **pchSelectedFiles )
|
||||
{
|
||||
PRINT_DEBUG("Steam_HTMLsurface::FileLoadDialogResponse\n");
|
||||
}
|
||||
|
||||
};
|
||||
51
all/steam_applist.cpp
Normal file
51
all/steam_applist.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_applist.h"
|
||||
|
||||
uint32 Steam_Applist::GetNumInstalledApps()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Applist::GetNumInstalledApps\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 Steam_Applist::GetInstalledApps( AppId_t *pvecAppID, uint32 unMaxAppIDs )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Applist::GetInstalledApps\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// returns -1 if no name was found
|
||||
int Steam_Applist::GetAppName( AppId_t nAppID, STEAM_OUT_STRING() char *pchName, int cchNameMax )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Applist::GetAppName\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// returns -1 if no dir was found
|
||||
int Steam_Applist::GetAppInstallDir( AppId_t nAppID, char *pchDirectory, int cchNameMax )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Applist::GetAppInstallDir\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// return the buildid of this app, may change at any time based on backend updates to the game
|
||||
int Steam_Applist::GetAppBuildId( AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Applist::GetAppBuildId\n");
|
||||
return 10;
|
||||
}
|
||||
30
all/steam_applist.h
Normal file
30
all/steam_applist.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Applist : public ISteamAppList
|
||||
{
|
||||
public:
|
||||
uint32 GetNumInstalledApps();
|
||||
uint32 GetInstalledApps( AppId_t *pvecAppID, uint32 unMaxAppIDs );
|
||||
|
||||
int GetAppName( AppId_t nAppID, STEAM_OUT_STRING() char *pchName, int cchNameMax ); // returns -1 if no name was found
|
||||
int GetAppInstallDir( AppId_t nAppID, char *pchDirectory, int cchNameMax ); // returns -1 if no dir was found
|
||||
|
||||
int GetAppBuildId( AppId_t nAppID ); // return the buildid of this app, may change at any time based on backend updates to the game
|
||||
};
|
||||
346
all/steam_apps.cpp
Normal file
346
all/steam_apps.cpp
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_apps.h"
|
||||
#include "../sha/sha1.hpp"
|
||||
|
||||
Steam_Apps::Steam_Apps(Settings *settings, class SteamCallResults *callback_results)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->callback_results = callback_results;
|
||||
}
|
||||
|
||||
// returns 0 if the key does not exist
|
||||
// this may be true on first call, since the app data may not be cached locally yet
|
||||
// If you expect it to exists wait for the AppDataChanged_t after the first failure and ask again
|
||||
int Steam_Apps::GetAppData( AppId_t nAppID, const char *pchKey, char *pchValue, int cchValueMax )
|
||||
{
|
||||
//TODO
|
||||
PRINT_DEBUG("GetAppData %u %s\n", nAppID, pchKey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Steam_Apps::BIsSubscribed()
|
||||
{
|
||||
PRINT_DEBUG("BIsSubscribed\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Steam_Apps::BIsLowViolence()
|
||||
{
|
||||
PRINT_DEBUG("BIsLowViolence\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_Apps::BIsCybercafe()
|
||||
{
|
||||
PRINT_DEBUG("BIsCybercafe\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_Apps::BIsVACBanned()
|
||||
{
|
||||
PRINT_DEBUG("BIsVACBanned\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *Steam_Apps::GetCurrentGameLanguage()
|
||||
{
|
||||
PRINT_DEBUG("GetCurrentGameLanguage\n");
|
||||
return settings->get_language();
|
||||
}
|
||||
|
||||
const char *Steam_Apps::GetAvailableGameLanguages()
|
||||
{
|
||||
PRINT_DEBUG("GetAvailableGameLanguages\n");
|
||||
//TODO?
|
||||
return settings->get_language();
|
||||
}
|
||||
|
||||
|
||||
// only use this member if you need to check ownership of another game related to yours, a demo for example
|
||||
bool Steam_Apps::BIsSubscribedApp( AppId_t appID )
|
||||
{
|
||||
PRINT_DEBUG("BIsSubscribedApp %u\n", appID);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (appID == 0) return true; //I think appid 0 is always owned
|
||||
if (appID == settings->get_local_game_id().AppID()) return true;
|
||||
return settings->hasDLC(appID);
|
||||
}
|
||||
|
||||
|
||||
// Takes AppID of DLC and checks if the user owns the DLC & if the DLC is installed
|
||||
bool Steam_Apps::BIsDlcInstalled( AppId_t appID )
|
||||
{
|
||||
PRINT_DEBUG("BIsDlcInstalled %u\n", appID);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (appID == 0) return true;
|
||||
return settings->hasDLC(appID);
|
||||
}
|
||||
|
||||
|
||||
// returns the Unix time of the purchase of the app
|
||||
uint32 Steam_Apps::GetEarliestPurchaseUnixTime( AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("GetEarliestPurchaseUnixTime\n");
|
||||
//TODO ?
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Checks if the user is subscribed to the current app through a free weekend
|
||||
// This function will return false for users who have a retail or other type of license
|
||||
// Before using, please ask your Valve technical contact how to package and secure your free weekened
|
||||
bool Steam_Apps::BIsSubscribedFromFreeWeekend()
|
||||
{
|
||||
PRINT_DEBUG("BIsSubscribedFromFreeWeekend\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of DLC pieces for the running app
|
||||
int Steam_Apps::GetDLCCount()
|
||||
{
|
||||
PRINT_DEBUG("GetDLCCount\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return settings->DLCCount();
|
||||
}
|
||||
|
||||
|
||||
// Returns metadata for DLC by index, of range [0, GetDLCCount()]
|
||||
bool Steam_Apps::BGetDLCDataByIndex( int iDLC, AppId_t *pAppID, bool *pbAvailable, char *pchName, int cchNameBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("BGetDLCDataByIndex\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
AppId_t appid;
|
||||
bool available;
|
||||
std::string name;
|
||||
if (!settings->getDLC(iDLC, appid, available, name)) return false;
|
||||
|
||||
if (pAppID) *pAppID = appid;
|
||||
if (pbAvailable) *pbAvailable = available;
|
||||
if (pchName && cchNameBufferSize > 0) {
|
||||
if (cchNameBufferSize > name.size()) {
|
||||
cchNameBufferSize = name.size();
|
||||
pchName[cchNameBufferSize] = 0;
|
||||
}
|
||||
|
||||
//TODO: should pchName be null terminated if the size is smaller or equal to the name size?
|
||||
memcpy(pchName, name.data(), cchNameBufferSize);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Install/Uninstall control for optional DLC
|
||||
void Steam_Apps::InstallDLC( AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("InstallDLC\n");
|
||||
}
|
||||
|
||||
void Steam_Apps::UninstallDLC( AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("UninstallDLC\n");
|
||||
}
|
||||
|
||||
|
||||
// Request legacy cd-key for yourself or owned DLC. If you are interested in this
|
||||
// data then make sure you provide us with a list of valid keys to be distributed
|
||||
// to users when they purchase the game, before the game ships.
|
||||
// You'll receive an AppProofOfPurchaseKeyResponse_t callback when
|
||||
// the key is available (which may be immediately).
|
||||
void Steam_Apps::RequestAppProofOfPurchaseKey( AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("RequestAppProofOfPurchaseKey\n");
|
||||
}
|
||||
|
||||
// returns current beta branch name, 'public' is the default branch
|
||||
bool Steam_Apps::GetCurrentBetaName( char *pchName, int cchNameBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("GetCurrentBetaName %i\n", cchNameBufferSize);
|
||||
if (sizeof("public") > cchNameBufferSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(pchName, "public", sizeof("public"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// signal Steam that game files seems corrupt or missing
|
||||
bool Steam_Apps::MarkContentCorrupt( bool bMissingFilesOnly )
|
||||
{
|
||||
PRINT_DEBUG("MarkContentCorrupt\n");
|
||||
//TODO: warn user
|
||||
return true;
|
||||
}
|
||||
|
||||
// return installed depots in mount order
|
||||
uint32 Steam_Apps::GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots )
|
||||
{
|
||||
PRINT_DEBUG("GetInstalledDepots %u, %u\n", appID, cMaxDepots);
|
||||
//TODO not sure about the behavior of this function, I didn't actually test this.
|
||||
if (!pvecDepots) return 0;
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
unsigned int count = settings->depots.size();
|
||||
if (cMaxDepots < count) count = cMaxDepots;
|
||||
std::copy(settings->depots.begin(), settings->depots.begin() + count, pvecDepots);
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32 Steam_Apps::GetInstalledDepots( DepotId_t *pvecDepots, uint32 cMaxDepots )
|
||||
{
|
||||
PRINT_DEBUG("GetInstalledDepots old\n");
|
||||
return GetInstalledDepots( settings->get_local_game_id().AppID(), pvecDepots, cMaxDepots );
|
||||
}
|
||||
|
||||
// returns current app install folder for AppID, returns folder name length
|
||||
uint32 Steam_Apps::GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("GetAppInstallDir %u %p %u\n", appID, pchFolder, cchFolderBufferSize);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
//TODO return real path instead of dll path
|
||||
std::string installed_path = settings->getAppInstallPath(appID);
|
||||
|
||||
if (installed_path.size() == 0) {
|
||||
std::string dll_path = get_full_program_path();
|
||||
std::string current_path = get_current_path();
|
||||
PRINT_DEBUG("paths %s %s\n", dll_path.c_str(), current_path.c_str());
|
||||
|
||||
//Just pick the smallest path, it has the most chances of being the good one
|
||||
if (dll_path.size() > current_path.size() && current_path.size()) {
|
||||
installed_path = current_path;
|
||||
} else {
|
||||
installed_path = dll_path;
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_DEBUG("path %s\n", installed_path.c_str());
|
||||
if (cchFolderBufferSize && pchFolder) {
|
||||
snprintf(pchFolder, cchFolderBufferSize, "%s", installed_path.c_str());
|
||||
}
|
||||
|
||||
return installed_path.length(); //Real steam always returns the actual path length, not the copied one.
|
||||
}
|
||||
|
||||
// returns true if that app is installed (not necessarily owned)
|
||||
bool Steam_Apps::BIsAppInstalled( AppId_t appID )
|
||||
{
|
||||
PRINT_DEBUG("BIsAppInstalled %u\n", appID);
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns the SteamID of the original owner. If different from current user, it's borrowed
|
||||
CSteamID Steam_Apps::GetAppOwner()
|
||||
{
|
||||
PRINT_DEBUG("GetAppOwner\n");
|
||||
return settings->get_local_steam_id();
|
||||
}
|
||||
|
||||
// Returns the associated launch param if the game is run via steam://run/<appid>//?param1=value1;param2=value2;param3=value3 etc.
|
||||
// Parameter names starting with the character '@' are reserved for internal use and will always return and empty string.
|
||||
// Parameter names starting with an underscore '_' are reserved for steam features -- they can be queried by the game,
|
||||
// but it is advised that you not param names beginning with an underscore for your own features.
|
||||
const char *Steam_Apps::GetLaunchQueryParam( const char *pchKey )
|
||||
{
|
||||
PRINT_DEBUG("GetLaunchQueryParam\n");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
// get download progress for optional DLC
|
||||
bool Steam_Apps::GetDlcDownloadProgress( AppId_t nAppID, uint64 *punBytesDownloaded, uint64 *punBytesTotal )
|
||||
{
|
||||
PRINT_DEBUG("GetDlcDownloadProgress\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// return the buildid of this app, may change at any time based on backend updates to the game
|
||||
int Steam_Apps::GetAppBuildId()
|
||||
{
|
||||
PRINT_DEBUG("GetAppBuildId\n");
|
||||
return this->settings->build_id;
|
||||
}
|
||||
|
||||
|
||||
// Request all proof of purchase keys for the calling appid and asociated DLC.
|
||||
// A series of AppProofOfPurchaseKeyResponse_t callbacks will be sent with
|
||||
// appropriate appid values, ending with a final callback where the m_nAppId
|
||||
// member is k_uAppIdInvalid (zero).
|
||||
void Steam_Apps::RequestAllProofOfPurchaseKeys()
|
||||
{
|
||||
PRINT_DEBUG("RequestAllProofOfPurchaseKeys\n");
|
||||
}
|
||||
|
||||
|
||||
STEAM_CALL_RESULT( FileDetailsResult_t )
|
||||
SteamAPICall_t Steam_Apps::GetFileDetails( const char* pszFileName )
|
||||
{
|
||||
PRINT_DEBUG("GetFileDetails %s\n", pszFileName);
|
||||
FileDetailsResult_t data = {};
|
||||
//TODO? this function should only return found if file is actually part of the steam depots
|
||||
if (file_exists_(pszFileName)) {
|
||||
data.m_eResult = k_EResultOK; //
|
||||
std::ifstream stream(utf8_decode(pszFileName), std::ios::binary);
|
||||
SHA1 checksum;
|
||||
checksum.update(stream);
|
||||
checksum.final().copy((char *)data.m_FileSHA, sizeof(data.m_FileSHA));
|
||||
data.m_ulFileSize = file_size_(pszFileName);
|
||||
//TODO data.m_unFlags; 0 is file //TODO: check if 64 is folder
|
||||
} else {
|
||||
data.m_eResult = k_EResultFileNotFound;
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
// Get command line if game was launched via Steam URL, e.g. steam://run/<appid>//<command line>/.
|
||||
// This method of passing a connect string (used when joining via rich presence, accepting an
|
||||
// invite, etc) is preferable to passing the connect string on the operating system command
|
||||
// line, which is a security risk. In order for rich presence joins to go through this
|
||||
// path and not be placed on the OS command line, you must set a value in your app's
|
||||
// configuration on Steam. Ask Valve for help with this.
|
||||
//
|
||||
// If game was already running and launched again, the NewUrlLaunchParameters_t will be fired.
|
||||
int Steam_Apps::GetLaunchCommandLine( char *pszCommandLine, int cubCommandLine )
|
||||
{
|
||||
PRINT_DEBUG("GetLaunchCommandLine\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if user borrowed this game via Family Sharing, If true, call GetAppOwner() to get the lender SteamID
|
||||
bool Steam_Apps::BIsSubscribedFromFamilySharing()
|
||||
{
|
||||
PRINT_DEBUG("BIsSubscribedFromFamilySharing\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if game is a timed trial with limited playtime
|
||||
bool Steam_Apps::BIsTimedTrial( uint32* punSecondsAllowed, uint32* punSecondsPlayed )
|
||||
{
|
||||
PRINT_DEBUG("BIsTimedTrial\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// set current DLC AppID being played (or 0 if none). Allows Steam to track usage of major DLC extensions
|
||||
bool Steam_Apps::SetDlcContext( AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("SetDlcContext %u\n", nAppID);
|
||||
return true;
|
||||
}
|
||||
112
all/steam_apps.h
Normal file
112
all/steam_apps.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#include "base.h"
|
||||
|
||||
class Steam_Apps :
|
||||
public ISteamApps001,
|
||||
public ISteamApps002,
|
||||
public ISteamApps003,
|
||||
public ISteamApps004,
|
||||
public ISteamApps005,
|
||||
public ISteamApps006,
|
||||
public ISteamApps007,
|
||||
public ISteamApps
|
||||
{
|
||||
Settings *settings;
|
||||
class SteamCallResults *callback_results;
|
||||
|
||||
public:
|
||||
Steam_Apps(Settings *settings, class SteamCallResults *callback_results);
|
||||
|
||||
// returns 0 if the key does not exist
|
||||
// this may be true on first call, since the app data may not be cached locally yet
|
||||
// If you expect it to exists wait for the AppDataChanged_t after the first failure and ask again
|
||||
int GetAppData( AppId_t nAppID, const char *pchKey, char *pchValue, int cchValueMax );
|
||||
|
||||
bool BIsSubscribed();
|
||||
bool BIsLowViolence();
|
||||
bool BIsCybercafe();
|
||||
bool BIsVACBanned();
|
||||
const char *GetCurrentGameLanguage();
|
||||
const char *GetAvailableGameLanguages();
|
||||
|
||||
// only use this member if you need to check ownership of another game related to yours, a demo for example
|
||||
bool BIsSubscribedApp( AppId_t appID );
|
||||
|
||||
// Takes AppID of DLC and checks if the user owns the DLC & if the DLC is installed
|
||||
bool BIsDlcInstalled( AppId_t appID );
|
||||
|
||||
// returns the Unix time of the purchase of the app
|
||||
uint32 GetEarliestPurchaseUnixTime( AppId_t nAppID );
|
||||
|
||||
// Checks if the user is subscribed to the current app through a free weekend
|
||||
// This function will return false for users who have a retail or other type of license
|
||||
// Before using, please ask your Valve technical contact how to package and secure your free weekened
|
||||
bool BIsSubscribedFromFreeWeekend();
|
||||
|
||||
// Returns the number of DLC pieces for the running app
|
||||
int GetDLCCount();
|
||||
|
||||
// Returns metadata for DLC by index, of range [0, GetDLCCount()]
|
||||
bool BGetDLCDataByIndex( int iDLC, AppId_t *pAppID, bool *pbAvailable, char *pchName, int cchNameBufferSize );
|
||||
|
||||
// Install/Uninstall control for optional DLC
|
||||
void InstallDLC( AppId_t nAppID );
|
||||
void UninstallDLC( AppId_t nAppID );
|
||||
|
||||
// Request legacy cd-key for yourself or owned DLC. If you are interested in this
|
||||
// data then make sure you provide us with a list of valid keys to be distributed
|
||||
// to users when they purchase the game, before the game ships.
|
||||
// You'll receive an AppProofOfPurchaseKeyResponse_t callback when
|
||||
// the key is available (which may be immediately).
|
||||
void RequestAppProofOfPurchaseKey( AppId_t nAppID );
|
||||
|
||||
bool GetCurrentBetaName( char *pchName, int cchNameBufferSize ); // returns current beta branch name, 'public' is the default branch
|
||||
bool MarkContentCorrupt( bool bMissingFilesOnly ); // signal Steam that game files seems corrupt or missing
|
||||
uint32 GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots ); // return installed depots in mount order
|
||||
uint32 GetInstalledDepots( DepotId_t *pvecDepots, uint32 cMaxDepots );
|
||||
|
||||
// returns current app install folder for AppID, returns folder name length
|
||||
uint32 GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize );
|
||||
bool BIsAppInstalled( AppId_t appID ); // returns true if that app is installed (not necessarily owned)
|
||||
|
||||
CSteamID GetAppOwner(); // returns the SteamID of the original owner. If different from current user, it's borrowed
|
||||
|
||||
// Returns the associated launch param if the game is run via steam://run/<appid>//?param1=value1;param2=value2;param3=value3 etc.
|
||||
// Parameter names starting with the character '@' are reserved for internal use and will always return and empty string.
|
||||
// Parameter names starting with an underscore '_' are reserved for steam features -- they can be queried by the game,
|
||||
// but it is advised that you not param names beginning with an underscore for your own features.
|
||||
const char *GetLaunchQueryParam( const char *pchKey );
|
||||
|
||||
// get download progress for optional DLC
|
||||
bool GetDlcDownloadProgress( AppId_t nAppID, uint64 *punBytesDownloaded, uint64 *punBytesTotal );
|
||||
|
||||
// return the buildid of this app, may change at any time based on backend updates to the game
|
||||
int GetAppBuildId();
|
||||
|
||||
// Request all proof of purchase keys for the calling appid and asociated DLC.
|
||||
// A series of AppProofOfPurchaseKeyResponse_t callbacks will be sent with
|
||||
// appropriate appid values, ending with a final callback where the m_nAppId
|
||||
// member is k_uAppIdInvalid (zero).
|
||||
void RequestAllProofOfPurchaseKeys();
|
||||
|
||||
STEAM_CALL_RESULT( FileDetailsResult_t )
|
||||
SteamAPICall_t GetFileDetails( const char* pszFileName );
|
||||
|
||||
// Get command line if game was launched via Steam URL, e.g. steam://run/<appid>//<command line>/.
|
||||
// This method of passing a connect string (used when joining via rich presence, accepting an
|
||||
// invite, etc) is preferable to passing the connect string on the operating system command
|
||||
// line, which is a security risk. In order for rich presence joins to go through this
|
||||
// path and not be placed on the OS command line, you must set a value in your app's
|
||||
// configuration on Steam. Ask Valve for help with this.
|
||||
//
|
||||
// If game was already running and launched again, the NewUrlLaunchParameters_t will be fired.
|
||||
int GetLaunchCommandLine( char *pszCommandLine, int cubCommandLine );
|
||||
|
||||
// Check if user borrowed this game via Family Sharing, If true, call GetAppOwner() to get the lender SteamID
|
||||
bool BIsSubscribedFromFamilySharing();
|
||||
|
||||
// check if game is a timed trial with limited playtime
|
||||
bool BIsTimedTrial( uint32* punSecondsAllowed, uint32* punSecondsPlayed );
|
||||
|
||||
// set current DLC AppID being played (or 0 if none). Allows Steam to track usage of major DLC extensions
|
||||
bool SetDlcContext( AppId_t nAppID );
|
||||
};
|
||||
1647
all/steam_client.cpp
Normal file
1647
all/steam_client.cpp
Normal file
File diff suppressed because it is too large
Load diff
304
all/steam_client.h
Normal file
304
all/steam_client.h
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
#include "steam_user.h"
|
||||
#include "steam_friends.h"
|
||||
#include "steam_utils.h"
|
||||
#include "steam_matchmaking.h"
|
||||
#include "steam_matchmaking_servers.h"
|
||||
#include "steam_user_stats.h"
|
||||
#include "steam_apps.h"
|
||||
#include "steam_networking.h"
|
||||
#include "steam_remote_storage.h"
|
||||
#include "steam_screenshots.h"
|
||||
#include "steam_http.h"
|
||||
#include "steam_controller.h"
|
||||
#include "steam_ugc.h"
|
||||
#include "steam_applist.h"
|
||||
#include "steam_music.h"
|
||||
#include "steam_musicremote.h"
|
||||
#include "steam_HTMLsurface.h"
|
||||
#include "steam_inventory.h"
|
||||
#include "steam_video.h"
|
||||
#include "steam_parental.h"
|
||||
#include "steam_game_coordinator.h"
|
||||
#include "steam_networking_socketsserialized.h"
|
||||
#include "steam_networking_sockets.h"
|
||||
#include "steam_networking_messages.h"
|
||||
#include "steam_networking_utils.h"
|
||||
#include "steam_unified_messages.h"
|
||||
#include "steam_gamesearch.h"
|
||||
#include "steam_parties.h"
|
||||
#include "steam_remoteplay.h"
|
||||
#include "steam_tv.h"
|
||||
|
||||
#include "steam_gameserver.h"
|
||||
#include "steam_gameserverstats.h"
|
||||
#include "steam_masterserver_updater.h"
|
||||
|
||||
#include "../overlay_experimental/steam_overlay.h"
|
||||
|
||||
enum Steam_Pipe {
|
||||
NO_USER,
|
||||
CLIENT,
|
||||
SERVER
|
||||
};
|
||||
|
||||
class Steam_Client :
|
||||
public ISteamClient007,
|
||||
public ISteamClient008,
|
||||
public ISteamClient009,
|
||||
public ISteamClient010,
|
||||
public ISteamClient011,
|
||||
public ISteamClient012,
|
||||
public ISteamClient013,
|
||||
public ISteamClient014,
|
||||
public ISteamClient015,
|
||||
public ISteamClient016,
|
||||
public ISteamClient017,
|
||||
public ISteamClient018,
|
||||
public ISteamClient019,
|
||||
public ISteamClient
|
||||
{
|
||||
public:
|
||||
Networking *network;
|
||||
SteamCallResults *callback_results_server, *callback_results_client;
|
||||
SteamCallBacks *callbacks_server, *callbacks_client;
|
||||
Settings *settings_client, *settings_server;
|
||||
Local_Storage *local_storage;
|
||||
RunEveryRunCB *run_every_runcb;
|
||||
|
||||
Steam_User *steam_user;
|
||||
Steam_Friends *steam_friends;
|
||||
Steam_Utils *steam_utils;
|
||||
Steam_Matchmaking *steam_matchmaking;
|
||||
Steam_Matchmaking_Servers *steam_matchmaking_servers;
|
||||
Steam_User_Stats *steam_user_stats;
|
||||
Steam_Apps *steam_apps;
|
||||
Steam_Networking *steam_networking;
|
||||
Steam_Remote_Storage *steam_remote_storage;
|
||||
Steam_Screenshots *steam_screenshots;
|
||||
Steam_HTTP *steam_http;
|
||||
Steam_Controller *steam_controller;
|
||||
Steam_UGC *steam_ugc;
|
||||
Steam_Applist *steam_applist;
|
||||
Steam_Music *steam_music;
|
||||
Steam_MusicRemote *steam_musicremote;
|
||||
Steam_HTMLsurface *steam_HTMLsurface;
|
||||
Steam_Inventory *steam_inventory;
|
||||
Steam_Video *steam_video;
|
||||
Steam_Parental *steam_parental;
|
||||
Steam_Networking_Sockets *steam_networking_sockets;
|
||||
Steam_Networking_Sockets_Serialized *steam_networking_sockets_serialized;
|
||||
Steam_Networking_Messages *steam_networking_messages;
|
||||
Steam_Game_Coordinator *steam_game_coordinator;
|
||||
Steam_Networking_Utils *steam_networking_utils;
|
||||
Steam_Unified_Messages *steam_unified_messages;
|
||||
Steam_Game_Search *steam_game_search;
|
||||
Steam_Parties *steam_parties;
|
||||
Steam_RemotePlay *steam_remoteplay;
|
||||
Steam_TV *steam_tv;
|
||||
|
||||
Steam_GameServer *steam_gameserver;
|
||||
Steam_Utils *steam_gameserver_utils;
|
||||
Steam_GameServerStats *steam_gameserverstats;
|
||||
Steam_Networking *steam_gameserver_networking;
|
||||
Steam_HTTP *steam_gameserver_http;
|
||||
Steam_Inventory *steam_gameserver_inventory;
|
||||
Steam_UGC *steam_gameserver_ugc;
|
||||
Steam_Apps *steam_gameserver_apps;
|
||||
Steam_Networking_Sockets *steam_gameserver_networking_sockets;
|
||||
Steam_Networking_Sockets_Serialized *steam_gameserver_networking_sockets_serialized;
|
||||
Steam_Networking_Messages *steam_gameserver_networking_messages;
|
||||
Steam_Game_Coordinator *steam_gameserver_game_coordinator;
|
||||
Steam_Masterserver_Updater *steam_masterserver_updater;
|
||||
|
||||
Steam_Overlay* steam_overlay;
|
||||
|
||||
bool user_logged_in = false;
|
||||
bool server_init = false;
|
||||
std::thread background_keepalive;
|
||||
bool steamclient_server_inited = false;
|
||||
std::atomic<unsigned long long> last_cb_run;
|
||||
|
||||
unsigned steam_pipe_counter = 1;
|
||||
std::map<HSteamPipe, enum Steam_Pipe> steam_pipes;
|
||||
|
||||
bool gameserver_has_ipv6_functions;
|
||||
|
||||
Steam_Client();
|
||||
~Steam_Client();
|
||||
// Creates a communication pipe to the Steam client.
|
||||
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
|
||||
HSteamPipe CreateSteamPipe();
|
||||
|
||||
// Releases a previously created communications pipe
|
||||
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
|
||||
bool BReleaseSteamPipe( HSteamPipe hSteamPipe );
|
||||
|
||||
// connects to an existing global user, failing if none exists
|
||||
// used by the game to coordinate with the steamUI
|
||||
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
|
||||
HSteamUser ConnectToGlobalUser( HSteamPipe hSteamPipe );
|
||||
|
||||
// used by game servers, create a steam user that won't be shared with anyone else
|
||||
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
|
||||
HSteamUser CreateLocalUser( HSteamPipe *phSteamPipe, EAccountType eAccountType );
|
||||
HSteamUser CreateLocalUser( HSteamPipe *phSteamPipe );
|
||||
|
||||
// removes an allocated user
|
||||
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
|
||||
void ReleaseUser( HSteamPipe hSteamPipe, HSteamUser hUser );
|
||||
|
||||
// retrieves the ISteamUser interface associated with the handle
|
||||
ISteamUser *GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// retrieves the ISteamGameServer interface associated with the handle
|
||||
ISteamGameServer *GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// set the local IP and Port to bind to
|
||||
// this must be set before CreateLocalUser()
|
||||
void SetLocalIPBinding( uint32 unIP, uint16 usPort );
|
||||
void SetLocalIPBinding( const SteamIPAddress_t &unIP, uint16 usPort );
|
||||
|
||||
// returns the ISteamFriends interface
|
||||
ISteamFriends *GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// returns the ISteamUtils interface
|
||||
ISteamUtils *GetISteamUtils( HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// returns the ISteamMatchmaking interface
|
||||
ISteamMatchmaking *GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// returns the ISteamMatchmakingServers interface
|
||||
ISteamMatchmakingServers *GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// returns the a generic interface
|
||||
void *GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// returns the ISteamUserStats interface
|
||||
ISteamUserStats *GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// returns the ISteamGameServerStats interface
|
||||
ISteamGameServerStats *GetISteamGameServerStats( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// returns apps interface
|
||||
ISteamApps *GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// networking
|
||||
ISteamNetworking *GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// remote storage
|
||||
ISteamRemoteStorage *GetISteamRemoteStorage( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// user screenshots
|
||||
ISteamScreenshots *GetISteamScreenshots( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
|
||||
// Deprecated. Applications should use SteamAPI_RunCallbacks() or SteamGameServer_RunCallbacks() instead.
|
||||
STEAM_PRIVATE_API( void RunFrame(); )
|
||||
|
||||
// returns the number of IPC calls made since the last time this function was called
|
||||
// Used for perf debugging so you can understand how many IPC calls your game makes per frame
|
||||
// Every IPC call is at minimum a thread context switch if not a process one so you want to rate
|
||||
// control how often you do them.
|
||||
uint32 GetIPCCallCount();
|
||||
|
||||
// API warning handling
|
||||
// 'int' is the severity; 0 for msg, 1 for warning
|
||||
// 'const char *' is the text of the message
|
||||
// callbacks will occur directly after the API function is called that generated the warning or message.
|
||||
void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction );
|
||||
|
||||
// Trigger global shutdown for the DLL
|
||||
bool BShutdownIfAllPipesClosed();
|
||||
|
||||
// Expose HTTP interface
|
||||
ISteamHTTP *GetISteamHTTP( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// Deprecated - the ISteamUnifiedMessages interface is no longer intended for public consumption.
|
||||
STEAM_PRIVATE_API( void *DEPRECATED_GetISteamUnifiedMessages( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) ; )
|
||||
ISteamUnifiedMessages *GetISteamUnifiedMessages( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// Exposes the ISteamController interface
|
||||
ISteamController *GetISteamController( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// Exposes the ISteamUGC interface
|
||||
ISteamUGC *GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// returns app list interface, only available on specially registered apps
|
||||
ISteamAppList *GetISteamAppList( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// Music Player
|
||||
ISteamMusic *GetISteamMusic( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// Music Player Remote
|
||||
ISteamMusicRemote *GetISteamMusicRemote(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion);
|
||||
|
||||
// html page display
|
||||
ISteamHTMLSurface *GetISteamHTMLSurface(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion);
|
||||
|
||||
// Helper functions for internal Steam usage
|
||||
STEAM_PRIVATE_API( void DEPRECATED_Set_SteamAPI_CPostAPIResultInProcess( void (*)() ); )
|
||||
STEAM_PRIVATE_API( void DEPRECATED_Remove_SteamAPI_CPostAPIResultInProcess( void (*)() ); )
|
||||
STEAM_PRIVATE_API( void Set_SteamAPI_CCheckCallbackRegisteredInProcess( SteamAPI_CheckCallbackRegistered_t func ); )
|
||||
void Set_SteamAPI_CPostAPIResultInProcess( SteamAPI_PostAPIResultInProcess_t func );
|
||||
void Remove_SteamAPI_CPostAPIResultInProcess( SteamAPI_PostAPIResultInProcess_t func );
|
||||
|
||||
// inventory
|
||||
ISteamInventory *GetISteamInventory( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// Video
|
||||
ISteamVideo *GetISteamVideo( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// Parental controls
|
||||
ISteamParentalSettings *GetISteamParentalSettings( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
//
|
||||
ISteamMasterServerUpdater *GetISteamMasterServerUpdater( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
ISteamContentServer *GetISteamContentServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// game search
|
||||
ISteamGameSearch *GetISteamGameSearch( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
// Exposes the Steam Input interface for controller support
|
||||
ISteamInput *GetISteamInput( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// Steam Parties interface
|
||||
ISteamParties *GetISteamParties( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
// Steam Remote Play interface
|
||||
ISteamRemotePlay *GetISteamRemotePlay( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
||||
void RegisterCallback( class CCallbackBase *pCallback, int iCallback);
|
||||
void UnregisterCallback( class CCallbackBase *pCallback);
|
||||
|
||||
void RegisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall);
|
||||
void UnregisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall);
|
||||
|
||||
void RunCallbacks(bool runClientCB, bool runGameserverCB);
|
||||
void setAppID(uint32 appid);
|
||||
void userLogIn();
|
||||
void serverInit();
|
||||
void serverShutdown();
|
||||
void clientShutdown();
|
||||
bool IsServerInit();
|
||||
bool IsUserLogIn();
|
||||
|
||||
void DestroyAllInterfaces();
|
||||
};
|
||||
1247
all/steam_controller.h
Normal file
1247
all/steam_controller.h
Normal file
File diff suppressed because it is too large
Load diff
1182
all/steam_friends.h
Normal file
1182
all/steam_friends.h
Normal file
File diff suppressed because it is too large
Load diff
154
all/steam_game_coordinator.h
Normal file
154
all/steam_game_coordinator.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Game_Coordinator :
|
||||
public ISteamGameCoordinator
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
|
||||
static const uint32 protobuf_mask = 0x80000000;
|
||||
std::queue<std::string> outgoing_messages;
|
||||
|
||||
void push_incoming(std::string message)
|
||||
{
|
||||
outgoing_messages.push(message);
|
||||
|
||||
struct GCMessageAvailable_t data;
|
||||
data.m_nMessageSize = message.size();
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_gamecoordinator_callback\n");
|
||||
|
||||
Steam_Game_Coordinator *steam_gamecoordinator = (Steam_Game_Coordinator *)object;
|
||||
steam_gamecoordinator->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_gamecoordinator_run_every_runcb\n");
|
||||
|
||||
Steam_Game_Coordinator *steam_gamecoordinator = (Steam_Game_Coordinator *)object;
|
||||
steam_gamecoordinator->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Game_Coordinator(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Game_Coordinator::steam_callback, this);
|
||||
this->run_every_runcb->add(&Steam_Game_Coordinator::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_Game_Coordinator()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
this->run_every_runcb->remove(&Steam_Game_Coordinator::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
// sends a message to the Game Coordinator
|
||||
EGCResults SendMessage_( uint32 unMsgType, const void *pubData, uint32 cubData )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Coordinator::SendMessage %X %u len %u\n", unMsgType, (~protobuf_mask) & unMsgType, cubData);
|
||||
if (protobuf_mask & unMsgType) {
|
||||
uint32 message_type = (~protobuf_mask) & unMsgType;
|
||||
if (message_type == 4006) { //client hello
|
||||
std::string message("\xA4\x0F\x00\x80\x00\x00\x00\x00\x08\x00", 10);
|
||||
push_incoming(message);
|
||||
} else
|
||||
if (message_type == 4007) { //server hello
|
||||
std::string message("\xA5\x0F\x00\x80\x00\x00\x00\x00\x08\x00", 10);
|
||||
push_incoming(message);
|
||||
}
|
||||
}
|
||||
|
||||
return k_EGCResultOK;
|
||||
}
|
||||
|
||||
// returns true if there is a message waiting from the game coordinator
|
||||
bool IsMessageAvailable( uint32 *pcubMsgSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Coordinator::IsMessageAvailable\n");
|
||||
if (outgoing_messages.size()) {
|
||||
if (pcubMsgSize) *pcubMsgSize = outgoing_messages.front().size();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// fills the provided buffer with the first message in the queue and returns k_EGCResultOK or
|
||||
// returns k_EGCResultNoMessage if there is no message waiting. pcubMsgSize is filled with the message size.
|
||||
// If the provided buffer is not large enough to fit the entire message, k_EGCResultBufferTooSmall is returned
|
||||
// and the message remains at the head of the queue.
|
||||
EGCResults RetrieveMessage( uint32 *punMsgType, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Coordinator::RetrieveMessage\n");
|
||||
if (outgoing_messages.size()) {
|
||||
if (outgoing_messages.front().size() > cubDest) {
|
||||
return k_EGCResultBufferTooSmall;
|
||||
}
|
||||
|
||||
outgoing_messages.front().copy((char *)pubDest, cubDest);
|
||||
if (pcubMsgSize) *pcubMsgSize = outgoing_messages.front().size();
|
||||
if (punMsgType && outgoing_messages.front().size() >= sizeof(uint32)) {
|
||||
outgoing_messages.front().copy((char *)punMsgType, sizeof(uint32));
|
||||
*punMsgType = ntohl(*punMsgType);
|
||||
}
|
||||
|
||||
outgoing_messages.pop();
|
||||
return k_EGCResultOK;
|
||||
} else {
|
||||
return k_EGCResultNoMessage;
|
||||
}
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->has_networking_sockets()) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
215
all/steam_gamesearch.h
Normal file
215
all/steam_gamesearch.h
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Game_Search :
|
||||
public ISteamGameSearch
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
|
||||
FSteamNetworkingSocketsDebugOutput debug_function;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_gamesearch_callback\n");
|
||||
|
||||
Steam_Game_Search *steam_gamesearch = (Steam_Game_Search *)object;
|
||||
steam_gamesearch->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_gamesearch_run_every_runcb\n");
|
||||
|
||||
Steam_Game_Search *steam_gamesearch = (Steam_Game_Search *)object;
|
||||
steam_gamesearch->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Game_Search(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Game_Search::steam_callback, this);
|
||||
this->run_every_runcb->add(&Steam_Game_Search::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_Game_Search()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
this->run_every_runcb->remove(&Steam_Game_Search::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
// =============================================================================================
|
||||
// Game Player APIs
|
||||
|
||||
// a keyname and a list of comma separated values: one of which is must be found in order for the match to qualify
|
||||
// fails if a search is currently in progress
|
||||
EGameSearchErrorCode_t AddGameSearchParams( const char *pchKeyToFind, const char *pchValuesToFind )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::AddGameSearchParams\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// all players in lobby enter the queue and await a SearchForGameNotificationCallback_t callback. fails if another search is currently in progress
|
||||
// if not the owner of the lobby or search already in progress this call fails
|
||||
// periodic callbacks will be sent as queue time estimates change
|
||||
EGameSearchErrorCode_t SearchForGameWithLobby( CSteamID steamIDLobby, int nPlayerMin, int nPlayerMax )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::SearchForGameWithLobby\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// user enter the queue and await a SearchForGameNotificationCallback_t callback. fails if another search is currently in progress
|
||||
// periodic callbacks will be sent as queue time estimates change
|
||||
EGameSearchErrorCode_t SearchForGameSolo( int nPlayerMin, int nPlayerMax )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::SearchForGameSolo\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// after receiving SearchForGameResultCallback_t, accept or decline the game
|
||||
// multiple SearchForGameResultCallback_t will follow as players accept game until the host starts or cancels the game
|
||||
EGameSearchErrorCode_t AcceptGame()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::AcceptGame\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
EGameSearchErrorCode_t DeclineGame()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::DeclineGame\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// after receiving GameStartedByHostCallback_t get connection details to server
|
||||
EGameSearchErrorCode_t RetrieveConnectionDetails( CSteamID steamIDHost, char *pchConnectionDetails, int cubConnectionDetails )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::RetrieveConnectionDetails\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// leaves queue if still waiting
|
||||
EGameSearchErrorCode_t EndGameSearch()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::EndGameSearch\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================================
|
||||
// Game Host APIs
|
||||
|
||||
// a keyname and a list of comma separated values: all the values you allow
|
||||
EGameSearchErrorCode_t SetGameHostParams( const char *pchKey, const char *pchValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::SetGameHostParams\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// set connection details for players once game is found so they can connect to this server
|
||||
EGameSearchErrorCode_t SetConnectionDetails( const char *pchConnectionDetails, int cubConnectionDetails )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::SetConnectionDetails\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// mark server as available for more players with nPlayerMin,nPlayerMax desired
|
||||
// accept no lobbies with playercount greater than nMaxTeamSize
|
||||
// the set of lobbies returned must be partitionable into teams of no more than nMaxTeamSize
|
||||
// RequestPlayersForGameNotificationCallback_t callback will be sent when the search has started
|
||||
// multple RequestPlayersForGameResultCallback_t callbacks will follow when players are found
|
||||
EGameSearchErrorCode_t RequestPlayersForGame( int nPlayerMin, int nPlayerMax, int nMaxTeamSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::RequestPlayersForGame\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// accept the player list and release connection details to players
|
||||
// players will only be given connection details and host steamid when this is called
|
||||
// ( allows host to accept after all players confirm, some confirm, or none confirm. decision is entirely up to the host )
|
||||
EGameSearchErrorCode_t HostConfirmGameStart( uint64 ullUniqueGameID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::HostConfirmGameStart\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// cancel request and leave the pool of game hosts looking for players
|
||||
// if a set of players has already been sent to host, all players will receive SearchForGameHostFailedToConfirm_t
|
||||
EGameSearchErrorCode_t CancelRequestPlayersForGame()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::CancelRequestPlayersForGame\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// submit a result for one player. does not end the game. ullUniqueGameID continues to describe this game
|
||||
EGameSearchErrorCode_t SubmitPlayerResult( uint64 ullUniqueGameID, CSteamID steamIDPlayer, EPlayerResult_t EPlayerResult )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::SubmitPlayerResult\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
// ends the game. no further SubmitPlayerResults for ullUniqueGameID will be accepted
|
||||
// any future requests will provide a new ullUniqueGameID
|
||||
EGameSearchErrorCode_t EndGame( uint64 ullUniqueGameID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::EndGame\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->has_networking_sockets()) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
734
all/steam_gameserver.cpp
Normal file
734
all/steam_gameserver.cpp
Normal file
|
|
@ -0,0 +1,734 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_gameserver.h"
|
||||
|
||||
#define SEND_SERVER_RATE 5.0
|
||||
|
||||
Steam_GameServer::Steam_GameServer(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks)
|
||||
{
|
||||
this->network = network;
|
||||
this->settings = settings;
|
||||
server_data.set_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
this->callbacks = callbacks;
|
||||
ticket_manager = new Auth_Ticket_Manager(settings, network, callbacks);
|
||||
}
|
||||
|
||||
Steam_GameServer::~Steam_GameServer()
|
||||
{
|
||||
delete ticket_manager;
|
||||
}
|
||||
|
||||
//
|
||||
// Basic server data. These properties, if set, must be set before before calling LogOn. They
|
||||
// may not be changed after logged in.
|
||||
//
|
||||
|
||||
/// This is called by SteamGameServer_Init, and you will usually not need to call it directly
|
||||
bool Steam_GameServer::InitGameServer( uint32 unIP, uint16 usGamePort, uint16 usQueryPort, uint32 unFlags, AppId_t nGameAppId, const char *pchVersionString )
|
||||
{
|
||||
PRINT_DEBUG("InitGameServer\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (logged_in) return false; // may not be changed after logged in.
|
||||
std::string version(pchVersionString);
|
||||
version.erase(std::remove(version.begin(), version.end(), ' '), version.end());
|
||||
version.erase(std::remove(version.begin(), version.end(), '.'), version.end());
|
||||
server_data.set_version(stoi(version));
|
||||
|
||||
server_data.set_ip(unIP);
|
||||
server_data.set_port(usGamePort);
|
||||
server_data.set_query_port(usQueryPort);
|
||||
server_data.set_offline(false);
|
||||
if (!settings->get_local_game_id().AppID()) settings->set_game_id(CGameID(nGameAppId));
|
||||
//TODO: flags should be k_unServerFlag
|
||||
flags = unFlags;
|
||||
policy_response_called = false;
|
||||
call_servers_connected = false;
|
||||
call_servers_disconnected = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Game product identifier. This is currently used by the master server for version checking purposes.
|
||||
/// It's a required field, but will eventually will go away, and the AppID will be used for this purpose.
|
||||
void Steam_GameServer::SetProduct( const char *pszProduct )
|
||||
{
|
||||
PRINT_DEBUG("SetProduct\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_product(pszProduct);
|
||||
}
|
||||
|
||||
|
||||
/// Description of the game. This is a required field and is displayed in the steam server browser....for now.
|
||||
/// This is a required field, but it will go away eventually, as the data should be determined from the AppID.
|
||||
void Steam_GameServer::SetGameDescription( const char *pszGameDescription )
|
||||
{
|
||||
PRINT_DEBUG("SetGameDescription\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_game_description(pszGameDescription);
|
||||
}
|
||||
|
||||
|
||||
/// If your game is a "mod," pass the string that identifies it. The default is an empty string, meaning
|
||||
/// this application is the original game, not a mod.
|
||||
///
|
||||
/// @see k_cbMaxGameServerGameDir
|
||||
void Steam_GameServer::SetModDir( const char *pszModDir )
|
||||
{
|
||||
PRINT_DEBUG("SetModDir\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_mod_dir(pszModDir);
|
||||
}
|
||||
|
||||
|
||||
/// Is this is a dedicated server? The default value is false.
|
||||
void Steam_GameServer::SetDedicatedServer( bool bDedicated )
|
||||
{
|
||||
PRINT_DEBUG("SetDedicatedServer\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_dedicated_server(bDedicated);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Login
|
||||
//
|
||||
|
||||
/// Begin process to login to a persistent game server account
|
||||
///
|
||||
/// You need to register for callbacks to determine the result of this operation.
|
||||
/// @see SteamServersConnected_t
|
||||
/// @see SteamServerConnectFailure_t
|
||||
/// @see SteamServersDisconnected_t
|
||||
void Steam_GameServer::LogOn( const char *pszToken )
|
||||
{
|
||||
PRINT_DEBUG("LogOn %s\n", pszToken);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
call_servers_connected = true;
|
||||
logged_in = true;
|
||||
}
|
||||
|
||||
void Steam_GameServer::LogOn(
|
||||
const char *pszAccountName,
|
||||
const char *pszPassword
|
||||
)
|
||||
{
|
||||
PRINT_DEBUG("LogOn %s %s\n", pszAccountName, pszPassword);
|
||||
LogOn(pszAccountName);
|
||||
}
|
||||
|
||||
/// Login to a generic, anonymous account.
|
||||
///
|
||||
/// Note: in previous versions of the SDK, this was automatically called within SteamGameServer_Init,
|
||||
/// but this is no longer the case.
|
||||
void Steam_GameServer::LogOnAnonymous()
|
||||
{
|
||||
PRINT_DEBUG("LogOnAnonymous\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
call_servers_connected = true;
|
||||
logged_in = true;
|
||||
}
|
||||
|
||||
void Steam_GameServer::LogOn()
|
||||
{
|
||||
PRINT_DEBUG("LogOn\n");
|
||||
LogOnAnonymous();
|
||||
}
|
||||
|
||||
/// Begin process of logging game server out of steam
|
||||
void Steam_GameServer::LogOff()
|
||||
{
|
||||
PRINT_DEBUG("LogOff\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (logged_in) {
|
||||
call_servers_disconnected = true;
|
||||
}
|
||||
|
||||
logged_in = false;
|
||||
}
|
||||
|
||||
|
||||
// status functions
|
||||
bool Steam_GameServer::BLoggedOn()
|
||||
{
|
||||
PRINT_DEBUG("BLoggedOn\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return logged_in;
|
||||
}
|
||||
|
||||
bool Steam_GameServer::BSecure()
|
||||
{
|
||||
PRINT_DEBUG("BSecure\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (!policy_response_called) return false;
|
||||
return !!(flags & k_unServerFlagSecure);
|
||||
}
|
||||
|
||||
CSteamID Steam_GameServer::GetSteamID()
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServer::GetSteamID\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (!logged_in) return k_steamIDNil;
|
||||
return settings->get_local_steam_id();
|
||||
}
|
||||
|
||||
|
||||
/// Returns true if the master server has requested a restart.
|
||||
/// Only returns true once per request.
|
||||
bool Steam_GameServer::WasRestartRequested()
|
||||
{
|
||||
PRINT_DEBUG("WasRestartRequested\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Server state. These properties may be changed at any time.
|
||||
//
|
||||
|
||||
/// Max player count that will be reported to server browser and client queries
|
||||
void Steam_GameServer::SetMaxPlayerCount( int cPlayersMax )
|
||||
{
|
||||
PRINT_DEBUG("SetMaxPlayerCount\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_max_player_count(cPlayersMax);
|
||||
}
|
||||
|
||||
|
||||
/// Number of bots. Default value is zero
|
||||
void Steam_GameServer::SetBotPlayerCount( int cBotplayers )
|
||||
{
|
||||
PRINT_DEBUG("SetBotPlayerCount\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_bot_player_count(cBotplayers);
|
||||
}
|
||||
|
||||
|
||||
/// Set the name of server as it will appear in the server browser
|
||||
///
|
||||
/// @see k_cbMaxGameServerName
|
||||
void Steam_GameServer::SetServerName( const char *pszServerName )
|
||||
{
|
||||
PRINT_DEBUG("SetServerName\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_server_name(pszServerName);
|
||||
}
|
||||
|
||||
|
||||
/// Set name of map to report in the server browser
|
||||
///
|
||||
/// @see k_cbMaxGameServerName
|
||||
void Steam_GameServer::SetMapName( const char *pszMapName )
|
||||
{
|
||||
PRINT_DEBUG("SetMapName\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_map_name(pszMapName);
|
||||
}
|
||||
|
||||
|
||||
/// Let people know if your server will require a password
|
||||
void Steam_GameServer::SetPasswordProtected( bool bPasswordProtected )
|
||||
{
|
||||
PRINT_DEBUG("SetPasswordProtected\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_password_protected(bPasswordProtected);
|
||||
}
|
||||
|
||||
|
||||
/// Spectator server. The default value is zero, meaning the service
|
||||
/// is not used.
|
||||
void Steam_GameServer::SetSpectatorPort( uint16 unSpectatorPort )
|
||||
{
|
||||
PRINT_DEBUG("SetSpectatorPort\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_spectator_port(unSpectatorPort);
|
||||
}
|
||||
|
||||
|
||||
/// Name of the spectator server. (Only used if spectator port is nonzero.)
|
||||
///
|
||||
/// @see k_cbMaxGameServerMapName
|
||||
void Steam_GameServer::SetSpectatorServerName( const char *pszSpectatorServerName )
|
||||
{
|
||||
PRINT_DEBUG("SetSpectatorServerName\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_spectator_server_name(pszSpectatorServerName);
|
||||
}
|
||||
|
||||
|
||||
/// Call this to clear the whole list of key/values that are sent in rules queries.
|
||||
void Steam_GameServer::ClearAllKeyValues()
|
||||
{
|
||||
PRINT_DEBUG("ClearAllKeyValues\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.clear_values();
|
||||
}
|
||||
|
||||
|
||||
/// Call this to add/update a key/value pair.
|
||||
void Steam_GameServer::SetKeyValue( const char *pKey, const char *pValue )
|
||||
{
|
||||
PRINT_DEBUG("SetKeyValue %s %s\n", pKey, pValue);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
(*server_data.mutable_values())[std::string(pKey)] = std::string(pValue);
|
||||
}
|
||||
|
||||
|
||||
/// Sets a string defining the "gametags" for this server, this is optional, but if it is set
|
||||
/// it allows users to filter in the matchmaking/server-browser interfaces based on the value
|
||||
///
|
||||
/// @see k_cbMaxGameServerTags
|
||||
void Steam_GameServer::SetGameTags( const char *pchGameTags )
|
||||
{
|
||||
PRINT_DEBUG("SetGameTags\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_tags(pchGameTags);
|
||||
}
|
||||
|
||||
|
||||
/// Sets a string defining the "gamedata" for this server, this is optional, but if it is set
|
||||
/// it allows users to filter in the matchmaking/server-browser interfaces based on the value
|
||||
/// don't set this unless it actually changes, its only uploaded to the master once (when
|
||||
/// acknowledged)
|
||||
///
|
||||
/// @see k_cbMaxGameServerGameData
|
||||
void Steam_GameServer::SetGameData( const char *pchGameData )
|
||||
{
|
||||
PRINT_DEBUG("SetGameData\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_gamedata(pchGameData);
|
||||
}
|
||||
|
||||
|
||||
/// Region identifier. This is an optional field, the default value is empty, meaning the "world" region
|
||||
void Steam_GameServer::SetRegion( const char *pszRegion )
|
||||
{
|
||||
PRINT_DEBUG("SetRegion\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_region(pszRegion);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Player list management / authentication
|
||||
//
|
||||
|
||||
// Handles receiving a new connection from a Steam user. This call will ask the Steam
|
||||
// servers to validate the users identity, app ownership, and VAC status. If the Steam servers
|
||||
// are off-line, then it will validate the cached ticket itself which will validate app ownership
|
||||
// and identity. The AuthBlob here should be acquired on the game client using SteamUser()->InitiateGameConnection()
|
||||
// and must then be sent up to the game server for authentication.
|
||||
//
|
||||
// Return Value: returns true if the users ticket passes basic checks. pSteamIDUser will contain the Steam ID of this user. pSteamIDUser must NOT be NULL
|
||||
// If the call succeeds then you should expect a GSClientApprove_t or GSClientDeny_t callback which will tell you whether authentication
|
||||
// for the user has succeeded or failed (the steamid in the callback will match the one returned by this call)
|
||||
bool Steam_GameServer::SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser )
|
||||
{
|
||||
PRINT_DEBUG("SendUserConnectAndAuthenticate %u %u\n", unIPClient, cubAuthBlobSize);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
return ticket_manager->SendUserConnectAndAuthenticate(unIPClient, pvAuthBlob, cubAuthBlobSize, pSteamIDUser);
|
||||
}
|
||||
|
||||
void Steam_GameServer::SendUserConnectAndAuthenticate( CSteamID steamIDUser, uint32 unIPClient, void *pvAuthBlob, uint32 cubAuthBlobSize )
|
||||
{
|
||||
SendUserConnectAndAuthenticate(unIPClient, pvAuthBlob, cubAuthBlobSize, NULL);
|
||||
}
|
||||
|
||||
// Creates a fake user (ie, a bot) which will be listed as playing on the server, but skips validation.
|
||||
//
|
||||
// Return Value: Returns a SteamID for the user to be tracked with, you should call HandleUserDisconnect()
|
||||
// when this user leaves the server just like you would for a real user.
|
||||
CSteamID Steam_GameServer::CreateUnauthenticatedUserConnection()
|
||||
{
|
||||
PRINT_DEBUG("CreateUnauthenticatedUserConnection\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
return ticket_manager->fakeUser();
|
||||
}
|
||||
|
||||
|
||||
// Should be called whenever a user leaves our game server, this lets Steam internally
|
||||
// track which users are currently on which servers for the purposes of preventing a single
|
||||
// account being logged into multiple servers, showing who is currently on a server, etc.
|
||||
void Steam_GameServer::SendUserDisconnect( CSteamID steamIDUser )
|
||||
{
|
||||
PRINT_DEBUG("SendUserDisconnect\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
ticket_manager->endAuth(steamIDUser);
|
||||
}
|
||||
|
||||
|
||||
// Update the data to be displayed in the server browser and matchmaking interfaces for a user
|
||||
// currently connected to the server. For regular users you must call this after you receive a
|
||||
// GSUserValidationSuccess callback.
|
||||
//
|
||||
// Return Value: true if successful, false if failure (ie, steamIDUser wasn't for an active player)
|
||||
bool Steam_GameServer::BUpdateUserData( CSteamID steamIDUser, const char *pchPlayerName, uint32 uScore )
|
||||
{
|
||||
PRINT_DEBUG("BUpdateUserData %llu %s %u\n", steamIDUser.ConvertToUint64(), pchPlayerName, uScore);
|
||||
return true;
|
||||
}
|
||||
|
||||
// You shouldn't need to call this as it is called internally by SteamGameServer_Init() and can only be called once.
|
||||
//
|
||||
// To update the data in this call which may change during the servers lifetime see UpdateServerStatus() below.
|
||||
//
|
||||
// Input: nGameAppID - The Steam assigned AppID for the game
|
||||
// unServerFlags - Any applicable combination of flags (see k_unServerFlag____ constants below)
|
||||
// unGameIP - The IP Address the server is listening for client connections on (might be INADDR_ANY)
|
||||
// unGamePort - The port which the server is listening for client connections on
|
||||
// unSpectatorPort - the port on which spectators can join to observe the server, 0 if spectating is not supported
|
||||
// usQueryPort - The port which the ISteamMasterServerUpdater API should use in order to listen for matchmaking requests
|
||||
// pchGameDir - A unique string identifier for your game
|
||||
// pchVersion - The current version of the server as a string like 1.0.0.0
|
||||
// bLanMode - Is this a LAN only server?
|
||||
//
|
||||
// bugbug jmccaskey - figure out how to remove this from the API and only expose via SteamGameServer_Init... or make this actually used,
|
||||
// and stop calling it in SteamGameServer_Init()?
|
||||
bool Steam_GameServer::BSetServerType( uint32 unServerFlags, uint32 unGameIP, uint16 unGamePort,
|
||||
uint16 unSpectatorPort, uint16 usQueryPort, const char *pchGameDir, const char *pchVersion, bool bLANMode )
|
||||
{
|
||||
PRINT_DEBUG("BSetServerType\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_ip(unGameIP);
|
||||
server_data.set_port(unGamePort);
|
||||
server_data.set_query_port(usQueryPort);
|
||||
server_data.set_spectator_port(unSpectatorPort);
|
||||
|
||||
std::string version(pchVersion);
|
||||
version.erase(std::remove(version.begin(), version.end(), ' '), version.end());
|
||||
version.erase(std::remove(version.begin(), version.end(), '.'), version.end());
|
||||
server_data.set_version(stoi(version));
|
||||
flags = unServerFlags;
|
||||
|
||||
//TODO?
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Steam_GameServer::BSetServerType( int32 nGameAppId, uint32 unServerFlags, uint32 unGameIP, uint16 unGamePort,
|
||||
uint16 unSpectatorPort, uint16 usQueryPort, const char *pchGameDir, const char *pchVersion, bool bLANMode )
|
||||
{
|
||||
return BSetServerType(unServerFlags, unGameIP, unGamePort, unSpectatorPort, usQueryPort, pchGameDir, pchVersion, bLANMode);
|
||||
}
|
||||
|
||||
// Updates server status values which shows up in the server browser and matchmaking APIs
|
||||
void Steam_GameServer::UpdateServerStatus( int cPlayers, int cPlayersMax, int cBotPlayers,
|
||||
const char *pchServerName, const char *pSpectatorServerName,
|
||||
const char *pchMapName )
|
||||
{
|
||||
PRINT_DEBUG("UpdateServerStatus\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
server_data.set_num_players(cPlayers);
|
||||
server_data.set_max_player_count(cPlayersMax);
|
||||
server_data.set_bot_player_count(cBotPlayers);
|
||||
server_data.set_server_name(pchServerName);
|
||||
server_data.set_spectator_server_name(pSpectatorServerName);
|
||||
server_data.set_map_name(pchMapName);
|
||||
}
|
||||
|
||||
// This can be called if spectator goes away or comes back (passing 0 means there is no spectator server now).
|
||||
void Steam_GameServer::UpdateSpectatorPort( uint16 unSpectatorPort )
|
||||
{
|
||||
PRINT_DEBUG("UpdateSpectatorPort\n");
|
||||
SetSpectatorPort(unSpectatorPort);
|
||||
}
|
||||
|
||||
// Sets a string defining the "gametype" for this server, this is optional, but if it is set
|
||||
// it allows users to filter in the matchmaking/server-browser interfaces based on the value
|
||||
void Steam_GameServer::SetGameType( const char *pchGameType )
|
||||
{
|
||||
PRINT_DEBUG("SetGameType\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
}
|
||||
|
||||
// Ask if a user has a specific achievement for this game, will get a callback on reply
|
||||
bool Steam_GameServer::BGetUserAchievementStatus( CSteamID steamID, const char *pchAchievementName )
|
||||
{
|
||||
PRINT_DEBUG("BGetUserAchievementStatus\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// New auth system APIs - do not mix with the old auth system APIs.
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
// Retrieve ticket to be sent to the entity who wishes to authenticate you ( using BeginAuthSession API ).
|
||||
// pcbTicket retrieves the length of the actual ticket.
|
||||
HAuthTicket Steam_GameServer::GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServer::GetAuthSessionTicket\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
return ticket_manager->getTicket(pTicket, cbMaxTicket, pcbTicket);
|
||||
}
|
||||
|
||||
|
||||
// Authenticate ticket ( from GetAuthSessionTicket ) from entity steamID to be sure it is valid and isnt reused
|
||||
// Registers for callbacks if the entity goes offline or cancels the ticket ( see ValidateAuthTicketResponse_t callback and EAuthSessionResponse )
|
||||
EBeginAuthSessionResult Steam_GameServer::BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServer::BeginAuthSession %i %llu\n", cbAuthTicket, steamID.ConvertToUint64());
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
return ticket_manager->beginAuth(pAuthTicket, cbAuthTicket, steamID );
|
||||
}
|
||||
|
||||
|
||||
// Stop tracking started by BeginAuthSession - called when no longer playing game with this entity
|
||||
void Steam_GameServer::EndAuthSession( CSteamID steamID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServer::EndAuthSession %llu\n", steamID.ConvertToUint64());
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
ticket_manager->endAuth(steamID);
|
||||
}
|
||||
|
||||
|
||||
// Cancel auth ticket from GetAuthSessionTicket, called when no longer playing game with the entity you gave the ticket to
|
||||
void Steam_GameServer::CancelAuthTicket( HAuthTicket hAuthTicket )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServer::CancelAuthTicket\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
ticket_manager->cancelTicket(hAuthTicket);
|
||||
}
|
||||
|
||||
|
||||
// After receiving a user's authentication data, and passing it to SendUserConnectAndAuthenticate, use this function
|
||||
// to determine if the user owns downloadable content specified by the provided AppID.
|
||||
EUserHasLicenseForAppResult Steam_GameServer::UserHasLicenseForApp( CSteamID steamID, AppId_t appID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GameServer::UserHasLicenseForApp\n");
|
||||
return k_EUserHasLicenseResultHasLicense;
|
||||
}
|
||||
|
||||
|
||||
// Ask if a user in in the specified group, results returns async by GSUserGroupStatus_t
|
||||
// returns false if we're not connected to the steam servers and thus cannot ask
|
||||
bool Steam_GameServer::RequestUserGroupStatus( CSteamID steamIDUser, CSteamID steamIDGroup )
|
||||
{
|
||||
PRINT_DEBUG("RequestUserGroupStatus\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// these two functions s are deprecated, and will not return results
|
||||
// they will be removed in a future version of the SDK
|
||||
void Steam_GameServer::GetGameplayStats( )
|
||||
{
|
||||
PRINT_DEBUG("GetGameplayStats\n");
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( GSReputation_t )
|
||||
SteamAPICall_t Steam_GameServer::GetServerReputation()
|
||||
{
|
||||
PRINT_DEBUG("GetServerReputation\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Returns the public IP of the server according to Steam, useful when the server is
|
||||
// behind NAT and you want to advertise its IP in a lobby for other clients to directly
|
||||
// connect to
|
||||
uint32 Steam_GameServer::GetPublicIP_old()
|
||||
{
|
||||
PRINT_DEBUG("GetPublicIP_old\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
uint32 ip = network->getOwnIP();
|
||||
PRINT_DEBUG("%X\n", ip);
|
||||
return ip;
|
||||
}
|
||||
|
||||
SteamIPAddress_t Steam_GameServer::GetPublicIP()
|
||||
{
|
||||
PRINT_DEBUG("GetPublicIP\n");
|
||||
SteamIPAddress_t ip = SteamIPAddress_t::IPv4Any();
|
||||
ip.m_unIPv4 = GetPublicIP_old();
|
||||
return ip;
|
||||
}
|
||||
|
||||
void Steam_GameServer::GetPublicIP_fix(SteamIPAddress_t *out)
|
||||
{
|
||||
PRINT_DEBUG("GetPublicIP_fix\n");
|
||||
if (out) *out = GetPublicIP();
|
||||
}
|
||||
|
||||
// These are in GameSocketShare mode, where instead of ISteamGameServer creating its own
|
||||
// socket to talk to the master server on, it lets the game use its socket to forward messages
|
||||
// back and forth. This prevents us from requiring server ops to open up yet another port
|
||||
// in their firewalls.
|
||||
//
|
||||
// the IP address and port should be in host order, i.e 127.0.0.1 == 0x7f000001
|
||||
|
||||
// These are used when you've elected to multiplex the game server's UDP socket
|
||||
// rather than having the master server updater use its own sockets.
|
||||
//
|
||||
// Source games use this to simplify the job of the server admins, so they
|
||||
// don't have to open up more ports on their firewalls.
|
||||
|
||||
// Call this when a packet that starts with 0xFFFFFFFF comes in. That means
|
||||
// it's for us.
|
||||
bool Steam_GameServer::HandleIncomingPacket( const void *pData, int cbData, uint32 srcIP, uint16 srcPort )
|
||||
{
|
||||
PRINT_DEBUG("HandleIncomingPacket %i %X %i\n", cbData, srcIP, srcPort);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// AFTER calling HandleIncomingPacket for any packets that came in that frame, call this.
|
||||
// This gets a packet that the master server updater needs to send out on UDP.
|
||||
// It returns the length of the packet it wants to send, or 0 if there are no more packets to send.
|
||||
// Call this each frame until it returns 0.
|
||||
int Steam_GameServer::GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *pNetAdr, uint16 *pPort )
|
||||
{
|
||||
PRINT_DEBUG("GetNextOutgoingPacket\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (outgoing_packets.size() == 0) return 0;
|
||||
|
||||
if (outgoing_packets.back().data.size() < cbMaxOut) cbMaxOut = outgoing_packets.back().data.size();
|
||||
if (pOut) memcpy(pOut, outgoing_packets.back().data.data(), cbMaxOut);
|
||||
if (pNetAdr) *pNetAdr = outgoing_packets.back().ip;
|
||||
if (pPort) *pPort = outgoing_packets.back().port;
|
||||
outgoing_packets.pop_back();
|
||||
return cbMaxOut;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Control heartbeats / advertisement with master server
|
||||
//
|
||||
|
||||
// Call this as often as you like to tell the master server updater whether or not
|
||||
// you want it to be active (default: off).
|
||||
void Steam_GameServer::EnableHeartbeats( bool bActive )
|
||||
{
|
||||
PRINT_DEBUG("EnableHeartbeats\n");
|
||||
}
|
||||
|
||||
/// Indicate whether you wish to be listed on the master server list
|
||||
/// and/or respond to server browser / LAN discovery packets.
|
||||
/// The server starts with this value set to false. You should set all
|
||||
/// relevant server parameters before enabling advertisement on the server.
|
||||
///
|
||||
/// (This function used to be named EnableHeartbeats, so if you are wondering
|
||||
/// where that function went, it's right here. It does the same thing as before,
|
||||
/// the old name was just confusing.)
|
||||
void Steam_GameServer::SetAdvertiseServerActive( bool bActive )
|
||||
{
|
||||
PRINT_DEBUG("SetAdvertiseServerActive\n");
|
||||
EnableHeartbeats(bActive);
|
||||
}
|
||||
|
||||
// You usually don't need to modify this.
|
||||
// Pass -1 to use the default value for iHeartbeatInterval.
|
||||
// Some mods change this.
|
||||
void Steam_GameServer::SetHeartbeatInterval( int iHeartbeatInterval )
|
||||
{
|
||||
PRINT_DEBUG("SetHeartbeatInterval\n");
|
||||
}
|
||||
|
||||
void Steam_GameServer::SetMasterServerHeartbeatInterval_DEPRECATED( int iHeartbeatInterval )
|
||||
{
|
||||
PRINT_DEBUG("SetMasterServerHeartbeatInterval_DEPRECATED\n");
|
||||
}
|
||||
|
||||
|
||||
// Force a heartbeat to steam at the next opportunity
|
||||
void Steam_GameServer::ForceHeartbeat()
|
||||
{
|
||||
PRINT_DEBUG("ForceHeartbeat\n");
|
||||
}
|
||||
|
||||
void Steam_GameServer::ForceMasterServerHeartbeat_DEPRECATED()
|
||||
{
|
||||
PRINT_DEBUG("ForceMasterServerHeartbeat_DEPRECATED\n");
|
||||
}
|
||||
|
||||
|
||||
// associate this game server with this clan for the purposes of computing player compat
|
||||
STEAM_CALL_RESULT( AssociateWithClanResult_t )
|
||||
SteamAPICall_t Steam_GameServer::AssociateWithClan( CSteamID steamIDClan )
|
||||
{
|
||||
PRINT_DEBUG("AssociateWithClan\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ask if any of the current players dont want to play with this new player - or vice versa
|
||||
STEAM_CALL_RESULT( ComputeNewPlayerCompatibilityResult_t )
|
||||
SteamAPICall_t Steam_GameServer::ComputeNewPlayerCompatibility( CSteamID steamIDNewPlayer )
|
||||
{
|
||||
PRINT_DEBUG("ComputeNewPlayerCompatibility\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Steam_GameServer::RunCallbacks()
|
||||
{
|
||||
bool temp_call_servers_connected = call_servers_connected;
|
||||
bool temp_call_servers_disconnected = call_servers_disconnected;
|
||||
call_servers_disconnected = call_servers_connected = false;
|
||||
|
||||
if (temp_call_servers_connected) {
|
||||
PRINT_DEBUG("Steam_GameServer::SteamServersConnected_t\n");
|
||||
SteamServersConnected_t data;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
|
||||
}
|
||||
|
||||
if (logged_in && !policy_response_called) {
|
||||
PRINT_DEBUG("Steam_GameServer::GSPolicyResponse_t\n");
|
||||
GSPolicyResponse_t data;
|
||||
data.m_bSecure = !!(flags & k_unServerFlagSecure);
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.11);
|
||||
policy_response_called = true;
|
||||
}
|
||||
|
||||
if (logged_in && check_timedout(last_sent_server_info, SEND_SERVER_RATE)) {
|
||||
PRINT_DEBUG("Steam_GameServer Sending Gameserver\n");
|
||||
Common_Message msg;
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
server_data.set_appid(settings->get_local_game_id().AppID());
|
||||
msg.set_allocated_gameserver(new Gameserver(server_data));
|
||||
msg.mutable_gameserver()->set_num_players(ticket_manager->countInboundAuth());
|
||||
network->sendToAllIndividuals(&msg, true);
|
||||
last_sent_server_info = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
if (temp_call_servers_disconnected) {
|
||||
SteamServersDisconnected_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
|
||||
if (!logged_in) {
|
||||
Common_Message msg;
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_allocated_gameserver(new Gameserver(server_data));
|
||||
msg.mutable_gameserver()->set_offline(true);
|
||||
network->sendToAllIndividuals(&msg, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
351
all/steam_gameserver.h
Normal file
351
all/steam_gameserver.h
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Functions for authenticating users via Steam to play on a game server
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct Gameserver_Outgoing_Packet {
|
||||
std::string data;
|
||||
|
||||
uint32 ip;
|
||||
uint16 port;
|
||||
};
|
||||
|
||||
class Steam_GameServer :
|
||||
public ISteamGameServer004,
|
||||
public ISteamGameServer005,
|
||||
public ISteamGameServer008,
|
||||
public ISteamGameServer009,
|
||||
public ISteamGameServer010,
|
||||
public ISteamGameServer011,
|
||||
public ISteamGameServer012,
|
||||
public ISteamGameServer013,
|
||||
public ISteamGameServer
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallBacks *callbacks;
|
||||
|
||||
CSteamID steam_id;
|
||||
|
||||
bool call_servers_connected = false;
|
||||
bool logged_in = false;
|
||||
bool call_servers_disconnected = false;
|
||||
Gameserver server_data;
|
||||
|
||||
uint32 flags;
|
||||
bool policy_response_called;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point last_sent_server_info;
|
||||
Auth_Ticket_Manager *ticket_manager;
|
||||
|
||||
std::vector<struct Gameserver_Outgoing_Packet> outgoing_packets;
|
||||
public:
|
||||
|
||||
Steam_GameServer(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks);
|
||||
~Steam_GameServer();
|
||||
//
|
||||
// Basic server data. These properties, if set, must be set before before calling LogOn. They
|
||||
// may not be changed after logged in.
|
||||
//
|
||||
|
||||
/// This is called by SteamGameServer_Init, and you will usually not need to call it directly
|
||||
bool InitGameServer( uint32 unIP, uint16 usGamePort, uint16 usQueryPort, uint32 unFlags, AppId_t nGameAppId, const char *pchVersionString );
|
||||
|
||||
/// Game product identifier. This is currently used by the master server for version checking purposes.
|
||||
/// It's a required field, but will eventually will go away, and the AppID will be used for this purpose.
|
||||
void SetProduct( const char *pszProduct );
|
||||
|
||||
/// Description of the game. This is a required field and is displayed in the steam server browser....for now.
|
||||
/// This is a required field, but it will go away eventually, as the data should be determined from the AppID.
|
||||
void SetGameDescription( const char *pszGameDescription );
|
||||
|
||||
/// If your game is a "mod," pass the string that identifies it. The default is an empty string, meaning
|
||||
/// this application is the original game, not a mod.
|
||||
///
|
||||
/// @see k_cbMaxGameServerGameDir
|
||||
void SetModDir( const char *pszModDir );
|
||||
|
||||
/// Is this is a dedicated server? The default value is false.
|
||||
void SetDedicatedServer( bool bDedicated );
|
||||
|
||||
//
|
||||
// Login
|
||||
//
|
||||
|
||||
/// Begin process to login to a persistent game server account
|
||||
///
|
||||
/// You need to register for callbacks to determine the result of this operation.
|
||||
/// @see SteamServersConnected_t
|
||||
/// @see SteamServerConnectFailure_t
|
||||
/// @see SteamServersDisconnected_t
|
||||
void LogOn( const char *pszToken );
|
||||
void LogOn(
|
||||
const char *pszAccountName,
|
||||
const char *pszPassword
|
||||
);
|
||||
void LogOn();
|
||||
|
||||
/// Login to a generic, anonymous account.
|
||||
///
|
||||
/// Note: in previous versions of the SDK, this was automatically called within SteamGameServer_Init,
|
||||
/// but this is no longer the case.
|
||||
void LogOnAnonymous();
|
||||
|
||||
/// Begin process of logging game server out of steam
|
||||
void LogOff();
|
||||
|
||||
// status functions
|
||||
bool BLoggedOn();
|
||||
bool BSecure();
|
||||
CSteamID GetSteamID();
|
||||
|
||||
/// Returns true if the master server has requested a restart.
|
||||
/// Only returns true once per request.
|
||||
bool WasRestartRequested();
|
||||
|
||||
//
|
||||
// Server state. These properties may be changed at any time.
|
||||
//
|
||||
|
||||
/// Max player count that will be reported to server browser and client queries
|
||||
void SetMaxPlayerCount( int cPlayersMax );
|
||||
|
||||
/// Number of bots. Default value is zero
|
||||
void SetBotPlayerCount( int cBotplayers );
|
||||
|
||||
/// Set the name of server as it will appear in the server browser
|
||||
///
|
||||
/// @see k_cbMaxGameServerName
|
||||
void SetServerName( const char *pszServerName );
|
||||
|
||||
/// Set name of map to report in the server browser
|
||||
///
|
||||
/// @see k_cbMaxGameServerName
|
||||
void SetMapName( const char *pszMapName );
|
||||
|
||||
/// Let people know if your server will require a password
|
||||
void SetPasswordProtected( bool bPasswordProtected );
|
||||
|
||||
/// Spectator server. The default value is zero, meaning the service
|
||||
/// is not used.
|
||||
void SetSpectatorPort( uint16 unSpectatorPort );
|
||||
|
||||
/// Name of the spectator server. (Only used if spectator port is nonzero.)
|
||||
///
|
||||
/// @see k_cbMaxGameServerMapName
|
||||
void SetSpectatorServerName( const char *pszSpectatorServerName );
|
||||
|
||||
/// Call this to clear the whole list of key/values that are sent in rules queries.
|
||||
void ClearAllKeyValues();
|
||||
|
||||
/// Call this to add/update a key/value pair.
|
||||
void SetKeyValue( const char *pKey, const char *pValue );
|
||||
|
||||
/// Sets a string defining the "gametags" for this server, this is optional, but if it is set
|
||||
/// it allows users to filter in the matchmaking/server-browser interfaces based on the value
|
||||
///
|
||||
/// @see k_cbMaxGameServerTags
|
||||
void SetGameTags( const char *pchGameTags );
|
||||
|
||||
/// Sets a string defining the "gamedata" for this server, this is optional, but if it is set
|
||||
/// it allows users to filter in the matchmaking/server-browser interfaces based on the value
|
||||
/// don't set this unless it actually changes, its only uploaded to the master once (when
|
||||
/// acknowledged)
|
||||
///
|
||||
/// @see k_cbMaxGameServerGameData
|
||||
void SetGameData( const char *pchGameData );
|
||||
|
||||
/// Region identifier. This is an optional field, the default value is empty, meaning the "world" region
|
||||
void SetRegion( const char *pszRegion );
|
||||
|
||||
//
|
||||
// Player list management / authentication
|
||||
//
|
||||
|
||||
// Handles receiving a new connection from a Steam user. This call will ask the Steam
|
||||
// servers to validate the users identity, app ownership, and VAC status. If the Steam servers
|
||||
// are off-line, then it will validate the cached ticket itself which will validate app ownership
|
||||
// and identity. The AuthBlob here should be acquired on the game client using SteamUser()->InitiateGameConnection()
|
||||
// and must then be sent up to the game server for authentication.
|
||||
//
|
||||
// Return Value: returns true if the users ticket passes basic checks. pSteamIDUser will contain the Steam ID of this user. pSteamIDUser must NOT be NULL
|
||||
// If the call succeeds then you should expect a GSClientApprove_t or GSClientDeny_t callback which will tell you whether authentication
|
||||
// for the user has succeeded or failed (the steamid in the callback will match the one returned by this call)
|
||||
bool SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser );
|
||||
void SendUserConnectAndAuthenticate( CSteamID steamIDUser, uint32 unIPClient, void *pvAuthBlob, uint32 cubAuthBlobSize );
|
||||
|
||||
// Creates a fake user (ie, a bot) which will be listed as playing on the server, but skips validation.
|
||||
//
|
||||
// Return Value: Returns a SteamID for the user to be tracked with, you should call HandleUserDisconnect()
|
||||
// when this user leaves the server just like you would for a real user.
|
||||
CSteamID CreateUnauthenticatedUserConnection();
|
||||
|
||||
// Should be called whenever a user leaves our game server, this lets Steam internally
|
||||
// track which users are currently on which servers for the purposes of preventing a single
|
||||
// account being logged into multiple servers, showing who is currently on a server, etc.
|
||||
void SendUserDisconnect( CSteamID steamIDUser );
|
||||
|
||||
// Update the data to be displayed in the server browser and matchmaking interfaces for a user
|
||||
// currently connected to the server. For regular users you must call this after you receive a
|
||||
// GSUserValidationSuccess callback.
|
||||
//
|
||||
// Return Value: true if successful, false if failure (ie, steamIDUser wasn't for an active player)
|
||||
bool BUpdateUserData( CSteamID steamIDUser, const char *pchPlayerName, uint32 uScore );
|
||||
|
||||
// You shouldn't need to call this as it is called internally by SteamGameServer_Init() and can only be called once.
|
||||
//
|
||||
// To update the data in this call which may change during the servers lifetime see UpdateServerStatus() below.
|
||||
//
|
||||
// Input: nGameAppID - The Steam assigned AppID for the game
|
||||
// unServerFlags - Any applicable combination of flags (see k_unServerFlag____ constants below)
|
||||
// unGameIP - The IP Address the server is listening for client connections on (might be INADDR_ANY)
|
||||
// unGamePort - The port which the server is listening for client connections on
|
||||
// unSpectatorPort - the port on which spectators can join to observe the server, 0 if spectating is not supported
|
||||
// usQueryPort - The port which the ISteamMasterServerUpdater API should use in order to listen for matchmaking requests
|
||||
// pchGameDir - A unique string identifier for your game
|
||||
// pchVersion - The current version of the server as a string like 1.0.0.0
|
||||
// bLanMode - Is this a LAN only server?
|
||||
//
|
||||
// bugbug jmccaskey - figure out how to remove this from the API and only expose via SteamGameServer_Init... or make this actually used,
|
||||
// and stop calling it in SteamGameServer_Init()?
|
||||
bool BSetServerType( uint32 unServerFlags, uint32 unGameIP, uint16 unGamePort,
|
||||
uint16 unSpectatorPort, uint16 usQueryPort, const char *pchGameDir, const char *pchVersion, bool bLANMode );
|
||||
|
||||
bool BSetServerType( int32 nGameAppId, uint32 unServerFlags, uint32 unGameIP, uint16 unGamePort,
|
||||
uint16 unSpectatorPort, uint16 usQueryPort, const char *pchGameDir, const char *pchVersion, bool bLANMode );
|
||||
|
||||
// Updates server status values which shows up in the server browser and matchmaking APIs
|
||||
void UpdateServerStatus( int cPlayers, int cPlayersMax, int cBotPlayers,
|
||||
const char *pchServerName, const char *pSpectatorServerName,
|
||||
const char *pchMapName );
|
||||
|
||||
// This can be called if spectator goes away or comes back (passing 0 means there is no spectator server now).
|
||||
void UpdateSpectatorPort( uint16 unSpectatorPort );
|
||||
|
||||
// Sets a string defining the "gametype" for this server, this is optional, but if it is set
|
||||
// it allows users to filter in the matchmaking/server-browser interfaces based on the value
|
||||
void SetGameType( const char *pchGameType );
|
||||
|
||||
// Ask if a user has a specific achievement for this game, will get a callback on reply
|
||||
bool BGetUserAchievementStatus( CSteamID steamID, const char *pchAchievementName );
|
||||
|
||||
// New auth system APIs - do not mix with the old auth system APIs.
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
// Retrieve ticket to be sent to the entity who wishes to authenticate you ( using BeginAuthSession API ).
|
||||
// pcbTicket retrieves the length of the actual ticket.
|
||||
HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket );
|
||||
|
||||
// Authenticate ticket ( from GetAuthSessionTicket ) from entity steamID to be sure it is valid and isnt reused
|
||||
// Registers for callbacks if the entity goes offline or cancels the ticket ( see ValidateAuthTicketResponse_t callback and EAuthSessionResponse )
|
||||
EBeginAuthSessionResult BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID );
|
||||
|
||||
// Stop tracking started by BeginAuthSession - called when no longer playing game with this entity
|
||||
void EndAuthSession( CSteamID steamID );
|
||||
|
||||
// Cancel auth ticket from GetAuthSessionTicket, called when no longer playing game with the entity you gave the ticket to
|
||||
void CancelAuthTicket( HAuthTicket hAuthTicket );
|
||||
|
||||
// After receiving a user's authentication data, and passing it to SendUserConnectAndAuthenticate, use this function
|
||||
// to determine if the user owns downloadable content specified by the provided AppID.
|
||||
EUserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID );
|
||||
|
||||
// Ask if a user in in the specified group, results returns async by GSUserGroupStatus_t
|
||||
// returns false if we're not connected to the steam servers and thus cannot ask
|
||||
bool RequestUserGroupStatus( CSteamID steamIDUser, CSteamID steamIDGroup );
|
||||
|
||||
|
||||
// these two functions s are deprecated, and will not return results
|
||||
// they will be removed in a future version of the SDK
|
||||
void GetGameplayStats( );
|
||||
STEAM_CALL_RESULT( GSReputation_t )
|
||||
SteamAPICall_t GetServerReputation();
|
||||
|
||||
// Returns the public IP of the server according to Steam, useful when the server is
|
||||
// behind NAT and you want to advertise its IP in a lobby for other clients to directly
|
||||
// connect to
|
||||
uint32 GetPublicIP_old();
|
||||
SteamIPAddress_t GetPublicIP();
|
||||
void GetPublicIP_fix(SteamIPAddress_t *out);
|
||||
|
||||
// These are in GameSocketShare mode, where instead of ISteamGameServer creating its own
|
||||
// socket to talk to the master server on, it lets the game use its socket to forward messages
|
||||
// back and forth. This prevents us from requiring server ops to open up yet another port
|
||||
// in their firewalls.
|
||||
//
|
||||
// the IP address and port should be in host order, i.e 127.0.0.1 == 0x7f000001
|
||||
|
||||
// These are used when you've elected to multiplex the game server's UDP socket
|
||||
// rather than having the master server updater use its own sockets.
|
||||
//
|
||||
// Source games use this to simplify the job of the server admins, so they
|
||||
// don't have to open up more ports on their firewalls.
|
||||
|
||||
// Call this when a packet that starts with 0xFFFFFFFF comes in. That means
|
||||
// it's for us.
|
||||
bool HandleIncomingPacket( const void *pData, int cbData, uint32 srcIP, uint16 srcPort );
|
||||
|
||||
// AFTER calling HandleIncomingPacket for any packets that came in that frame, call this.
|
||||
// This gets a packet that the master server updater needs to send out on UDP.
|
||||
// It returns the length of the packet it wants to send, or 0 if there are no more packets to send.
|
||||
// Call this each frame until it returns 0.
|
||||
int GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *pNetAdr, uint16 *pPort );
|
||||
|
||||
//
|
||||
// Control heartbeats / advertisement with master server
|
||||
//
|
||||
|
||||
// Call this as often as you like to tell the master server updater whether or not
|
||||
// you want it to be active (default: off).
|
||||
void EnableHeartbeats( bool bActive );
|
||||
|
||||
/// Indicate whether you wish to be listed on the master server list
|
||||
/// and/or respond to server browser / LAN discovery packets.
|
||||
/// The server starts with this value set to false. You should set all
|
||||
/// relevant server parameters before enabling advertisement on the server.
|
||||
///
|
||||
/// (This function used to be named EnableHeartbeats, so if you are wondering
|
||||
/// where that function went, it's right here. It does the same thing as before,
|
||||
/// the old name was just confusing.)
|
||||
void SetAdvertiseServerActive( bool bActive );
|
||||
|
||||
// You usually don't need to modify this.
|
||||
// Pass -1 to use the default value for iHeartbeatInterval.
|
||||
// Some mods change this.
|
||||
void SetHeartbeatInterval( int iHeartbeatInterval );
|
||||
|
||||
// Force a heartbeat to steam at the next opportunity
|
||||
void ForceHeartbeat();
|
||||
|
||||
void SetMasterServerHeartbeatInterval_DEPRECATED( int iHeartbeatInterval );
|
||||
void ForceMasterServerHeartbeat_DEPRECATED();
|
||||
|
||||
// associate this game server with this clan for the purposes of computing player compat
|
||||
STEAM_CALL_RESULT( AssociateWithClanResult_t )
|
||||
SteamAPICall_t AssociateWithClan( CSteamID steamIDClan );
|
||||
|
||||
// ask if any of the current players dont want to play with this new player - or vice versa
|
||||
STEAM_CALL_RESULT( ComputeNewPlayerCompatibilityResult_t )
|
||||
SteamAPICall_t ComputeNewPlayerCompatibility( CSteamID steamIDNewPlayer );
|
||||
|
||||
//
|
||||
void RunCallbacks();
|
||||
};
|
||||
118
all/steam_gameserverstats.cpp
Normal file
118
all/steam_gameserverstats.cpp
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_gameserverstats.h"
|
||||
|
||||
Steam_GameServerStats::Steam_GameServerStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
// downloads stats for the user
|
||||
// returns a GSStatsReceived_t callback when completed
|
||||
// if the user has no stats, GSStatsReceived_t.m_eResult will be set to k_EResultFail
|
||||
// these stats will only be auto-updated for clients playing on the server. For other
|
||||
// users you'll need to call RequestUserStats() again to refresh any data
|
||||
STEAM_CALL_RESULT( GSStatsReceived_t )
|
||||
SteamAPICall_t Steam_GameServerStats::RequestUserStats( CSteamID steamIDUser )
|
||||
{
|
||||
PRINT_DEBUG("RequestUserStats\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
GSStatsReceived_t data;
|
||||
data.m_eResult = k_EResultFail;//k_EResultOK;
|
||||
data.m_steamIDUser = steamIDUser;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
|
||||
// requests stat information for a user, usable after a successful call to RequestUserStats()
|
||||
bool Steam_GameServerStats::GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData )
|
||||
{
|
||||
PRINT_DEBUG("GetUserStat\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_GameServerStats::GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData )
|
||||
{
|
||||
PRINT_DEBUG("GetUserStat\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_GameServerStats::GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved )
|
||||
{
|
||||
PRINT_DEBUG("GetUserAchievement\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Set / update stats and achievements.
|
||||
// Note: These updates will work only on stats game servers are allowed to edit and only for
|
||||
// game servers that have been declared as officially controlled by the game creators.
|
||||
// Set the IP range of your official servers on the Steamworks page
|
||||
bool Steam_GameServerStats::SetUserStat( CSteamID steamIDUser, const char *pchName, int32 nData )
|
||||
{
|
||||
PRINT_DEBUG("SetUserStat\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_GameServerStats::SetUserStat( CSteamID steamIDUser, const char *pchName, float fData )
|
||||
{
|
||||
PRINT_DEBUG("SetUserStat\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_GameServerStats::UpdateUserAvgRateStat( CSteamID steamIDUser, const char *pchName, float flCountThisSession, double dSessionLength )
|
||||
{
|
||||
PRINT_DEBUG("UpdateUserAvgRateStat\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Steam_GameServerStats::SetUserAchievement( CSteamID steamIDUser, const char *pchName )
|
||||
{
|
||||
PRINT_DEBUG("SetUserAchievement\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_GameServerStats::ClearUserAchievement( CSteamID steamIDUser, const char *pchName )
|
||||
{
|
||||
PRINT_DEBUG("ClearUserAchievement\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Store the current data on the server, will get a GSStatsStored_t callback when set.
|
||||
//
|
||||
// If the callback has a result of k_EResultInvalidParam, one or more stats
|
||||
// uploaded has been rejected, either because they broke constraints
|
||||
// or were out of date. In this case the server sends back updated values.
|
||||
// The stats should be re-iterated to keep in sync.
|
||||
STEAM_CALL_RESULT( GSStatsStored_t )
|
||||
SteamAPICall_t Steam_GameServerStats::StoreUserStats( CSteamID steamIDUser )
|
||||
{
|
||||
PRINT_DEBUG("StoreUserStats\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
GSStatsStored_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
data.m_steamIDUser = steamIDUser;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
63
all/steam_gameserverstats.h
Normal file
63
all/steam_gameserverstats.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Functions for authenticating users via Steam to play on a game server
|
||||
//-----------------------------------------------------------------------------
|
||||
class Steam_GameServerStats : public ISteamGameServerStats
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
public:
|
||||
Steam_GameServerStats(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks);
|
||||
// downloads stats for the user
|
||||
// returns a GSStatsReceived_t callback when completed
|
||||
// if the user has no stats, GSStatsReceived_t.m_eResult will be set to k_EResultFail
|
||||
// these stats will only be auto-updated for clients playing on the server. For other
|
||||
// users you'll need to call RequestUserStats() again to refresh any data
|
||||
STEAM_CALL_RESULT( GSStatsReceived_t )
|
||||
SteamAPICall_t RequestUserStats( CSteamID steamIDUser );
|
||||
|
||||
// requests stat information for a user, usable after a successful call to RequestUserStats()
|
||||
bool GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData );
|
||||
bool GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData );
|
||||
bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved );
|
||||
|
||||
// Set / update stats and achievements.
|
||||
// Note: These updates will work only on stats game servers are allowed to edit and only for
|
||||
// game servers that have been declared as officially controlled by the game creators.
|
||||
// Set the IP range of your official servers on the Steamworks page
|
||||
bool SetUserStat( CSteamID steamIDUser, const char *pchName, int32 nData );
|
||||
bool SetUserStat( CSteamID steamIDUser, const char *pchName, float fData );
|
||||
bool UpdateUserAvgRateStat( CSteamID steamIDUser, const char *pchName, float flCountThisSession, double dSessionLength );
|
||||
|
||||
bool SetUserAchievement( CSteamID steamIDUser, const char *pchName );
|
||||
bool ClearUserAchievement( CSteamID steamIDUser, const char *pchName );
|
||||
|
||||
// Store the current data on the server, will get a GSStatsStored_t callback when set.
|
||||
//
|
||||
// If the callback has a result of k_EResultInvalidParam, one or more stats
|
||||
// uploaded has been rejected, either because they broke constraints
|
||||
// or were out of date. In this case the server sends back updated values.
|
||||
// The stats should be re-iterated to keep in sync.
|
||||
STEAM_CALL_RESULT( GSStatsStored_t )
|
||||
SteamAPICall_t StoreUserStats( CSteamID steamIDUser );
|
||||
};
|
||||
405
all/steam_http.cpp
Normal file
405
all/steam_http.cpp
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_http.h"
|
||||
|
||||
Steam_HTTP::Steam_HTTP(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
Steam_Http_Request *Steam_HTTP::get_request(HTTPRequestHandle hRequest)
|
||||
{
|
||||
auto conn = std::find_if(requests.begin(), requests.end(), [&hRequest](struct Steam_Http_Request const& conn) { return conn.handle == hRequest;});
|
||||
if (conn == requests.end()) return NULL;
|
||||
return &(*conn);
|
||||
}
|
||||
|
||||
// Initializes a new HTTP request, returning a handle to use in further operations on it. Requires
|
||||
// the method (GET or POST) and the absolute URL for the request. Both http and https are supported,
|
||||
// so this string must start with http:// or https:// and should look like http://store.steampowered.com/app/250/
|
||||
// or such.
|
||||
HTTPRequestHandle Steam_HTTP::CreateHTTPRequest( EHTTPMethod eHTTPRequestMethod, const char *pchAbsoluteURL )
|
||||
{
|
||||
PRINT_DEBUG("CreateHTTPRequest %i %s\n", eHTTPRequestMethod, pchAbsoluteURL);
|
||||
if (!pchAbsoluteURL) return INVALID_HTTPREQUEST_HANDLE;
|
||||
std::string url = pchAbsoluteURL;
|
||||
unsigned url_index = 0;
|
||||
if (url.rfind("https://", 0) == 0) {
|
||||
url_index = sizeof("https://") - 1;
|
||||
} else if (url.rfind("http://", 0) == 0) {
|
||||
url_index = sizeof("http://") - 1;
|
||||
}
|
||||
|
||||
struct Steam_Http_Request request;
|
||||
if (url_index) {
|
||||
if (url[url.size() - 1] == '/') url += "index.html";
|
||||
std::string file_path = Local_Storage::get_game_settings_path() + "http/" + url.substr(url_index);
|
||||
unsigned long long file_size = file_size_(file_path);
|
||||
if (file_size) {
|
||||
request.response.resize(file_size);
|
||||
long long read = Local_Storage::get_file_data(file_path, (char *)request.response.data(), file_size, 0);
|
||||
if (read < 0) read = 0;
|
||||
if (read != file_size) request.response.resize(read);
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
static HTTPRequestHandle h;
|
||||
++h;
|
||||
|
||||
request.handle = h;
|
||||
request.context_value = 0;
|
||||
|
||||
requests.push_back(request);
|
||||
return request.handle;
|
||||
}
|
||||
|
||||
|
||||
// Set a context value for the request, which will be returned in the HTTPRequestCompleted_t callback after
|
||||
// sending the request. This is just so the caller can easily keep track of which callbacks go with which request data.
|
||||
bool Steam_HTTP::SetHTTPRequestContextValue( HTTPRequestHandle hRequest, uint64 ulContextValue )
|
||||
{
|
||||
PRINT_DEBUG("SetHTTPRequestContextValue\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
request->context_value = ulContextValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Set a timeout in seconds for the HTTP request, must be called prior to sending the request. Default
|
||||
// timeout is 60 seconds if you don't call this. Returns false if the handle is invalid, or the request
|
||||
// has already been sent.
|
||||
bool Steam_HTTP::SetHTTPRequestNetworkActivityTimeout( HTTPRequestHandle hRequest, uint32 unTimeoutSeconds )
|
||||
{
|
||||
PRINT_DEBUG("SetHTTPRequestNetworkActivityTimeout\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Set a request header value for the request, must be called prior to sending the request. Will
|
||||
// return false if the handle is invalid or the request is already sent.
|
||||
bool Steam_HTTP::SetHTTPRequestHeaderValue( HTTPRequestHandle hRequest, const char *pchHeaderName, const char *pchHeaderValue )
|
||||
{
|
||||
PRINT_DEBUG("SetHTTPRequestHeaderValue %s %s\n", pchHeaderName, pchHeaderValue);
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Set a GET or POST parameter value on the request, which is set will depend on the EHTTPMethod specified
|
||||
// when creating the request. Must be called prior to sending the request. Will return false if the
|
||||
// handle is invalid or the request is already sent.
|
||||
bool Steam_HTTP::SetHTTPRequestGetOrPostParameter( HTTPRequestHandle hRequest, const char *pchParamName, const char *pchParamValue )
|
||||
{
|
||||
PRINT_DEBUG("SetHTTPRequestGetOrPostParameter\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Sends the HTTP request, will return false on a bad handle, otherwise use SteamCallHandle to wait on
|
||||
// asynchronous response via callback.
|
||||
//
|
||||
// Note: If the user is in offline mode in Steam, then this will add a only-if-cached cache-control
|
||||
// header and only do a local cache lookup rather than sending any actual remote request.
|
||||
bool Steam_HTTP::SendHTTPRequest( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle )
|
||||
{
|
||||
PRINT_DEBUG("SendHTTPRequest %u %p\n", hRequest, pCallHandle);
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct HTTPRequestCompleted_t data = {};
|
||||
data.m_hRequest = request->handle;
|
||||
data.m_ulContextValue = request->context_value;
|
||||
if (request->response.size() == 0) {
|
||||
data.m_bRequestSuccessful = false;
|
||||
data.m_eStatusCode = k_EHTTPStatusCode404NotFound;
|
||||
data.m_unBodySize = request->response.size();
|
||||
} else {
|
||||
data.m_bRequestSuccessful = true;
|
||||
data.m_eStatusCode = k_EHTTPStatusCode200OK;
|
||||
data.m_unBodySize = request->response.size();
|
||||
}
|
||||
|
||||
if (pCallHandle) {
|
||||
*pCallHandle = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Sends the HTTP request, will return false on a bad handle, otherwise use SteamCallHandle to wait on
|
||||
// asynchronous response via callback for completion, and listen for HTTPRequestHeadersReceived_t and
|
||||
// HTTPRequestDataReceived_t callbacks while streaming.
|
||||
bool Steam_HTTP::SendHTTPRequestAndStreamResponse( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle )
|
||||
{
|
||||
PRINT_DEBUG("SendHTTPRequestAndStreamResponse\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Defers a request you have sent, the actual HTTP client code may have many requests queued, and this will move
|
||||
// the specified request to the tail of the queue. Returns false on invalid handle, or if the request is not yet sent.
|
||||
bool Steam_HTTP::DeferHTTPRequest( HTTPRequestHandle hRequest )
|
||||
{
|
||||
PRINT_DEBUG("DeferHTTPRequest\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Prioritizes a request you have sent, the actual HTTP client code may have many requests queued, and this will move
|
||||
// the specified request to the head of the queue. Returns false on invalid handle, or if the request is not yet sent.
|
||||
bool Steam_HTTP::PrioritizeHTTPRequest( HTTPRequestHandle hRequest )
|
||||
{
|
||||
PRINT_DEBUG("PrioritizeHTTPRequest\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Checks if a response header is present in a HTTP response given a handle from HTTPRequestCompleted_t, also
|
||||
// returns the size of the header value if present so the caller and allocate a correctly sized buffer for
|
||||
// GetHTTPResponseHeaderValue.
|
||||
bool Steam_HTTP::GetHTTPResponseHeaderSize( HTTPRequestHandle hRequest, const char *pchHeaderName, uint32 *unResponseHeaderSize )
|
||||
{
|
||||
PRINT_DEBUG("GetHTTPResponseHeaderSize\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Gets header values from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
|
||||
// header is not present or if your buffer is too small to contain it's value. You should first call
|
||||
// BGetHTTPResponseHeaderSize to check for the presence of the header and to find out the size buffer needed.
|
||||
bool Steam_HTTP::GetHTTPResponseHeaderValue( HTTPRequestHandle hRequest, const char *pchHeaderName, uint8 *pHeaderValueBuffer, uint32 unBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("GetHTTPResponseHeaderValue\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Gets the size of the body data from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
|
||||
// handle is invalid.
|
||||
bool Steam_HTTP::GetHTTPResponseBodySize( HTTPRequestHandle hRequest, uint32 *unBodySize )
|
||||
{
|
||||
PRINT_DEBUG("GetHTTPResponseBodySize\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unBodySize) *unBodySize = request->response.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Gets the body data from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
|
||||
// handle is invalid or is to a streaming response, or if the provided buffer is not the correct size. Use BGetHTTPResponseBodySize first to find out
|
||||
// the correct buffer size to use.
|
||||
bool Steam_HTTP::GetHTTPResponseBodyData( HTTPRequestHandle hRequest, uint8 *pBodyDataBuffer, uint32 unBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("GetHTTPResponseBodyData\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unBufferSize < request->response.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pBodyDataBuffer) memcpy(pBodyDataBuffer, request->response.data(), request->response.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Gets the body data from a streaming HTTP response given a handle from HTTPRequestDataReceived_t. Will return false if the
|
||||
// handle is invalid or is to a non-streaming response (meaning it wasn't sent with SendHTTPRequestAndStreamResponse), or if the buffer size and offset
|
||||
// do not match the size and offset sent in HTTPRequestDataReceived_t.
|
||||
bool Steam_HTTP::GetHTTPStreamingResponseBodyData( HTTPRequestHandle hRequest, uint32 cOffset, uint8 *pBodyDataBuffer, uint32 unBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("GetHTTPStreamingResponseBodyData\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Releases an HTTP response handle, should always be called to free resources after receiving a HTTPRequestCompleted_t
|
||||
// callback and finishing using the response.
|
||||
bool Steam_HTTP::ReleaseHTTPRequest( HTTPRequestHandle hRequest )
|
||||
{
|
||||
PRINT_DEBUG("ReleaseHTTPRequest\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
auto c = std::begin(requests);
|
||||
while (c != std::end(requests)) {
|
||||
if (c->handle == hRequest) {
|
||||
c = requests.erase(c);
|
||||
return true;
|
||||
} else {
|
||||
++c;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Gets progress on downloading the body for the request. This will be zero unless a response header has already been
|
||||
// received which included a content-length field. For responses that contain no content-length it will report
|
||||
// zero for the duration of the request as the size is unknown until the connection closes.
|
||||
bool Steam_HTTP::GetHTTPDownloadProgressPct( HTTPRequestHandle hRequest, float *pflPercentOut )
|
||||
{
|
||||
PRINT_DEBUG("GetHTTPDownloadProgressPct\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Sets the body for an HTTP Post request. Will fail and return false on a GET request, and will fail if POST params
|
||||
// have already been set for the request. Setting this raw body makes it the only contents for the post, the pchContentType
|
||||
// parameter will set the content-type header for the request so the server may know how to interpret the body.
|
||||
bool Steam_HTTP::SetHTTPRequestRawPostBody( HTTPRequestHandle hRequest, const char *pchContentType, uint8 *pubBody, uint32 unBodyLen )
|
||||
{
|
||||
PRINT_DEBUG("SetHTTPRequestRawPostBody %s\n", pchContentType);
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Creates a cookie container handle which you must later free with ReleaseCookieContainer(). If bAllowResponsesToModify=true
|
||||
// than any response to your requests using this cookie container may add new cookies which may be transmitted with
|
||||
// future requests. If bAllowResponsesToModify=false than only cookies you explicitly set will be sent. This API is just for
|
||||
// during process lifetime, after steam restarts no cookies are persisted and you have no way to access the cookie container across
|
||||
// repeat executions of your process.
|
||||
HTTPCookieContainerHandle Steam_HTTP::CreateCookieContainer( bool bAllowResponsesToModify )
|
||||
{
|
||||
PRINT_DEBUG("CreateCookieContainer\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Release a cookie container you are finished using, freeing it's memory
|
||||
bool Steam_HTTP::ReleaseCookieContainer( HTTPCookieContainerHandle hCookieContainer )
|
||||
{
|
||||
PRINT_DEBUG("ReleaseCookieContainer\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Adds a cookie to the specified cookie container that will be used with future requests.
|
||||
bool Steam_HTTP::SetCookie( HTTPCookieContainerHandle hCookieContainer, const char *pchHost, const char *pchUrl, const char *pchCookie )
|
||||
{
|
||||
PRINT_DEBUG("SetCookie\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Set the cookie container to use for a HTTP request
|
||||
bool Steam_HTTP::SetHTTPRequestCookieContainer( HTTPRequestHandle hRequest, HTTPCookieContainerHandle hCookieContainer )
|
||||
{
|
||||
PRINT_DEBUG("SetHTTPRequestCookieContainer\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Set the extra user agent info for a request, this doesn't clobber the normal user agent, it just adds the extra info on the end
|
||||
bool Steam_HTTP::SetHTTPRequestUserAgentInfo( HTTPRequestHandle hRequest, const char *pchUserAgentInfo )
|
||||
{
|
||||
PRINT_DEBUG("SetHTTPRequestUserAgentInfo\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Set that https request should require verified SSL certificate via machines certificate trust store
|
||||
bool Steam_HTTP::SetHTTPRequestRequiresVerifiedCertificate( HTTPRequestHandle hRequest, bool bRequireVerifiedCertificate )
|
||||
{
|
||||
PRINT_DEBUG("SetHTTPRequestRequiresVerifiedCertificate\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Set an absolute timeout on the HTTP request, this is just a total time timeout different than the network activity timeout
|
||||
// which can bump everytime we get more data
|
||||
bool Steam_HTTP::SetHTTPRequestAbsoluteTimeoutMS( HTTPRequestHandle hRequest, uint32 unMilliseconds )
|
||||
{
|
||||
PRINT_DEBUG("SetHTTPRequestAbsoluteTimeoutMS\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Check if the reason the request failed was because we timed it out (rather than some harder failure)
|
||||
bool Steam_HTTP::GetHTTPRequestWasTimedOut( HTTPRequestHandle hRequest, bool *pbWasTimedOut )
|
||||
{
|
||||
PRINT_DEBUG("GetHTTPRequestWasTimedOut\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pbWasTimedOut) *pbWasTimedOut = false;
|
||||
return true;
|
||||
}
|
||||
154
all/steam_http.h
Normal file
154
all/steam_http.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
|
||||
struct Steam_Http_Request {
|
||||
HTTPRequestHandle handle;
|
||||
uint64 context_value;
|
||||
|
||||
std::string response;
|
||||
};
|
||||
|
||||
class Steam_HTTP :
|
||||
public ISteamHTTP001,
|
||||
public ISteamHTTP002,
|
||||
public ISteamHTTP
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
|
||||
std::vector<Steam_Http_Request> requests;
|
||||
|
||||
Steam_Http_Request *get_request(HTTPRequestHandle hRequest);
|
||||
public:
|
||||
Steam_HTTP(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks);
|
||||
|
||||
// Initializes a new HTTP request, returning a handle to use in further operations on it. Requires
|
||||
// the method (GET or POST) and the absolute URL for the request. Both http and https are supported,
|
||||
// so this string must start with http:// or https:// and should look like http://store.steampowered.com/app/250/
|
||||
// or such.
|
||||
HTTPRequestHandle CreateHTTPRequest( EHTTPMethod eHTTPRequestMethod, const char *pchAbsoluteURL );
|
||||
|
||||
// Set a context value for the request, which will be returned in the HTTPRequestCompleted_t callback after
|
||||
// sending the request. This is just so the caller can easily keep track of which callbacks go with which request data.
|
||||
bool SetHTTPRequestContextValue( HTTPRequestHandle hRequest, uint64 ulContextValue );
|
||||
|
||||
// Set a timeout in seconds for the HTTP request, must be called prior to sending the request. Default
|
||||
// timeout is 60 seconds if you don't call this. Returns false if the handle is invalid, or the request
|
||||
// has already been sent.
|
||||
bool SetHTTPRequestNetworkActivityTimeout( HTTPRequestHandle hRequest, uint32 unTimeoutSeconds );
|
||||
|
||||
// Set a request header value for the request, must be called prior to sending the request. Will
|
||||
// return false if the handle is invalid or the request is already sent.
|
||||
bool SetHTTPRequestHeaderValue( HTTPRequestHandle hRequest, const char *pchHeaderName, const char *pchHeaderValue );
|
||||
|
||||
// Set a GET or POST parameter value on the request, which is set will depend on the EHTTPMethod specified
|
||||
// when creating the request. Must be called prior to sending the request. Will return false if the
|
||||
// handle is invalid or the request is already sent.
|
||||
bool SetHTTPRequestGetOrPostParameter( HTTPRequestHandle hRequest, const char *pchParamName, const char *pchParamValue );
|
||||
|
||||
// Sends the HTTP request, will return false on a bad handle, otherwise use SteamCallHandle to wait on
|
||||
// asynchronous response via callback.
|
||||
//
|
||||
// Note: If the user is in offline mode in Steam, then this will add a only-if-cached cache-control
|
||||
// header and only do a local cache lookup rather than sending any actual remote request.
|
||||
bool SendHTTPRequest( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle );
|
||||
|
||||
// Sends the HTTP request, will return false on a bad handle, otherwise use SteamCallHandle to wait on
|
||||
// asynchronous response via callback for completion, and listen for HTTPRequestHeadersReceived_t and
|
||||
// HTTPRequestDataReceived_t callbacks while streaming.
|
||||
bool SendHTTPRequestAndStreamResponse( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle );
|
||||
|
||||
// Defers a request you have sent, the actual HTTP client code may have many requests queued, and this will move
|
||||
// the specified request to the tail of the queue. Returns false on invalid handle, or if the request is not yet sent.
|
||||
bool DeferHTTPRequest( HTTPRequestHandle hRequest );
|
||||
|
||||
// Prioritizes a request you have sent, the actual HTTP client code may have many requests queued, and this will move
|
||||
// the specified request to the head of the queue. Returns false on invalid handle, or if the request is not yet sent.
|
||||
bool PrioritizeHTTPRequest( HTTPRequestHandle hRequest );
|
||||
|
||||
// Checks if a response header is present in a HTTP response given a handle from HTTPRequestCompleted_t, also
|
||||
// returns the size of the header value if present so the caller and allocate a correctly sized buffer for
|
||||
// GetHTTPResponseHeaderValue.
|
||||
bool GetHTTPResponseHeaderSize( HTTPRequestHandle hRequest, const char *pchHeaderName, uint32 *unResponseHeaderSize );
|
||||
|
||||
// Gets header values from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
|
||||
// header is not present or if your buffer is too small to contain it's value. You should first call
|
||||
// BGetHTTPResponseHeaderSize to check for the presence of the header and to find out the size buffer needed.
|
||||
bool GetHTTPResponseHeaderValue( HTTPRequestHandle hRequest, const char *pchHeaderName, uint8 *pHeaderValueBuffer, uint32 unBufferSize );
|
||||
|
||||
// Gets the size of the body data from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
|
||||
// handle is invalid.
|
||||
bool GetHTTPResponseBodySize( HTTPRequestHandle hRequest, uint32 *unBodySize );
|
||||
|
||||
// Gets the body data from a HTTP response given a handle from HTTPRequestCompleted_t, will return false if the
|
||||
// handle is invalid or is to a streaming response, or if the provided buffer is not the correct size. Use BGetHTTPResponseBodySize first to find out
|
||||
// the correct buffer size to use.
|
||||
bool GetHTTPResponseBodyData( HTTPRequestHandle hRequest, uint8 *pBodyDataBuffer, uint32 unBufferSize );
|
||||
|
||||
// Gets the body data from a streaming HTTP response given a handle from HTTPRequestDataReceived_t. Will return false if the
|
||||
// handle is invalid or is to a non-streaming response (meaning it wasn't sent with SendHTTPRequestAndStreamResponse), or if the buffer size and offset
|
||||
// do not match the size and offset sent in HTTPRequestDataReceived_t.
|
||||
bool GetHTTPStreamingResponseBodyData( HTTPRequestHandle hRequest, uint32 cOffset, uint8 *pBodyDataBuffer, uint32 unBufferSize );
|
||||
|
||||
// Releases an HTTP response handle, should always be called to free resources after receiving a HTTPRequestCompleted_t
|
||||
// callback and finishing using the response.
|
||||
bool ReleaseHTTPRequest( HTTPRequestHandle hRequest );
|
||||
|
||||
// Gets progress on downloading the body for the request. This will be zero unless a response header has already been
|
||||
// received which included a content-length field. For responses that contain no content-length it will report
|
||||
// zero for the duration of the request as the size is unknown until the connection closes.
|
||||
bool GetHTTPDownloadProgressPct( HTTPRequestHandle hRequest, float *pflPercentOut );
|
||||
|
||||
// Sets the body for an HTTP Post request. Will fail and return false on a GET request, and will fail if POST params
|
||||
// have already been set for the request. Setting this raw body makes it the only contents for the post, the pchContentType
|
||||
// parameter will set the content-type header for the request so the server may know how to interpret the body.
|
||||
bool SetHTTPRequestRawPostBody( HTTPRequestHandle hRequest, const char *pchContentType, uint8 *pubBody, uint32 unBodyLen );
|
||||
|
||||
// Creates a cookie container handle which you must later free with ReleaseCookieContainer(). If bAllowResponsesToModify=true
|
||||
// than any response to your requests using this cookie container may add new cookies which may be transmitted with
|
||||
// future requests. If bAllowResponsesToModify=false than only cookies you explicitly set will be sent. This API is just for
|
||||
// during process lifetime, after steam restarts no cookies are persisted and you have no way to access the cookie container across
|
||||
// repeat executions of your process.
|
||||
HTTPCookieContainerHandle CreateCookieContainer( bool bAllowResponsesToModify );
|
||||
|
||||
// Release a cookie container you are finished using, freeing it's memory
|
||||
bool ReleaseCookieContainer( HTTPCookieContainerHandle hCookieContainer );
|
||||
|
||||
// Adds a cookie to the specified cookie container that will be used with future requests.
|
||||
bool SetCookie( HTTPCookieContainerHandle hCookieContainer, const char *pchHost, const char *pchUrl, const char *pchCookie );
|
||||
|
||||
// Set the cookie container to use for a HTTP request
|
||||
bool SetHTTPRequestCookieContainer( HTTPRequestHandle hRequest, HTTPCookieContainerHandle hCookieContainer );
|
||||
|
||||
// Set the extra user agent info for a request, this doesn't clobber the normal user agent, it just adds the extra info on the end
|
||||
bool SetHTTPRequestUserAgentInfo( HTTPRequestHandle hRequest, const char *pchUserAgentInfo );
|
||||
|
||||
// Set that https request should require verified SSL certificate via machines certificate trust store
|
||||
bool SetHTTPRequestRequiresVerifiedCertificate( HTTPRequestHandle hRequest, bool bRequireVerifiedCertificate );
|
||||
|
||||
// Set an absolute timeout on the HTTP request, this is just a total time timeout different than the network activity timeout
|
||||
// which can bump everytime we get more data
|
||||
bool SetHTTPRequestAbsoluteTimeoutMS( HTTPRequestHandle hRequest, uint32 unMilliseconds );
|
||||
|
||||
// Check if the reason the request failed was because we timed it out (rather than some harder failure)
|
||||
bool GetHTTPRequestWasTimedOut( HTTPRequestHandle hRequest, bool *pbWasTimedOut );
|
||||
};
|
||||
966
all/steam_inventory.h
Normal file
966
all/steam_inventory.h
Normal file
|
|
@ -0,0 +1,966 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h" // For SteamItemDef_t
|
||||
|
||||
struct Steam_Inventory_Requests {
|
||||
double timeout = 0.1;
|
||||
bool done = false;
|
||||
bool full_query;
|
||||
|
||||
SteamInventoryResult_t inventory_result;
|
||||
std::chrono::system_clock::time_point time_created;
|
||||
|
||||
std::vector<SteamItemInstanceID_t> instance_ids;
|
||||
|
||||
bool result_done() {
|
||||
return done;
|
||||
}
|
||||
|
||||
uint32 timestamp() {
|
||||
return std::chrono::duration_cast<std::chrono::duration<uint32>>(time_created.time_since_epoch()).count();
|
||||
}
|
||||
};
|
||||
|
||||
class Steam_Inventory :
|
||||
public ISteamInventory001,
|
||||
public ISteamInventory002,
|
||||
public ISteamInventory
|
||||
{
|
||||
public:
|
||||
static constexpr auto items_user_file = "items.json";
|
||||
static constexpr auto items_default_file = "default_items.json";
|
||||
|
||||
private:
|
||||
class Settings *settings;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
class Local_Storage* local_storage;
|
||||
|
||||
std::vector<struct Steam_Inventory_Requests> inventory_requests;
|
||||
|
||||
nlohmann::json defined_items;
|
||||
nlohmann::json user_items;
|
||||
|
||||
bool inventory_loaded;
|
||||
bool call_definition_update;
|
||||
bool item_definitions_loaded;
|
||||
|
||||
struct Steam_Inventory_Requests* new_inventory_result(bool full_query=true, const SteamItemInstanceID_t* pInstanceIDs = NULL, uint32 unCountInstanceIDs = 0)
|
||||
{
|
||||
static SteamInventoryResult_t result;
|
||||
++result;
|
||||
|
||||
struct Steam_Inventory_Requests request;
|
||||
request.inventory_result = result;
|
||||
request.full_query = full_query;
|
||||
if (pInstanceIDs && unCountInstanceIDs) {
|
||||
request.instance_ids.reserve(unCountInstanceIDs);
|
||||
std::copy(pInstanceIDs, pInstanceIDs + unCountInstanceIDs, std::back_inserter(request.instance_ids));
|
||||
}
|
||||
|
||||
request.time_created = std::chrono::system_clock::now();
|
||||
inventory_requests.push_back(request);
|
||||
|
||||
return &(inventory_requests.back());
|
||||
}
|
||||
|
||||
struct Steam_Inventory_Requests *get_inventory_result(SteamInventoryResult_t resultHandle)
|
||||
{
|
||||
auto request = std::find_if(inventory_requests.begin(), inventory_requests.end(), [&resultHandle](struct Steam_Inventory_Requests const& item) { return item.inventory_result == resultHandle; });
|
||||
if (inventory_requests.end() == request)
|
||||
return NULL;
|
||||
|
||||
return &(*request);
|
||||
}
|
||||
|
||||
void read_items_db()
|
||||
{
|
||||
std::string items_db_path = Local_Storage::get_game_settings_path() + items_user_file;
|
||||
PRINT_DEBUG("Items file path: %s\n", items_db_path.c_str());
|
||||
local_storage->load_json(items_db_path, defined_items);
|
||||
}
|
||||
|
||||
void read_inventory_db()
|
||||
{
|
||||
// If we havn't got any inventory
|
||||
if (!local_storage->load_json_file("", items_user_file, user_items))
|
||||
{
|
||||
// Try to load a default one
|
||||
std::string items_db_path = Local_Storage::get_game_settings_path() + items_default_file;
|
||||
PRINT_DEBUG("Default items file path: %s\n", items_db_path.c_str());
|
||||
local_storage->load_json(items_db_path, user_items);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static void run_every_runcb_cb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("Steam_Inventory::run_every_runcb\n");
|
||||
|
||||
Steam_Inventory *obj = (Steam_Inventory *)object;
|
||||
obj->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb, class Local_Storage *local_storage):
|
||||
settings(settings),
|
||||
callback_results(callback_results),
|
||||
callbacks(callbacks),
|
||||
run_every_runcb(run_every_runcb),
|
||||
local_storage(local_storage),
|
||||
defined_items(nlohmann::json::object()),
|
||||
user_items(nlohmann::json::object()),
|
||||
inventory_loaded(false),
|
||||
call_definition_update(false),
|
||||
item_definitions_loaded(false)
|
||||
{
|
||||
this->run_every_runcb->add(&Steam_Inventory::run_every_runcb_cb, this);
|
||||
}
|
||||
|
||||
~Steam_Inventory()
|
||||
{
|
||||
this->run_every_runcb->remove(&Steam_Inventory::run_every_runcb_cb, this);
|
||||
}
|
||||
|
||||
// INVENTORY ASYNC RESULT MANAGEMENT
|
||||
//
|
||||
// Asynchronous inventory queries always output a result handle which can be used with
|
||||
// GetResultStatus, GetResultItems, etc. A SteamInventoryResultReady_t callback will
|
||||
// be triggered when the asynchronous result becomes ready (or fails).
|
||||
//
|
||||
|
||||
// Find out the status of an asynchronous inventory result handle. Possible values:
|
||||
// k_EResultPending - still in progress
|
||||
// k_EResultOK - done, result ready
|
||||
// k_EResultExpired - done, result ready, maybe out of date (see DeserializeResult)
|
||||
// k_EResultInvalidParam - ERROR: invalid API call parameters
|
||||
// k_EResultServiceUnavailable - ERROR: service temporarily down, you may retry later
|
||||
// k_EResultLimitExceeded - ERROR: operation would exceed per-user inventory limits
|
||||
// k_EResultFail - ERROR: unknown / generic error
|
||||
STEAM_METHOD_DESC(Find out the status of an asynchronous inventory result handle.)
|
||||
EResult GetResultStatus( SteamInventoryResult_t resultHandle )
|
||||
{
|
||||
PRINT_DEBUG("GetResultStatus\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests *request = get_inventory_result(resultHandle);
|
||||
if (!request) return k_EResultInvalidParam;
|
||||
if (!request->result_done()) return k_EResultPending;
|
||||
return k_EResultOK;
|
||||
}
|
||||
|
||||
|
||||
// Copies the contents of a result set into a flat array. The specific
|
||||
// contents of the result set depend on which query which was used.
|
||||
STEAM_METHOD_DESC(Copies the contents of a result set into a flat array. The specific contents of the result set depend on which query which was used.)
|
||||
bool GetResultItems( SteamInventoryResult_t resultHandle,
|
||||
STEAM_OUT_ARRAY_COUNT( punOutItemsArraySize,Output array) SteamItemDetails_t *pOutItemsArray,
|
||||
uint32 *punOutItemsArraySize )
|
||||
{
|
||||
PRINT_DEBUG("GetResultItems\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests *request = get_inventory_result(resultHandle);
|
||||
if (!request) return false;
|
||||
if (!request->result_done()) return false;
|
||||
if (!inventory_loaded) return false;
|
||||
|
||||
if (pOutItemsArray != nullptr)
|
||||
{
|
||||
SteamItemDetails_t *items_array_base = pOutItemsArray;
|
||||
uint32 max_items = *punOutItemsArraySize;
|
||||
|
||||
if (request->full_query) {
|
||||
// We end if we reached the end of items or the end of buffer
|
||||
for( auto i = user_items.begin(); i != user_items.end() && max_items; ++i, --max_items )
|
||||
{
|
||||
pOutItemsArray->m_iDefinition = std::stoi(i.key());
|
||||
pOutItemsArray->m_itemId = pOutItemsArray->m_iDefinition;
|
||||
try
|
||||
{
|
||||
pOutItemsArray->m_unQuantity = i.value().get<int>();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pOutItemsArray->m_unQuantity = 0;
|
||||
}
|
||||
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
|
||||
++pOutItemsArray;
|
||||
}
|
||||
} else {
|
||||
for (auto &itemid : request->instance_ids) {
|
||||
if (!max_items) break;
|
||||
auto it = user_items.find(std::to_string(itemid));
|
||||
if (it != user_items.end()) {
|
||||
pOutItemsArray->m_iDefinition = itemid;
|
||||
pOutItemsArray->m_itemId = itemid;
|
||||
|
||||
try
|
||||
{
|
||||
pOutItemsArray->m_unQuantity = it->get<int>();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pOutItemsArray->m_unQuantity = 0;
|
||||
}
|
||||
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
|
||||
++pOutItemsArray;
|
||||
--max_items;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*punOutItemsArraySize = pOutItemsArray - items_array_base;
|
||||
}
|
||||
else if (punOutItemsArraySize != nullptr)
|
||||
{
|
||||
if (request->full_query) {
|
||||
*punOutItemsArraySize = user_items.size();
|
||||
} else {
|
||||
*punOutItemsArraySize = std::count_if(request->instance_ids.begin(), request->instance_ids.end(), [this](SteamItemInstanceID_t item_id){ return user_items.find(std::to_string(item_id)) != user_items.end();});
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_DEBUG("GetResultItems good\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// In combination with GetResultItems, you can use GetResultItemProperty to retrieve
|
||||
// dynamic string properties for a given item returned in the result set.
|
||||
//
|
||||
// Property names are always composed of ASCII letters, numbers, and/or underscores.
|
||||
//
|
||||
// Pass a NULL pointer for pchPropertyName to get a comma - separated list of available
|
||||
// property names.
|
||||
//
|
||||
// If pchValueBuffer is NULL, *punValueBufferSize will contain the
|
||||
// suggested buffer size. Otherwise it will be the number of bytes actually copied
|
||||
// to pchValueBuffer. If the results do not fit in the given buffer, partial
|
||||
// results may be copied.
|
||||
bool GetResultItemProperty( SteamInventoryResult_t resultHandle,
|
||||
uint32 unItemIndex,
|
||||
const char *pchPropertyName,
|
||||
STEAM_OUT_STRING_COUNT( punValueBufferSizeOut ) char *pchValueBuffer, uint32 *punValueBufferSizeOut )
|
||||
{
|
||||
PRINT_DEBUG("GetResultItemProperty\n");
|
||||
//TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Returns the server time at which the result was generated. Compare against
|
||||
// the value of IClientUtils::GetServerRealTime() to determine age.
|
||||
STEAM_METHOD_DESC(Returns the server time at which the result was generated. Compare against the value of IClientUtils::GetServerRealTime() to determine age.)
|
||||
uint32 GetResultTimestamp( SteamInventoryResult_t resultHandle )
|
||||
{
|
||||
PRINT_DEBUG("GetResultTimestamp\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests *request = get_inventory_result(resultHandle);
|
||||
if (!request || !request->result_done()) return 0;
|
||||
return request->timestamp();
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the result belongs to the target steam ID, false if the
|
||||
// result does not. This is important when using DeserializeResult, to verify
|
||||
// that a remote player is not pretending to have a different user's inventory.
|
||||
STEAM_METHOD_DESC(Returns true if the result belongs to the target steam ID or false if the result does not. This is important when using DeserializeResult to verify that a remote player is not pretending to have a different users inventory.)
|
||||
bool CheckResultSteamID( SteamInventoryResult_t resultHandle, CSteamID steamIDExpected )
|
||||
{
|
||||
PRINT_DEBUG("CheckResultSteamID %llu\n", steamIDExpected.ConvertToUint64());
|
||||
//TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Destroys a result handle and frees all associated memory.
|
||||
STEAM_METHOD_DESC(Destroys a result handle and frees all associated memory.)
|
||||
void DestroyResult( SteamInventoryResult_t resultHandle )
|
||||
{
|
||||
PRINT_DEBUG("DestroyResult\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto request = std::find_if(inventory_requests.begin(), inventory_requests.end(), [&resultHandle](struct Steam_Inventory_Requests const& item) { return item.inventory_result == resultHandle; });
|
||||
if (inventory_requests.end() == request)
|
||||
return;
|
||||
|
||||
inventory_requests.erase(request);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// INVENTORY ASYNC QUERY
|
||||
//
|
||||
|
||||
// Captures the entire state of the current user's Steam inventory.
|
||||
// You must call DestroyResult on this handle when you are done with it.
|
||||
// Returns false and sets *pResultHandle to zero if inventory is unavailable.
|
||||
// Note: calls to this function are subject to rate limits and may return
|
||||
// cached results if called too frequently. It is suggested that you call
|
||||
// this function only when you are about to display the user's full inventory,
|
||||
// or if you expect that the inventory may have changed.
|
||||
STEAM_METHOD_DESC(Captures the entire state of the current users Steam inventory.)
|
||||
bool GetAllItems( SteamInventoryResult_t *pResultHandle )
|
||||
{
|
||||
PRINT_DEBUG("GetAllItems\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests* request = new_inventory_result();
|
||||
|
||||
if (pResultHandle != nullptr)
|
||||
*pResultHandle = request->inventory_result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Captures the state of a subset of the current user's Steam inventory,
|
||||
// identified by an array of item instance IDs. The results from this call
|
||||
// can be serialized and passed to other players to "prove" that the current
|
||||
// user owns specific items, without exposing the user's entire inventory.
|
||||
// For example, you could call GetItemsByID with the IDs of the user's
|
||||
// currently equipped cosmetic items and serialize this to a buffer, and
|
||||
// then transmit this buffer to other players upon joining a game.
|
||||
STEAM_METHOD_DESC(Captures the state of a subset of the current users Steam inventory identified by an array of item instance IDs.)
|
||||
bool GetItemsByID( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT( unCountInstanceIDs ) const SteamItemInstanceID_t *pInstanceIDs, uint32 unCountInstanceIDs )
|
||||
{
|
||||
PRINT_DEBUG("GetItemsByID\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (pResultHandle) {
|
||||
struct Steam_Inventory_Requests *request = new_inventory_result(false, pInstanceIDs, unCountInstanceIDs);
|
||||
*pResultHandle = request->inventory_result;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// RESULT SERIALIZATION AND AUTHENTICATION
|
||||
//
|
||||
// Serialized result sets contain a short signature which can't be forged
|
||||
// or replayed across different game sessions. A result set can be serialized
|
||||
// on the local client, transmitted to other players via your game networking,
|
||||
// and deserialized by the remote players. This is a secure way of preventing
|
||||
// hackers from lying about posessing rare/high-value items.
|
||||
|
||||
// Serializes a result set with signature bytes to an output buffer. Pass
|
||||
// NULL as an output buffer to get the required size via punOutBufferSize.
|
||||
// The size of a serialized result depends on the number items which are being
|
||||
// serialized. When securely transmitting items to other players, it is
|
||||
// recommended to use "GetItemsByID" first to create a minimal result set.
|
||||
// Results have a built-in timestamp which will be considered "expired" after
|
||||
// an hour has elapsed. See DeserializeResult for expiration handling.
|
||||
bool SerializeResult( SteamInventoryResult_t resultHandle, STEAM_OUT_BUFFER_COUNT(punOutBufferSize) void *pOutBuffer, uint32 *punOutBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("SerializeResult %i\n", resultHandle);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
//TODO
|
||||
struct Steam_Inventory_Requests *request = get_inventory_result(resultHandle);
|
||||
if (!request) return false;
|
||||
if (!request->result_done()) return false;
|
||||
|
||||
uint8 buffer[8 + 128] = {};
|
||||
memset(buffer, 0x5F, sizeof(buffer));
|
||||
|
||||
if (!punOutBufferSize) return false;
|
||||
PRINT_DEBUG("Size %u\n", *punOutBufferSize);
|
||||
if (!pOutBuffer) {
|
||||
*punOutBufferSize = sizeof(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*punOutBufferSize < sizeof(buffer)) {
|
||||
*punOutBufferSize = sizeof(buffer);
|
||||
return false; //??
|
||||
}
|
||||
|
||||
memcpy(pOutBuffer, buffer, sizeof(buffer));
|
||||
*punOutBufferSize = sizeof(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Deserializes a result set and verifies the signature bytes. Returns false
|
||||
// if bRequireFullOnlineVerify is set but Steam is running in Offline mode.
|
||||
// Otherwise returns true and then delivers error codes via GetResultStatus.
|
||||
//
|
||||
// The bRESERVED_MUST_BE_FALSE flag is reserved for future use and should not
|
||||
// be set to true by your game at this time.
|
||||
//
|
||||
// DeserializeResult has a potential soft-failure mode where the handle status
|
||||
// is set to k_EResultExpired. GetResultItems() still succeeds in this mode.
|
||||
// The "expired" result could indicate that the data may be out of date - not
|
||||
// just due to timed expiration (one hour), but also because one of the items
|
||||
// in the result set may have been traded or consumed since the result set was
|
||||
// generated. You could compare the timestamp from GetResultTimestamp() to
|
||||
// ISteamUtils::GetServerRealTime() to determine how old the data is. You could
|
||||
// simply ignore the "expired" result code and continue as normal, or you
|
||||
// could challenge the player with expired data to send an updated result set.
|
||||
bool DeserializeResult( SteamInventoryResult_t *pOutResultHandle, STEAM_BUFFER_COUNT(punOutBufferSize) const void *pBuffer, uint32 unBufferSize, bool bRESERVED_MUST_BE_FALSE)
|
||||
{
|
||||
PRINT_DEBUG("DeserializeResult\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
//TODO
|
||||
if (pOutResultHandle) {
|
||||
struct Steam_Inventory_Requests *request = new_inventory_result(false);
|
||||
*pOutResultHandle = request->inventory_result;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// INVENTORY ASYNC MODIFICATION
|
||||
//
|
||||
|
||||
// GenerateItems() creates one or more items and then generates a SteamInventoryCallback_t
|
||||
// notification with a matching nCallbackContext parameter. This API is only intended
|
||||
// for prototyping - it is only usable by Steam accounts that belong to the publisher group
|
||||
// for your game.
|
||||
// If punArrayQuantity is not NULL, it should be the same length as pArrayItems and should
|
||||
// describe the quantity of each item to generate.
|
||||
bool GenerateItems( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, STEAM_ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength )
|
||||
{
|
||||
PRINT_DEBUG("GenerateItems\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// GrantPromoItems() checks the list of promotional items for which the user may be eligible
|
||||
// and grants the items (one time only). On success, the result set will include items which
|
||||
// were granted, if any. If no items were granted because the user isn't eligible for any
|
||||
// promotions, this is still considered a success.
|
||||
STEAM_METHOD_DESC(GrantPromoItems() checks the list of promotional items for which the user may be eligible and grants the items (one time only).)
|
||||
bool GrantPromoItems( SteamInventoryResult_t *pResultHandle )
|
||||
{
|
||||
PRINT_DEBUG("GrantPromoItems\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests* request = new_inventory_result(false);
|
||||
|
||||
if (pResultHandle != nullptr)
|
||||
*pResultHandle = request->inventory_result;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// AddPromoItem() / AddPromoItems() are restricted versions of GrantPromoItems(). Instead of
|
||||
// scanning for all eligible promotional items, the check is restricted to a single item
|
||||
// definition or set of item definitions. This can be useful if your game has custom UI for
|
||||
// showing a specific promo item to the user.
|
||||
bool AddPromoItem( SteamInventoryResult_t *pResultHandle, SteamItemDef_t itemDef )
|
||||
{
|
||||
PRINT_DEBUG("AddPromoItem\n");
|
||||
//TODO
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests* request = new_inventory_result(false);
|
||||
|
||||
if (pResultHandle != nullptr)
|
||||
*pResultHandle = request->inventory_result;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddPromoItems( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, uint32 unArrayLength )
|
||||
{
|
||||
PRINT_DEBUG("AddPromoItems\n");
|
||||
//TODO
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests* request = new_inventory_result(false);
|
||||
|
||||
if (pResultHandle != nullptr)
|
||||
*pResultHandle = request->inventory_result;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ConsumeItem() removes items from the inventory, permanently. They cannot be recovered.
|
||||
// Not for the faint of heart - if your game implements item removal at all, a high-friction
|
||||
// UI confirmation process is highly recommended.
|
||||
STEAM_METHOD_DESC(ConsumeItem() removes items from the inventory permanently.)
|
||||
bool ConsumeItem( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemConsume, uint32 unQuantity )
|
||||
{
|
||||
PRINT_DEBUG("ConsumeItem %llu %u\n", itemConsume, unQuantity);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
auto it = user_items.find(std::to_string(itemConsume));
|
||||
if (it != user_items.end()) {
|
||||
try
|
||||
{
|
||||
uint32 current = it->get<int>();
|
||||
PRINT_DEBUG("ConsumeItem previous %u\n", current);
|
||||
if (current < unQuantity) unQuantity = current;
|
||||
uint32 result = current - unQuantity;
|
||||
if (result == 0) {
|
||||
user_items.erase(it);
|
||||
} else {
|
||||
*it = result;
|
||||
}
|
||||
}
|
||||
catch (...) {}
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct Steam_Inventory_Requests* request = new_inventory_result(false, &itemConsume, 1);
|
||||
|
||||
if (pResultHandle != nullptr)
|
||||
*pResultHandle = request->inventory_result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ExchangeItems() is an atomic combination of item generation and consumption.
|
||||
// It can be used to implement crafting recipes or transmutations, or items which unpack
|
||||
// themselves into other items (e.g., a chest).
|
||||
// Exchange recipes are defined in the ItemDef, and explicitly list the required item
|
||||
// types and resulting generated type.
|
||||
// Exchange recipes are evaluated atomically by the Inventory Service; if the supplied
|
||||
// components do not match the recipe, or do not contain sufficient quantity, the
|
||||
// exchange will fail.
|
||||
bool ExchangeItems( SteamInventoryResult_t *pResultHandle,
|
||||
STEAM_ARRAY_COUNT(unArrayGenerateLength) const SteamItemDef_t *pArrayGenerate, STEAM_ARRAY_COUNT(unArrayGenerateLength) const uint32 *punArrayGenerateQuantity, uint32 unArrayGenerateLength,
|
||||
STEAM_ARRAY_COUNT(unArrayDestroyLength) const SteamItemInstanceID_t *pArrayDestroy, STEAM_ARRAY_COUNT(unArrayDestroyLength) const uint32 *punArrayDestroyQuantity, uint32 unArrayDestroyLength )
|
||||
{
|
||||
PRINT_DEBUG("ExchangeItems\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TransferItemQuantity() is intended for use with items which are "stackable" (can have
|
||||
// quantity greater than one). It can be used to split a stack into two, or to transfer
|
||||
// quantity from one stack into another stack of identical items. To split one stack into
|
||||
// two, pass k_SteamItemInstanceIDInvalid for itemIdDest and a new item will be generated.
|
||||
bool TransferItemQuantity( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemIdSource, uint32 unQuantity, SteamItemInstanceID_t itemIdDest )
|
||||
{
|
||||
PRINT_DEBUG("TransferItemQuantity\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TIMED DROPS AND PLAYTIME CREDIT
|
||||
//
|
||||
|
||||
// Deprecated. Calling this method is not required for proper playtime accounting.
|
||||
STEAM_METHOD_DESC( Deprecated method. Playtime accounting is performed on the Steam servers. )
|
||||
void SendItemDropHeartbeat()
|
||||
{
|
||||
PRINT_DEBUG("SendItemDropHeartbeat\n");
|
||||
}
|
||||
|
||||
|
||||
// Playtime credit must be consumed and turned into item drops by your game. Only item
|
||||
// definitions which are marked as "playtime item generators" can be spawned. The call
|
||||
// will return an empty result set if there is not enough playtime credit for a drop.
|
||||
// Your game should call TriggerItemDrop at an appropriate time for the user to receive
|
||||
// new items, such as between rounds or while the player is dead. Note that players who
|
||||
// hack their clients could modify the value of "dropListDefinition", so do not use it
|
||||
// to directly control rarity.
|
||||
// See your Steamworks configuration to set playtime drop rates for individual itemdefs.
|
||||
// The client library will suppress too-frequent calls to this method.
|
||||
STEAM_METHOD_DESC(Playtime credit must be consumed and turned into item drops by your game.)
|
||||
bool TriggerItemDrop( SteamInventoryResult_t *pResultHandle, SteamItemDef_t dropListDefinition )
|
||||
{
|
||||
PRINT_DEBUG("TriggerItemDrop %p %i\n", pResultHandle, dropListDefinition);
|
||||
//TODO: if gameserver return false
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests* request = new_inventory_result(false);
|
||||
|
||||
if (pResultHandle != nullptr)
|
||||
*pResultHandle = request->inventory_result;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Deprecated. This method is not supported.
|
||||
bool TradeItems( SteamInventoryResult_t *pResultHandle, CSteamID steamIDTradePartner,
|
||||
STEAM_ARRAY_COUNT(nArrayGiveLength) const SteamItemInstanceID_t *pArrayGive, STEAM_ARRAY_COUNT(nArrayGiveLength) const uint32 *pArrayGiveQuantity, uint32 nArrayGiveLength,
|
||||
STEAM_ARRAY_COUNT(nArrayGetLength) const SteamItemInstanceID_t *pArrayGet, STEAM_ARRAY_COUNT(nArrayGetLength) const uint32 *pArrayGetQuantity, uint32 nArrayGetLength )
|
||||
{
|
||||
PRINT_DEBUG("TradeItems\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ITEM DEFINITIONS
|
||||
//
|
||||
// Item definitions are a mapping of "definition IDs" (integers between 1 and 1000000)
|
||||
// to a set of string properties. Some of these properties are required to display items
|
||||
// on the Steam community web site. Other properties can be defined by applications.
|
||||
// Use of these functions is optional; there is no reason to call LoadItemDefinitions
|
||||
// if your game hardcodes the numeric definition IDs (eg, purple face mask = 20, blue
|
||||
// weapon mod = 55) and does not allow for adding new item types without a client patch.
|
||||
//
|
||||
|
||||
// LoadItemDefinitions triggers the automatic load and refresh of item definitions.
|
||||
// Every time new item definitions are available (eg, from the dynamic addition of new
|
||||
// item types while players are still in-game), a SteamInventoryDefinitionUpdate_t
|
||||
// callback will be fired.
|
||||
STEAM_METHOD_DESC(LoadItemDefinitions triggers the automatic load and refresh of item definitions.)
|
||||
bool LoadItemDefinitions()
|
||||
{
|
||||
PRINT_DEBUG("LoadItemDefinitions\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (!item_definitions_loaded) {
|
||||
call_definition_update = true;
|
||||
}
|
||||
|
||||
//real steam launches a SteamInventoryResultReady_t which is why I create a new inventory result
|
||||
new_inventory_result(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// GetItemDefinitionIDs returns the set of all defined item definition IDs (which are
|
||||
// defined via Steamworks configuration, and not necessarily contiguous integers).
|
||||
// If pItemDefIDs is null, the call will return true and *punItemDefIDsArraySize will
|
||||
// contain the total size necessary for a subsequent call. Otherwise, the call will
|
||||
// return false if and only if there is not enough space in the output array.
|
||||
bool GetItemDefinitionIDs(
|
||||
STEAM_OUT_ARRAY_COUNT(punItemDefIDsArraySize,List of item definition IDs) SteamItemDef_t *pItemDefIDs,
|
||||
STEAM_DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize )
|
||||
{
|
||||
PRINT_DEBUG("GetItemDefinitionIDs %p\n", pItemDefIDs);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (!punItemDefIDsArraySize)
|
||||
return false;
|
||||
|
||||
PRINT_DEBUG("array_size %u\n", *punItemDefIDsArraySize);
|
||||
|
||||
if (!item_definitions_loaded)
|
||||
return false;
|
||||
|
||||
if (pItemDefIDs == nullptr || *punItemDefIDsArraySize == 0)
|
||||
{
|
||||
*punItemDefIDsArraySize = defined_items.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*punItemDefIDsArraySize < defined_items.size())
|
||||
return false;
|
||||
|
||||
for (auto i = defined_items.begin(); i != defined_items.end(); ++i)
|
||||
*pItemDefIDs++ = std::stoi(i.key());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// GetItemDefinitionProperty returns a string property from a given item definition.
|
||||
// Note that some properties (for example, "name") may be localized and will depend
|
||||
// on the current Steam language settings (see ISteamApps::GetCurrentGameLanguage).
|
||||
// Property names are always composed of ASCII letters, numbers, and/or underscores.
|
||||
// Pass a NULL pointer for pchPropertyName to get a comma - separated list of available
|
||||
// property names. If pchValueBuffer is NULL, *punValueBufferSize will contain the
|
||||
// suggested buffer size. Otherwise it will be the number of bytes actually copied
|
||||
// to pchValueBuffer. If the results do not fit in the given buffer, partial
|
||||
// results may be copied.
|
||||
bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPropertyName,
|
||||
STEAM_OUT_STRING_COUNT(punValueBufferSizeOut) char *pchValueBuffer, uint32 *punValueBufferSizeOut )
|
||||
{
|
||||
PRINT_DEBUG("GetItemDefinitionProperty %i %s\n", iDefinition, pchPropertyName);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
auto item = defined_items.find(std::to_string(iDefinition));
|
||||
if (item != defined_items.end())
|
||||
{
|
||||
if (pchPropertyName != nullptr)
|
||||
{
|
||||
// Should I check for punValueBufferSizeOut == nullptr ?
|
||||
// Try to get the property
|
||||
auto attr = item.value().find(pchPropertyName);
|
||||
if (attr != item.value().end())
|
||||
{
|
||||
std::string val;
|
||||
try
|
||||
{
|
||||
val = attr.value().get<std::string>();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pchPropertyName = "";
|
||||
*punValueBufferSizeOut = 0;
|
||||
PRINT_DEBUG("Error, item: %d, attr: %s is not a string!", iDefinition, pchPropertyName);
|
||||
return true;
|
||||
}
|
||||
if (pchValueBuffer != nullptr)
|
||||
{
|
||||
// copy what we can
|
||||
strncpy(pchValueBuffer, val.c_str(), *punValueBufferSizeOut);
|
||||
*punValueBufferSizeOut = std::min(static_cast<uint32>(val.length() + 1), *punValueBufferSizeOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set punValueBufferSizeOut to the property size
|
||||
*punValueBufferSizeOut = val.length() + 1;
|
||||
}
|
||||
|
||||
if (pchValueBuffer != nullptr)
|
||||
{
|
||||
// Make sure we have a null terminator
|
||||
pchValueBuffer[*punValueBufferSizeOut-1] = '\0';
|
||||
}
|
||||
}
|
||||
// Property not found
|
||||
else
|
||||
{
|
||||
*punValueBufferSizeOut = 0;
|
||||
PRINT_DEBUG("Attr %s not found for item %d\n", pchPropertyName, iDefinition);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else // Pass a NULL pointer for pchPropertyName to get a comma - separated list of available property names.
|
||||
{
|
||||
// If pchValueBuffer is NULL, *punValueBufferSize will contain the suggested buffer size
|
||||
if (pchValueBuffer == nullptr)
|
||||
{
|
||||
// Should I check for punValueBufferSizeOut == nullptr ?
|
||||
*punValueBufferSizeOut = 0;
|
||||
for (auto i = item.value().begin(); i != item.value().end(); ++i)
|
||||
*punValueBufferSizeOut += i.key().length() + 1; // Size of key + comma, and the last is not a comma but null char
|
||||
}
|
||||
else
|
||||
{
|
||||
// strncat always add the null terminator, so remove 1 to the string length
|
||||
uint32_t len = *punValueBufferSizeOut-1;
|
||||
*punValueBufferSizeOut = 0;
|
||||
memset(pchValueBuffer, 0, len);
|
||||
for( auto i = item.value().begin(); i != item.value().end() && len > 0; ++i )
|
||||
{
|
||||
strncat(pchValueBuffer, i.key().c_str(), len);
|
||||
// Count how many chars we copied
|
||||
// Either the string length or the buffer size if its too small
|
||||
uint32 x = std::min(len, static_cast<uint32>(i.key().length()));
|
||||
*punValueBufferSizeOut += x;
|
||||
len -= x;
|
||||
|
||||
if (len && std::distance(i, item.value().end()) != 1) // If this is not the last item, add a comma
|
||||
strncat(pchValueBuffer, ",", len--);
|
||||
|
||||
// Always add 1, its a comma or the null terminator
|
||||
++*punValueBufferSizeOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Request the list of "eligible" promo items that can be manually granted to the given
|
||||
// user. These are promo items of type "manual" that won't be granted automatically.
|
||||
// An example usage of this is an item that becomes available every week.
|
||||
STEAM_CALL_RESULT( SteamInventoryEligiblePromoItemDefIDs_t )
|
||||
SteamAPICall_t RequestEligiblePromoItemDefinitionsIDs( CSteamID steamID )
|
||||
{
|
||||
PRINT_DEBUG("RequestEligiblePromoItemDefinitionsIDs\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// After handling a SteamInventoryEligiblePromoItemDefIDs_t call result, use this
|
||||
// function to pull out the list of item definition ids that the user can be
|
||||
// manually granted via the AddPromoItems() call.
|
||||
bool GetEligiblePromoItemDefinitionIDs(
|
||||
CSteamID steamID,
|
||||
STEAM_OUT_ARRAY_COUNT(punItemDefIDsArraySize,List of item definition IDs) SteamItemDef_t *pItemDefIDs,
|
||||
STEAM_DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize )
|
||||
{
|
||||
PRINT_DEBUG("GetEligiblePromoItemDefinitionIDs\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Starts the purchase process for the given item definitions. The callback SteamInventoryStartPurchaseResult_t
|
||||
// will be posted if Steam was able to initialize the transaction.
|
||||
//
|
||||
// Once the purchase has been authorized and completed by the user, the callback SteamInventoryResultReady_t
|
||||
// will be posted.
|
||||
STEAM_CALL_RESULT( SteamInventoryStartPurchaseResult_t )
|
||||
SteamAPICall_t StartPurchase( STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, STEAM_ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength )
|
||||
{
|
||||
PRINT_DEBUG("StartPurchase\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Request current prices for all applicable item definitions
|
||||
STEAM_CALL_RESULT( SteamInventoryRequestPricesResult_t )
|
||||
SteamAPICall_t RequestPrices()
|
||||
{
|
||||
PRINT_DEBUG("RequestPrices\n");
|
||||
SteamInventoryRequestPricesResult_t data;
|
||||
data.m_result = k_EResultOK;
|
||||
memcpy(data.m_rgchCurrency, "USD", 4);
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.2);
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of items with prices. Need to call RequestPrices() first.
|
||||
uint32 GetNumItemsWithPrices()
|
||||
{
|
||||
PRINT_DEBUG("GetNumItemsWithPrices\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GetItemsWithPrices( STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pArrayItemDefs, Items with prices) SteamItemDef_t *pArrayItemDefs,
|
||||
STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pPrices, List of prices for the given item defs) uint64 *pCurrentPrices,
|
||||
STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pPrices, List of prices for the given item defs) uint64 *pBasePrices,
|
||||
uint32 unArrayLength )
|
||||
{
|
||||
PRINT_DEBUG("GetItemsWithPrices\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns item definition ids and their prices in the user's local currency.
|
||||
// Need to call RequestPrices() first.
|
||||
bool GetItemsWithPrices( STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pArrayItemDefs, Items with prices) SteamItemDef_t *pArrayItemDefs,
|
||||
STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pPrices, List of prices for the given item defs) uint64 *pPrices,
|
||||
uint32 unArrayLength )
|
||||
{
|
||||
PRINT_DEBUG("GetItemsWithPrices old\n");
|
||||
return GetItemsWithPrices(pArrayItemDefs, pPrices, NULL, unArrayLength);
|
||||
}
|
||||
|
||||
bool GetItemPrice( SteamItemDef_t iDefinition, uint64 *pCurrentPrice, uint64 *pBasePrice )
|
||||
{
|
||||
PRINT_DEBUG("GetItemPrice\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieves the price for the item definition id
|
||||
// Returns false if there is no price stored for the item definition.
|
||||
bool GetItemPrice( SteamItemDef_t iDefinition, uint64 *pPrice )
|
||||
{
|
||||
PRINT_DEBUG("GetItemPrice old\n");
|
||||
return GetItemPrice(iDefinition, pPrice, NULL);
|
||||
}
|
||||
|
||||
|
||||
// Create a request to update properties on items
|
||||
SteamInventoryUpdateHandle_t StartUpdateProperties()
|
||||
{
|
||||
PRINT_DEBUG("StartUpdateProperties\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remove the property on the item
|
||||
bool RemoveProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName )
|
||||
{
|
||||
PRINT_DEBUG("RemoveProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Accessor methods to set properties on items
|
||||
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, const char *pchPropertyValue )
|
||||
{
|
||||
PRINT_DEBUG("SetProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("SetProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, int64 nValue )
|
||||
{
|
||||
PRINT_DEBUG("SetProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, float flValue )
|
||||
{
|
||||
PRINT_DEBUG("SetProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Submit the update request by handle
|
||||
bool SubmitUpdateProperties( SteamInventoryUpdateHandle_t handle, SteamInventoryResult_t * pResultHandle )
|
||||
{
|
||||
PRINT_DEBUG("SubmitUpdateProperties\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InspectItem( SteamInventoryResult_t *pResultHandle, const char *pchItemToken )
|
||||
{
|
||||
PRINT_DEBUG("InspectItem\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
if (call_definition_update || !inventory_requests.empty()) {
|
||||
if (!item_definitions_loaded) {
|
||||
read_items_db();
|
||||
item_definitions_loaded = true;
|
||||
|
||||
//only gets called once
|
||||
//also gets called when getting items
|
||||
SteamInventoryDefinitionUpdate_t data = {};
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.05);
|
||||
}
|
||||
|
||||
call_definition_update = false;
|
||||
}
|
||||
|
||||
if (!inventory_requests.empty() && !inventory_loaded) {
|
||||
read_inventory_db();
|
||||
inventory_loaded = true;
|
||||
}
|
||||
|
||||
if (inventory_loaded)
|
||||
{
|
||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||
for (auto& r : inventory_requests) {
|
||||
if (!r.done && std::chrono::duration_cast<std::chrono::duration<double>>(now - r.time_created).count() > r.timeout) {
|
||||
if (r.full_query) {
|
||||
// SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems
|
||||
// successfully returns a result which is newer / fresher than the last
|
||||
// known result.
|
||||
struct SteamInventoryFullUpdate_t data;
|
||||
data.m_handle = r.inventory_result;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
{
|
||||
struct SteamInventoryResultReady_t data;
|
||||
data.m_handle = r.inventory_result;
|
||||
data.m_result = k_EResultOK;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
r.done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
219
all/steam_masterserver_updater.h
Normal file
219
all/steam_masterserver_updater.h
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Masterserver_Updater :
|
||||
public ISteamMasterServerUpdater
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_masterserverupdater_callback\n");
|
||||
|
||||
Steam_Masterserver_Updater *steam_masterserverupdater = (Steam_Masterserver_Updater *)object;
|
||||
steam_masterserverupdater->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_masterserverupdater_run_every_runcb\n");
|
||||
|
||||
Steam_Masterserver_Updater *steam_masterserverupdater = (Steam_Masterserver_Updater *)object;
|
||||
steam_masterserverupdater->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Masterserver_Updater(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Masterserver_Updater::steam_callback, this);
|
||||
this->run_every_runcb->add(&Steam_Masterserver_Updater::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_Masterserver_Updater()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
this->run_every_runcb->remove(&Steam_Masterserver_Updater::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
// Call this as often as you like to tell the master server updater whether or not
|
||||
// you want it to be active (default: off).
|
||||
void SetActive( bool bActive )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::SetActive\n");
|
||||
}
|
||||
|
||||
|
||||
// You usually don't need to modify this.
|
||||
// Pass -1 to use the default value for iHeartbeatInterval.
|
||||
// Some mods change this.
|
||||
void SetHeartbeatInterval( int iHeartbeatInterval )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::SetHeartbeatInterval\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// These are in GameSocketShare mode, where instead of ISteamMasterServerUpdater creating its own
|
||||
// socket to talk to the master server on, it lets the game use its socket to forward messages
|
||||
// back and forth. This prevents us from requiring server ops to open up yet another port
|
||||
// in their firewalls.
|
||||
//
|
||||
// the IP address and port should be in host order, i.e 127.0.0.1 == 0x7f000001
|
||||
|
||||
// These are used when you've elected to multiplex the game server's UDP socket
|
||||
// rather than having the master server updater use its own sockets.
|
||||
//
|
||||
// Source games use this to simplify the job of the server admins, so they
|
||||
// don't have to open up more ports on their firewalls.
|
||||
|
||||
// Call this when a packet that starts with 0xFFFFFFFF comes in. That means
|
||||
// it's for us.
|
||||
bool HandleIncomingPacket( const void *pData, int cbData, uint32 srcIP, uint16 srcPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::HandleIncomingPacket\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// AFTER calling HandleIncomingPacket for any packets that came in that frame, call this.
|
||||
// This gets a packet that the master server updater needs to send out on UDP.
|
||||
// It returns the length of the packet it wants to send, or 0 if there are no more packets to send.
|
||||
// Call this each frame until it returns 0.
|
||||
int GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *pNetAdr, uint16 *pPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::GetNextOutgoingPacket\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Functions to set various fields that are used to respond to queries.
|
||||
|
||||
// Call this to set basic data that is passed to the server browser.
|
||||
void SetBasicServerData(
|
||||
unsigned short nProtocolVersion,
|
||||
bool bDedicatedServer,
|
||||
const char *pRegionName,
|
||||
const char *pProductName,
|
||||
unsigned short nMaxReportedClients,
|
||||
bool bPasswordProtected,
|
||||
const char *pGameDescription )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::SetBasicServerData\n");
|
||||
}
|
||||
|
||||
|
||||
// Call this to clear the whole list of key/values that are sent in rules queries.
|
||||
void ClearAllKeyValues()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::ClearAllKeyValues\n");
|
||||
}
|
||||
|
||||
|
||||
// Call this to add/update a key/value pair.
|
||||
void SetKeyValue( const char *pKey, const char *pValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::SetKeyValue\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// You can call this upon shutdown to clear out data stored for this game server and
|
||||
// to tell the master servers that this server is going away.
|
||||
void NotifyShutdown()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::NotifyShutdown\n");
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the master server has requested a restart.
|
||||
// Only returns true once per request.
|
||||
bool WasRestartRequested()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::WasRestartRequested\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Force it to request a heartbeat from the master servers.
|
||||
void ForceHeartbeat()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::ForceHeartbeat\n");
|
||||
}
|
||||
|
||||
|
||||
// Manually edit and query the master server list.
|
||||
// It will provide name resolution and use the default master server port if none is provided.
|
||||
bool AddMasterServer( const char *pServerAddress )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::AddMasterServer\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoveMasterServer( const char *pServerAddress )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::RemoveMasterServer\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int GetNumMasterServers()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::GetNumMasterServers\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Returns the # of bytes written to pOut.
|
||||
int GetMasterServerAddress( int iServer, char *pOut, int outBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Masterserver_Updater::GetMasterServerAddress\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
1546
all/steam_matchmaking.h
Normal file
1546
all/steam_matchmaking.h
Normal file
File diff suppressed because it is too large
Load diff
609
all/steam_matchmaking_servers.cpp
Normal file
609
all/steam_matchmaking_servers.cpp
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_matchmaking_servers.h"
|
||||
|
||||
|
||||
static void network_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_matchmaking_servers_callback\n");
|
||||
|
||||
Steam_Matchmaking_Servers *obj = (Steam_Matchmaking_Servers *)object;
|
||||
obj->Callback(msg);
|
||||
}
|
||||
|
||||
Steam_Matchmaking_Servers::Steam_Matchmaking_Servers(class Settings *settings, class Networking *network)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->network->setCallback(CALLBACK_ID_GAMESERVER, (uint64) 0, &network_callback, this);
|
||||
}
|
||||
|
||||
static int server_list_request;
|
||||
|
||||
// Request a new list of servers of a particular type. These calls each correspond to one of the EMatchMakingType values.
|
||||
// Each call allocates a new asynchronous request object.
|
||||
// Request object must be released by calling ReleaseRequest( hServerListRequest )
|
||||
HServerListRequest Steam_Matchmaking_Servers::RequestInternetServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("RequestInternetServerList\n");
|
||||
//TODO
|
||||
return RequestLANServerList(iApp, pRequestServersResponse);
|
||||
}
|
||||
|
||||
HServerListRequest Steam_Matchmaking_Servers::RequestLANServerList( AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("RequestLANServerList %u\n", iApp);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Matchmaking_Request request;
|
||||
request.appid = iApp;
|
||||
request.callbacks = pRequestServersResponse;
|
||||
request.old_callbacks = NULL;
|
||||
request.cancelled = false;
|
||||
request.completed = false;
|
||||
requests.push_back(request);
|
||||
++server_list_request;
|
||||
requests[requests.size() - 1].id = (void *)server_list_request;
|
||||
HServerListRequest id = requests[requests.size() - 1].id;
|
||||
PRINT_DEBUG("request id: %p\n", id);
|
||||
return id;
|
||||
}
|
||||
|
||||
HServerListRequest Steam_Matchmaking_Servers::RequestFriendsServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("RequestFriendsServerList\n");
|
||||
//TODO
|
||||
return RequestLANServerList(iApp, pRequestServersResponse);
|
||||
}
|
||||
|
||||
HServerListRequest Steam_Matchmaking_Servers::RequestFavoritesServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("RequestFavoritesServerList\n");
|
||||
//TODO
|
||||
return RequestLANServerList(iApp, pRequestServersResponse);
|
||||
}
|
||||
|
||||
HServerListRequest Steam_Matchmaking_Servers::RequestHistoryServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("RequestHistoryServerList\n");
|
||||
//TODO
|
||||
return RequestLANServerList(iApp, pRequestServersResponse);
|
||||
}
|
||||
|
||||
HServerListRequest Steam_Matchmaking_Servers::RequestSpectatorServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("RequestSpectatorServerList\n");
|
||||
//TODO
|
||||
return RequestLANServerList(iApp, pRequestServersResponse);
|
||||
}
|
||||
|
||||
void Steam_Matchmaking_Servers::RequestOldServerList(AppId_t iApp, ISteamMatchmakingServerListResponse001 *pRequestServersResponse, EMatchMakingType type)
|
||||
{
|
||||
PRINT_DEBUG("RequestOldServerList %u\n", iApp);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto g = std::begin(requests);
|
||||
while (g != std::end(requests)) {
|
||||
if (g->id == ((void *)type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
++g;
|
||||
}
|
||||
|
||||
struct Steam_Matchmaking_Request request;
|
||||
request.appid = iApp;
|
||||
request.callbacks = NULL;
|
||||
request.old_callbacks = pRequestServersResponse;
|
||||
request.cancelled = false;
|
||||
request.completed = false;
|
||||
requests.push_back(request);
|
||||
requests[requests.size() - 1].id = (void *)type;
|
||||
}
|
||||
|
||||
void Steam_Matchmaking_Servers::RequestInternetServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse001 *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("%s old\n", __FUNCTION__);
|
||||
//TODO
|
||||
RequestOldServerList(iApp, pRequestServersResponse, eInternetServer);
|
||||
}
|
||||
|
||||
void Steam_Matchmaking_Servers::RequestLANServerList( AppId_t iApp, ISteamMatchmakingServerListResponse001 *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("%s old\n", __FUNCTION__);
|
||||
//TODO
|
||||
RequestOldServerList(iApp, pRequestServersResponse, eLANServer);
|
||||
}
|
||||
|
||||
void Steam_Matchmaking_Servers::RequestFriendsServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse001 *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("%s old\n", __FUNCTION__);
|
||||
//TODO
|
||||
RequestOldServerList(iApp, pRequestServersResponse, eFriendsServer);
|
||||
}
|
||||
|
||||
void Steam_Matchmaking_Servers::RequestFavoritesServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse001 *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("%s old\n", __FUNCTION__);
|
||||
//TODO
|
||||
RequestOldServerList(iApp, pRequestServersResponse, eFavoritesServer);
|
||||
}
|
||||
|
||||
void Steam_Matchmaking_Servers::RequestHistoryServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse001 *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("%s old\n", __FUNCTION__);
|
||||
//TODO
|
||||
RequestOldServerList(iApp, pRequestServersResponse, eHistoryServer);
|
||||
}
|
||||
|
||||
void Steam_Matchmaking_Servers::RequestSpectatorServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse001 *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("%s old\n", __FUNCTION__);
|
||||
//TODO
|
||||
RequestOldServerList(iApp, pRequestServersResponse, eSpectatorServer);
|
||||
}
|
||||
|
||||
|
||||
// Releases the asynchronous request object and cancels any pending query on it if there's a pending query in progress.
|
||||
// RefreshComplete callback is not posted when request is released.
|
||||
void Steam_Matchmaking_Servers::ReleaseRequest( HServerListRequest hServerListRequest )
|
||||
{
|
||||
PRINT_DEBUG("ReleaseRequest %p\n", hServerListRequest);
|
||||
auto g = std::begin(requests);
|
||||
while (g != std::end(requests)) {
|
||||
if (g->id == hServerListRequest) {
|
||||
//NOTE: some garbage games release the request before getting server details from it.
|
||||
// g = requests.erase(g);
|
||||
// } else {
|
||||
//TODO: eventually delete the released request.
|
||||
g->cancelled = true;
|
||||
g->released = true;
|
||||
}
|
||||
|
||||
++g;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* the filter operation codes that go in the key part of MatchMakingKeyValuePair_t should be one of these:
|
||||
|
||||
"map"
|
||||
- Server passes the filter if the server is playing the specified map.
|
||||
"gamedataand"
|
||||
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) contains all of the
|
||||
specified strings. The value field is a comma-delimited list of strings to match.
|
||||
"gamedataor"
|
||||
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) contains at least one of the
|
||||
specified strings. The value field is a comma-delimited list of strings to match.
|
||||
"gamedatanor"
|
||||
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) does not contain any
|
||||
of the specified strings. The value field is a comma-delimited list of strings to check.
|
||||
"gametagsand"
|
||||
- Server passes the filter if the server's game tags (ISteamGameServer::SetGameTags) contains all
|
||||
of the specified strings. The value field is a comma-delimited list of strings to check.
|
||||
"gametagsnor"
|
||||
- Server passes the filter if the server's game tags (ISteamGameServer::SetGameTags) does not contain any
|
||||
of the specified strings. The value field is a comma-delimited list of strings to check.
|
||||
"and" (x1 && x2 && ... && xn)
|
||||
"or" (x1 || x2 || ... || xn)
|
||||
"nand" !(x1 && x2 && ... && xn)
|
||||
"nor" !(x1 || x2 || ... || xn)
|
||||
- Performs Boolean operation on the following filters. The operand to this filter specifies
|
||||
the "size" of the Boolean inputs to the operation, in Key/value pairs. (The keyvalue
|
||||
pairs must immediately follow, i.e. this is a prefix logical operator notation.)
|
||||
In the simplest case where Boolean expressions are not nested, this is simply
|
||||
the number of operands.
|
||||
|
||||
For example, to match servers on a particular map or with a particular tag, would would
|
||||
use these filters.
|
||||
|
||||
( server.map == "cp_dustbowl" || server.gametags.contains("payload") )
|
||||
"or", "2"
|
||||
"map", "cp_dustbowl"
|
||||
"gametagsand", "payload"
|
||||
|
||||
If logical inputs are nested, then the operand specifies the size of the entire
|
||||
"length" of its operands, not the number of immediate children.
|
||||
|
||||
( server.map == "cp_dustbowl" || ( server.gametags.contains("payload") && !server.gametags.contains("payloadrace") ) )
|
||||
"or", "4"
|
||||
"map", "cp_dustbowl"
|
||||
"and", "2"
|
||||
"gametagsand", "payload"
|
||||
"gametagsnor", "payloadrace"
|
||||
|
||||
Unary NOT can be achieved using either "nand" or "nor" with a single operand.
|
||||
|
||||
"addr"
|
||||
- Server passes the filter if the server's query address matches the specified IP or IP:port.
|
||||
"gameaddr"
|
||||
- Server passes the filter if the server's game address matches the specified IP or IP:port.
|
||||
|
||||
The following filter operations ignore the "value" part of MatchMakingKeyValuePair_t
|
||||
|
||||
"dedicated"
|
||||
- Server passes the filter if it passed true to SetDedicatedServer.
|
||||
"secure"
|
||||
- Server passes the filter if the server is VAC-enabled.
|
||||
"notfull"
|
||||
- Server passes the filter if the player count is less than the reported max player count.
|
||||
"hasplayers"
|
||||
- Server passes the filter if the player count is greater than zero.
|
||||
"noplayers"
|
||||
- Server passes the filter if it doesn't have any players.
|
||||
"linux"
|
||||
- Server passes the filter if it's a linux server
|
||||
*/
|
||||
|
||||
void Steam_Matchmaking_Servers::server_details(Gameserver *g, gameserveritem_t *server)
|
||||
{
|
||||
uint16 query_port = g->query_port();
|
||||
if (g->query_port() == 0xFFFF) {
|
||||
query_port = g->port();
|
||||
}
|
||||
|
||||
server->m_NetAdr.Init(g->ip(), query_port, g->port());
|
||||
server->m_nPing = 10; //TODO
|
||||
server->m_bHadSuccessfulResponse = true;
|
||||
server->m_bDoNotRefresh = false;
|
||||
strncpy(server->m_szGameDir, g->mod_dir().c_str(), k_cbMaxGameServerGameDir - 1);
|
||||
strncpy(server->m_szMap, g->map_name().c_str(), k_cbMaxGameServerMapName - 1);
|
||||
strncpy(server->m_szGameDescription, g->game_description().c_str(), k_cbMaxGameServerGameDescription - 1);
|
||||
|
||||
server->m_szGameDir[k_cbMaxGameServerGameDir - 1] = 0;
|
||||
server->m_szMap[k_cbMaxGameServerMapName - 1] = 0;
|
||||
server->m_szGameDescription[k_cbMaxGameServerGameDescription - 1] = 0;
|
||||
|
||||
server->m_nAppID = g->appid();
|
||||
server->m_nPlayers = g->num_players();
|
||||
server->m_nMaxPlayers = g->max_player_count();
|
||||
server->m_nBotPlayers = g->bot_player_count();
|
||||
server->m_bPassword = g->password_protected();
|
||||
server->m_bSecure = g->secure();
|
||||
server->m_ulTimeLastPlayed = 0;
|
||||
server->m_nServerVersion = g->version();
|
||||
server->SetName(g->server_name().c_str());
|
||||
server->m_steamID = CSteamID((uint64)g->id());
|
||||
PRINT_DEBUG("server_details %llu\n", g->id());
|
||||
|
||||
strncpy(server->m_szGameTags, g->tags().c_str(), k_cbMaxGameServerTags - 1);
|
||||
server->m_szGameTags[k_cbMaxGameServerTags - 1] = 0;
|
||||
}
|
||||
|
||||
// Get details on a given server in the list, you can get the valid range of index
|
||||
// values by calling GetServerCount(). You will also receive index values in
|
||||
// ISteamMatchmakingServerListResponse::ServerResponded() callbacks
|
||||
gameserveritem_t *Steam_Matchmaking_Servers::GetServerDetails( HServerListRequest hRequest, int iServer )
|
||||
{
|
||||
PRINT_DEBUG("GetServerDetails %p %i\n", hRequest, iServer);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers_filtered;
|
||||
auto g = std::begin(requests);
|
||||
while (g != std::end(requests)) {
|
||||
PRINT_DEBUG("equal? %p %p\n", hRequest, g->id);
|
||||
if (g->id == hRequest) {
|
||||
gameservers_filtered = g->gameservers_filtered;
|
||||
PRINT_DEBUG("found %u\n", gameservers_filtered.size());
|
||||
break;
|
||||
}
|
||||
|
||||
++g;
|
||||
}
|
||||
|
||||
if (iServer >= gameservers_filtered.size() || iServer < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Gameserver *gs = &gameservers_filtered[iServer].server;
|
||||
gameserveritem_t *server = new gameserveritem_t(); //TODO: is the new here ok?
|
||||
server_details(gs, server);
|
||||
PRINT_DEBUG("Returned server details\n");
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
// Cancel an request which is operation on the given list type. You should call this to cancel
|
||||
// any in-progress requests before destructing a callback object that may have been passed
|
||||
// to one of the above list request calls. Not doing so may result in a crash when a callback
|
||||
// occurs on the destructed object.
|
||||
// Canceling a query does not release the allocated request handle.
|
||||
// The request handle must be released using ReleaseRequest( hRequest )
|
||||
void Steam_Matchmaking_Servers::CancelQuery( HServerListRequest hRequest )
|
||||
{
|
||||
PRINT_DEBUG("CancelQuery %p\n", hRequest);
|
||||
auto g = std::begin(requests);
|
||||
while (g != std::end(requests)) {
|
||||
if (g->id == hRequest) {
|
||||
g->cancelled = true;
|
||||
}
|
||||
|
||||
++g;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Ping every server in your list again but don't update the list of servers
|
||||
// Query callback installed when the server list was requested will be used
|
||||
// again to post notifications and RefreshComplete, so the callback must remain
|
||||
// valid until another RefreshComplete is called on it or the request
|
||||
// is released with ReleaseRequest( hRequest )
|
||||
void Steam_Matchmaking_Servers::RefreshQuery( HServerListRequest hRequest )
|
||||
{
|
||||
PRINT_DEBUG("RefreshQuery %p\n", hRequest);
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the list is currently refreshing its server list
|
||||
bool Steam_Matchmaking_Servers::IsRefreshing( HServerListRequest hRequest )
|
||||
{
|
||||
PRINT_DEBUG("IsRefreshing %p\n", hRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// How many servers in the given list, GetServerDetails above takes 0... GetServerCount() - 1
|
||||
int Steam_Matchmaking_Servers::GetServerCount( HServerListRequest hRequest )
|
||||
{
|
||||
PRINT_DEBUG("GetServerCount %p\n", hRequest);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
int size = 0;
|
||||
auto g = std::begin(requests);
|
||||
while (g != std::end(requests)) {
|
||||
if (g->id == hRequest) {
|
||||
size = g->gameservers_filtered.size();
|
||||
break;
|
||||
}
|
||||
|
||||
++g;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
// Refresh a single server inside of a query (rather than all the servers )
|
||||
void Steam_Matchmaking_Servers::RefreshServer( HServerListRequest hRequest, int iServer )
|
||||
{
|
||||
PRINT_DEBUG("RefreshServer %p\n", hRequest);
|
||||
//TODO
|
||||
}
|
||||
|
||||
static HServerQuery new_server_query()
|
||||
{
|
||||
static int a;
|
||||
++a;
|
||||
if (!a) ++a;
|
||||
return a;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Queries to individual servers directly via IP/Port
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Request updated ping time and other details from a single server
|
||||
HServerQuery Steam_Matchmaking_Servers::PingServer( uint32 unIP, uint16 usPort, ISteamMatchmakingPingResponse *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("PingServer %hhu.%hhu.%hhu.%hhu:%hu\n", ((unsigned char *)&unIP)[3], ((unsigned char *)&unIP)[2], ((unsigned char *)&unIP)[1], ((unsigned char *)&unIP)[0], usPort);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
Steam_Matchmaking_Servers_Direct_IP_Request r;
|
||||
r.id = new_server_query();
|
||||
r.ip = unIP;
|
||||
r.port = usPort;
|
||||
r.ping_response = pRequestServersResponse;
|
||||
r.created = std::chrono::high_resolution_clock::now();
|
||||
direct_ip_requests.push_back(r);
|
||||
return r.id;
|
||||
}
|
||||
|
||||
// Request the list of players currently playing on a server
|
||||
HServerQuery Steam_Matchmaking_Servers::PlayerDetails( uint32 unIP, uint16 usPort, ISteamMatchmakingPlayersResponse *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("PlayerDetails %hhu.%hhu.%hhu.%hhu:%hu\n", ((unsigned char *)&unIP)[3], ((unsigned char *)&unIP)[2], ((unsigned char *)&unIP)[1], ((unsigned char *)&unIP)[0], usPort);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
Steam_Matchmaking_Servers_Direct_IP_Request r;
|
||||
r.id = new_server_query();
|
||||
r.ip = unIP;
|
||||
r.port = usPort;
|
||||
r.players_response = pRequestServersResponse;
|
||||
r.created = std::chrono::high_resolution_clock::now();
|
||||
direct_ip_requests.push_back(r);
|
||||
return r.id;
|
||||
}
|
||||
|
||||
|
||||
// Request the list of rules that the server is running (See ISteamGameServer::SetKeyValue() to set the rules server side)
|
||||
HServerQuery Steam_Matchmaking_Servers::ServerRules( uint32 unIP, uint16 usPort, ISteamMatchmakingRulesResponse *pRequestServersResponse )
|
||||
{
|
||||
PRINT_DEBUG("ServerRules %hhu.%hhu.%hhu.%hhu:%hu\n", ((unsigned char *)&unIP)[3], ((unsigned char *)&unIP)[2], ((unsigned char *)&unIP)[1], ((unsigned char *)&unIP)[0], usPort);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
Steam_Matchmaking_Servers_Direct_IP_Request r;
|
||||
r.id = new_server_query();
|
||||
r.ip = unIP;
|
||||
r.port = usPort;
|
||||
r.rules_response = pRequestServersResponse;
|
||||
r.created = std::chrono::high_resolution_clock::now();
|
||||
direct_ip_requests.push_back(r);
|
||||
return r.id;
|
||||
}
|
||||
|
||||
|
||||
// Cancel an outstanding Ping/Players/Rules query from above. You should call this to cancel
|
||||
// any in-progress requests before destructing a callback object that may have been passed
|
||||
// to one of the above calls to avoid crashing when callbacks occur.
|
||||
void Steam_Matchmaking_Servers::CancelServerQuery( HServerQuery hServerQuery )
|
||||
{
|
||||
PRINT_DEBUG("CancelServerQuery\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto r = std::find_if(direct_ip_requests.begin(), direct_ip_requests.end(), [&hServerQuery](Steam_Matchmaking_Servers_Direct_IP_Request const& item) { return item.id == hServerQuery; });
|
||||
if (direct_ip_requests.end() == r) return;
|
||||
direct_ip_requests.erase(r);
|
||||
}
|
||||
|
||||
void Steam_Matchmaking_Servers::RunCallbacks()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Matchmaking_Servers::RunCallbacks\n");
|
||||
|
||||
{
|
||||
auto g = std::begin(gameservers);
|
||||
while (g != std::end(gameservers)) {
|
||||
if (check_timedout(g->last_recv, SERVER_TIMEOUT)) {
|
||||
g = gameservers.erase(g);
|
||||
PRINT_DEBUG("SERVER TIMEOUT\n");
|
||||
} else {
|
||||
++g;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_DEBUG("REQUESTS %zu gs: %zu\n", requests.size(), gameservers.size());
|
||||
|
||||
for (auto &r : requests) {
|
||||
if (r.cancelled || r.completed) continue;
|
||||
|
||||
r.gameservers_filtered.clear();
|
||||
for (auto &g : gameservers) {
|
||||
PRINT_DEBUG("game_server_check %u %u\n", g.server.appid(), r.appid);
|
||||
if (g.server.appid() == r.appid) {
|
||||
PRINT_DEBUG("REQUESTS server found\n");
|
||||
r.gameservers_filtered.push_back(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector <struct Steam_Matchmaking_Request> requests_temp(requests);
|
||||
PRINT_DEBUG("REQUESTS_TEMP %zu\n", requests_temp.size());
|
||||
for (auto &r : requests) {
|
||||
r.completed = true;
|
||||
}
|
||||
|
||||
for (auto &r : requests_temp) {
|
||||
if (r.cancelled || r.completed) continue;
|
||||
int i = 0;
|
||||
|
||||
if (r.callbacks) {
|
||||
for (auto &g : r.gameservers_filtered) {
|
||||
PRINT_DEBUG("REQUESTS server responded cb %p\n", r.id);
|
||||
r.callbacks->ServerResponded(r.id, i);
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i) {
|
||||
r.callbacks->RefreshComplete(r.id, eServerResponded);
|
||||
} else {
|
||||
r.callbacks->RefreshComplete(r.id, eNoServersListedOnMasterServer);
|
||||
}
|
||||
}
|
||||
|
||||
if (r.old_callbacks) {
|
||||
for (auto &g : r.gameservers_filtered) {
|
||||
PRINT_DEBUG("old REQUESTS server responded cb %p\n", r.id);
|
||||
r.old_callbacks->ServerResponded(i);
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i) {
|
||||
r.old_callbacks->RefreshComplete(eServerResponded);
|
||||
} else {
|
||||
r.old_callbacks->RefreshComplete(eNoServersListedOnMasterServer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests_temp;
|
||||
auto dip = std::begin(direct_ip_requests);
|
||||
while (dip != std::end(direct_ip_requests)) {
|
||||
if (check_timedout(dip->created, DIRECT_IP_DELAY)) {
|
||||
direct_ip_requests_temp.push_back(*dip);
|
||||
dip = direct_ip_requests.erase(dip);
|
||||
} else {
|
||||
++dip;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &r : direct_ip_requests_temp) {
|
||||
PRINT_DEBUG("dip request: %lu:%hu\n", r.ip, r.port);
|
||||
for (auto &g : gameservers) {
|
||||
PRINT_DEBUG("server: %lu:%hu\n", g.server.ip(), g.server.query_port());
|
||||
uint16 query_port = g.server.query_port();
|
||||
if (query_port == 0xFFFF) {
|
||||
query_port = g.server.port();
|
||||
}
|
||||
|
||||
if (query_port == r.port && g.server.ip() == r.ip) {
|
||||
if (r.rules_response) {
|
||||
int number_rules = g.server.values().size();
|
||||
PRINT_DEBUG("rules: %lu\n", number_rules);
|
||||
auto rule = g.server.values().begin();
|
||||
for (int i = 0; i < number_rules; ++i) {
|
||||
PRINT_DEBUG("RULE %s %s\n", rule->first.c_str(), rule->second.c_str());
|
||||
r.rules_response->RulesResponded(rule->first.c_str(), rule->second.c_str());
|
||||
++rule;
|
||||
}
|
||||
|
||||
r.rules_response->RulesRefreshComplete();
|
||||
r.rules_response = NULL;
|
||||
}
|
||||
|
||||
if (r.ping_response) {
|
||||
gameserveritem_t server;
|
||||
server_details(&(g.server), &server);
|
||||
r.ping_response->ServerResponded(server);
|
||||
r.ping_response = NULL;
|
||||
}
|
||||
//TODO: players response
|
||||
}
|
||||
}
|
||||
|
||||
if (r.rules_response) r.rules_response->RulesRefreshComplete();
|
||||
//TODO: player response
|
||||
if (r.players_response) r.players_response->PlayersRefreshComplete();
|
||||
if (r.ping_response) r.ping_response->ServerFailedToRespond();
|
||||
}
|
||||
}
|
||||
|
||||
void Steam_Matchmaking_Servers::Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_gameserver()) {
|
||||
PRINT_DEBUG("got SERVER %llu, offline:%u\n", msg->gameserver().id(), msg->gameserver().offline());
|
||||
if (msg->gameserver().offline()) {
|
||||
for (auto &g : gameservers) {
|
||||
if (g.server.id() == msg->gameserver().id()) {
|
||||
g.last_recv = std::chrono::high_resolution_clock::time_point();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool already = false;
|
||||
for (auto &g : gameservers) {
|
||||
if (g.server.id() == msg->gameserver().id()) {
|
||||
g.last_recv = std::chrono::high_resolution_clock::now();
|
||||
g.server = msg->gameserver();
|
||||
g.server.set_ip(msg->source_ip());
|
||||
already = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!already) {
|
||||
struct Steam_Matchmaking_Servers_Gameserver g;
|
||||
g.last_recv = std::chrono::high_resolution_clock::now();
|
||||
g.server = msg->gameserver();
|
||||
g.server.set_ip(msg->source_ip());
|
||||
gameservers.push_back(g);
|
||||
PRINT_DEBUG("SERVER ADDED\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
225
all/steam_matchmaking_servers.h
Normal file
225
all/steam_matchmaking_servers.h
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SERVER_TIMEOUT 10.0
|
||||
#define DIRECT_IP_DELAY 0.05
|
||||
|
||||
struct Steam_Matchmaking_Servers_Direct_IP_Request {
|
||||
HServerQuery id;
|
||||
uint32 ip;
|
||||
uint16 port;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point created;
|
||||
ISteamMatchmakingRulesResponse *rules_response = NULL;
|
||||
ISteamMatchmakingPlayersResponse *players_response = NULL;
|
||||
ISteamMatchmakingPingResponse *ping_response = NULL;
|
||||
};
|
||||
|
||||
struct Steam_Matchmaking_Servers_Gameserver {
|
||||
Gameserver server;
|
||||
std::chrono::high_resolution_clock::time_point last_recv;
|
||||
};
|
||||
|
||||
struct Steam_Matchmaking_Request {
|
||||
AppId_t appid;
|
||||
HServerListRequest id;
|
||||
ISteamMatchmakingServerListResponse *callbacks;
|
||||
ISteamMatchmakingServerListResponse001 *old_callbacks;
|
||||
bool completed, cancelled, released;
|
||||
std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers_filtered;
|
||||
};
|
||||
|
||||
class Steam_Matchmaking_Servers : public ISteamMatchmakingServers,
|
||||
public ISteamMatchmakingServers001
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
|
||||
std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers;
|
||||
std::vector <struct Steam_Matchmaking_Request> requests;
|
||||
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests;
|
||||
void RequestOldServerList(AppId_t iApp, ISteamMatchmakingServerListResponse001 *pRequestServersResponse, EMatchMakingType type);
|
||||
public:
|
||||
Steam_Matchmaking_Servers(class Settings *settings, class Networking *network);
|
||||
// Request a new list of servers of a particular type. These calls each correspond to one of the EMatchMakingType values.
|
||||
// Each call allocates a new asynchronous request object.
|
||||
// Request object must be released by calling ReleaseRequest( hServerListRequest )
|
||||
HServerListRequest RequestInternetServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse );
|
||||
HServerListRequest RequestLANServerList( AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse );
|
||||
HServerListRequest RequestFriendsServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse );
|
||||
HServerListRequest RequestFavoritesServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse );
|
||||
HServerListRequest RequestHistoryServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse );
|
||||
HServerListRequest RequestSpectatorServerList( AppId_t iApp, STEAM_ARRAY_COUNT(nFilters) MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse );
|
||||
|
||||
void RequestInternetServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse001 *pRequestServersResponse );
|
||||
void RequestLANServerList( AppId_t iApp, ISteamMatchmakingServerListResponse001 *pRequestServersResponse );
|
||||
void RequestFriendsServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse001 *pRequestServersResponse );
|
||||
void RequestFavoritesServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse001 *pRequestServersResponse );
|
||||
void RequestHistoryServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse001 *pRequestServersResponse );
|
||||
void RequestSpectatorServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse001 *pRequestServersResponse );
|
||||
|
||||
// Releases the asynchronous request object and cancels any pending query on it if there's a pending query in progress.
|
||||
// RefreshComplete callback is not posted when request is released.
|
||||
void ReleaseRequest( HServerListRequest hServerListRequest );
|
||||
|
||||
/* the filter operation codes that go in the key part of MatchMakingKeyValuePair_t should be one of these:
|
||||
|
||||
"map"
|
||||
- Server passes the filter if the server is playing the specified map.
|
||||
"gamedataand"
|
||||
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) contains all of the
|
||||
specified strings. The value field is a comma-delimited list of strings to match.
|
||||
"gamedataor"
|
||||
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) contains at least one of the
|
||||
specified strings. The value field is a comma-delimited list of strings to match.
|
||||
"gamedatanor"
|
||||
- Server passes the filter if the server's game data (ISteamGameServer::SetGameData) does not contain any
|
||||
of the specified strings. The value field is a comma-delimited list of strings to check.
|
||||
"gametagsand"
|
||||
- Server passes the filter if the server's game tags (ISteamGameServer::SetGameTags) contains all
|
||||
of the specified strings. The value field is a comma-delimited list of strings to check.
|
||||
"gametagsnor"
|
||||
- Server passes the filter if the server's game tags (ISteamGameServer::SetGameTags) does not contain any
|
||||
of the specified strings. The value field is a comma-delimited list of strings to check.
|
||||
"and" (x1 && x2 && ... && xn)
|
||||
"or" (x1 || x2 || ... || xn)
|
||||
"nand" !(x1 && x2 && ... && xn)
|
||||
"nor" !(x1 || x2 || ... || xn)
|
||||
- Performs Boolean operation on the following filters. The operand to this filter specifies
|
||||
the "size" of the Boolean inputs to the operation, in Key/value pairs. (The keyvalue
|
||||
pairs must immediately follow, i.e. this is a prefix logical operator notation.)
|
||||
In the simplest case where Boolean expressions are not nested, this is simply
|
||||
the number of operands.
|
||||
|
||||
For example, to match servers on a particular map or with a particular tag, would would
|
||||
use these filters.
|
||||
|
||||
( server.map == "cp_dustbowl" || server.gametags.contains("payload") )
|
||||
"or", "2"
|
||||
"map", "cp_dustbowl"
|
||||
"gametagsand", "payload"
|
||||
|
||||
If logical inputs are nested, then the operand specifies the size of the entire
|
||||
"length" of its operands, not the number of immediate children.
|
||||
|
||||
( server.map == "cp_dustbowl" || ( server.gametags.contains("payload") && !server.gametags.contains("payloadrace") ) )
|
||||
"or", "4"
|
||||
"map", "cp_dustbowl"
|
||||
"and", "2"
|
||||
"gametagsand", "payload"
|
||||
"gametagsnor", "payloadrace"
|
||||
|
||||
Unary NOT can be achieved using either "nand" or "nor" with a single operand.
|
||||
|
||||
"addr"
|
||||
- Server passes the filter if the server's query address matches the specified IP or IP:port.
|
||||
"gameaddr"
|
||||
- Server passes the filter if the server's game address matches the specified IP or IP:port.
|
||||
|
||||
The following filter operations ignore the "value" part of MatchMakingKeyValuePair_t
|
||||
|
||||
"dedicated"
|
||||
- Server passes the filter if it passed true to SetDedicatedServer.
|
||||
"secure"
|
||||
- Server passes the filter if the server is VAC-enabled.
|
||||
"notfull"
|
||||
- Server passes the filter if the player count is less than the reported max player count.
|
||||
"hasplayers"
|
||||
- Server passes the filter if the player count is greater than zero.
|
||||
"noplayers"
|
||||
- Server passes the filter if it doesn't have any players.
|
||||
"linux"
|
||||
- Server passes the filter if it's a linux server
|
||||
*/
|
||||
|
||||
// Get details on a given server in the list, you can get the valid range of index
|
||||
// values by calling GetServerCount(). You will also receive index values in
|
||||
// ISteamMatchmakingServerListResponse::ServerResponded() callbacks
|
||||
gameserveritem_t *GetServerDetails( HServerListRequest hRequest, int iServer );
|
||||
|
||||
// Cancel an request which is operation on the given list type. You should call this to cancel
|
||||
// any in-progress requests before destructing a callback object that may have been passed
|
||||
// to one of the above list request calls. Not doing so may result in a crash when a callback
|
||||
// occurs on the destructed object.
|
||||
// Canceling a query does not release the allocated request handle.
|
||||
// The request handle must be released using ReleaseRequest( hRequest )
|
||||
void CancelQuery( HServerListRequest hRequest );
|
||||
|
||||
// Ping every server in your list again but don't update the list of servers
|
||||
// Query callback installed when the server list was requested will be used
|
||||
// again to post notifications and RefreshComplete, so the callback must remain
|
||||
// valid until another RefreshComplete is called on it or the request
|
||||
// is released with ReleaseRequest( hRequest )
|
||||
void RefreshQuery( HServerListRequest hRequest );
|
||||
|
||||
// Returns true if the list is currently refreshing its server list
|
||||
bool IsRefreshing( HServerListRequest hRequest );
|
||||
|
||||
// How many servers in the given list, GetServerDetails above takes 0... GetServerCount() - 1
|
||||
int GetServerCount( HServerListRequest hRequest );
|
||||
|
||||
// Refresh a single server inside of a query (rather than all the servers )
|
||||
void RefreshServer( HServerListRequest hRequest, int iServer );
|
||||
|
||||
// Get details on a given server in the list, you can get the valid range of index
|
||||
// values by calling GetServerCount(). You will also receive index values in
|
||||
// ISteamMatchmakingServerListResponse::ServerResponded() callbacks
|
||||
gameserveritem_t *GetServerDetails( EMatchMakingType eType, int iServer ) { return GetServerDetails((HServerListRequest) eType , iServer ); }
|
||||
|
||||
// Cancel an request which is operation on the given list type. You should call this to cancel
|
||||
// any in-progress requests before destructing a callback object that may have been passed
|
||||
// to one of the above list request calls. Not doing so may result in a crash when a callback
|
||||
// occurs on the destructed object.
|
||||
void CancelQuery( EMatchMakingType eType ) { return CancelQuery((HServerListRequest) eType); }
|
||||
|
||||
// Ping every server in your list again but don't update the list of servers
|
||||
void RefreshQuery( EMatchMakingType eType ) { return RefreshQuery((HServerListRequest) eType); }
|
||||
|
||||
// Returns true if the list is currently refreshing its server list
|
||||
bool IsRefreshing( EMatchMakingType eType ) { return IsRefreshing((HServerListRequest) eType); }
|
||||
|
||||
// How many servers in the given list, GetServerDetails above takes 0... GetServerCount() - 1
|
||||
int GetServerCount( EMatchMakingType eType ) { return GetServerCount((HServerListRequest) eType); }
|
||||
|
||||
// Refresh a single server inside of a query (rather than all the servers )
|
||||
void RefreshServer( EMatchMakingType eType, int iServer ) { return RefreshServer((HServerListRequest) eType, iServer); }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Queries to individual servers directly via IP/Port
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Request updated ping time and other details from a single server
|
||||
HServerQuery PingServer( uint32 unIP, uint16 usPort, ISteamMatchmakingPingResponse *pRequestServersResponse );
|
||||
|
||||
// Request the list of players currently playing on a server
|
||||
HServerQuery PlayerDetails( uint32 unIP, uint16 usPort, ISteamMatchmakingPlayersResponse *pRequestServersResponse );
|
||||
|
||||
// Request the list of rules that the server is running (See ISteamGameServer::SetKeyValue() to set the rules server side)
|
||||
HServerQuery ServerRules( uint32 unIP, uint16 usPort, ISteamMatchmakingRulesResponse *pRequestServersResponse );
|
||||
|
||||
// Cancel an outstanding Ping/Players/Rules query from above. You should call this to cancel
|
||||
// any in-progress requests before destructing a callback object that may have been passed
|
||||
// to one of the above calls to avoid crashing when callbacks occur.
|
||||
void CancelServerQuery( HServerQuery hServerQuery );
|
||||
|
||||
//
|
||||
void RunCallbacks();
|
||||
void Callback(Common_Message *msg);
|
||||
void server_details(Gameserver *g, gameserveritem_t *server);
|
||||
};
|
||||
116
all/steam_music.cpp
Normal file
116
all/steam_music.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_music.h"
|
||||
|
||||
Steam_Music::Steam_Music(class SteamCallBacks *callbacks)
|
||||
{
|
||||
playing = 0;
|
||||
volume = 1.0;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
void Steam_Music::change_playstate(int new_playing)
|
||||
{
|
||||
if (new_playing != playing) {
|
||||
PlaybackStatusHasChanged_t data;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
playing = new_playing;
|
||||
}
|
||||
|
||||
bool Steam_Music::BIsEnabled()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Music::BIsEnabled\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Steam_Music::BIsPlaying()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Music::BIsPlaying\n");
|
||||
return playing > 0;
|
||||
}
|
||||
|
||||
|
||||
AudioPlayback_Status Steam_Music::GetPlaybackStatus()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Music::GetPlaybackStatus\n");
|
||||
if (playing == 0) {
|
||||
return AudioPlayback_Idle;
|
||||
}
|
||||
|
||||
if (playing == 1) {
|
||||
return AudioPlayback_Paused;
|
||||
}
|
||||
|
||||
if (playing == 2) {
|
||||
return AudioPlayback_Playing;
|
||||
}
|
||||
|
||||
return AudioPlayback_Idle;
|
||||
}
|
||||
|
||||
void Steam_Music::Play()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Music::Play\n");
|
||||
change_playstate(2);
|
||||
}
|
||||
|
||||
void Steam_Music::Pause()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Music::Pause\n");
|
||||
change_playstate(1);
|
||||
}
|
||||
|
||||
void Steam_Music::PlayPrevious()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Music::PlayPrevious\n");
|
||||
change_playstate(2);
|
||||
}
|
||||
|
||||
void Steam_Music::PlayNext()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Music::PlayNext\n");
|
||||
change_playstate(2);
|
||||
}
|
||||
|
||||
|
||||
// volume is between 0.0 and 1.0
|
||||
void Steam_Music::SetVolume( float flVolume )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Music::SetVolume\n");
|
||||
|
||||
if (flVolume > 1.0)
|
||||
flVolume = 1.0;
|
||||
if (flVolume < 0.0)
|
||||
flVolume = 0.0;
|
||||
|
||||
if (flVolume != volume) {
|
||||
VolumeHasChanged_t data;
|
||||
data.m_flNewVolume = volume;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
volume = flVolume;
|
||||
}
|
||||
|
||||
float Steam_Music::GetVolume()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Music::GetVolume\n");
|
||||
return volume;
|
||||
}
|
||||
43
all/steam_music.h
Normal file
43
all/steam_music.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Music : public ISteamMusic
|
||||
{
|
||||
int playing;
|
||||
float volume;
|
||||
void change_playstate(int new_playing);
|
||||
|
||||
class SteamCallBacks *callbacks;
|
||||
public:
|
||||
Steam_Music(class SteamCallBacks *callbacks);
|
||||
|
||||
bool BIsEnabled();
|
||||
bool BIsPlaying();
|
||||
|
||||
AudioPlayback_Status GetPlaybackStatus();
|
||||
|
||||
void Play();
|
||||
void Pause();
|
||||
void PlayPrevious();
|
||||
void PlayNext();
|
||||
|
||||
// volume is between 0.0 and 1.0
|
||||
void SetVolume( float flVolume );
|
||||
float GetVolume();
|
||||
};
|
||||
222
all/steam_musicremote.cpp
Normal file
222
all/steam_musicremote.cpp
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_musicremote.h"
|
||||
|
||||
// Service Definition
|
||||
bool Steam_MusicRemote::RegisterSteamMusicRemote( const char *pchName )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::DeregisterSteamMusicRemote()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::BIsCurrentMusicRemote()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::BActivationSuccess( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Steam_MusicRemote::SetDisplayName( const char *pchDisplayName )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::SetPNGIcon_64x64( void *pvBuffer, uint32 cbBufferLength )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Abilities for the user interface
|
||||
bool Steam_MusicRemote::EnablePlayPrevious(bool bValue)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::EnablePlayNext( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::EnableShuffled( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::EnableLooped( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::EnableQueue( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::EnablePlaylists( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Status
|
||||
bool Steam_MusicRemote::UpdatePlaybackStatus( AudioPlayback_Status nStatus )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateShuffled( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateLooped( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateVolume( float flValue )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
// volume is between 0.0 and 1.0
|
||||
|
||||
// Current Entry
|
||||
bool Steam_MusicRemote::CurrentEntryWillChange()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::CurrentEntryIsAvailable( bool bAvailable )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateCurrentEntryText( const char *pchText )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateCurrentEntryElapsedSeconds( int nValue )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateCurrentEntryCoverArt( void *pvBuffer, uint32 cbBufferLength )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::CurrentEntryDidChange()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Queue
|
||||
bool Steam_MusicRemote::QueueWillChange()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::ResetQueueEntries()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::SetQueueEntry( int nID, int nPosition, const char *pchEntryText )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::SetCurrentQueueEntry( int nID )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::QueueDidChange()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Playlist
|
||||
bool Steam_MusicRemote::PlaylistWillChange()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::ResetPlaylistEntries()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::SetPlaylistEntry( int nID, int nPosition, const char *pchEntryText )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::SetCurrentPlaylistEntry( int nID )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::PlaylistDidChange()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
67
all/steam_musicremote.h
Normal file
67
all/steam_musicremote.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_MusicRemote : public ISteamMusicRemote
|
||||
{
|
||||
public:
|
||||
// Service Definition
|
||||
bool RegisterSteamMusicRemote( const char *pchName );
|
||||
bool DeregisterSteamMusicRemote();
|
||||
bool BIsCurrentMusicRemote();
|
||||
bool BActivationSuccess( bool bValue );
|
||||
|
||||
bool SetDisplayName( const char *pchDisplayName );
|
||||
bool SetPNGIcon_64x64( void *pvBuffer, uint32 cbBufferLength );
|
||||
|
||||
// Abilities for the user interface
|
||||
bool EnablePlayPrevious(bool bValue);
|
||||
bool EnablePlayNext( bool bValue );
|
||||
bool EnableShuffled( bool bValue );
|
||||
bool EnableLooped( bool bValue );
|
||||
bool EnableQueue( bool bValue );
|
||||
bool EnablePlaylists( bool bValue );
|
||||
|
||||
// Status
|
||||
bool UpdatePlaybackStatus( AudioPlayback_Status nStatus );
|
||||
bool UpdateShuffled( bool bValue );
|
||||
bool UpdateLooped( bool bValue );
|
||||
bool UpdateVolume( float flValue ); // volume is between 0.0 and 1.0
|
||||
|
||||
// Current Entry
|
||||
bool CurrentEntryWillChange();
|
||||
bool CurrentEntryIsAvailable( bool bAvailable );
|
||||
bool UpdateCurrentEntryText( const char *pchText );
|
||||
bool UpdateCurrentEntryElapsedSeconds( int nValue );
|
||||
bool UpdateCurrentEntryCoverArt( void *pvBuffer, uint32 cbBufferLength );
|
||||
bool CurrentEntryDidChange();
|
||||
|
||||
// Queue
|
||||
bool QueueWillChange();
|
||||
bool ResetQueueEntries();
|
||||
bool SetQueueEntry( int nID, int nPosition, const char *pchEntryText );
|
||||
bool SetCurrentQueueEntry( int nID );
|
||||
bool QueueDidChange();
|
||||
|
||||
// Playlist
|
||||
bool PlaylistWillChange();
|
||||
bool ResetPlaylistEntries();
|
||||
bool SetPlaylistEntry( int nID, int nPosition, const char *pchEntryText );
|
||||
bool SetCurrentPlaylistEntry( int nID );
|
||||
bool PlaylistDidChange();
|
||||
};
|
||||
1028
all/steam_networking.h
Normal file
1028
all/steam_networking.h
Normal file
File diff suppressed because it is too large
Load diff
446
all/steam_networking_messages.h
Normal file
446
all/steam_networking_messages.h
Normal file
|
|
@ -0,0 +1,446 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define NETWORKING_MESSAGES_TIMEOUT 30.0
|
||||
|
||||
struct Steam_Message_Connection {
|
||||
SteamNetworkingIdentity remote_identity;
|
||||
std::map<int, std::queue<std::string>> data;
|
||||
|
||||
std::list<int> channels;
|
||||
bool accepted = false;
|
||||
bool dead = false;
|
||||
|
||||
unsigned id;
|
||||
unsigned remote_id = 0;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point created = std::chrono::high_resolution_clock::now();
|
||||
};
|
||||
|
||||
class Steam_Networking_Messages :
|
||||
public ISteamNetworkingMessages
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
|
||||
std::map<CSteamID, Steam_Message_Connection> connections;
|
||||
std::list<Common_Message> incoming_data;
|
||||
|
||||
unsigned id_counter = 0;
|
||||
std::chrono::steady_clock::time_point created;
|
||||
public:
|
||||
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_networking_messages_callback\n");
|
||||
|
||||
Steam_Networking_Messages *steam_networking_messages = (Steam_Networking_Messages *)object;
|
||||
steam_networking_messages->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_networking_messages_run_every_runcb\n");
|
||||
|
||||
Steam_Networking_Messages *steam_networking_messages = (Steam_Networking_Messages *)object;
|
||||
steam_networking_messages->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Networking_Messages(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
this->network->setCallback(CALLBACK_ID_NETWORKING_MESSAGES, settings->get_local_steam_id(), &Steam_Networking_Messages::steam_callback, this);
|
||||
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Messages::steam_callback, this);
|
||||
this->run_every_runcb->add(&Steam_Networking_Messages::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
|
||||
this->created = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
~Steam_Networking_Messages()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
this->run_every_runcb->remove(&Steam_Networking_Messages::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
std::map<CSteamID, Steam_Message_Connection>::iterator find_or_create_message_connection(SteamNetworkingIdentity identityRemote, bool incoming, bool restartbroken)
|
||||
{
|
||||
auto conn = connections.find(identityRemote.GetSteamID());
|
||||
if (conn == connections.end() || (conn->second.dead && restartbroken)) {
|
||||
++id_counter;
|
||||
struct Steam_Message_Connection con;
|
||||
con.remote_identity = identityRemote;
|
||||
con.id = id_counter;
|
||||
connections[identityRemote.GetSteamID()] = con;
|
||||
|
||||
Common_Message msg;
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_dest_id(con.remote_identity.GetSteamID64());
|
||||
msg.set_allocated_networking_messages(new Networking_Messages);
|
||||
if (incoming) {
|
||||
msg.mutable_networking_messages()->set_type(Networking_Messages::CONNECTION_ACCEPT);
|
||||
} else {
|
||||
msg.mutable_networking_messages()->set_type(Networking_Messages::CONNECTION_NEW);
|
||||
}
|
||||
msg.mutable_networking_messages()->set_channel(0);
|
||||
msg.mutable_networking_messages()->set_id_from(con.id);
|
||||
network->sendTo(&msg, true);
|
||||
|
||||
conn = connections.find(identityRemote.GetSteamID());
|
||||
|
||||
if (incoming) {
|
||||
SteamNetworkingMessagesSessionRequest_t data;
|
||||
data.m_identityRemote = con.remote_identity;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
}
|
||||
|
||||
if (!incoming) {
|
||||
conn->second.accepted = true;
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
/// Sends a message to the specified host. If we don't already have a session with that user,
|
||||
/// a session is implicitly created. There might be some handshaking that needs to happen
|
||||
/// before we can actually begin sending message data. If this handshaking fails and we can't
|
||||
/// get through, an error will be posted via the callback SteamNetworkingMessagesSessionFailed_t.
|
||||
/// There is no notification when the operation succeeds. (You should have the peer send a reply
|
||||
/// for this purpose.)
|
||||
///
|
||||
/// Sending a message to a host will also implicitly accept any incoming connection from that host.
|
||||
///
|
||||
/// nSendFlags is a bitmask of k_nSteamNetworkingSend_xxx options
|
||||
///
|
||||
/// nRemoteChannel is a routing number you can use to help route message to different systems.
|
||||
/// You'll have to call ReceiveMessagesOnChannel() with the same channel number in order to retrieve
|
||||
/// the data on the other end.
|
||||
///
|
||||
/// Using different channels to talk to the same user will still use the same underlying
|
||||
/// connection, saving on resources. If you don't need this feature, use 0.
|
||||
/// Otherwise, small integers are the most efficient.
|
||||
///
|
||||
/// It is guaranteed that reliable messages to the same host on the same channel
|
||||
/// will be be received by the remote host (if they are received at all) exactly once,
|
||||
/// and in the same order that they were send.
|
||||
///
|
||||
/// NO other order guarantees exist! In particular, unreliable messages may be dropped,
|
||||
/// received out of order with respect to each other and with respect to reliable data,
|
||||
/// or may be received multiple times. Messages on different channels are *not* guaranteed
|
||||
/// to be received in the order they were sent.
|
||||
///
|
||||
/// A note for those familiar with TCP/IP ports, or converting an existing codebase that
|
||||
/// opened multiple sockets: You might notice that there is only one channel, and with
|
||||
/// TCP/IP each endpoint has a port number. You can think of the channel number as the
|
||||
/// *destination* port. If you need each message to also include a "source port" (so the
|
||||
/// recipient can route the reply), then just put that in your message. That is essentially
|
||||
/// how UDP works!
|
||||
///
|
||||
/// Returns:
|
||||
/// - k_EREsultOK on success.
|
||||
/// - k_EResultNoConnection will be returned if the session has failed or was closed by the peer,
|
||||
/// and k_nSteamNetworkingSend_AutoRestartBrokwnSession is not used. (You can use
|
||||
/// GetSessionConnectionInfo to get the details.) In order to acknowledge the broken session
|
||||
/// and start a new one, you must call CloseSessionWithUser
|
||||
/// - See SendMessageToConnection::SendMessageToConnection for more
|
||||
EResult SendMessageToUser( const SteamNetworkingIdentity &identityRemote, const void *pubData, uint32 cubData, int nSendFlags, int nRemoteChannel )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::SendMessageToUser\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
const SteamNetworkingIPAddr *ip = identityRemote.GetIPAddr();
|
||||
bool reliable = false;
|
||||
if (nSendFlags & k_nSteamNetworkingSend_Reliable) {
|
||||
reliable = true;
|
||||
}
|
||||
|
||||
bool restart_broken = false;
|
||||
if (nSendFlags & k_nSteamNetworkingSend_AutoRestartBrokenSession) {
|
||||
restart_broken = true;
|
||||
}
|
||||
|
||||
if (identityRemote.m_eType == k_ESteamNetworkingIdentityType_SteamID) {
|
||||
PRINT_DEBUG("Steam_Networking_Messages::SendMessageToUser %llu\n", identityRemote.GetSteamID64());
|
||||
//steam id identity
|
||||
} else if (ip) {
|
||||
PRINT_DEBUG("Steam_Networking_Messages::SendMessageToUser %u:%u ipv4? %u\n", ip->GetIPv4(), ip->m_port, ip->IsIPv4());
|
||||
//ip addr
|
||||
return k_EResultNoConnection; //TODO
|
||||
} else {
|
||||
return k_EResultNoConnection;
|
||||
}
|
||||
|
||||
auto conn = find_or_create_message_connection(identityRemote, false, restart_broken);
|
||||
if (conn->second.dead) {
|
||||
return k_EResultNoConnection;
|
||||
}
|
||||
|
||||
Common_Message msg;
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_dest_id(conn->second.remote_identity.GetSteamID64());
|
||||
msg.set_allocated_networking_messages(new Networking_Messages);
|
||||
msg.mutable_networking_messages()->set_type(Networking_Messages::DATA);
|
||||
msg.mutable_networking_messages()->set_channel(nRemoteChannel);
|
||||
msg.mutable_networking_messages()->set_id_from(conn->second.id);
|
||||
msg.mutable_networking_messages()->set_data(pubData, cubData);
|
||||
|
||||
network->sendTo(&msg, reliable);
|
||||
return k_EResultOK;
|
||||
}
|
||||
|
||||
static void free_steam_message_data(SteamNetworkingMessage_t *pMsg)
|
||||
{
|
||||
free(pMsg->m_pData);
|
||||
pMsg->m_pData = NULL;
|
||||
}
|
||||
|
||||
static void delete_steam_message(SteamNetworkingMessage_t *pMsg)
|
||||
{
|
||||
if (pMsg->m_pfnFreeData) pMsg->m_pfnFreeData(pMsg);
|
||||
delete pMsg;
|
||||
}
|
||||
|
||||
/// Reads the next message that has been sent from another user via SendMessageToUser() on the given channel.
|
||||
/// Returns number of messages returned into your list. (0 if no message are available on that channel.)
|
||||
///
|
||||
/// When you're done with the message object(s), make sure and call Release()!
|
||||
int ReceiveMessagesOnChannel( int nLocalChannel, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::ReceiveMessagesOnChannel\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
int message_counter = 0;
|
||||
|
||||
for (auto & conn : connections) {
|
||||
auto chan = conn.second.data.find(nLocalChannel);
|
||||
if (chan != conn.second.data.end()) {
|
||||
while (!chan->second.empty() && message_counter < nMaxMessages) {
|
||||
SteamNetworkingMessage_t *pMsg = new SteamNetworkingMessage_t(); //TODO size is wrong
|
||||
unsigned long size = chan->second.front().size();
|
||||
pMsg->m_pData = malloc(size);
|
||||
pMsg->m_cbSize = size;
|
||||
memcpy(pMsg->m_pData, chan->second.front().data(), size);
|
||||
pMsg->m_conn = conn.second.id;
|
||||
pMsg->m_identityPeer = conn.second.remote_identity;
|
||||
pMsg->m_nConnUserData = -1;
|
||||
pMsg->m_usecTimeReceived = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - created).count();
|
||||
//TODO: messagenumber?
|
||||
// pMsg->m_nMessageNumber = connect_socket->second.packet_receive_counter;
|
||||
// ++connect_socket->second.packet_receive_counter;
|
||||
|
||||
pMsg->m_pfnFreeData = &free_steam_message_data;
|
||||
pMsg->m_pfnRelease = &delete_steam_message;
|
||||
pMsg->m_nChannel = nLocalChannel;
|
||||
ppOutMessages[message_counter] = pMsg;
|
||||
++message_counter;
|
||||
chan->second.pop();
|
||||
}
|
||||
}
|
||||
|
||||
if (message_counter >= nMaxMessages) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_DEBUG("Steam_Networking_Messages::ReceiveMessagesOnChannel got %u\n", message_counter);
|
||||
return message_counter;
|
||||
}
|
||||
|
||||
/// AcceptSessionWithUser() should only be called in response to a SteamP2PSessionRequest_t callback
|
||||
/// SteamP2PSessionRequest_t will be posted if another user tries to send you a message, and you haven't
|
||||
/// tried to talk to them. If you don't want to talk to them, just ignore the request.
|
||||
/// If the user continues to send you messages, SteamP2PSessionRequest_t callbacks will continue to
|
||||
/// be posted periodically. This may be called multiple times for a single user.
|
||||
///
|
||||
/// Calling SendMessage() on the other user, this implicitly accepts any pending session request.
|
||||
bool AcceptSessionWithUser( const SteamNetworkingIdentity &identityRemote )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::AcceptSessionWithUser\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto conn = connections.find(identityRemote.GetSteamID());
|
||||
if (conn == connections.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
conn->second.accepted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Call this when you're done talking to a user to immediately free up resources under-the-hood.
|
||||
/// If the remote user tries to send data to you again, another P2PSessionRequest_t callback will
|
||||
/// be posted.
|
||||
///
|
||||
/// Note that sessions that go unused for a few minutes are automatically timed out.
|
||||
bool CloseSessionWithUser( const SteamNetworkingIdentity &identityRemote )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::CloseSessionWithUser\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto conn = connections.find(identityRemote.GetSteamID());
|
||||
if (conn == connections.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common_Message msg;
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_dest_id(conn->second.remote_identity.GetSteamID64());
|
||||
msg.set_allocated_networking_messages(new Networking_Messages);
|
||||
msg.mutable_networking_messages()->set_type(Networking_Messages::CONNECTION_END);
|
||||
msg.mutable_networking_messages()->set_channel(0);
|
||||
msg.mutable_networking_messages()->set_id_from(conn->second.id);
|
||||
network->sendTo(&msg, true);
|
||||
|
||||
connections.erase(conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Call this when you're done talking to a user on a specific channel. Once all
|
||||
/// open channels to a user have been closed, the open session to the user will be
|
||||
/// closed, and any new data from this user will trigger a SteamP2PSessionRequest_t
|
||||
/// callback
|
||||
bool CloseChannelWithUser( const SteamNetworkingIdentity &identityRemote, int nLocalChannel )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::CloseChannelWithUser\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
//TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns information about the latest state of a connection, if any, with the given peer.
|
||||
/// Primarily intended for debugging purposes, but can also be used to get more detailed
|
||||
/// failure information. (See SendMessageToUser and k_nSteamNetworkingSend_AutoRestartBrokwnSession.)
|
||||
///
|
||||
/// Returns the value of SteamNetConnectionInfo_t::m_eState, or k_ESteamNetworkingConnectionState_None
|
||||
/// if no connection exists with specified peer. You may pass nullptr for either parameter if
|
||||
/// you do not need the corresponding details. Note that sessions time out after a while,
|
||||
/// so if a connection fails, or SendMessageToUser returns SendMessageToUser, you cannot wait
|
||||
/// indefinitely to obtain the reason for failure.
|
||||
ESteamNetworkingConnectionState GetSessionConnectionInfo( const SteamNetworkingIdentity &identityRemote, SteamNetConnectionInfo_t *pConnectionInfo, SteamNetConnectionRealTimeStatus_t *pQuickStatus )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::GetSessionConnectionInfo\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto conn = connections.find(identityRemote.GetSteamID());
|
||||
if (conn == connections.end()) {
|
||||
return k_ESteamNetworkingConnectionState_None;
|
||||
}
|
||||
|
||||
ESteamNetworkingConnectionState state = k_ESteamNetworkingConnectionState_Connected;
|
||||
if (conn->second.remote_id == 0 || !conn->second.accepted) {
|
||||
state = k_ESteamNetworkingConnectionState_Connecting;
|
||||
} else if (conn->second.dead) {
|
||||
state = k_ESteamNetworkingConnectionState_ClosedByPeer;
|
||||
}
|
||||
|
||||
if (pConnectionInfo) {
|
||||
memset(pConnectionInfo, 0, sizeof(SteamNetConnectionInfo_t));
|
||||
pConnectionInfo->m_eState = state;
|
||||
pConnectionInfo->m_identityRemote = conn->second.remote_identity;
|
||||
//TODO
|
||||
}
|
||||
|
||||
if (pQuickStatus) {
|
||||
memset(pQuickStatus, 0, sizeof(SteamNetConnectionRealTimeStatus_t));
|
||||
pQuickStatus->m_eState = state;
|
||||
pQuickStatus->m_nPing = 10; //TODO: calculate real numbers?
|
||||
pQuickStatus->m_flConnectionQualityLocal = 1.0;
|
||||
pQuickStatus->m_flConnectionQualityRemote = 1.0;
|
||||
//TODO
|
||||
}
|
||||
|
||||
return k_ESteamNetworkingConnectionState_Connected;
|
||||
}
|
||||
|
||||
void end_connection(CSteamID steam_id)
|
||||
{
|
||||
auto conn = connections.find(steam_id);
|
||||
if (conn != connections.end()) {
|
||||
conn->second.dead = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
auto msg = std::begin(incoming_data);
|
||||
while (msg != std::end(incoming_data)) {
|
||||
CSteamID source_id((uint64)msg->source_id());
|
||||
|
||||
auto conn = connections.find(source_id);
|
||||
if (conn != connections.end()) {
|
||||
if (conn->second.remote_id == msg->networking_messages().id_from())
|
||||
conn->second.data[msg->networking_messages().channel()].push(msg->networking_messages().data());
|
||||
}
|
||||
|
||||
msg = incoming_data.erase(msg);
|
||||
}
|
||||
|
||||
auto conn = std::begin(connections);
|
||||
while (conn != std::end(connections)) {
|
||||
if (!conn->second.accepted && check_timedout(conn->second.created, NETWORKING_MESSAGES_TIMEOUT)) {
|
||||
conn = connections.erase(conn);
|
||||
} else {
|
||||
++conn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
end_connection((uint64)msg->source_id());
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->has_networking_messages()) {
|
||||
PRINT_DEBUG("Steam_Networking_Messages: got network socket msg %u\n", msg->networking_messages().type());
|
||||
if (msg->networking_messages().type() == Networking_Messages::CONNECTION_NEW) {
|
||||
SteamNetworkingIdentity identity;
|
||||
identity.SetSteamID64(msg->source_id());
|
||||
auto conn = find_or_create_message_connection(identity, true, false);
|
||||
conn->second.remote_id = msg->networking_messages().id_from();
|
||||
conn->second.dead = false;
|
||||
}
|
||||
|
||||
if (msg->networking_messages().type() == Networking_Messages::CONNECTION_ACCEPT) {
|
||||
auto conn = connections.find((uint64)msg->source_id());
|
||||
if (conn != connections.end()) {
|
||||
conn->second.remote_id = msg->networking_messages().id_from();
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->networking_messages().type() == Networking_Messages::CONNECTION_END) {
|
||||
end_connection((uint64)msg->source_id());
|
||||
}
|
||||
|
||||
if (msg->networking_messages().type() == Networking_Messages::DATA) {
|
||||
incoming_data.push_back(Common_Message(*msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
2105
all/steam_networking_sockets.h
Normal file
2105
all/steam_networking_sockets.h
Normal file
File diff suppressed because it is too large
Load diff
156
all/steam_networking_socketsserialized.h
Normal file
156
all/steam_networking_socketsserialized.h
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Networking_Sockets_Serialized :
|
||||
public ISteamNetworkingSocketsSerialized002,
|
||||
public ISteamNetworkingSocketsSerialized003,
|
||||
public ISteamNetworkingSocketsSerialized004,
|
||||
public ISteamNetworkingSocketsSerialized005
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_networkingsockets_callback\n");
|
||||
|
||||
Steam_Networking_Sockets_Serialized *steam_networkingsockets = (Steam_Networking_Sockets_Serialized *)object;
|
||||
steam_networkingsockets->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_networkingsockets_run_every_runcb\n");
|
||||
|
||||
Steam_Networking_Sockets_Serialized *steam_networkingsockets = (Steam_Networking_Sockets_Serialized *)object;
|
||||
steam_networkingsockets->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Networking_Sockets_Serialized(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Sockets_Serialized::steam_callback, this);
|
||||
this->run_every_runcb->add(&Steam_Networking_Sockets_Serialized::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_Networking_Sockets_Serialized()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
this->run_every_runcb->remove(&Steam_Networking_Sockets_Serialized::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
void SendP2PRendezvous( CSteamID steamIDRemote, uint32 unConnectionIDSrc, const void *pMsgRendezvous, uint32 cbRendezvous )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::SendP2PRendezvous\n");
|
||||
}
|
||||
|
||||
void SendP2PConnectionFailure( CSteamID steamIDRemote, uint32 unConnectionIDDest, uint32 nReason, const char *pszReason )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::SendP2PConnectionFailure\n");
|
||||
}
|
||||
|
||||
SteamAPICall_t GetCertAsync()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetCertAsync\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct SteamNetworkingSocketsCert_t data = {};
|
||||
data.m_eResult = k_EResultOK;
|
||||
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
int GetNetworkConfigJSON( void *buf, uint32 cbBuf, const char *pszLauncherPartner )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetNetworkConfigJSON %s\n", pszLauncherPartner);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetNetworkConfigJSON( void *buf, uint32 cbBuf )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetNetworkConfigJSON old\n");
|
||||
return GetNetworkConfigJSON(buf, cbBuf, "");
|
||||
}
|
||||
|
||||
void CacheRelayTicket( const void *pTicket, uint32 cbTicket )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::CacheRelayTicket\n");
|
||||
}
|
||||
|
||||
uint32 GetCachedRelayTicketCount()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetCachedRelayTicketCount\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetCachedRelayTicket( uint32 idxTicket, void *buf, uint32 cbBuf )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetCachedRelayTicket\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PostConnectionStateMsg( const void *pMsg, uint32 cbMsg )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::PostConnectionStateMsg\n");
|
||||
}
|
||||
|
||||
bool GetSTUNServer(int dont_know, char *buf, unsigned int len)
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetSTUNServer %i %p %u\n", dont_know, buf, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BAllowDirectConnectToPeer(SteamNetworkingIdentity const &identity)
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::BAllowDirectConnectToPeer\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
int BeginAsyncRequestFakeIP(int a)
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::BeginAsyncRequestFakeIP\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
751
all/steam_networking_utils.h
Normal file
751
all/steam_networking_utils.h
Normal file
|
|
@ -0,0 +1,751 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Networking_Utils :
|
||||
public ISteamNetworkingUtils001,
|
||||
public ISteamNetworkingUtils002,
|
||||
public ISteamNetworkingUtils003,
|
||||
public ISteamNetworkingUtils
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
|
||||
FSteamNetworkingSocketsDebugOutput debug_function;
|
||||
bool relay_initialized = false;
|
||||
bool init_relay = false;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_networkingutils_callback\n");
|
||||
|
||||
Steam_Networking_Utils *steam_networkingutils = (Steam_Networking_Utils *)object;
|
||||
steam_networkingutils->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_networkingutils_run_every_runcb\n");
|
||||
|
||||
Steam_Networking_Utils *steam_networkingutils = (Steam_Networking_Utils *)object;
|
||||
steam_networkingutils->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Networking_Utils(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Utils::steam_callback, this);
|
||||
this->run_every_runcb->add(&Steam_Networking_Utils::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_Networking_Utils()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
this->run_every_runcb->remove(&Steam_Networking_Utils::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
static void free_steam_message_data(SteamNetworkingMessage_t *pMsg)
|
||||
{
|
||||
free(pMsg->m_pData);
|
||||
pMsg->m_pData = NULL;
|
||||
}
|
||||
|
||||
static void delete_steam_message(SteamNetworkingMessage_t *pMsg)
|
||||
{
|
||||
if (pMsg->m_pfnFreeData) pMsg->m_pfnFreeData(pMsg);
|
||||
delete pMsg;
|
||||
}
|
||||
|
||||
/// Allocate and initialize a message object. Usually the reason
|
||||
/// you call this is to pass it to ISteamNetworkingSockets::SendMessages.
|
||||
/// The returned object will have all of the relevant fields cleared to zero.
|
||||
///
|
||||
/// Optionally you can also request that this system allocate space to
|
||||
/// hold the payload itself. If cbAllocateBuffer is nonzero, the system
|
||||
/// will allocate memory to hold a payload of at least cbAllocateBuffer bytes.
|
||||
/// m_pData will point to the allocated buffer, m_cbSize will be set to the
|
||||
/// size, and m_pfnFreeData will be set to the proper function to free up
|
||||
/// the buffer.
|
||||
///
|
||||
/// If cbAllocateBuffer=0, then no buffer is allocated. m_pData will be NULL,
|
||||
/// m_cbSize will be zero, and m_pfnFreeData will be NULL. You will need to
|
||||
/// set each of these.
|
||||
///
|
||||
/// You can use SteamNetworkingMessage_t::Release to free up the message
|
||||
/// bookkeeping object and any associated buffer. See
|
||||
/// ISteamNetworkingSockets::SendMessages for details on reference
|
||||
/// counting and ownership.
|
||||
SteamNetworkingMessage_t *AllocateMessage( int cbAllocateBuffer )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::AllocateMessage\n");
|
||||
SteamNetworkingMessage_t *pMsg = new SteamNetworkingMessage_t();
|
||||
pMsg->m_pfnFreeData = &free_steam_message_data;
|
||||
pMsg->m_pfnRelease = &delete_steam_message;
|
||||
if (cbAllocateBuffer < 0)
|
||||
cbAllocateBuffer = 0;
|
||||
|
||||
if (cbAllocateBuffer)
|
||||
pMsg->m_pData = malloc(cbAllocateBuffer);
|
||||
|
||||
pMsg->m_cbSize = cbAllocateBuffer;
|
||||
return pMsg;
|
||||
}
|
||||
|
||||
bool InitializeRelayAccess()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::InitializeRelayAccess\n");
|
||||
init_relay = true;
|
||||
return relay_initialized;
|
||||
}
|
||||
|
||||
SteamRelayNetworkStatus_t get_network_status()
|
||||
{
|
||||
SteamRelayNetworkStatus_t data = {};
|
||||
data.m_eAvail = k_ESteamNetworkingAvailability_Current;
|
||||
data.m_bPingMeasurementInProgress = 0;
|
||||
data.m_eAvailAnyRelay = k_ESteamNetworkingAvailability_Current;
|
||||
data.m_eAvailNetworkConfig = k_ESteamNetworkingAvailability_Current;
|
||||
strcpy(data.m_debugMsg, "OK");
|
||||
return data;
|
||||
}
|
||||
|
||||
/// Fetch current status of the relay network.
|
||||
///
|
||||
/// SteamRelayNetworkStatus_t is also a callback. It will be triggered on
|
||||
/// both the user and gameserver interfaces any time the status changes, or
|
||||
/// ping measurement starts or stops.
|
||||
///
|
||||
/// SteamRelayNetworkStatus_t::m_eAvail is returned. If you want
|
||||
/// more details, you can pass a non-NULL value.
|
||||
ESteamNetworkingAvailability GetRelayNetworkStatus( SteamRelayNetworkStatus_t *pDetails )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetRelayNetworkStatus %p\n", pDetails);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
//TODO: check if this is how real steam returns it
|
||||
SteamRelayNetworkStatus_t data = {};
|
||||
if (relay_initialized) {
|
||||
data = get_network_status();
|
||||
}
|
||||
|
||||
if (pDetails) {
|
||||
*pDetails = data;
|
||||
}
|
||||
|
||||
return k_ESteamNetworkingAvailability_Current;
|
||||
}
|
||||
|
||||
float GetLocalPingLocation( SteamNetworkPingLocation_t &result )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetLocalPingLocation\n");
|
||||
if (relay_initialized) {
|
||||
result.m_data[2] = 123;
|
||||
result.m_data[8] = 67;
|
||||
return 2.0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int EstimatePingTimeBetweenTwoLocations( const SteamNetworkPingLocation_t &location1, const SteamNetworkPingLocation_t &location2 )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::EstimatePingTimeBetweenTwoLocations\n");
|
||||
//return k_nSteamNetworkingPing_Unknown;
|
||||
return 10;
|
||||
}
|
||||
|
||||
|
||||
int EstimatePingTimeFromLocalHost( const SteamNetworkPingLocation_t &remoteLocation )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::EstimatePingTimeFromLocalHost\n");
|
||||
return 10;
|
||||
}
|
||||
|
||||
|
||||
void ConvertPingLocationToString( const SteamNetworkPingLocation_t &location, char *pszBuf, int cchBufSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::ConvertPingLocationToString\n");
|
||||
strncpy(pszBuf, "fra=10+2", cchBufSize);
|
||||
}
|
||||
|
||||
|
||||
bool ParsePingLocationString( const char *pszString, SteamNetworkPingLocation_t &result )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::ParsePingLocationString\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CheckPingDataUpToDate( float flMaxAgeSeconds )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::CheckPingDataUpToDate %f\n", flMaxAgeSeconds);
|
||||
init_relay = true;
|
||||
return relay_initialized;
|
||||
}
|
||||
|
||||
|
||||
bool IsPingMeasurementInProgress()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::IsPingMeasurementInProgress\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int GetPingToDataCenter( SteamNetworkingPOPID popID, SteamNetworkingPOPID *pViaRelayPoP )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetPingToDataCenter\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int GetDirectPingToPOP( SteamNetworkingPOPID popID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetDirectPingToPOP\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int GetPOPCount()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetPOPCount\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int GetPOPList( SteamNetworkingPOPID *list, int nListSz )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetPOPList\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Misc
|
||||
//
|
||||
|
||||
/// Fetch current timestamp. This timer has the following properties:
|
||||
///
|
||||
/// - Monotonicity is guaranteed.
|
||||
/// - The initial value will be at least 24*3600*30*1e6, i.e. about
|
||||
/// 30 days worth of microseconds. In this way, the timestamp value of
|
||||
/// 0 will always be at least "30 days ago". Also, negative numbers
|
||||
/// will never be returned.
|
||||
/// - Wraparound / overflow is not a practical concern.
|
||||
///
|
||||
/// If you are running under the debugger and stop the process, the clock
|
||||
/// might not advance the full wall clock time that has elapsed between
|
||||
/// calls. If the process is not blocked from normal operation, the
|
||||
/// timestamp values will track wall clock time, even if you don't call
|
||||
/// the function frequently.
|
||||
///
|
||||
/// The value is only meaningful for this run of the process. Don't compare
|
||||
/// it to values obtained on another computer, or other runs of the same process.
|
||||
SteamNetworkingMicroseconds GetLocalTimestamp()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetLocalTimestamp\n");
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - initialized_time).count() + (SteamNetworkingMicroseconds)24*3600*30*1e6;
|
||||
}
|
||||
|
||||
|
||||
/// Set a function to receive network-related information that is useful for debugging.
|
||||
/// This can be very useful during development, but it can also be useful for troubleshooting
|
||||
/// problems with tech savvy end users. If you have a console or other log that customers
|
||||
/// can examine, these log messages can often be helpful to troubleshoot network issues.
|
||||
/// (Especially any warning/error messages.)
|
||||
///
|
||||
/// The detail level indicates what message to invoke your callback on. Lower numeric
|
||||
/// value means more important, and the value you pass is the lowest priority (highest
|
||||
/// numeric value) you wish to receive callbacks for.
|
||||
///
|
||||
/// Except when debugging, you should only use k_ESteamNetworkingSocketsDebugOutputType_Msg
|
||||
/// or k_ESteamNetworkingSocketsDebugOutputType_Warning. For best performance, do NOT
|
||||
/// request a high detail level and then filter out messages in your callback. Instead,
|
||||
/// call function function to adjust the desired level of detail.
|
||||
///
|
||||
/// IMPORTANT: This may be called from a service thread, while we own a mutex, etc.
|
||||
/// Your output function must be threadsafe and fast! Do not make any other
|
||||
/// Steamworks calls from within the handler.
|
||||
void SetDebugOutputFunction( ESteamNetworkingSocketsDebugOutputType eDetailLevel, FSteamNetworkingSocketsDebugOutput pfnFunc )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SetDebugOutputFunction %i\n", eDetailLevel);
|
||||
if (eDetailLevel != k_ESteamNetworkingSocketsDebugOutputType_None) {
|
||||
debug_function = pfnFunc;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Fake IP
|
||||
//
|
||||
// Useful for interfacing with code that assumes peers are identified using an IPv4 address
|
||||
//
|
||||
|
||||
/// Return true if an IPv4 address is one that might be used as a "fake" one.
|
||||
/// This function is fast; it just does some logical tests on the IP and does
|
||||
/// not need to do any lookup operations.
|
||||
// inline bool IsFakeIPv4( uint32 nIPv4 ) { return GetIPv4FakeIPType( nIPv4 ) > k_ESteamNetworkingFakeIPType_NotFake; }
|
||||
ESteamNetworkingFakeIPType GetIPv4FakeIPType( uint32 nIPv4 )
|
||||
{
|
||||
PRINT_DEBUG("TODO: %s\n", __FUNCTION__);
|
||||
return k_ESteamNetworkingFakeIPType_NotFake;
|
||||
}
|
||||
|
||||
/// Get the real identity associated with a given FakeIP.
|
||||
///
|
||||
/// On failure, returns:
|
||||
/// - k_EResultInvalidParam: the IP is not a FakeIP.
|
||||
/// - k_EResultNoMatch: we don't recognize that FakeIP and don't know the corresponding identity.
|
||||
///
|
||||
/// FakeIP's used by active connections, or the FakeIPs assigned to local identities,
|
||||
/// will always work. FakeIPs for recently destroyed connections will continue to
|
||||
/// return results for a little while, but not forever. At some point, we will forget
|
||||
/// FakeIPs to save space. It's reasonably safe to assume that you can read back the
|
||||
/// real identity of a connection very soon after it is destroyed. But do not wait
|
||||
/// indefinitely.
|
||||
EResult GetRealIdentityForFakeIP( const SteamNetworkingIPAddr &fakeIP, SteamNetworkingIdentity *pOutRealIdentity )
|
||||
{
|
||||
PRINT_DEBUG("TODO: %s\n", __FUNCTION__);
|
||||
return k_EResultNoMatch;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Set and get configuration values, see ESteamNetworkingConfigValue for individual descriptions.
|
||||
//
|
||||
|
||||
// Shortcuts for common cases. (Implemented as inline functions below)
|
||||
/*
|
||||
bool SetGlobalConfigValueInt32( ESteamNetworkingConfigValue eValue, int32 val );
|
||||
bool SetGlobalConfigValueFloat( ESteamNetworkingConfigValue eValue, float val );
|
||||
bool SetGlobalConfigValueString( ESteamNetworkingConfigValue eValue, const char *val );
|
||||
bool SetConnectionConfigValueInt32( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, int32 val );
|
||||
bool SetConnectionConfigValueFloat( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, float val );
|
||||
bool SetConnectionConfigValueString( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, const char *val );
|
||||
*/
|
||||
/// Set a configuration value.
|
||||
/// - eValue: which value is being set
|
||||
/// - eScope: Onto what type of object are you applying the setting?
|
||||
/// - scopeArg: Which object you want to change? (Ignored for global scope). E.g. connection handle, listen socket handle, interface pointer, etc.
|
||||
/// - eDataType: What type of data is in the buffer at pValue? This must match the type of the variable exactly!
|
||||
/// - pArg: Value to set it to. You can pass NULL to remove a non-global sett at this scope,
|
||||
/// causing the value for that object to use global defaults. Or at global scope, passing NULL
|
||||
/// will reset any custom value and restore it to the system default.
|
||||
/// NOTE: When setting callback functions, do not pass the function pointer directly.
|
||||
/// Your argument should be a pointer to a function pointer.
|
||||
bool SetConfigValue( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj,
|
||||
ESteamNetworkingConfigDataType eDataType, const void *pArg )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SetConfigValue %i %i %p %i %p\n", eValue, eScopeType, scopeObj, eDataType, pArg);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Get a configuration value.
|
||||
/// - eValue: which value to fetch
|
||||
/// - eScopeType: query setting on what type of object
|
||||
/// - eScopeArg: the object to query the setting for
|
||||
/// - pOutDataType: If non-NULL, the data type of the value is returned.
|
||||
/// - pResult: Where to put the result. Pass NULL to query the required buffer size. (k_ESteamNetworkingGetConfigValue_BufferTooSmall will be returned.)
|
||||
/// - cbResult: IN: the size of your buffer. OUT: the number of bytes filled in or required.
|
||||
ESteamNetworkingGetConfigValueResult GetConfigValue( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj,
|
||||
ESteamNetworkingConfigDataType *pOutDataType, void *pResult, size_t *cbResult )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetConfigValue\n");
|
||||
return k_ESteamNetworkingGetConfigValue_BadValue;
|
||||
}
|
||||
|
||||
|
||||
/// Returns info about a configuration value. Returns false if the value does not exist.
|
||||
/// pOutNextValue can be used to iterate through all of the known configuration values.
|
||||
/// (Use GetFirstConfigValue() to begin the iteration, will be k_ESteamNetworkingConfig_Invalid on the last value)
|
||||
/// Any of the output parameters can be NULL if you do not need that information.
|
||||
bool GetConfigValueInfo( ESteamNetworkingConfigValue eValue, const char **pOutName, ESteamNetworkingConfigDataType *pOutDataType, ESteamNetworkingConfigScope *pOutScope, ESteamNetworkingConfigValue *pOutNextValue )
|
||||
{
|
||||
PRINT_DEBUG("TODO: Steam_Networking_Utils::GetConfigValueInfo old\n");
|
||||
//TODO flat api
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Get info about a configuration value. Returns the name of the value,
|
||||
/// or NULL if the value doesn't exist. Other output parameters can be NULL
|
||||
/// if you do not need them.
|
||||
const char *GetConfigValueInfo( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigDataType *pOutDataType, ESteamNetworkingConfigScope *pOutScope )
|
||||
{
|
||||
PRINT_DEBUG("TODO: %s\n", __FUNCTION__);
|
||||
//TODO flat api
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Return the lowest numbered configuration value available in the current environment.
|
||||
ESteamNetworkingConfigValue GetFirstConfigValue()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetFirstConfigValue\n");
|
||||
return k_ESteamNetworkingConfig_Invalid;
|
||||
}
|
||||
|
||||
/// Iterate the list of all configuration values in the current environment that it might
|
||||
/// be possible to display or edit using a generic UI. To get the first iterable value,
|
||||
/// pass k_ESteamNetworkingConfig_Invalid. Returns k_ESteamNetworkingConfig_Invalid
|
||||
/// to signal end of list.
|
||||
///
|
||||
/// The bEnumerateDevVars argument can be used to include "dev" vars. These are vars that
|
||||
/// are recommended to only be editable in "debug" or "dev" mode and typically should not be
|
||||
/// shown in a retail environment where a malicious local user might use this to cheat.
|
||||
ESteamNetworkingConfigValue IterateGenericEditableConfigValues( ESteamNetworkingConfigValue eCurrent, bool bEnumerateDevVars )
|
||||
{
|
||||
PRINT_DEBUG("TODO: %s\n", __FUNCTION__);
|
||||
return k_ESteamNetworkingConfig_Invalid;
|
||||
}
|
||||
|
||||
|
||||
// String conversions. You'll usually access these using the respective
|
||||
// inline methods.
|
||||
void SteamNetworkingIPAddr_ToString( const SteamNetworkingIPAddr &addr, char *buf, size_t cbBuf, bool bWithPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIPAddr_ToString\n");
|
||||
if (buf == nullptr || cbBuf == 0)
|
||||
return;
|
||||
|
||||
char buffer[64]; // Its enought for ipv4 & ipv6 + port
|
||||
std::string str_addr;
|
||||
if (addr.IsIPv4())
|
||||
{
|
||||
in_addr ipv4_addr;
|
||||
ipv4_addr.s_addr = htonl(addr.GetIPv4());
|
||||
|
||||
if (inet_ntop(AF_INET, &ipv4_addr, buffer, sizeof(buffer) / sizeof(*buffer)) != nullptr)
|
||||
{
|
||||
if (bWithPort)
|
||||
{
|
||||
str_addr = buffer;
|
||||
str_addr += ':';
|
||||
str_addr += std::to_string(addr.m_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_addr = buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
in6_addr ipv6_addr;
|
||||
memcpy(ipv6_addr.s6_addr, addr.m_ipv6, sizeof(addr.m_ipv6));
|
||||
|
||||
if (inet_ntop(AF_INET6, &ipv6_addr, buffer, sizeof(buffer) / sizeof(*buffer)) != nullptr)
|
||||
{
|
||||
if (bWithPort)
|
||||
{
|
||||
str_addr = '[';
|
||||
str_addr += buffer;
|
||||
str_addr += "]:";
|
||||
str_addr += std::to_string(addr.m_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_addr = buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cbBuf = std::min<size_t>(cbBuf, str_addr.length() + 1);
|
||||
strncpy(buf, str_addr.c_str(), cbBuf);
|
||||
buf[cbBuf - 1] = '\0';
|
||||
}
|
||||
|
||||
bool SteamNetworkingIPAddr_ParseString( SteamNetworkingIPAddr *pAddr, const char *pszStr )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIPAddr_ParseString\n");
|
||||
|
||||
bool valid = false;
|
||||
|
||||
if (pAddr == nullptr || pszStr == nullptr)
|
||||
return valid;
|
||||
|
||||
std::string str(pszStr);
|
||||
size_t pos = str.find(':');
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{// Try ipv4 with port
|
||||
in_addr ipv4_addr;
|
||||
std::string tmp(str);
|
||||
tmp[pos] = 0;
|
||||
const char* ip = tmp.c_str();
|
||||
const char* port = &tmp[pos + 1];
|
||||
|
||||
if (inet_pton(AF_INET, ip, &ipv4_addr) == 1)
|
||||
{
|
||||
valid = true;
|
||||
pAddr->SetIPv4(ntohl(ipv4_addr.s_addr), strtoul(port, nullptr, 10));
|
||||
}
|
||||
}
|
||||
else
|
||||
{// Try ipv4 without port
|
||||
in_addr ipv4_addr;
|
||||
if (inet_pton(AF_INET, str.c_str(), &ipv4_addr) == 1)
|
||||
{
|
||||
valid = true;
|
||||
pAddr->SetIPv4(ntohl(ipv4_addr.s_addr), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{// Try ipv6
|
||||
addrinfo* info = nullptr;
|
||||
addrinfo hints = {};
|
||||
hints.ai_family = AF_INET6;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
|
||||
|
||||
size_t sep_pos = 0;
|
||||
std::string ip;
|
||||
int sep_count = 0;
|
||||
for (int i = 0; i < str.length(); ++i)
|
||||
{
|
||||
if (str[i] == ':')
|
||||
{
|
||||
sep_pos = i;
|
||||
++sep_count;
|
||||
}
|
||||
}
|
||||
if (sep_count == 8)
|
||||
{
|
||||
ip = std::move(std::string(str.begin(), str.begin() + sep_pos));
|
||||
}
|
||||
else
|
||||
{
|
||||
ip = str;
|
||||
}
|
||||
|
||||
if (getaddrinfo(ip.c_str(), nullptr, &hints, &info) == 0)
|
||||
{
|
||||
sockaddr_in6* maddr = (sockaddr_in6*)info->ai_addr;
|
||||
|
||||
size_t pos = str.find(']');
|
||||
std::string str_port("0");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
str_port = std::move(std::string(str.begin() + pos + 2, str.end()));
|
||||
}
|
||||
else if (sep_count == 8)
|
||||
{
|
||||
str_port = std::move(std::string(str.begin() + sep_pos + 1, str.end()));
|
||||
}
|
||||
try
|
||||
{
|
||||
int port = std::stoi(str_port);
|
||||
if (port >= 0 && port <= 65535)
|
||||
{
|
||||
pAddr->SetIPv6(maddr->sin6_addr.s6_addr, port);
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{ }
|
||||
}
|
||||
|
||||
if (info)
|
||||
{
|
||||
freeaddrinfo(info);
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
pAddr->Clear();
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
ESteamNetworkingFakeIPType SteamNetworkingIPAddr_GetFakeIPType( const SteamNetworkingIPAddr &addr )
|
||||
{
|
||||
PRINT_DEBUG("TODO: %s\n", __FUNCTION__);
|
||||
return k_ESteamNetworkingFakeIPType_NotFake;
|
||||
}
|
||||
|
||||
|
||||
void SteamNetworkingIdentity_ToString( const SteamNetworkingIdentity &identity, char *buf, size_t cbBuf )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIdentity_ToString\n");
|
||||
if (buf == nullptr)
|
||||
return;
|
||||
|
||||
std::string str;
|
||||
str.reserve(SteamNetworkingIdentity::k_cchMaxString);
|
||||
switch (identity.m_eType)
|
||||
{
|
||||
case k_ESteamNetworkingIdentityType_SteamID:
|
||||
{
|
||||
str = "steamid:";
|
||||
str += std::move(std::to_string(identity.GetSteamID64()));
|
||||
}
|
||||
break;
|
||||
|
||||
case k_ESteamNetworkingIdentityType_IPAddress:
|
||||
{
|
||||
str = "ip:";
|
||||
char buff[SteamNetworkingIPAddr::k_cchMaxString];
|
||||
auto& addr = *identity.GetIPAddr();
|
||||
SteamNetworkingIPAddr_ToString(addr, buff, sizeof(buff), true);
|
||||
str += buff;
|
||||
}
|
||||
break;
|
||||
|
||||
case k_ESteamNetworkingIdentityType_GenericBytes:
|
||||
{
|
||||
int generic_len;
|
||||
const uint8* pBuf = identity.GetGenericBytes(generic_len);
|
||||
|
||||
str = "gen:";
|
||||
str.resize(4 + (generic_len * 2));
|
||||
|
||||
char* pDest = &str[4];
|
||||
while(generic_len--)
|
||||
{
|
||||
// I don't care for the last char, I've reserved the max string size
|
||||
snprintf(pDest, 3, "%02x", *pBuf);
|
||||
++pBuf;
|
||||
pDest += 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case k_ESteamNetworkingIdentityType_GenericString:
|
||||
{
|
||||
str = "str:";
|
||||
str += identity.GetGenericString();
|
||||
}
|
||||
break;
|
||||
|
||||
case k_ESteamNetworkingIdentityType_UnknownType:
|
||||
{
|
||||
str = identity.m_szUnknownRawString;
|
||||
}
|
||||
break;
|
||||
}
|
||||
cbBuf = std::min<size_t>(cbBuf, str.length() + 1);
|
||||
strncpy(buf, str.c_str(), cbBuf);
|
||||
buf[cbBuf - 1] = '\0';
|
||||
}
|
||||
|
||||
bool SteamNetworkingIdentity_ParseString( SteamNetworkingIdentity *pIdentity, const char *pszStr )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIdentity_ParseString\n");
|
||||
bool valid = false;
|
||||
if (pIdentity == nullptr)
|
||||
{
|
||||
return valid;
|
||||
}
|
||||
|
||||
if (pszStr != nullptr)
|
||||
{
|
||||
const char* end = strchr(pszStr, ':');
|
||||
if (end != nullptr)
|
||||
{
|
||||
++end;
|
||||
if (strncmp(pszStr, "gen:", end - pszStr) == 0)
|
||||
{
|
||||
size_t length = strlen(end);
|
||||
if (!(length % 2) && length <= (sizeof(pIdentity->m_genericBytes) * 2))
|
||||
{// Must be even
|
||||
valid = true;
|
||||
length /= 2;
|
||||
pIdentity->m_eType = k_ESteamNetworkingIdentityType_GenericBytes;
|
||||
pIdentity->m_cbSize = length;
|
||||
uint8* pBytes = pIdentity->m_genericBytes;
|
||||
|
||||
char hex[3] = { 0,0,0 };
|
||||
while (length)
|
||||
{
|
||||
hex[0] = end[0];
|
||||
hex[1] = end[1];
|
||||
// Steam doesn't check if wasn't a hex char
|
||||
*pBytes = strtol(hex, nullptr, 16);
|
||||
|
||||
++pBytes;
|
||||
end += 2;
|
||||
--length;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strncmp(pszStr, "steamid:", end - pszStr) == 0)
|
||||
{
|
||||
CSteamID steam_id(uint64(strtoull(end, nullptr, 10)));
|
||||
if (steam_id.IsValid())
|
||||
{
|
||||
valid = true;
|
||||
pIdentity->SetSteamID(steam_id);
|
||||
}
|
||||
}
|
||||
else if (strncmp(pszStr, "str:", end - pszStr) == 0)
|
||||
{
|
||||
valid = pIdentity->SetGenericString(end);
|
||||
}
|
||||
else if (strncmp(pszStr, "ip:", end - pszStr) == 0)
|
||||
{
|
||||
SteamNetworkingIPAddr steam_addr;
|
||||
if (SteamNetworkingIPAddr_ParseString(&steam_addr, end))
|
||||
{
|
||||
valid = true;
|
||||
pIdentity->SetIPAddr(steam_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
if (init_relay && !relay_initialized) {
|
||||
relay_initialized = true;
|
||||
SteamRelayNetworkStatus_t data = get_network_status();
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->has_networking_sockets()) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
56
all/steam_parental.cpp
Normal file
56
all/steam_parental.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_parental.h"
|
||||
|
||||
bool Steam_Parental::BIsParentalLockEnabled()
|
||||
{
|
||||
PRINT_DEBUG("BIsParentalLockEnabled\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_Parental::BIsParentalLockLocked()
|
||||
{
|
||||
PRINT_DEBUG("BIsParentalLockLocked\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Steam_Parental::BIsAppBlocked( AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("BIsAppBlocked\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_Parental::BIsAppInBlockList( AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("BIsAppInBlockList\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Steam_Parental::BIsFeatureBlocked( EParentalFeature eFeature )
|
||||
{
|
||||
PRINT_DEBUG("BIsFeatureBlocked\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_Parental::BIsFeatureInBlockList( EParentalFeature eFeature )
|
||||
{
|
||||
PRINT_DEBUG("BIsFeatureInBlockList\n");
|
||||
return false;
|
||||
}
|
||||
31
all/steam_parental.h
Normal file
31
all/steam_parental.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Parental : public ISteamParentalSettings
|
||||
{
|
||||
public:
|
||||
bool BIsParentalLockEnabled();
|
||||
bool BIsParentalLockLocked();
|
||||
|
||||
bool BIsAppBlocked( AppId_t nAppID );
|
||||
bool BIsAppInBlockList( AppId_t nAppID );
|
||||
|
||||
bool BIsFeatureBlocked( EParentalFeature eFeature );
|
||||
bool BIsFeatureInBlockList( EParentalFeature eFeature );
|
||||
};
|
||||
194
all/steam_parties.h
Normal file
194
all/steam_parties.h
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Parties :
|
||||
public ISteamParties
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
|
||||
FSteamNetworkingSocketsDebugOutput debug_function;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_parties_callback\n");
|
||||
|
||||
Steam_Parties *steam_parties = (Steam_Parties *)object;
|
||||
steam_parties->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_parties_run_every_runcb\n");
|
||||
|
||||
Steam_Parties *steam_parties = (Steam_Parties *)object;
|
||||
steam_parties->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Parties(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Parties::steam_callback, this);
|
||||
this->run_every_runcb->add(&Steam_Parties::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_Parties()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
this->run_every_runcb->remove(&Steam_Parties::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================================
|
||||
// Party Client APIs
|
||||
|
||||
// Enumerate any active beacons for parties you may wish to join
|
||||
uint32 GetNumActiveBeacons()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetNumActiveBeacons\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
PartyBeaconID_t GetBeaconByIndex( uint32 unIndex )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetBeaconByIndex\n");
|
||||
return k_ulPartyBeaconIdInvalid;
|
||||
}
|
||||
|
||||
bool GetBeaconDetails( PartyBeaconID_t ulBeaconID, CSteamID *pSteamIDBeaconOwner, STEAM_OUT_STRUCT() SteamPartyBeaconLocation_t *pLocation, STEAM_OUT_STRING_COUNT(cchMetadata) char *pchMetadata, int cchMetadata )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetBeaconDetails\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Join an open party. Steam will reserve one beacon slot for your SteamID,
|
||||
// and return the necessary JoinGame string for you to use to connect
|
||||
STEAM_CALL_RESULT( JoinPartyCallback_t )
|
||||
SteamAPICall_t JoinParty( PartyBeaconID_t ulBeaconID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::JoinParty\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================================
|
||||
// Party Host APIs
|
||||
|
||||
// Get a list of possible beacon locations
|
||||
bool GetNumAvailableBeaconLocations( uint32 *puNumLocations )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetNumAvailableBeaconLocations\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetAvailableBeaconLocations( SteamPartyBeaconLocation_t *pLocationList, uint32 uMaxNumLocations )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetAvailableBeaconLocations\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Create a new party beacon and activate it in the selected location.
|
||||
// unOpenSlots is the maximum number of users that Steam will send to you.
|
||||
// When people begin responding to your beacon, Steam will send you
|
||||
// PartyReservationCallback_t callbacks to let you know who is on the way.
|
||||
STEAM_CALL_RESULT( CreateBeaconCallback_t )
|
||||
SteamAPICall_t CreateBeacon( uint32 unOpenSlots, SteamPartyBeaconLocation_t *pBeaconLocation, const char *pchConnectString, const char *pchMetadata )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::CreateBeacon\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Call this function when a user that had a reservation (see callback below)
|
||||
// has successfully joined your party.
|
||||
// Steam will manage the remaining open slots automatically.
|
||||
void OnReservationCompleted( PartyBeaconID_t ulBeacon, CSteamID steamIDUser )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::OnReservationCompleted\n");
|
||||
}
|
||||
|
||||
|
||||
// To cancel a reservation (due to timeout or user input), call this.
|
||||
// Steam will open a new reservation slot.
|
||||
// Note: The user may already be in-flight to your game, so it's possible they will still connect and try to join your party.
|
||||
void CancelReservation( PartyBeaconID_t ulBeacon, CSteamID steamIDUser )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::CancelReservation\n");
|
||||
}
|
||||
|
||||
|
||||
// Change the number of open beacon reservation slots.
|
||||
// Call this if, for example, someone without a reservation joins your party (eg a friend, or via your own matchmaking system).
|
||||
STEAM_CALL_RESULT( ChangeNumOpenSlotsCallback_t )
|
||||
SteamAPICall_t ChangeNumOpenSlots( PartyBeaconID_t ulBeacon, uint32 unOpenSlots )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::ChangeNumOpenSlots\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Turn off the beacon.
|
||||
bool DestroyBeacon( PartyBeaconID_t ulBeacon )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::DestroyBeacon\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Utils
|
||||
bool GetBeaconLocationData( SteamPartyBeaconLocation_t BeaconLocation, ESteamPartyBeaconLocationData eData, STEAM_OUT_STRING_COUNT(cchDataStringOut) char *pchDataStringOut, int cchDataStringOut )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetBeaconLocationData\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->has_networking_sockets()) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
793
all/steam_remote_storage.h
Normal file
793
all/steam_remote_storage.h
Normal file
|
|
@ -0,0 +1,793 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
struct Async_Read {
|
||||
SteamAPICall_t api_call;
|
||||
uint32 offset;
|
||||
uint32 to_read;
|
||||
uint32 size;
|
||||
std::string file_name;
|
||||
};
|
||||
|
||||
struct Stream_Write {
|
||||
std::string file_name;
|
||||
UGCFileWriteStreamHandle_t write_stream_handle;
|
||||
std::vector<char> file_data;
|
||||
};
|
||||
|
||||
struct Downloaded_File {
|
||||
std::string file;
|
||||
uint64 total_size;
|
||||
};
|
||||
|
||||
class Steam_Remote_Storage :
|
||||
public ISteamRemoteStorage001,
|
||||
public ISteamRemoteStorage002,
|
||||
public ISteamRemoteStorage003,
|
||||
public ISteamRemoteStorage004,
|
||||
public ISteamRemoteStorage005,
|
||||
public ISteamRemoteStorage006,
|
||||
public ISteamRemoteStorage007,
|
||||
public ISteamRemoteStorage008,
|
||||
public ISteamRemoteStorage009,
|
||||
public ISteamRemoteStorage010,
|
||||
public ISteamRemoteStorage011,
|
||||
public ISteamRemoteStorage012,
|
||||
public ISteamRemoteStorage013,
|
||||
public ISteamRemoteStorage014,
|
||||
public ISteamRemoteStorage
|
||||
{
|
||||
private:
|
||||
class Settings *settings;
|
||||
Local_Storage *local_storage;
|
||||
class SteamCallResults *callback_results;
|
||||
bool steam_cloud_enabled;
|
||||
std::vector<struct Async_Read> async_reads;
|
||||
std::vector<struct Stream_Write> stream_writes;
|
||||
std::map<UGCHandle_t, std::string> shared_files;
|
||||
std::map<UGCHandle_t, struct Downloaded_File> downloaded_files;
|
||||
|
||||
public:
|
||||
Steam_Remote_Storage(class Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->local_storage = local_storage;
|
||||
this->callback_results = callback_results;
|
||||
steam_cloud_enabled = true;
|
||||
local_storage->update_save_filenames(Local_Storage::remote_storage_folder);
|
||||
}
|
||||
|
||||
// NOTE
|
||||
//
|
||||
// Filenames are case-insensitive, and will be converted to lowercase automatically.
|
||||
// So "foo.bar" and "Foo.bar" are the same file, and if you write "Foo.bar" then
|
||||
// iterate the files, the filename returned will be "foo.bar".
|
||||
//
|
||||
|
||||
// file operations
|
||||
bool FileWrite( const char *pchFile, const void *pvData, int32 cubData )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileWrite %s %u\n", pchFile, cubData);
|
||||
if (!pchFile || cubData <= 0 || cubData > k_unMaxCloudFileChunkSize || !pvData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
int data_stored = local_storage->store_data(Local_Storage::remote_storage_folder, pchFile, (char* )pvData, cubData);
|
||||
PRINT_DEBUG("Steam_Remote_Storage::Stored %i, %u\n", data_stored, data_stored == cubData);
|
||||
return data_stored == cubData;
|
||||
}
|
||||
|
||||
int32 FileRead( const char *pchFile, void *pvData, int32 cubDataToRead )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileRead %s %i\n", pchFile, cubDataToRead);
|
||||
if (!pchFile || !pvData || !cubDataToRead) return 0;
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
int read_data = local_storage->get_data(Local_Storage::remote_storage_folder, pchFile, (char* )pvData, cubDataToRead);
|
||||
if (read_data < 0) read_data = 0;
|
||||
PRINT_DEBUG("Read %i\n", read_data);
|
||||
return read_data;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageFileWriteAsyncComplete_t )
|
||||
SteamAPICall_t FileWriteAsync( const char *pchFile, const void *pvData, uint32 cubData )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileWriteAsync\n");
|
||||
if (!pchFile || cubData > k_unMaxCloudFileChunkSize || cubData == 0 || !pvData) {
|
||||
return k_uAPICallInvalid;
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
bool success = local_storage->store_data(Local_Storage::remote_storage_folder, pchFile, (char* )pvData, cubData) == cubData;
|
||||
RemoteStorageFileWriteAsyncComplete_t data;
|
||||
data.m_eResult = success ? k_EResultOK : k_EResultFail;
|
||||
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.01);
|
||||
}
|
||||
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageFileReadAsyncComplete_t )
|
||||
SteamAPICall_t FileReadAsync( const char *pchFile, uint32 nOffset, uint32 cubToRead )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileReadAsync\n");
|
||||
if (!pchFile) return k_uAPICallInvalid;
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
unsigned int size = local_storage->file_size(Local_Storage::remote_storage_folder, pchFile);
|
||||
|
||||
RemoteStorageFileReadAsyncComplete_t data;
|
||||
if (size <= nOffset) {
|
||||
return k_uAPICallInvalid;
|
||||
}
|
||||
|
||||
if ((size - nOffset) < cubToRead) cubToRead = size - nOffset;
|
||||
|
||||
struct Async_Read a_read;
|
||||
data.m_eResult = k_EResultOK;
|
||||
a_read.offset = data.m_nOffset = nOffset;
|
||||
a_read.api_call = data.m_hFileReadAsync = callback_results->reserveCallResult();
|
||||
a_read.to_read = data.m_cubRead = cubToRead;
|
||||
a_read.file_name = std::string(pchFile);
|
||||
a_read.size = size;
|
||||
|
||||
async_reads.push_back(a_read);
|
||||
callback_results->addCallResult(data.m_hFileReadAsync, data.k_iCallback, &data, sizeof(data), 0.0);
|
||||
return data.m_hFileReadAsync;
|
||||
}
|
||||
|
||||
bool FileReadAsyncComplete( SteamAPICall_t hReadCall, void *pvBuffer, uint32 cubToRead )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileReadAsyncComplete\n");
|
||||
if (!pvBuffer) return false;
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
auto a_read = std::find_if(async_reads.begin(), async_reads.end(), [&hReadCall](Async_Read const& item) { return item.api_call == hReadCall; });
|
||||
if (async_reads.end() == a_read)
|
||||
return false;
|
||||
|
||||
if (cubToRead < a_read->to_read)
|
||||
return false;
|
||||
|
||||
char *temp = new char[a_read->size];
|
||||
int read_data = local_storage->get_data(Local_Storage::remote_storage_folder, a_read->file_name, (char* )temp, a_read->size);
|
||||
if (read_data < a_read->to_read + a_read->offset) {
|
||||
delete[] temp;
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(pvBuffer, temp + a_read->offset, a_read->to_read);
|
||||
delete[] temp;
|
||||
async_reads.erase(a_read);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FileForget( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileForget\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileDelete( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileDelete\n");
|
||||
return local_storage->file_delete(Local_Storage::remote_storage_folder, pchFile);
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageFileShareResult_t )
|
||||
SteamAPICall_t FileShare( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileShare\n");
|
||||
if (!pchFile) return k_uAPICallInvalid;
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
RemoteStorageFileShareResult_t data = {};
|
||||
if (local_storage->file_exists(Local_Storage::remote_storage_folder, pchFile)) {
|
||||
data.m_eResult = k_EResultOK;
|
||||
data.m_hFile = generate_steam_api_call_id();
|
||||
strncpy(data.m_rgchFilename, pchFile, sizeof(data.m_rgchFilename) - 1);
|
||||
shared_files[data.m_hFile] = pchFile;
|
||||
} else {
|
||||
data.m_eResult = k_EResultFileNotFound;
|
||||
}
|
||||
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
bool SetSyncPlatforms( const char *pchFile, ERemoteStoragePlatform eRemoteStoragePlatform )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::SetSyncPlatforms\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// file operations that cause network IO
|
||||
UGCFileWriteStreamHandle_t FileWriteStreamOpen( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileWriteStreamOpen\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
static UGCFileWriteStreamHandle_t handle;
|
||||
++handle;
|
||||
struct Stream_Write stream_write;
|
||||
stream_write.file_name = std::string(pchFile);
|
||||
stream_write.write_stream_handle = handle;
|
||||
stream_writes.push_back(stream_write);
|
||||
return stream_write.write_stream_handle;
|
||||
}
|
||||
|
||||
bool FileWriteStreamWriteChunk( UGCFileWriteStreamHandle_t writeHandle, const void *pvData, int32 cubData )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileWriteStreamWriteChunk\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto request = std::find_if(stream_writes.begin(), stream_writes.end(), [&writeHandle](struct Stream_Write const& item) { return item.write_stream_handle == writeHandle; });
|
||||
if (stream_writes.end() == request)
|
||||
return false;
|
||||
|
||||
std::copy((char *)pvData, (char *)pvData + cubData, std::back_inserter(request->file_data));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileWriteStreamClose( UGCFileWriteStreamHandle_t writeHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileWriteStreamClose\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto request = std::find_if(stream_writes.begin(), stream_writes.end(), [&writeHandle](struct Stream_Write const& item) { return item.write_stream_handle == writeHandle; });
|
||||
if (stream_writes.end() == request)
|
||||
return false;
|
||||
|
||||
local_storage->store_data(Local_Storage::remote_storage_folder, request->file_name, request->file_data.data(), request->file_data.size());
|
||||
stream_writes.erase(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileWriteStreamCancel( UGCFileWriteStreamHandle_t writeHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileWriteStreamCancel\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto request = std::find_if(stream_writes.begin(), stream_writes.end(), [&writeHandle](struct Stream_Write const& item) { return item.write_stream_handle == writeHandle; });
|
||||
if (stream_writes.end() == request)
|
||||
return false;
|
||||
|
||||
stream_writes.erase(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
// file information
|
||||
bool FileExists( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileExists %s\n", pchFile);
|
||||
return local_storage->file_exists(Local_Storage::remote_storage_folder, pchFile);
|
||||
}
|
||||
|
||||
bool FilePersisted( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FilePersisted\n");
|
||||
return local_storage->file_exists(Local_Storage::remote_storage_folder, pchFile);
|
||||
}
|
||||
|
||||
int32 GetFileSize( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetFileSize %s\n", pchFile);
|
||||
return local_storage->file_size(Local_Storage::remote_storage_folder, pchFile);
|
||||
}
|
||||
|
||||
int64 GetFileTimestamp( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetFileTimestamp\n");
|
||||
return local_storage->file_timestamp(Local_Storage::remote_storage_folder, pchFile);
|
||||
}
|
||||
|
||||
ERemoteStoragePlatform GetSyncPlatforms( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetSyncPlatforms\n");
|
||||
return k_ERemoteStoragePlatformAll;
|
||||
}
|
||||
|
||||
|
||||
// iteration
|
||||
int32 GetFileCount()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetFileCount\n");
|
||||
int32 num = local_storage->count_files(Local_Storage::remote_storage_folder);
|
||||
PRINT_DEBUG("Steam_Remote_Storage::File count: %i\n", num);
|
||||
return num;
|
||||
}
|
||||
|
||||
const char *GetFileNameAndSize( int iFile, int32 *pnFileSizeInBytes )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetFileNameAndSize %i\n", iFile);
|
||||
static char output_filename[MAX_FILENAME_LENGTH];
|
||||
if (local_storage->iterate_file(Local_Storage::remote_storage_folder, iFile, output_filename, pnFileSizeInBytes)) {
|
||||
PRINT_DEBUG("Steam_Remote_Storage::Name: |%s|, size: %i\n", output_filename, pnFileSizeInBytes ? *pnFileSizeInBytes : 0);
|
||||
return output_filename;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// configuration management
|
||||
bool GetQuota( uint64 *pnTotalBytes, uint64 *puAvailableBytes )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetQuota\n");
|
||||
uint64 quota = 2 << 26;
|
||||
*pnTotalBytes = quota;
|
||||
*puAvailableBytes = (quota);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetQuota( int32 *pnTotalBytes, int32 *puAvailableBytes )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetQuota\n");
|
||||
uint64 quota = 2 << 26;
|
||||
*pnTotalBytes = quota;
|
||||
*puAvailableBytes = (quota);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsCloudEnabledForAccount()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::IsCloudEnabledForAccount\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsCloudEnabledForApp()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::IsCloudEnabledForApp\n");
|
||||
return steam_cloud_enabled;
|
||||
}
|
||||
|
||||
bool IsCloudEnabledThisApp()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::IsCloudEnabledThisApp\n");
|
||||
return steam_cloud_enabled;
|
||||
}
|
||||
|
||||
void SetCloudEnabledForApp( bool bEnabled )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::SetCloudEnabledForApp\n");
|
||||
steam_cloud_enabled = bEnabled;
|
||||
}
|
||||
|
||||
bool SetCloudEnabledThisApp( bool bEnabled )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::SetCloudEnabledThisApp\n");
|
||||
steam_cloud_enabled = bEnabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
// user generated content
|
||||
|
||||
// Downloads a UGC file. A priority value of 0 will download the file immediately,
|
||||
// otherwise it will wait to download the file until all downloads with a lower priority
|
||||
// value are completed. Downloads with equal priority will occur simultaneously.
|
||||
STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
|
||||
SteamAPICall_t UGCDownload( UGCHandle_t hContent, uint32 unPriority )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UGCDownload\n");
|
||||
RemoteStorageDownloadUGCResult_t data = {};
|
||||
if (shared_files.count(hContent)) {
|
||||
data.m_eResult = k_EResultOK;
|
||||
data.m_hFile = hContent;
|
||||
data.m_nAppID = settings->get_local_game_id().AppID();
|
||||
data.m_nSizeInBytes = local_storage->file_size(Local_Storage::remote_storage_folder, shared_files[hContent]);
|
||||
shared_files[hContent].copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1);
|
||||
data.m_ulSteamIDOwner = settings->get_local_steam_id().ConvertToUint64();
|
||||
downloaded_files[hContent].file = shared_files[hContent];
|
||||
downloaded_files[hContent].total_size = data.m_nSizeInBytes;
|
||||
} else {
|
||||
data.m_eResult = k_EResultFileNotFound; //TODO: not sure if this is the right result
|
||||
}
|
||||
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
|
||||
SteamAPICall_t UGCDownload( UGCHandle_t hContent )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UGCDownload old\n");
|
||||
return UGCDownload(hContent, 1);
|
||||
}
|
||||
|
||||
|
||||
// Gets the amount of data downloaded so far for a piece of content. pnBytesExpected can be 0 if function returns false
|
||||
// or if the transfer hasn't started yet, so be careful to check for that before dividing to get a percentage
|
||||
bool GetUGCDownloadProgress( UGCHandle_t hContent, int32 *pnBytesDownloaded, int32 *pnBytesExpected )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDownloadProgress\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetUGCDownloadProgress( UGCHandle_t hContent, uint32 *pnBytesDownloaded, uint32 *pnBytesExpected )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDownloadProgress old\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Gets metadata for a file after it has been downloaded. This is the same metadata given in the RemoteStorageDownloadUGCResult_t call result
|
||||
bool GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID, STEAM_OUT_STRING() char **ppchName, int32 *pnFileSizeInBytes, STEAM_OUT_STRUCT() CSteamID *pSteamIDOwner )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDetails\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// After download, gets the content of the file.
|
||||
// Small files can be read all at once by calling this function with an offset of 0 and cubDataToRead equal to the size of the file.
|
||||
// Larger files can be read in chunks to reduce memory usage (since both sides of the IPC client and the game itself must allocate
|
||||
// enough memory for each chunk). Once the last byte is read, the file is implicitly closed and further calls to UGCRead will fail
|
||||
// unless UGCDownload is called again.
|
||||
// For especially large files (anything over 100MB) it is a requirement that the file is read in chunks.
|
||||
int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead, uint32 cOffset, EUGCReadAction eAction )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UGCRead\n");
|
||||
if (!downloaded_files.count(hContent) || cubDataToRead < 0) {
|
||||
return -1; //TODO: is this the right return value?
|
||||
}
|
||||
|
||||
Downloaded_File f = downloaded_files[hContent];
|
||||
int read_data = local_storage->get_data(Local_Storage::remote_storage_folder, f.file, (char* )pvData, cubDataToRead, cOffset);
|
||||
|
||||
if (eAction == k_EUGCRead_Close || (eAction == k_EUGCRead_ContinueReadingUntilFinished && (read_data < cubDataToRead || (cOffset + cubDataToRead) >= f.total_size))) {
|
||||
downloaded_files.erase(hContent);
|
||||
}
|
||||
|
||||
PRINT_DEBUG("Read %i\n", read_data);
|
||||
return read_data;
|
||||
}
|
||||
|
||||
int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UGCRead old\n");
|
||||
return UGCRead( hContent, pvData, cubDataToRead, 0);
|
||||
}
|
||||
|
||||
int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead, uint32 cOffset)
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UGCRead old2\n");
|
||||
return UGCRead(hContent, pvData, cubDataToRead, cOffset, k_EUGCRead_ContinueReadingUntilFinished);
|
||||
}
|
||||
|
||||
// Functions to iterate through UGC that has finished downloading but has not yet been read via UGCRead()
|
||||
int32 GetCachedUGCCount()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetCachedUGCCount\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
UGCHandle_t GetCachedUGCHandle( int32 iCachedContent )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetCachedUGCHandle\n");
|
||||
return k_UGCHandleInvalid;
|
||||
}
|
||||
|
||||
|
||||
// The following functions are only necessary on the Playstation 3. On PC & Mac, the Steam client will handle these operations for you
|
||||
// On Playstation 3, the game controls which files are stored in the cloud, via FilePersist, FileFetch, and FileForget.
|
||||
|
||||
#if defined(_PS3) || defined(_SERVER)
|
||||
// Connect to Steam and get a list of files in the Cloud - results in a RemoteStorageAppSyncStatusCheck_t callback
|
||||
void GetFileListFromServer()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetFileListFromServer\n");
|
||||
}
|
||||
|
||||
// Indicate this file should be downloaded in the next sync
|
||||
bool FileFetch( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FileFetch\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Indicate this file should be persisted in the next sync
|
||||
bool FilePersist( const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::FilePersist\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pull any requested files down from the Cloud - results in a RemoteStorageAppSyncedClient_t callback
|
||||
bool SynchronizeToClient()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::SynchronizeToClient\n");
|
||||
}
|
||||
|
||||
// Upload any requested files to the Cloud - results in a RemoteStorageAppSyncedServer_t callback
|
||||
bool SynchronizeToServer()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::SynchronizeToServer\n");
|
||||
}
|
||||
|
||||
// Reset any fetch/persist/etc requests
|
||||
bool ResetFileRequestState()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::ResetFileRequestState\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// publishing UGC
|
||||
STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
|
||||
SteamAPICall_t PublishWorkshopFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags, EWorkshopFileType eWorkshopFileType )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::PublishWorkshopFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
PublishedFileUpdateHandle_t CreatePublishedFileUpdateRequest( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::CreatePublishedFileUpdateRequest\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileFile( PublishedFileUpdateHandle_t updateHandle, const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileFile\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
SteamAPICall_t PublishFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::PublishFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SteamAPICall_t PublishWorkshopFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::PublishWorkshopFile old\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SteamAPICall_t UpdatePublishedFile( RemoteStorageUpdatePublishedFileRequest_t updatePublishedFileRequest )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFilePreviewFile( PublishedFileUpdateHandle_t updateHandle, const char *pchPreviewFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFilePreviewFile\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileTitle( PublishedFileUpdateHandle_t updateHandle, const char *pchTitle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileTitle\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchDescription )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileDescription\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileVisibility( PublishedFileUpdateHandle_t updateHandle, ERemoteStoragePublishedFileVisibility eVisibility )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileVisibility\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileTags( PublishedFileUpdateHandle_t updateHandle, SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileTags\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageUpdatePublishedFileResult_t )
|
||||
SteamAPICall_t CommitPublishedFileUpdate( PublishedFileUpdateHandle_t updateHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::CommitPublishedFileUpdate\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Gets published file details for the given publishedfileid. If unMaxSecondsOld is greater than 0,
|
||||
// cached data may be returned, depending on how long ago it was cached. A value of 0 will force a refresh.
|
||||
// A value of k_WorkshopForceLoadPublishedFileDetailsFromCache will use cached data if it exists, no matter how old it is.
|
||||
STEAM_CALL_RESULT( RemoteStorageGetPublishedFileDetailsResult_t )
|
||||
SteamAPICall_t GetPublishedFileDetails( PublishedFileId_t unPublishedFileId, uint32 unMaxSecondsOld )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedFileDetails %llu\n", unPublishedFileId);
|
||||
//TODO: check what this function really returns
|
||||
return 0;
|
||||
/*
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
RemoteStorageGetPublishedFileDetailsResult_t data = {};
|
||||
data.m_eResult = k_EResultFail;
|
||||
data.m_nPublishedFileId = unPublishedFileId;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
*/
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageGetPublishedFileDetailsResult_t )
|
||||
SteamAPICall_t GetPublishedFileDetails( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedFileDetails old\n");
|
||||
return GetPublishedFileDetails(unPublishedFileId, 0);
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageDeletePublishedFileResult_t )
|
||||
SteamAPICall_t DeletePublishedFile( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::DeletePublishedFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// enumerate the files that the current user published with this app
|
||||
STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t )
|
||||
SteamAPICall_t EnumerateUserPublishedFiles( uint32 unStartIndex )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::EnumerateUserPublishedFiles\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
RemoteStorageEnumerateUserPublishedFilesResult_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
data.m_nResultsReturned = 0;
|
||||
data.m_nTotalResultCount = 0;
|
||||
//data.m_rgPublishedFileId;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t )
|
||||
SteamAPICall_t SubscribePublishedFile( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::SubscribePublishedFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageEnumerateUserSubscribedFilesResult_t )
|
||||
SteamAPICall_t EnumerateUserSubscribedFiles( uint32 unStartIndex )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::EnumerateUserSubscribedFiles\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
RemoteStorageEnumerateUserSubscribedFilesResult_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
data.m_nResultsReturned = 0;
|
||||
data.m_nTotalResultCount = 0;
|
||||
//data.m_rgPublishedFileId;
|
||||
//data.m_rgRTimeSubscribed;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t )
|
||||
SteamAPICall_t UnsubscribePublishedFile( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UnsubscribePublishedFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileSetChangeDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchChangeDescription )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileSetChangeDescription\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t )
|
||||
SteamAPICall_t GetPublishedItemVoteDetails( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedItemVoteDetails\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageUpdateUserPublishedItemVoteResult_t )
|
||||
SteamAPICall_t UpdateUserPublishedItemVote( PublishedFileId_t unPublishedFileId, bool bVoteUp )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdateUserPublishedItemVote\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t )
|
||||
SteamAPICall_t GetUserPublishedItemVoteDetails( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetUserPublishedItemVoteDetails\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t )
|
||||
SteamAPICall_t EnumerateUserSharedWorkshopFiles( CSteamID steamId, uint32 unStartIndex, SteamParamStringArray_t *pRequiredTags, SteamParamStringArray_t *pExcludedTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::EnumerateUserSharedWorkshopFiles\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
RemoteStorageEnumerateUserPublishedFilesResult_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
data.m_nResultsReturned = 0;
|
||||
data.m_nTotalResultCount = 0;
|
||||
//data.m_rgPublishedFileId;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t )
|
||||
SteamAPICall_t EnumerateUserSharedWorkshopFiles(AppId_t nAppId, CSteamID steamId, uint32 unStartIndex, SteamParamStringArray_t *pRequiredTags, SteamParamStringArray_t *pExcludedTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::EnumerateUserSharedWorkshopFiles old\n");
|
||||
return EnumerateUserSharedWorkshopFiles(steamId, unStartIndex, pRequiredTags, pExcludedTags);
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
|
||||
SteamAPICall_t PublishVideo( EWorkshopVideoProvider eVideoProvider, const char *pchVideoAccount, const char *pchVideoIdentifier, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::PublishVideo\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
|
||||
SteamAPICall_t PublishVideo(const char *pchFileName, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::PublishVideo old\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageSetUserPublishedFileActionResult_t )
|
||||
SteamAPICall_t SetUserPublishedFileAction( PublishedFileId_t unPublishedFileId, EWorkshopFileAction eAction )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::SetUserPublishedFileAction\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageEnumeratePublishedFilesByUserActionResult_t )
|
||||
SteamAPICall_t EnumeratePublishedFilesByUserAction( EWorkshopFileAction eAction, uint32 unStartIndex )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::EnumeratePublishedFilesByUserAction\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// this method enumerates the public view of workshop files
|
||||
STEAM_CALL_RESULT( RemoteStorageEnumerateWorkshopFilesResult_t )
|
||||
SteamAPICall_t EnumeratePublishedWorkshopFiles( EWorkshopEnumerationType eEnumerationType, uint32 unStartIndex, uint32 unCount, uint32 unDays, SteamParamStringArray_t *pTags, SteamParamStringArray_t *pUserTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::EnumeratePublishedWorkshopFiles\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
|
||||
SteamAPICall_t UGCDownloadToLocation( UGCHandle_t hContent, const char *pchLocation, uint32 unPriority )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UGCDownloadToLocation\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Cloud dynamic state change notification
|
||||
int32 GetLocalFileChangeCount()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *GetLocalFileChange( int iFile, ERemoteStorageLocalFileChange *pEChangeType, ERemoteStorageFilePathType *pEFilePathType )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return "";
|
||||
}
|
||||
|
||||
// Indicate to Steam the beginning / end of a set of local file
|
||||
// operations - for example, writing a game save that requires updating two files.
|
||||
bool BeginFileWriteBatch()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EndFileWriteBatch()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
137
all/steam_remoteplay.h
Normal file
137
all/steam_remoteplay.h
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_RemotePlay :
|
||||
public ISteamRemotePlay
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_remoteplay_callback\n");
|
||||
|
||||
Steam_RemotePlay *steam_remoteplay = (Steam_RemotePlay *)object;
|
||||
steam_remoteplay->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_remoteplay_run_every_runcb\n");
|
||||
|
||||
Steam_RemotePlay *steam_remoteplay = (Steam_RemotePlay *)object;
|
||||
steam_remoteplay->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_RemotePlay(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_RemotePlay::steam_callback, this);
|
||||
this->run_every_runcb->add(&Steam_RemotePlay::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_RemotePlay()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
this->run_every_runcb->remove(&Steam_RemotePlay::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
// Get the number of currently connected Steam Remote Play sessions
|
||||
uint32 GetSessionCount()
|
||||
{
|
||||
PRINT_DEBUG("Steam_RemotePlay::GetSessionCount\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get the currently connected Steam Remote Play session ID at the specified index. Returns zero if index is out of bounds.
|
||||
uint32 GetSessionID( int iSessionIndex )
|
||||
{
|
||||
PRINT_DEBUG("Steam_RemotePlay::GetSessionID\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get the SteamID of the connected user
|
||||
CSteamID GetSessionSteamID( uint32 unSessionID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_RemotePlay::GetSessionSteamID\n");
|
||||
return k_steamIDNil;
|
||||
}
|
||||
|
||||
// Get the name of the session client device
|
||||
// This returns NULL if the sessionID is not valid
|
||||
const char *GetSessionClientName( uint32 unSessionID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_RemotePlay::GetSessionClientName\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the form factor of the session client device
|
||||
ESteamDeviceFormFactor GetSessionClientFormFactor( uint32 unSessionID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_RemotePlay::GetSessionClientFormFactor\n");
|
||||
return k_ESteamDeviceFormFactorUnknown;
|
||||
}
|
||||
|
||||
// Get the resolution, in pixels, of the session client device
|
||||
// This is set to 0x0 if the resolution is not available
|
||||
bool BGetSessionClientResolution( uint32 unSessionID, int *pnResolutionX, int *pnResolutionY )
|
||||
{
|
||||
PRINT_DEBUG("Steam_RemotePlay::BGetSessionClientResolution\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Invite a friend to Remote Play Together
|
||||
// This returns false if the invite can't be sent
|
||||
bool BSendRemotePlayTogetherInvite( CSteamID steamIDFriend )
|
||||
{
|
||||
PRINT_DEBUG("Steam_RemotePlay::BSendRemotePlayTogetherInvite\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->has_networking_sockets()) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
184
all/steam_screenshots.cpp
Normal file
184
all/steam_screenshots.cpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_screenshots.h"
|
||||
|
||||
Steam_Screenshots::Steam_Screenshots(class Local_Storage* local_storage, class SteamCallBacks* callbacks) :
|
||||
local_storage(local_storage),
|
||||
callbacks(callbacks)
|
||||
{
|
||||
}
|
||||
|
||||
ScreenshotHandle Steam_Screenshots::create_screenshot_handle()
|
||||
{
|
||||
static ScreenshotHandle handle = 100;
|
||||
return handle++;
|
||||
}
|
||||
|
||||
// Writes a screenshot to the user's screenshot library given the raw image data, which must be in RGB format.
|
||||
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
|
||||
ScreenshotHandle Steam_Screenshots::WriteScreenshot( void *pubRGB, uint32 cubRGB, int nWidth, int nHeight )
|
||||
{
|
||||
PRINT_DEBUG("WriteScreenshot\n");
|
||||
|
||||
char buff[128];
|
||||
auto now = std::chrono::system_clock::now();
|
||||
time_t now_time;
|
||||
now_time = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
|
||||
strftime(buff, 128, "%a_%b_%d_%H_%M_%S_%Y", localtime(&now_time));
|
||||
std::string screenshot_name = buff;
|
||||
screenshot_name += ".png";
|
||||
|
||||
if (!local_storage->save_screenshot( screenshot_name, (uint8_t*)pubRGB, nWidth, nHeight, 3))
|
||||
return INVALID_SCREENSHOT_HANDLE;
|
||||
|
||||
auto handle = create_screenshot_handle();
|
||||
auto& infos = _screenshots[handle];
|
||||
infos.screenshot_name = buff;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
// Adds a screenshot to the user's screenshot library from disk. If a thumbnail is provided, it must be 200 pixels wide and the same aspect ratio
|
||||
// as the screenshot, otherwise a thumbnail will be generated if the user uploads the screenshot. The screenshots must be in either JPEG or TGA format.
|
||||
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
|
||||
// JPEG, TGA, and PNG formats are supported.
|
||||
ScreenshotHandle Steam_Screenshots::AddScreenshotToLibrary( const char *pchFilename, const char *pchThumbnailFilename, int nWidth, int nHeight )
|
||||
{
|
||||
PRINT_DEBUG("AddScreenshotToLibrary\n");
|
||||
|
||||
if (pchFilename == nullptr)
|
||||
return INVALID_SCREENSHOT_HANDLE;
|
||||
|
||||
std::vector<image_pixel_t> pixels(std::move(local_storage->load_image(pchFilename)));
|
||||
if (pixels.size() != size_t(nWidth) * size_t(nHeight))
|
||||
return INVALID_SCREENSHOT_HANDLE;
|
||||
|
||||
char buff[128];
|
||||
auto now = std::chrono::system_clock::now();
|
||||
time_t now_time;
|
||||
now_time = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
|
||||
strftime(buff, 128, "%a_%b_%d_%H_%M_%S_%Y", localtime(&now_time));
|
||||
std::string screenshot_name = buff;
|
||||
screenshot_name += ".png";
|
||||
|
||||
if (!local_storage->save_screenshot(screenshot_name, (uint8_t*)pixels.data(), nWidth, nHeight, 4))
|
||||
return INVALID_SCREENSHOT_HANDLE;
|
||||
|
||||
auto handle = create_screenshot_handle();
|
||||
auto& infos = _screenshots[handle];
|
||||
infos.screenshot_name = buff;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
// Causes the Steam overlay to take a screenshot. If screenshots are being hooked by the game then a ScreenshotRequested_t callback is sent back to the game instead.
|
||||
void Steam_Screenshots::TriggerScreenshot()
|
||||
{
|
||||
PRINT_DEBUG("TriggerScreenshot\n");
|
||||
|
||||
if (hooked)
|
||||
{
|
||||
ScreenshotRequested_t data;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
else
|
||||
{
|
||||
PRINT_DEBUG("TODO: Make the overlay take a screenshot");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Toggles whether the overlay handles screenshots when the user presses the screenshot hotkey, or the game handles them. If the game is hooking screenshots,
|
||||
// then the ScreenshotRequested_t callback will be sent if the user presses the hotkey, and the game is expected to call WriteScreenshot or AddScreenshotToLibrary
|
||||
// in response.
|
||||
void Steam_Screenshots::HookScreenshots( bool bHook )
|
||||
{
|
||||
PRINT_DEBUG("HookScreenshots\n");
|
||||
hooked = bHook;
|
||||
}
|
||||
|
||||
|
||||
// Sets metadata about a screenshot's location (for example, the name of the map)
|
||||
bool Steam_Screenshots::SetLocation( ScreenshotHandle hScreenshot, const char *pchLocation )
|
||||
{
|
||||
PRINT_DEBUG("SetLocation\n");
|
||||
|
||||
auto it = _screenshots.find(hScreenshot);
|
||||
if (it == _screenshots.end())
|
||||
return false;
|
||||
|
||||
it->second.metadatas["locations"].push_back(pchLocation);
|
||||
local_storage->write_json_file(Local_Storage::screenshots_folder, it->second.screenshot_name + ".json", it->second.metadatas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Tags a user as being visible in the screenshot
|
||||
bool Steam_Screenshots::TagUser( ScreenshotHandle hScreenshot, CSteamID steamID )
|
||||
{
|
||||
PRINT_DEBUG("TagUser\n");
|
||||
|
||||
auto it = _screenshots.find(hScreenshot);
|
||||
if (it == _screenshots.end())
|
||||
return false;
|
||||
|
||||
it->second.metadatas["users"].push_back(uint64_t(steamID.ConvertToUint64()));
|
||||
local_storage->write_json_file(Local_Storage::screenshots_folder, it->second.screenshot_name + ".json", it->second.metadatas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Tags a published file as being visible in the screenshot
|
||||
bool Steam_Screenshots::TagPublishedFile( ScreenshotHandle hScreenshot, PublishedFileId_t unPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("TagPublishedFile\n");
|
||||
|
||||
auto it = _screenshots.find(hScreenshot);
|
||||
if (it == _screenshots.end())
|
||||
return false;
|
||||
|
||||
it->second.metadatas["published_files"].push_back(uint64_t(unPublishedFileID));
|
||||
local_storage->write_json_file(Local_Storage::screenshots_folder, it->second.screenshot_name + ".json", it->second.metadatas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the app has hooked the screenshot
|
||||
bool Steam_Screenshots::IsScreenshotsHooked()
|
||||
{
|
||||
PRINT_DEBUG("IsScreenshotsHooked\n");
|
||||
return hooked;
|
||||
}
|
||||
|
||||
|
||||
// Adds a VR screenshot to the user's screenshot library from disk in the supported type.
|
||||
// pchFilename should be the normal 2D image used in the library view
|
||||
// pchVRFilename should contain the image that matches the correct type
|
||||
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
|
||||
// JPEG, TGA, and PNG formats are supported.
|
||||
ScreenshotHandle Steam_Screenshots::AddVRScreenshotToLibrary( EVRScreenshotType eType, const char *pchFilename, const char *pchVRFilename )
|
||||
{
|
||||
PRINT_DEBUG("AddVRScreenshotToLibrary\n");
|
||||
return INVALID_SCREENSHOT_HANDLE;
|
||||
}
|
||||
|
||||
75
all/steam_screenshots.h
Normal file
75
all/steam_screenshots.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
struct screenshot_infos_t
|
||||
{
|
||||
std::string screenshot_name;
|
||||
nlohmann::json metadatas;
|
||||
};
|
||||
|
||||
class Steam_Screenshots : public ISteamScreenshots
|
||||
{
|
||||
bool hooked = false;
|
||||
std::map<ScreenshotHandle, screenshot_infos_t> _screenshots;
|
||||
|
||||
class Local_Storage* local_storage;
|
||||
class SteamCallBacks* callbacks;
|
||||
|
||||
ScreenshotHandle create_screenshot_handle();
|
||||
|
||||
public:
|
||||
Steam_Screenshots(class Local_Storage* local_storage, class SteamCallBacks* callbacks);
|
||||
|
||||
// Writes a screenshot to the user's screenshot library given the raw image data, which must be in RGB format.
|
||||
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
|
||||
ScreenshotHandle WriteScreenshot( void *pubRGB, uint32 cubRGB, int nWidth, int nHeight );
|
||||
|
||||
// Adds a screenshot to the user's screenshot library from disk. If a thumbnail is provided, it must be 200 pixels wide and the same aspect ratio
|
||||
// as the screenshot, otherwise a thumbnail will be generated if the user uploads the screenshot. The screenshots must be in either JPEG or TGA format.
|
||||
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
|
||||
// JPEG, TGA, and PNG formats are supported.
|
||||
ScreenshotHandle AddScreenshotToLibrary( const char *pchFilename, const char *pchThumbnailFilename, int nWidth, int nHeight );
|
||||
|
||||
// Causes the Steam overlay to take a screenshot. If screenshots are being hooked by the game then a ScreenshotRequested_t callback is sent back to the game instead.
|
||||
void TriggerScreenshot();
|
||||
|
||||
// Toggles whether the overlay handles screenshots when the user presses the screenshot hotkey, or the game handles them. If the game is hooking screenshots,
|
||||
// then the ScreenshotRequested_t callback will be sent if the user presses the hotkey, and the game is expected to call WriteScreenshot or AddScreenshotToLibrary
|
||||
// in response.
|
||||
void HookScreenshots( bool bHook );
|
||||
|
||||
// Sets metadata about a screenshot's location (for example, the name of the map)
|
||||
bool SetLocation( ScreenshotHandle hScreenshot, const char *pchLocation );
|
||||
|
||||
// Tags a user as being visible in the screenshot
|
||||
bool TagUser( ScreenshotHandle hScreenshot, CSteamID steamID );
|
||||
|
||||
// Tags a published file as being visible in the screenshot
|
||||
bool TagPublishedFile( ScreenshotHandle hScreenshot, PublishedFileId_t unPublishedFileID );
|
||||
|
||||
// Returns true if the app has hooked the screenshot
|
||||
bool IsScreenshotsHooked();
|
||||
|
||||
// Adds a VR screenshot to the user's screenshot library from disk in the supported type.
|
||||
// pchFilename should be the normal 2D image used in the library view
|
||||
// pchVRFilename should contain the image that matches the correct type
|
||||
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
|
||||
// JPEG, TGA, and PNG formats are supported.
|
||||
ScreenshotHandle AddVRScreenshotToLibrary( EVRScreenshotType eType, const char *pchFilename, const char *pchVRFilename );
|
||||
};
|
||||
124
all/steam_tv.h
Normal file
124
all/steam_tv.h
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_TV :
|
||||
public ISteamTV
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
|
||||
FSteamNetworkingSocketsDebugOutput debug_function;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_tv_callback\n");
|
||||
|
||||
Steam_TV *steam_parties = (Steam_TV *)object;
|
||||
steam_parties->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_tv_run_every_runcb\n");
|
||||
|
||||
Steam_TV *steam_parties = (Steam_TV *)object;
|
||||
steam_parties->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_TV(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_TV::steam_callback, this);
|
||||
// this->run_every_runcb->add(&Steam_TV::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_TV()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
//this->run_every_runcb->remove(&Steam_TV::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
bool IsBroadcasting(int *pnNumViewers)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
void AddBroadcastGameData(const char * pchKey, const char * pchValue)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void RemoveBroadcastGameData(const char * pchKey)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void AddTimelineMarker(const char * pchTemplateName, bool bPersistent, uint8 nColorR, uint8 nColorG, uint8 nColorB)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void RemoveTimelineMarker()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
uint32 AddRegion(const char * pchElementName, const char * pchTimelineDataSection, const SteamTVRegion_t * pSteamTVRegion, ESteamTVRegionBehavior eSteamTVRegionBehavior)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RemoveRegion(uint32 unRegionHandle)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->has_networking_sockets()) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
888
all/steam_ugc.h
Normal file
888
all/steam_ugc.h
Normal file
|
|
@ -0,0 +1,888 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
struct UGC_query {
|
||||
UGCQueryHandle_t handle;
|
||||
std::set<PublishedFileId_t> return_only;
|
||||
bool return_all_subscribed;
|
||||
|
||||
std::set<PublishedFileId_t> results;
|
||||
};
|
||||
|
||||
class Steam_UGC :
|
||||
public ISteamUGC001,
|
||||
public ISteamUGC002,
|
||||
public ISteamUGC003,
|
||||
public ISteamUGC004,
|
||||
public ISteamUGC005,
|
||||
public ISteamUGC006,
|
||||
public ISteamUGC007,
|
||||
public ISteamUGC008,
|
||||
public ISteamUGC009,
|
||||
public ISteamUGC010,
|
||||
public ISteamUGC012,
|
||||
public ISteamUGC013,
|
||||
public ISteamUGC014,
|
||||
public ISteamUGC015,
|
||||
public ISteamUGC
|
||||
{
|
||||
class Settings *settings;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
|
||||
std::set<PublishedFileId_t> subscribed;
|
||||
UGCQueryHandle_t handle = 0;
|
||||
std::vector<struct UGC_query> ugc_queries;
|
||||
|
||||
UGCQueryHandle_t new_ugc_query(bool return_all_subscribed = false, std::set<PublishedFileId_t> return_only = std::set<PublishedFileId_t>())
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct UGC_query query;
|
||||
++handle;
|
||||
query.handle = handle;
|
||||
query.return_all_subscribed = return_all_subscribed;
|
||||
query.return_only = return_only;
|
||||
ugc_queries.push_back(query);
|
||||
return query.handle;
|
||||
}
|
||||
|
||||
void set_details(PublishedFileId_t id, SteamUGCDetails_t *pDetails)
|
||||
{
|
||||
if (pDetails) {
|
||||
if (settings->isModInstalled(id)) {
|
||||
pDetails->m_eResult = k_EResultOK;
|
||||
pDetails->m_nPublishedFileId = id;
|
||||
pDetails->m_eFileType = k_EWorkshopFileTypeCommunity;
|
||||
pDetails->m_nCreatorAppID = settings->get_local_game_id().AppID();
|
||||
pDetails->m_nConsumerAppID = settings->get_local_game_id().AppID();
|
||||
snprintf(pDetails->m_rgchTitle, sizeof(pDetails->m_rgchDescription), "%s", settings->getMod(id).title.c_str());
|
||||
//TODO
|
||||
} else {
|
||||
pDetails->m_nPublishedFileId = id;
|
||||
pDetails->m_eResult = k_EResultFail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Steam_UGC(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->callbacks = callbacks;
|
||||
this->callback_results = callback_results;
|
||||
|
||||
subscribed = settings->modSet();
|
||||
}
|
||||
|
||||
|
||||
// Query UGC associated with a user. Creator app id or consumer app id must be valid and be set to the current running app. unPage should start at 1.
|
||||
UGCQueryHandle_t CreateQueryUserUGCRequest( AccountID_t unAccountID, EUserUGCList eListType, EUGCMatchingUGCType eMatchingUGCType, EUserUGCListSortOrder eSortOrder, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint32 unPage )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::CreateQueryUserUGCRequest %u %i %i %i %u %u %u\n", unAccountID, eListType, eMatchingUGCType, eSortOrder, nCreatorAppID, nConsumerAppID, unPage);
|
||||
//TODO
|
||||
return new_ugc_query(eListType == k_EUserUGCList_Subscribed || eListType == k_EUserUGCList_Published);
|
||||
}
|
||||
|
||||
|
||||
// Query for all matching UGC. Creator app id or consumer app id must be valid and be set to the current running app. unPage should start at 1.
|
||||
UGCQueryHandle_t CreateQueryAllUGCRequest( EUGCQuery eQueryType, EUGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint32 unPage )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::CreateQueryAllUGCRequest\n");
|
||||
//TODO
|
||||
return new_ugc_query();
|
||||
}
|
||||
|
||||
// Query for all matching UGC using the new deep paging interface. Creator app id or consumer app id must be valid and be set to the current running app. pchCursor should be set to NULL or "*" to get the first result set.
|
||||
UGCQueryHandle_t CreateQueryAllUGCRequest( EUGCQuery eQueryType, EUGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, const char *pchCursor = NULL )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::CreateQueryAllUGCRequest other\n");
|
||||
//TODO
|
||||
return new_ugc_query();
|
||||
}
|
||||
|
||||
// Query for the details of the given published file ids (the RequestUGCDetails call is deprecated and replaced with this)
|
||||
UGCQueryHandle_t CreateQueryUGCDetailsRequest( PublishedFileId_t *pvecPublishedFileID, uint32 unNumPublishedFileIDs )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::CreateQueryUGCDetailsRequest\n");
|
||||
std::set<PublishedFileId_t> only(pvecPublishedFileID, pvecPublishedFileID + unNumPublishedFileIDs);
|
||||
return new_ugc_query(false, only);
|
||||
}
|
||||
|
||||
|
||||
// Send the query to Steam
|
||||
STEAM_CALL_RESULT( SteamUGCQueryCompleted_t )
|
||||
SteamAPICall_t SendQueryUGCRequest( UGCQueryHandle_t handle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SendQueryUGCRequest\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto request = std::find_if(ugc_queries.begin(), ugc_queries.end(), [&handle](struct UGC_query const& item) { return item.handle == handle; });
|
||||
if (ugc_queries.end() == request)
|
||||
return 0;
|
||||
|
||||
if (request->return_all_subscribed) {
|
||||
request->results = subscribed;
|
||||
}
|
||||
|
||||
if (request->return_only.size()) {
|
||||
for (auto & s : request->return_only) {
|
||||
if (subscribed.count(s)) {
|
||||
request->results.insert(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SteamUGCQueryCompleted_t data = {};
|
||||
data.m_handle = handle;
|
||||
data.m_eResult = k_EResultOK;
|
||||
data.m_unNumResultsReturned = request->results.size();
|
||||
data.m_unTotalMatchingResults = request->results.size();
|
||||
data.m_bCachedData = false;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
|
||||
// Retrieve an individual result after receiving the callback for querying UGC
|
||||
bool GetQueryUGCResult( UGCQueryHandle_t handle, uint32 index, SteamUGCDetails_t *pDetails )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCResult %u\n", index);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (pDetails) {
|
||||
memset(pDetails, 0, sizeof(SteamUGCDetails_t));
|
||||
pDetails->m_eResult = k_EResultFail;
|
||||
}
|
||||
|
||||
auto request = std::find_if(ugc_queries.begin(), ugc_queries.end(), [&handle](struct UGC_query const& item) { return item.handle == handle; });
|
||||
if (ugc_queries.end() == request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index >= request->results.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = request->results.begin();
|
||||
std::advance(it, index);
|
||||
set_details(*it, pDetails);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 GetQueryUGCNumTags( UGCQueryHandle_t handle, uint32 index )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCNumTags\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GetQueryUGCTag( UGCQueryHandle_t handle, uint32 index, uint32 indexTag, STEAM_OUT_STRING_COUNT( cchValueSize ) char* pchValue, uint32 cchValueSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCTag\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetQueryUGCTagDisplayName( UGCQueryHandle_t handle, uint32 index, uint32 indexTag, STEAM_OUT_STRING_COUNT( cchValueSize ) char* pchValue, uint32 cchValueSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCTagDisplayName\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetQueryUGCPreviewURL( UGCQueryHandle_t handle, uint32 index, STEAM_OUT_STRING_COUNT(cchURLSize) char *pchURL, uint32 cchURLSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCPreviewURL\n");
|
||||
//TODO: escape simulator tries downloading this url and unsubscribes if it fails
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GetQueryUGCMetadata( UGCQueryHandle_t handle, uint32 index, STEAM_OUT_STRING_COUNT(cchMetadatasize) char *pchMetadata, uint32 cchMetadatasize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCMetadata\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GetQueryUGCChildren( UGCQueryHandle_t handle, uint32 index, PublishedFileId_t* pvecPublishedFileID, uint32 cMaxEntries )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCChildren\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GetQueryUGCStatistic( UGCQueryHandle_t handle, uint32 index, EItemStatistic eStatType, uint64 *pStatValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCStatistic\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetQueryUGCStatistic( UGCQueryHandle_t handle, uint32 index, EItemStatistic eStatType, uint32 *pStatValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCStatistic old\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 GetQueryUGCNumAdditionalPreviews( UGCQueryHandle_t handle, uint32 index )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCNumAdditionalPreviews\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool GetQueryUGCAdditionalPreview( UGCQueryHandle_t handle, uint32 index, uint32 previewIndex, STEAM_OUT_STRING_COUNT(cchURLSize) char *pchURLOrVideoID, uint32 cchURLSize, STEAM_OUT_STRING_COUNT(cchURLSize) char *pchOriginalFileName, uint32 cchOriginalFileNameSize, EItemPreviewType *pPreviewType )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCAdditionalPreview\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetQueryUGCAdditionalPreview( UGCQueryHandle_t handle, uint32 index, uint32 previewIndex, char *pchURLOrVideoID, uint32 cchURLSize, bool *hz )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCAdditionalPreview old\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 GetQueryUGCNumKeyValueTags( UGCQueryHandle_t handle, uint32 index )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCNumKeyValueTags\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool GetQueryUGCKeyValueTag( UGCQueryHandle_t handle, uint32 index, uint32 keyValueTagIndex, STEAM_OUT_STRING_COUNT(cchKeySize) char *pchKey, uint32 cchKeySize, STEAM_OUT_STRING_COUNT(cchValueSize) char *pchValue, uint32 cchValueSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCKeyValueTag\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetQueryUGCKeyValueTag( UGCQueryHandle_t handle, uint32 index, const char *pchKey, STEAM_OUT_STRING_COUNT(cchValueSize) char *pchValue, uint32 cchValueSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetQueryUGCKeyValueTag2\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Release the request to free up memory, after retrieving results
|
||||
bool ReleaseQueryUGCRequest( UGCQueryHandle_t handle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::ReleaseQueryUGCRequest\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto request = std::find_if(ugc_queries.begin(), ugc_queries.end(), [&handle](struct UGC_query const& item) { return item.handle == handle; });
|
||||
if (ugc_queries.end() == request)
|
||||
return false;
|
||||
|
||||
ugc_queries.erase(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Options to set for querying UGC
|
||||
bool AddRequiredTag( UGCQueryHandle_t handle, const char *pTagName )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddRequiredTag\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddRequiredTagGroup( UGCQueryHandle_t handle, const SteamParamStringArray_t *pTagGroups )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddRequiredTagGroup\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddExcludedTag( UGCQueryHandle_t handle, const char *pTagName )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddExcludedTag\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetReturnOnlyIDs( UGCQueryHandle_t handle, bool bReturnOnlyIDs )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetReturnOnlyIDs\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetReturnKeyValueTags( UGCQueryHandle_t handle, bool bReturnKeyValueTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetReturnKeyValueTags\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetReturnLongDescription( UGCQueryHandle_t handle, bool bReturnLongDescription )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetReturnLongDescription\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetReturnMetadata( UGCQueryHandle_t handle, bool bReturnMetadata )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetReturnMetadata\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetReturnChildren( UGCQueryHandle_t handle, bool bReturnChildren )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetReturnChildren\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetReturnAdditionalPreviews( UGCQueryHandle_t handle, bool bReturnAdditionalPreviews )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetReturnAdditionalPreviews\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetReturnTotalOnly( UGCQueryHandle_t handle, bool bReturnTotalOnly )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetReturnTotalOnly\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetReturnPlaytimeStats( UGCQueryHandle_t handle, uint32 unDays )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetReturnPlaytimeStats\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetLanguage( UGCQueryHandle_t handle, const char *pchLanguage )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetLanguage\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetAllowCachedResponse( UGCQueryHandle_t handle, uint32 unMaxAgeSeconds )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetAllowCachedResponse\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Options only for querying user UGC
|
||||
bool SetCloudFileNameFilter( UGCQueryHandle_t handle, const char *pMatchCloudFileName )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetCloudFileNameFilter\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Options only for querying all UGC
|
||||
bool SetMatchAnyTag( UGCQueryHandle_t handle, bool bMatchAnyTag )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetMatchAnyTag\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetSearchText( UGCQueryHandle_t handle, const char *pSearchText )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetSearchText\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SetRankedByTrendDays( UGCQueryHandle_t handle, uint32 unDays )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetRankedByTrendDays\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool AddRequiredKeyValueTag( UGCQueryHandle_t handle, const char *pKey, const char *pValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddRequiredKeyValueTag\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetTimeCreatedDateRange( UGCQueryHandle_t handle, RTime32 rtStart, RTime32 rtEnd )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetTimeCreatedDateRange\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetTimeUpdatedDateRange( UGCQueryHandle_t handle, RTime32 rtStart, RTime32 rtEnd )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetTimeUpdatedDateRange\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// DEPRECATED - Use CreateQueryUGCDetailsRequest call above instead!
|
||||
SteamAPICall_t RequestUGCDetails( PublishedFileId_t nPublishedFileID, uint32 unMaxAgeSeconds )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::RequestUGCDetails\n");
|
||||
SteamUGCRequestUGCDetailsResult_t data = {};
|
||||
data.m_bCachedData = false;
|
||||
set_details(nPublishedFileID, &(data.m_details));
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
SteamAPICall_t RequestUGCDetails( PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::RequestUGCDetails old\n");
|
||||
return RequestUGCDetails(nPublishedFileID, 0);
|
||||
}
|
||||
|
||||
|
||||
// Steam Workshop Creator API
|
||||
STEAM_CALL_RESULT( CreateItemResult_t )
|
||||
SteamAPICall_t CreateItem( AppId_t nConsumerAppId, EWorkshopFileType eFileType )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::CreateItem\n");
|
||||
return 0;
|
||||
}
|
||||
// create new item for this app with no content attached yet
|
||||
|
||||
|
||||
UGCUpdateHandle_t StartItemUpdate( AppId_t nConsumerAppId, PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::StartItemUpdate\n");
|
||||
return 0;
|
||||
}
|
||||
// start an UGC item update. Set changed properties before commiting update with CommitItemUpdate()
|
||||
|
||||
|
||||
bool SetItemTitle( UGCUpdateHandle_t handle, const char *pchTitle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetItemTitle\n");
|
||||
return false;
|
||||
}
|
||||
// change the title of an UGC item
|
||||
|
||||
|
||||
bool SetItemDescription( UGCUpdateHandle_t handle, const char *pchDescription )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetItemDescription\n");
|
||||
return false;
|
||||
}
|
||||
// change the description of an UGC item
|
||||
|
||||
|
||||
bool SetItemUpdateLanguage( UGCUpdateHandle_t handle, const char *pchLanguage )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetItemUpdateLanguage\n");
|
||||
return false;
|
||||
}
|
||||
// specify the language of the title or description that will be set
|
||||
|
||||
|
||||
bool SetItemMetadata( UGCUpdateHandle_t handle, const char *pchMetaData )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetItemMetadata\n");
|
||||
return false;
|
||||
}
|
||||
// change the metadata of an UGC item (max = k_cchDeveloperMetadataMax)
|
||||
|
||||
|
||||
bool SetItemVisibility( UGCUpdateHandle_t handle, ERemoteStoragePublishedFileVisibility eVisibility )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetItemVisibility\n");
|
||||
return false;
|
||||
}
|
||||
// change the visibility of an UGC item
|
||||
|
||||
|
||||
bool SetItemTags( UGCUpdateHandle_t updateHandle, const SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetItemTags\n");
|
||||
return false;
|
||||
}
|
||||
// change the tags of an UGC item
|
||||
|
||||
|
||||
bool SetItemContent( UGCUpdateHandle_t handle, const char *pszContentFolder )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetItemContent\n");
|
||||
return false;
|
||||
}
|
||||
// update item content from this local folder
|
||||
|
||||
|
||||
bool SetItemPreview( UGCUpdateHandle_t handle, const char *pszPreviewFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetItemPreview\n");
|
||||
return false;
|
||||
}
|
||||
// change preview image file for this item. pszPreviewFile points to local image file, which must be under 1MB in size
|
||||
|
||||
bool SetAllowLegacyUpload( UGCUpdateHandle_t handle, bool bAllowLegacyUpload )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetAllowLegacyUpload\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RemoveAllItemKeyValueTags( UGCUpdateHandle_t handle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::RemoveAllItemKeyValueTags\n");
|
||||
return false;
|
||||
}
|
||||
// remove all existing key-value tags (you can add new ones via the AddItemKeyValueTag function)
|
||||
|
||||
bool RemoveItemKeyValueTags( UGCUpdateHandle_t handle, const char *pchKey )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::RemoveItemKeyValueTags\n");
|
||||
return false;
|
||||
}
|
||||
// remove any existing key-value tags with the specified key
|
||||
|
||||
|
||||
bool AddItemKeyValueTag( UGCUpdateHandle_t handle, const char *pchKey, const char *pchValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddItemKeyValueTag\n");
|
||||
return false;
|
||||
}
|
||||
// add new key-value tags for the item. Note that there can be multiple values for a tag.
|
||||
|
||||
|
||||
bool AddItemPreviewFile( UGCUpdateHandle_t handle, const char *pszPreviewFile, EItemPreviewType type )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddItemPreviewFile\n");
|
||||
return false;
|
||||
}
|
||||
// add preview file for this item. pszPreviewFile points to local file, which must be under 1MB in size
|
||||
|
||||
|
||||
bool AddItemPreviewVideo( UGCUpdateHandle_t handle, const char *pszVideoID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddItemPreviewVideo\n");
|
||||
return false;
|
||||
}
|
||||
// add preview video for this item
|
||||
|
||||
|
||||
bool UpdateItemPreviewFile( UGCUpdateHandle_t handle, uint32 index, const char *pszPreviewFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::UpdateItemPreviewFile\n");
|
||||
return false;
|
||||
}
|
||||
// updates an existing preview file for this item. pszPreviewFile points to local file, which must be under 1MB in size
|
||||
|
||||
|
||||
bool UpdateItemPreviewVideo( UGCUpdateHandle_t handle, uint32 index, const char *pszVideoID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::UpdateItemPreviewVideo\n");
|
||||
return false;
|
||||
}
|
||||
// updates an existing preview video for this item
|
||||
|
||||
|
||||
bool RemoveItemPreview( UGCUpdateHandle_t handle, uint32 index )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::RemoveItemPreview %llu %u\n", handle, index);
|
||||
return false;
|
||||
}
|
||||
// remove a preview by index starting at 0 (previews are sorted)
|
||||
|
||||
|
||||
STEAM_CALL_RESULT( SubmitItemUpdateResult_t )
|
||||
SteamAPICall_t SubmitItemUpdate( UGCUpdateHandle_t handle, const char *pchChangeNote )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SubmitItemUpdate\n");
|
||||
return 0;
|
||||
}
|
||||
// commit update process started with StartItemUpdate()
|
||||
|
||||
|
||||
EItemUpdateStatus GetItemUpdateProgress( UGCUpdateHandle_t handle, uint64 *punBytesProcessed, uint64* punBytesTotal )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetItemUpdateProgress\n");
|
||||
return k_EItemUpdateStatusInvalid;
|
||||
}
|
||||
|
||||
|
||||
// Steam Workshop Consumer API
|
||||
|
||||
STEAM_CALL_RESULT( SetUserItemVoteResult_t )
|
||||
SteamAPICall_t SetUserItemVote( PublishedFileId_t nPublishedFileID, bool bVoteUp )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SetUserItemVote\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
STEAM_CALL_RESULT( GetUserItemVoteResult_t )
|
||||
SteamAPICall_t GetUserItemVote( PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetUserItemVote\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
STEAM_CALL_RESULT( UserFavoriteItemsListChanged_t )
|
||||
SteamAPICall_t AddItemToFavorites( AppId_t nAppId, PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddItemToFavorites\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
STEAM_CALL_RESULT( UserFavoriteItemsListChanged_t )
|
||||
SteamAPICall_t RemoveItemFromFavorites( AppId_t nAppId, PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::RemoveItemFromFavorites\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t )
|
||||
SteamAPICall_t SubscribeItem( PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SubscribeItem %llu\n", nPublishedFileID);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
subscribed.insert(nPublishedFileID);
|
||||
|
||||
RemoteStorageSubscribePublishedFileResult_t data;
|
||||
if (settings->isModInstalled(nPublishedFileID)) {
|
||||
data.m_eResult = k_EResultOK;
|
||||
} else {
|
||||
data.m_eResult = k_EResultFail;
|
||||
}
|
||||
data.m_nPublishedFileId = nPublishedFileID;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
// subscribe to this item, will be installed ASAP
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t )
|
||||
SteamAPICall_t UnsubscribeItem( PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::UnsubscribeItem %llu\n", nPublishedFileID);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
RemoteStorageUnsubscribePublishedFileResult_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
if (subscribed.count(nPublishedFileID) == 0) {
|
||||
data.m_eResult = k_EResultFail; //TODO: check if this is accurate
|
||||
}
|
||||
|
||||
subscribed.erase(nPublishedFileID);
|
||||
data.m_nPublishedFileId = nPublishedFileID;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
// unsubscribe from this item, will be uninstalled after game quits
|
||||
|
||||
uint32 GetNumSubscribedItems()
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetNumSubscribedItems\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return subscribed.size();
|
||||
}
|
||||
// number of subscribed items
|
||||
|
||||
uint32 GetSubscribedItems( PublishedFileId_t* pvecPublishedFileID, uint32 cMaxEntries )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetSubscribedItems\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (cMaxEntries > subscribed.size()) {
|
||||
cMaxEntries = subscribed.size();
|
||||
}
|
||||
|
||||
std::copy_n(subscribed.begin(), cMaxEntries, pvecPublishedFileID);
|
||||
return cMaxEntries;
|
||||
}
|
||||
// all subscribed item PublishFileIDs
|
||||
|
||||
// get EItemState flags about item on this client
|
||||
uint32 GetItemState( PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetItemState %llu\n", nPublishedFileID);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (subscribed.count(nPublishedFileID)) {
|
||||
if (settings->isModInstalled(nPublishedFileID)) {
|
||||
return k_EItemStateInstalled | k_EItemStateSubscribed;
|
||||
}
|
||||
|
||||
return k_EItemStateSubscribed;
|
||||
}
|
||||
|
||||
return k_EItemStateNone;
|
||||
}
|
||||
|
||||
|
||||
// get info about currently installed content on disc for items that have k_EItemStateInstalled set
|
||||
// if k_EItemStateLegacyItem is set, pchFolder contains the path to the legacy file itself (not a folder)
|
||||
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, STEAM_OUT_STRING_COUNT( cchFolderSize ) char *pchFolder, uint32 cchFolderSize, uint32 *punTimeStamp )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetItemInstallInfo\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (!settings->isModInstalled(nPublishedFileID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (punSizeOnDisk) *punSizeOnDisk = 1000000;
|
||||
if (punTimeStamp) *punTimeStamp = 1554997000;
|
||||
if (pchFolder && cchFolderSize) {
|
||||
snprintf(pchFolder, cchFolderSize, "%s", settings->getMod(nPublishedFileID).path.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// get info about pending update for items that have k_EItemStateNeedsUpdate set. punBytesTotal will be valid after download started once
|
||||
bool GetItemDownloadInfo( PublishedFileId_t nPublishedFileID, uint64 *punBytesDownloaded, uint64 *punBytesTotal )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetItemDownloadInfo\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, STEAM_OUT_STRING_COUNT( cchFolderSize ) char *pchFolder, uint32 cchFolderSize, bool *pbLegacyItem ) // returns true if item is installed
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetItemInstallInfo old\n");
|
||||
return GetItemInstallInfo(nPublishedFileID, punSizeOnDisk, pchFolder, cchFolderSize, (uint32*) nullptr);
|
||||
}
|
||||
|
||||
bool GetItemUpdateInfo( PublishedFileId_t nPublishedFileID, bool *pbNeedsUpdate, bool *pbIsDownloading, uint64 *punBytesDownloaded, uint64 *punBytesTotal )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetItemDownloadInfo old\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, char *pchFolder, uint32 cchFolderSize ) // returns true if item is installed
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetItemInstallInfo older\n");
|
||||
return GetItemInstallInfo(nPublishedFileID, punSizeOnDisk, pchFolder, cchFolderSize, (uint32*) nullptr);
|
||||
}
|
||||
|
||||
|
||||
// download new or update already installed item. If function returns true, wait for DownloadItemResult_t. If the item is already installed,
|
||||
// then files on disk should not be used until callback received. If item is not subscribed to, it will be cached for some time.
|
||||
// If bHighPriority is set, any other item download will be suspended and this item downloaded ASAP.
|
||||
bool DownloadItem( PublishedFileId_t nPublishedFileID, bool bHighPriority )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::DownloadItem\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// game servers can set a specific workshop folder before issuing any UGC commands.
|
||||
// This is helpful if you want to support multiple game servers running out of the same install folder
|
||||
bool BInitWorkshopForGameServer( DepotId_t unWorkshopDepotID, const char *pszFolder )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::BInitWorkshopForGameServer\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// SuspendDownloads( true ) will suspend all workshop downloads until SuspendDownloads( false ) is called or the game ends
|
||||
void SuspendDownloads( bool bSuspend )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::SuspendDownloads\n");
|
||||
}
|
||||
|
||||
|
||||
// usage tracking
|
||||
STEAM_CALL_RESULT( StartPlaytimeTrackingResult_t )
|
||||
SteamAPICall_t StartPlaytimeTracking( PublishedFileId_t *pvecPublishedFileID, uint32 unNumPublishedFileIDs )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::StartPlaytimeTracking\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
StopPlaytimeTrackingResult_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( StopPlaytimeTrackingResult_t )
|
||||
SteamAPICall_t StopPlaytimeTracking( PublishedFileId_t *pvecPublishedFileID, uint32 unNumPublishedFileIDs )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::StopPlaytimeTracking\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
StopPlaytimeTrackingResult_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( StopPlaytimeTrackingResult_t )
|
||||
SteamAPICall_t StopPlaytimeTrackingForAllItems()
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::StopPlaytimeTrackingForAllItems\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
StopPlaytimeTrackingResult_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
|
||||
// parent-child relationship or dependency management
|
||||
STEAM_CALL_RESULT( AddUGCDependencyResult_t )
|
||||
SteamAPICall_t AddDependency( PublishedFileId_t nParentPublishedFileID, PublishedFileId_t nChildPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddDependency\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoveUGCDependencyResult_t )
|
||||
SteamAPICall_t RemoveDependency( PublishedFileId_t nParentPublishedFileID, PublishedFileId_t nChildPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::RemoveDependency\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// add/remove app dependence/requirements (usually DLC)
|
||||
STEAM_CALL_RESULT( AddAppDependencyResult_t )
|
||||
SteamAPICall_t AddAppDependency( PublishedFileId_t nPublishedFileID, AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddAppDependency\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoveAppDependencyResult_t )
|
||||
SteamAPICall_t RemoveAppDependency( PublishedFileId_t nPublishedFileID, AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::RemoveAppDependency\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// request app dependencies. note that whatever callback you register for GetAppDependenciesResult_t may be called multiple times
|
||||
// until all app dependencies have been returned
|
||||
STEAM_CALL_RESULT( GetAppDependenciesResult_t )
|
||||
SteamAPICall_t GetAppDependencies( PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetAppDependencies\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// delete the item without prompting the user
|
||||
STEAM_CALL_RESULT( DeleteItemResult_t )
|
||||
SteamAPICall_t DeleteItem( PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::DeleteItem\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Show the app's latest Workshop EULA to the user in an overlay window, where they can accept it or not
|
||||
bool ShowWorkshopEULA()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve information related to the user's acceptance or not of the app's specific Workshop EULA
|
||||
STEAM_CALL_RESULT( WorkshopEULAStatus_t )
|
||||
SteamAPICall_t GetWorkshopEULAStatus()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
123
all/steam_unified_messages.h
Normal file
123
all/steam_unified_messages.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Unified_Messages :
|
||||
public ISteamUnifiedMessages
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_steamunifiedmessages_callback\n");
|
||||
|
||||
Steam_Unified_Messages *steam_steamunifiedmessages = (Steam_Unified_Messages *)object;
|
||||
steam_steamunifiedmessages->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_steamunifiedmessages_run_every_runcb\n");
|
||||
|
||||
Steam_Unified_Messages *steam_steamunifiedmessages = (Steam_Unified_Messages *)object;
|
||||
steam_steamunifiedmessages->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Unified_Messages(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
// this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Unified_Messages::steam_callback, this);
|
||||
// this->run_every_runcb->add(&Steam_Unified_Messages::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_Unified_Messages()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
// this->run_every_runcb->remove(&Steam_Unified_Messages::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
// Sends a service method (in binary serialized form) using the Steam Client.
|
||||
// Returns a unified message handle (k_InvalidUnifiedMessageHandle if could not send the message).
|
||||
ClientUnifiedMessageHandle SendMethod( const char *pchServiceMethod, const void *pRequestBuffer, uint32 unRequestBufferSize, uint64 unContext )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Unified_Messages::SendMethod\n");
|
||||
return ISteamUnifiedMessages::k_InvalidUnifiedMessageHandle;
|
||||
}
|
||||
|
||||
|
||||
// Gets the size of the response and the EResult. Returns false if the response is not ready yet.
|
||||
bool GetMethodResponseInfo( ClientUnifiedMessageHandle hHandle, uint32 *punResponseSize, EResult *peResult )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Unified_Messages::GetMethodResponseInfo\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Gets a response in binary serialized form (and optionally release the corresponding allocated memory).
|
||||
bool GetMethodResponseData( ClientUnifiedMessageHandle hHandle, void *pResponseBuffer, uint32 unResponseBufferSize, bool bAutoRelease )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Unified_Messages::GetMethodResponseData\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Releases the message and its corresponding allocated memory.
|
||||
bool ReleaseMethod( ClientUnifiedMessageHandle hHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Unified_Messages::ReleaseMethod\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Sends a service notification (in binary serialized form) using the Steam Client.
|
||||
// Returns true if the notification was sent successfully.
|
||||
bool SendNotification( const char *pchServiceNotification, const void *pNotificationBuffer, uint32 unNotificationBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Unified_Messages::SendNotification\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
482
all/steam_user.h
Normal file
482
all/steam_user.h
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_User :
|
||||
public ISteamUser009,
|
||||
public ISteamUser010,
|
||||
public ISteamUser011,
|
||||
public ISteamUser012,
|
||||
public ISteamUser013,
|
||||
public ISteamUser014,
|
||||
public ISteamUser015,
|
||||
public ISteamUser016,
|
||||
public ISteamUser017,
|
||||
public ISteamUser018,
|
||||
public ISteamUser019,
|
||||
public ISteamUser020,
|
||||
public ISteamUser
|
||||
{
|
||||
Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallBacks *callbacks;
|
||||
class SteamCallResults *callback_results;
|
||||
Local_Storage *local_storage;
|
||||
|
||||
bool recording = false;
|
||||
std::chrono::high_resolution_clock::time_point last_get_voice;
|
||||
std::string encrypted_app_ticket;
|
||||
Auth_Ticket_Manager *ticket_manager;
|
||||
|
||||
public:
|
||||
|
||||
Steam_User(Settings *settings, Local_Storage *local_storage, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->local_storage = local_storage;
|
||||
this->network = network;
|
||||
this->callbacks = callbacks;
|
||||
this->callback_results = callback_results;
|
||||
recording = false;
|
||||
ticket_manager = new Auth_Ticket_Manager(settings, network, callbacks);
|
||||
}
|
||||
|
||||
~Steam_User()
|
||||
{
|
||||
delete ticket_manager;
|
||||
}
|
||||
|
||||
// returns the HSteamUser this interface represents
|
||||
// this is only used internally by the API, and by a few select interfaces that support multi-user
|
||||
HSteamUser GetHSteamUser()
|
||||
{
|
||||
PRINT_DEBUG("GetHSteamUser\n");
|
||||
return CLIENT_HSTEAMUSER;
|
||||
}
|
||||
|
||||
// returns true if the Steam client current has a live connection to the Steam servers.
|
||||
// If false, it means there is no active connection due to either a networking issue on the local machine, or the Steam server is down/busy.
|
||||
// The Steam client will automatically be trying to recreate the connection as often as possible.
|
||||
bool BLoggedOn()
|
||||
{
|
||||
PRINT_DEBUG("BLoggedOn\n");
|
||||
return !settings->is_offline();
|
||||
}
|
||||
|
||||
// returns the CSteamID of the account currently logged into the Steam client
|
||||
// a CSteamID is a unique identifier for an account, and used to differentiate users in all parts of the Steamworks API
|
||||
CSteamID GetSteamID()
|
||||
{
|
||||
PRINT_DEBUG("Steam_User::GetSteamID\n");
|
||||
CSteamID id = settings->get_local_steam_id();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
// Multiplayer Authentication functions
|
||||
|
||||
// InitiateGameConnection() starts the state machine for authenticating the game client with the game server
|
||||
// It is the client portion of a three-way handshake between the client, the game server, and the steam servers
|
||||
//
|
||||
// Parameters:
|
||||
// void *pAuthBlob - a pointer to empty memory that will be filled in with the authentication token.
|
||||
// int cbMaxAuthBlob - the number of bytes of allocated memory in pBlob. Should be at least 2048 bytes.
|
||||
// CSteamID steamIDGameServer - the steamID of the game server, received from the game server by the client
|
||||
// CGameID gameID - the ID of the current game. For games without mods, this is just CGameID( <appID> )
|
||||
// uint32 unIPServer, uint16 usPortServer - the IP address of the game server
|
||||
// bool bSecure - whether or not the client thinks that the game server is reporting itself as secure (i.e. VAC is running)
|
||||
//
|
||||
// return value - returns the number of bytes written to pBlob. If the return is 0, then the buffer passed in was too small, and the call has failed
|
||||
// The contents of pBlob should then be sent to the game server, for it to use to complete the authentication process.
|
||||
|
||||
//steam returns 206 bytes
|
||||
#define INITIATE_GAME_CONNECTION_TICKET_SIZE 206
|
||||
|
||||
int InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer, bool bSecure )
|
||||
{
|
||||
PRINT_DEBUG("InitiateGameConnection %i %llu %u %u %u\n", cbMaxAuthBlob, steamIDGameServer.ConvertToUint64(), unIPServer, usPortServer, bSecure);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (cbMaxAuthBlob < INITIATE_GAME_CONNECTION_TICKET_SIZE) return 0;
|
||||
uint32 out_size = INITIATE_GAME_CONNECTION_TICKET_SIZE;
|
||||
ticket_manager->getTicketData(pAuthBlob, INITIATE_GAME_CONNECTION_TICKET_SIZE, &out_size);
|
||||
return out_size;
|
||||
}
|
||||
|
||||
int InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, CGameID gameID, uint32 unIPServer, uint16 usPortServer, bool bSecure )
|
||||
{
|
||||
PRINT_DEBUG("InitiateGameConnection old\n");
|
||||
return InitiateGameConnection(pAuthBlob, cbMaxAuthBlob, steamIDGameServer, unIPServer, usPortServer, bSecure);
|
||||
}
|
||||
|
||||
// notify of disconnect
|
||||
// needs to occur when the game client leaves the specified game server, needs to match with the InitiateGameConnection() call
|
||||
void TerminateGameConnection( uint32 unIPServer, uint16 usPortServer )
|
||||
{
|
||||
PRINT_DEBUG("TerminateGameConnection\n");
|
||||
}
|
||||
|
||||
// Legacy functions
|
||||
|
||||
// used by only a few games to track usage events
|
||||
void TrackAppUsageEvent( CGameID gameID, int eAppUsageEvent, const char *pchExtraInfo)
|
||||
{
|
||||
PRINT_DEBUG("TrackAppUsageEvent\n");
|
||||
}
|
||||
|
||||
void RefreshSteam2Login()
|
||||
{
|
||||
PRINT_DEBUG("RefreshSteam2Login\n");
|
||||
}
|
||||
|
||||
// get the local storage folder for current Steam account to write application data, e.g. save games, configs etc.
|
||||
// this will usually be something like "C:\Progam Files\Steam\userdata\<SteamID>\<AppID>\local"
|
||||
bool GetUserDataFolder( char *pchBuffer, int cubBuffer )
|
||||
{
|
||||
PRINT_DEBUG("GetUserDataFolder\n");
|
||||
if (!cubBuffer) return false;
|
||||
|
||||
std::string user_data = local_storage->get_path(Local_Storage::user_data_storage);
|
||||
strncpy(pchBuffer, user_data.c_str(), cubBuffer - 1);
|
||||
pchBuffer[cubBuffer - 1] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Starts voice recording. Once started, use GetVoice() to get the data
|
||||
void StartVoiceRecording( )
|
||||
{
|
||||
PRINT_DEBUG("StartVoiceRecording\n");
|
||||
last_get_voice = std::chrono::high_resolution_clock::now();
|
||||
recording = true;
|
||||
//TODO:fix
|
||||
recording = false;
|
||||
}
|
||||
|
||||
// Stops voice recording. Because people often release push-to-talk keys early, the system will keep recording for
|
||||
// a little bit after this function is called. GetVoice() should continue to be called until it returns
|
||||
// k_eVoiceResultNotRecording
|
||||
void StopVoiceRecording( )
|
||||
{
|
||||
PRINT_DEBUG("StopVoiceRecording\n");
|
||||
recording = false;
|
||||
}
|
||||
|
||||
// Determine the size of captured audio data that is available from GetVoice.
|
||||
// Most applications will only use compressed data and should ignore the other
|
||||
// parameters, which exist primarily for backwards compatibility. See comments
|
||||
// below for further explanation of "uncompressed" data.
|
||||
EVoiceResult GetAvailableVoice( uint32 *pcbCompressed, uint32 *pcbUncompressed_Deprecated, uint32 nUncompressedVoiceDesiredSampleRate_Deprecated )
|
||||
{
|
||||
PRINT_DEBUG("GetAvailableVoice\n");
|
||||
if (pcbCompressed) *pcbCompressed = 0;
|
||||
if (pcbUncompressed_Deprecated) *pcbUncompressed_Deprecated = 0;
|
||||
if (!recording) return k_EVoiceResultNotRecording;
|
||||
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - last_get_voice).count();
|
||||
if (pcbCompressed) *pcbCompressed = seconds * 1024.0 * 64.0 / 8.0;
|
||||
if (pcbUncompressed_Deprecated) *pcbUncompressed_Deprecated = seconds * (double)nUncompressedVoiceDesiredSampleRate_Deprecated * 2.0;
|
||||
|
||||
return k_EVoiceResultOK;
|
||||
}
|
||||
|
||||
EVoiceResult GetAvailableVoice(uint32 *pcbCompressed, uint32 *pcbUncompressed)
|
||||
{
|
||||
PRINT_DEBUG("GetAvailableVoice old\n");
|
||||
return GetAvailableVoice(pcbCompressed, pcbUncompressed, 11025);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// NOTE: "uncompressed" audio is a deprecated feature and should not be used
|
||||
// by most applications. It is raw single-channel 16-bit PCM wave data which
|
||||
// may have been run through preprocessing filters and/or had silence removed,
|
||||
// so the uncompressed audio could have a shorter duration than you expect.
|
||||
// There may be no data at all during long periods of silence. Also, fetching
|
||||
// uncompressed audio will cause GetVoice to discard any leftover compressed
|
||||
// audio, so you must fetch both types at once. Finally, GetAvailableVoice is
|
||||
// not precisely accurate when the uncompressed size is requested. So if you
|
||||
// really need to use uncompressed audio, you should call GetVoice frequently
|
||||
// with two very large (20kb+) output buffers instead of trying to allocate
|
||||
// perfectly-sized buffers. But most applications should ignore all of these
|
||||
// details and simply leave the "uncompressed" parameters as NULL/zero.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Read captured audio data from the microphone buffer. This should be called
|
||||
// at least once per frame, and preferably every few milliseconds, to keep the
|
||||
// microphone input delay as low as possible. Most applications will only use
|
||||
// compressed data and should pass NULL/zero for the "uncompressed" parameters.
|
||||
// Compressed data can be transmitted by your application and decoded into raw
|
||||
// using the DecompressVoice function below.
|
||||
EVoiceResult GetVoice( bool bWantCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, bool bWantUncompressed_Deprecated, void *pUncompressedDestBuffer_Deprecated , uint32 cbUncompressedDestBufferSize_Deprecated , uint32 *nUncompressBytesWritten_Deprecated , uint32 nUncompressedVoiceDesiredSampleRate_Deprecated )
|
||||
{
|
||||
PRINT_DEBUG("GetVoice\n");
|
||||
if (!recording) return k_EVoiceResultNotRecording;
|
||||
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - last_get_voice).count();
|
||||
if (bWantCompressed) {
|
||||
uint32 towrite = seconds * 1024.0 * 64.0 / 8.0;
|
||||
if (cbDestBufferSize < towrite) towrite = cbDestBufferSize;
|
||||
if (pDestBuffer) memset(pDestBuffer, 0, towrite);
|
||||
if (nBytesWritten) *nBytesWritten = towrite;
|
||||
}
|
||||
|
||||
if (bWantUncompressed_Deprecated) {
|
||||
PRINT_DEBUG("Wanted Uncompressed\n");
|
||||
}
|
||||
|
||||
last_get_voice = std::chrono::high_resolution_clock::now();
|
||||
return k_EVoiceResultOK;
|
||||
}
|
||||
|
||||
EVoiceResult GetVoice( bool bWantCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, bool bWantUncompressed, void *pUncompressedDestBuffer, uint32 cbUncompressedDestBufferSize, uint32 *nUncompressBytesWritten )
|
||||
{
|
||||
PRINT_DEBUG("GetVoice old\n");
|
||||
return GetVoice(bWantCompressed, pDestBuffer, cbDestBufferSize, nBytesWritten, bWantUncompressed, pUncompressedDestBuffer, cbUncompressedDestBufferSize, nUncompressBytesWritten, 11025);
|
||||
}
|
||||
|
||||
EVoiceResult GetCompressedVoice( void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
|
||||
{
|
||||
PRINT_DEBUG("GetCompressedVoice\n");
|
||||
return GetVoice(true, pDestBuffer, cbDestBufferSize, nBytesWritten, false, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
// Decodes the compressed voice data returned by GetVoice. The output data is
|
||||
// raw single-channel 16-bit PCM audio. The decoder supports any sample rate
|
||||
// from 11025 to 48000; see GetVoiceOptimalSampleRate() below for details.
|
||||
// If the output buffer is not large enough, then *nBytesWritten will be set
|
||||
// to the required buffer size, and k_EVoiceResultBufferTooSmall is returned.
|
||||
// It is suggested to start with a 20kb buffer and reallocate as necessary.
|
||||
EVoiceResult DecompressVoice( const void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, uint32 nDesiredSampleRate )
|
||||
{
|
||||
PRINT_DEBUG("DecompressVoice\n");
|
||||
if (!recording) return k_EVoiceResultNotRecording;
|
||||
uint32 uncompressed = (double)cbCompressed * ((double)nDesiredSampleRate / 8192.0);
|
||||
if(nBytesWritten) *nBytesWritten = uncompressed;
|
||||
if (uncompressed > cbDestBufferSize) uncompressed = cbDestBufferSize;
|
||||
if (pDestBuffer) memset(pDestBuffer, 0, uncompressed);
|
||||
|
||||
return k_EVoiceResultOK;
|
||||
}
|
||||
|
||||
EVoiceResult DecompressVoice( const void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
|
||||
{
|
||||
PRINT_DEBUG("DecompressVoice old\n");
|
||||
return DecompressVoice(pCompressed, cbCompressed, pDestBuffer, cbDestBufferSize, nBytesWritten, 11025);
|
||||
}
|
||||
|
||||
EVoiceResult DecompressVoice( void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
|
||||
{
|
||||
PRINT_DEBUG("DecompressVoice older\n");
|
||||
return DecompressVoice(pCompressed, cbCompressed, pDestBuffer, cbDestBufferSize, nBytesWritten, 11025);
|
||||
}
|
||||
|
||||
// This returns the native sample rate of the Steam voice decompressor
|
||||
// this sample rate for DecompressVoice will perform the least CPU processing.
|
||||
// However, the final audio quality will depend on how well the audio device
|
||||
// (and/or your application's audio output SDK) deals with lower sample rates.
|
||||
// You may find that you get the best audio output quality when you ignore
|
||||
// this function and use the native sample rate of your audio output device,
|
||||
// which is usually 48000 or 44100.
|
||||
uint32 GetVoiceOptimalSampleRate()
|
||||
{
|
||||
PRINT_DEBUG("GetVoiceOptimalSampleRate\n");
|
||||
return 48000;
|
||||
}
|
||||
|
||||
// Retrieve ticket to be sent to the entity who wishes to authenticate you.
|
||||
// pcbTicket retrieves the length of the actual ticket.
|
||||
HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
|
||||
{
|
||||
PRINT_DEBUG("Steam_User::GetAuthSessionTicket %i\n", cbMaxTicket);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
return ticket_manager->getTicket(pTicket, cbMaxTicket, pcbTicket);
|
||||
}
|
||||
|
||||
// Authenticate ticket from entity steamID to be sure it is valid and isnt reused
|
||||
// Registers for callbacks if the entity goes offline or cancels the ticket ( see ValidateAuthTicketResponse_t callback and EAuthSessionResponse )
|
||||
EBeginAuthSessionResult BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_User::BeginAuthSession %i %llu\n", cbAuthTicket, steamID.ConvertToUint64());
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
return ticket_manager->beginAuth(pAuthTicket, cbAuthTicket, steamID);
|
||||
}
|
||||
|
||||
// Stop tracking started by BeginAuthSession - called when no longer playing game with this entity
|
||||
void EndAuthSession( CSteamID steamID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_User::EndAuthSession\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
ticket_manager->endAuth(steamID);
|
||||
}
|
||||
|
||||
// Cancel auth ticket from GetAuthSessionTicket, called when no longer playing game with the entity you gave the ticket to
|
||||
void CancelAuthTicket( HAuthTicket hAuthTicket )
|
||||
{
|
||||
PRINT_DEBUG("Steam_User::CancelAuthTicket\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
ticket_manager->cancelTicket(hAuthTicket);
|
||||
}
|
||||
|
||||
// After receiving a user's authentication data, and passing it to BeginAuthSession, use this function
|
||||
// to determine if the user owns downloadable content specified by the provided AppID.
|
||||
EUserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_User::UserHasLicenseForApp\n");
|
||||
return k_EUserHasLicenseResultHasLicense;
|
||||
}
|
||||
|
||||
// returns true if this users looks like they are behind a NAT device. Only valid once the user has connected to steam
|
||||
// (i.e a SteamServersConnected_t has been issued) and may not catch all forms of NAT.
|
||||
bool BIsBehindNAT()
|
||||
{
|
||||
PRINT_DEBUG("BIsBehindNAT\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// set data to be replicated to friends so that they can join your game
|
||||
// CSteamID steamIDGameServer - the steamID of the game server, received from the game server by the client
|
||||
// uint32 unIPServer, uint16 usPortServer - the IP address of the game server
|
||||
void AdvertiseGame( CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer )
|
||||
{
|
||||
PRINT_DEBUG("AdvertiseGame\n");
|
||||
}
|
||||
|
||||
// Requests a ticket encrypted with an app specific shared key
|
||||
// pDataToInclude, cbDataToInclude will be encrypted into the ticket
|
||||
// ( This is asynchronous, you must wait for the ticket to be completed by the server )
|
||||
STEAM_CALL_RESULT( EncryptedAppTicketResponse_t )
|
||||
SteamAPICall_t RequestEncryptedAppTicket( void *pDataToInclude, int cbDataToInclude )
|
||||
{
|
||||
PRINT_DEBUG("Steam_User::RequestEncryptedAppTicket %i\n", cbDataToInclude);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
EncryptedAppTicketResponse_t data;
|
||||
data.m_eResult = k_EResultOK;
|
||||
|
||||
encrypted_app_ticket = std::string((char *)pDataToInclude, cbDataToInclude);
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
// retrieve a finished ticket
|
||||
bool GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
|
||||
{
|
||||
PRINT_DEBUG("Steam_User::GetEncryptedAppTicket %i\n", cbMaxTicket);
|
||||
if (!pcbTicket) return false;
|
||||
unsigned int ticket_size = encrypted_app_ticket.size() + 126;
|
||||
if (!cbMaxTicket) {
|
||||
*pcbTicket = ticket_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!pTicket) return false;
|
||||
|
||||
//TODO figure out exact sizes?
|
||||
if (ticket_size < cbMaxTicket) cbMaxTicket = ticket_size;
|
||||
char ticket_base[] = {0x08, 0x01};
|
||||
memset(pTicket, 'g', cbMaxTicket);
|
||||
memcpy(pTicket, ticket_base, sizeof(ticket_base));
|
||||
*pcbTicket = cbMaxTicket;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Trading Card badges data access
|
||||
// if you only have one set of cards, the series will be 1
|
||||
// the user has can have two different badges for a series; the regular (max level 5) and the foil (max level 1)
|
||||
int GetGameBadgeLevel( int nSeries, bool bFoil )
|
||||
{
|
||||
PRINT_DEBUG("GetGameBadgeLevel\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// gets the Steam Level of the user, as shown on their profile
|
||||
int GetPlayerSteamLevel()
|
||||
{
|
||||
PRINT_DEBUG("GetPlayerSteamLevel\n");
|
||||
return 100;
|
||||
}
|
||||
|
||||
// Requests a URL which authenticates an in-game browser for store check-out,
|
||||
// and then redirects to the specified URL. As long as the in-game browser
|
||||
// accepts and handles session cookies, Steam microtransaction checkout pages
|
||||
// will automatically recognize the user instead of presenting a login page.
|
||||
// The result of this API call will be a StoreAuthURLResponse_t callback.
|
||||
// NOTE: The URL has a very short lifetime to prevent history-snooping attacks,
|
||||
// so you should only call this API when you are about to launch the browser,
|
||||
// or else immediately navigate to the result URL using a hidden browser window.
|
||||
// NOTE 2: The resulting authorization cookie has an expiration time of one day,
|
||||
// so it would be a good idea to request and visit a new auth URL every 12 hours.
|
||||
STEAM_CALL_RESULT( StoreAuthURLResponse_t )
|
||||
SteamAPICall_t RequestStoreAuthURL( const char *pchRedirectURL )
|
||||
{
|
||||
PRINT_DEBUG("RequestStoreAuthURL\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// gets whether the users phone number is verified
|
||||
bool BIsPhoneVerified()
|
||||
{
|
||||
PRINT_DEBUG("BIsPhoneVerified\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// gets whether the user has two factor enabled on their account
|
||||
bool BIsTwoFactorEnabled()
|
||||
{
|
||||
PRINT_DEBUG("BIsTwoFactorEnabled\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// gets whether the users phone number is identifying
|
||||
bool BIsPhoneIdentifying()
|
||||
{
|
||||
PRINT_DEBUG("BIsPhoneIdentifying\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// gets whether the users phone number is awaiting (re)verification
|
||||
bool BIsPhoneRequiringVerification()
|
||||
{
|
||||
PRINT_DEBUG("BIsPhoneRequiringVerification\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( MarketEligibilityResponse_t )
|
||||
SteamAPICall_t GetMarketEligibility()
|
||||
{
|
||||
PRINT_DEBUG("GetMarketEligibility\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Retrieves anti indulgence / duration control for current user
|
||||
STEAM_CALL_RESULT( DurationControl_t )
|
||||
SteamAPICall_t GetDurationControl()
|
||||
{
|
||||
PRINT_DEBUG("GetDurationControl\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Advise steam china duration control system about the online state of the game.
|
||||
// This will prevent offline gameplay time from counting against a user's
|
||||
// playtime limits.
|
||||
bool BSetDurationControlOnlineState( EDurationControlOnlineState eNewState )
|
||||
{
|
||||
PRINT_DEBUG("BSetDurationControlOnlineState\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
1037
all/steam_user_stats.h
Normal file
1037
all/steam_user_stats.h
Normal file
File diff suppressed because it is too large
Load diff
444
all/steam_utils.h
Normal file
444
all/steam_utils.h
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
#include "local_storage.h"
|
||||
#include "../overlay_experimental/steam_overlay.h"
|
||||
|
||||
static std::chrono::time_point<std::chrono::steady_clock> app_initialized_time = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
class Steam_Utils :
|
||||
public ISteamUtils002,
|
||||
public ISteamUtils003,
|
||||
public ISteamUtils004,
|
||||
public ISteamUtils005,
|
||||
public ISteamUtils006,
|
||||
public ISteamUtils007,
|
||||
public ISteamUtils008,
|
||||
public ISteamUtils009,
|
||||
public ISteamUtils
|
||||
{
|
||||
private:
|
||||
Settings *settings;
|
||||
class SteamCallResults *callback_results;
|
||||
Steam_Overlay* overlay;
|
||||
|
||||
public:
|
||||
Steam_Utils(Settings *settings, class SteamCallResults *callback_results, Steam_Overlay *overlay):
|
||||
settings(settings),
|
||||
callback_results(callback_results),
|
||||
overlay(overlay)
|
||||
{}
|
||||
|
||||
// return the number of seconds since the user
|
||||
uint32 GetSecondsSinceAppActive()
|
||||
{
|
||||
PRINT_DEBUG("GetSecondsSinceAppActive\n");
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - app_initialized_time).count();
|
||||
}
|
||||
|
||||
uint32 GetSecondsSinceComputerActive()
|
||||
{
|
||||
PRINT_DEBUG("GetSecondsSinceComputerActive\n");
|
||||
return GetSecondsSinceAppActive() + 2000;
|
||||
}
|
||||
|
||||
|
||||
// the universe this client is connecting to
|
||||
EUniverse GetConnectedUniverse()
|
||||
{
|
||||
PRINT_DEBUG("GetConnectedUniverse\n");
|
||||
return k_EUniversePublic;
|
||||
}
|
||||
|
||||
|
||||
// Steam server time. Number of seconds since January 1, 1970, GMT (i.e unix time)
|
||||
uint32 GetServerRealTime()
|
||||
{
|
||||
PRINT_DEBUG("GetServerRealTime\n");
|
||||
uint32 server_time = std::chrono::duration_cast<std::chrono::duration<uint32>>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
PRINT_DEBUG("Time %lu\n", server_time);
|
||||
return server_time;
|
||||
}
|
||||
|
||||
|
||||
// returns the 2 digit ISO 3166-1-alpha-2 format country code this client is running in (as looked up via an IP-to-location database)
|
||||
// e.g "US" or "UK".
|
||||
const char *GetIPCountry()
|
||||
{
|
||||
PRINT_DEBUG("GetIPCountry\n");
|
||||
return "US";
|
||||
}
|
||||
|
||||
// returns true if the image exists, and valid sizes were filled out
|
||||
bool GetImageSize( int iImage, uint32 *pnWidth, uint32 *pnHeight )
|
||||
{
|
||||
PRINT_DEBUG("GetImageSize %i\n", iImage);
|
||||
if (!iImage || !pnWidth || !pnHeight) return false;
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
auto image = settings->images.find(iImage);
|
||||
if (image == settings->images.end()) return false;
|
||||
|
||||
*pnWidth = image->second.width;
|
||||
*pnHeight = image->second.height;
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true if the image exists, and the buffer was successfully filled out
|
||||
// results are returned in RGBA format
|
||||
// the destination buffer size should be 4 * height * width * sizeof(char)
|
||||
bool GetImageRGBA( int iImage, uint8 *pubDest, int nDestBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("GetImageRGBA %i\n", iImage);
|
||||
if (!iImage || !pubDest || !nDestBufferSize) return false;
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
auto image = settings->images.find(iImage);
|
||||
if (image == settings->images.end()) return false;
|
||||
|
||||
unsigned size = image->second.data.size();
|
||||
if (nDestBufferSize < size) size = nDestBufferSize;
|
||||
image->second.data.copy((char *)pubDest, nDestBufferSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns the IP of the reporting server for valve - currently only used in Source engine games
|
||||
bool GetCSERIPPort( uint32 *unIP, uint16 *usPort )
|
||||
{
|
||||
PRINT_DEBUG("GetCSERIPPort\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// return the amount of battery power left in the current system in % [0..100], 255 for being on AC power
|
||||
uint8 GetCurrentBatteryPower()
|
||||
{
|
||||
PRINT_DEBUG("GetCurrentBatteryPower\n");
|
||||
return 255;
|
||||
}
|
||||
|
||||
|
||||
// returns the appID of the current process
|
||||
uint32 GetAppID()
|
||||
{
|
||||
PRINT_DEBUG("GetAppID\n");
|
||||
return settings->get_local_game_id().AppID();
|
||||
}
|
||||
|
||||
|
||||
// Sets the position where the overlay instance for the currently calling game should show notifications.
|
||||
// This position is per-game and if this function is called from outside of a game context it will do nothing.
|
||||
void SetOverlayNotificationPosition( ENotificationPosition eNotificationPosition )
|
||||
{
|
||||
PRINT_DEBUG("SetOverlayNotificationPosition\n");
|
||||
overlay->SetNotificationPosition(eNotificationPosition);
|
||||
}
|
||||
|
||||
|
||||
// API asynchronous call results
|
||||
// can be used directly, but more commonly used via the callback dispatch API (see steam_api.h)
|
||||
bool IsAPICallCompleted( SteamAPICall_t hSteamAPICall, bool *pbFailed )
|
||||
{
|
||||
PRINT_DEBUG("IsAPICallCompleted: %llu\n", hSteamAPICall);
|
||||
if (hSteamAPICall == 1) { //bug ? soul calibur 6 calls this function with the return value 1 of Steam_User_Stats::RequestCurrentStats and expects this function to return true
|
||||
if (pbFailed) *pbFailed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (!callback_results->exists(hSteamAPICall)) return false;
|
||||
if (pbFailed) *pbFailed = false;
|
||||
return true; //all api calls "complete" right away
|
||||
}
|
||||
|
||||
ESteamAPICallFailure GetAPICallFailureReason( SteamAPICall_t hSteamAPICall )
|
||||
{
|
||||
PRINT_DEBUG("GetAPICallFailureReason\n");
|
||||
return k_ESteamAPICallFailureNone;
|
||||
}
|
||||
|
||||
bool GetAPICallResult( SteamAPICall_t hSteamAPICall, void *pCallback, int cubCallback, int iCallbackExpected, bool *pbFailed )
|
||||
{
|
||||
PRINT_DEBUG("GetAPICallResult %llu %i %i %p\n", hSteamAPICall, cubCallback, iCallbackExpected, pbFailed);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (callback_results->callback_result(hSteamAPICall, pCallback, cubCallback)) {
|
||||
if (pbFailed) *pbFailed = false;
|
||||
PRINT_DEBUG("GetAPICallResult Succeeded\n");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Deprecated. Applications should use SteamAPI_RunCallbacks() instead. Game servers do not need to call this function.
|
||||
STEAM_PRIVATE_API( void RunFrame()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Utils::RunFrame\n");
|
||||
}
|
||||
)
|
||||
|
||||
// returns the number of IPC calls made since the last time this function was called
|
||||
// Used for perf debugging so you can understand how many IPC calls your game makes per frame
|
||||
// Every IPC call is at minimum a thread context switch if not a process one so you want to rate
|
||||
// control how often you do them.
|
||||
uint32 GetIPCCallCount()
|
||||
{
|
||||
PRINT_DEBUG("GetIPCCallCount\n");
|
||||
static int i = 0;
|
||||
i += 123;
|
||||
return i; //TODO
|
||||
}
|
||||
|
||||
|
||||
// API warning handling
|
||||
// 'int' is the severity; 0 for msg, 1 for warning
|
||||
// 'const char *' is the text of the message
|
||||
// callbacks will occur directly after the API function is called that generated the warning or message
|
||||
void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Utils::SetWarningMessageHook\n");
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the overlay is running & the user can access it. The overlay process could take a few seconds to
|
||||
// start & hook the game process, so this function will initially return false while the overlay is loading.
|
||||
bool IsOverlayEnabled()
|
||||
{
|
||||
PRINT_DEBUG("IsOverlayEnabled\n");
|
||||
return overlay->Ready();
|
||||
}
|
||||
|
||||
|
||||
// Normally this call is unneeded if your game has a constantly running frame loop that calls the
|
||||
// D3D Present API, or OGL SwapBuffers API every frame.
|
||||
//
|
||||
// However, if you have a game that only refreshes the screen on an event driven basis then that can break
|
||||
// the overlay, as it uses your Present/SwapBuffers calls to drive it's internal frame loop and it may also
|
||||
// need to Present() to the screen any time an even needing a notification happens or when the overlay is
|
||||
// brought up over the game by a user. You can use this API to ask the overlay if it currently need a present
|
||||
// in that case, and then you can check for this periodically (roughly 33hz is desirable) and make sure you
|
||||
// refresh the screen with Present or SwapBuffers to allow the overlay to do it's work.
|
||||
bool BOverlayNeedsPresent()
|
||||
{
|
||||
PRINT_DEBUG("BOverlayNeedsPresent\n");
|
||||
return overlay->NeedPresent();
|
||||
}
|
||||
|
||||
|
||||
// Asynchronous call to check if an executable file has been signed using the public key set on the signing tab
|
||||
// of the partner site, for example to refuse to load modified executable files.
|
||||
// The result is returned in CheckFileSignature_t.
|
||||
// k_ECheckFileSignatureNoSignaturesFoundForThisApp - This app has not been configured on the signing tab of the partner site to enable this function.
|
||||
// k_ECheckFileSignatureNoSignaturesFoundForThisFile - This file is not listed on the signing tab for the partner site.
|
||||
// k_ECheckFileSignatureFileNotFound - The file does not exist on disk.
|
||||
// k_ECheckFileSignatureInvalidSignature - The file exists, and the signing tab has been set for this file, but the file is either not signed or the signature does not match.
|
||||
// k_ECheckFileSignatureValidSignature - The file is signed and the signature is valid.
|
||||
STEAM_CALL_RESULT( CheckFileSignature_t )
|
||||
SteamAPICall_t CheckFileSignature( const char *szFileName )
|
||||
{
|
||||
PRINT_DEBUG("CheckFileSignature\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
CheckFileSignature_t data;
|
||||
data.m_eCheckFileSignature = k_ECheckFileSignatureValidSignature;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
|
||||
// Activates the Big Picture text input dialog which only supports gamepad input
|
||||
bool ShowGamepadTextInput( EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32 unCharMax, const char *pchExistingText )
|
||||
{
|
||||
PRINT_DEBUG("ShowGamepadTextInput\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShowGamepadTextInput( EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32 unCharMax )
|
||||
{
|
||||
PRINT_DEBUG("ShowGamepadTextInput old\n");
|
||||
return ShowGamepadTextInput(eInputMode, eLineInputMode, pchDescription, unCharMax, NULL);
|
||||
}
|
||||
|
||||
// Returns previously entered text & length
|
||||
uint32 GetEnteredGamepadTextLength()
|
||||
{
|
||||
PRINT_DEBUG("GetEnteredGamepadTextLength\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GetEnteredGamepadTextInput( char *pchText, uint32 cchText )
|
||||
{
|
||||
PRINT_DEBUG("GetEnteredGamepadTextInput\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// returns the language the steam client is running in, you probably want ISteamApps::GetCurrentGameLanguage instead, this is for very special usage cases
|
||||
const char *GetSteamUILanguage()
|
||||
{
|
||||
PRINT_DEBUG("GetSteamUILanguage\n");
|
||||
return settings->get_language();
|
||||
}
|
||||
|
||||
|
||||
// returns true if Steam itself is running in VR mode
|
||||
bool IsSteamRunningInVR()
|
||||
{
|
||||
PRINT_DEBUG("IsSteamRunningInVR\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Sets the inset of the overlay notification from the corner specified by SetOverlayNotificationPosition.
|
||||
void SetOverlayNotificationInset( int nHorizontalInset, int nVerticalInset )
|
||||
{
|
||||
PRINT_DEBUG("SetOverlayNotificationInset\n");
|
||||
overlay->SetNotificationInset(nHorizontalInset, nVerticalInset);
|
||||
}
|
||||
|
||||
|
||||
// returns true if Steam & the Steam Overlay are running in Big Picture mode
|
||||
// Games much be launched through the Steam client to enable the Big Picture overlay. During development,
|
||||
// a game can be added as a non-steam game to the developers library to test this feature
|
||||
bool IsSteamInBigPictureMode()
|
||||
{
|
||||
PRINT_DEBUG("IsSteamInBigPictureMode\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// ask SteamUI to create and render its OpenVR dashboard
|
||||
void StartVRDashboard()
|
||||
{
|
||||
PRINT_DEBUG("StartVRDashboard\n");
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the HMD content will be streamed via Steam In-Home Streaming
|
||||
bool IsVRHeadsetStreamingEnabled()
|
||||
{
|
||||
PRINT_DEBUG("IsVRHeadsetStreamingEnabled\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Set whether the HMD content will be streamed via Steam In-Home Streaming
|
||||
// If this is set to true, then the scene in the HMD headset will be streamed, and remote input will not be allowed.
|
||||
// If this is set to false, then the application window will be streamed instead, and remote input will be allowed.
|
||||
// The default is true unless "VRHeadsetStreaming" "0" is in the extended appinfo for a game.
|
||||
// (this is useful for games that have asymmetric multiplayer gameplay)
|
||||
void SetVRHeadsetStreamingEnabled( bool bEnabled )
|
||||
{
|
||||
PRINT_DEBUG("SetVRHeadsetStreamingEnabled\n");
|
||||
}
|
||||
|
||||
// Returns whether this steam client is a Steam China specific client, vs the global client.
|
||||
bool IsSteamChinaLauncher()
|
||||
{
|
||||
PRINT_DEBUG("IsSteamChinaLauncher\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initializes text filtering.
|
||||
// Returns false if filtering is unavailable for the language the user is currently running in.
|
||||
bool InitFilterText()
|
||||
{
|
||||
PRINT_DEBUG("InitFilterText old\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initializes text filtering.
|
||||
// unFilterOptions are reserved for future use and should be set to 0
|
||||
// Returns false if filtering is unavailable for the language the user is currently running in.
|
||||
bool InitFilterText( uint32 unFilterOptions )
|
||||
{
|
||||
PRINT_DEBUG("InitFilterText\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filters the provided input message and places the filtered result into pchOutFilteredText.
|
||||
// pchOutFilteredText is where the output will be placed, even if no filtering or censoring is performed
|
||||
// nByteSizeOutFilteredText is the size (in bytes) of pchOutFilteredText
|
||||
// pchInputText is the input string that should be filtered, which can be ASCII or UTF-8
|
||||
// bLegalOnly should be false if you want profanity and legally required filtering (where required) and true if you want legally required filtering only
|
||||
// Returns the number of characters (not bytes) filtered.
|
||||
int FilterText( char* pchOutFilteredText, uint32 nByteSizeOutFilteredText, const char * pchInputMessage, bool bLegalOnly )
|
||||
{
|
||||
PRINT_DEBUG("FilterText old\n");
|
||||
return FilterText(k_ETextFilteringContextUnknown, CSteamID(), pchInputMessage, pchOutFilteredText, nByteSizeOutFilteredText );
|
||||
}
|
||||
|
||||
// Filters the provided input message and places the filtered result into pchOutFilteredText, using legally required filtering and additional filtering based on the context and user settings
|
||||
// eContext is the type of content in the input string
|
||||
// sourceSteamID is the Steam ID that is the source of the input string (e.g. the player with the name, or who said the chat text)
|
||||
// pchInputText is the input string that should be filtered, which can be ASCII or UTF-8
|
||||
// pchOutFilteredText is where the output will be placed, even if no filtering is performed
|
||||
// nByteSizeOutFilteredText is the size (in bytes) of pchOutFilteredText, should be at least strlen(pchInputText)+1
|
||||
// Returns the number of characters (not bytes) filtered
|
||||
int FilterText( ETextFilteringContext eContext, CSteamID sourceSteamID, const char *pchInputMessage, char *pchOutFilteredText, uint32 nByteSizeOutFilteredText )
|
||||
{
|
||||
PRINT_DEBUG("FilterText\n");
|
||||
if (!nByteSizeOutFilteredText) return 0;
|
||||
unsigned len = strlen(pchInputMessage);
|
||||
if (!len) return 0;
|
||||
len += 1;
|
||||
if (len > nByteSizeOutFilteredText) len = nByteSizeOutFilteredText;
|
||||
len -= 1;
|
||||
|
||||
memcpy(pchOutFilteredText, pchInputMessage, len);
|
||||
pchOutFilteredText[len] = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
// Return what we believe your current ipv6 connectivity to "the internet" is on the specified protocol.
|
||||
// This does NOT tell you if the Steam client is currently connected to Steam via ipv6.
|
||||
ESteamIPv6ConnectivityState GetIPv6ConnectivityState( ESteamIPv6ConnectivityProtocol eProtocol )
|
||||
{
|
||||
PRINT_DEBUG("GetIPv6ConnectivityState\n");
|
||||
return k_ESteamIPv6ConnectivityState_Unknown;
|
||||
}
|
||||
|
||||
// returns true if currently running on the Steam Deck device
|
||||
bool IsSteamRunningOnSteamDeck()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Opens a floating keyboard over the game content and sends OS keyboard keys directly to the game.
|
||||
// The text field position is specified in pixels relative the origin of the game window and is used to position the floating keyboard in a way that doesn't cover the text field
|
||||
bool ShowFloatingGamepadTextInput( EFloatingGamepadTextInputMode eKeyboardMode, int nTextFieldXPosition, int nTextFieldYPosition, int nTextFieldWidth, int nTextFieldHeight )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// In game launchers that don't have controller support you can call this to have Steam Input translate the controller input into mouse/kb to navigate the launcher
|
||||
void SetGameLauncherMode( bool bLauncherMode )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
bool DismissFloatingGamepadTextInput()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
46
all/steam_video.cpp
Normal file
46
all/steam_video.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "steam_video.h"
|
||||
|
||||
// Get a URL suitable for streaming the given Video app ID's video
|
||||
void Steam_Video::GetVideoURL( AppId_t unVideoAppID )
|
||||
{
|
||||
PRINT_DEBUG("GetVideoURL\n");
|
||||
}
|
||||
|
||||
|
||||
// returns true if user is uploading a live broadcast
|
||||
bool Steam_Video::IsBroadcasting( int *pnNumViewers )
|
||||
{
|
||||
PRINT_DEBUG("IsBroadcasting\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Get the OPF Details for 360 Video Playback
|
||||
STEAM_CALL_BACK( GetOPFSettingsResult_t )
|
||||
void Steam_Video::GetOPFSettings( AppId_t unVideoAppID )
|
||||
{
|
||||
PRINT_DEBUG("GetOPFSettings\n");
|
||||
}
|
||||
|
||||
bool Steam_Video::GetOPFStringForApp( AppId_t unVideoAppID, char *pchBuffer, int32 *pnBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("GetOPFStringForApp\n");
|
||||
return false;
|
||||
}
|
||||
34
all/steam_video.h
Normal file
34
all/steam_video.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Video : public ISteamVideo
|
||||
{
|
||||
public:
|
||||
|
||||
// Get a URL suitable for streaming the given Video app ID's video
|
||||
void GetVideoURL( AppId_t unVideoAppID );
|
||||
|
||||
// returns true if user is uploading a live broadcast
|
||||
bool IsBroadcasting( int *pnNumViewers );
|
||||
|
||||
// Get the OPF Details for 360 Video Playback
|
||||
STEAM_CALL_BACK( GetOPFSettingsResult_t )
|
||||
void GetOPFSettings( AppId_t unVideoAppID );
|
||||
bool GetOPFStringForApp( AppId_t unVideoAppID, char *pchBuffer, int32 *pnBufferSize );
|
||||
};
|
||||
566
all/wrap.cpp
Normal file
566
all/wrap.cpp
Normal file
|
|
@ -0,0 +1,566 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#if defined(WIN64) || defined(_WIN64) || defined(__MINGW64__)
|
||||
#define __WINDOWS_64__
|
||||
#elif defined(WIN32) || defined(_WIN32) || defined(__MINGW32__)
|
||||
#define __WINDOWS_32__
|
||||
#endif
|
||||
|
||||
#if defined(__WINDOWS_32__) || defined(__WINDOWS_64__)
|
||||
// Nothing to be done here
|
||||
#else
|
||||
#define STEAM_API_FUNCTIONS_IMPL
|
||||
#include "base.h"
|
||||
#include "dll.h"
|
||||
|
||||
#define PATH_SEPARATOR_CHAR '/'
|
||||
#define STEAM_PATH_CACHE_SIZE 4096
|
||||
|
||||
const char *STEAM_PATH;
|
||||
size_t STEAM_PATH_SIZE;
|
||||
|
||||
// Returns a '/' terminated absolute path to the steam folder in user's home,
|
||||
// root is returned if env home is not set
|
||||
const char *get_steam_path()
|
||||
{
|
||||
char *home_path = getenv("HOME");
|
||||
char steam_path[STEAM_PATH_CACHE_SIZE];
|
||||
char *steam_realpath = nullptr;
|
||||
|
||||
// Build steam_path from home
|
||||
int required_size = snprintf(steam_path, STEAM_PATH_CACHE_SIZE, "%s/.steam/steam", home_path);
|
||||
|
||||
// Allocate more space for steam_path if needed (required_size does not count terminator)
|
||||
if (required_size > 0 && required_size >= STEAM_PATH_CACHE_SIZE) {
|
||||
char *large_steam_path = (char *)malloc(sizeof(char) * (required_size + 1));
|
||||
int check_size = snprintf(large_steam_path, required_size + 1, "%s/.steam/steam", home_path);
|
||||
// Check that path fits this time
|
||||
if (check_size == required_size) {
|
||||
steam_realpath = realpath(large_steam_path, nullptr);
|
||||
}
|
||||
free(large_steam_path);
|
||||
} else {
|
||||
steam_realpath = realpath(steam_path, nullptr);
|
||||
}
|
||||
|
||||
// Terminate path with a file separator
|
||||
if (steam_realpath && *steam_realpath) {
|
||||
size_t path_size = strlen(steam_realpath);
|
||||
if (steam_realpath[path_size - 1] != PATH_SEPARATOR_CHAR) {
|
||||
steam_realpath = (char *)realloc(steam_realpath, path_size + 2);
|
||||
steam_realpath[path_size] = PATH_SEPARATOR_CHAR;
|
||||
steam_realpath[path_size + 1] = 0;
|
||||
}
|
||||
} else {
|
||||
// Failsafe to root
|
||||
steam_realpath = strdup("/");
|
||||
}
|
||||
|
||||
return steam_realpath;
|
||||
}
|
||||
|
||||
// Fixes given path by navigating filesystem and lowering case to match
|
||||
// existing entries on disk
|
||||
bool match_path(char *path, int start, bool accept_same_case)
|
||||
{
|
||||
if (!path[start + 1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Snap to the next separator in path
|
||||
int separator = start + 1;
|
||||
while (path[separator] != PATH_SEPARATOR_CHAR && path[separator]) {
|
||||
separator++;
|
||||
}
|
||||
|
||||
bool is_last_component = path[separator] != PATH_SEPARATOR_CHAR;
|
||||
|
||||
char stored_char = path[separator];
|
||||
path[separator] = 0;
|
||||
bool path_accessible = access(path, F_OK) == 0;
|
||||
path[separator] = stored_char;
|
||||
|
||||
if (!path_accessible || (!is_last_component && !match_path(path, separator, accept_same_case))) {
|
||||
DIR *current_directory = nullptr;
|
||||
int component = start + 1;
|
||||
|
||||
if (start) {
|
||||
stored_char = path[start];
|
||||
path[start] = 0;
|
||||
current_directory = opendir(path);
|
||||
path[start] = stored_char;
|
||||
component = start + 1;
|
||||
} else {
|
||||
if (*path == PATH_SEPARATOR_CHAR) {
|
||||
component = start + 1;
|
||||
current_directory = opendir("/");
|
||||
} else {
|
||||
component = start;
|
||||
current_directory = opendir(".");
|
||||
}
|
||||
}
|
||||
|
||||
// 0123456789012345678901234567890123456789
|
||||
// path = /this/is/a/sample/path/to/file.txt
|
||||
// ^^ ^
|
||||
// ab c
|
||||
// a. start = 10
|
||||
// b. component = 11
|
||||
// c. separator = 17
|
||||
// current_directory = /this/is/a/
|
||||
|
||||
if (current_directory) {
|
||||
dirent64 *entry = (dirent64 *)readdir64(current_directory);
|
||||
while (entry) {
|
||||
const char *entry_name = entry->d_name;
|
||||
stored_char = path[separator];
|
||||
path[separator] = 0;
|
||||
|
||||
// Fix current component if entry with similar name exists
|
||||
if (!strcasecmp(&path[component], entry_name)) {
|
||||
bool case_differs = strcmp(&path[component], entry_name) != 0;
|
||||
path[separator] = stored_char;
|
||||
if (case_differs) {
|
||||
char *iterator = &path[component];
|
||||
// Replace with entry name
|
||||
while (*entry_name != PATH_SEPARATOR_CHAR && *entry_name) {
|
||||
*(iterator++) = *(entry_name++);
|
||||
}
|
||||
// Fix next component
|
||||
if (is_last_component || match_path(path, separator, accept_same_case)) {
|
||||
closedir(current_directory);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
path[separator] = stored_char;
|
||||
}
|
||||
entry = (dirent64 *)readdir64(current_directory);
|
||||
}
|
||||
}
|
||||
|
||||
if (current_directory) {
|
||||
closedir(current_directory);
|
||||
}
|
||||
|
||||
return accept_same_case && is_last_component;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Tries to convert the given path to the preferred lower-cased version
|
||||
const char *lowercase_path(const char *path, bool accept_same_case, bool stop_at_separator)
|
||||
{
|
||||
std::locale loc;
|
||||
char *path_lowercased = nullptr;
|
||||
|
||||
if (path && *path) {
|
||||
// If file does not exist
|
||||
if (access(path, F_OK)) {
|
||||
// Make a copy of the path on which to work on
|
||||
path_lowercased = strdup(path);
|
||||
if (!path_lowercased) {
|
||||
return nullptr;
|
||||
}
|
||||
// Load steam path if not done already
|
||||
if (!STEAM_PATH) {
|
||||
STEAM_PATH = get_steam_path();
|
||||
STEAM_PATH_SIZE = strlen(STEAM_PATH);
|
||||
}
|
||||
|
||||
char *lowercase_iterator = path_lowercased;
|
||||
// Lowercase whole steam path if possible
|
||||
bool has_steam_root = false;
|
||||
if (!strncasecmp(path_lowercased, STEAM_PATH, STEAM_PATH_SIZE)) {
|
||||
memcpy(path_lowercased, STEAM_PATH, STEAM_PATH_SIZE);
|
||||
lowercase_iterator = &path_lowercased[STEAM_PATH_SIZE - 1];
|
||||
has_steam_root = true;
|
||||
}
|
||||
// Lowercase rest of the path
|
||||
char *iterator = lowercase_iterator;
|
||||
while ((!stop_at_separator || *iterator != PATH_SEPARATOR_CHAR) && *iterator) {
|
||||
*iterator = std::tolower(*iterator, loc);
|
||||
iterator++;
|
||||
}
|
||||
|
||||
// Check if we can access the lowered-case path
|
||||
int error = access(path_lowercased, F_OK);
|
||||
if (!error) {
|
||||
// The new path is valid
|
||||
return path_lowercased;
|
||||
} else {
|
||||
if (accept_same_case) {
|
||||
const char *name_iterator = &path[lowercase_iterator - path_lowercased];
|
||||
while (*lowercase_iterator) {
|
||||
*(lowercase_iterator++) = *(name_iterator++);
|
||||
}
|
||||
}
|
||||
// Retry accesing the file again and tweak the path if needed
|
||||
if (match_path(path_lowercased, has_steam_root? STEAM_PATH_SIZE - 1 : 0, accept_same_case)) {
|
||||
return path_lowercased;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
STEAMAPI_API FILE *__wrap_freopen(const char *path, const char *modes, FILE *stream)
|
||||
{
|
||||
bool is_writable = strpbrk(modes, "wa+") != 0;
|
||||
const char *path_lowercased = lowercase_path(path, is_writable, true);
|
||||
FILE *result = freopen(path_lowercased, modes, stream);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API FILE *__wrap_fopen(const char *path, const char *modes)
|
||||
{
|
||||
bool is_writable = strpbrk(modes, "wa+") != 0;
|
||||
const char *path_lowercased = lowercase_path(path, is_writable, true);
|
||||
FILE *result = fopen(path_lowercased, modes);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API FILE *__wrap_fopen64(const char *path, const char *modes)
|
||||
{
|
||||
bool is_writable = strpbrk(modes, "wa+") != 0;
|
||||
const char *path_lowercased = lowercase_path(path, is_writable, true);
|
||||
FILE *result = fopen64(path_lowercased, modes);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_open(const char *path, int flags, mode_t mode)
|
||||
{
|
||||
bool is_writable = flags & (X_OK | W_OK);
|
||||
const char *path_lowercased = lowercase_path(path, is_writable, true);
|
||||
int result = open(path_lowercased, flags, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_open64(const char *path, int flags, mode_t mode)
|
||||
{
|
||||
bool is_writable = flags & (X_OK | W_OK);
|
||||
const char *path_lowercased = lowercase_path(path, is_writable, true);
|
||||
int result = open64(path_lowercased, flags, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_access(const char *path, int mode)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = access(path_lowercased, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap___xstat(int ver, const char * path, struct stat * stat_buf)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = __xstat(ver, path_lowercased, stat_buf);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_stat(const char * path, struct stat * stat_buf)
|
||||
{
|
||||
return __wrap___xstat(3, path, stat_buf);
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap___lxstat(int ver, const char * path, struct stat * stat_buf)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = __lxstat(ver, path_lowercased, stat_buf);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_lstat(const char * path, struct stat * stat_buf)
|
||||
{
|
||||
return __wrap___lxstat(3, path, stat_buf);
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_scandir(const char *path, struct dirent ***namelist, int (*sel)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **))
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = scandir(path_lowercased, namelist, sel, compar);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_scandir64(const char *path, struct dirent64 ***namelist, int (*sel)(const struct dirent64 *), int (*compar)(const struct dirent64 **, const struct dirent64 **))
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = scandir64(path_lowercased, namelist, sel, compar);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API DIR *__wrap_opendir(const char *path)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
DIR *result = opendir(path_lowercased);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap___xstat64(int ver, const char *path, struct stat64 *stat_buf)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = __xstat64(ver, path_lowercased, stat_buf);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap___lxstat64(int ver, const char *path, struct stat64 *stat_buf)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = __lxstat64(ver, path_lowercased, stat_buf);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_statvfs(const char *path, struct statvfs *buf)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = statvfs(path_lowercased, buf);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_statvfs64(const char *path, struct statvfs64 *buf)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = statvfs64(path_lowercased, buf);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_chmod(const char *path, mode_t mode)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = chmod(path_lowercased, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_chown(const char *path, uid_t owner, gid_t group)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = chown(path_lowercased, owner, group);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_lchown(const char *path, uid_t owner, gid_t group)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = lchown(path_lowercased, owner, group);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_symlink(const char *path1, const char *path2)
|
||||
{
|
||||
const char *path_lowercased1 = lowercase_path(path1, true, true);
|
||||
const char *path_lowercased2 = lowercase_path(path2, false, false);
|
||||
int result = symlink(path_lowercased1, path_lowercased2);
|
||||
if (path_lowercased1 != path1) {
|
||||
free((void *)path_lowercased1);
|
||||
}
|
||||
if (path_lowercased2 != path2) {
|
||||
free((void *)path_lowercased2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_link(const char *path1, const char *path2)
|
||||
{
|
||||
const char *path_lowercased1 = lowercase_path(path1, true, true);
|
||||
const char *path_lowercased2 = lowercase_path(path2, false, false);
|
||||
int result = link(path_lowercased1, path_lowercased2);
|
||||
if (path_lowercased1 != path1) {
|
||||
free((void *)path_lowercased1);
|
||||
}
|
||||
if (path_lowercased2 != path2) {
|
||||
free((void *)path_lowercased2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_mknod(const char *path, mode_t mode, dev_t dev)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, true, true);
|
||||
int result = __xmknod(1, path_lowercased, mode, &dev);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data)
|
||||
{
|
||||
const char *source_lowercased = lowercase_path(source, false, false);
|
||||
const char *target_lowercased = lowercase_path(target, false, false);
|
||||
int result = mount(source_lowercased, target_lowercased, filesystemtype, mountflags, data);
|
||||
if (source_lowercased != source) {
|
||||
free((void *)source_lowercased);
|
||||
}
|
||||
if (target_lowercased != target) {
|
||||
free((void *)target_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_unlink(const char *path)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = unlink(path);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_mkfifo(const char *path, mode_t mode)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, true, true);
|
||||
int result = mkfifo(path, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_rename(const char *old_name, const char *new_name)
|
||||
{
|
||||
const char *old_name_lowercased = lowercase_path(old_name, true, true);
|
||||
const char *new_name_lowercased = lowercase_path(new_name, false, false);
|
||||
int result = rename(old_name_lowercased, new_name_lowercased);
|
||||
if (old_name_lowercased != old_name) {
|
||||
free((void *)old_name_lowercased);
|
||||
}
|
||||
if (new_name_lowercased != new_name) {
|
||||
free((void *)new_name_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_utime(const char *path, const struct utimbuf *times)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = utime(path_lowercased, times);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_utimes(const char *path, const struct timeval times[2])
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = utimes(path_lowercased, times);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_mkdir(const char *path, mode_t mode)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, true, true);
|
||||
int result = mkdir(path_lowercased, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_rmdir(const char *path)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = rmdir(path_lowercased);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API void *__wrap_dlopen(const char *path, int mode)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
void * result = dlopen(path_lowercased, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API void *__wrap_dlmopen(Lmid_t lmid, const char *path, int flags)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
void * result = dlmopen(lmid, path_lowercased, flags);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue