separate .h/.cpp files

This commit is contained in:
otavepto 2024-05-03 01:29:57 +03:00
parent 1169822f70
commit e5d1a8bda7
81 changed files with 22736 additions and 17634 deletions

266
dll/appticket.cpp Normal file
View File

@ -0,0 +1,266 @@
/* 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 "dll/appticket.h"
void AppTicketV1::Reset()
{
TicketSize = 0;
TicketVersion = 0;
Unk2 = 0;
UserData.clear();
}
std::vector<uint8_t> AppTicketV1::Serialize() const
{
std::vector<uint8_t> buffer{};
uint8_t* pBuffer{};
buffer.resize(16 + UserData.size());
pBuffer = buffer.data();
*reinterpret_cast<uint32_t*>(pBuffer) = TicketSize; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = TicketVersion; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = UserData.size(); pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = Unk2; pBuffer += 4;
memcpy(pBuffer, UserData.data(), UserData.size());
return buffer;
}
bool AppTicketV1::Deserialize(const uint8_t* pBuffer, size_t size)
{
if (size < 16)
return false;
uint32_t user_data_size;
TicketSize = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
TicketVersion = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
user_data_size = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
if (size < (user_data_size + 16))
return false;
Unk2 = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
UserData.resize(user_data_size);
memcpy(UserData.data(), pBuffer, user_data_size);
return true;
}
void AppTicketV2::Reset()
{
TicketSize = 0;
TicketVersion = 0;
SteamID = 0;
AppID = 0;
Unk1 = 0;
Unk2 = 0;
TicketFlags = 0;
TicketIssueTime = 0;
TicketValidityEnd = 0;
}
std::vector<uint8_t> AppTicketV2::Serialize() const
{
std::vector<uint8_t> buffer{};
uint8_t* pBuffer{};
buffer.resize(40);
pBuffer = buffer.data();
*reinterpret_cast<uint32_t*>(pBuffer) = TicketSize; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = TicketVersion; pBuffer += 4;
*reinterpret_cast<uint64_t*>(pBuffer) = SteamID; pBuffer += 8;
*reinterpret_cast<uint32_t*>(pBuffer) = AppID; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = Unk1; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = Unk2; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = TicketFlags; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = TicketIssueTime; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = TicketValidityEnd;
return buffer;
}
bool AppTicketV2::Deserialize(const uint8_t* pBuffer, size_t size)
{
if (size < 40)
return false;
TicketSize = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
TicketVersion = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
SteamID = *reinterpret_cast<const uint64_t*>(pBuffer); pBuffer += 8;
AppID = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
Unk1 = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
Unk2 = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
TicketFlags = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
TicketIssueTime = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
TicketValidityEnd = *reinterpret_cast<const uint32_t*>(pBuffer);
return true;
}
void AppTicketV4::Reset()
{
AppIDs.clear();
HasVACStatus = false;
HasAppValue = false;
}
std::vector<uint8_t> AppTicketV4::Serialize()
{
std::vector<uint32_t> appids = AppIDs;
if (appids.size() == 0) {
appids.emplace_back(0);
}
uint16_t appid_count = static_cast<uint16_t>(appids.size() > 140 ? 140 : appids.size());
size_t buffer_size = static_cast<uint32_t>(appid_count) * 4ul + 2ul;
std::vector<uint8_t> buffer{};
uint8_t* pBuffer{};
if (HasAppValue) {// VACStatus + AppValue
buffer_size += 4;
if (!HasVACStatus) {
HasVACStatus = true;
VACStatus = 0;
}
}
if (HasVACStatus) {// VACStatus only
buffer_size += 4;
}
buffer.resize(buffer_size);
pBuffer = buffer.data();
*reinterpret_cast<uint16_t*>(pBuffer) = appid_count;
pBuffer += 2;
for (int i = 0; i < appid_count && i < 140; ++i) {
*reinterpret_cast<uint32_t*>(pBuffer) = appids[i];
pBuffer += 4;
}
if (HasVACStatus) {
*reinterpret_cast<uint32_t*>(pBuffer) = VACStatus;
pBuffer += 4;
}
if (HasAppValue) {
*reinterpret_cast<uint32_t*>(pBuffer) = AppValue;
}
return buffer;
}
bool AppTicketV4::Deserialize(const uint8_t* pBuffer, size_t size)
{
if (size < 2)
return false;
uint16_t appid_count = *reinterpret_cast<const uint16_t*>(pBuffer);
if (size < (appid_count * 4 + 2) || appid_count >= 140)
return false;
AppIDs.resize(appid_count);
pBuffer += 2;
size -= 2;
for (int i = 0; i < appid_count; ++i) {
AppIDs[i] = *reinterpret_cast<const uint32_t*>(pBuffer);
pBuffer += 4;
size -= 4;
}
HasVACStatus = false;
HasAppValue = false;
if (size < 4)
return true;
HasVACStatus = true;
VACStatus = *reinterpret_cast<const uint32_t*>(pBuffer);
pBuffer += 4;
size -= 4;
if (size < 4)
return true;
HasAppValue = true;
AppValue = *reinterpret_cast<const uint32_t*>(pBuffer);
return true;
}
bool DecryptedAppTicket::DeserializeTicket(const uint8_t* pBuffer, size_t buf_size)
{
if (!TicketV1.Deserialize(pBuffer, buf_size))
return false;
pBuffer += 16 + TicketV1.UserData.size();
buf_size -= 16 + TicketV1.UserData.size();
if (!TicketV2.Deserialize(pBuffer, buf_size))
return false;
if (TicketV2.TicketVersion > 2) {
pBuffer += 40;
buf_size -= 40;
if (!TicketV4.Deserialize(pBuffer, buf_size))
return false;
}
return true;
}
std::vector<uint8_t> DecryptedAppTicket::SerializeTicket()
{
std::vector<uint8_t> buffer{};
TicketV1.TicketSize = TicketV1.UserData.size() + 40 + 2 + ((TicketV4.AppIDs.size() == 0 ? 1 : TicketV4.AppIDs.size()) * 4) + (TicketV4.HasVACStatus ? 4 : 0) + (TicketV4.HasAppValue ? 4 : 0);
TicketV2.TicketSize = TicketV1.TicketSize - TicketV1.UserData.size();
buffer = TicketV1.Serialize();
auto v = TicketV2.Serialize();
buffer.insert(buffer.end(), v.begin(), v.end());
v = TicketV4.Serialize();
buffer.insert(buffer.end(), v.begin(), v.end());
return buffer;
}
Steam_AppTicket::Steam_AppTicket(class Settings *settings) :
settings(settings)
{
}
uint32 Steam_AppTicket::GetAppOwnershipTicketData( uint32 nAppID, void *pvBuffer, uint32 cbBufferLength, uint32 *piAppId, uint32 *piSteamId, uint32 *piSignature, uint32 *pcbSignature )
{
PRINT_DEBUG("TODO %u, %p, %u, %p, %p, %p, %p", nAppID, pvBuffer, cbBufferLength, piAppId, piSteamId, piSignature, pcbSignature);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}

View File

@ -1,5 +1,17 @@
#include "dll/auth.h" #include "dll/auth.h"
#define STEAM_ID_OFFSET_TICKET (4 + 8)
#define STEAM_TICKET_MIN_SIZE (4 + 8 + 8)
#define STEAM_TICKET_MIN_SIZE_NEW 170
#define STEAM_TICKET_PROCESS_TIME 0.03
//Conan Exiles doesn't work with 512 or 128, 256 seems to be the good size
// Usually steam send as 1024 (or recommend sending as that)
//Steam returns 234
#define STEAM_AUTH_TICKET_SIZE 256 //234
static inline int generate_random_int() { static inline int generate_random_int() {
int a; int a;
randombytes((char *)&a, sizeof(a)); randombytes((char *)&a, sizeof(a));
@ -24,7 +36,568 @@ static uint32_t get_ticket_count() {
} }
static void steam_auth_manager_ticket_callback(void *object, Common_Message *msg) // source: https://github.com/Detanup01/stmsrv/blob/main/Cert/AppTicket.key
// thanks Detanup01
const static std::string app_ticket_key =
"-----BEGIN PRIVATE KEY-----\n"
"MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMITHOY6pfsvaGTI\n"
"llmilPa1+ev4BsUV0IW3+F/3pQlZ+o57CO1HbepSh2a37cbGUSehOVQ7lREPVXP3\n"
"UdyF5tU5IMytJef5N7euM5z2IG9IszeOReO87h2AmtlwGqnRj7qd0MeVxSAuUq7P\n"
"C/Ir1VyOg58+wAKxaPL18upylnGJAgMBAAECgYEAnKQQj0KG9VYuTCoaL/6pfPcj\n"
"4PEvhaM1yrfSIKMg8YtOT/G+IsWkUZyK7L1HjUhD+FiIjRQKHNrjfdYAnJz20Xom\n"
"k6iVt7ugihIne1Q3pGYG8TY9P1DPdN7zEnAVY1Bh2PAlqJWrif3v8v1dUGE/dYr2\n"
"U3M0JhvzO7VL1B/chIECQQDqW9G5azGMA/cL4jOg0pbj9GfxjJZeT7M2rBoIaRWP\n"
"C3ROndyb+BNahlKk6tbvqillvvMQQiSFGw/PbmCwtLL3AkEA0/79W0q9d3YCXQGW\n"
"k3hQvR8HEbxLmRaRF2gU4MOa5C0JqwsmxzdK4mKoJCpVAiu1gmFonLjn2hm8i+vK\n"
"b7hffwJAEiMpCACTxRJJfFH1TOz/YIT5xmfq+0GPzRtkqGH5mSh5x9vPxwJb/RWI\n"
"L9s85y90JLuyc/+qc+K0Rol0Ujip4QJAGLXVJEn+8ajAt8SSn5fbmV+/fDK9gRef\n"
"S+Im5NgH+ubBBL3lBD2Orfqf7K8+f2VG3+6oufPXmpV7Y7fVPdZ40wJALDujJXgi\n"
"XiCBSht1YScYjfmJh2/xZWh8/w+vs5ZTtrnW2FQvfvVDG9c1hrChhpvmA0QxdgWB\n"
"zSsAno/utcuB9w==\n"
"-----END PRIVATE KEY-----\n";
static std::vector<uint8_t> sign_auth_data(const std::string &private_key_content, const std::vector<uint8_t> &data, size_t effective_data_len)
{
std::vector<uint8_t> signature{};
// Hash the data using SHA-1
constexpr static int SHA1_DIGEST_LENGTH = 20;
uint8_t hash[SHA1_DIGEST_LENGTH]{};
int result = mbedtls_sha1(data.data(), effective_data_len, hash);
if (result != 0) {
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
std::string err_msg(256, 0);
mbedtls_strerror(result, &err_msg[0], err_msg.size());
PRINT_DEBUG("failed to hash the data via SHA1: %s", err_msg.c_str());
#endif
return signature;
}
mbedtls_entropy_context entropy_ctx; // entropy context for random number generation
mbedtls_entropy_init(&entropy_ctx);
mbedtls_ctr_drbg_context ctr_drbg_ctx; // CTR-DRBG context for deterministic random number generation
mbedtls_ctr_drbg_init(&ctr_drbg_ctx);
// seed the CTR-DRBG context with random numbers
result = mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, mbedtls_entropy_func, &entropy_ctx, nullptr, 0);
if (mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, mbedtls_entropy_func, &entropy_ctx, nullptr, 0) != 0) {
mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
mbedtls_entropy_free(&entropy_ctx);
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
std::string err_msg(256, 0);
mbedtls_strerror(result, &err_msg[0], err_msg.size());
PRINT_DEBUG("failed to seed the CTR-DRBG context: %s", err_msg.c_str());
#endif
return signature;
}
mbedtls_pk_context private_key_ctx; // holds the parsed private key
mbedtls_pk_init(&private_key_ctx);
result = mbedtls_pk_parse_key(
&private_key_ctx, // will hold the parsed private key
(const unsigned char *)private_key_content.c_str(),
private_key_content.size() + 1, // we MUST include the null terminator, otherwise this API returns an error!
nullptr, 0, // no password stuff, private key isn't protected
mbedtls_ctr_drbg_random, &ctr_drbg_ctx // random number generation function + the CTR-DRBG context it requires as an input
);
if (result != 0) {
mbedtls_pk_free(&private_key_ctx);
mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
mbedtls_entropy_free(&entropy_ctx);
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
std::string err_msg(256, 0);
mbedtls_strerror(result, &err_msg[0], err_msg.size());
PRINT_DEBUG("failed to parse private key: %s", err_msg.c_str());
#endif
return signature;
}
// private key must be valid RSA key
if (mbedtls_pk_get_type(&private_key_ctx) != MBEDTLS_PK_RSA || // invalid type
mbedtls_pk_can_do(&private_key_ctx, MBEDTLS_PK_RSA) == 0) { // or initialized but not properly setup (maybe freed?)
mbedtls_pk_free(&private_key_ctx);
mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
mbedtls_entropy_free(&entropy_ctx);
PRINT_DEBUG("parsed key is not a valid RSA private key");
return signature;
}
// get the underlying RSA context from the parsed private key
mbedtls_rsa_context* rsa_ctx = mbedtls_pk_rsa(private_key_ctx);
// resize the output buffer to accomodate the size of the private key
const size_t private_key_len = mbedtls_pk_get_len(&private_key_ctx);
if (private_key_len == 0) { // TODO must be 128 siglen
mbedtls_pk_free(&private_key_ctx);
mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
mbedtls_entropy_free(&entropy_ctx);
PRINT_DEBUG("failed to get private key (final buffer) length");
return signature;
}
PRINT_DEBUG("computed private key (final buffer) length = %zu", private_key_len);
signature.resize(private_key_len);
// finally sign the computed hash using RSA and PKCS#1 padding
result = mbedtls_rsa_pkcs1_sign(
rsa_ctx,
mbedtls_ctr_drbg_random, &ctr_drbg_ctx,
MBEDTLS_MD_SHA1, // we used SHA1 to hash the data
sizeof(hash), hash,
signature.data() // output
);
mbedtls_pk_free(&private_key_ctx);
mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
mbedtls_entropy_free(&entropy_ctx);
if (result != 0) {
signature.clear();
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
std::string err_msg(256, 0);
mbedtls_strerror(result, &err_msg[0], err_msg.size());
PRINT_DEBUG("RSA signing failed: %s", err_msg.c_str());
#endif
}
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(signature);
PRINT_DEBUG("final signature [%zu bytes]:\n %s", signature.size(), str.c_str());
#endif
return signature;
}
std::vector<uint8_t> DLC::Serialize() const
{
PRINT_DEBUG("AppId = %u, Licenses count = %zu", AppId, Licenses.size());
// we need this variable because we depend on the sizeof, must be 2 bytes
const uint16_t dlcs_licenses_count = (uint16_t)Licenses.size();
const size_t dlcs_licenses_total_size =
Licenses.size() * sizeof(Licenses[0]); // count * element size
const size_t total_size =
sizeof(AppId) +
sizeof(dlcs_licenses_count) +
dlcs_licenses_total_size;
std::vector<uint8_t> buffer{};
buffer.resize(total_size);
uint8_t* pBuffer = &buffer[0];
#define SER_VAR(v) \
*reinterpret_cast<std::remove_const<decltype(v)>::type *>(pBuffer) = v; \
pBuffer += sizeof(v)
SER_VAR(AppId);
SER_VAR(dlcs_licenses_count);
for(uint32_t dlc_license : Licenses) {
SER_VAR(dlc_license);
}
#undef SER_VAR
PRINT_DEBUG("final size = %zu", buffer.size());
return buffer;
}
std::vector<uint8_t> AppTicketGC::Serialize() const
{
const uint64_t steam_id = id.ConvertToUint64();
// must be 52
constexpr size_t total_size =
sizeof(STEAM_APPTICKET_GCLen) +
sizeof(GCToken) +
sizeof(steam_id) +
sizeof(ticketGenDate) +
sizeof(STEAM_APPTICKET_SESSIONLEN) +
sizeof(one) +
sizeof(two) +
sizeof(ExternalIP) +
sizeof(InternalIP) +
sizeof(TimeSinceStartup) +
sizeof(TicketGeneratedCount);
// check the size at compile time, we must ensure the correct size
#ifndef EMU_RELEASE_BUILD
static_assert(
total_size == 52,
"AUTH::AppTicketGC::SER calculated size of serialized data != 52 bytes, your compiler has some incorrect sizes"
);
#endif
PRINT_DEBUG(
"\n"
" GCToken: " "%" PRIu64 "\n"
" user steam_id: " "%" PRIu64 "\n"
" ticketGenDate: %u\n"
" ExternalIP: 0x%08X, InternalIP: 0x%08X\n"
" TimeSinceStartup: %u, TicketGeneratedCount: %u\n"
" SER size = %zu",
GCToken,
steam_id,
ticketGenDate,
ExternalIP, InternalIP,
TimeSinceStartup, TicketGeneratedCount,
total_size
);
std::vector<uint8_t> buffer{};
buffer.resize(total_size);
uint8_t* pBuffer = &buffer[0];
#define SER_VAR(v) \
*reinterpret_cast<std::remove_const<decltype(v)>::type *>(pBuffer) = v; \
pBuffer += sizeof(v)
SER_VAR(STEAM_APPTICKET_GCLen);
SER_VAR(GCToken);
SER_VAR(steam_id);
SER_VAR(ticketGenDate);
SER_VAR(STEAM_APPTICKET_SESSIONLEN);
SER_VAR(one);
SER_VAR(two);
SER_VAR(ExternalIP);
SER_VAR(InternalIP);
SER_VAR(TimeSinceStartup);
SER_VAR(TicketGeneratedCount);
#undef SER_VAR
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
PRINT_DEBUG("final data [%zu bytes]:\n %s", buffer.size(), str.c_str());
#endif
return buffer;
}
std::vector<uint8_t> AppTicket::Serialize() const
{
const uint64_t steam_id = id.ConvertToUint64();
PRINT_DEBUG(
"\n"
" Version: %u\n"
" user steam_id: " "%" PRIu64 "\n"
" AppId: %u\n"
" ExternalIP: 0x%08X, InternalIP: 0x%08X\n"
" TicketGeneratedDate: %u, TicketGeneratedExpireDate: %u\n"
" Licenses count: %zu, DLCs count: %zu",
Version,
steam_id,
AppId,
ExternalIP, InternalIP,
TicketGeneratedDate, TicketGeneratedExpireDate,
Licenses.size(), DLCs.size()
);
// we need this variable because we depend on the sizeof, must be 2 bytes
const uint16_t licenses_count = (uint16_t)Licenses.size();
const size_t licenses_total_size =
Licenses.size() * sizeof(Licenses[0]); // total count * element size
// we need this variable because we depend on the sizeof, must be 2 bytes
const uint16_t dlcs_count = (uint16_t)DLCs.size();
size_t dlcs_total_size = 0;
std::vector<std::vector<uint8_t>> serialized_dlcs{};
for (const DLC &dlc : DLCs) {
auto dlc_ser = dlc.Serialize();
dlcs_total_size += dlc_ser.size();
serialized_dlcs.push_back(dlc_ser);
}
//padding
constexpr uint16_t padding = (uint16_t)0;
// must be 42
constexpr size_t static_fields_size =
sizeof(Version) +
sizeof(steam_id) +
sizeof(AppId) +
sizeof(ExternalIP) +
sizeof(InternalIP) +
sizeof(AlwaysZero) +
sizeof(TicketGeneratedDate) +
sizeof(TicketGeneratedExpireDate) +
sizeof(licenses_count) +
sizeof(dlcs_count) +
sizeof(padding);
// check the size at compile time, we must ensure the correct size
#ifndef EMU_RELEASE_BUILD
static_assert(
static_fields_size == 42,
"AUTH::AppTicket::SER calculated size of serialized data != 42 bytes, your compiler has some incorrect sizes"
);
#endif
const size_t total_size =
static_fields_size +
licenses_total_size +
dlcs_total_size;
PRINT_DEBUG("final size = %zu", total_size);
std::vector<uint8_t> buffer{};
buffer.resize(total_size);
uint8_t* pBuffer = &buffer[0];
#define SER_VAR(v) \
*reinterpret_cast<std::remove_const<decltype(v)>::type *>(pBuffer) = v; \
pBuffer += sizeof(v)
SER_VAR(Version);
SER_VAR(steam_id);
SER_VAR(AppId);
SER_VAR(ExternalIP);
SER_VAR(InternalIP);
SER_VAR(AlwaysZero);
SER_VAR(TicketGeneratedDate);
SER_VAR(TicketGeneratedExpireDate);
#ifndef EMU_RELEASE_BUILD
{
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
PRINT_DEBUG("(before licenses + DLCs):\n %s", str.c_str());
}
#endif
/*
* layout of licenses:
* ------------------------
* 2 bytes: count of licenses
* ------------------------
* [
* ------------------------
* | 4 bytes: license element
* ------------------------
* ]
*/
SER_VAR(licenses_count);
for(uint32_t license : Licenses) {
SER_VAR(license);
}
/*
* layout of DLCs:
* ------------------------
* | 2 bytes: count of DLCs
* ------------------------
* [
* ------------------------
* | 4 bytes: app id
* ------------------------
* | 2 bytes: DLC licenses count
* ------------------------
* [
* 4 bytes: DLC license element
* ]
* ]
*/
SER_VAR(dlcs_count);
for (const auto &dlc_ser : serialized_dlcs){
memcpy(pBuffer, dlc_ser.data(), dlc_ser.size());
pBuffer += dlc_ser.size();
}
//padding
SER_VAR(padding);
#undef SER_VAR
#ifndef EMU_RELEASE_BUILD
{
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
PRINT_DEBUG("final data [%zu bytes]:\n %s", buffer.size(), str.c_str());
}
#endif
return buffer;
}
std::vector<uint8_t> Auth_Data::Serialize() const
{
/*
* layout of Auth_Data with GC:
* ------------------------
* X bytes: GC data blob (currently 52 bytes)
* ------------------------
* 4 bytes: remaining Auth_Data blob size (4 + Y + Z)
* ------------------------
* 4 bytes: size of ticket data layout (not blob!, hence blob + 4)
* ------------------------
* Y bytes: ticket data blob
* ------------------------
* Z bytes: App Ticket signature
* ------------------------
*
* total layout length = X + 4 + 4 + Y + Z
*/
/*
* layout of Auth_Data without GC:
* ------------------------
* 4 bytes: size of ticket data layout (not blob!, hence blob + 4)
* ------------------------
* Y bytes: ticket data blob
* ------------------------
* Z bytes: App Ticket signature
* ------------------------
*
* total layout length = 4 + Y + Z
*/
const uint64_t steam_id = id.ConvertToUint64();
PRINT_DEBUG(
"\n"
" HasGC: %u\n"
" user steam_id: " "%" PRIu64 "\n"
" number: " "%" PRIu64 ,
(int)HasGC,
steam_id,
number
);
/*
* layout of ticket data:
* ------------------------
* 4 bytes: size of ticket data layout (not blob!, hence blob + 4)
* ------------------------
* Y bytes: ticket data blob
* ------------------------
*
* total layout length = 4 + Y
*/
std::vector<uint8_t> tickedData = Ticket.Serialize();
// we need this variable because we depend on the sizeof, must be 4 bytes
const uint32_t ticket_data_layout_length =
sizeof(uint32_t) + // size of this uint32_t because it is included!
(uint32_t)tickedData.size();
size_t total_size_without_siglen = ticket_data_layout_length;
std::vector<uint8_t> GCData{};
size_t gc_data_layout_length = 0;
if (HasGC) {
/*
* layout of GC data:
* ------------------------
* X bytes: GC data blob (currently 52 bytes)
* ------------------------
* 4 bytes: remaining Auth_Data blob size
* ------------------------
*
* total layout length = X + 4
*/
GCData = GC.Serialize();
gc_data_layout_length +=
GCData.size() +
sizeof(uint32_t);
total_size_without_siglen += gc_data_layout_length;
}
const size_t final_buffer_size = total_size_without_siglen + STEAM_APPTICKET_SIGLEN;
PRINT_DEBUG("size without sig len = %zu, size with sig len (final size) = %zu",
total_size_without_siglen,
final_buffer_size
);
std::vector<uint8_t> buffer;
buffer.resize(final_buffer_size);
uint8_t* pBuffer = &buffer[0];
#define SER_VAR(v) \
*reinterpret_cast<std::remove_const<decltype(v)>::type *>(pBuffer) = v; \
pBuffer += sizeof(v)
// serialize the GC data first
if (HasGC) {
memcpy(pBuffer, GCData.data(), GCData.size());
pBuffer += GCData.size();
// when GC data is written (HasGC),
// the next 4 bytes after the GCData will be the length of the remaining data in the final buffer
// i.e. final buffer size - length of GCData layout
// i.e. ticket data length + STEAM_APPTICKET_SIGLEN
//
// notice that we subtract the entire layout length, not just GCData.size(),
// otherwise these next 4 bytes will include themselves!
uint32_t remaining_length = (uint32_t)(final_buffer_size - gc_data_layout_length);
SER_VAR(remaining_length);
}
// serialize the ticket data
SER_VAR(ticket_data_layout_length);
memcpy(pBuffer, tickedData.data(), tickedData.size());
#ifndef EMU_RELEASE_BUILD
{
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
PRINT_DEBUG("final data (before signature) [%zu bytes]:\n %s", buffer.size(), str.c_str());
}
#endif
//Todo make a signature
std::vector<uint8_t> signature = sign_auth_data(app_ticket_key, tickedData, total_size_without_siglen);
if (signature.size() == STEAM_APPTICKET_SIGLEN) {
memcpy(buffer.data() + total_size_without_siglen, signature.data(), signature.size());
#ifndef EMU_RELEASE_BUILD
{
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
PRINT_DEBUG("final data (after signature) [%zu bytes]:\n %s", buffer.size(), str.c_str());
}
#endif
} else {
PRINT_DEBUG("signature size [%zu] is invalid", signature.size());
}
#undef SER_VAR
return buffer;
}
void Auth_Manager::ticket_callback(void *object, Common_Message *msg)
{ {
// PRINT_DEBUG_ENTRY(); // PRINT_DEBUG_ENTRY();
@ -32,22 +605,22 @@ static void steam_auth_manager_ticket_callback(void *object, Common_Message *msg
auth_manager->Callback(msg); auth_manager->Callback(msg);
} }
Auth_Manager::Auth_Manager(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks) { Auth_Manager::Auth_Manager(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks)
{
this->network = network; this->network = network;
this->settings = settings; this->settings = settings;
this->callbacks = callbacks; this->callbacks = callbacks;
this->network->setCallback(CALLBACK_ID_AUTH_TICKET, settings->get_local_steam_id(), &steam_auth_manager_ticket_callback, this); this->network->setCallback(CALLBACK_ID_AUTH_TICKET, settings->get_local_steam_id(), &Auth_Manager::ticket_callback, this);
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &steam_auth_manager_ticket_callback, this); this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Auth_Manager::ticket_callback, this);
} }
Auth_Manager::~Auth_Manager() Auth_Manager::~Auth_Manager()
{ {
this->network->rmCallback(CALLBACK_ID_AUTH_TICKET, settings->get_local_steam_id(), &steam_auth_manager_ticket_callback, this); this->network->rmCallback(CALLBACK_ID_AUTH_TICKET, settings->get_local_steam_id(), &Auth_Manager::ticket_callback, this);
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &steam_auth_manager_ticket_callback, this); this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Auth_Manager::ticket_callback, this);
} }
#define STEAM_TICKET_PROCESS_TIME 0.03
void Auth_Manager::launch_callback(CSteamID id, EAuthSessionResponse resp, double delay) void Auth_Manager::launch_callback(CSteamID id, EAuthSessionResponse resp, double delay)
{ {
@ -72,10 +645,6 @@ void Auth_Manager::launch_callback_gs(CSteamID id, bool approved)
} }
} }
#define STEAM_ID_OFFSET_TICKET (4 + 8)
#define STEAM_TICKET_MIN_SIZE (4 + 8 + 8)
#define STEAM_TICKET_MIN_SIZE_NEW 170
Auth_Data Auth_Manager::getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) Auth_Data Auth_Manager::getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{ {
@ -153,11 +722,6 @@ Auth_Data Auth_Manager::getTicketData( void *pTicket, int cbMaxTicket, uint32 *p
return ticket_data; return ticket_data;
} }
//Conan Exiles doesn't work with 512 or 128, 256 seems to be the good size
// Usually steam send as 1024 (or recommend sending as that)
//Steam returns 234
#define STEAM_AUTH_TICKET_SIZE 256 //234
HAuthTicket Auth_Manager::getTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) HAuthTicket Auth_Manager::getTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{ {
if (settings->enable_new_app_ticket) if (settings->enable_new_app_ticket)
@ -301,6 +865,8 @@ bool Auth_Manager::endAuth(CSteamID id)
return erased; return erased;
} }
void Auth_Manager::Callback(Common_Message *msg) void Auth_Manager::Callback(Common_Message *msg)
{ {
if (msg->has_low_level()) { if (msg->has_low_level()) {
@ -338,4 +904,4 @@ void Auth_Manager::Callback(Common_Message *msg)
} }
} }
} }
} }

View File

@ -17,6 +17,12 @@
#include "dll/base.h" #include "dll/base.h"
std::recursive_mutex global_mutex{};
// some arbitrary counter/time for reference
const std::chrono::time_point<std::chrono::high_resolution_clock> startup_counter = std::chrono::high_resolution_clock::now();
const std::chrono::time_point<std::chrono::system_clock> startup_time = std::chrono::system_clock::now();
#ifdef __WINDOWS__ #ifdef __WINDOWS__
void randombytes(char *buf, size_t size) void randombytes(char *buf, size_t size)
@ -93,11 +99,6 @@ bool set_env_variable(const std::string &name, const std::string &value)
#endif #endif
std::recursive_mutex global_mutex{};
// some arbitrary counter/time for reference
const std::chrono::time_point<std::chrono::high_resolution_clock> startup_counter = std::chrono::high_resolution_clock::now();
const std::chrono::time_point<std::chrono::system_clock> startup_time = std::chrono::system_clock::now();
#ifndef EMU_RELEASE_BUILD #ifndef EMU_RELEASE_BUILD
const std::string dbg_log_file = get_full_program_path() + "STEAM_LOG.txt"; const std::string dbg_log_file = get_full_program_path() + "STEAM_LOG.txt";
@ -147,10 +148,6 @@ CSteamID generate_steam_id_lobby()
return CSteamID(generate_account_id(), k_EChatInstanceFlagLobby | k_EChatInstanceFlagMMSLobby, k_EUniversePublic, k_EAccountTypeChat); return CSteamID(generate_account_id(), k_EChatInstanceFlagLobby | k_EChatInstanceFlagMMSLobby, k_EUniversePublic, k_EAccountTypeChat);
} }
/// @brief Check for a timeout given some initial timepoint and a timeout in sec.
/// @param old The initial timepoint which will be compared against current time
/// @param timeout The max allowed time in seconds
/// @return true if the timepoint has exceeded the max allowed timeout, false otherwise
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout) bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout)
{ {
if (timeout == 0.0) return true; if (timeout == 0.0) return true;

406
dll/callsystem.cpp Normal file
View File

@ -0,0 +1,406 @@
/* 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 "dll/callsystem.h"
void CCallbackMgr::SetRegister(class CCallbackBase *pCallback, int iCallback)
{
pCallback->m_nCallbackFlags |= CCallbackBase::k_ECallbackFlagsRegistered;
pCallback->m_iCallback = iCallback;
};
void CCallbackMgr::SetUnregister(class CCallbackBase *pCallback)
{
if (pCallback)
pCallback->m_nCallbackFlags &= !CCallbackBase::k_ECallbackFlagsRegistered;
};
bool CCallbackMgr::isServer(class CCallbackBase *pCallback)
{
return (pCallback->m_nCallbackFlags & CCallbackBase::k_ECallbackFlagsGameServer) != 0;
};
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);
}
run_in = r_in;
run_call_completed_cb = run_cc_cb;
iCallback = icb;
created = std::chrono::high_resolution_clock::now();
}
bool Steam_Call_Result::operator==(const struct Steam_Call_Result& other) const
{
return other.api_call == api_call && other.callbacks == callbacks;
}
bool Steam_Call_Result::timed_out() const
{
return check_timedout(created, STEAM_CALLRESULT_TIMEOUT);
}
bool Steam_Call_Result::call_completed() const
{
return (!reserved) && check_timedout(created, run_in);
}
bool Steam_Call_Result::can_execute() const
{
return (!to_delete) && call_completed() && (has_cb() || check_timedout(created, STEAM_CALLRESULT_WAIT_FOR_CB));
}
bool Steam_Call_Result::has_cb() const
{
return callbacks.size() > 0;
}
void SteamCallResults::addCallCompleted(class CCallbackBase *cb)
{
if (std::find(completed_callbacks.begin(), completed_callbacks.end(), cb) == completed_callbacks.end()) {
completed_callbacks.push_back(cb);
}
}
void SteamCallResults::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 SteamCallResults::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 SteamCallResults::exists(SteamAPICall_t api_call) const
{
auto cr = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) {
return item.api_call == api_call;
});
if (callresults.end() == cr) return false;
if (!cr->call_completed()) return false;
return true;
}
bool SteamCallResults::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 SteamCallResults::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 SteamCallResults::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 SteamCallResults::addCallResult(SteamAPICall_t api_call, int iCallback, void *result, unsigned int size, double timeout, bool run_call_completed_cb)
{
PRINT_DEBUG("%i", iCallback);
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("ERROR");
return k_uAPICallInvalid;
}
SteamAPICall_t SteamCallResults::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 SteamCallResults::addCallResult(int iCallback, void *result, unsigned int size, double timeout, bool run_call_completed_cb)
{
return addCallResult(generate_steam_api_call_id(), iCallback, result, size, timeout, run_call_completed_cb);
}
void SteamCallResults::setCbAll(void (*cb_all)(std::vector<char> result, int callback))
{
this->cb_all = cb_all;
}
void SteamCallResults::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", 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");
}
}
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", 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("erase to_delete");
auto c = std::begin(callresults);
while (c != std::end(callresults)) {
if (c->to_delete) {
if (c->timed_out()) {
PRINT_DEBUG("removed callresult %i", c->iCallback);
c = callresults.erase(c);
} else {
++c;
}
} else {
++c;
}
}
}
SteamCallBacks::SteamCallBacks(SteamCallResults *results)
{
this->results = results;
}
void SteamCallBacks::addCallBack(int iCallback, class CCallbackBase *cb)
{
PRINT_DEBUG("%i", 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 SteamCallBacks::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 SteamCallBacks::addCBResult(int iCallback, void *result, unsigned int size)
{
addCBResult(iCallback, result, size, DEFAULT_CB_TIMEOUT, false);
}
void SteamCallBacks::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 SteamCallBacks::addCBResult(int iCallback, void *result, unsigned int size, double timeout)
{
addCBResult(iCallback, result, size, timeout, false);
}
void SteamCallBacks::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 SteamCallBacks::runCallBacks()
{
for (auto & c : callbacks) {
c.second.results.clear();
}
}
void RunEveryRunCB::add(void (*cb)(void *object), void *object)
{
remove(cb, object);
RunCBs rcb;
rcb.function = cb;
rcb.object = object;
cbs.push_back(rcb);
}
void RunEveryRunCB::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 RunEveryRunCB::run() const
{
std::vector<struct RunCBs> temp_cbs = cbs;
for (auto c : temp_cbs) {
c.function(c.object);
}
}

View File

@ -24,55 +24,16 @@
struct AppTicketV1 struct AppTicketV1
{ {
// Total ticket size - 16 // Total ticket size - 16
uint32_t TicketSize; uint32_t TicketSize{};
uint32_t TicketVersion; uint32_t TicketVersion{};
uint32_t Unk2; uint32_t Unk2{};
std::vector<uint8_t> UserData; std::vector<uint8_t> UserData{};
void Reset() void Reset();
{
TicketSize = 0;
TicketVersion = 0;
Unk2 = 0;
UserData.clear();
}
std::vector<uint8_t> Serialize() std::vector<uint8_t> Serialize() const;
{
std::vector<uint8_t> buffer;
uint8_t* pBuffer;
buffer.resize(16 + UserData.size()); bool Deserialize(const uint8_t* pBuffer, size_t size);
pBuffer = buffer.data();
*reinterpret_cast<uint32_t*>(pBuffer) = TicketSize; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = TicketVersion; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = UserData.size(); pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = Unk2; pBuffer += 4;
memcpy(pBuffer, UserData.data(), UserData.size());
return buffer;
}
bool Deserialize(const uint8_t* pBuffer, size_t size)
{
if (size < 16)
return false;
uint32_t user_data_size;
TicketSize = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
TicketVersion = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
user_data_size = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
if (size < (user_data_size + 16))
return false;
Unk2 = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
UserData.resize(user_data_size);
memcpy(UserData.data(), pBuffer, user_data_size);
return true;
}
//inline uint32_t TicketSize() { return *reinterpret_cast<uint32_t*>(_Buffer); } //inline uint32_t TicketSize() { return *reinterpret_cast<uint32_t*>(_Buffer); }
//inline uint32_t TicketVersion(){ return *reinterpret_cast<uint32_t*>(reinterpret_cast<uintptr_t>(_Buffer) + 4); } //inline uint32_t TicketVersion(){ return *reinterpret_cast<uint32_t*>(reinterpret_cast<uintptr_t>(_Buffer) + 4); }
@ -84,69 +45,24 @@ struct AppTicketV1
struct AppTicketV2 struct AppTicketV2
{ {
// Totals ticket size - 16 - TicketV1::UserData.size() // Totals ticket size - 16 - TicketV1::UserData.size()
uint32_t TicketSize; uint32_t TicketSize{};
uint32_t TicketVersion; uint32_t TicketVersion{};
uint64_t SteamID; uint64_t SteamID{};
uint32_t AppID; uint32_t AppID{};
uint32_t Unk1; uint32_t Unk1{};
uint32_t Unk2; uint32_t Unk2{};
uint32_t TicketFlags; uint32_t TicketFlags{};
uint32_t TicketIssueTime; uint32_t TicketIssueTime{};
uint32_t TicketValidityEnd; uint32_t TicketValidityEnd{};
static constexpr uint32_t LicenseBorrowed = 0x00000002; // Bit 1: IsLicenseBorrowed static constexpr const uint32_t LicenseBorrowed = 0x00000002; // Bit 1: IsLicenseBorrowed
static constexpr uint32_t LicenseTemporary = 0x00000004; // Bit 2: IsLicenseTemporary static constexpr const uint32_t LicenseTemporary = 0x00000004; // Bit 2: IsLicenseTemporary
void Reset() void Reset();
{
TicketSize = 0;
TicketVersion = 0;
SteamID = 0;
AppID = 0;
Unk1 = 0;
Unk2 = 0;
TicketFlags = 0;
TicketIssueTime = 0;
TicketValidityEnd = 0;
}
std::vector<uint8_t> Serialize() std::vector<uint8_t> Serialize() const;
{
std::vector<uint8_t> buffer;
uint8_t* pBuffer;
buffer.resize(40); bool Deserialize(const uint8_t* pBuffer, size_t size);
pBuffer = buffer.data();
*reinterpret_cast<uint32_t*>(pBuffer) = TicketSize; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = TicketVersion; pBuffer += 4;
*reinterpret_cast<uint64_t*>(pBuffer) = SteamID; pBuffer += 8;
*reinterpret_cast<uint32_t*>(pBuffer) = AppID; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = Unk1; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = Unk2; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = TicketFlags; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = TicketIssueTime; pBuffer += 4;
*reinterpret_cast<uint32_t*>(pBuffer) = TicketValidityEnd;
return buffer;
}
bool Deserialize(const uint8_t* pBuffer, size_t size)
{
if (size < 40)
return false;
TicketSize = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
TicketVersion = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
SteamID = *reinterpret_cast<const uint64_t*>(pBuffer); pBuffer += 8;
AppID = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
Unk1 = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
Unk2 = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
TicketFlags = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
TicketIssueTime = *reinterpret_cast<const uint32_t*>(pBuffer); pBuffer += 4;
TicketValidityEnd = *reinterpret_cast<const uint32_t*>(pBuffer);
return true;
}
//inline uint32_t TicketSize() { return *reinterpret_cast<uint32_t*>(_Buffer); } //inline uint32_t TicketSize() { return *reinterpret_cast<uint32_t*>(_Buffer); }
//inline uint32_t TicketVersion() { return *reinterpret_cast<uint32_t*>(reinterpret_cast<uintptr_t>(_Buffer) + 4); } //inline uint32_t TicketVersion() { return *reinterpret_cast<uint32_t*>(reinterpret_cast<uintptr_t>(_Buffer) + 4); }
@ -161,109 +77,18 @@ struct AppTicketV2
struct AppTicketV4 struct AppTicketV4
{ {
std::vector<uint32_t> AppIDs; std::vector<uint32_t> AppIDs{};
bool HasVACStatus = false; bool HasVACStatus = false;
uint32_t VACStatus; uint32_t VACStatus{};
bool HasAppValue = false; bool HasAppValue = false;
uint32_t AppValue; uint32_t AppValue{};
void Reset() void Reset();
{
AppIDs.clear();
HasVACStatus = false;
HasAppValue = false;
}
std::vector<uint8_t> Serialize() std::vector<uint8_t> Serialize();
{
std::vector<uint32_t> appids = AppIDs;
if (appids.size() == 0)
{
appids.emplace_back(0);
}
uint16_t appid_count = static_cast<uint16_t>(appids.size() > 140 ? 140 : appids.size()); bool Deserialize(const uint8_t* pBuffer, size_t size);
size_t buffer_size = static_cast<uint32_t>(appid_count) * 4ul + 2ul;
std::vector<uint8_t> buffer;
uint8_t* pBuffer;
if (HasAppValue)
{// VACStatus + AppValue
buffer_size += 4;
if (!HasVACStatus)
{
HasVACStatus = true;
VACStatus = 0;
}
}
if (HasVACStatus)
{// VACStatus only
buffer_size += 4;
}
buffer.resize(buffer_size);
pBuffer = buffer.data();
*reinterpret_cast<uint16_t*>(pBuffer) = appid_count;
pBuffer += 2;
for (int i = 0; i < appid_count && i < 140; ++i)
{
*reinterpret_cast<uint32_t*>(pBuffer) = appids[i];
pBuffer += 4;
}
if (HasVACStatus)
{
*reinterpret_cast<uint32_t*>(pBuffer) = VACStatus;
pBuffer += 4;
}
if (HasAppValue)
{
*reinterpret_cast<uint32_t*>(pBuffer) = AppValue;
}
return buffer;
}
bool Deserialize(const uint8_t* pBuffer, size_t size)
{
if (size < 2)
return false;
uint16_t appid_count = *reinterpret_cast<const uint16_t*>(pBuffer);
if (size < (appid_count * 4 + 2) || appid_count >= 140)
return false;
AppIDs.resize(appid_count);
pBuffer += 2;
size -= 2;
for (int i = 0; i < appid_count; ++i)
{
AppIDs[i] = *reinterpret_cast<const uint32_t*>(pBuffer);
pBuffer += 4;
size -= 4;
}
HasVACStatus = false;
HasAppValue = false;
if (size < 4)
return true;
HasVACStatus = true;
VACStatus = *reinterpret_cast<const uint32_t*>(pBuffer);
pBuffer += 4;
size -= 4;
if (size < 4)
return true;
HasAppValue = true;
AppValue = *reinterpret_cast<const uint32_t*>(pBuffer);
return true;
}
// Often 1 with empty appid // Often 1 with empty appid
//inline uint16_t AppIDCount() { return *reinterpret_cast<uint16_t*>(_Buffer); } //inline uint16_t AppIDCount() { return *reinterpret_cast<uint16_t*>(_Buffer); }
@ -277,48 +102,13 @@ struct AppTicketV4
class DecryptedAppTicket class DecryptedAppTicket
{ {
public: public:
AppTicketV1 TicketV1; AppTicketV1 TicketV1{};
AppTicketV2 TicketV2; AppTicketV2 TicketV2{};
AppTicketV4 TicketV4; AppTicketV4 TicketV4{};
bool DeserializeTicket(const uint8_t* pBuffer, size_t buf_size) bool DeserializeTicket(const uint8_t* pBuffer, size_t buf_size);
{
if (!TicketV1.Deserialize(pBuffer, buf_size))
return false;
pBuffer += 16 + TicketV1.UserData.size(); std::vector<uint8_t> SerializeTicket();
buf_size -= 16 + TicketV1.UserData.size();
if (!TicketV2.Deserialize(pBuffer, buf_size))
return false;
if (TicketV2.TicketVersion > 2)
{
pBuffer += 40;
buf_size -= 40;
if (!TicketV4.Deserialize(pBuffer, buf_size))
return false;
}
return true;
}
std::vector<uint8_t> SerializeTicket()
{
std::vector<uint8_t> buffer;
TicketV1.TicketSize = TicketV1.UserData.size() + 40 + 2 + ((TicketV4.AppIDs.size() == 0 ? 1 : TicketV4.AppIDs.size()) * 4) + (TicketV4.HasVACStatus ? 4 : 0) + (TicketV4.HasAppValue ? 4 : 0);
TicketV2.TicketSize = TicketV1.TicketSize - TicketV1.UserData.size();
buffer = TicketV1.Serialize();
auto v = TicketV2.Serialize();
buffer.insert(buffer.end(), v.begin(), v.end());
v = TicketV4.Serialize();
buffer.insert(buffer.end(), v.begin(), v.end());
return buffer;
}
}; };
@ -326,23 +116,13 @@ class Steam_AppTicket :
public ISteamAppTicket public ISteamAppTicket
{ {
private: private:
class Settings *settings; class Settings *settings{};
public: public:
Steam_AppTicket(class Settings *settings) : Steam_AppTicket(class Settings *settings);
settings(settings)
{
} virtual uint32 GetAppOwnershipTicketData( uint32 nAppID, void *pvBuffer, uint32 cbBufferLength, uint32 *piAppId, uint32 *piSteamId, uint32 *piSignature, uint32 *pcbSignature );
virtual uint32 GetAppOwnershipTicketData( uint32 nAppID, void *pvBuffer, uint32 cbBufferLength, uint32 *piAppId, uint32 *piSteamId, uint32 *piSignature, uint32 *pcbSignature )
{
PRINT_DEBUG("TODO %u, %p, %u, %p, %p, %p, %p", nAppID, pvBuffer, cbBufferLength, piAppId, piSteamId, piSignature, pcbSignature);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
}; };
#endif // __INCLUDED_STEAM_APP_TICKET_H__ #endif // __INCLUDED_STEAM_APP_TICKET_H__

View File

@ -1,8 +1,8 @@
// source: https://github.com/Detanup01/stmsrv/blob/main/Steam3Server/Others/AppTickets.cs // source: https://github.com/Detanup01/stmsrv/blob/main/Steam3Server/Others/AppTickets.cs
// thanks Detanup01 // thanks Detanup01
#ifndef AUTH_INCLUDE #ifndef AUTH_INCLUDE_H
#define AUTH_INCLUDE #define AUTH_INCLUDE_H
#include "base.h" #include "base.h"
#include "mbedtls/pk.h" #include "mbedtls/pk.h"
@ -14,646 +14,96 @@
// the data type is important, we depend on sizeof() for each one of them // the data type is important, we depend on sizeof() for each one of them
constexpr uint32_t STEAM_APPTICKET_SIGLEN = 128; constexpr const static uint32_t STEAM_APPTICKET_SIGLEN = 128;
constexpr uint32_t STEAM_APPTICKET_GCLen = 20; constexpr const static uint32_t STEAM_APPTICKET_GCLen = 20;
constexpr uint32_t STEAM_APPTICKET_SESSIONLEN = 24; constexpr const static uint32_t STEAM_APPTICKET_SESSIONLEN = 24;
// source: https://github.com/Detanup01/stmsrv/blob/main/Cert/AppTicket.key
// thanks Detanup01
const static std::string app_ticket_key =
"-----BEGIN PRIVATE KEY-----\n"
"MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMITHOY6pfsvaGTI\n"
"llmilPa1+ev4BsUV0IW3+F/3pQlZ+o57CO1HbepSh2a37cbGUSehOVQ7lREPVXP3\n"
"UdyF5tU5IMytJef5N7euM5z2IG9IszeOReO87h2AmtlwGqnRj7qd0MeVxSAuUq7P\n"
"C/Ir1VyOg58+wAKxaPL18upylnGJAgMBAAECgYEAnKQQj0KG9VYuTCoaL/6pfPcj\n"
"4PEvhaM1yrfSIKMg8YtOT/G+IsWkUZyK7L1HjUhD+FiIjRQKHNrjfdYAnJz20Xom\n"
"k6iVt7ugihIne1Q3pGYG8TY9P1DPdN7zEnAVY1Bh2PAlqJWrif3v8v1dUGE/dYr2\n"
"U3M0JhvzO7VL1B/chIECQQDqW9G5azGMA/cL4jOg0pbj9GfxjJZeT7M2rBoIaRWP\n"
"C3ROndyb+BNahlKk6tbvqillvvMQQiSFGw/PbmCwtLL3AkEA0/79W0q9d3YCXQGW\n"
"k3hQvR8HEbxLmRaRF2gU4MOa5C0JqwsmxzdK4mKoJCpVAiu1gmFonLjn2hm8i+vK\n"
"b7hffwJAEiMpCACTxRJJfFH1TOz/YIT5xmfq+0GPzRtkqGH5mSh5x9vPxwJb/RWI\n"
"L9s85y90JLuyc/+qc+K0Rol0Ujip4QJAGLXVJEn+8ajAt8SSn5fbmV+/fDK9gRef\n"
"S+Im5NgH+ubBBL3lBD2Orfqf7K8+f2VG3+6oufPXmpV7Y7fVPdZ40wJALDujJXgi\n"
"XiCBSht1YScYjfmJh2/xZWh8/w+vs5ZTtrnW2FQvfvVDG9c1hrChhpvmA0QxdgWB\n"
"zSsAno/utcuB9w==\n"
"-----END PRIVATE KEY-----\n";
static std::vector<uint8_t> sign_auth_data(const std::string &private_key_content, const std::vector<uint8_t> &data, size_t effective_data_len) {
std::vector<uint8_t> signature{};
// Hash the data using SHA-1
constexpr static int SHA1_DIGEST_LENGTH = 20;
uint8_t hash[SHA1_DIGEST_LENGTH]{};
int result = mbedtls_sha1(data.data(), effective_data_len, hash);
if (result != 0)
{
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
std::string err_msg(256, 0);
mbedtls_strerror(result, &err_msg[0], err_msg.size());
PRINT_DEBUG("failed to hash the data via SHA1: %s", err_msg.c_str());
#endif
return signature;
}
mbedtls_entropy_context entropy_ctx; // entropy context for random number generation
mbedtls_entropy_init(&entropy_ctx);
mbedtls_ctr_drbg_context ctr_drbg_ctx; // CTR-DRBG context for deterministic random number generation
mbedtls_ctr_drbg_init(&ctr_drbg_ctx);
// seed the CTR-DRBG context with random numbers
result = mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, mbedtls_entropy_func, &entropy_ctx, nullptr, 0);
if (mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, mbedtls_entropy_func, &entropy_ctx, nullptr, 0) != 0)
{
mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
mbedtls_entropy_free(&entropy_ctx);
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
std::string err_msg(256, 0);
mbedtls_strerror(result, &err_msg[0], err_msg.size());
PRINT_DEBUG("failed to seed the CTR-DRBG context: %s", err_msg.c_str());
#endif
return signature;
}
mbedtls_pk_context private_key_ctx; // holds the parsed private key
mbedtls_pk_init(&private_key_ctx);
result = mbedtls_pk_parse_key(
&private_key_ctx, // will hold the parsed private key
(const unsigned char *)private_key_content.c_str(),
private_key_content.size() + 1, // we MUST include the null terminator, otherwise this API returns an error!
nullptr, 0, // no password stuff, private key isn't protected
mbedtls_ctr_drbg_random, &ctr_drbg_ctx // random number generation function + the CTR-DRBG context it requires as an input
);
if (result != 0)
{
mbedtls_pk_free(&private_key_ctx);
mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
mbedtls_entropy_free(&entropy_ctx);
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
std::string err_msg(256, 0);
mbedtls_strerror(result, &err_msg[0], err_msg.size());
PRINT_DEBUG("failed to parse private key: %s", err_msg.c_str());
#endif
return signature;
}
// private key must be valid RSA key
if (mbedtls_pk_get_type(&private_key_ctx) != MBEDTLS_PK_RSA || // invalid type
mbedtls_pk_can_do(&private_key_ctx, MBEDTLS_PK_RSA) == 0) // or initialized but not properly setup (maybe freed?)
{
mbedtls_pk_free(&private_key_ctx);
mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
mbedtls_entropy_free(&entropy_ctx);
PRINT_DEBUG("parsed key is not a valid RSA private key");
return signature;
}
// get the underlying RSA context from the parsed private key
mbedtls_rsa_context* rsa_ctx = mbedtls_pk_rsa(private_key_ctx);
// resize the output buffer to accomodate the size of the private key
const size_t private_key_len = mbedtls_pk_get_len(&private_key_ctx);
if (private_key_len == 0) // TODO must be 128 siglen
{
mbedtls_pk_free(&private_key_ctx);
mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
mbedtls_entropy_free(&entropy_ctx);
PRINT_DEBUG("failed to get private key (final buffer) length");
return signature;
}
PRINT_DEBUG("computed private key (final buffer) length = %zu", private_key_len);
signature.resize(private_key_len);
// finally sign the computed hash using RSA and PKCS#1 padding
result = mbedtls_rsa_pkcs1_sign(
rsa_ctx,
mbedtls_ctr_drbg_random, &ctr_drbg_ctx,
MBEDTLS_MD_SHA1, // we used SHA1 to hash the data
sizeof(hash), hash,
signature.data() // output
);
mbedtls_pk_free(&private_key_ctx);
mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
mbedtls_entropy_free(&entropy_ctx);
if (result != 0)
{
signature.clear();
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
std::string err_msg(256, 0);
mbedtls_strerror(result, &err_msg[0], err_msg.size());
PRINT_DEBUG("RSA signing failed: %s", err_msg.c_str());
#endif
}
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(signature);
PRINT_DEBUG("final signature [%zu bytes]:\n %s", signature.size(), str.c_str());
#endif
return signature;
}
struct DLC { struct DLC {
uint32_t AppId; uint32_t AppId{};
std::vector<uint32_t> Licenses; std::vector<uint32_t> Licenses{};
std::vector<uint8_t> Serialize() std::vector<uint8_t> Serialize() const;
{
PRINT_DEBUG("AppId = %u, Licenses count = %zu", AppId, Licenses.size());
// we need this variable because we depend on the sizeof, must be 2 bytes
const uint16_t dlcs_licenses_count = (uint16_t)Licenses.size();
const size_t dlcs_licenses_total_size =
Licenses.size() * sizeof(Licenses[0]); // count * element size
const size_t total_size =
sizeof(AppId) +
sizeof(dlcs_licenses_count) +
dlcs_licenses_total_size;
std::vector<uint8_t> buffer;
buffer.resize(total_size);
uint8_t* pBuffer = buffer.data();
#define SER_VAR(v) \
*reinterpret_cast<std::remove_const<decltype(v)>::type *>(pBuffer) = v; \
pBuffer += sizeof(v)
SER_VAR(AppId);
SER_VAR(dlcs_licenses_count);
for(uint32_t dlc_license : Licenses)
{
SER_VAR(dlc_license);
}
#undef SER_VAR
PRINT_DEBUG("final size = %zu", buffer.size());
return buffer;
}
}; };
struct AppTicketGC { struct AppTicketGC {
uint64_t GCToken; uint64_t GCToken{};
CSteamID id; CSteamID id{};
uint32_t ticketGenDate; //epoch uint32_t ticketGenDate{}; //epoch
uint32_t ExternalIP; uint32_t ExternalIP{};
uint32_t InternalIP; uint32_t InternalIP{};
uint32_t TimeSinceStartup; uint32_t TimeSinceStartup{};
uint32_t TicketGeneratedCount; uint32_t TicketGeneratedCount{};
private: private:
uint32_t one = 1; uint32_t one = 1;
uint32_t two = 2; uint32_t two = 2;
public: public:
std::vector<uint8_t> Serialize() std::vector<uint8_t> Serialize() const;
{
const uint64_t steam_id = id.ConvertToUint64();
// must be 52
constexpr size_t total_size =
sizeof(STEAM_APPTICKET_GCLen) +
sizeof(GCToken) +
sizeof(steam_id) +
sizeof(ticketGenDate) +
sizeof(STEAM_APPTICKET_SESSIONLEN) +
sizeof(one) +
sizeof(two) +
sizeof(ExternalIP) +
sizeof(InternalIP) +
sizeof(TimeSinceStartup) +
sizeof(TicketGeneratedCount);
// check the size at compile time, we must ensure the correct size
#ifndef EMU_RELEASE_BUILD
static_assert(
total_size == 52,
"AUTH::AppTicketGC::SER calculated size of serialized data != 52 bytes, your compiler has some incorrect sizes"
);
#endif
PRINT_DEBUG(
"\n"
" GCToken: " "%" PRIu64 "\n"
" user steam_id: " "%" PRIu64 "\n"
" ticketGenDate: %u\n"
" ExternalIP: 0x%08X, InternalIP: 0x%08X\n"
" TimeSinceStartup: %u, TicketGeneratedCount: %u\n"
" SER size = %zu",
GCToken,
steam_id,
ticketGenDate,
ExternalIP, InternalIP,
TimeSinceStartup, TicketGeneratedCount,
total_size
);
std::vector<uint8_t> buffer;
buffer.resize(total_size);
uint8_t* pBuffer = buffer.data();
#define SER_VAR(v) \
*reinterpret_cast<std::remove_const<decltype(v)>::type *>(pBuffer) = v; \
pBuffer += sizeof(v)
SER_VAR(STEAM_APPTICKET_GCLen);
SER_VAR(GCToken);
SER_VAR(steam_id);
SER_VAR(ticketGenDate);
SER_VAR(STEAM_APPTICKET_SESSIONLEN);
SER_VAR(one);
SER_VAR(two);
SER_VAR(ExternalIP);
SER_VAR(InternalIP);
SER_VAR(TimeSinceStartup);
SER_VAR(TicketGeneratedCount);
#undef SER_VAR
#ifndef EMU_RELEASE_BUILD
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
PRINT_DEBUG("final data [%zu bytes]:\n %s", buffer.size(), str.c_str());
#endif
return buffer;
}
}; };
struct AppTicket { struct AppTicket {
uint32_t Version; uint32_t Version{};
CSteamID id; CSteamID id{};
uint32_t AppId; uint32_t AppId{};
uint32_t ExternalIP; uint32_t ExternalIP{};
uint32_t InternalIP; uint32_t InternalIP{};
uint32_t AlwaysZero = 0; //OwnershipFlags? uint32_t AlwaysZero = 0; //OwnershipFlags?
uint32_t TicketGeneratedDate; uint32_t TicketGeneratedDate{};
uint32_t TicketGeneratedExpireDate; uint32_t TicketGeneratedExpireDate{};
std::vector<uint32_t> Licenses; std::vector<uint32_t> Licenses{};
std::vector<DLC> DLCs; std::vector<DLC> DLCs{};
std::vector<uint8_t> Serialize() std::vector<uint8_t> Serialize() const;
{
const uint64_t steam_id = id.ConvertToUint64();
PRINT_DEBUG(
"\n"
" Version: %u\n"
" user steam_id: " "%" PRIu64 "\n"
" AppId: %u\n"
" ExternalIP: 0x%08X, InternalIP: 0x%08X\n"
" TicketGeneratedDate: %u, TicketGeneratedExpireDate: %u\n"
" Licenses count: %zu, DLCs count: %zu",
Version,
steam_id,
AppId,
ExternalIP, InternalIP,
TicketGeneratedDate, TicketGeneratedExpireDate,
Licenses.size(), DLCs.size()
);
// we need this variable because we depend on the sizeof, must be 2 bytes
const uint16_t licenses_count = (uint16_t)Licenses.size();
const size_t licenses_total_size =
Licenses.size() * sizeof(Licenses[0]); // total count * element size
// we need this variable because we depend on the sizeof, must be 2 bytes
const uint16_t dlcs_count = (uint16_t)DLCs.size();
size_t dlcs_total_size = 0;
std::vector<std::vector<uint8_t>> serialized_dlcs;
for (DLC &dlc : DLCs)
{
auto dlc_ser = dlc.Serialize();
dlcs_total_size += dlc_ser.size();
serialized_dlcs.push_back(dlc_ser);
}
//padding
constexpr uint16_t padding = (uint16_t)0;
// must be 42
constexpr size_t static_fields_size =
sizeof(Version) +
sizeof(steam_id) +
sizeof(AppId) +
sizeof(ExternalIP) +
sizeof(InternalIP) +
sizeof(AlwaysZero) +
sizeof(TicketGeneratedDate) +
sizeof(TicketGeneratedExpireDate) +
sizeof(licenses_count) +
sizeof(dlcs_count) +
sizeof(padding);
// check the size at compile time, we must ensure the correct size
#ifndef EMU_RELEASE_BUILD
static_assert(
static_fields_size == 42,
"AUTH::AppTicket::SER calculated size of serialized data != 42 bytes, your compiler has some incorrect sizes"
);
#endif
const size_t total_size =
static_fields_size +
licenses_total_size +
dlcs_total_size;
PRINT_DEBUG("final size = %zu", total_size);
std::vector<uint8_t> buffer;
buffer.resize(total_size);
uint8_t* pBuffer = buffer.data();
#define SER_VAR(v) \
*reinterpret_cast<std::remove_const<decltype(v)>::type *>(pBuffer) = v; \
pBuffer += sizeof(v)
SER_VAR(Version);
SER_VAR(steam_id);
SER_VAR(AppId);
SER_VAR(ExternalIP);
SER_VAR(InternalIP);
SER_VAR(AlwaysZero);
SER_VAR(TicketGeneratedDate);
SER_VAR(TicketGeneratedExpireDate);
#ifndef EMU_RELEASE_BUILD
{
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
PRINT_DEBUG("(before licenses + DLCs):\n %s", str.c_str());
}
#endif
/*
* layout of licenses:
* ------------------------
* 2 bytes: count of licenses
* ------------------------
* [
* ------------------------
* | 4 bytes: license element
* ------------------------
* ]
*/
SER_VAR(licenses_count);
for(uint32_t license : Licenses)
{
SER_VAR(license);
}
/*
* layout of DLCs:
* ------------------------
* | 2 bytes: count of DLCs
* ------------------------
* [
* ------------------------
* | 4 bytes: app id
* ------------------------
* | 2 bytes: DLC licenses count
* ------------------------
* [
* 4 bytes: DLC license element
* ]
* ]
*/
SER_VAR(dlcs_count);
for (std::vector<uint8_t> &dlc_ser : serialized_dlcs)
{
memcpy(pBuffer, dlc_ser.data(), dlc_ser.size());
pBuffer += dlc_ser.size();
}
//padding
SER_VAR(padding);
#undef SER_VAR
#ifndef EMU_RELEASE_BUILD
{
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
PRINT_DEBUG("final data [%zu bytes]:\n %s", buffer.size(), str.c_str());
}
#endif
return buffer;
}
}; };
struct Auth_Data { struct Auth_Data {
bool HasGC; bool HasGC{};
AppTicketGC GC; AppTicketGC GC{};
AppTicket Ticket; AppTicket Ticket{};
//old data //old data
CSteamID id; CSteamID id{};
uint64_t number; uint64_t number{};
std::chrono::high_resolution_clock::time_point created; std::chrono::high_resolution_clock::time_point created{};
std::vector<uint8_t> Serialize() std::vector<uint8_t> Serialize() const;
{
/*
* layout of Auth_Data with GC:
* ------------------------
* X bytes: GC data blob (currently 52 bytes)
* ------------------------
* 4 bytes: remaining Auth_Data blob size (4 + Y + Z)
* ------------------------
* 4 bytes: size of ticket data layout (not blob!, hence blob + 4)
* ------------------------
* Y bytes: ticket data blob
* ------------------------
* Z bytes: App Ticket signature
* ------------------------
*
* total layout length = X + 4 + 4 + Y + Z
*/
/*
* layout of Auth_Data without GC:
* ------------------------
* 4 bytes: size of ticket data layout (not blob!, hence blob + 4)
* ------------------------
* Y bytes: ticket data blob
* ------------------------
* Z bytes: App Ticket signature
* ------------------------
*
* total layout length = 4 + Y + Z
*/
const uint64_t steam_id = id.ConvertToUint64();
PRINT_DEBUG(
"\n"
" HasGC: %u\n"
" user steam_id: " "%" PRIu64 "\n"
" number: " "%" PRIu64 ,
(int)HasGC,
steam_id,
number
);
/*
* layout of ticket data:
* ------------------------
* 4 bytes: size of ticket data layout (not blob!, hence blob + 4)
* ------------------------
* Y bytes: ticket data blob
* ------------------------
*
* total layout length = 4 + Y
*/
std::vector<uint8_t> tickedData = Ticket.Serialize();
// we need this variable because we depend on the sizeof, must be 4 bytes
const uint32_t ticket_data_layout_length =
sizeof(uint32_t) + // size of this uint32_t because it is included!
(uint32_t)tickedData.size();
size_t total_size_without_siglen = ticket_data_layout_length;
std::vector<uint8_t> GCData;
size_t gc_data_layout_length = 0;
if (HasGC)
{
/*
* layout of GC data:
* ------------------------
* X bytes: GC data blob (currently 52 bytes)
* ------------------------
* 4 bytes: remaining Auth_Data blob size
* ------------------------
*
* total layout length = X + 4
*/
GCData = GC.Serialize();
gc_data_layout_length +=
GCData.size() +
sizeof(uint32_t);
total_size_without_siglen += gc_data_layout_length;
}
const size_t final_buffer_size = total_size_without_siglen + STEAM_APPTICKET_SIGLEN;
PRINT_DEBUG("size without sig len = %zu, size with sig len (final size) = %zu",
total_size_without_siglen,
final_buffer_size
);
std::vector<uint8_t> buffer;
buffer.resize(final_buffer_size);
uint8_t* pBuffer = buffer.data();
#define SER_VAR(v) \
*reinterpret_cast<std::remove_const<decltype(v)>::type *>(pBuffer) = v; \
pBuffer += sizeof(v)
// serialize the GC data first
if (HasGC)
{
memcpy(pBuffer, GCData.data(), GCData.size());
pBuffer += GCData.size();
// when GC data is written (HasGC),
// the next 4 bytes after the GCData will be the length of the remaining data in the final buffer
// i.e. final buffer size - length of GCData layout
// i.e. ticket data length + STEAM_APPTICKET_SIGLEN
//
// notice that we subtract the entire layout length, not just GCData.size(),
// otherwise these next 4 bytes will include themselves!
uint32_t remaining_length = (uint32_t)(final_buffer_size - gc_data_layout_length);
SER_VAR(remaining_length);
}
// serialize the ticket data
SER_VAR(ticket_data_layout_length);
memcpy(pBuffer, tickedData.data(), tickedData.size());
#ifndef EMU_RELEASE_BUILD
{
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
PRINT_DEBUG("final data (before signature) [%zu bytes]:\n %s", buffer.size(), str.c_str());
}
#endif
//Todo make a signature
std::vector<uint8_t> signature = sign_auth_data(app_ticket_key, tickedData, total_size_without_siglen);
if (signature.size() == STEAM_APPTICKET_SIGLEN) {
memcpy(buffer.data() + total_size_without_siglen, signature.data(), signature.size());
#ifndef EMU_RELEASE_BUILD
{
// we nedd a live object until the printf does its job, hence this special handling
auto str = common_helpers::uint8_vector_to_hex_string(buffer);
PRINT_DEBUG("final data (after signature) [%zu bytes]:\n %s", buffer.size(), str.c_str());
}
#endif
} else {
PRINT_DEBUG("signature size [%zu] is invalid", signature.size());
}
#undef SER_VAR
return buffer;
}
}; };
class Auth_Manager { class Auth_Manager {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
std::vector<struct Auth_Data> inbound{};
std::vector<struct Auth_Data> outbound{};
void launch_callback(CSteamID id, EAuthSessionResponse resp, double delay=0); void launch_callback(CSteamID id, EAuthSessionResponse resp, double delay=0);
void launch_callback_gs(CSteamID id, bool approved); void launch_callback_gs(CSteamID id, bool approved);
std::vector<struct Auth_Data> inbound;
std::vector<struct Auth_Data> outbound; static void ticket_callback(void *object, Common_Message *msg);
void Callback(Common_Message *msg);
public: public:
Auth_Manager(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks); Auth_Manager(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks);
~Auth_Manager(); ~Auth_Manager();
void Callback(Common_Message *msg);
HAuthTicket getTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ); HAuthTicket getTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket );
HAuthTicket getWebApiTicket( const char *pchIdentity ); HAuthTicket getWebApiTicket( const char *pchIdentity );
void cancelTicket(uint32 number); void cancelTicket(uint32 number);
EBeginAuthSessionResult beginAuth(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID); EBeginAuthSessionResult beginAuth(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID);
bool endAuth(CSteamID id); bool endAuth(CSteamID id);
uint32 countInboundAuth(); uint32 countInboundAuth();
bool SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser ); bool SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser );
CSteamID fakeUser(); CSteamID fakeUser();
Auth_Data getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ); Auth_Data getTicketData( void *pTicket, int cbMaxTicket, uint32 *pcbTicket );
}; };
#endif // AUTH_INCLUDE #endif // AUTH_INCLUDE_H

View File

@ -15,17 +15,14 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef BASE_INCLUDE #ifndef BASE_INCLUDE_H
#define BASE_INCLUDE #define BASE_INCLUDE_H
#include "common_includes.h" #include "common_includes.h"
#include "callsystem.h"
#define PUSH_BACK_IF_NOT_IN(vector, element) { if(std::find(vector.begin(), vector.end(), element) == vector.end()) vector.push_back(element); } #define PUSH_BACK_IF_NOT_IN(vector, element) { if(std::find(vector.begin(), vector.end(), element) == vector.end()) vector.push_back(element); }
#define STEAM_CALLRESULT_TIMEOUT 120.0
#define STEAM_CALLRESULT_WAIT_FOR_CB 0.01
#define DEFAULT_CB_TIMEOUT 0.002
extern std::recursive_mutex global_mutex; extern std::recursive_mutex global_mutex;
extern const std::chrono::time_point<std::chrono::high_resolution_clock> startup_counter; extern const std::chrono::time_point<std::chrono::high_resolution_clock> startup_counter;
@ -34,70 +31,13 @@ extern const std::chrono::time_point<std::chrono::system_clock> startup_time;
void randombytes(char *buf, size_t size); void randombytes(char *buf, size_t size);
std::string get_env_variable(const std::string &name); std::string get_env_variable(const std::string &name);
bool set_env_variable(const std::string &name, const std::string &value); bool set_env_variable(const std::string &name, const std::string &value);
/// @brief Check for a timeout given some initial timepoint and a timeout in sec.
/// @param old The initial timepoint which will be compared against current time
/// @param timeout The max allowed time in seconds
/// @return true if the timepoint has exceeded the max allowed timeout, false otherwise
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout); 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;
};
};
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{};
};
unsigned generate_account_id(); unsigned generate_account_id();
CSteamID generate_steam_anon_user(); CSteamID generate_steam_anon_user();
SteamAPICall_t generate_steam_api_call_id(); SteamAPICall_t generate_steam_api_call_id();
@ -112,334 +52,10 @@ std::string canonical_path(const std::string &path);
bool file_exists_(const std::string &full_path); bool file_exists_(const std::string &full_path);
unsigned int file_size_(const std::string &full_path); unsigned int file_size_(const std::string &full_path);
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) {
PRINT_DEBUG("%i", iCallback);
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("ERROR");
return k_uAPICallInvalid;
}
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", 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");
}
}
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", 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("erase to_delete");
auto c = std::begin(callresults);
while (c != std::end(callresults)) {
if (c->to_delete) {
if (c->timed_out()) {
PRINT_DEBUG("removed callresult %i", c->iCallback);
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("%i", 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 RunCBs {
void (*function)(void *object) = nullptr;
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); void set_whitelist_ips(uint32_t *from, uint32_t *to, unsigned num_ips);
#ifdef EMU_EXPERIMENTAL_BUILD #ifdef EMU_EXPERIMENTAL_BUILD
bool crack_SteamAPI_RestartAppIfNecessary(uint32 unOwnAppID); bool crack_SteamAPI_RestartAppIfNecessary(uint32 unOwnAppID);
bool crack_SteamAPI_Init(); bool crack_SteamAPI_Init();
#endif #endif
#endif #endif // BASE_INCLUDE_H

132
dll/dll/callsystem.h Normal file
View File

@ -0,0 +1,132 @@
/* 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_CALLSYSTEM_H__
#define __INCLUDED_CALLSYSTEM_H__
#include "common_includes.h"
#define DEFAULT_CB_TIMEOUT 0.002
#define STEAM_CALLRESULT_TIMEOUT 120.0
#define STEAM_CALLRESULT_WAIT_FOR_CB 0.01
class CCallbackMgr
{
public:
static void SetRegister(class CCallbackBase *pCallback, int iCallback);
static void SetUnregister(class CCallbackBase *pCallback);
static bool isServer(class CCallbackBase *pCallback);
};
struct Steam_Call_Result {
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{};
Steam_Call_Result(SteamAPICall_t a, int icb, void *r, unsigned int s, double r_in, bool run_cc_cb);
bool operator==(const struct Steam_Call_Result& other) const;
bool timed_out() const;
bool call_completed() const;
bool can_execute() const;
bool has_cb() const;
};
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);
void rmCallCompleted(class CCallbackBase *cb);
void addCallBack(SteamAPICall_t api_call, class CCallbackBase *cb);
bool exists(SteamAPICall_t api_call) const;
bool callback_result(SteamAPICall_t api_call, void *copy_to, unsigned int size);
void rmCallBack(SteamAPICall_t api_call, class CCallbackBase *cb);
void rmCallBack(class CCallbackBase *cb);
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);
SteamAPICall_t reserveCallResult();
SteamAPICall_t addCallResult(int iCallback, void *result, unsigned int size, double timeout=DEFAULT_CB_TIMEOUT, bool run_call_completed_cb=true);
void setCbAll(void (*cb_all)(std::vector<char> result, int callback));
void runCallResults();
};
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);
void addCallBack(int iCallback, class CCallbackBase *cb);
void addCBResult(int iCallback, void *result, unsigned int size, double timeout, bool dont_post_if_already);
void addCBResult(int iCallback, void *result, unsigned int size);
void addCBResult(int iCallback, void *result, unsigned int size, bool dont_post_if_already);
void addCBResult(int iCallback, void *result, unsigned int size, double timeout);
void rmCallBack(int iCallback, class CCallbackBase *cb);
void runCallBacks();
};
struct RunCBs {
void (*function)(void *object) = nullptr;
void *object{};
};
class RunEveryRunCB {
std::vector<struct RunCBs> cbs{};
public:
void add(void (*cb)(void *object), void *object);
void remove(void (*cb)(void *object), void *object);
void run() const;
};
#endif // __INCLUDED_CALLSYSTEM_H__

View File

@ -244,4 +244,4 @@ static inline void reset_LastError()
#define LOBBY_CONNECT_APPID ((uint32)-2) #define LOBBY_CONNECT_APPID ((uint32)-2)
#endif//__INCLUDED_COMMON_INCLUDES__ #endif //__INCLUDED_COMMON_INCLUDES__

View File

@ -15,6 +15,9 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_DLL_H__
#define __INCLUDED_DLL_H__
#include "steam_client.h" #include "steam_client.h"
#ifdef STEAMCLIENT_DLL #ifdef STEAMCLIENT_DLL
@ -32,3 +35,5 @@ HSteamUser flat_hsteamuser();
HSteamPipe flat_hsteampipe(); HSteamPipe flat_hsteampipe();
HSteamUser flat_gs_hsteamuser(); HSteamUser flat_gs_hsteamuser();
HSteamPipe flat_gs_hsteampipe(); HSteamPipe flat_gs_hsteampipe();
#endif // __INCLUDED_DLL_H__

View File

@ -15,18 +15,17 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef LOCAL_STORAGE_INCLUDE #ifndef LOCAL_STORAGE_INCLUDE_H
#define LOCAL_STORAGE_INCLUDE #define LOCAL_STORAGE_INCLUDE_H
#include "base.h" #include "base.h"
#define MAX_FILENAME_LENGTH 300 #define MAX_FILENAME_LENGTH 300
union image_pixel_t union image_pixel_t {
{
uint32_t pixel; uint32_t pixel;
struct pixel_channels_t
{ struct pixel_channels_t {
uint8_t r; uint8_t r;
uint8_t g; uint8_t g;
uint8_t b; uint8_t b;
@ -34,11 +33,10 @@ union image_pixel_t
} channels; } channels;
}; };
struct image_t struct image_t {
{ size_t width{};
size_t width; size_t height{};
size_t height; std::vector<image_pixel_t> pix_map{};
std::vector<image_pixel_t> pix_map;
}; };
class Local_Storage { class Local_Storage {
@ -105,4 +103,4 @@ public:
static std::string desanitize_string(std::string name); static std::string desanitize_string(std::string name);
}; };
#endif #endif // LOCAL_STORAGE_INCLUDE_H

View File

@ -21,13 +21,6 @@
#include "base.h" #include "base.h"
#include <curl/curl.h> #include <curl/curl.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 #define DEFAULT_PORT 47584
#if defined(STEAM_WIN32) #if defined(STEAM_WIN32)
@ -36,9 +29,18 @@ typedef unsigned int sock_t;
typedef int sock_t; typedef int sock_t;
#endif #endif
static 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());
}
struct IP_PORT { struct IP_PORT {
uint32 ip; uint32 ip{};
uint16 port; uint16 port{};
bool operator <(const IP_PORT& other) const bool operator <(const IP_PORT& other) const
{ {
return (ip < other.ip) || (ip == other.ip && port < other.port); return (ip < other.ip) || (ip == other.ip && port < other.port);
@ -46,9 +48,9 @@ struct IP_PORT {
}; };
struct Network_Callback { struct Network_Callback {
void (*message_callback)(void *object, Common_Message *msg); void (*message_callback)(void *object, Common_Message *msg) = nullptr;
void *object; void *object{};
CSteamID steam_id; CSteamID steam_id{};
}; };
enum Callback_Ids { enum Callback_Ids {
@ -73,39 +75,33 @@ struct Network_Callback_Container {
}; };
struct TCP_Socket { struct TCP_Socket {
sock_t sock = ~0; sock_t sock = static_cast<sock_t>(~0);
bool received_data = false; bool received_data = false;
std::vector<char> recv_buffer; std::vector<char> recv_buffer{};
std::vector<char> send_buffer; std::vector<char> send_buffer{};
std::chrono::high_resolution_clock::time_point last_heartbeat_sent, last_heartbeat_received; std::chrono::high_resolution_clock::time_point last_heartbeat_sent{}, last_heartbeat_received{};
}; };
struct Connection { struct Connection {
struct TCP_Socket tcp_socket_outgoing, tcp_socket_incoming; struct TCP_Socket tcp_socket_outgoing{}, tcp_socket_incoming{};
bool connected = false; bool connected = false;
IP_PORT udp_ip_port; IP_PORT udp_ip_port{};
bool udp_pinged = false; bool udp_pinged = false;
IP_PORT tcp_ip_port; IP_PORT tcp_ip_port{};
std::vector<CSteamID> ids; std::vector<CSteamID> ids{};
uint32 appid; uint32 appid{};
std::chrono::high_resolution_clock::time_point last_received; std::chrono::high_resolution_clock::time_point last_received{};
}; };
class Networking { class Networking
{
bool enabled = false; bool enabled = false;
bool query_alive; bool query_alive{};
std::chrono::high_resolution_clock::time_point last_run; std::chrono::high_resolution_clock::time_point last_run{};
sock_t query_socket, udp_socket, tcp_socket; sock_t query_socket, udp_socket{}, tcp_socket{};
uint16 udp_port, tcp_port; uint16 udp_port{}, tcp_port{};
uint32 own_ip; uint32 own_ip{};
std::vector<struct Connection> connections; 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; std::vector<CSteamID> ids;
uint32 appid; uint32 appid;
@ -118,18 +114,30 @@ class Networking {
struct Network_Callback_Container callbacks[CALLBACK_IDS_MAX]; struct Network_Callback_Container callbacks[CALLBACK_IDS_MAX];
std::vector<Common_Message> local_send; std::vector<Common_Message> local_send;
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();
bool add_id_connection(struct Connection *connection, CSteamID steam_id); bool add_id_connection(struct Connection *connection, CSteamID steam_id);
void run_callbacks(Callback_Ids id, Common_Message *msg); void run_callbacks(Callback_Ids id, Common_Message *msg);
void run_callback_user(CSteamID steam_id, bool online, uint32 appid); void run_callback_user(CSteamID steam_id, bool online, uint32 appid);
void do_callbacks_message(Common_Message *msg); void do_callbacks_message(Common_Message *msg);
Common_Message create_announce(bool request); Common_Message create_announce(bool request);
public: public:
Networking(CSteamID id, uint32 appid, uint16 port, std::set<IP_PORT> *custom_broadcasts, bool disable_sockets);
~Networking();
//NOTE: for all functions ips/ports are passed/returned in host byte order //NOTE: for all functions ips/ports are passed/returned in host byte order
//ex: 127.0.0.1 should be passed as 0x7F000001 //ex: 127.0.0.1 should be passed as 0x7F000001
static std::set<IP_PORT> resolve_ip(std::string dns); 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);
~Networking();
void addListenId(CSteamID id); void addListenId(CSteamID id);
void setAppID(uint32 appid); void setAppID(uint32 appid);
void Run(); void Run();
@ -161,4 +169,4 @@ public:
bool isQueryAlive(); bool isQueryAlive();
}; };
#endif #endif // NETWORK_INCLUDE_H

View File

@ -15,54 +15,57 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef SETTINGS_INCLUDE #ifndef SETTINGS_INCLUDE_H
#define SETTINGS_INCLUDE #define SETTINGS_INCLUDE_H
#include "base.h" #include "base.h"
struct IP_PORT; struct IP_PORT;
struct DLC_entry { struct DLC_entry {
AppId_t appID; AppId_t appID{};
std::string name; std::string name{};
bool available; bool available{};
}; };
struct Mod_entry { struct Mod_entry {
PublishedFileId_t id; PublishedFileId_t id{};
std::string title; std::string title{};
std::string path; std::string path{};
std::string previewURL{};
EWorkshopFileType fileType{};
std::string description{};
uint64 steamIDOwner{};
uint32 timeCreated{};
uint32 timeUpdated{};
uint32 timeAddedToUserList{};
ERemoteStoragePublishedFileVisibility visibility{};
bool banned = false;
bool acceptedForUse{};
bool tagsTruncated{};
std::string tags{};
std::string previewURL;
EWorkshopFileType fileType;
std::string description;
uint64 steamIDOwner;
uint32 timeCreated;
uint32 timeUpdated;
uint32 timeAddedToUserList;
ERemoteStoragePublishedFileVisibility visibility;
bool banned;
bool acceptedForUse;
bool tagsTruncated;
std::string tags;
// file/url information // file/url information
UGCHandle_t handleFile = generate_file_handle(); UGCHandle_t handleFile = generate_file_handle();
UGCHandle_t handlePreviewFile = generate_file_handle(); UGCHandle_t handlePreviewFile = generate_file_handle();
std::string primaryFileName;
int32 primaryFileSize; std::string primaryFileName{};
std::string previewFileName; int32 primaryFileSize{};
int32 previewFileSize; std::string previewFileName{};
std::string workshopItemURL; int32 previewFileSize{};
std::string workshopItemURL{};
// voting information // voting information
uint32 votesUp; uint32 votesUp{};
uint32 votesDown; uint32 votesDown{};
float score; float score{};
// collection details // collection details
uint32 numChildren; uint32 numChildren{}; // TODO
private: private:
UGCHandle_t generate_file_handle() UGCHandle_t generate_file_handle() {
{
static UGCHandle_t val = 0; static UGCHandle_t val = 0;
++val; ++val;
@ -74,12 +77,12 @@ private:
}; };
struct Leaderboard_config { struct Leaderboard_config {
enum ELeaderboardSortMethod sort_method; enum ELeaderboardSortMethod sort_method{};
enum ELeaderboardDisplayType display_type; enum ELeaderboardDisplayType display_type{};
}; };
struct Stat_config { struct Stat_config {
GameServerStats_Messages::StatInfo::Stat_Type type; GameServerStats_Messages::StatInfo::Stat_Type type{};
union { union {
float default_value_float; float default_value_float;
int32 default_value_int; int32 default_value_int;
@ -87,21 +90,21 @@ struct Stat_config {
}; };
struct Image_Data { struct Image_Data {
uint32 width; uint32 width{};
uint32 height; uint32 height{};
std::string data; std::string data{};
}; };
struct Controller_Settings { 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::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::string> action_set_layer_parents{};
std::map<std::string, std::map<std::string, std::pair<std::set<std::string>, std::string>>> action_set_layers; std::map<std::string, std::map<std::string, std::pair<std::set<std::string>, std::string>>> action_set_layers{};
}; };
struct Group_Clans { struct Group_Clans {
CSteamID id; CSteamID id{};
std::string name; std::string name{};
std::string tag; std::string tag{};
}; };
struct Overlay_Appearance { struct Overlay_Appearance {
@ -112,7 +115,7 @@ struct Overlay_Appearance {
constexpr const static NotificationPosition default_pos = NotificationPosition::top_right; constexpr const static NotificationPosition default_pos = NotificationPosition::top_right;
std::string font_override{}; // path to a custom user-provided font std::string font_override{}; // path to a custom user-provided TTF font
float font_size = 16.0f; float font_size = 16.0f;
float icon_size = 64.0f; float icon_size = 64.0f;
@ -157,23 +160,12 @@ struct Overlay_Appearance {
NotificationPosition invite_pos = default_pos; // lobby/game invitation NotificationPosition invite_pos = default_pos; // lobby/game invitation
NotificationPosition chat_msg_pos = NotificationPosition::top_center; // chat message from a friend NotificationPosition chat_msg_pos = NotificationPosition::top_center; // chat message from a friend
static NotificationPosition translate_notification_position(const std::string &str) static NotificationPosition translate_notification_position(const std::string &str);
{
if (str == "top_left") return NotificationPosition::top_left;
else if (str == "top_center") return NotificationPosition::top_center;
else if (str == "top_right") return NotificationPosition::top_right;
else if (str == "bot_left") return NotificationPosition::bot_left;
else if (str == "bot_center") return NotificationPosition::bot_center;
else if (str == "bot_right") return NotificationPosition::bot_right;
PRINT_DEBUG("Invalid position '%s'", str.c_str());
return default_pos;
}
}; };
class Settings { class Settings {
CSteamID steam_id; // user id CSteamID steam_id{}; // user id
CGameID game_id; CGameID game_id{};
std::string name{}; std::string name{};
std::string language{}; // default "english" std::string language{}; // default "english"
CSteamID lobby_id = k_steamIDNil; CSteamID lobby_id = k_steamIDNil;
@ -294,17 +286,21 @@ public:
#ifdef LOBBY_CONNECT #ifdef LOBBY_CONNECT
static const bool is_lobby_connect = true; static constexpr const bool is_lobby_connect = true;
#else #else
static const bool is_lobby_connect = false; static constexpr const bool is_lobby_connect = false;
#endif #endif
static std::string sanitize(const std::string &name);
Settings(CSteamID steam_id, CGameID game_id, const std::string &name, const std::string &language, bool offline); Settings(CSteamID steam_id, CGameID game_id, const std::string &name, const std::string &language, bool offline);
static std::string sanitize(const std::string &name);
CSteamID get_local_steam_id(); CSteamID get_local_steam_id();
CGameID get_local_game_id(); CGameID get_local_game_id();
const char *get_local_name(); const char *get_local_name();
void set_local_name(const char *name); void set_local_name(const char *name);
const char *get_language(); const char *get_language();
void set_language(const char *language); void set_language(const char *language);
@ -313,9 +309,12 @@ public:
const std::string& get_supported_languages() const; const std::string& get_supported_languages() const;
void set_game_id(CGameID game_id); void set_game_id(CGameID game_id);
void set_lobby(CSteamID lobby_id); void set_lobby(CSteamID lobby_id);
CSteamID get_lobby(); CSteamID get_lobby();
bool is_offline(); bool is_offline();
uint16 get_port(); uint16 get_port();
void set_port(uint16 port); void set_port(uint16 port);
@ -361,4 +360,4 @@ public:
}; };
#endif #endif // SETTINGS_INCLUDE_H

View File

@ -15,10 +15,10 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#include "settings.h" #ifndef SETTINGS_PARSER_INCLUDE_H
#define SETTINGS_PARSER_INCLUDE_H
#ifndef SETTINGS_PARSER_INCLUDE #include "settings.h"
#define SETTINGS_PARSER_INCLUDE
//returns game appid //returns game appid
uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out); uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out);
@ -55,4 +55,4 @@ enum class SettingsItf {
const std::map<SettingsItf, std::string>& settings_old_interfaces(); const std::map<SettingsItf, std::string>& settings_old_interfaces();
#endif #endif // SETTINGS_PARSER_INCLUDE_H

View File

@ -15,14 +15,18 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_SOURCE_QUERY_H__
#define __INCLUDED_SOURCE_QUERY_H__
#include "base.h" #include "base.h"
class Source_Query class Source_Query
{ {
Source_Query () = delete; Source_Query() = delete;
~Source_Query() = delete; ~Source_Query() = delete;
public: public:
static std::vector<uint8_t> handle_source_query(const void* buffer, size_t len, Gameserver const& gs); static std::vector<uint8_t> handle_source_query(const void* buffer, size_t len, Gameserver const& gs);
}; };
#endif // __INCLUDED_SOURCE_QUERY_H__

View File

@ -15,6 +15,9 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_HTMLSURFACE_H__
#define __INCLUDED_STEAM_HTMLSURFACE_H__
#include "base.h" #include "base.h"
class Steam_HTMLsurface : class Steam_HTMLsurface :
@ -24,354 +27,166 @@ public ISteamHTMLSurface003,
public ISteamHTMLSurface004, public ISteamHTMLSurface004,
public ISteamHTMLSurface public ISteamHTMLSurface
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
public: public:
Steam_HTMLsurface(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks) 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 // Must call init and shutdown when starting/ending use of the interface
bool Init() bool Init();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
bool Shutdown() bool Shutdown();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
// Create a browser object for display of a html page, when creation is complete the call handle // 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. // 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 // 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. // identify your client on web servers.
// The userCSS string lets you apply a CSS style sheet to every displayed page, leave null if // The userCSS string lets you apply a CSS style sheet to every displayed page, leave null if
// you do not require this functionality. // you do not require this functionality.
// //
// YOU MUST HAVE IMPLEMENTED HANDLERS FOR HTML_BrowserReady_t, HTML_StartRequest_t, // 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 // 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 // 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 // not implement these callback handlers, the browser may appear to hang instead of
// navigating to new pages or triggering javascript popups. // navigating to new pages or triggering javascript popups.
// //
STEAM_CALL_RESULT( HTML_BrowserReady_t ) STEAM_CALL_RESULT( HTML_BrowserReady_t )
SteamAPICall_t CreateBrowser( const char *pchUserAgent, const char *pchUserCSS ) SteamAPICall_t CreateBrowser( const char *pchUserAgent, const char *pchUserCSS );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
HTML_BrowserReady_t data;
data.unBrowserHandle = 1234869;
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
}
// Call this when you are done with a html surface, this lets us free the resources being used by it // Call this when you are done with a html surface, this lets us free the resources being used by it
void RemoveBrowser( HHTMLBrowser unBrowserHandle ) void RemoveBrowser( HHTMLBrowser unBrowserHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Navigate to this URL, results in a HTML_StartRequest_t as the request commences // Navigate to this URL, results in a HTML_StartRequest_t as the request commences
void LoadURL( HHTMLBrowser unBrowserHandle, const char *pchURL, const char *pchPostData ) void LoadURL( HHTMLBrowser unBrowserHandle, const char *pchURL, const char *pchPostData );
{
PRINT_DEBUG("TODO %s %s", 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 // Tells the surface the size in pixels to display the surface
void SetSize( HHTMLBrowser unBrowserHandle, uint32 unWidth, uint32 unHeight ) void SetSize( HHTMLBrowser unBrowserHandle, uint32 unWidth, uint32 unHeight );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Stop the load of the current html page // Stop the load of the current html page
void StopLoad( HHTMLBrowser unBrowserHandle ) void StopLoad( HHTMLBrowser unBrowserHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Reload (most likely from local cache) the current page // Reload (most likely from local cache) the current page
void Reload( HHTMLBrowser unBrowserHandle ) void Reload( HHTMLBrowser unBrowserHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// navigate back in the page history // navigate back in the page history
void GoBack( HHTMLBrowser unBrowserHandle ) void GoBack( HHTMLBrowser unBrowserHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// navigate forward in the page history // navigate forward in the page history
void GoForward( HHTMLBrowser unBrowserHandle ) void GoForward( HHTMLBrowser unBrowserHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// add this header to any url requests from this browser // add this header to any url requests from this browser
void AddHeader( HHTMLBrowser unBrowserHandle, const char *pchKey, const char *pchValue ) void AddHeader( HHTMLBrowser unBrowserHandle, const char *pchKey, const char *pchValue );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// run this javascript script in the currently loaded page // run this javascript script in the currently loaded page
void ExecuteJavascript( HHTMLBrowser unBrowserHandle, const char *pchScript ) void ExecuteJavascript( HHTMLBrowser unBrowserHandle, const char *pchScript );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Mouse click and mouse movement commands // Mouse click and mouse movement commands
void MouseUp( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton ) void MouseUp( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void MouseDown( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton ) void MouseDown( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void MouseDoubleClick( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton ) void MouseDoubleClick( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// x and y are relative to the HTML bounds // x and y are relative to the HTML bounds
void MouseMove( HHTMLBrowser unBrowserHandle, int x, int y ) void MouseMove( HHTMLBrowser unBrowserHandle, int x, int y );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// nDelta is pixels of scroll // nDelta is pixels of scroll
void MouseWheel( HHTMLBrowser unBrowserHandle, int32 nDelta ) void MouseWheel( HHTMLBrowser unBrowserHandle, int32 nDelta );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// keyboard interactions, native keycode is the key code value from your OS // keyboard interactions, native keycode is the key code value from your OS
void KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers, bool bIsSystemKey = false ) void KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers, bool bIsSystemKey = false );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers) void KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers);
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
KeyDown(unBrowserHandle, nNativeKeyCode, eHTMLKeyModifiers, false);
}
void KeyUp( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers ) void KeyUp( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers );
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// cUnicodeChar is the unicode character point for this keypress (and potentially multiple chars per press) // cUnicodeChar is the unicode character point for this keypress (and potentially multiple chars per press)
void KeyChar( HHTMLBrowser unBrowserHandle, uint32 cUnicodeChar, EHTMLKeyModifiers eHTMLKeyModifiers ) void KeyChar( HHTMLBrowser unBrowserHandle, uint32 cUnicodeChar, EHTMLKeyModifiers eHTMLKeyModifiers );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// programmatically scroll this many pixels on the page // programmatically scroll this many pixels on the page
void SetHorizontalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll ) void SetHorizontalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void SetVerticalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll ) void SetVerticalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// tell the html control if it has key focus currently, controls showing the I-beam cursor in text controls amongst other things // 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 ) void SetKeyFocus( HHTMLBrowser unBrowserHandle, bool bHasKeyFocus );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// open the current pages html code in the local editor of choice, used for debugging // open the current pages html code in the local editor of choice, used for debugging
void ViewSource( HHTMLBrowser unBrowserHandle ) void ViewSource( HHTMLBrowser unBrowserHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// copy the currently selected text on the html page to the local clipboard // copy the currently selected text on the html page to the local clipboard
void CopyToClipboard( HHTMLBrowser unBrowserHandle ) void CopyToClipboard( HHTMLBrowser unBrowserHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// paste from the local clipboard to the current html page // paste from the local clipboard to the current html page
void PasteFromClipboard( HHTMLBrowser unBrowserHandle ) void PasteFromClipboard( HHTMLBrowser unBrowserHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// find this string in the browser, if bCurrentlyInFind is true then instead cycle to the next matching element // 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 ) void Find( HHTMLBrowser unBrowserHandle, const char *pchSearchStr, bool bCurrentlyInFind, bool bReverse );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// cancel a currently running find // cancel a currently running find
void StopFind( HHTMLBrowser unBrowserHandle ) void StopFind( HHTMLBrowser unBrowserHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// return details about the link at position x,y on the current page // return details about the link at position x,y on the current page
void GetLinkAtPosition( HHTMLBrowser unBrowserHandle, int x, int y ) void GetLinkAtPosition( HHTMLBrowser unBrowserHandle, int x, int y );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// set a webcookie for the hostname in question // 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 ) void SetCookie( const char *pchHostname, const char *pchKey, const char *pchValue, const char *pchPath, RTime32 nExpires, bool bSecure, bool bHTTPOnly );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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) // 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 ) void SetPageScaleFactor( HHTMLBrowser unBrowserHandle, float flZoom, int nPointX, int nPointY );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Enable/disable low-resource background mode, where javascript and repaint timers are throttled, resources are // 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, // 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". // 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()". // When background mode is disabled, any video or audio objects with that property will resume with ".play()".
void SetBackgroundMode( HHTMLBrowser unBrowserHandle, bool bBackgroundMode ) void SetBackgroundMode( HHTMLBrowser unBrowserHandle, bool bBackgroundMode );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Scale the output display space by this factor, this is useful when displaying content on high dpi devices. // 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. // Specifies the ratio between physical and logical pixels.
void SetDPIScalingFactor( HHTMLBrowser unBrowserHandle, float flDPIScaling ) void SetDPIScalingFactor( HHTMLBrowser unBrowserHandle, float flDPIScaling );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void OpenDeveloperTools( HHTMLBrowser unBrowserHandle ) void OpenDeveloperTools( HHTMLBrowser unBrowserHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// CALLBACKS // CALLBACKS
// //
// These set of functions are used as responses to callback requests // These set of functions are used as responses to callback requests
// //
// You MUST call this in response to a HTML_StartRequest_t callback // 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 // 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 // on the current page. You can use this feature to limit the valid pages
// allowed in your HTML surface. // allowed in your HTML surface.
void AllowStartRequest( HHTMLBrowser unBrowserHandle, bool bAllowed ) void AllowStartRequest( HHTMLBrowser unBrowserHandle, bool bAllowed );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// You MUST call this in response to a HTML_JSAlert_t or HTML_JSConfirm_t callback // 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 // Set bResult to true for the OK option of a confirm, use false otherwise
void JSDialogResponse( HHTMLBrowser unBrowserHandle, bool bResult ) void JSDialogResponse( HHTMLBrowser unBrowserHandle, bool bResult );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// You MUST call this in response to a HTML_FileOpenDialog_t callback // You MUST call this in response to a HTML_FileOpenDialog_t callback
STEAM_IGNOREATTR() STEAM_IGNOREATTR()
void FileLoadDialogResponse( HHTMLBrowser unBrowserHandle, const char **pchSelectedFiles ) void FileLoadDialogResponse( HHTMLBrowser unBrowserHandle, const char **pchSelectedFiles );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
}; };
#endif // __INCLUDED_STEAM_HTMLSURFACE_H__

View File

@ -15,9 +15,13 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_APPLIST_H__
#define __INCLUDED_STEAM_APPLIST_H__
#include "base.h" #include "base.h"
class Steam_Applist : public ISteamAppList class Steam_Applist :
public ISteamAppList
{ {
public: public:
uint32 GetNumInstalledApps(); uint32 GetNumInstalledApps();
@ -28,3 +32,5 @@ public:
int GetAppBuildId( AppId_t nAppID ); // return the buildid of this app, may change at any time based on backend updates to the game int GetAppBuildId( AppId_t nAppID ); // return the buildid of this app, may change at any time based on backend updates to the game
}; };
#endif // __INCLUDED_STEAM_APPLIST_H__

View File

@ -1,3 +1,23 @@
/* 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_STEAM_APPS_H__
#define __INCLUDED_STEAM_APPS_H__
#include "base.h" #include "base.h"
class Steam_Apps : class Steam_Apps :
@ -10,7 +30,7 @@ public ISteamApps006,
public ISteamApps007, public ISteamApps007,
public ISteamApps public ISteamApps
{ {
Settings *settings{}; class Settings *settings{};
class SteamCallResults *callback_results{}; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks{}; class SteamCallBacks *callbacks{};
@ -115,3 +135,5 @@ public:
// set current DLC AppID being played (or 0 if none). Allows Steam to track usage of major DLC extensions // set current DLC AppID being played (or 0 if none). Allows Steam to track usage of major DLC extensions
bool SetDlcContext( AppId_t nAppID ); bool SetDlcContext( AppId_t nAppID );
}; };
#endif // __INCLUDED_STEAM_APPS_H__

View File

@ -15,6 +15,9 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_CLIENT_H__
#define __INCLUDED_STEAM_CLIENT_H__
#include "base.h" #include "base.h"
#include "appticket.h" #include "appticket.h"
#include "steam_user.h" #include "steam_user.h"
@ -79,78 +82,81 @@ public ISteamClient020,
public ISteamClient public ISteamClient
{ {
public: public:
Networking *network; Networking *network{};
SteamCallResults *callback_results_server, *callback_results_client; SteamCallResults *callback_results_server{}, *callback_results_client{};
SteamCallBacks *callbacks_server, *callbacks_client; SteamCallBacks *callbacks_server{}, *callbacks_client{};
Settings *settings_client, *settings_server; Settings *settings_client{}, *settings_server{};
Local_Storage *local_storage; Local_Storage *local_storage{};
RunEveryRunCB *run_every_runcb; RunEveryRunCB *run_every_runcb{};
Ugc_Remote_Storage_Bridge *ugc_bridge; Ugc_Remote_Storage_Bridge *ugc_bridge{};
Steam_User *steam_user; Steam_User *steam_user{};
Steam_Friends *steam_friends; Steam_Friends *steam_friends{};
Steam_Utils *steam_utils; Steam_Utils *steam_utils{};
Steam_Matchmaking *steam_matchmaking; Steam_Matchmaking *steam_matchmaking{};
Steam_Matchmaking_Servers *steam_matchmaking_servers; Steam_Matchmaking_Servers *steam_matchmaking_servers{};
Steam_User_Stats *steam_user_stats; Steam_User_Stats *steam_user_stats{};
Steam_Apps *steam_apps; Steam_Apps *steam_apps{};
Steam_Networking *steam_networking; Steam_Networking *steam_networking{};
Steam_Remote_Storage *steam_remote_storage; Steam_Remote_Storage *steam_remote_storage{};
Steam_Screenshots *steam_screenshots; Steam_Screenshots *steam_screenshots{};
Steam_HTTP *steam_http; Steam_HTTP *steam_http{};
Steam_Controller *steam_controller; Steam_Controller *steam_controller{};
Steam_UGC *steam_ugc; Steam_UGC *steam_ugc{};
Steam_Applist *steam_applist; Steam_Applist *steam_applist{};
Steam_Music *steam_music; Steam_Music *steam_music{};
Steam_MusicRemote *steam_musicremote; Steam_MusicRemote *steam_musicremote{};
Steam_HTMLsurface *steam_HTMLsurface; Steam_HTMLsurface *steam_HTMLsurface{};
Steam_Inventory *steam_inventory; Steam_Inventory *steam_inventory{};
Steam_Video *steam_video; Steam_Video *steam_video{};
Steam_Parental *steam_parental; Steam_Parental *steam_parental{};
Steam_Networking_Sockets *steam_networking_sockets; Steam_Networking_Sockets *steam_networking_sockets{};
Steam_Networking_Sockets_Serialized *steam_networking_sockets_serialized; Steam_Networking_Sockets_Serialized *steam_networking_sockets_serialized{};
Steam_Networking_Messages *steam_networking_messages; Steam_Networking_Messages *steam_networking_messages{};
Steam_Game_Coordinator *steam_game_coordinator; Steam_Game_Coordinator *steam_game_coordinator{};
Steam_Networking_Utils *steam_networking_utils; Steam_Networking_Utils *steam_networking_utils{};
Steam_Unified_Messages *steam_unified_messages; Steam_Unified_Messages *steam_unified_messages{};
Steam_Game_Search *steam_game_search; Steam_Game_Search *steam_game_search{};
Steam_Parties *steam_parties; Steam_Parties *steam_parties{};
Steam_RemotePlay *steam_remoteplay; Steam_RemotePlay *steam_remoteplay{};
Steam_TV *steam_tv; Steam_TV *steam_tv{};
Steam_GameServer *steam_gameserver; Steam_GameServer *steam_gameserver{};
Steam_Utils *steam_gameserver_utils; Steam_Utils *steam_gameserver_utils{};
Steam_GameServerStats *steam_gameserverstats; Steam_GameServerStats *steam_gameserverstats{};
Steam_Networking *steam_gameserver_networking; Steam_Networking *steam_gameserver_networking{};
Steam_HTTP *steam_gameserver_http; Steam_HTTP *steam_gameserver_http{};
Steam_Inventory *steam_gameserver_inventory; Steam_Inventory *steam_gameserver_inventory{};
Steam_UGC *steam_gameserver_ugc; Steam_UGC *steam_gameserver_ugc{};
Steam_Apps *steam_gameserver_apps; Steam_Apps *steam_gameserver_apps{};
Steam_Networking_Sockets *steam_gameserver_networking_sockets; Steam_Networking_Sockets *steam_gameserver_networking_sockets{};
Steam_Networking_Sockets_Serialized *steam_gameserver_networking_sockets_serialized; Steam_Networking_Sockets_Serialized *steam_gameserver_networking_sockets_serialized{};
Steam_Networking_Messages *steam_gameserver_networking_messages; Steam_Networking_Messages *steam_gameserver_networking_messages{};
Steam_Game_Coordinator *steam_gameserver_game_coordinator; Steam_Game_Coordinator *steam_gameserver_game_coordinator{};
Steam_Masterserver_Updater *steam_masterserver_updater; Steam_Masterserver_Updater *steam_masterserver_updater{};
Steam_AppTicket *steam_app_ticket; Steam_AppTicket *steam_app_ticket{};
Steam_Overlay* steam_overlay; Steam_Overlay* steam_overlay{};
bool user_logged_in = false; bool user_logged_in = false;
bool server_init = false; bool server_init = false;
std::thread background_keepalive;
bool steamclient_server_inited = false; bool steamclient_server_inited = false;
bool gameserver_has_ipv6_functions{};
std::thread background_keepalive{};
std::atomic<unsigned long long> last_cb_run{}; std::atomic<unsigned long long> last_cb_run{};
std::atomic_bool cb_run_active = false; std::atomic_bool cb_run_active = false;
unsigned steam_pipe_counter = 1; unsigned steam_pipe_counter = 1;
std::map<HSteamPipe, enum Steam_Pipe> steam_pipes; std::map<HSteamPipe, enum Steam_Pipe> steam_pipes{};
bool gameserver_has_ipv6_functions;
Steam_Client(); Steam_Client();
~Steam_Client(); ~Steam_Client();
// Creates a communication pipe to the Steam client.
// Creates a communication pipe to the Steam client.
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling // NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
HSteamPipe CreateSteamPipe(); HSteamPipe CreateSteamPipe();
@ -311,3 +317,5 @@ public:
void DestroyAllInterfaces(); void DestroyAllInterfaces();
}; };
#endif // __INCLUDED_STEAM_CLIENT_H__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,143 +15,48 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_GAME_COORDINATOR_H__
#define __INCLUDED_STEAM_GAME_COORDINATOR_H__
#include "base.h" #include "base.h"
class Steam_Game_Coordinator : class Steam_Game_Coordinator :
public ISteamGameCoordinator public ISteamGameCoordinator
{ {
class Settings *settings; constexpr const static uint32 protobuf_mask = 0x80000000;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
static const uint32 protobuf_mask = 0x80000000; class Settings *settings{};
std::queue<std::string> outgoing_messages; class Networking *network{};
class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb{};
void push_incoming(std::string message) std::queue<std::string> outgoing_messages{};
{
outgoing_messages.push(message);
struct GCMessageAvailable_t data; void push_incoming(std::string message);
data.m_nMessageSize = message.size();
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); static void steam_callback(void *object, Common_Message *msg);
} static void steam_run_every_runcb(void *object);
void RunCallbacks();
void Callback(Common_Message *msg);
public: public:
static void steam_callback(void *object, Common_Message *msg) Steam_Game_Coordinator(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
{ ~Steam_Game_Coordinator();
// PRINT_DEBUG_ENTRY();
Steam_Game_Coordinator *steam_gamecoordinator = (Steam_Game_Coordinator *)object; // sends a message to the Game Coordinator
steam_gamecoordinator->Callback(msg); EGCResults SendMessage_( uint32 unMsgType, const void *pubData, uint32 cubData );
}
static void steam_run_every_runcb(void *object) // returns true if there is a message waiting from the game coordinator
{ bool IsMessageAvailable( uint32 *pcubMsgSize );
// PRINT_DEBUG_ENTRY();
Steam_Game_Coordinator *steam_gamecoordinator = (Steam_Game_Coordinator *)object; // fills the provided buffer with the first message in the queue and returns k_EGCResultOK or
steam_gamecoordinator->RunCallbacks(); // 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.
Steam_Game_Coordinator(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb) EGCResults RetrieveMessage( uint32 *punMsgType, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize );
{
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()
{
//this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Game_Coordinator::steam_callback, this);
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("%X %u len %u", unMsgType, (~protobuf_mask) & unMsgType, cubData);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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()) {
}
}
}; };
#endif // __INCLUDED_STEAM_GAME_COORDINATOR_H__

View File

@ -15,215 +15,104 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_GAMESEARCH_H__
#define __INCLUDED_STEAM_GAMESEARCH_H__
#include "base.h" #include "base.h"
class Steam_Game_Search : class Steam_Game_Search :
public ISteamGameSearch public ISteamGameSearch
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb; class RunEveryRunCB *run_every_runcb{};
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now(); std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
FSteamNetworkingSocketsDebugOutput debug_function; FSteamNetworkingSocketsDebugOutput debug_function{};
static void steam_callback(void *object, Common_Message *msg);
static void steam_run_every_runcb(void *object);
void RunCallbacks();
void Callback(Common_Message *msg);
public: public:
static void steam_callback(void *object, Common_Message *msg) Steam_Game_Search(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
{ ~Steam_Game_Search();
// PRINT_DEBUG_ENTRY();
Steam_Game_Search *steam_gamesearch = (Steam_Game_Search *)object; // =============================================================================================
steam_gamesearch->Callback(msg); // Game Player APIs
}
static void steam_run_every_runcb(void *object) // 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
// PRINT_DEBUG_ENTRY(); EGameSearchErrorCode_t AddGameSearchParams( const char *pchKeyToFind, const char *pchValuesToFind );
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()
{
//this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Game_Search::steam_callback, this);
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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 // 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 // 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 // periodic callbacks will be sent as queue time estimates change
EGameSearchErrorCode_t SearchForGameWithLobby( CSteamID steamIDLobby, int nPlayerMin, int nPlayerMax ) EGameSearchErrorCode_t SearchForGameWithLobby( CSteamID steamIDLobby, int nPlayerMin, int nPlayerMax );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// user enter the queue and await a SearchForGameNotificationCallback_t callback. fails if another search is currently in progress // 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 // periodic callbacks will be sent as queue time estimates change
EGameSearchErrorCode_t SearchForGameSolo( int nPlayerMin, int nPlayerMax ) EGameSearchErrorCode_t SearchForGameSolo( int nPlayerMin, int nPlayerMax );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// after receiving SearchForGameResultCallback_t, accept or decline the game // 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 // multiple SearchForGameResultCallback_t will follow as players accept game until the host starts or cancels the game
EGameSearchErrorCode_t AcceptGame() EGameSearchErrorCode_t AcceptGame();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
EGameSearchErrorCode_t DeclineGame() EGameSearchErrorCode_t DeclineGame();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// after receiving GameStartedByHostCallback_t get connection details to server // after receiving GameStartedByHostCallback_t get connection details to server
EGameSearchErrorCode_t RetrieveConnectionDetails( CSteamID steamIDHost, char *pchConnectionDetails, int cubConnectionDetails ) EGameSearchErrorCode_t RetrieveConnectionDetails( CSteamID steamIDHost, char *pchConnectionDetails, int cubConnectionDetails );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// leaves queue if still waiting // leaves queue if still waiting
EGameSearchErrorCode_t EndGameSearch() EGameSearchErrorCode_t EndGameSearch();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// ============================================================================================= // =============================================================================================
// Game Host APIs // Game Host APIs
// a keyname and a list of comma separated values: all the values you allow // a keyname and a list of comma separated values: all the values you allow
EGameSearchErrorCode_t SetGameHostParams( const char *pchKey, const char *pchValue ) EGameSearchErrorCode_t SetGameHostParams( const char *pchKey, const char *pchValue );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// set connection details for players once game is found so they can connect to this server // set connection details for players once game is found so they can connect to this server
EGameSearchErrorCode_t SetConnectionDetails( const char *pchConnectionDetails, int cubConnectionDetails ) EGameSearchErrorCode_t SetConnectionDetails( const char *pchConnectionDetails, int cubConnectionDetails );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// mark server as available for more players with nPlayerMin,nPlayerMax desired // mark server as available for more players with nPlayerMin,nPlayerMax desired
// accept no lobbies with playercount greater than nMaxTeamSize // accept no lobbies with playercount greater than nMaxTeamSize
// the set of lobbies returned must be partitionable into teams of no more 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 // RequestPlayersForGameNotificationCallback_t callback will be sent when the search has started
// multple RequestPlayersForGameResultCallback_t callbacks will follow when players are found // multple RequestPlayersForGameResultCallback_t callbacks will follow when players are found
EGameSearchErrorCode_t RequestPlayersForGame( int nPlayerMin, int nPlayerMax, int nMaxTeamSize ) EGameSearchErrorCode_t RequestPlayersForGame( int nPlayerMin, int nPlayerMax, int nMaxTeamSize );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// accept the player list and release connection details to players // accept the player list and release connection details to players
// players will only be given connection details and host steamid when this is called // 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 ) // ( 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 ) EGameSearchErrorCode_t HostConfirmGameStart( uint64 ullUniqueGameID );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// cancel request and leave the pool of game hosts looking for players // 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 // if a set of players has already been sent to host, all players will receive SearchForGameHostFailedToConfirm_t
EGameSearchErrorCode_t CancelRequestPlayersForGame() EGameSearchErrorCode_t CancelRequestPlayersForGame();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// submit a result for one player. does not end the game. ullUniqueGameID continues to describe this game // 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 ) EGameSearchErrorCode_t SubmitPlayerResult( uint64 ullUniqueGameID, CSteamID steamIDPlayer, EPlayerResult_t EPlayerResult );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// ends the game. no further SubmitPlayerResults for ullUniqueGameID will be accepted // ends the game. no further SubmitPlayerResults for ullUniqueGameID will be accepted
// any future requests will provide a new ullUniqueGameID // any future requests will provide a new ullUniqueGameID
EGameSearchErrorCode_t EndGame( uint64 ullUniqueGameID ) EGameSearchErrorCode_t EndGame( uint64 ullUniqueGameID );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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()) {
}
}
}; };
#endif // __INCLUDED_STEAM_GAMESEARCH_H__

View File

@ -15,6 +15,9 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_GAMESERVER_H__
#define __INCLUDED_STEAM_GAMESERVER_H__
#include "base.h" #include "base.h"
#include "auth.h" #include "auth.h"
@ -23,16 +26,16 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
struct Gameserver_Outgoing_Packet { struct Gameserver_Outgoing_Packet {
std::vector<uint8_t> data; std::vector<uint8_t> data{};
uint32 ip; uint32 ip{};
uint16 port; uint16 port{};
}; };
struct Gameserver_Player_Info_t { struct Gameserver_Player_Info_t {
std::chrono::steady_clock::time_point join_time; std::chrono::steady_clock::time_point join_time{};
std::string name; std::string name{};
uint32 score; uint32 score{};
}; };
class Steam_GameServer : class Steam_GameServer :
@ -47,27 +50,28 @@ public ISteamGameServer013,
public ISteamGameServer014, public ISteamGameServer014,
public ISteamGameServer public ISteamGameServer
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
CSteamID steam_id; CSteamID steam_id{};
bool call_servers_connected = false; bool call_servers_connected = false;
bool logged_in = false; bool logged_in = false;
bool call_servers_disconnected = false; bool call_servers_disconnected = false;
Gameserver server_data; Gameserver server_data{};
std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>> players; std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>> players{};
uint32 flags; uint32 flags{};
bool policy_response_called; bool policy_response_called{};
std::chrono::high_resolution_clock::time_point last_sent_server_info{};
Auth_Manager *auth_manager{};
std::vector<struct Gameserver_Outgoing_Packet> outgoing_packets{};
std::chrono::high_resolution_clock::time_point last_sent_server_info;
Auth_Manager *auth_manager;
std::vector<struct Gameserver_Outgoing_Packet> outgoing_packets;
public: public:
Steam_GameServer(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks); Steam_GameServer(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks);
~Steam_GameServer(); ~Steam_GameServer();
@ -361,6 +365,10 @@ public:
STEAM_CALL_RESULT( ComputeNewPlayerCompatibilityResult_t ) STEAM_CALL_RESULT( ComputeNewPlayerCompatibilityResult_t )
SteamAPICall_t ComputeNewPlayerCompatibility( CSteamID steamIDNewPlayer ); SteamAPICall_t ComputeNewPlayerCompatibility( CSteamID steamIDNewPlayer );
//
// called by steam_client::runcallbacks
void RunCallbacks(); void RunCallbacks();
}; };
#endif // __INCLUDED_STEAM_GAMESERVER_H__

View File

@ -15,18 +15,22 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_GAMESERVERSTATS_H__
#define __INCLUDED_STEAM_GAMESERVERSTATS_H__
#include "base.h" #include "base.h"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: Functions for authenticating users via Steam to play on a game server // Purpose: Functions for authenticating users via Steam to play on a game server
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
class Steam_GameServerStats : public ISteamGameServerStats class Steam_GameServerStats :
public ISteamGameServerStats
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb; class RunEveryRunCB *run_every_runcb{};
struct RequestAllStats { struct RequestAllStats {
std::chrono::high_resolution_clock::time_point created{}; std::chrono::high_resolution_clock::time_point created{};
@ -109,3 +113,5 @@ public:
STEAM_CALL_RESULT( GSStatsStored_t ) STEAM_CALL_RESULT( GSStatsStored_t )
SteamAPICall_t StoreUserStats( CSteamID steamIDUser ); SteamAPICall_t StoreUserStats( CSteamID steamIDUser );
}; };
#endif // __INCLUDED_STEAM_GAMESERVERSTATS_H__

View File

@ -15,14 +15,16 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_HTTP_H__
#define __INCLUDED_STEAM_HTTP_H__
#include "base.h" #include "base.h"
#include "common_includes.h"
#include <curl/curl.h> #include <curl/curl.h>
struct Steam_Http_Request { struct Steam_Http_Request {
HTTPRequestHandle handle; HTTPRequestHandle handle{};
EHTTPMethod request_method; EHTTPMethod request_method{};
std::string url{}; std::string url{};
uint64 timeout_sec = 60; uint64 timeout_sec = 60;
bool requires_valid_ssl = false; bool requires_valid_ssl = false;
@ -37,7 +39,7 @@ struct Steam_Http_Request {
std::map<std::string, std::string> get_or_post_params{}; std::map<std::string, std::string> get_or_post_params{};
std::string post_raw{}; std::string post_raw{};
uint64 context_value; uint64 context_value{};
// target local filepath to save // target local filepath to save
std::string target_filepath{}; std::string target_filepath{};
@ -48,17 +50,18 @@ struct Steam_Http_Request {
std::string response{}; std::string response{};
}; };
class Steam_HTTP : class Steam_HTTP :
public ISteamHTTP001, public ISteamHTTP001,
public ISteamHTTP002, public ISteamHTTP002,
public ISteamHTTP public ISteamHTTP
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
std::vector<Steam_Http_Request> requests; std::vector<Steam_Http_Request> requests{};
Steam_Http_Request *get_request(HTTPRequestHandle hRequest); Steam_Http_Request *get_request(HTTPRequestHandle hRequest);
void online_http_request(Steam_Http_Request *request, SteamAPICall_t *pCallHandle); void online_http_request(Steam_Http_Request *request, SteamAPICall_t *pCallHandle);
@ -177,3 +180,5 @@ public:
// Check if the reason the request failed was because we timed it out (rather than some harder failure) // Check if the reason the request failed was because we timed it out (rather than some harder failure)
bool GetHTTPRequestWasTimedOut( HTTPRequestHandle hRequest, bool *pbWasTimedOut ); bool GetHTTPRequestWasTimedOut( HTTPRequestHandle hRequest, bool *pbWasTimedOut );
}; };
#endif // __INCLUDED_STEAM_HTTP_H__

File diff suppressed because it is too large Load Diff

View File

@ -15,219 +15,117 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_MASTERSERVER_UPDATER_H__
#define __INCLUDED_STEAM_MASTERSERVER_UPDATER_H__
#include "base.h" #include "base.h"
class Steam_Masterserver_Updater : class Steam_Masterserver_Updater :
public ISteamMasterServerUpdater public ISteamMasterServerUpdater
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb; class RunEveryRunCB *run_every_runcb{};
static void steam_callback(void *object, Common_Message *msg);
static void steam_run_every_runcb(void *object);
void RunCallbacks();
void Callback(Common_Message *msg);
public: public:
static void steam_callback(void *object, Common_Message *msg) Steam_Masterserver_Updater(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
{ ~Steam_Masterserver_Updater();
// PRINT_DEBUG_ENTRY();
Steam_Masterserver_Updater *steam_masterserverupdater = (Steam_Masterserver_Updater *)object; // Call this as often as you like to tell the master server updater whether or not
steam_masterserverupdater->Callback(msg); // you want it to be active (default: off).
} void SetActive( bool bActive );
static void steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
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()
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Masterserver_Updater::steam_callback, this);
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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// You usually don't need to modify this. // You usually don't need to modify this.
// Pass -1 to use the default value for iHeartbeatInterval. // Pass -1 to use the default value for iHeartbeatInterval.
// Some mods change this. // Some mods change this.
void SetHeartbeatInterval( int iHeartbeatInterval ) void SetHeartbeatInterval( int iHeartbeatInterval );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// These are in GameSocketShare mode, where instead of ISteamMasterServerUpdater creating its own // 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 // 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 // back and forth. This prevents us from requiring server ops to open up yet another port
// in their firewalls. // in their firewalls.
// //
// the IP address and port should be in host order, i.e 127.0.0.1 == 0x7f000001 // 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 // 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. // 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 // 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. // don't have to open up more ports on their firewalls.
// Call this when a packet that starts with 0xFFFFFFFF comes in. That means // Call this when a packet that starts with 0xFFFFFFFF comes in. That means
// it's for us. // it's for us.
bool HandleIncomingPacket( const void *pData, int cbData, uint32 srcIP, uint16 srcPort ) bool HandleIncomingPacket( const void *pData, int cbData, uint32 srcIP, uint16 srcPort );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
// AFTER calling HandleIncomingPacket for any packets that came in that frame, call this. // 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. // 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. // 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. // Call this each frame until it returns 0.
int GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *pNetAdr, uint16 *pPort ) int GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *pNetAdr, uint16 *pPort );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Functions to set various fields that are used to respond to queries. // 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. // Call this to set basic data that is passed to the server browser.
void SetBasicServerData( void SetBasicServerData(
unsigned short nProtocolVersion, unsigned short nProtocolVersion,
bool bDedicatedServer, bool bDedicatedServer,
const char *pRegionName, const char *pRegionName,
const char *pProductName, const char *pProductName,
unsigned short nMaxReportedClients, unsigned short nMaxReportedClients,
bool bPasswordProtected, bool bPasswordProtected,
const char *pGameDescription ) const char *pGameDescription );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Call this to clear the whole list of key/values that are sent in rules queries. // Call this to clear the whole list of key/values that are sent in rules queries.
void ClearAllKeyValues() void ClearAllKeyValues();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Call this to add/update a key/value pair. // Call this to add/update a key/value pair.
void SetKeyValue( const char *pKey, const char *pValue ) void SetKeyValue( const char *pKey, const char *pValue );
{
PRINT_DEBUG("TODO '%s'='%s'", pKey, pValue);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// You can call this upon shutdown to clear out data stored for this game server and // 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. // to tell the master servers that this server is going away.
void NotifyShutdown() void NotifyShutdown();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Returns true if the master server has requested a restart. // Returns true if the master server has requested a restart.
// Only returns true once per request. // Only returns true once per request.
bool WasRestartRequested() bool WasRestartRequested();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Force it to request a heartbeat from the master servers. // Force it to request a heartbeat from the master servers.
void ForceHeartbeat() void ForceHeartbeat();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Manually edit and query the master server list. // Manually edit and query the master server list.
// It will provide name resolution and use the default master server port if none is provided. // It will provide name resolution and use the default master server port if none is provided.
bool AddMasterServer( const char *pServerAddress ) bool AddMasterServer( const char *pServerAddress );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
bool RemoveMasterServer( const char *pServerAddress ) bool RemoveMasterServer( const char *pServerAddress );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
int GetNumMasterServers() int GetNumMasterServers();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Returns the # of bytes written to pOut. // Returns the # of bytes written to pOut.
int GetMasterServerAddress( int iServer, char *pOut, int outBufferSize ) int GetMasterServerAddress( int iServer, char *pOut, int outBufferSize );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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) {
}
}
}
}; };
#endif // __INCLUDED_STEAM_MASTERSERVER_UPDATER_H__

File diff suppressed because it is too large Load Diff

View File

@ -15,59 +15,69 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_MATCHMAKING_SERVERS_H__
#define __INCLUDED_STEAM_MATCHMAKING_SERVERS_H__
#include "base.h" #include "base.h"
#include <ssq/a2s.h> #include <ssq/a2s.h>
#define SERVER_TIMEOUT 10.0
#define DIRECT_IP_DELAY 0.05
struct Steam_Matchmaking_Servers_Direct_IP_Request { struct Steam_Matchmaking_Servers_Direct_IP_Request {
HServerQuery id; HServerQuery id{};
uint32 ip; uint32 ip{};
uint16 port; uint16 port{};
std::chrono::high_resolution_clock::time_point created; std::chrono::high_resolution_clock::time_point created{};
ISteamMatchmakingRulesResponse *rules_response = NULL; ISteamMatchmakingRulesResponse *rules_response{};
ISteamMatchmakingPlayersResponse *players_response = NULL; ISteamMatchmakingPlayersResponse *players_response{};
ISteamMatchmakingPingResponse *ping_response = NULL; ISteamMatchmakingPingResponse *ping_response{};
}; };
struct Steam_Matchmaking_Servers_Gameserver_Friends { struct Steam_Matchmaking_Servers_Gameserver_Friends {
uint64 source_id; uint64 source_id{};
uint32 ip; uint32 ip{};
uint16 port; uint16 port{};
std::chrono::high_resolution_clock::time_point last_recv; std::chrono::high_resolution_clock::time_point last_recv{};
}; };
struct Steam_Matchmaking_Servers_Gameserver { struct Steam_Matchmaking_Servers_Gameserver {
Gameserver server; Gameserver server{};
std::chrono::high_resolution_clock::time_point last_recv; std::chrono::high_resolution_clock::time_point last_recv{};
EMatchMakingType type; EMatchMakingType type{};
}; };
struct Steam_Matchmaking_Request { struct Steam_Matchmaking_Request {
AppId_t appid; AppId_t appid{};
HServerListRequest id; HServerListRequest id{};
ISteamMatchmakingServerListResponse *callbacks; ISteamMatchmakingServerListResponse *callbacks{};
ISteamMatchmakingServerListResponse001 *old_callbacks; ISteamMatchmakingServerListResponse001 *old_callbacks{};
bool completed, cancelled, released; bool completed{}, cancelled{}, released{};
std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers_filtered; std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers_filtered{};
EMatchMakingType type; EMatchMakingType type{};
}; };
class Steam_Matchmaking_Servers : public ISteamMatchmakingServers, class Steam_Matchmaking_Servers :
public ISteamMatchmakingServers001 public ISteamMatchmakingServers001,
public ISteamMatchmakingServers
{ {
class Settings *settings{}; class Settings *settings{};
class Local_Storage *local_storage{}; class Local_Storage *local_storage{};
class Networking *network{}; class Networking *network{};
std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers; std::vector <struct Steam_Matchmaking_Servers_Gameserver> gameservers{};
std::vector <struct Steam_Matchmaking_Servers_Gameserver_Friends> gameservers_friends; std::vector <struct Steam_Matchmaking_Servers_Gameserver_Friends> gameservers_friends{};
std::vector <struct Steam_Matchmaking_Request> requests; std::vector <struct Steam_Matchmaking_Request> requests{};
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests; std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests{};
HServerListRequest RequestServerList(AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse, EMatchMakingType type); HServerListRequest RequestServerList(AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse, EMatchMakingType type);
void RequestOldServerList(AppId_t iApp, ISteamMatchmakingServerListResponse001 *pRequestServersResponse, EMatchMakingType type); void RequestOldServerList(AppId_t iApp, ISteamMatchmakingServerListResponse001 *pRequestServersResponse, EMatchMakingType type);
//
static void network_callback(void *object, Common_Message *msg);
void server_details(Gameserver *g, gameserveritem_t *server);
void server_details_players(Gameserver *g, Steam_Matchmaking_Servers_Direct_IP_Request *r);
void server_details_rules(Gameserver *g, Steam_Matchmaking_Servers_Direct_IP_Request *r);
void Callback(Common_Message *msg);
public: public:
Steam_Matchmaking_Servers(class Settings *settings, class Local_Storage *local_storage, class Networking *network); Steam_Matchmaking_Servers(class Settings *settings, class Local_Storage *local_storage, class Networking *network);
~Steam_Matchmaking_Servers(); ~Steam_Matchmaking_Servers();
@ -195,25 +205,25 @@ public:
// Get details on a given server in the list, you can get the valid range of index // 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 // values by calling GetServerCount(). You will also receive index values in
// ISteamMatchmakingServerListResponse::ServerResponded() callbacks // ISteamMatchmakingServerListResponse::ServerResponded() callbacks
gameserveritem_t *GetServerDetails( EMatchMakingType eType, int iServer ) { return GetServerDetails((HServerListRequest) eType , iServer ); } gameserveritem_t *GetServerDetails( EMatchMakingType eType, int iServer );
// Cancel an request which is operation on the given list type. You should call this to cancel // 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 // 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 // to one of the above list request calls. Not doing so may result in a crash when a callback
// occurs on the destructed object. // occurs on the destructed object.
void CancelQuery( EMatchMakingType eType ) { return CancelQuery((HServerListRequest) eType); } void CancelQuery( EMatchMakingType eType );
// Ping every server in your list again but don't update the list of servers // Ping every server in your list again but don't update the list of servers
void RefreshQuery( EMatchMakingType eType ) { return RefreshQuery((HServerListRequest) eType); } void RefreshQuery( EMatchMakingType eType );
// Returns true if the list is currently refreshing its server list // Returns true if the list is currently refreshing its server list
bool IsRefreshing( EMatchMakingType eType ) { return IsRefreshing((HServerListRequest) eType); } bool IsRefreshing( EMatchMakingType eType );
// How many servers in the given list, GetServerDetails above takes 0... GetServerCount() - 1 // How many servers in the given list, GetServerDetails above takes 0... GetServerCount() - 1
int GetServerCount( EMatchMakingType eType ) { return GetServerCount((HServerListRequest) eType); } int GetServerCount( EMatchMakingType eType );
// Refresh a single server inside of a query (rather than all the servers ) // Refresh a single server inside of a query (rather than all the servers )
void RefreshServer( EMatchMakingType eType, int iServer ) { return RefreshServer((HServerListRequest) eType, iServer); } void RefreshServer( EMatchMakingType eType, int iServer );
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Queries to individual servers directly via IP/Port // Queries to individual servers directly via IP/Port
@ -233,10 +243,9 @@ public:
// to one of the above calls to avoid crashing when callbacks occur. // to one of the above calls to avoid crashing when callbacks occur.
void CancelServerQuery( HServerQuery hServerQuery ); void CancelServerQuery( HServerQuery hServerQuery );
// // called by steam_client::runcallbacks
void RunCallbacks(); void RunCallbacks();
void Callback(Common_Message *msg);
void server_details(Gameserver *g, gameserveritem_t *server);
void server_details_players(Gameserver *g, Steam_Matchmaking_Servers_Direct_IP_Request *r);
void server_details_rules(Gameserver *g, Steam_Matchmaking_Servers_Direct_IP_Request *r);
}; };
#endif // __INCLUDED_STEAM_MATCHMAKING_SERVERS_H__

View File

@ -15,15 +15,20 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_MUSIC_H__
#define __INCLUDED_STEAM_MUSIC_H__
#include "base.h" #include "base.h"
class Steam_Music : public ISteamMusic class Steam_Music :
public ISteamMusic
{ {
int playing; int playing{};;
float volume; float volume{};;
void change_playstate(int new_playing); void change_playstate(int new_playing);
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
public: public:
Steam_Music(class SteamCallBacks *callbacks); Steam_Music(class SteamCallBacks *callbacks);
@ -41,3 +46,5 @@ public:
void SetVolume( float flVolume ); void SetVolume( float flVolume );
float GetVolume(); float GetVolume();
}; };
#endif // __INCLUDED_STEAM_MUSIC_H__

View File

@ -15,9 +15,13 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_MUSICREMOTE_H__
#define __INCLUDED_STEAM_MUSICREMOTE_H__
#include "base.h" #include "base.h"
class Steam_MusicRemote : public ISteamMusicRemote class Steam_MusicRemote :
public ISteamMusicRemote
{ {
public: public:
// Service Definition // Service Definition
@ -65,3 +69,5 @@ public:
bool SetCurrentPlaylistEntry( int nID ); bool SetCurrentPlaylistEntry( int nID );
bool PlaylistDidChange(); bool PlaylistDidChange();
}; };
#endif // __INCLUDED_STEAM_MUSICREMOTE_H__

File diff suppressed because it is too large Load Diff

View File

@ -15,19 +15,20 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_NETWORKING_MESSAGES_H__
#define __INCLUDED_STEAM_NETWORKING_MESSAGES_H__
#include "base.h" #include "base.h"
#define NETWORKING_MESSAGES_TIMEOUT 30.0
struct Steam_Message_Connection { struct Steam_Message_Connection {
SteamNetworkingIdentity remote_identity; SteamNetworkingIdentity remote_identity{};
std::map<int, std::queue<std::string>> data; std::map<int, std::queue<std::string>> data{};
std::list<int> channels; std::list<int> channels{};
bool accepted = false; bool accepted = false;
bool dead = false; bool dead = false;
unsigned id; unsigned id{};
unsigned remote_id = 0; unsigned remote_id = 0;
std::chrono::high_resolution_clock::time_point created = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point created = std::chrono::high_resolution_clock::now();
@ -36,412 +37,119 @@ struct Steam_Message_Connection {
class Steam_Networking_Messages : class Steam_Networking_Messages :
public ISteamNetworkingMessages public ISteamNetworkingMessages
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb; class RunEveryRunCB *run_every_runcb{};
std::map<CSteamID, Steam_Message_Connection> connections; std::map<CSteamID, Steam_Message_Connection> connections{};
std::list<Common_Message> incoming_data; std::list<Common_Message> incoming_data{};
unsigned id_counter = 0; unsigned id_counter = 0;
std::chrono::steady_clock::time_point created; std::chrono::steady_clock::time_point created{};
static void free_steam_message_data(SteamNetworkingMessage_t *pMsg);
static void delete_steam_message(SteamNetworkingMessage_t *pMsg);
static void steam_callback(void *object, Common_Message *msg);
static void steam_run_every_runcb(void *object);
std::map<CSteamID, Steam_Message_Connection>::iterator find_or_create_message_connection(SteamNetworkingIdentity identityRemote, bool incoming, bool restartbroken);
void end_connection(CSteamID steam_id);
void RunCallbacks();
void Callback(Common_Message *msg);
public: public:
static void steam_callback(void *object, Common_Message *msg) Steam_Networking_Messages(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
{ ~Steam_Networking_Messages();
// PRINT_DEBUG_ENTRY();
Steam_Networking_Messages *steam_networking_messages = (Steam_Networking_Messages *)object; /// Sends a message to the specified host. If we don't already have a session with that user,
steam_networking_messages->Callback(msg); /// 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 );
static void steam_run_every_runcb(void *object) /// 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.)
// PRINT_DEBUG_ENTRY(); ///
/// When you're done with the message object(s), make sure and call Release()!
int ReceiveMessagesOnChannel( int nLocalChannel, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages );
Steam_Networking_Messages *steam_networking_messages = (Steam_Networking_Messages *)object; /// AcceptSessionWithUser() should only be called in response to a SteamP2PSessionRequest_t callback
steam_networking_messages->RunCallbacks(); /// 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 );
Steam_Networking_Messages(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb) /// 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
this->settings = settings; /// be posted.
this->network = network; ///
this->run_every_runcb = run_every_runcb; /// Note that sessions that go unused for a few minutes are automatically timed out.
this->network->setCallback(CALLBACK_ID_NETWORKING_MESSAGES, settings->get_local_steam_id(), &Steam_Networking_Messages::steam_callback, this); bool CloseSessionWithUser( const SteamNetworkingIdentity &identityRemote );
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; /// Call this when you're done talking to a user on a specific channel. Once all
this->callbacks = callbacks; /// 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 );
this->created = std::chrono::steady_clock::now(); /// 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.)
~Steam_Networking_Messages() ///
{ /// Returns the value of SteamNetConnectionInfo_t::m_eState, or k_ESteamNetworkingConnectionState_None
this->network->rmCallback(CALLBACK_ID_NETWORKING_MESSAGES, settings->get_local_steam_id(), &Steam_Networking_Messages::steam_callback, this); /// if no connection exists with specified peer. You may pass nullptr for either parameter if
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Messages::steam_callback, this); /// you do not need the corresponding details. Note that sessions time out after a while,
this->run_every_runcb->remove(&Steam_Networking_Messages::steam_run_every_runcb, this); /// 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 );
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_ENTRY();
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("%llu", identityRemote.GetSteamID64());
//steam id identity
} else if (ip) {
PRINT_DEBUG("%u:%u ipv4? %u", 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_ENTRY();
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("got %u", 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_ENTRY();
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_ENTRY();
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_TODO();
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_ENTRY();
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("got network socket msg %u", 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));
}
}
}
}; };
#endif // __INCLUDED_STEAM_NETWORKING_MESSAGES_H__

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,9 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_NETWORKING_SOCKETSERIALIZED_H__
#define __INCLUDED_STEAM_NETWORKING_SOCKETSERIALIZED_H__
#include "base.h" #include "base.h"
class Steam_Networking_Sockets_Serialized : class Steam_Networking_Sockets_Serialized :
@ -23,145 +26,47 @@ public ISteamNetworkingSocketsSerialized003,
public ISteamNetworkingSocketsSerialized004, public ISteamNetworkingSocketsSerialized004,
public ISteamNetworkingSocketsSerialized005 public ISteamNetworkingSocketsSerialized005
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb; class RunEveryRunCB *run_every_runcb{};
static void steam_callback(void *object, Common_Message *msg);
static void steam_run_every_runcb(void *object);
public: public:
static void steam_callback(void *object, Common_Message *msg) Steam_Networking_Sockets_Serialized(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
{ ~Steam_Networking_Sockets_Serialized();
// PRINT_DEBUG_ENTRY();
Steam_Networking_Sockets_Serialized *steam_networkingsockets = (Steam_Networking_Sockets_Serialized *)object; void SendP2PRendezvous( CSteamID steamIDRemote, uint32 unConnectionIDSrc, const void *pMsgRendezvous, uint32 cbRendezvous );
steam_networkingsockets->Callback(msg);
}
static void steam_run_every_runcb(void *object) void SendP2PConnectionFailure( CSteamID steamIDRemote, uint32 unConnectionIDDest, uint32 nReason, const char *pszReason );
{
// PRINT_DEBUG_ENTRY();
Steam_Networking_Sockets_Serialized *steam_networkingsockets = (Steam_Networking_Sockets_Serialized *)object; SteamAPICall_t GetCertAsync();
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) int GetNetworkConfigJSON( void *buf, uint32 cbBuf, const char *pszLauncherPartner );
{
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; int GetNetworkConfigJSON( void *buf, uint32 cbBuf );
this->callbacks = callbacks;
}
~Steam_Networking_Sockets_Serialized() void CacheRelayTicket( const void *pTicket, uint32 cbTicket );
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Sockets_Serialized::steam_callback, this);
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 ) uint32 GetCachedRelayTicketCount();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void SendP2PConnectionFailure( CSteamID steamIDRemote, uint32 unConnectionIDDest, uint32 nReason, const char *pszReason ) int GetCachedRelayTicket( uint32 idxTicket, void *buf, uint32 cbBuf );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
SteamAPICall_t GetCertAsync() void PostConnectionStateMsg( const void *pMsg, uint32 cbMsg );
{
PRINT_DEBUG_TODO();
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)); bool GetSTUNServer(int dont_know, char *buf, unsigned int len);
}
int GetNetworkConfigJSON( void *buf, uint32 cbBuf, const char *pszLauncherPartner ) bool BAllowDirectConnectToPeer(SteamNetworkingIdentity const &identity);
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int GetNetworkConfigJSON( void *buf, uint32 cbBuf ) int BeginAsyncRequestFakeIP(int a);
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return GetNetworkConfigJSON(buf, cbBuf, "");
}
void CacheRelayTicket( const void *pTicket, uint32 cbTicket ) void RunCallbacks();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
uint32 GetCachedRelayTicketCount() void Callback(Common_Message *msg);
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int GetCachedRelayTicket( uint32 idxTicket, void *buf, uint32 cbBuf )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
void PostConnectionStateMsg( const void *pMsg, uint32 cbMsg )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
bool GetSTUNServer(int dont_know, char *buf, unsigned int len)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
bool BAllowDirectConnectToPeer(SteamNetworkingIdentity const &identity)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
int BeginAsyncRequestFakeIP(int a)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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) {
}
}
}
}; };
#endif // __INCLUDED_STEAM_NETWORKING_SOCKETSERIALIZED_H__

View File

@ -15,6 +15,9 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_NETWORKING_UTILS_H__
#define __INCLUDED_STEAM_NETWORKING_UTILS_H__
#include "base.h" #include "base.h"
class Steam_Networking_Utils : class Steam_Networking_Utils :
@ -23,728 +26,246 @@ public ISteamNetworkingUtils002,
public ISteamNetworkingUtils003, public ISteamNetworkingUtils003,
public ISteamNetworkingUtils public ISteamNetworkingUtils
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb; class RunEveryRunCB *run_every_runcb{};
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now(); std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
FSteamNetworkingSocketsDebugOutput debug_function; FSteamNetworkingSocketsDebugOutput debug_function{};
bool relay_initialized = false; bool relay_initialized = false;
bool init_relay = false; bool init_relay = false;
static void free_steam_message_data(SteamNetworkingMessage_t *pMsg);
static void delete_steam_message(SteamNetworkingMessage_t *pMsg);
static void steam_callback(void *object, Common_Message *msg);
static void steam_run_every_runcb(void *object);
public: public:
static void steam_callback(void *object, Common_Message *msg) Steam_Networking_Utils(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
{ ~Steam_Networking_Utils();
// PRINT_DEBUG_ENTRY();
Steam_Networking_Utils *steam_networkingutils = (Steam_Networking_Utils *)object;
steam_networkingutils->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
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()
{
//this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Utils::steam_callback, this);
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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
SteamNetworkingMessage_t *pMsg = new SteamNetworkingMessage_t();
pMsg->m_pfnFreeData = &free_steam_message_data;
pMsg->m_pfnRelease = &delete_steam_message;
if (cbAllocateBuffer < 0)
cbAllocateBuffer = 0;
pMsg->m_pData = nullptr;
if (cbAllocateBuffer)
pMsg->m_pData = malloc(cbAllocateBuffer);
pMsg->m_cbSize = cbAllocateBuffer;
return pMsg;
}
bool InitializeRelayAccess()
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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("TODO %p", 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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//return k_nSteamNetworkingPing_Unknown;
return 2;
}
int EstimatePingTimeFromLocalHost( const SteamNetworkPingLocation_t &remoteLocation )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 2;
}
void ConvertPingLocationToString( const SteamNetworkPingLocation_t &location, char *pszBuf, int cchBufSize )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
strncpy(pszBuf, "fra=10+2", cchBufSize);
}
bool ParsePingLocationString( const char *pszString, SteamNetworkPingLocation_t &result )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
bool CheckPingDataUpToDate( float flMaxAgeSeconds )
{
PRINT_DEBUG("TODO %f", flMaxAgeSeconds);
init_relay = true;
return relay_initialized;
}
bool IsPingMeasurementInProgress()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
int GetPingToDataCenter( SteamNetworkingPOPID popID, SteamNetworkingPOPID *pViaRelayPoP )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int GetDirectPingToPOP( SteamNetworkingPOPID popID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int GetPOPCount()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int GetPOPList( SteamNetworkingPOPID *list, int nListSz )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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("%i", eDetailLevel);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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("TODO %i %i " "%" PRIdPTR " %i %p", eValue, eScopeType, scopeObj, eDataType, pArg);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//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();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO flat api
return NULL;
}
/// Return the lowest numbered configuration value available in the current environment.
ESteamNetworkingConfigValue GetFirstConfigValue()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (buf == nullptr || cbBuf == 0) return;
char buffer[64]{}; // enough 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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_ESteamNetworkingFakeIPType_NotFake;
}
void SteamNetworkingIdentity_ToString( const SteamNetworkingIdentity &identity, char *buf, size_t cbBuf )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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()) {
}
}
/// 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 );
bool InitializeRelayAccess();
SteamRelayNetworkStatus_t get_network_status();
/// 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 );
float GetLocalPingLocation( SteamNetworkPingLocation_t &result );
int EstimatePingTimeBetweenTwoLocations( const SteamNetworkPingLocation_t &location1, const SteamNetworkPingLocation_t &location2 );
int EstimatePingTimeFromLocalHost( const SteamNetworkPingLocation_t &remoteLocation );
void ConvertPingLocationToString( const SteamNetworkPingLocation_t &location, char *pszBuf, int cchBufSize );
bool ParsePingLocationString( const char *pszString, SteamNetworkPingLocation_t &result );
bool CheckPingDataUpToDate( float flMaxAgeSeconds );
bool IsPingMeasurementInProgress();
int GetPingToDataCenter( SteamNetworkingPOPID popID, SteamNetworkingPOPID *pViaRelayPoP );
int GetDirectPingToPOP( SteamNetworkingPOPID popID );
int GetPOPCount();
int GetPOPList( SteamNetworkingPOPID *list, int nListSz );
//
// 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();
/// 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 );
//
// 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 );
/// 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 );
//
// 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 );
/// 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 );
/// 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 );
/// 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 );
/// Return the lowest numbered configuration value available in the current environment.
ESteamNetworkingConfigValue GetFirstConfigValue();
/// 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 );
// 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 );
bool SteamNetworkingIPAddr_ParseString( SteamNetworkingIPAddr *pAddr, const char *pszStr );
ESteamNetworkingFakeIPType SteamNetworkingIPAddr_GetFakeIPType( const SteamNetworkingIPAddr &addr );
void SteamNetworkingIdentity_ToString( const SteamNetworkingIdentity &identity, char *buf, size_t cbBuf );
bool SteamNetworkingIdentity_ParseString( SteamNetworkingIdentity *pIdentity, const char *pszStr );
void RunCallbacks();
void Callback(Common_Message *msg);
}; };
#endif // __INCLUDED_STEAM_NETWORKING_UTILS_H__

View File

@ -15,9 +15,13 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_PARENTAL_H__
#define __INCLUDED_STEAM_PARENTAL_H__
#include "base.h" #include "base.h"
class Steam_Parental : public ISteamParentalSettings class Steam_Parental :
public ISteamParentalSettings
{ {
public: public:
bool BIsParentalLockEnabled(); bool BIsParentalLockEnabled();
@ -29,3 +33,5 @@ public:
bool BIsFeatureBlocked( EParentalFeature eFeature ); bool BIsFeatureBlocked( EParentalFeature eFeature );
bool BIsFeatureInBlockList( EParentalFeature eFeature ); bool BIsFeatureInBlockList( EParentalFeature eFeature );
}; };
#endif // __INCLUDED_STEAM_PARENTAL_H__

View File

@ -15,192 +15,95 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_PARTIES_H__
#define __INCLUDED_STEAM_PARTIES_H__
#include "base.h" #include "base.h"
class Steam_Parties : class Steam_Parties :
public ISteamParties public ISteamParties
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb; class RunEveryRunCB *run_every_runcb{};
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now(); std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
FSteamNetworkingSocketsDebugOutput debug_function; FSteamNetworkingSocketsDebugOutput debug_function{};
static void steam_callback(void *object, Common_Message *msg);
static void steam_run_every_runcb(void *object);
public: public:
static void steam_callback(void *object, Common_Message *msg) Steam_Parties(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
{ ~Steam_Parties();
// PRINT_DEBUG_ENTRY();
Steam_Parties *steam_parties = (Steam_Parties *)object;
steam_parties->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
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()
{
//this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Parties::steam_callback, this);
this->run_every_runcb->remove(&Steam_Parties::steam_run_every_runcb, this);
}
// ============================================================================================= // =============================================================================================
// Party Client APIs // Party Client APIs
// Enumerate any active beacons for parties you may wish to join // Enumerate any active beacons for parties you may wish to join
uint32 GetNumActiveBeacons() uint32 GetNumActiveBeacons();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
PartyBeaconID_t GetBeaconByIndex( uint32 unIndex ) PartyBeaconID_t GetBeaconByIndex( uint32 unIndex );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 ) bool GetBeaconDetails( PartyBeaconID_t ulBeaconID, CSteamID *pSteamIDBeaconOwner, STEAM_OUT_STRUCT() SteamPartyBeaconLocation_t *pLocation, STEAM_OUT_STRING_COUNT(cchMetadata) char *pchMetadata, int cchMetadata );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Join an open party. Steam will reserve one beacon slot for your SteamID, // 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 // and return the necessary JoinGame string for you to use to connect
STEAM_CALL_RESULT( JoinPartyCallback_t ) STEAM_CALL_RESULT( JoinPartyCallback_t )
SteamAPICall_t JoinParty( PartyBeaconID_t ulBeaconID ) SteamAPICall_t JoinParty( PartyBeaconID_t ulBeaconID );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// ============================================================================================= // =============================================================================================
// Party Host APIs // Party Host APIs
// Get a list of possible beacon locations // Get a list of possible beacon locations
bool GetNumAvailableBeaconLocations( uint32 *puNumLocations ) bool GetNumAvailableBeaconLocations( uint32 *puNumLocations );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
bool GetAvailableBeaconLocations( SteamPartyBeaconLocation_t *pLocationList, uint32 uMaxNumLocations ) bool GetAvailableBeaconLocations( SteamPartyBeaconLocation_t *pLocationList, uint32 uMaxNumLocations );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Create a new party beacon and activate it in the selected location. // 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. // unOpenSlots is the maximum number of users that Steam will send to you.
// When people begin responding to your beacon, Steam will send you // When people begin responding to your beacon, Steam will send you
// PartyReservationCallback_t callbacks to let you know who is on the way. // PartyReservationCallback_t callbacks to let you know who is on the way.
STEAM_CALL_RESULT( CreateBeaconCallback_t ) STEAM_CALL_RESULT( CreateBeaconCallback_t )
SteamAPICall_t CreateBeacon( uint32 unOpenSlots, SteamPartyBeaconLocation_t *pBeaconLocation, const char *pchConnectString, const char *pchMetadata ) SteamAPICall_t CreateBeacon( uint32 unOpenSlots, SteamPartyBeaconLocation_t *pBeaconLocation, const char *pchConnectString, const char *pchMetadata );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Call this function when a user that had a reservation (see callback below) // Call this function when a user that had a reservation (see callback below)
// has successfully joined your party. // has successfully joined your party.
// Steam will manage the remaining open slots automatically. // Steam will manage the remaining open slots automatically.
void OnReservationCompleted( PartyBeaconID_t ulBeacon, CSteamID steamIDUser ) void OnReservationCompleted( PartyBeaconID_t ulBeacon, CSteamID steamIDUser );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// To cancel a reservation (due to timeout or user input), call this. // To cancel a reservation (due to timeout or user input), call this.
// Steam will open a new reservation slot. // 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. // 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 ) void CancelReservation( PartyBeaconID_t ulBeacon, CSteamID steamIDUser );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Change the number of open beacon reservation slots. // 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). // 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 ) STEAM_CALL_RESULT( ChangeNumOpenSlotsCallback_t )
SteamAPICall_t ChangeNumOpenSlots( PartyBeaconID_t ulBeacon, uint32 unOpenSlots ) SteamAPICall_t ChangeNumOpenSlots( PartyBeaconID_t ulBeacon, uint32 unOpenSlots );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Turn off the beacon. // Turn off the beacon.
bool DestroyBeacon( PartyBeaconID_t ulBeacon ) bool DestroyBeacon( PartyBeaconID_t ulBeacon );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Utils // Utils
bool GetBeaconLocationData( SteamPartyBeaconLocation_t BeaconLocation, ESteamPartyBeaconLocationData eData, STEAM_OUT_STRING_COUNT(cchDataStringOut) char *pchDataStringOut, int cchDataStringOut ) bool GetBeaconLocationData( SteamPartyBeaconLocation_t BeaconLocation, ESteamPartyBeaconLocationData eData, STEAM_OUT_STRING_COUNT(cchDataStringOut) char *pchDataStringOut, int cchDataStringOut );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
void RunCallbacks() void RunCallbacks();
{
}
void Callback(Common_Message *msg) 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()) {
}
}
}; };
#endif // __INCLUDED_STEAM_PARTIES_H__

File diff suppressed because it is too large Load Diff

View File

@ -15,140 +15,58 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_REMOTEPLAY_H__
#define __INCLUDED_STEAM_REMOTEPLAY_H__
#include "base.h" #include "base.h"
class Steam_RemotePlay : class Steam_RemotePlay :
public ISteamRemotePlay001, public ISteamRemotePlay001,
public ISteamRemotePlay public ISteamRemotePlay
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb; class RunEveryRunCB *run_every_runcb{};
static void steam_callback(void *object, Common_Message *msg);
static void steam_run_every_runcb(void *object);
public: public:
static void steam_callback(void *object, Common_Message *msg) Steam_RemotePlay(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
{ ~Steam_RemotePlay();
// PRINT_DEBUG_ENTRY();
Steam_RemotePlay *steam_remoteplay = (Steam_RemotePlay *)object; // Get the number of currently connected Steam Remote Play sessions
steam_remoteplay->Callback(msg); uint32 GetSessionCount();
}
static void steam_run_every_runcb(void *object) // 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_ENTRY();
Steam_RemotePlay *steam_remoteplay = (Steam_RemotePlay *)object; // Get the SteamID of the connected user
steam_remoteplay->RunCallbacks(); CSteamID GetSessionSteamID( uint32 unSessionID );
}
Steam_RemotePlay(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb) // Get the name of the session client device
{ // This returns NULL if the sessionID is not valid
this->settings = settings; const char *GetSessionClientName( uint32 unSessionID );
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; // Get the form factor of the session client device
this->callbacks = callbacks; ESteamDeviceFormFactor GetSessionClientFormFactor( uint32 unSessionID );
}
~Steam_RemotePlay() // Get the resolution, in pixels, of the session client device
{ // This is set to 0x0 if the resolution is not available
//this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_RemotePlay::steam_callback, this); bool BGetSessionClientResolution( uint32 unSessionID, int *pnResolutionX, int *pnResolutionY );
this->run_every_runcb->remove(&Steam_RemotePlay::steam_run_every_runcb, this);
}
// Get the number of currently connected Steam Remote Play sessions bool BStartRemotePlayTogether( bool bShowOverlay );
uint32 GetSessionCount()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Get the currently connected Steam Remote Play session ID at the specified index. Returns zero if index is out of bounds. // Invite a friend to Remote Play Together
uint32 GetSessionID( int iSessionIndex ) // This returns false if the invite can't be sent
{ bool BSendRemotePlayTogetherInvite( CSteamID steamIDFriend );
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Get the SteamID of the connected user void RunCallbacks();
CSteamID GetSessionSteamID( uint32 unSessionID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_steamIDNil;
}
// Get the name of the session client device void Callback(Common_Message *msg);
// This returns NULL if the sessionID is not valid
const char *GetSessionClientName( uint32 unSessionID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return NULL;
}
// Get the form factor of the session client device
ESteamDeviceFormFactor GetSessionClientFormFactor( uint32 unSessionID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pnResolutionX) *pnResolutionX = 0;
if (pnResolutionY) *pnResolutionY = 0;
return false;
}
bool BStartRemotePlayTogether( bool bShowOverlay )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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()) {
}
}
}; };
#endif // __INCLUDED_STEAM_REMOTEPLAY_H__

View File

@ -15,21 +15,24 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_SCRNSHOTS_H__
#define __INCLUDED_STEAM_SCRNSHOTS_H__
#include "base.h" #include "base.h"
struct screenshot_infos_t struct screenshot_infos_t {
{ std::string screenshot_name{};
std::string screenshot_name; nlohmann::json metadatas{};
nlohmann::json metadatas;
}; };
class Steam_Screenshots : public ISteamScreenshots class Steam_Screenshots :
public ISteamScreenshots
{ {
bool hooked = false; class Local_Storage *local_storage{};
std::map<ScreenshotHandle, screenshot_infos_t> _screenshots; class SteamCallBacks *callbacks{};
class Local_Storage* local_storage; bool hooked = false;
class SteamCallBacks* callbacks; std::map<ScreenshotHandle, screenshot_infos_t> _screenshots{};
ScreenshotHandle create_screenshot_handle(); ScreenshotHandle create_screenshot_handle();
@ -73,3 +76,5 @@ public:
// JPEG, TGA, and PNG formats are supported. // JPEG, TGA, and PNG formats are supported.
ScreenshotHandle AddVRScreenshotToLibrary( EVRScreenshotType eType, const char *pchFilename, const char *pchVRFilename ); ScreenshotHandle AddVRScreenshotToLibrary( EVRScreenshotType eType, const char *pchFilename, const char *pchVRFilename );
}; };
#endif //__INCLUDED_STEAM_SCRNSHOTS_H__

View File

@ -15,117 +15,47 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_TV_H__
#define __INCLUDED_STEAM_TV_H__
#include "base.h" #include "base.h"
class Steam_TV : class Steam_TV :
public ISteamTV public ISteamTV
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb; class RunEveryRunCB *run_every_runcb{};
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now(); std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
FSteamNetworkingSocketsDebugOutput debug_function; FSteamNetworkingSocketsDebugOutput debug_function{};
static void steam_callback(void *object, Common_Message *msg);
static void steam_run_every_runcb(void *object);
public: public:
static void steam_callback(void *object, Common_Message *msg) Steam_TV(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
{ ~Steam_TV();
// PRINT_DEBUG_ENTRY();
Steam_TV *steam_parties = (Steam_TV *)object; bool IsBroadcasting(int *pnNumViewers);
steam_parties->Callback(msg);
}
static void steam_run_every_runcb(void *object) void AddBroadcastGameData(const char * pchKey, const char * pchValue);
{
// PRINT_DEBUG_ENTRY();
Steam_TV *steam_parties = (Steam_TV *)object; void RemoveBroadcastGameData(const char * pchKey);
steam_parties->RunCallbacks();
}
Steam_TV(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb) void AddTimelineMarker(const char * pchTemplateName, bool bPersistent, uint8 nColorR, uint8 nColorG, uint8 nColorB);
{
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; void RemoveTimelineMarker();
this->callbacks = callbacks;
}
~Steam_TV() uint32 AddRegion(const char * pchElementName, const char * pchTimelineDataSection, const SteamTVRegion_t * pSteamTVRegion, ESteamTVRegionBehavior eSteamTVRegionBehavior);
{
//this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_TV::steam_callback, this);
//this->run_every_runcb->remove(&Steam_TV::steam_run_every_runcb, this);
}
bool IsBroadcasting(int *pnNumViewers) void RemoveRegion(uint32 unRegionHandle);
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
void AddBroadcastGameData(const char * pchKey, const char * pchValue) void RunCallbacks();
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void RemoveBroadcastGameData(const char * pchKey) void Callback(Common_Message *msg);
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void AddTimelineMarker(const char * pchTemplateName, bool bPersistent, uint8 nColorR, uint8 nColorG, uint8 nColorB)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void RemoveTimelineMarker()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
uint32 AddRegion(const char * pchElementName, const char * pchTimelineDataSection, const SteamTVRegion_t * pSteamTVRegion, ESteamTVRegionBehavior eSteamTVRegionBehavior)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
void RemoveRegion(uint32 unRegionHandle)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
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()) {
}
}
}; };
#endif // __INCLUDED_STEAM_TV_H__

File diff suppressed because it is too large Load Diff

View File

@ -15,114 +15,53 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_UNIFIED_MESSAGES_H__
#define __INCLUDED_STEAM_UNIFIED_MESSAGES_H__
#include "base.h" #include "base.h"
class Steam_Unified_Messages : class Steam_Unified_Messages:
public ISteamUnifiedMessages public ISteamUnifiedMessages
{ {
class Settings *settings; class Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class RunEveryRunCB *run_every_runcb; class RunEveryRunCB *run_every_runcb{};
static void network_callback(void *object, Common_Message *msg);
static void steam_runcb(void *object);
public: public:
static void steam_callback(void *object, Common_Message *msg) Steam_Unified_Messages(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb);
{ ~Steam_Unified_Messages();
// PRINT_DEBUG_ENTRY();
Steam_Unified_Messages *steam_steamunifiedmessages = (Steam_Unified_Messages *)object; // Sends a service method (in binary serialized form) using the Steam Client.
steam_steamunifiedmessages->Callback(msg); // 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 );
static void steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
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()
{
// this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Unified_Messages::steam_callback, this);
// 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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return ISteamUnifiedMessages::k_InvalidUnifiedMessageHandle;
}
// Gets the size of the response and the EResult. Returns false if the response is not ready yet. // 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 ) bool GetMethodResponseInfo( ClientUnifiedMessageHandle hHandle, uint32 *punResponseSize, EResult *peResult );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Gets a response in binary serialized form (and optionally release the corresponding allocated memory). // Gets a response in binary serialized form (and optionally release the corresponding allocated memory).
bool GetMethodResponseData( ClientUnifiedMessageHandle hHandle, void *pResponseBuffer, uint32 unResponseBufferSize, bool bAutoRelease ) bool GetMethodResponseData( ClientUnifiedMessageHandle hHandle, void *pResponseBuffer, uint32 unResponseBufferSize, bool bAutoRelease );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Releases the message and its corresponding allocated memory. // Releases the message and its corresponding allocated memory.
bool ReleaseMethod( ClientUnifiedMessageHandle hHandle ) bool ReleaseMethod( ClientUnifiedMessageHandle hHandle );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Sends a service notification (in binary serialized form) using the Steam Client. // Sends a service notification (in binary serialized form) using the Steam Client.
// Returns true if the notification was sent successfully. // Returns true if the notification was sent successfully.
bool SendNotification( const char *pchServiceNotification, const void *pNotificationBuffer, uint32 unNotificationBufferSize ) bool SendNotification( const char *pchServiceNotification, const void *pNotificationBuffer, uint32 unNotificationBufferSize );
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
void RunCallbacks() void RunCallbacks();
{
}
void Callback(Common_Message *msg) 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) {
}
}
}
}; };
#endif // __INCLUDED_STEAM_UNIFIED_MESSAGES_H__

View File

@ -15,9 +15,11 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_USER_H__
#define __INCLUDED_STEAM_USER_H__
#include "base.h" #include "base.h"
#include "auth.h" #include "auth.h"
#include "appticket.h"
class Steam_User : class Steam_User :
public ISteamUser009, public ISteamUser009,
@ -35,524 +37,223 @@ public ISteamUser020,
public ISteamUser021, public ISteamUser021,
public ISteamUser public ISteamUser
{ {
Settings *settings; Settings *settings{};
class Networking *network; class Networking *network{};
class SteamCallBacks *callbacks; class SteamCallBacks *callbacks{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
Local_Storage *local_storage; Local_Storage *local_storage{};
bool recording = false; bool recording = false;
std::chrono::high_resolution_clock::time_point last_get_voice; std::chrono::high_resolution_clock::time_point last_get_voice{};
std::string encrypted_app_ticket; std::string encrypted_app_ticket{};
Auth_Manager *auth_manager; Auth_Manager *auth_manager{};
public: public:
Steam_User(Settings *settings, Local_Storage *local_storage, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks);
Steam_User(Settings *settings, Local_Storage *local_storage, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks) ~Steam_User();
{
this->settings = settings; // returns the HSteamUser this interface represents
this->local_storage = local_storage; // this is only used internally by the API, and by a few select interfaces that support multi-user
this->network = network; HSteamUser GetHSteamUser();
this->callbacks = callbacks;
this->callback_results = callback_results; // returns true if the Steam client current has a live connection to the Steam servers.
recording = false; // 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.
auth_manager = new Auth_Manager(settings, network, callbacks); // The Steam client will automatically be trying to recreate the connection as often as possible.
} bool BLoggedOn();
~Steam_User() // 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
delete auth_manager; CSteamID GetSteamID();
}
// Multiplayer Authentication functions
// returns the HSteamUser this interface represents
// this is only used internally by the API, and by a few select interfaces that support multi-user // InitiateGameConnection() starts the state machine for authenticating the game client with the game server
HSteamUser GetHSteamUser() // It is the client portion of a three-way handshake between the client, the game server, and the steam servers
{ //
PRINT_DEBUG_ENTRY(); // Parameters:
return CLIENT_HSTEAMUSER; // 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
// returns true if the Steam client current has a live connection to the Steam servers. // CGameID gameID - the ID of the current game. For games without mods, this is just CGameID( <appID> )
// 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. // uint32 unIPServer, uint16 usPortServer - the IP address of the game server
// The Steam client will automatically be trying to recreate the connection as often as possible. // bool bSecure - whether or not the client thinks that the game server is reporting itself as secure (i.e. VAC is running)
bool BLoggedOn() //
{ // 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
PRINT_DEBUG_ENTRY(); // The contents of pBlob should then be sent to the game server, for it to use to complete the authentication process.
return !settings->is_offline();
} int InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer, bool bSecure );
// returns the CSteamID of the account currently logged into the Steam client int InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, CGameID gameID, uint32 unIPServer, uint16 usPortServer, bool bSecure );
// a CSteamID is a unique identifier for an account, and used to differentiate users in all parts of the Steamworks API
CSteamID GetSteamID() // notify of disconnect
{ // needs to occur when the game client leaves the specified game server, needs to match with the InitiateGameConnection() call
PRINT_DEBUG_ENTRY(); void TerminateGameConnection( uint32 unIPServer, uint16 usPortServer );
CSteamID id = settings->get_local_steam_id();
// Legacy functions
return id;
} // used by only a few games to track usage events
void TrackAppUsageEvent( CGameID gameID, int eAppUsageEvent, const char *pchExtraInfo);
// Multiplayer Authentication functions
void RefreshSteam2Login();
// 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 // 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"
// Parameters: bool GetUserDataFolder( char *pchBuffer, int cubBuffer );
// 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. // Starts voice recording. Once started, use GetVoice() to get the data
// CSteamID steamIDGameServer - the steamID of the game server, received from the game server by the client void StartVoiceRecording( );
// 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 // Stops voice recording. Because people often release push-to-talk keys early, the system will keep recording for
// bool bSecure - whether or not the client thinks that the game server is reporting itself as secure (i.e. VAC is running) // a little bit after this function is called. GetVoice() should continue to be called until it returns
// // k_eVoiceResultNotRecording
// 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 void StopVoiceRecording( );
// The contents of pBlob should then be sent to the game server, for it to use to complete the authentication process.
// Determine the size of captured audio data that is available from GetVoice.
//steam returns 206 bytes // Most applications will only use compressed data and should ignore the other
#define INITIATE_GAME_CONNECTION_TICKET_SIZE 206 // parameters, which exist primarily for backwards compatibility. See comments
// below for further explanation of "uncompressed" data.
int InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer, bool bSecure ) EVoiceResult GetAvailableVoice( uint32 *pcbCompressed, uint32 *pcbUncompressed_Deprecated, uint32 nUncompressedVoiceDesiredSampleRate_Deprecated );
{
PRINT_DEBUG("%i %llu %u %u %u %p", cbMaxAuthBlob, steamIDGameServer.ConvertToUint64(), unIPServer, usPortServer, bSecure, pAuthBlob); EVoiceResult GetAvailableVoice(uint32 *pcbCompressed, uint32 *pcbUncompressed);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (cbMaxAuthBlob < INITIATE_GAME_CONNECTION_TICKET_SIZE) return 0; // ---------------------------------------------------------------------------
if (!pAuthBlob) return 0; // NOTE: "uncompressed" audio is a deprecated feature and should not be used
uint32 out_size = INITIATE_GAME_CONNECTION_TICKET_SIZE; // by most applications. It is raw single-channel 16-bit PCM wave data which
auth_manager->getTicketData(pAuthBlob, INITIATE_GAME_CONNECTION_TICKET_SIZE, &out_size); // may have been run through preprocessing filters and/or had silence removed,
if (out_size > INITIATE_GAME_CONNECTION_TICKET_SIZE) // so the uncompressed audio could have a shorter duration than you expect.
return 0; // There may be no data at all during long periods of silence. Also, fetching
return out_size; // 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
int InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, CGameID gameID, uint32 unIPServer, uint16 usPortServer, bool bSecure ) // really need to use uncompressed audio, you should call GetVoice frequently
{ // with two very large (20kb+) output buffers instead of trying to allocate
PRINT_DEBUG_ENTRY(); // perfectly-sized buffers. But most applications should ignore all of these
return InitiateGameConnection(pAuthBlob, cbMaxAuthBlob, steamIDGameServer, unIPServer, usPortServer, bSecure); // details and simply leave the "uncompressed" parameters as NULL/zero.
} // ---------------------------------------------------------------------------
// notify of disconnect // Read captured audio data from the microphone buffer. This should be called
// needs to occur when the game client leaves the specified game server, needs to match with the InitiateGameConnection() call // at least once per frame, and preferably every few milliseconds, to keep the
void TerminateGameConnection( uint32 unIPServer, uint16 usPortServer ) // microphone input delay as low as possible. Most applications will only use
{ // compressed data and should pass NULL/zero for the "uncompressed" parameters.
PRINT_DEBUG_TODO(); // 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 );
// Legacy functions
EVoiceResult GetVoice( bool bWantCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, bool bWantUncompressed, void *pUncompressedDestBuffer, uint32 cbUncompressedDestBufferSize, uint32 *nUncompressBytesWritten );
// used by only a few games to track usage events
void TrackAppUsageEvent( CGameID gameID, int eAppUsageEvent, const char *pchExtraInfo) EVoiceResult GetCompressedVoice( void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten );
{
PRINT_DEBUG_TODO(); // 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.
void RefreshSteam2Login() // If the output buffer is not large enough, then *nBytesWritten will be set
{ // to the required buffer size, and k_EVoiceResultBufferTooSmall is returned.
PRINT_DEBUG_TODO(); // 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 );
// get the local storage folder for current Steam account to write application data, e.g. save games, configs etc. EVoiceResult DecompressVoice( const void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten );
// this will usually be something like "C:\Progam Files\Steam\userdata\<SteamID>\<AppID>\local"
bool GetUserDataFolder( char *pchBuffer, int cubBuffer ) EVoiceResult DecompressVoice( void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten );
{
PRINT_DEBUG_ENTRY(); // This returns the native sample rate of the Steam voice decompressor
if (!cubBuffer) return false; // this sample rate for DecompressVoice will perform the least CPU processing.
// However, the final audio quality will depend on how well the audio device
std::string user_data = local_storage->get_path(Local_Storage::user_data_storage); // (and/or your application's audio output SDK) deals with lower sample rates.
strncpy(pchBuffer, user_data.c_str(), cubBuffer - 1); // You may find that you get the best audio output quality when you ignore
pchBuffer[cubBuffer - 1] = 0; // this function and use the native sample rate of your audio output device,
return true; // which is usually 48000 or 44100.
} uint32 GetVoiceOptimalSampleRate();
// Starts voice recording. Once started, use GetVoice() to get the data // Retrieve ticket to be sent to the entity who wishes to authenticate you.
void StartVoiceRecording( ) // pcbTicket retrieves the length of the actual ticket.
{ HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket );
PRINT_DEBUG_ENTRY(); // SteamNetworkingIdentity is an optional input parameter to hold the public IP address or SteamID of the entity you are connecting to
last_get_voice = std::chrono::high_resolution_clock::now(); // if an IP address is passed Steam will only allow the ticket to be used by an entity with that IP address
recording = true; // if a Steam ID is passed Steam will only allow the ticket to be used by that Steam ID
//TODO:fix HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket, const SteamNetworkingIdentity *pSteamNetworkingIdentity );
recording = false;
} // Request a ticket which will be used for webapi "ISteamUserAuth\AuthenticateUserTicket"
// pchIdentity is an optional input parameter to identify the service the ticket will be sent to
// Stops voice recording. Because people often release push-to-talk keys early, the system will keep recording for // the ticket will be returned in callback GetTicketForWebApiResponse_t
// a little bit after this function is called. GetVoice() should continue to be called until it returns HAuthTicket GetAuthTicketForWebApi( const char *pchIdentity );
// k_eVoiceResultNotRecording
void StopVoiceRecording( ) // 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 )
PRINT_DEBUG_ENTRY(); EBeginAuthSessionResult BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID );
recording = false;
} // Stop tracking started by BeginAuthSession - called when no longer playing game with this entity
void EndAuthSession( CSteamID steamID );
// Determine the size of captured audio data that is available from GetVoice.
// Most applications will only use compressed data and should ignore the other // Cancel auth ticket from GetAuthSessionTicket, called when no longer playing game with the entity you gave the ticket to
// parameters, which exist primarily for backwards compatibility. See comments void CancelAuthTicket( HAuthTicket hAuthTicket );
// below for further explanation of "uncompressed" data.
EVoiceResult GetAvailableVoice( uint32 *pcbCompressed, uint32 *pcbUncompressed_Deprecated, uint32 nUncompressedVoiceDesiredSampleRate_Deprecated ) // 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.
PRINT_DEBUG_ENTRY(); EUserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID );
if (pcbCompressed) *pcbCompressed = 0;
if (pcbUncompressed_Deprecated) *pcbUncompressed_Deprecated = 0; // returns true if this users looks like they are behind a NAT device. Only valid once the user has connected to steam
if (!recording) return k_EVoiceResultNotRecording; // (i.e a SteamServersConnected_t has been issued) and may not catch all forms of NAT.
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - last_get_voice).count(); bool BIsBehindNAT();
if (pcbCompressed) *pcbCompressed = seconds * 1024.0 * 64.0 / 8.0;
if (pcbUncompressed_Deprecated) *pcbUncompressed_Deprecated = seconds * (double)nUncompressedVoiceDesiredSampleRate_Deprecated * 2.0; // 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
return k_EVoiceResultOK; // uint32 unIPServer, uint16 usPortServer - the IP address of the game server
} void AdvertiseGame( CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer );
EVoiceResult GetAvailableVoice(uint32 *pcbCompressed, uint32 *pcbUncompressed) // Requests a ticket encrypted with an app specific shared key
{ // pDataToInclude, cbDataToInclude will be encrypted into the ticket
PRINT_DEBUG("old"); // ( This is asynchronous, you must wait for the ticket to be completed by the server )
return GetAvailableVoice(pcbCompressed, pcbUncompressed, 11025); STEAM_CALL_RESULT( EncryptedAppTicketResponse_t )
} SteamAPICall_t RequestEncryptedAppTicket( void *pDataToInclude, int cbDataToInclude );
// --------------------------------------------------------------------------- // retrieve a finished ticket
// NOTE: "uncompressed" audio is a deprecated feature and should not be used bool GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket );
// 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, // Trading Card badges data access
// so the uncompressed audio could have a shorter duration than you expect. // if you only have one set of cards, the series will be 1
// There may be no data at all during long periods of silence. Also, fetching // the user has can have two different badges for a series; the regular (max level 5) and the foil (max level 1)
// uncompressed audio will cause GetVoice to discard any leftover compressed int GetGameBadgeLevel( int nSeries, bool bFoil );
// audio, so you must fetch both types at once. Finally, GetAvailableVoice is
// not precisely accurate when the uncompressed size is requested. So if you // gets the Steam Level of the user, as shown on their profile
// really need to use uncompressed audio, you should call GetVoice frequently int GetPlayerSteamLevel();
// with two very large (20kb+) output buffers instead of trying to allocate
// perfectly-sized buffers. But most applications should ignore all of these // Requests a URL which authenticates an in-game browser for store check-out,
// details and simply leave the "uncompressed" parameters as NULL/zero. // 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.
// Read captured audio data from the microphone buffer. This should be called // The result of this API call will be a StoreAuthURLResponse_t callback.
// at least once per frame, and preferably every few milliseconds, to keep the // NOTE: The URL has a very short lifetime to prevent history-snooping attacks,
// microphone input delay as low as possible. Most applications will only use // so you should only call this API when you are about to launch the browser,
// compressed data and should pass NULL/zero for the "uncompressed" parameters. // or else immediately navigate to the result URL using a hidden browser window.
// Compressed data can be transmitted by your application and decoded into raw // NOTE 2: The resulting authorization cookie has an expiration time of one day,
// using the DecompressVoice function below. // so it would be a good idea to request and visit a new auth URL every 12 hours.
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 ) STEAM_CALL_RESULT( StoreAuthURLResponse_t )
{ SteamAPICall_t RequestStoreAuthURL( const char *pchRedirectURL );
PRINT_DEBUG_ENTRY();
if (!recording) return k_EVoiceResultNotRecording; // gets whether the users phone number is verified
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - last_get_voice).count(); bool BIsPhoneVerified();
if (bWantCompressed) {
uint32 towrite = seconds * 1024.0 * 64.0 / 8.0; // gets whether the user has two factor enabled on their account
if (cbDestBufferSize < towrite) towrite = cbDestBufferSize; bool BIsTwoFactorEnabled();
if (pDestBuffer) memset(pDestBuffer, 0, towrite);
if (nBytesWritten) *nBytesWritten = towrite; // gets whether the users phone number is identifying
} bool BIsPhoneIdentifying();
if (bWantUncompressed_Deprecated) { // gets whether the users phone number is awaiting (re)verification
PRINT_DEBUG("Wanted Uncompressed"); bool BIsPhoneRequiringVerification();
}
STEAM_CALL_RESULT( MarketEligibilityResponse_t )
last_get_voice = std::chrono::high_resolution_clock::now(); SteamAPICall_t GetMarketEligibility();
return k_EVoiceResultOK;
} // Retrieves anti indulgence / duration control for current user
STEAM_CALL_RESULT( DurationControl_t )
EVoiceResult GetVoice( bool bWantCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, bool bWantUncompressed, void *pUncompressedDestBuffer, uint32 cbUncompressedDestBufferSize, uint32 *nUncompressBytesWritten ) SteamAPICall_t GetDurationControl();
{
PRINT_DEBUG("old"); // Advise steam china duration control system about the online state of the game.
return GetVoice(bWantCompressed, pDestBuffer, cbDestBufferSize, nBytesWritten, bWantUncompressed, pUncompressedDestBuffer, cbUncompressedDestBufferSize, nUncompressBytesWritten, 11025); // This will prevent offline gameplay time from counting against a user's
} // playtime limits.
bool BSetDurationControlOnlineState( EDurationControlOnlineState eNewState );
EVoiceResult GetCompressedVoice( void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
{
PRINT_DEBUG_ENTRY();
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_ENTRY();
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("old");
return DecompressVoice(pCompressed, cbCompressed, pDestBuffer, cbDestBufferSize, nBytesWritten, 11025);
}
EVoiceResult DecompressVoice( void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
{
PRINT_DEBUG("older");
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_ENTRY();
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 )
{
return GetAuthSessionTicket(pTicket, cbMaxTicket, pcbTicket, NULL);
}
// SteamNetworkingIdentity is an optional input parameter to hold the public IP address or SteamID of the entity you are connecting to
// if an IP address is passed Steam will only allow the ticket to be used by an entity with that IP address
// if a Steam ID is passed Steam will only allow the ticket to be used by that Steam ID
HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket, const SteamNetworkingIdentity *pSteamNetworkingIdentity )
{
PRINT_DEBUG("%p [%i] %p", pTicket, cbMaxTicket, pcbTicket);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!pTicket) return k_HAuthTicketInvalid;
return auth_manager->getTicket(pTicket, cbMaxTicket, pcbTicket);
}
// Request a ticket which will be used for webapi "ISteamUserAuth\AuthenticateUserTicket"
// pchIdentity is an optional input parameter to identify the service the ticket will be sent to
// the ticket will be returned in callback GetTicketForWebApiResponse_t
HAuthTicket GetAuthTicketForWebApi( const char *pchIdentity )
{
PRINT_DEBUG("%s", pchIdentity);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return auth_manager->getWebApiTicket(pchIdentity);
}
// 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("%i %llu", cbAuthTicket, steamID.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return auth_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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auth_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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auth_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_ENTRY();
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_ENTRY();
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_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
Gameserver *server = new Gameserver();
server->set_id(steamIDGameServer.ConvertToUint64());
server->set_ip(unIPServer);
server->set_port(usPortServer);
server->set_query_port(usPortServer);
server->set_appid(settings->get_local_game_id().ToUint64());
if (settings->matchmaking_server_list_always_lan_type)
server->set_type(eLANServer);
else
server->set_type(eFriendsServer);
Common_Message msg;
msg.set_allocated_gameserver(server);
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
network->sendToAllIndividuals(&msg, true);
}
// 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("%i", cbDataToInclude);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
EncryptedAppTicketResponse_t data;
data.m_eResult = k_EResultOK;
DecryptedAppTicket ticket;
ticket.TicketV1.Reset();
ticket.TicketV2.Reset();
ticket.TicketV4.Reset();
ticket.TicketV1.TicketVersion = 1;
if (pDataToInclude) {
ticket.TicketV1.UserData.assign((uint8_t*)pDataToInclude, (uint8_t*)pDataToInclude + cbDataToInclude);
}
ticket.TicketV2.TicketVersion = 4;
ticket.TicketV2.SteamID = settings->get_local_steam_id().ConvertToUint64();
ticket.TicketV2.TicketIssueTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
ticket.TicketV2.TicketValidityEnd = ticket.TicketV2.TicketIssueTime + (21 * 24 * 60 * 60);
for (int i = 0; i < 140; ++i)
{
AppId_t appid;
bool available;
std::string name;
if (!settings->getDLC(appid, appid, available, name)) break;
ticket.TicketV4.AppIDs.emplace_back(appid);
}
ticket.TicketV4.HasVACStatus = true;
ticket.TicketV4.VACStatus = 0;
auto serialized = ticket.SerializeTicket();
SteamAppTicket_pb pb;
pb.set_ticket_version_no(1);
pb.set_crc_encryptedticket(0); // TODO: Find out how to compute the CRC
pb.set_cb_encrypteduserdata(cbDataToInclude);
pb.set_cb_encrypted_appownershipticket(serialized.size() - 16);
pb.mutable_encrypted_ticket()->assign(serialized.begin(), serialized.end()); // TODO: Find how to encrypt datas
encrypted_app_ticket = pb.SerializeAsString();
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// retrieve a finished ticket
bool GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{
PRINT_DEBUG("%i", cbMaxTicket);
unsigned int ticket_size = encrypted_app_ticket.size();
if (!cbMaxTicket) {
if (!pcbTicket) return false;
*pcbTicket = ticket_size;
return true;
}
if (!pTicket) return false;
if (ticket_size > cbMaxTicket) return false;
encrypted_app_ticket.copy((char *)pTicket, cbMaxTicket);
if (pcbTicket) *pcbTicket = ticket_size;
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_ENTRY();
return 0;
}
// gets the Steam Level of the user, as shown on their profile
int GetPlayerSteamLevel()
{
PRINT_DEBUG_ENTRY();
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_ENTRY();
return 0;
}
// gets whether the users phone number is verified
bool BIsPhoneVerified()
{
PRINT_DEBUG_ENTRY();
return true;
}
// gets whether the user has two factor enabled on their account
bool BIsTwoFactorEnabled()
{
PRINT_DEBUG_ENTRY();
return true;
}
// gets whether the users phone number is identifying
bool BIsPhoneIdentifying()
{
PRINT_DEBUG_ENTRY();
return false;
}
// gets whether the users phone number is awaiting (re)verification
bool BIsPhoneRequiringVerification()
{
PRINT_DEBUG_ENTRY();
return false;
}
STEAM_CALL_RESULT( MarketEligibilityResponse_t )
SteamAPICall_t GetMarketEligibility()
{
PRINT_DEBUG_ENTRY();
return 0;
}
// Retrieves anti indulgence / duration control for current user
STEAM_CALL_RESULT( DurationControl_t )
SteamAPICall_t GetDurationControl()
{
PRINT_DEBUG_ENTRY();
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_ENTRY();
return false;
}
}; };
#endif // __INCLUDED_STEAM_USER_H__

View File

@ -50,21 +50,8 @@ struct achievement_trigger {
std::string min_value{}; std::string min_value{};
std::string max_value{}; std::string max_value{};
bool check_triggered(float stat) { bool check_triggered(float stat) const;
try { bool check_triggered(int32 stat) const;
if (std::stof(max_value) <= stat) return true;
} catch (...) {}
return false;
}
bool check_triggered(int32 stat) {
try {
if (std::stoi(max_value) <= stat) return true;
} catch (...) {}
return false;
}
}; };
class Steam_User_Stats : class Steam_User_Stats :
@ -91,9 +78,9 @@ private:
T current_val{}; T current_val{};
}; };
Local_Storage *local_storage{}; class Local_Storage *local_storage{};
Settings *settings{}; class Settings *settings{};
SteamCallResults *callback_results{}; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks{}; class SteamCallBacks *callbacks{};
class Networking *network{}; class Networking *network{};
class RunEveryRunCB *run_every_runcb{}; class RunEveryRunCB *run_every_runcb{};
@ -115,7 +102,6 @@ private:
GameServerStats_Messages::AllStats pending_server_updates{}; GameServerStats_Messages::AllStats pending_server_updates{};
void load_achievements_db(); void load_achievements_db();
void load_achievements(); void load_achievements();
void save_achievements(); void save_achievements();

View File

@ -1,5 +1,3 @@
#ifndef __STEAM_UTILS_H__
#define __STEAM_UTILS_H__
/* Copyright (C) 2019 Mr Goldberg /* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator This file is part of the Goldberg Emulator
@ -18,6 +16,9 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __STEAM_UTILS_H__
#define __STEAM_UTILS_H__
#include "common_includes.h" #include "common_includes.h"
#include "local_storage.h" #include "local_storage.h"
#include "overlay/steam_overlay.h" #include "overlay/steam_overlay.h"
@ -35,10 +36,10 @@ public ISteamUtils009,
public ISteamUtils public ISteamUtils
{ {
private: private:
Settings *settings; Settings *settings{};
class SteamCallResults *callback_results; class SteamCallResults *callback_results{};
class SteamCallBacks *callbacks{}; class SteamCallBacks *callbacks{};
Steam_Overlay* overlay; Steam_Overlay* overlay{};
public: public:
Steam_Utils(Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, Steam_Overlay *overlay); Steam_Utils(Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, Steam_Overlay *overlay);

View File

@ -15,9 +15,13 @@
License along with the Goldberg Emulator; if not, see License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_VIDEO_H__
#define __INCLUDED_STEAM_VIDEO_H__
#include "base.h" #include "base.h"
class Steam_Video : public ISteamVideo class Steam_Video :
public ISteamVideo
{ {
public: public:
@ -32,3 +36,5 @@ public:
void GetOPFSettings( AppId_t unVideoAppID ); void GetOPFSettings( AppId_t unVideoAppID );
bool GetOPFStringForApp( AppId_t unVideoAppID, char *pchBuffer, int32 *pnBufferSize ); bool GetOPFStringForApp( AppId_t unVideoAppID, char *pchBuffer, int32 *pnBufferSize );
}; };
#endif // __INCLUDED_STEAM_VIDEO_H__

View File

@ -1,3 +1,19 @@
/* 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_UGC_REMOTE_STORAGE_BRIDGE_H__ #ifndef __INCLUDED_UGC_REMOTE_STORAGE_BRIDGE_H__
#define __INCLUDED_UGC_REMOTE_STORAGE_BRIDGE_H__ #define __INCLUDED_UGC_REMOTE_STORAGE_BRIDGE_H__
@ -8,19 +24,20 @@ class Ugc_Remote_Storage_Bridge
{ {
public: public:
struct QueryInfo { struct QueryInfo {
PublishedFileId_t mod_id; // mod id PublishedFileId_t mod_id{}; // mod id
bool is_primary_file; // was this query for the primary mod file or preview file bool is_primary_file{}; // was this query for the primary mod file or preview file
}; };
private: private:
class Settings *settings; class Settings *settings{};
// key: UGCHandle_t which is the file handle (primary or preview) // key: UGCHandle_t which is the file handle (primary or preview)
// value: the mod id, true if UGCHandle_t of primary file | false if UGCHandle_t of preview file // value: the mod id, true if UGCHandle_t of primary file | false if UGCHandle_t of preview file
std::map<UGCHandle_t, QueryInfo> steam_ugc_queries{}; std::map<UGCHandle_t, QueryInfo> steam_ugc_queries{};
std::set<PublishedFileId_t> subscribed; // just to keep the running state of subscription std::set<PublishedFileId_t> subscribed{}; // just to keep the running state of subscription
public: public:
Ugc_Remote_Storage_Bridge(class Settings *settings); Ugc_Remote_Storage_Bridge(class Settings *settings);
~Ugc_Remote_Storage_Bridge();
// called from Steam_UGC::SendQueryUGCRequest() after a successful query // called from Steam_UGC::SendQueryUGCRequest() after a successful query
void add_ugc_query_result(UGCHandle_t file_handle, PublishedFileId_t fileid, bool handle_of_primary_file); void add_ugc_query_result(UGCHandle_t file_handle, PublishedFileId_t fileid, bool handle_of_primary_file);
@ -34,7 +51,6 @@ public:
std::set<PublishedFileId_t>::iterator subbed_mods_itr_begin() const; std::set<PublishedFileId_t>::iterator subbed_mods_itr_begin() const;
std::set<PublishedFileId_t>::iterator subbed_mods_itr_end() const; std::set<PublishedFileId_t>::iterator subbed_mods_itr_end() const;
~Ugc_Remote_Storage_Bridge();
}; };

View File

@ -121,6 +121,19 @@ static void merge_ini(const CSimpleIniA &new_ini, bool overwrite = false) {
} }
Overlay_Appearance::NotificationPosition Overlay_Appearance::translate_notification_position(const std::string &str)
{
if (str == "top_left") return NotificationPosition::top_left;
else if (str == "top_center") return NotificationPosition::top_center;
else if (str == "top_right") return NotificationPosition::top_right;
else if (str == "bot_left") return NotificationPosition::bot_left;
else if (str == "bot_center") return NotificationPosition::bot_center;
else if (str == "bot_right") return NotificationPosition::bot_right;
PRINT_DEBUG("Invalid position '%s'", str.c_str());
return default_pos;
}
// custom_broadcasts.txt // custom_broadcasts.txt
static void load_custom_broadcasts(const std::string &base_path, std::set<IP_PORT> &custom_broadcasts) static void load_custom_broadcasts(const std::string &base_path, std::set<IP_PORT> &custom_broadcasts)

View File

@ -18,58 +18,48 @@
#include "dll/source_query.h" #include "dll/source_query.h"
#include "dll/dll.h" #include "dll/dll.h"
using lock_t = std::lock_guard<std::mutex>; enum class source_query_magic : uint32_t {
enum class source_query_magic : uint32_t
{
simple = 0xFFFFFFFFul, simple = 0xFFFFFFFFul,
multi = 0xFFFFFFFEul, // <--- TODO ? multi = 0xFFFFFFFEul, // <--- TODO ?
}; };
enum class source_query_header : uint8_t enum class source_query_header : uint8_t {
{
A2S_INFO = 'T', A2S_INFO = 'T',
A2S_PLAYER = 'U', A2S_PLAYER = 'U',
A2S_RULES = 'V', A2S_RULES = 'V',
}; };
enum class source_response_header : uint8_t enum class source_response_header : uint8_t {
{
A2S_CHALLENGE = 'A', A2S_CHALLENGE = 'A',
A2S_INFO = 'I', A2S_INFO = 'I',
A2S_PLAYER = 'D', A2S_PLAYER = 'D',
A2S_RULES = 'E', A2S_RULES = 'E',
}; };
enum class source_server_type : uint8_t enum class source_server_type : uint8_t {
{
dedicated = 'd', dedicated = 'd',
non_dedicated = 'i', non_dedicated = 'i',
source_tc = 'p', source_tc = 'p',
}; };
enum class source_server_env : uint8_t enum class source_server_env : uint8_t {
{
linux = 'l', linux = 'l',
windows = 'w', windows = 'w',
old_mac = 'm', old_mac = 'm',
mac = 'o', mac = 'o',
}; };
enum class source_server_visibility : uint8_t enum class source_server_visibility : uint8_t {
{
_public = 0, _public = 0,
_private = 1, _private = 1,
}; };
enum class source_server_vac : uint8_t enum class source_server_vac : uint8_t {
{
unsecured = 0, unsecured = 0,
secured = 1, secured = 1,
}; };
enum source_server_extra_flag : uint8_t enum source_server_extra_flag : uint8_t {
{
none = 0x00, none = 0x00,
gameid = 0x01, gameid = 0x01,
steamid = 0x10, steamid = 0x10,
@ -79,21 +69,20 @@ enum source_server_extra_flag : uint8_t
}; };
#if defined(STEAM_WIN32) #if defined(STEAM_WIN32)
static constexpr source_server_env my_server_env = source_server_env::windows; static constexpr const source_server_env my_server_env = source_server_env::windows;
#else #else
static constexpr source_server_env my_server_env = source_server_env::linux; static constexpr const source_server_env my_server_env = source_server_env::linux;
#endif #endif
#pragma pack(push) #pragma pack(push)
#pragma pack(1) #pragma pack(1)
constexpr static const char a2s_info_payload[] = "Source Engine Query";
constexpr static const size_t a2s_info_payload_size = sizeof(a2s_info_payload);
constexpr char a2s_info_payload[] = "Source Engine Query"; struct source_query_data {
constexpr size_t a2s_info_payload_size = sizeof(a2s_info_payload); source_query_magic magic{};
source_query_header header{};
struct source_query_data
{
source_query_magic magic;
source_query_header header;
union union
{ {
char a2s_info_payload[a2s_info_payload_size]; char a2s_info_payload[a2s_info_payload_size];
@ -101,12 +90,12 @@ struct source_query_data
}; };
}; };
static constexpr size_t source_query_header_size = sizeof(source_query_magic) + sizeof(source_query_header); static constexpr const size_t source_query_header_size = sizeof(source_query_magic) + sizeof(source_query_header);
static constexpr size_t a2s_query_info_size = source_query_header_size + sizeof(source_query_data::a2s_info_payload); static constexpr const size_t a2s_query_info_size = source_query_header_size + sizeof(source_query_data::a2s_info_payload);
static constexpr size_t a2s_query_challenge_size = source_query_header_size + sizeof(source_query_data::challenge); static constexpr const size_t a2s_query_challenge_size = source_query_header_size + sizeof(source_query_data::challenge);
#pragma pack(pop) #pragma pack(pop)
void serialize_response(std::vector<uint8_t>& buffer, const void* _data, size_t len) void serialize_response(std::vector<uint8_t>& buffer, const void* _data, size_t len)
{ {
const uint8_t* data = reinterpret_cast<const uint8_t*>(_data); const uint8_t* data = reinterpret_cast<const uint8_t*>(_data);
@ -156,10 +145,9 @@ std::vector<uint8_t> Source_Query::handle_source_query(const void* buffer, size_
switch (query.header) switch (query.header)
{ {
case source_query_header::A2S_INFO: case source_query_header::A2S_INFO: {
PRINT_DEBUG("got request for server info"); PRINT_DEBUG("got request for server info");
if (len >= a2s_query_info_size && !strncmp(query.a2s_info_payload, a2s_info_payload, a2s_info_payload_size)) if (len >= a2s_query_info_size && !strncmp(query.a2s_info_payload, a2s_info_payload, a2s_info_payload_size)) {
{
std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>> const& players = *get_steam_client()->steam_gameserver->get_players(); std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>> const& players = *get_steam_client()->steam_gameserver->get_players();
serialize_response(output_buffer, source_query_magic::simple); serialize_response(output_buffer, source_query_magic::simple);
@ -181,93 +169,77 @@ std::vector<uint8_t> Source_Query::handle_source_query(const void* buffer, size_
uint8_t flags = source_server_extra_flag::none; uint8_t flags = source_server_extra_flag::none;
if (gs.port() != 0) if (gs.port() != 0) flags |= source_server_extra_flag::port;
flags |= source_server_extra_flag::port;
if (gs.spectator_port() != 0) if (gs.spectator_port() != 0) flags |= source_server_extra_flag::spectator;
flags |= source_server_extra_flag::spectator;
if(CGameID(gs.appid()).IsValid()) if (CGameID(gs.appid()).IsValid()) flags |= source_server_extra_flag::gameid;
flags |= source_server_extra_flag::gameid;
if (flags != source_server_extra_flag::none) if (flags != source_server_extra_flag::none) serialize_response(output_buffer, flags);
serialize_response(output_buffer, flags);
if (flags & source_server_extra_flag::port) if (flags & source_server_extra_flag::port) serialize_response(output_buffer, static_cast<uint16_t>(gs.port()));
serialize_response(output_buffer, static_cast<uint16_t>(gs.port()));
// add steamid // add steamid
if (flags & source_server_extra_flag::spectator) if (flags & source_server_extra_flag::spectator) {
{
serialize_response(output_buffer, static_cast<uint16_t>(gs.spectator_port())); serialize_response(output_buffer, static_cast<uint16_t>(gs.spectator_port()));
serialize_response(output_buffer, gs.spectator_server_name()); serialize_response(output_buffer, gs.spectator_server_name());
} }
// keywords // keywords
if (flags & source_server_extra_flag::gameid) if (flags & source_server_extra_flag::gameid) serialize_response(output_buffer, CGameID(gs.appid()).ToUint64());
serialize_response(output_buffer, CGameID(gs.appid()).ToUint64());
} }
break; }
break;
case source_query_header::A2S_PLAYER: case source_query_header::A2S_PLAYER: {
PRINT_DEBUG("got request for player info"); PRINT_DEBUG("got request for player info");
if (len >= a2s_query_challenge_size) if (len >= a2s_query_challenge_size) {
{ if (query.challenge == 0xFFFFFFFFul) {
if (query.challenge == 0xFFFFFFFFul)
{
get_challenge(output_buffer); get_challenge(output_buffer);
} } else if (query.challenge == 0x00112233ul) {
else if (query.challenge == 0x00112233ul)
{
std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>> const& players = *get_steam_client()->steam_gameserver->get_players(); std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>> const& players = *get_steam_client()->steam_gameserver->get_players();
serialize_response(output_buffer, source_query_magic::simple); serialize_response(output_buffer, source_query_magic::simple);
serialize_response(output_buffer, source_response_header::A2S_PLAYER); serialize_response(output_buffer, source_response_header::A2S_PLAYER);
serialize_response(output_buffer, static_cast<uint8_t>(players.size())); // num_players serialize_response(output_buffer, static_cast<uint8_t>(players.size())); // num_players
for (int i = 0; i < players.size(); ++i) for (int i = 0; i < players.size(); ++i) {
{
serialize_response(output_buffer, static_cast<uint8_t>(i)); // player index serialize_response(output_buffer, static_cast<uint8_t>(i)); // player index
serialize_response(output_buffer, players[i].second.name); // player name serialize_response(output_buffer, players[i].second.name); // player name
serialize_response(output_buffer, players[i].second.score); // player score serialize_response(output_buffer, players[i].second.score); // player score
serialize_response(output_buffer, static_cast<float>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - players[i].second.join_time).count())); serialize_response(output_buffer, static_cast<float>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - players[i].second.join_time).count()));
} }
} }
} }
break; }
break;
case source_query_header::A2S_RULES: case source_query_header::A2S_RULES: {
PRINT_DEBUG("got request for rules info"); PRINT_DEBUG("got request for rules info");
if (len >= a2s_query_challenge_size) if (len >= a2s_query_challenge_size) {
{ if (query.challenge == 0xFFFFFFFFul) {
if (query.challenge == 0xFFFFFFFFul)
{
get_challenge(output_buffer); get_challenge(output_buffer);
} } else if (query.challenge == 0x00112233ul) {
else if (query.challenge == 0x00112233ul)
{
auto values = gs.values(); auto values = gs.values();
serialize_response(output_buffer, source_query_magic::simple); serialize_response(output_buffer, source_query_magic::simple);
serialize_response(output_buffer, source_response_header::A2S_RULES); serialize_response(output_buffer, source_response_header::A2S_RULES);
serialize_response(output_buffer, static_cast<uint16_t>(values.size())); serialize_response(output_buffer, static_cast<uint16_t>(values.size()));
for (auto const& i : values) for (const auto &i : values) {
{
serialize_response(output_buffer, i.first); serialize_response(output_buffer, i.first);
serialize_response(output_buffer, i.second); serialize_response(output_buffer, i.second);
} }
} }
} }
break;
default:
PRINT_DEBUG("got unknown request");
break;
} }
break;
default: PRINT_DEBUG("got unknown request"); break;
}
return output_buffer; return output_buffer;
} }

363
dll/steam_HTMLsurface.cpp Normal file
View File

@ -0,0 +1,363 @@
/* 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 "dll/steam_HTMLsurface.h"
Steam_HTMLsurface::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 Steam_HTMLsurface::Init()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
bool Steam_HTMLsurface::Shutdown()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_HTMLsurface::CreateBrowser( const char *pchUserAgent, const char *pchUserCSS )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
HTML_BrowserReady_t data;
data.unBrowserHandle = 1234869;
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return ret;
}
// Call this when you are done with a html surface, this lets us free the resources being used by it
void Steam_HTMLsurface::RemoveBrowser( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Navigate to this URL, results in a HTML_StartRequest_t as the request commences
void Steam_HTMLsurface::LoadURL( HHTMLBrowser unBrowserHandle, const char *pchURL, const char *pchPostData )
{
PRINT_DEBUG("TODO %s %s", 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 Steam_HTMLsurface::SetSize( HHTMLBrowser unBrowserHandle, uint32 unWidth, uint32 unHeight )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Stop the load of the current html page
void Steam_HTMLsurface::StopLoad( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Reload (most likely from local cache) the current page
void Steam_HTMLsurface::Reload( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// navigate back in the page history
void Steam_HTMLsurface::GoBack( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// navigate forward in the page history
void Steam_HTMLsurface::GoForward( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// add this header to any url requests from this browser
void Steam_HTMLsurface::AddHeader( HHTMLBrowser unBrowserHandle, const char *pchKey, const char *pchValue )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// run this javascript script in the currently loaded page
void Steam_HTMLsurface::ExecuteJavascript( HHTMLBrowser unBrowserHandle, const char *pchScript )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Mouse click and mouse movement commands
void Steam_HTMLsurface::MouseUp( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void Steam_HTMLsurface::MouseDown( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void Steam_HTMLsurface::MouseDoubleClick( HHTMLBrowser unBrowserHandle, EHTMLMouseButton eMouseButton )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// x and y are relative to the HTML bounds
void Steam_HTMLsurface::MouseMove( HHTMLBrowser unBrowserHandle, int x, int y )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// nDelta is pixels of scroll
void Steam_HTMLsurface::MouseWheel( HHTMLBrowser unBrowserHandle, int32 nDelta )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// keyboard interactions, native keycode is the key code value from your OS
void Steam_HTMLsurface::KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers, bool bIsSystemKey )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void Steam_HTMLsurface::KeyDown( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
KeyDown(unBrowserHandle, nNativeKeyCode, eHTMLKeyModifiers, false);
}
void Steam_HTMLsurface::KeyUp( HHTMLBrowser unBrowserHandle, uint32 nNativeKeyCode, EHTMLKeyModifiers eHTMLKeyModifiers )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// cUnicodeChar is the unicode character point for this keypress (and potentially multiple chars per press)
void Steam_HTMLsurface::KeyChar( HHTMLBrowser unBrowserHandle, uint32 cUnicodeChar, EHTMLKeyModifiers eHTMLKeyModifiers )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// programmatically scroll this many pixels on the page
void Steam_HTMLsurface::SetHorizontalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void Steam_HTMLsurface::SetVerticalScroll( HHTMLBrowser unBrowserHandle, uint32 nAbsolutePixelScroll )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// tell the html control if it has key focus currently, controls showing the I-beam cursor in text controls amongst other things
void Steam_HTMLsurface::SetKeyFocus( HHTMLBrowser unBrowserHandle, bool bHasKeyFocus )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// open the current pages html code in the local editor of choice, used for debugging
void Steam_HTMLsurface::ViewSource( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// copy the currently selected text on the html page to the local clipboard
void Steam_HTMLsurface::CopyToClipboard( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// paste from the local clipboard to the current html page
void Steam_HTMLsurface::PasteFromClipboard( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// find this string in the browser, if bCurrentlyInFind is true then instead cycle to the next matching element
void Steam_HTMLsurface::Find( HHTMLBrowser unBrowserHandle, const char *pchSearchStr, bool bCurrentlyInFind, bool bReverse )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// cancel a currently running find
void Steam_HTMLsurface::StopFind( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// return details about the link at position x,y on the current page
void Steam_HTMLsurface::GetLinkAtPosition( HHTMLBrowser unBrowserHandle, int x, int y )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// set a webcookie for the hostname in question
void Steam_HTMLsurface::SetCookie( const char *pchHostname, const char *pchKey, const char *pchValue, const char *pchPath, RTime32 nExpires, bool bSecure, bool bHTTPOnly )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_HTMLsurface::SetPageScaleFactor( HHTMLBrowser unBrowserHandle, float flZoom, int nPointX, int nPointY )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_HTMLsurface::SetBackgroundMode( HHTMLBrowser unBrowserHandle, bool bBackgroundMode )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_HTMLsurface::SetDPIScalingFactor( HHTMLBrowser unBrowserHandle, float flDPIScaling )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void Steam_HTMLsurface::OpenDeveloperTools( HHTMLBrowser unBrowserHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_HTMLsurface::AllowStartRequest( HHTMLBrowser unBrowserHandle, bool bAllowed )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_HTMLsurface::JSDialogResponse( HHTMLBrowser unBrowserHandle, bool bResult )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// You MUST call this in response to a HTML_FileOpenDialog_t callback
STEAM_IGNOREATTR()
void Steam_HTMLsurface::FileLoadDialogResponse( HHTMLBrowser unBrowserHandle, const char **pchSelectedFiles )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}

View File

@ -18,13 +18,13 @@
#include "dll/steam_client.h" #include "dll/steam_client.h"
#include "dll/settings_parser.h" #include "dll/settings_parser.h"
static std::mutex kill_background_thread_mutex; static std::mutex kill_background_thread_mutex{};
static std::condition_variable kill_background_thread_cv; static std::condition_variable kill_background_thread_cv{};
static bool kill_background_thread; static bool kill_background_thread{};
static void background_thread(Steam_Client *client) static void background_thread(Steam_Client *client)
{ {
// max allowed time in which RunCallbacks() might not be called // max allowed time in which RunCallbacks() might not be called
constexpr const static auto max_stall_ms = std::chrono::milliseconds(200); constexpr const static auto max_stall_ms = std::chrono::milliseconds(300);
// wait 1 sec // wait 1 sec
{ {
@ -228,12 +228,13 @@ Steam_Client::~Steam_Client()
delete ugc_bridge; ugc_bridge = nullptr; delete ugc_bridge; ugc_bridge = nullptr;
delete network; network = nullptr;
delete run_every_runcb; run_every_runcb = nullptr;
delete callbacks_server; callbacks_server = nullptr; delete callbacks_server; callbacks_server = nullptr;
delete callbacks_client; callbacks_client = nullptr; delete callbacks_client; callbacks_client = nullptr;
delete callback_results_server; callback_results_server = nullptr; delete callback_results_server; callback_results_server = nullptr;
delete callback_results_client; callback_results_client = nullptr; delete callback_results_client; callback_results_client = nullptr;
delete network; network = nullptr;
delete run_every_runcb; run_every_runcb = nullptr;
} }
void Steam_Client::userLogIn() void Steam_Client::userLogIn()
@ -275,11 +276,13 @@ void Steam_Client::setAppID(uint32 appid)
settings_server->set_game_id(CGameID(appid)); settings_server->set_game_id(CGameID(appid));
local_storage->setAppId(appid); local_storage->setAppId(appid);
network->setAppID(appid); network->setAppID(appid);
set_env_variable("SteamAppId", std::to_string(appid));
set_env_variable("SteamGameId", std::to_string(appid)); std::string appid_str(std::to_string(appid));
set_env_variable("SteamAppId", appid_str);
set_env_variable("SteamGameId", appid_str);
if (!settings_client->disable_steamoverlaygameid_env_var) { if (!settings_client->disable_steamoverlaygameid_env_var) {
set_env_variable("SteamOverlayGameId", std::to_string(appid)); set_env_variable("SteamOverlayGameId", appid_str);
} }
} }
@ -1974,5 +1977,5 @@ void Steam_Client::RunCallbacks(bool runClientCB, bool runGameserverCB)
void Steam_Client::DestroyAllInterfaces() void Steam_Client::DestroyAllInterfaces()
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_TODO();
} }

1167
dll/steam_controller.cpp Normal file

File diff suppressed because it is too large Load Diff

1291
dll/steam_friends.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,146 @@
/* 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 "dll/steam_game_coordinator.h"
void Steam_Game_Coordinator::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));
}
void Steam_Game_Coordinator::steam_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_Game_Coordinator *steam_gamecoordinator = (Steam_Game_Coordinator *)object;
steam_gamecoordinator->Callback(msg);
}
void Steam_Game_Coordinator::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_Game_Coordinator *steam_gamecoordinator = (Steam_Game_Coordinator *)object;
steam_gamecoordinator->RunCallbacks();
}
Steam_Game_Coordinator::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->callback_results = callback_results;
this->callbacks = callbacks;
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);
}
Steam_Game_Coordinator::~Steam_Game_Coordinator()
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Game_Coordinator::steam_callback, this);
this->run_every_runcb->remove(&Steam_Game_Coordinator::steam_run_every_runcb, this);
}
// sends a message to the Game Coordinator
EGCResults Steam_Game_Coordinator::SendMessage_( uint32 unMsgType, const void *pubData, uint32 cubData )
{
PRINT_DEBUG("%X %u len %u", unMsgType, (~protobuf_mask) & unMsgType, cubData);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Game_Coordinator::IsMessageAvailable( uint32 *pcubMsgSize )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Game_Coordinator::RetrieveMessage( uint32 *punMsgType, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Game_Coordinator::RunCallbacks()
{
}
void Steam_Game_Coordinator::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
dll/steam_gamesearch.cpp Normal file
View 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 "dll/steam_gamesearch.h"
void Steam_Game_Search::steam_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_Game_Search *steam_gamesearch = (Steam_Game_Search *)object;
steam_gamesearch->Callback(msg);
}
void Steam_Game_Search::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_Game_Search *steam_gamesearch = (Steam_Game_Search *)object;
steam_gamesearch->RunCallbacks();
}
Steam_Game_Search::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->callback_results = callback_results;
this->callbacks = callbacks;
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);
}
Steam_Game_Search::~Steam_Game_Search()
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Game_Search::steam_callback, this);
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 Steam_Game_Search::AddGameSearchParams( const char *pchKeyToFind, const char *pchValuesToFind )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Game_Search::SearchForGameWithLobby( CSteamID steamIDLobby, int nPlayerMin, int nPlayerMax )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Game_Search::SearchForGameSolo( int nPlayerMin, int nPlayerMax )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Game_Search::AcceptGame()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
EGameSearchErrorCode_t Steam_Game_Search::DeclineGame()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// after receiving GameStartedByHostCallback_t get connection details to server
EGameSearchErrorCode_t Steam_Game_Search::RetrieveConnectionDetails( CSteamID steamIDHost, char *pchConnectionDetails, int cubConnectionDetails )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// leaves queue if still waiting
EGameSearchErrorCode_t Steam_Game_Search::EndGameSearch()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// =============================================================================================
// Game Host APIs
// a keyname and a list of comma separated values: all the values you allow
EGameSearchErrorCode_t Steam_Game_Search::SetGameHostParams( const char *pchKey, const char *pchValue )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// set connection details for players once game is found so they can connect to this server
EGameSearchErrorCode_t Steam_Game_Search::SetConnectionDetails( const char *pchConnectionDetails, int cubConnectionDetails )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Game_Search::RequestPlayersForGame( int nPlayerMin, int nPlayerMax, int nMaxTeamSize )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Game_Search::HostConfirmGameStart( uint64 ullUniqueGameID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Game_Search::CancelRequestPlayersForGame()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
// submit a result for one player. does not end the game. ullUniqueGameID continues to describe this game
EGameSearchErrorCode_t Steam_Game_Search::SubmitPlayerResult( uint64 ullUniqueGameID, CSteamID steamIDPlayer, EPlayerResult_t EPlayerResult )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Game_Search::EndGame( uint64 ullUniqueGameID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EGameSearchErrorCode_Failed_Offline;
}
void Steam_Game_Search::RunCallbacks()
{
}
void Steam_Game_Search::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()) {
}
}

View File

@ -20,20 +20,24 @@
#define SEND_SERVER_RATE 5.0 #define SEND_SERVER_RATE 5.0
Steam_GameServer::Steam_GameServer(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks) Steam_GameServer::Steam_GameServer(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks)
{ {
this->network = network; this->network = network;
this->settings = settings; this->settings = settings;
server_data.set_id(settings->get_local_steam_id().ConvertToUint64());
this->callbacks = callbacks; this->callbacks = callbacks;
auth_manager = new Auth_Manager(settings, network, callbacks); auth_manager = new Auth_Manager(settings, network, callbacks);
server_data.set_id(settings->get_local_steam_id().ConvertToUint64());
} }
Steam_GameServer::~Steam_GameServer() Steam_GameServer::~Steam_GameServer()
{ {
delete auth_manager; delete auth_manager;
auth_manager = nullptr;
} }
std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>>* Steam_GameServer::get_players() std::vector<std::pair<CSteamID, Gameserver_Player_Info_t>>* Steam_GameServer::get_players()
{ {
return &players; return &players;
@ -803,6 +807,8 @@ SteamAPICall_t Steam_GameServer::ComputeNewPlayerCompatibility( CSteamID steamID
return 0; return 0;
} }
void Steam_GameServer::RunCallbacks() void Steam_GameServer::RunCallbacks()
{ {
bool temp_call_servers_connected = call_servers_connected; bool temp_call_servers_connected = call_servers_connected;

963
dll/steam_inventory.cpp Normal file
View File

@ -0,0 +1,963 @@
/* 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 "dll/steam_inventory.h"
bool Steam_Inventory_Requests::result_done() const
{
return done;
}
uint32 Steam_Inventory_Requests::timestamp() const
{
return std::chrono::duration_cast<std::chrono::duration<uint32>>(time_created.time_since_epoch()).count();
}
struct Steam_Inventory_Requests* Steam_Inventory::new_inventory_result(bool full_query, const SteamItemInstanceID_t* pInstanceIDs, uint32 unCountInstanceIDs)
{
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* Steam_Inventory::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 Steam_Inventory::read_items_db()
{
std::string items_db_path = Local_Storage::get_game_settings_path() + items_user_file;
PRINT_DEBUG("file path: %s", items_db_path.c_str());
local_storage->load_json(items_db_path, defined_items);
}
void Steam_Inventory::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("items file path: %s", items_db_path.c_str());
local_storage->load_json(items_db_path, user_items);
}
}
void Steam_Inventory::run_every_runcb_cb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_Inventory *obj = (Steam_Inventory *)object;
obj->RunCallbacks();
}
Steam_Inventory::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::~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 Steam_Inventory::GetResultStatus( SteamInventoryResult_t resultHandle )
{
PRINT_DEBUG_ENTRY();
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 Steam_Inventory::GetResultItems( SteamInventoryResult_t resultHandle,
STEAM_OUT_ARRAY_COUNT( punOutItemsArraySize,Output array) SteamItemDetails_t *pOutItemsArray,
uint32 *punOutItemsArraySize )
{
PRINT_DEBUG_ENTRY();
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("good");
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 Steam_Inventory::GetResultItemProperty( SteamInventoryResult_t resultHandle,
uint32 unItemIndex,
const char *pchPropertyName,
STEAM_OUT_STRING_COUNT( punValueBufferSizeOut ) char *pchValueBuffer, uint32 *punValueBufferSizeOut )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//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 Steam_Inventory::the server time at which the result was generated. Compare against the value of IClientUtils::GetServerRealTime() to determine age.)
uint32 Steam_Inventory::GetResultTimestamp( SteamInventoryResult_t resultHandle )
{
PRINT_DEBUG_ENTRY();
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 Steam_Inventory::CheckResultSteamID( SteamInventoryResult_t resultHandle, CSteamID steamIDExpected )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//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 Steam_Inventory::DestroyResult( SteamInventoryResult_t resultHandle )
{
PRINT_DEBUG_ENTRY();
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 Steam_Inventory::GetAllItems( SteamInventoryResult_t *pResultHandle )
{
PRINT_DEBUG_ENTRY();
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 Steam_Inventory::GetItemsByID( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT( unCountInstanceIDs ) const SteamItemInstanceID_t *pInstanceIDs, uint32 unCountInstanceIDs )
{
PRINT_DEBUG_ENTRY();
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 Steam_Inventory::SerializeResult( SteamInventoryResult_t resultHandle, STEAM_OUT_BUFFER_COUNT(punOutBufferSize) void *pOutBuffer, uint32 *punOutBufferSize )
{
PRINT_DEBUG("%i", 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", *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 Steam_Inventory::DeserializeResult( SteamInventoryResult_t *pOutResultHandle, STEAM_BUFFER_COUNT(punOutBufferSize) const void *pBuffer, uint32 unBufferSize, bool bRESERVED_MUST_BE_FALSE)
{
PRINT_DEBUG_ENTRY();
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 Steam_Inventory::GenerateItems( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, STEAM_ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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() Steam_Inventory::checks the list of promotional items for which the user may be eligible and grants the items (one time only).)
bool Steam_Inventory::GrantPromoItems( SteamInventoryResult_t *pResultHandle )
{
PRINT_DEBUG_ENTRY();
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 Steam_Inventory::AddPromoItem( SteamInventoryResult_t *pResultHandle, SteamItemDef_t itemDef )
{
PRINT_DEBUG_ENTRY();
//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 Steam_Inventory::AddPromoItems( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, uint32 unArrayLength )
{
PRINT_DEBUG_ENTRY();
//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 Steam_Inventory::ConsumeItem( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemConsume, uint32 unQuantity )
{
PRINT_DEBUG("%llu %u", 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("previous %u", 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 Steam_Inventory::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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Inventory::TransferItemQuantity( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemIdSource, uint32 unQuantity, SteamItemInstanceID_t itemIdDest )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Inventory::SendItemDropHeartbeat()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_Inventory::TriggerItemDrop( SteamInventoryResult_t *pResultHandle, SteamItemDef_t dropListDefinition )
{
PRINT_DEBUG("%p %i", 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 Steam_Inventory::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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Inventory::LoadItemDefinitions()
{
PRINT_DEBUG_ENTRY();
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 Steam_Inventory::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("%p", pItemDefIDs);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!punItemDefIDsArraySize)
return false;
PRINT_DEBUG(" array_size %u", *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 Steam_Inventory::GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPropertyName,
STEAM_OUT_STRING_COUNT(punValueBufferSizeOut) char *pchValueBuffer, uint32 *punValueBufferSizeOut )
{
PRINT_DEBUG("%i %s", 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", 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 Steam_Inventory::RequestEligiblePromoItemDefinitionsIDs( CSteamID steamID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Inventory::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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Inventory::StartPurchase( STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, STEAM_ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Request current prices for all applicable item definitions
STEAM_CALL_RESULT( SteamInventoryRequestPricesResult_t )
SteamAPICall_t Steam_Inventory::RequestPrices()
{
PRINT_DEBUG_ENTRY();
SteamInventoryRequestPricesResult_t data{};
data.m_result = k_EResultOK;
memcpy(data.m_rgchCurrency, "USD", 4);
auto ret = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.2);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.2);
return ret;
}
// Returns the number of items with prices. Need to call RequestPrices() first.
uint32 Steam_Inventory::GetNumItemsWithPrices()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
bool Steam_Inventory::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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Returns item definition ids and their prices in the user's local currency.
// Need to call RequestPrices() first.
bool Steam_Inventory::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_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return GetItemsWithPrices(pArrayItemDefs, pPrices, NULL, unArrayLength);
}
bool Steam_Inventory::GetItemPrice( SteamItemDef_t iDefinition, uint64 *pCurrentPrice, uint64 *pBasePrice )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Retrieves the price for the item definition id
// Returns false if there is no price stored for the item definition.
bool Steam_Inventory::GetItemPrice( SteamItemDef_t iDefinition, uint64 *pPrice )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return GetItemPrice(iDefinition, pPrice, NULL);
}
// Create a request to update properties on items
SteamInventoryUpdateHandle_t Steam_Inventory::StartUpdateProperties()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Remove the property on the item
bool Steam_Inventory::RemoveProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Accessor methods to set properties on items
bool Steam_Inventory::SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, const char *pchPropertyValue )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
bool Steam_Inventory::SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, bool bValue )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
bool Steam_Inventory::SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, int64 nValue )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
bool Steam_Inventory::SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, float flValue )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Submit the update request by handle
bool Steam_Inventory::SubmitUpdateProperties( SteamInventoryUpdateHandle_t handle, SteamInventoryResult_t * pResultHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
bool Steam_Inventory::InspectItem( SteamInventoryResult_t *pResultHandle, const char *pchItemToken )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
void Steam_Inventory::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;
}
}
}
}

View File

@ -0,0 +1,224 @@
/* 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 "dll/steam_masterserver_updater.h"
void Steam_Masterserver_Updater::steam_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_Masterserver_Updater *steam_masterserverupdater = (Steam_Masterserver_Updater *)object;
steam_masterserverupdater->Callback(msg);
}
void Steam_Masterserver_Updater::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_Masterserver_Updater *steam_masterserverupdater = (Steam_Masterserver_Updater *)object;
steam_masterserverupdater->RunCallbacks();
}
Steam_Masterserver_Updater::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->callback_results = callback_results;
this->callbacks = callbacks;
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);
}
Steam_Masterserver_Updater::~Steam_Masterserver_Updater()
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Masterserver_Updater::steam_callback, this);
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 Steam_Masterserver_Updater::SetActive( bool bActive )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// You usually don't need to modify this.
// Pass -1 to use the default value for iHeartbeatInterval.
// Some mods change this.
void Steam_Masterserver_Updater::SetHeartbeatInterval( int iHeartbeatInterval )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_Masterserver_Updater::HandleIncomingPacket( const void *pData, int cbData, uint32 srcIP, uint16 srcPort )
{
PRINT_DEBUG_TODO();
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_Masterserver_Updater::GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *pNetAdr, uint16 *pPort )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Masterserver_Updater::SetBasicServerData(
unsigned short nProtocolVersion,
bool bDedicatedServer,
const char *pRegionName,
const char *pProductName,
unsigned short nMaxReportedClients,
bool bPasswordProtected,
const char *pGameDescription )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Call this to clear the whole list of key/values that are sent in rules queries.
void Steam_Masterserver_Updater::ClearAllKeyValues()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Call this to add/update a key/value pair.
void Steam_Masterserver_Updater::SetKeyValue( const char *pKey, const char *pValue )
{
PRINT_DEBUG("TODO '%s'='%s'", pKey, pValue);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_Masterserver_Updater::NotifyShutdown()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// Returns true if the master server has requested a restart.
// Only returns true once per request.
bool Steam_Masterserver_Updater::WasRestartRequested()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Force it to request a heartbeat from the master servers.
void Steam_Masterserver_Updater::ForceHeartbeat()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_Masterserver_Updater::AddMasterServer( const char *pServerAddress )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
bool Steam_Masterserver_Updater::RemoveMasterServer( const char *pServerAddress )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
int Steam_Masterserver_Updater::GetNumMasterServers()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Returns the # of bytes written to pOut.
int Steam_Masterserver_Updater::GetMasterServerAddress( int iServer, char *pOut, int outBufferSize )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
void Steam_Masterserver_Updater::RunCallbacks()
{
}
void Steam_Masterserver_Updater::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) {
}
}
}

1624
dll/steam_matchmaking.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -17,35 +17,51 @@
#include "dll/dll.h" #include "dll/dll.h"
#define SERVER_TIMEOUT 10.0
#define DIRECT_IP_DELAY 0.05
static void network_callback(void *object, Common_Message *msg)
static HServerQuery new_server_query()
{ {
PRINT_DEBUG("steam_matchmaking_servers_callback"); std::lock_guard<std::recursive_mutex> lock(global_mutex);
static unsigned int a = 0;
++a;
if (!a) ++a;
return a;
}
void Steam_Matchmaking_Servers::network_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_Matchmaking_Servers *obj = (Steam_Matchmaking_Servers *)object; Steam_Matchmaking_Servers *obj = (Steam_Matchmaking_Servers *)object;
obj->Callback(msg); obj->Callback(msg);
} }
Steam_Matchmaking_Servers::Steam_Matchmaking_Servers(class Settings *settings, class Local_Storage *local_storage, class Networking *network) Steam_Matchmaking_Servers::Steam_Matchmaking_Servers(class Settings *settings, class Local_Storage *local_storage, class Networking *network)
{ {
this->settings = settings; this->settings = settings;
this->local_storage = local_storage; this->local_storage = local_storage;
this->network = network; this->network = network;
this->network->setCallback(CALLBACK_ID_GAMESERVER, (uint64) 0, &network_callback, this);
this->network->setCallback(CALLBACK_ID_GAMESERVER, (uint64)0, &network_callback, this);
} }
Steam_Matchmaking_Servers::~Steam_Matchmaking_Servers() Steam_Matchmaking_Servers::~Steam_Matchmaking_Servers()
{ {
this->network->rmCallback(CALLBACK_ID_GAMESERVER, (uint64) 0, &network_callback, this); this->network->rmCallback(CALLBACK_ID_GAMESERVER, (uint64)0, &network_callback, this);
} }
static unsigned server_list_request = 0;
HServerListRequest Steam_Matchmaking_Servers::RequestServerList(AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse, EMatchMakingType type) HServerListRequest Steam_Matchmaking_Servers::RequestServerList(AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse, EMatchMakingType type)
{ {
PRINT_DEBUG("%u %p, %i", iApp, pRequestServersResponse, (int)type); PRINT_DEBUG("%u %p, %i", iApp, pRequestServersResponse, (int)type);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
static unsigned server_list_request = 0;
++server_list_request; ++server_list_request;
if (!server_list_request) server_list_request = 1; if (!server_list_request) server_list_request = 1;
HServerListRequest id = (char *)0 + server_list_request; // (char *)0 silences the compiler warning HServerListRequest id = (char *)0 + server_list_request; // (char *)0 silences the compiler warning
@ -353,6 +369,221 @@ void Steam_Matchmaking_Servers::ReleaseRequest( HServerListRequest hServerListRe
- Server passes the filter if it's a linux server - 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 *Steam_Matchmaking_Servers::GetServerDetails( HServerListRequest hRequest, int iServer )
{
PRINT_DEBUG("%p %i", 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", hRequest, g->id);
if (g->id == hRequest) {
gameservers_filtered = g->gameservers_filtered;
PRINT_DEBUG(" found %zu", 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");
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("%p", hRequest);
auto g = std::begin(requests);
while (g != std::end(requests)) {
if (g->id == hRequest) {
g->cancelled = true;
PRINT_DEBUG("canceled request with id: %p", g->id);
}
++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("%p", hRequest);
}
// Returns true if the list is currently refreshing its server list
bool Steam_Matchmaking_Servers::IsRefreshing( HServerListRequest hRequest )
{
PRINT_DEBUG("%p", 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("%p", 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;
}
PRINT_DEBUG("final count = %i", size);
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("%p", hRequest);
//TODO
}
// 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( EMatchMakingType eType, int iServer )
{
PRINT_DEBUG_ENTRY();
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 Steam_Matchmaking_Servers::CancelQuery( EMatchMakingType eType )
{
PRINT_DEBUG_ENTRY();
return CancelQuery((HServerListRequest) eType);
}
// Ping every server in your list again but don't update the list of servers
void Steam_Matchmaking_Servers::RefreshQuery( EMatchMakingType eType )
{
PRINT_DEBUG_ENTRY();
return RefreshQuery((HServerListRequest) eType);
}
// Returns true if the list is currently refreshing its server list
bool Steam_Matchmaking_Servers::IsRefreshing( EMatchMakingType eType )
{
return IsRefreshing((HServerListRequest) eType);
}
// How many servers in the given list, GetServerDetails above takes 0... GetServerCount() - 1
int Steam_Matchmaking_Servers::GetServerCount( EMatchMakingType eType )
{
PRINT_DEBUG_ENTRY();
return GetServerCount((HServerListRequest) eType);
}
// Refresh a single server inside of a query (rather than all the servers )
void Steam_Matchmaking_Servers::RefreshServer( EMatchMakingType eType, int iServer )
{
PRINT_DEBUG_ENTRY();
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 Steam_Matchmaking_Servers::PingServer( uint32 unIP, uint16 usPort, ISteamMatchmakingPingResponse *pRequestServersResponse )
{
PRINT_DEBUG("%hhu.%hhu.%hhu.%hhu:%hu", ((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("%hhu.%hhu.%hhu.%hhu:%hu", ((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("%hhu.%hhu.%hhu.%hhu:%hu", ((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_ENTRY();
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::server_details(Gameserver *g, gameserveritem_t *server) void Steam_Matchmaking_Servers::server_details(Gameserver *g, gameserveritem_t *server)
{ {
PRINT_DEBUG_ENTRY(); PRINT_DEBUG_ENTRY();
@ -559,179 +790,6 @@ void Steam_Matchmaking_Servers::server_details_rules(Gameserver *g, Steam_Matchm
PRINT_DEBUG(" " "%" PRIu64 "", g->id()); PRINT_DEBUG(" " "%" PRIu64 "", g->id());
} }
// 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("%p %i", 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", hRequest, g->id);
if (g->id == hRequest) {
gameservers_filtered = g->gameservers_filtered;
PRINT_DEBUG(" found %zu", 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");
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("%p", hRequest);
auto g = std::begin(requests);
while (g != std::end(requests)) {
if (g->id == hRequest) {
g->cancelled = true;
PRINT_DEBUG("canceled request with id: %p", g->id);
}
++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("%p", hRequest);
}
// Returns true if the list is currently refreshing its server list
bool Steam_Matchmaking_Servers::IsRefreshing( HServerListRequest hRequest )
{
PRINT_DEBUG("%p", 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("%p", 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;
}
PRINT_DEBUG("final count = %i", size);
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("%p", hRequest);
//TODO
}
static HServerQuery new_server_query()
{
std::lock_guard<std::recursive_mutex> lock(global_mutex);
static int a = 0;
++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("%hhu.%hhu.%hhu.%hhu:%hu", ((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("%hhu.%hhu.%hhu.%hhu:%hu", ((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("%hhu.%hhu.%hhu.%hhu:%hu", ((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_ENTRY();
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() void Steam_Matchmaking_Servers::RunCallbacks()
{ {
// PRINT_DEBUG_ENTRY(); // PRINT_DEBUG_ENTRY();

972
dll/steam_networking.cpp Normal file
View File

@ -0,0 +1,972 @@
/* 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 "dll/steam_networking.h"
//packet timeout in seconds for non connections
#define ORPHANED_PACKET_TIMEOUT (20)
#define NEW_CONNECTION_TIMEOUT (20.0)
//kingdom 2 crowns doesn't work with a 0.3 delay or lower
#define NEW_CONNECTION_DELAY (0.4)
#define OLD_CHANNEL_NUMBER 1
bool Steam_Networking::connection_exists(CSteamID id)
{
std::lock_guard<std::recursive_mutex> lock(connections_edit_mutex);
return std::find_if(connections.begin(), connections.end(), [&id](struct Steam_Networking_Connection const& conn) { return conn.remote == id;}) != connections.end();
}
struct Steam_Networking_Connection* Steam_Networking::get_or_create_connection(CSteamID id)
{
std::lock_guard<std::recursive_mutex> lock(connections_edit_mutex);
auto conn = std::find_if(connections.begin(), connections.end(), [&id](struct Steam_Networking_Connection const& conn) { return conn.remote == id;});
if (connections.end() == conn) {
struct Steam_Networking_Connection connection;
connection.remote = id;
connections.push_back(connection);
return &(connections[connections.size() - 1]);
} else {
return &(*conn);
}
}
void Steam_Networking::remove_connection(CSteamID id)
{
{
std::lock_guard<std::recursive_mutex> lock(connections_edit_mutex);
auto conn = std::begin(connections);
while (conn != std::end(connections)) {
if (conn->remote == id) {
conn = connections.erase(conn);
} else {
++conn;
}
}
}
//pretty sure steam also clears the entire queue of messages for that connection
{
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
auto msg = std::begin(messages);
while (msg != std::end(messages)) {
if (msg->source_id() == id.ConvertToUint64()) {
msg = messages.erase(msg);
} else {
++msg;
}
}
}
{
auto msg = std::begin(unprocessed_messages);
while (msg != std::end(unprocessed_messages)) {
if (msg->source_id() == id.ConvertToUint64()) {
msg = unprocessed_messages.erase(msg);
} else {
++msg;
}
}
}
}
SNetSocket_t Steam_Networking::create_connection_socket(CSteamID target, int nVirtualPort, uint32 nIP, uint16 nPort, SNetListenSocket_t id, enum steam_socket_connection_status status, SNetSocket_t other_id)
{
static SNetSocket_t socket_number = 0;
bool found = false;
do {
found = false;
++socket_number;
for (auto & c: connection_sockets) {
if (c.id == socket_number || socket_number == 0) {
found = true;
break;
}
}
} while (found);
struct steam_connection_socket socket{};
socket.id = socket_number;
socket.listen_id = id;
socket.status = status;
socket.target = target;
socket.nVirtualPort = nVirtualPort;
socket.nIP = nIP;
socket.nPort = nPort;
socket.other_id = other_id;
connection_sockets.push_back(socket);
Common_Message msg{};
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_dest_id(target.ConvertToUint64());
msg.set_allocated_network_old(new Network_Old);
if (nPort) {
msg.mutable_network_old()->set_type(Network_Old::CONNECTION_REQUEST_IP);
msg.mutable_network_old()->set_port(nPort);
} else {
msg.mutable_network_old()->set_type(Network_Old::CONNECTION_REQUEST_STEAMID);
msg.mutable_network_old()->set_port(nVirtualPort);
}
if (socket.status == SOCKET_CONNECTED) {
msg.mutable_network_old()->set_type(Network_Old::CONNECTION_ACCEPTED);
}
msg.mutable_network_old()->set_connection_id(socket.other_id);
msg.mutable_network_old()->set_connection_id_from(socket.id);
if (target.IsValid()) {
network->sendTo(&msg, true);
} else if (nIP) {
network->sendToIPPort(&msg, nIP, nPort, true);
}
return socket.id;
}
struct steam_connection_socket* Steam_Networking::get_connection_socket(SNetSocket_t id)
{
auto conn = std::find_if(connection_sockets.begin(), connection_sockets.end(), [&id](struct steam_connection_socket const& conn) {
return conn.id == id;
});
if (connection_sockets.end() == conn) return NULL;
return &(*conn);
}
void Steam_Networking::remove_killed_connection_sockets()
{
auto socket = std::begin(connection_sockets);
while (socket != std::end(connection_sockets)) {
if (socket->status == SOCKET_KILLED || socket->status == SOCKET_DISCONNECTED) {
socket = connection_sockets.erase(socket);
} else {
++socket;
}
}
}
void Steam_Networking::steam_networking_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_Networking *steam_networking = (Steam_Networking *)object;
steam_networking->Callback(msg);
}
void Steam_Networking::steam_networking_run_every_runcp(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_Networking *steam_networking = (Steam_Networking *)object;
steam_networking->RunCallbacks();
}
Steam_Networking::Steam_Networking(class Settings *settings, class Networking *network, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->callbacks = callbacks;
this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_NETWORKING, settings->get_local_steam_id(), &Steam_Networking::steam_networking_callback, this);
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking::steam_networking_callback, this);
this->run_every_runcb->add(&Steam_Networking::steam_networking_run_every_runcp, this);
PRINT_DEBUG("user id %llu messages: %p", settings->get_local_steam_id().ConvertToUint64(), &messages);
}
Steam_Networking::~Steam_Networking()
{
this->network->rmCallback(CALLBACK_ID_NETWORKING, settings->get_local_steam_id(), &Steam_Networking::steam_networking_callback, this);
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking::steam_networking_callback, this);
this->run_every_runcb->remove(&Steam_Networking::steam_networking_run_every_runcp, this);
}
////////////////////////////////////////////////////////////////////////////////////////////
// Session-less connection functions
// automatically establishes NAT-traversing or Relay server connections
// Sends a P2P packet to the specified user
// UDP-like, unreliable and a max packet size of 1200 bytes
// the first packet send may be delayed as the NAT-traversal code runs
// if we can't get through to the user, an error will be posted via the callback P2PSessionConnectFail_t
// see EP2PSend enum above for the descriptions of the different ways of sending packets
//
// nChannel is a routing number you can use to help route message to different systems - you'll have to call ReadP2PPacket()
// 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 p2p connection, saving on resources
bool Steam_Networking::SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData, EP2PSend eP2PSendType, int nChannel)
{
PRINT_DEBUG("len %u sendtype: %u channel: %u to: %llu", cubData, eP2PSendType, nChannel, steamIDRemote.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
bool reliable = false;
if (eP2PSendType == k_EP2PSendReliable || eP2PSendType == k_EP2PSendReliableWithBuffering) reliable = true;
Common_Message msg;
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_dest_id(steamIDRemote.ConvertToUint64());
msg.set_allocated_network(new Network_pb);
if (!connection_exists(steamIDRemote)) {
msg.mutable_network()->set_type(Network_pb::NEW_CONNECTION);
network->sendTo(&msg, true);
}
msg.mutable_network()->set_channel(nChannel);
msg.mutable_network()->set_data(pubData, cubData);
msg.mutable_network()->set_type(Network_pb::DATA);
struct Steam_Networking_Connection *conn = get_or_create_connection(steamIDRemote);
new_connection_times.erase(steamIDRemote);
conn->open_channels.insert(nChannel);
bool ret = network->sendTo(&msg, reliable);
PRINT_DEBUG("Sent message with size: %zu %u", msg.network().data().size(), ret);
return ret;
}
bool Steam_Networking::SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData, EP2PSend eP2PSendType )
{
PRINT_DEBUG("old");
return SendP2PPacket(steamIDRemote, pubData, cubData, eP2PSendType, OLD_CHANNEL_NUMBER);
}
// returns true if any data is available for read, and the amount of data that will need to be read
bool Steam_Networking::IsP2PPacketAvailable( uint32 *pcubMsgSize, int nChannel)
{
PRINT_DEBUG("channel: %i", nChannel);
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
//Not sure if this should be here because it slightly screws up games that don't like such low "pings"
//Commenting it out for now because it looks like it causes a bug where 20xx gets stuck in an infinite receive packet loop
//this->network->Run();
//RunCallbacks();
PRINT_DEBUG("Messages %zu %p", messages.size(), &messages);
for (auto &msg : messages) {
if (connection_exists((uint64)msg.source_id()) && msg.mutable_network()->channel() == nChannel && msg.network().processed()) {
uint32 size = msg.mutable_network()->data().size();
if (pcubMsgSize) *pcubMsgSize = size;
PRINT_DEBUG("available with size: %u", size);
return true;
}
}
PRINT_DEBUG("(not available)");
if (pcubMsgSize) *pcubMsgSize = 0;
return false;
}
bool Steam_Networking::IsP2PPacketAvailable( uint32 *pcubMsgSize)
{
PRINT_DEBUG("old");
return IsP2PPacketAvailable(pcubMsgSize, OLD_CHANNEL_NUMBER);
}
// reads in a packet that has been sent from another user via SendP2PPacket()
// returns the size of the message and the steamID of the user who sent it in the last two parameters
// if the buffer passed in is too small, the message will be truncated
// this call is not blocking, and will return false if no data is available
bool Steam_Networking::ReadP2PPacket( void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, CSteamID *psteamIDRemote, int nChannel)
{
PRINT_DEBUG("%u %i", cubDest, nChannel);
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
//Not sure if this should be here because it slightly screws up games that don't like such low "pings"
//Commenting it out for now because it looks like it causes a bug where 20xx gets stuck in an infinite receive packet loop
//this->network->Run();
//RunCallbacks();
bool read = false;
PRINT_DEBUG("Number messages %zu", messages.size());
auto msg = std::begin(messages);
while (msg != std::end(messages)) {
if (connection_exists((uint64)msg->source_id()) && msg->network().channel() == nChannel && msg->network().processed()) {
uint32 msg_size = msg->network().data().size();
if (msg_size > cubDest) msg_size = cubDest;
if (pcubMsgSize) *pcubMsgSize = msg_size;
memcpy(pubDest, msg->network().data().data(), msg_size);
PRINT_DEBUG("%s",
common_helpers::uint8_vector_to_hex_string(std::vector<uint8_t>((uint8_t*)pubDest, (uint8_t*)pubDest + msg_size)).c_str());
*psteamIDRemote = CSteamID((uint64)msg->source_id());
PRINT_DEBUG("len %u channel: %u from: " "%" PRIu64 "", msg_size, nChannel, msg->source_id());
msg = messages.erase(msg);
return true;
}
++msg;
}
if (pcubMsgSize) *pcubMsgSize = 0;
if (psteamIDRemote) *psteamIDRemote = k_steamIDNil;
return false;
}
bool Steam_Networking::ReadP2PPacket( void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, CSteamID *psteamIDRemote)
{
PRINT_DEBUG("old");
return ReadP2PPacket(pubDest, cubDest, pcubMsgSize, psteamIDRemote, OLD_CHANNEL_NUMBER);
}
// AcceptP2PSessionWithUser() should only be called in response to a P2PSessionRequest_t callback
// P2PSessionRequest_t will be posted if another user tries to send you a packet that you haven't talked to yet
// if you don't want to talk to the user, just ignore the request
// if the user continues to send you packets, another P2PSessionRequest_t will be posted periodically
// this may be called multiple times for a single user
// (if you've called SendP2PPacket() on the other user, this implicitly accepts the session request)
bool Steam_Networking::AcceptP2PSessionWithUser( CSteamID steamIDRemote )
{
PRINT_DEBUG("%llu", steamIDRemote.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Networking_Connection *conn = get_or_create_connection(steamIDRemote);
if (conn) new_connection_times.erase(steamIDRemote);
return !!conn;
}
// call CloseP2PSessionWithUser() when you're done talking to a user, will free up resources under-the-hood
// if the remote user tries to send data to you again, another P2PSessionRequest_t callback will be posted
bool Steam_Networking::CloseP2PSessionWithUser( CSteamID steamIDRemote )
{
PRINT_DEBUG("%llu", steamIDRemote.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!connection_exists(steamIDRemote)) {
return false;
}
remove_connection(steamIDRemote);
return true;
}
// call CloseP2PChannelWithUser() when you're done talking to a user on a specific channel. Once all channels
// open channels to a user have been closed, the open session to the user will be closed and new data from this
// user will trigger a P2PSessionRequest_t callback
bool Steam_Networking::CloseP2PChannelWithUser( CSteamID steamIDRemote, int nChannel )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!connection_exists(steamIDRemote)) {
return false;
}
struct Steam_Networking_Connection *conn = get_or_create_connection(steamIDRemote);
conn->open_channels.erase(nChannel);
if (conn->open_channels.size() == 0) {
remove_connection(steamIDRemote);
}
return true;
}
// fills out P2PSessionState_t structure with details about the underlying connection to the user
// should only needed for debugging purposes
// returns false if no connection exists to the specified user
bool Steam_Networking::GetP2PSessionState( CSteamID steamIDRemote, P2PSessionState_t *pConnectionState )
{
PRINT_DEBUG("%llu", steamIDRemote.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!connection_exists(steamIDRemote) && (steamIDRemote != settings->get_local_steam_id())) {
if (pConnectionState) {
pConnectionState->m_bConnectionActive = false;
pConnectionState->m_bConnecting = false;
pConnectionState->m_eP2PSessionError = 0;
pConnectionState->m_bUsingRelay = false;
pConnectionState->m_nBytesQueuedForSend = 0;
pConnectionState->m_nPacketsQueuedForSend = 0;
pConnectionState->m_nRemoteIP = 0;
pConnectionState->m_nRemotePort = 0;
}
PRINT_DEBUG("No Connection");
return false;
}
if (pConnectionState) {
pConnectionState->m_bConnectionActive = true;
pConnectionState->m_bConnecting = false;
pConnectionState->m_eP2PSessionError = 0;
pConnectionState->m_bUsingRelay = false;
pConnectionState->m_nBytesQueuedForSend = 0;
pConnectionState->m_nPacketsQueuedForSend = 0;
pConnectionState->m_nRemoteIP = network->getIP(steamIDRemote);
pConnectionState->m_nRemotePort = 12345;
}
PRINT_DEBUG("Connection");
return true;
}
// Allow P2P connections to fall back to being relayed through the Steam servers if a direct connection
// or NAT-traversal cannot be established. Only applies to connections created after setting this value,
// or to existing connections that need to automatically reconnect after this value is set.
//
// P2P packet relay is allowed by default
bool Steam_Networking::AllowP2PPacketRelay( bool bAllow )
{
PRINT_DEBUG("%u", bAllow);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////
// LISTEN / CONNECT style interface functions
//
// This is an older set of functions designed around the Berkeley TCP sockets model
// it's preferential that you use the above P2P functions, they're more robust
// and these older functions will be removed eventually
//
////////////////////////////////////////////////////////////////////////////////////////////
SNetListenSocket_t socket_number = 0;
// creates a socket and listens others to connect
// will trigger a SocketStatusCallback_t callback on another client connecting
// nVirtualP2PPort is the unique ID that the client will connect to, in case you have multiple ports
// this can usually just be 0 unless you want multiple sets of connections
// unIP is the local IP address to bind to
// pass in 0 if you just want the default local IP
// unPort is the port to use
// pass in 0 if you don't want users to be able to connect via IP/Port, but expect to be always peer-to-peer connections only
SNetListenSocket_t Steam_Networking::CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 nPort, bool bAllowUseOfPacketRelay )
{
PRINT_DEBUG("old %i %u %hu %u", nVirtualP2PPort, nIP, nPort, bAllowUseOfPacketRelay);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
for (auto & c : listen_sockets) {
if (c.nVirtualP2PPort == nVirtualP2PPort || c.nPort == nPort)
return 0;
}
++socket_number;
if (!socket_number) ++socket_number;
struct steam_listen_socket socket;
socket.id = socket_number;
socket.nVirtualP2PPort = nVirtualP2PPort;
socket.nIP = nIP;
socket.nPort = nPort;
listen_sockets.push_back(socket);
return socket.id;
}
SNetListenSocket_t Steam_Networking::CreateListenSocket( int nVirtualP2PPort, SteamIPAddress_t nIP, uint16 nPort, bool bAllowUseOfPacketRelay )
{
PRINT_DEBUG("%i %i %u %hu %u", nVirtualP2PPort, nIP.m_eType, nIP.m_unIPv4, nPort, bAllowUseOfPacketRelay);
//TODO: ipv6
return CreateListenSocket(nVirtualP2PPort, nIP.m_unIPv4, nPort, bAllowUseOfPacketRelay);
}
SNetListenSocket_t Steam_Networking::CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 nPort )
{
PRINT_DEBUG("old");
return CreateListenSocket(nVirtualP2PPort, nIP, nPort, true);
}
// creates a socket and begin connection to a remote destination
// can connect via a known steamID (client or game server), or directly to an IP
// on success will trigger a SocketStatusCallback_t callback
// on failure or timeout will trigger a SocketStatusCallback_t callback with a failure code in m_eSNetSocketState
SNetSocket_t Steam_Networking::CreateP2PConnectionSocket( CSteamID steamIDTarget, int nVirtualPort, int nTimeoutSec, bool bAllowUseOfPacketRelay )
{
PRINT_DEBUG("%llu %i %i %u", steamIDTarget.ConvertToUint64(), nVirtualPort, nTimeoutSec, bAllowUseOfPacketRelay);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO: nTimeoutSec
return create_connection_socket(steamIDTarget, nVirtualPort, 0, 0);
}
SNetSocket_t Steam_Networking::CreateP2PConnectionSocket( CSteamID steamIDTarget, int nVirtualPort, int nTimeoutSec )
{
PRINT_DEBUG("old");
return CreateP2PConnectionSocket(steamIDTarget, nVirtualPort, nTimeoutSec, true);
}
SNetSocket_t Steam_Networking::CreateConnectionSocket( uint32 nIP, uint16 nPort, int nTimeoutSec )
{
PRINT_DEBUG("%u %hu %i", nIP, nPort, nTimeoutSec);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO: nTimeoutSec
return create_connection_socket((uint64)0, 0, nIP, nPort);
}
SNetSocket_t Steam_Networking::CreateConnectionSocket( SteamIPAddress_t nIP, uint16 nPort, int nTimeoutSec )
{
PRINT_DEBUG("%i %u %hu %i", nIP.m_eType, nIP.m_unIPv4, nPort, nTimeoutSec);
//TODO: ipv6
return CreateConnectionSocket(nIP.m_unIPv4, nPort, nTimeoutSec);
}
// disconnects the connection to the socket, if any, and invalidates the handle
// any unread data on the socket will be thrown away
// if bNotifyRemoteEnd is set, socket will not be completely destroyed until the remote end acknowledges the disconnect
bool Steam_Networking::DestroySocket( SNetSocket_t hSocket, bool bNotifyRemoteEnd )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket || socket->status == SOCKET_KILLED) return false;
socket->status = SOCKET_KILLED;
return true;
}
// destroying a listen socket will automatically kill all the regular sockets generated from it
bool Steam_Networking::DestroyListenSocket( SNetListenSocket_t hSocket, bool bNotifyRemoteEnd )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto c = std::begin(listen_sockets);
while (c != std::end(listen_sockets)) {
if (c->id == hSocket) {
c = listen_sockets.erase(c);
for (auto & socket : connection_sockets) {
if (socket.listen_id == hSocket) {
socket.status = SOCKET_KILLED;
}
}
return true;
} else {
++c;
}
}
return false;
}
// sending data
// must be a handle to a connected socket
// data is all sent via UDP, and thus send sizes are limited to 1200 bytes; after this, many routers will start dropping packets
// use the reliable flag with caution; although the resend rate is pretty aggressive,
// it can still cause stalls in receiving data (like TCP)
bool Steam_Networking::SendDataOnSocket( SNetSocket_t hSocket, void *pubData, uint32 cubData, bool bReliable )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket || socket->status != SOCKET_CONNECTED) return false;
Common_Message msg;
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_dest_id(socket->target.ConvertToUint64());
msg.set_allocated_network_old(new Network_Old);
msg.mutable_network_old()->set_type(Network_Old::DATA);
msg.mutable_network_old()->set_connection_id(socket->other_id);
msg.mutable_network_old()->set_data(pubData, cubData);
return network->sendTo(&msg, bReliable);
}
// receiving data
// returns false if there is no data remaining
// fills out *pcubMsgSize with the size of the next message, in bytes
bool Steam_Networking::IsDataAvailableOnSocket( SNetSocket_t hSocket, uint32 *pcubMsgSize )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket) {
if (pcubMsgSize) *pcubMsgSize = 0;
return false;
}
if (socket->data_packets.size() == 0) return false;
if (pcubMsgSize) *pcubMsgSize = socket->data_packets[0].data().size();
return true;
}
// fills in pubDest with the contents of the message
// messages are always complete, of the same size as was sent (i.e. packetized, not streaming)
// if *pcubMsgSize < cubDest, only partial data is written
// returns false if no data is available
bool Steam_Networking::RetrieveDataFromSocket( SNetSocket_t hSocket, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket || socket->data_packets.size() == 0) return false;
auto msg = std::begin(socket->data_packets);
if (msg != std::end(socket->data_packets)) {
uint32 msg_size = msg->data().size();
if (msg_size > cubDest) msg_size = cubDest;
if (pcubMsgSize) *pcubMsgSize = msg_size;
memcpy(pubDest, msg->data().data(), msg_size);
msg = socket->data_packets.erase(msg);
return true;
}
return false;
}
// checks for data from any socket that has been connected off this listen socket
// returns false if there is no data remaining
// fills out *pcubMsgSize with the size of the next message, in bytes
// fills out *phSocket with the socket that data is available on
bool Steam_Networking::IsDataAvailable( SNetListenSocket_t hListenSocket, uint32 *pcubMsgSize, SNetSocket_t *phSocket )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!hListenSocket) return false;
for (auto & socket : connection_sockets) {
if (socket.listen_id == hListenSocket && socket.data_packets.size()) {
if (pcubMsgSize) *pcubMsgSize = socket.data_packets[0].data().size();
if (phSocket) *phSocket = socket.id;
return true;
}
}
return false;
}
// retrieves data from any socket that has been connected off this listen socket
// fills in pubDest with the contents of the message
// messages are always complete, of the same size as was sent (i.e. packetized, not streaming)
// if *pcubMsgSize < cubDest, only partial data is written
// returns false if no data is available
// fills out *phSocket with the socket that data is available on
bool Steam_Networking::RetrieveData( SNetListenSocket_t hListenSocket, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, SNetSocket_t *phSocket )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!hListenSocket) return false;
for (auto & socket : connection_sockets) {
if (socket.listen_id == hListenSocket && socket.data_packets.size()) {
auto msg = std::begin(socket.data_packets);
if (msg != std::end(socket.data_packets)) {
uint32 msg_size = msg->data().size();
if (msg_size > cubDest) msg_size = cubDest;
if (pcubMsgSize) *pcubMsgSize = msg_size;
if (phSocket) *phSocket = socket.id;
memcpy(pubDest, msg->data().data(), msg_size);
msg = socket.data_packets.erase(msg);
return true;
}
}
}
return false;
}
// returns information about the specified socket, filling out the contents of the pointers
bool Steam_Networking::GetSocketInfo( SNetSocket_t hSocket, CSteamID *pSteamIDRemote, int *peSocketStatus, uint32 *punIPRemote, uint16 *punPortRemote )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket) return false;
if (pSteamIDRemote) *pSteamIDRemote = socket->target;
if (peSocketStatus) {
//TODO: I'm not sure what peSocketStatus is supposed to be but I'm guessing it's ESNetSocketState
if (socket->status == SOCKET_CONNECTED) {
*peSocketStatus = k_ESNetSocketStateConnected;
} else if (socket->status == SOCKET_CONNECTING) {
*peSocketStatus = k_ESNetSocketStateInitiated;
} else if (socket->status == SOCKET_DISCONNECTED) {
*peSocketStatus = k_ESNetSocketStateDisconnecting;
} else if (socket->status == SOCKET_KILLED) {
*peSocketStatus = k_ESNetSocketStateConnectionBroken;
} else {
*peSocketStatus = k_ESNetSocketStateInvalid;
}
}
if (punIPRemote) *punIPRemote = socket->nIP;
if (punPortRemote) *punPortRemote = socket->nPort;
return true;
}
bool Steam_Networking::GetSocketInfo( SNetSocket_t hSocket, CSteamID *pSteamIDRemote, int *peSocketStatus, SteamIPAddress_t *punIPRemote, uint16 *punPortRemote )
{
PRINT_DEBUG_ENTRY();
//TODO: ipv6
uint32 *ip_remote = NULL;
if (punIPRemote) {
ip_remote = &(punIPRemote->m_unIPv4);
}
bool ret = GetSocketInfo(hSocket, pSteamIDRemote, peSocketStatus, ip_remote, punPortRemote );
if (punIPRemote && ret) {
punIPRemote->m_eType = k_ESteamIPTypeIPv4;
}
return ret;
}
// returns which local port the listen socket is bound to
// *pnIP and *pnPort will be 0 if the socket is set to listen for P2P connections only
bool Steam_Networking::GetListenSocketInfo( SNetListenSocket_t hListenSocket, uint32 *pnIP, uint16 *pnPort )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto conn = std::find_if(listen_sockets.begin(), listen_sockets.end(), [&hListenSocket](struct steam_listen_socket const& conn) { return conn.id == hListenSocket;});
if (conn == listen_sockets.end()) return false;
if (pnIP) *pnIP = conn->nIP;
if (pnPort) *pnPort = conn->nPort;
return true;
}
bool Steam_Networking::GetListenSocketInfo( SNetListenSocket_t hListenSocket, SteamIPAddress_t *pnIP, uint16 *pnPort )
{
PRINT_DEBUG_ENTRY();
//TODO: ipv6
uint32 *ip = NULL;
if (pnIP) {
ip = &(pnIP->m_unIPv4);
}
bool ret = GetListenSocketInfo(hListenSocket, ip, pnPort );
if (pnIP && ret) {
pnIP->m_eType = k_ESteamIPTypeIPv4;
}
return ret;
}
// returns true to describe how the socket ended up connecting
ESNetSocketConnectionType Steam_Networking::GetSocketConnectionType( SNetSocket_t hSocket )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket || socket->status != SOCKET_CONNECTED) return k_ESNetSocketConnectionTypeNotConnected;
else return k_ESNetSocketConnectionTypeUDP;
}
// max packet size, in bytes
int Steam_Networking::GetMaxPacketSize( SNetSocket_t hSocket )
{
PRINT_DEBUG_ENTRY();
return 1500;
}
void Steam_Networking::RunCallbacks()
{
uint64 current_time = std::chrono::duration_cast<std::chrono::duration<uint64>>(std::chrono::system_clock::now().time_since_epoch()).count();
{
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
{
auto msg = std::begin(unprocessed_messages);
while (msg != std::end(unprocessed_messages)) {
CSteamID source_id((uint64)msg->source_id());
if (!connection_exists(source_id)) {
if (new_connection_times.find(source_id) == new_connection_times.end()) {
new_connections_to_call_cb.push(source_id);
new_connection_times[source_id] = std::chrono::high_resolution_clock::now();
}
} else {
struct Steam_Networking_Connection *conn = get_or_create_connection(source_id);
conn->open_channels.insert(msg->network().channel());
}
msg->mutable_network()->set_processed(true);
msg->mutable_network()->set_time_processed(current_time);
messages.push_back(*msg);
msg = unprocessed_messages.erase(msg);
}
}
auto msg = std::begin(messages);
while (msg != std::end(messages)) {
bool deleted = false;
if (msg->network().processed()) {
if (!connection_exists((uint64)msg->source_id())) {
if (msg->network().time_processed() + ORPHANED_PACKET_TIMEOUT < current_time) {
deleted = true;
}
}
}
if (deleted) {
msg = messages.erase(msg);
} else {
++msg;
}
}
}
while (!new_connections_to_call_cb.empty()) {
CSteamID source_id = new_connections_to_call_cb.front();
auto t = new_connection_times.find(source_id);
if (t == new_connection_times.end()) {
new_connections_to_call_cb.pop();
continue;
}
if (!check_timedout(t->second, NEW_CONNECTION_DELAY)) {
break;
}
P2PSessionRequest_t data;
memset(&data, 0, sizeof(data));
data.m_steamIDRemote = source_id;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
new_connections_to_call_cb.pop();
}
//TODO: not sure if sockets should be wiped right away
remove_killed_connection_sockets();
for(auto it = new_connection_times.begin(); it != new_connection_times.end(); ) {
if (std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - it->second).count() > NEW_CONNECTION_TIMEOUT) {
it = new_connection_times.erase(it);
//TODO send packet to other side to tell them connection has "failed".
} else {
++it;
}
}
}
void Steam_Networking::Callback(Common_Message *msg)
{
if (msg->has_network()) {
PRINT_DEBUG("got msg from: " "%" PRIu64 " to: " "%" PRIu64 " size %zu type %u | messages %p: %zu",
msg->source_id(), msg->dest_id(), msg->network().data().size(), msg->network().type(), &messages, messages.size()
);
PRINT_DEBUG("msg data: '%s'",
common_helpers::uint8_vector_to_hex_string(std::vector<uint8_t>(msg->network().data().begin(), msg->network().data().end())).c_str());
if (msg->network().type() == Network_pb::DATA) {
unprocessed_messages.push_back(Common_Message(*msg));
}
if (msg->network().type() == Network_pb::NEW_CONNECTION) {
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
auto msg_temp = std::begin(messages);
while (msg_temp != std::end(messages)) {
//only delete processed to handle unreliable message arriving at the same time.
if (msg_temp->source_id() == msg->source_id() && msg_temp->network().processed()) {
msg_temp = messages.erase(msg_temp);
} else {
++msg_temp;
}
}
}
}
if (msg->has_network_old()) {
PRINT_DEBUG("got network socket msg %u", msg->network_old().type());
if (msg->network_old().type() == Network_Old::CONNECTION_REQUEST_IP) {
for (auto & listen : listen_sockets) {
if (listen.nPort == msg->network_old().port()) {
SNetSocket_t new_sock = create_connection_socket((uint64)msg->source_id(), 0, 0, msg->network_old().port(), listen.id, SOCKET_CONNECTED, msg->network_old().connection_id_from());
if (new_sock) {
struct SocketStatusCallback_t data;
data.m_hSocket = new_sock;
data.m_hListenSocket = listen.id;
data.m_steamIDRemote = (uint64)msg->source_id();
data.m_eSNetSocketState = k_ESNetSocketStateConnected; //TODO is this the right state?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
}
} else if (msg->network_old().type() == Network_Old::CONNECTION_REQUEST_STEAMID) {
for (auto & listen : listen_sockets) {
if (listen.nVirtualP2PPort == msg->network_old().port()) {
SNetSocket_t new_sock = create_connection_socket((uint64)msg->source_id(), msg->network_old().port(), 0, 0, listen.id, SOCKET_CONNECTED, msg->network_old().connection_id_from());
if (new_sock) {
struct SocketStatusCallback_t data;
data.m_hSocket = new_sock;
data.m_hListenSocket = listen.id;
data.m_steamIDRemote = (uint64)msg->source_id();
data.m_eSNetSocketState = k_ESNetSocketStateConnected; //TODO is this the right state?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
}
} else if (msg->network_old().type() == Network_Old::CONNECTION_ACCEPTED) {
struct steam_connection_socket *socket = get_connection_socket(msg->network_old().connection_id());
if (socket && socket->nPort && socket->status == SOCKET_CONNECTING && !socket->target.IsValid()) {
socket->target = (uint64)msg->source_id();
}
if (socket && socket->status == SOCKET_CONNECTING && msg->source_id() == socket->target.ConvertToUint64()) {
socket->status = SOCKET_CONNECTED;
socket->other_id = msg->network_old().connection_id_from();
struct SocketStatusCallback_t data;
data.m_hSocket = socket->id;
data.m_hListenSocket = socket->listen_id;
data.m_steamIDRemote = socket->target;
data.m_eSNetSocketState = k_ESNetSocketStateConnected; //TODO is this the right state?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
} else if (msg->network_old().type() == Network_Old::CONNECTION_END) {
struct steam_connection_socket *socket = get_connection_socket(msg->network_old().connection_id());
if (socket && socket->status == SOCKET_CONNECTED && msg->source_id() == socket->target.ConvertToUint64()) {
struct SocketStatusCallback_t data;
socket->status = SOCKET_DISCONNECTED;
data.m_hSocket = socket->id;
data.m_hListenSocket = socket->listen_id;
data.m_steamIDRemote = socket->target;
data.m_eSNetSocketState = k_ESNetSocketStateRemoteEndDisconnected; //TODO is this the right state?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
} else if (msg->network_old().type() == Network_Old::DATA) {
struct steam_connection_socket *socket = get_connection_socket(msg->network_old().connection_id());
if (socket && socket->status == SOCKET_CONNECTED && msg->source_id() == socket->target.ConvertToUint64()) {
socket->data_packets.push_back(msg->network_old());
}
}
}
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::DISCONNECT) {
CSteamID source_id((uint64)msg->source_id());
if (connection_exists(source_id)) {
P2PSessionConnectFail_t data;
data.m_steamIDRemote = source_id;
data.m_eP2PSessionError = k_EP2PSessionErrorDestinationNotLoggedIn;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
for (auto & socket : connection_sockets) {
if (socket.target.ConvertToUint64() == msg->source_id()) {
struct SocketStatusCallback_t data;
socket.status = SOCKET_DISCONNECTED;
data.m_hSocket = socket.id;
data.m_hListenSocket = socket.listen_id;
data.m_steamIDRemote = socket.target;
data.m_eSNetSocketState = k_ESNetSocketStateConnectionBroken; //TODO is this the right state?
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
} else
if (msg->low_level().type() == Low_Level::CONNECT) {
}
}
}

View File

@ -0,0 +1,419 @@
/* 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 "dll/steam_networking_messages.h"
#define NETWORKING_MESSAGES_TIMEOUT 30.0
void Steam_Networking_Messages::steam_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_Networking_Messages *steam_networking_messages = (Steam_Networking_Messages *)object;
steam_networking_messages->Callback(msg);
}
void Steam_Networking_Messages::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_Networking_Messages *steam_networking_messages = (Steam_Networking_Messages *)object;
steam_networking_messages->RunCallbacks();
}
void Steam_Networking_Messages::free_steam_message_data(SteamNetworkingMessage_t *pMsg)
{
free(pMsg->m_pData);
pMsg->m_pData = NULL;
}
void Steam_Networking_Messages::delete_steam_message(SteamNetworkingMessage_t *pMsg)
{
if (pMsg->m_pfnFreeData) pMsg->m_pfnFreeData(pMsg);
delete pMsg;
}
void Steam_Networking_Messages::end_connection(CSteamID steam_id)
{
auto conn = connections.find(steam_id);
if (conn != connections.end()) {
conn->second.dead = true;
}
}
std::map<CSteamID, Steam_Message_Connection>::iterator Steam_Networking_Messages::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;
}
Steam_Networking_Messages::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->callback_results = callback_results;
this->callbacks = callbacks;
this->run_every_runcb = run_every_runcb;
this->created = std::chrono::steady_clock::now();
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);
}
Steam_Networking_Messages::~Steam_Networking_Messages()
{
this->network->rmCallback(CALLBACK_ID_NETWORKING_MESSAGES, settings->get_local_steam_id(), &Steam_Networking_Messages::steam_callback, this);
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Messages::steam_callback, this);
this->run_every_runcb->remove(&Steam_Networking_Messages::steam_run_every_runcb, this);
}
/// 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 Steam_Networking_Messages::SendMessageToUser( const SteamNetworkingIdentity &identityRemote, const void *pubData, uint32 cubData, int nSendFlags, int nRemoteChannel )
{
PRINT_DEBUG_ENTRY();
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("%llu", identityRemote.GetSteamID64());
//steam id identity
} else if (ip) {
PRINT_DEBUG("%u:%u ipv4? %u", 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;
}
/// 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 Steam_Networking_Messages::ReceiveMessagesOnChannel( int nLocalChannel, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
{
PRINT_DEBUG_ENTRY();
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("got %u", 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 Steam_Networking_Messages::AcceptSessionWithUser( const SteamNetworkingIdentity &identityRemote )
{
PRINT_DEBUG_ENTRY();
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 Steam_Networking_Messages::CloseSessionWithUser( const SteamNetworkingIdentity &identityRemote )
{
PRINT_DEBUG_ENTRY();
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 Steam_Networking_Messages::CloseChannelWithUser( const SteamNetworkingIdentity &identityRemote, int nLocalChannel )
{
PRINT_DEBUG_TODO();
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 Steam_Networking_Messages::GetSessionConnectionInfo( const SteamNetworkingIdentity &identityRemote, SteamNetConnectionInfo_t *pConnectionInfo, SteamNetConnectionRealTimeStatus_t *pQuickStatus )
{
PRINT_DEBUG_ENTRY();
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 Steam_Networking_Messages::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 Steam_Networking_Messages::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("got network socket msg %u", 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));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,155 @@
/* 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 "dll/steam_networking_socketsserialized.h"
void Steam_Networking_Sockets_Serialized::steam_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_Networking_Sockets_Serialized *steam_networkingsockets = (Steam_Networking_Sockets_Serialized *)object;
steam_networkingsockets->Callback(msg);
}
void Steam_Networking_Sockets_Serialized::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_Networking_Sockets_Serialized *steam_networkingsockets = (Steam_Networking_Sockets_Serialized *)object;
steam_networkingsockets->RunCallbacks();
}
Steam_Networking_Sockets_Serialized::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->callback_results = callback_results;
this->callbacks = callbacks;
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);
}
Steam_Networking_Sockets_Serialized::~Steam_Networking_Sockets_Serialized()
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Sockets_Serialized::steam_callback, this);
this->run_every_runcb->remove(&Steam_Networking_Sockets_Serialized::steam_run_every_runcb, this);
}
void Steam_Networking_Sockets_Serialized::SendP2PRendezvous( CSteamID steamIDRemote, uint32 unConnectionIDSrc, const void *pMsgRendezvous, uint32 cbRendezvous )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void Steam_Networking_Sockets_Serialized::SendP2PConnectionFailure( CSteamID steamIDRemote, uint32 unConnectionIDDest, uint32 nReason, const char *pszReason )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
SteamAPICall_t Steam_Networking_Sockets_Serialized::GetCertAsync()
{
PRINT_DEBUG_TODO();
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 Steam_Networking_Sockets_Serialized::GetNetworkConfigJSON( void *buf, uint32 cbBuf, const char *pszLauncherPartner )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int Steam_Networking_Sockets_Serialized::GetNetworkConfigJSON( void *buf, uint32 cbBuf )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return GetNetworkConfigJSON(buf, cbBuf, "");
}
void Steam_Networking_Sockets_Serialized::CacheRelayTicket( const void *pTicket, uint32 cbTicket )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
uint32 Steam_Networking_Sockets_Serialized::GetCachedRelayTicketCount()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int Steam_Networking_Sockets_Serialized::GetCachedRelayTicket( uint32 idxTicket, void *buf, uint32 cbBuf )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
void Steam_Networking_Sockets_Serialized::PostConnectionStateMsg( const void *pMsg, uint32 cbMsg )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
bool Steam_Networking_Sockets_Serialized::GetSTUNServer(int dont_know, char *buf, unsigned int len)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
bool Steam_Networking_Sockets_Serialized::BAllowDirectConnectToPeer(SteamNetworkingIdentity const &identity)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
int Steam_Networking_Sockets_Serialized::BeginAsyncRequestFakeIP(int a)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
void Steam_Networking_Sockets_Serialized::RunCallbacks()
{
}
void Steam_Networking_Sockets_Serialized::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) {
}
}
}

View File

@ -0,0 +1,730 @@
/* 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 "dll/steam_networking_utils.h"
void Steam_Networking_Utils::steam_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_Networking_Utils *steam_networkingutils = (Steam_Networking_Utils *)object;
steam_networkingutils->Callback(msg);
}
void Steam_Networking_Utils::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_Networking_Utils *steam_networkingutils = (Steam_Networking_Utils *)object;
steam_networkingutils->RunCallbacks();
}
Steam_Networking_Utils::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->callback_results = callback_results;
this->callbacks = callbacks;
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);
}
Steam_Networking_Utils::~Steam_Networking_Utils()
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Utils::steam_callback, this);
this->run_every_runcb->remove(&Steam_Networking_Utils::steam_run_every_runcb, this);
}
void Steam_Networking_Utils::free_steam_message_data(SteamNetworkingMessage_t *pMsg)
{
free(pMsg->m_pData);
pMsg->m_pData = NULL;
}
void Steam_Networking_Utils::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* Steam_Networking_Utils::AllocateMessage( int cbAllocateBuffer )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
SteamNetworkingMessage_t *pMsg = new SteamNetworkingMessage_t();
pMsg->m_pfnFreeData = &free_steam_message_data;
pMsg->m_pfnRelease = &delete_steam_message;
if (cbAllocateBuffer < 0)
cbAllocateBuffer = 0;
pMsg->m_pData = nullptr;
if (cbAllocateBuffer)
pMsg->m_pData = malloc(cbAllocateBuffer);
pMsg->m_cbSize = cbAllocateBuffer;
return pMsg;
}
bool Steam_Networking_Utils::InitializeRelayAccess()
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
init_relay = true;
return relay_initialized;
}
SteamRelayNetworkStatus_t Steam_Networking_Utils::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 Steam_Networking_Utils::GetRelayNetworkStatus( SteamRelayNetworkStatus_t *pDetails )
{
PRINT_DEBUG("TODO %p", 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 Steam_Networking_Utils::GetLocalPingLocation( SteamNetworkPingLocation_t &result )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (relay_initialized) {
result.m_data[2] = 123;
result.m_data[8] = 67;
return 2.0;
}
return -1;
}
int Steam_Networking_Utils::EstimatePingTimeBetweenTwoLocations( const SteamNetworkPingLocation_t &location1, const SteamNetworkPingLocation_t &location2 )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//return k_nSteamNetworkingPing_Unknown;
return 2;
}
int Steam_Networking_Utils::EstimatePingTimeFromLocalHost( const SteamNetworkPingLocation_t &remoteLocation )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 2;
}
void Steam_Networking_Utils::ConvertPingLocationToString( const SteamNetworkPingLocation_t &location, char *pszBuf, int cchBufSize )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
strncpy(pszBuf, "fra=10+2", cchBufSize);
}
bool Steam_Networking_Utils::ParsePingLocationString( const char *pszString, SteamNetworkPingLocation_t &result )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return true;
}
bool Steam_Networking_Utils::CheckPingDataUpToDate( float flMaxAgeSeconds )
{
PRINT_DEBUG("TODO %f", flMaxAgeSeconds);
init_relay = true;
return relay_initialized;
}
bool Steam_Networking_Utils::IsPingMeasurementInProgress()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
int Steam_Networking_Utils::GetPingToDataCenter( SteamNetworkingPOPID popID, SteamNetworkingPOPID *pViaRelayPoP )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int Steam_Networking_Utils::GetDirectPingToPOP( SteamNetworkingPOPID popID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int Steam_Networking_Utils::GetPOPCount()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
int Steam_Networking_Utils::GetPOPList( SteamNetworkingPOPID *list, int nListSz )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Networking_Utils::GetLocalTimestamp()
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Networking_Utils::SetDebugOutputFunction( ESteamNetworkingSocketsDebugOutputType eDetailLevel, FSteamNetworkingSocketsDebugOutput pfnFunc )
{
PRINT_DEBUG("%i", eDetailLevel);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Networking_Utils::GetIPv4FakeIPType( uint32 nIPv4 )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Networking_Utils::GetRealIdentityForFakeIP( const SteamNetworkingIPAddr &fakeIP, SteamNetworkingIdentity *pOutRealIdentity )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_EResultNoMatch;
}
//
// Set and get configuration values, see ESteamNetworkingConfigValue for individual descriptions.
//
// Shortcuts for common cases. (Implemented as inline functions below)
/*
bool Steam_Networking_Utils::SetGlobalConfigValueInt32( ESteamNetworkingConfigValue eValue, int32 val );
bool Steam_Networking_Utils::SetGlobalConfigValueFloat( ESteamNetworkingConfigValue eValue, float val );
bool Steam_Networking_Utils::SetGlobalConfigValueString( ESteamNetworkingConfigValue eValue, const char *val );
bool Steam_Networking_Utils::SetConnectionConfigValueInt32( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, int32 val );
bool Steam_Networking_Utils::SetConnectionConfigValueFloat( HSteamNetConnection hConn, ESteamNetworkingConfigValue eValue, float val );
bool Steam_Networking_Utils::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 Steam_Networking_Utils::SetConfigValue( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj,
ESteamNetworkingConfigDataType eDataType, const void *pArg )
{
PRINT_DEBUG("TODO %i %i " "%" PRIdPTR " %i %p", eValue, eScopeType, scopeObj, eDataType, pArg);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Networking_Utils::GetConfigValue( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj,
ESteamNetworkingConfigDataType *pOutDataType, void *pResult, size_t *cbResult )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Networking_Utils::GetConfigValueInfo( ESteamNetworkingConfigValue eValue, const char **pOutName, ESteamNetworkingConfigDataType *pOutDataType, ESteamNetworkingConfigScope *pOutScope, ESteamNetworkingConfigValue *pOutNextValue )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//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* Steam_Networking_Utils::GetConfigValueInfo( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigDataType *pOutDataType, ESteamNetworkingConfigScope *pOutScope )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO flat api
return NULL;
}
/// Return the lowest numbered configuration value available in the current environment.
ESteamNetworkingConfigValue Steam_Networking_Utils::GetFirstConfigValue()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Networking_Utils::IterateGenericEditableConfigValues( ESteamNetworkingConfigValue eCurrent, bool bEnumerateDevVars )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_ESteamNetworkingConfig_Invalid;
}
// String conversions. You'll usually access these using the respective
// inline methods.
void Steam_Networking_Utils::SteamNetworkingIPAddr_ToString( const SteamNetworkingIPAddr &addr, char *buf, size_t cbBuf, bool bWithPort )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (buf == nullptr || cbBuf == 0) return;
char buffer[64]{}; // enough 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 Steam_Networking_Utils::SteamNetworkingIPAddr_ParseString( SteamNetworkingIPAddr *pAddr, const char *pszStr )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Networking_Utils::SteamNetworkingIPAddr_GetFakeIPType( const SteamNetworkingIPAddr &addr )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_ESteamNetworkingFakeIPType_NotFake;
}
void Steam_Networking_Utils::SteamNetworkingIdentity_ToString( const SteamNetworkingIdentity &identity, char *buf, size_t cbBuf )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Networking_Utils::SteamNetworkingIdentity_ParseString( SteamNetworkingIdentity *pIdentity, const char *pszStr )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Networking_Utils::RunCallbacks()
{
if (init_relay && !relay_initialized) {
relay_initialized = true;
SteamRelayNetworkStatus_t data = get_network_status();
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
void Steam_Networking_Utils::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()) {
}
}

195
dll/steam_parties.cpp Normal file
View File

@ -0,0 +1,195 @@
/* 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 "dll/steam_parties.h"
void Steam_Parties::steam_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_Parties *steam_parties = (Steam_Parties *)object;
steam_parties->Callback(msg);
}
void Steam_Parties::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_Parties *steam_parties = (Steam_Parties *)object;
steam_parties->RunCallbacks();
}
Steam_Parties::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->callback_results = callback_results;
this->callbacks = callbacks;
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);
}
Steam_Parties::~Steam_Parties()
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Parties::steam_callback, this);
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 Steam_Parties::GetNumActiveBeacons()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
PartyBeaconID_t Steam_Parties::GetBeaconByIndex( uint32 unIndex )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_ulPartyBeaconIdInvalid;
}
bool Steam_Parties::GetBeaconDetails( PartyBeaconID_t ulBeaconID, CSteamID *pSteamIDBeaconOwner, STEAM_OUT_STRUCT() SteamPartyBeaconLocation_t *pLocation, STEAM_OUT_STRING_COUNT(cchMetadata) char *pchMetadata, int cchMetadata )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Parties::JoinParty( PartyBeaconID_t ulBeaconID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// =============================================================================================
// Party Host APIs
// Get a list of possible beacon locations
bool Steam_Parties::GetNumAvailableBeaconLocations( uint32 *puNumLocations )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
bool Steam_Parties::GetAvailableBeaconLocations( SteamPartyBeaconLocation_t *pLocationList, uint32 uMaxNumLocations )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Parties::CreateBeacon( uint32 unOpenSlots, SteamPartyBeaconLocation_t *pBeaconLocation, const char *pchConnectString, const char *pchMetadata )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_Parties::OnReservationCompleted( PartyBeaconID_t ulBeacon, CSteamID steamIDUser )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_Parties::CancelReservation( PartyBeaconID_t ulBeacon, CSteamID steamIDUser )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
// 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 Steam_Parties::ChangeNumOpenSlots( PartyBeaconID_t ulBeacon, uint32 unOpenSlots )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Turn off the beacon.
bool Steam_Parties::DestroyBeacon( PartyBeaconID_t ulBeacon )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Utils
bool Steam_Parties::GetBeaconLocationData( SteamPartyBeaconLocation_t BeaconLocation, ESteamPartyBeaconLocationData eData, STEAM_OUT_STRING_COUNT(cchDataStringOut) char *pchDataStringOut, int cchDataStringOut )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
void Steam_Parties::RunCallbacks()
{
}
void Steam_Parties::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()) {
}
}

1187
dll/steam_remote_storage.cpp Normal file

File diff suppressed because it is too large Load Diff

142
dll/steam_remoteplay.cpp Normal file
View File

@ -0,0 +1,142 @@
/* 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 "dll/steam_remoteplay.h"
void Steam_RemotePlay::steam_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_RemotePlay *steam_remoteplay = (Steam_RemotePlay *)object;
steam_remoteplay->Callback(msg);
}
void Steam_RemotePlay::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_RemotePlay *steam_remoteplay = (Steam_RemotePlay *)object;
steam_remoteplay->RunCallbacks();
}
Steam_RemotePlay::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->callback_results = callback_results;
this->callbacks = callbacks;
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);
}
Steam_RemotePlay::~Steam_RemotePlay()
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_RemotePlay::steam_callback, this);
this->run_every_runcb->remove(&Steam_RemotePlay::steam_run_every_runcb, this);
}
// Get the number of currently connected Steam Remote Play sessions
uint32 Steam_RemotePlay::GetSessionCount()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Get the currently connected Steam Remote Play session ID at the specified index. Returns zero if index is out of bounds.
uint32 Steam_RemotePlay::GetSessionID( int iSessionIndex )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
// Get the SteamID of the connected user
CSteamID Steam_RemotePlay::GetSessionSteamID( uint32 unSessionID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return k_steamIDNil;
}
// Get the name of the session client device
// This returns NULL if the sessionID is not valid
const char* Steam_RemotePlay::GetSessionClientName( uint32 unSessionID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return NULL;
}
// Get the form factor of the session client device
ESteamDeviceFormFactor Steam_RemotePlay::GetSessionClientFormFactor( uint32 unSessionID )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
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 Steam_RemotePlay::BGetSessionClientResolution( uint32 unSessionID, int *pnResolutionX, int *pnResolutionY )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pnResolutionX) *pnResolutionX = 0;
if (pnResolutionY) *pnResolutionY = 0;
return false;
}
bool Steam_RemotePlay::BStartRemotePlayTogether( bool bShowOverlay )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Invite a friend to Remote Play Together
// This returns false if the invite can't be sent
bool Steam_RemotePlay::BSendRemotePlayTogetherInvite( CSteamID steamIDFriend )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
void Steam_RemotePlay::RunCallbacks()
{
}
void Steam_RemotePlay::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()) {
}
}

View File

@ -21,6 +21,7 @@ Steam_Screenshots::Steam_Screenshots(class Local_Storage* local_storage, class S
local_storage(local_storage), local_storage(local_storage),
callbacks(callbacks) callbacks(callbacks)
{ {
} }
ScreenshotHandle Steam_Screenshots::create_screenshot_handle() ScreenshotHandle Steam_Screenshots::create_screenshot_handle()

119
dll/steam_tv.cpp Normal file
View File

@ -0,0 +1,119 @@
/* 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 "dll/steam_tv.h"
void Steam_TV::steam_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_TV *steam_parties = (Steam_TV *)object;
steam_parties->Callback(msg);
}
void Steam_TV::steam_run_every_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_TV *steam_parties = (Steam_TV *)object;
steam_parties->RunCallbacks();
}
Steam_TV::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->callback_results = callback_results;
this->callbacks = callbacks;
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);
}
Steam_TV::~Steam_TV()
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_TV::steam_callback, this);
this->run_every_runcb->remove(&Steam_TV::steam_run_every_runcb, this);
}
bool Steam_TV::IsBroadcasting(int *pnNumViewers)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
void Steam_TV::AddBroadcastGameData(const char * pchKey, const char * pchValue)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void Steam_TV::RemoveBroadcastGameData(const char * pchKey)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void Steam_TV::AddTimelineMarker(const char * pchTemplateName, bool bPersistent, uint8 nColorR, uint8 nColorG, uint8 nColorB)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void Steam_TV::RemoveTimelineMarker()
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
uint32 Steam_TV::AddRegion(const char * pchElementName, const char * pchTimelineDataSection, const SteamTVRegion_t * pSteamTVRegion, ESteamTVRegionBehavior eSteamTVRegionBehavior)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return 0;
}
void Steam_TV::RemoveRegion(uint32 unRegionHandle)
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
}
void Steam_TV::RunCallbacks()
{
}
void Steam_TV::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()) {
}
}

1429
dll/steam_ugc.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,119 @@
/* 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 "dll/steam_unified_messages.h"
void Steam_Unified_Messages::network_callback(void *object, Common_Message *msg)
{
// PRINT_DEBUG_ENTRY();
Steam_Unified_Messages *steam_steamunifiedmessages = (Steam_Unified_Messages *)object;
steam_steamunifiedmessages->Callback(msg);
}
void Steam_Unified_Messages::steam_runcb(void *object)
{
// PRINT_DEBUG_ENTRY();
Steam_Unified_Messages *steam_steamunifiedmessages = (Steam_Unified_Messages *)object;
steam_steamunifiedmessages->RunCallbacks();
}
Steam_Unified_Messages::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->callback_results = callback_results;
this->callbacks = callbacks;
this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Unified_Messages::network_callback, this);
this->run_every_runcb->add(&Steam_Unified_Messages::steam_runcb, this);
}
Steam_Unified_Messages::~Steam_Unified_Messages()
{
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Unified_Messages::network_callback, this);
this->run_every_runcb->remove(&Steam_Unified_Messages::steam_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 Steam_Unified_Messages::SendMethod( const char *pchServiceMethod, const void *pRequestBuffer, uint32 unRequestBufferSize, uint64 unContext )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return ISteamUnifiedMessages::k_InvalidUnifiedMessageHandle;
}
// Gets the size of the response and the EResult. Returns false if the response is not ready yet.
bool Steam_Unified_Messages::GetMethodResponseInfo( ClientUnifiedMessageHandle hHandle, uint32 *punResponseSize, EResult *peResult )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Gets a response in binary serialized form (and optionally release the corresponding allocated memory).
bool Steam_Unified_Messages::GetMethodResponseData( ClientUnifiedMessageHandle hHandle, void *pResponseBuffer, uint32 unResponseBufferSize, bool bAutoRelease )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Releases the message and its corresponding allocated memory.
bool Steam_Unified_Messages::ReleaseMethod( ClientUnifiedMessageHandle hHandle )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
// Sends a service notification (in binary serialized form) using the Steam Client.
// Returns true if the notification was sent successfully.
bool Steam_Unified_Messages::SendNotification( const char *pchServiceNotification, const void *pNotificationBuffer, uint32 unNotificationBufferSize )
{
PRINT_DEBUG_TODO();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return false;
}
void Steam_Unified_Messages::RunCallbacks()
{
}
void Steam_Unified_Messages::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) {
}
}
}

528
dll/steam_user.cpp Normal file
View File

@ -0,0 +1,528 @@
/* 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 "dll/steam_user.h"
#include "dll/auth.h"
#include "dll/appticket.h"
Steam_User::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;
auth_manager = new Auth_Manager(settings, network, callbacks);
}
Steam_User::~Steam_User()
{
delete auth_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 Steam_User::GetHSteamUser()
{
PRINT_DEBUG_ENTRY();
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 Steam_User::BLoggedOn()
{
PRINT_DEBUG_ENTRY();
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 Steam_User::GetSteamID()
{
PRINT_DEBUG_ENTRY();
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 Steam_User::InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer, bool bSecure )
{
PRINT_DEBUG("%i %llu %u %u %u %p", cbMaxAuthBlob, steamIDGameServer.ConvertToUint64(), unIPServer, usPortServer, bSecure, pAuthBlob);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (cbMaxAuthBlob < INITIATE_GAME_CONNECTION_TICKET_SIZE) return 0;
if (!pAuthBlob) return 0;
uint32 out_size = INITIATE_GAME_CONNECTION_TICKET_SIZE;
auth_manager->getTicketData(pAuthBlob, INITIATE_GAME_CONNECTION_TICKET_SIZE, &out_size);
if (out_size > INITIATE_GAME_CONNECTION_TICKET_SIZE)
return 0;
return out_size;
}
int Steam_User::InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, CGameID gameID, uint32 unIPServer, uint16 usPortServer, bool bSecure )
{
PRINT_DEBUG_ENTRY();
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 Steam_User::TerminateGameConnection( uint32 unIPServer, uint16 usPortServer )
{
PRINT_DEBUG_TODO();
}
// Legacy functions
// used by only a few games to track usage events
void Steam_User::TrackAppUsageEvent( CGameID gameID, int eAppUsageEvent, const char *pchExtraInfo)
{
PRINT_DEBUG_TODO();
}
void Steam_User::RefreshSteam2Login()
{
PRINT_DEBUG_TODO();
}
// 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 Steam_User::GetUserDataFolder( char *pchBuffer, int cubBuffer )
{
PRINT_DEBUG_ENTRY();
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 Steam_User::StartVoiceRecording( )
{
PRINT_DEBUG_ENTRY();
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 Steam_User::StopVoiceRecording( )
{
PRINT_DEBUG_ENTRY();
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 Steam_User::GetAvailableVoice( uint32 *pcbCompressed, uint32 *pcbUncompressed_Deprecated, uint32 nUncompressedVoiceDesiredSampleRate_Deprecated )
{
PRINT_DEBUG_ENTRY();
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 Steam_User::GetAvailableVoice(uint32 *pcbCompressed, uint32 *pcbUncompressed)
{
PRINT_DEBUG("old");
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 Steam_User::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_ENTRY();
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");
}
last_get_voice = std::chrono::high_resolution_clock::now();
return k_EVoiceResultOK;
}
EVoiceResult Steam_User::GetVoice( bool bWantCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, bool bWantUncompressed, void *pUncompressedDestBuffer, uint32 cbUncompressedDestBufferSize, uint32 *nUncompressBytesWritten )
{
PRINT_DEBUG("old");
return GetVoice(bWantCompressed, pDestBuffer, cbDestBufferSize, nBytesWritten, bWantUncompressed, pUncompressedDestBuffer, cbUncompressedDestBufferSize, nUncompressBytesWritten, 11025);
}
EVoiceResult Steam_User::GetCompressedVoice( void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
{
PRINT_DEBUG_ENTRY();
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 Steam_User::DecompressVoice( const void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, uint32 nDesiredSampleRate )
{
PRINT_DEBUG_ENTRY();
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 Steam_User::DecompressVoice( const void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
{
PRINT_DEBUG("old");
return DecompressVoice(pCompressed, cbCompressed, pDestBuffer, cbDestBufferSize, nBytesWritten, 11025);
}
EVoiceResult Steam_User::DecompressVoice( void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten )
{
PRINT_DEBUG("older");
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 Steam_User::GetVoiceOptimalSampleRate()
{
PRINT_DEBUG_ENTRY();
return 48000;
}
// Retrieve ticket to be sent to the entity who wishes to authenticate you.
// pcbTicket retrieves the length of the actual ticket.
HAuthTicket Steam_User::GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{
return GetAuthSessionTicket(pTicket, cbMaxTicket, pcbTicket, NULL);
}
// SteamNetworkingIdentity is an optional input parameter to hold the public IP address or SteamID of the entity you are connecting to
// if an IP address is passed Steam will only allow the ticket to be used by an entity with that IP address
// if a Steam ID is passed Steam will only allow the ticket to be used by that Steam ID
HAuthTicket Steam_User::GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket, const SteamNetworkingIdentity *pSteamNetworkingIdentity )
{
PRINT_DEBUG("%p [%i] %p", pTicket, cbMaxTicket, pcbTicket);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!pTicket) return k_HAuthTicketInvalid;
return auth_manager->getTicket(pTicket, cbMaxTicket, pcbTicket);
}
// Request a ticket which will be used for webapi "ISteamUserAuth\AuthenticateUserTicket"
// pchIdentity is an optional input parameter to identify the service the ticket will be sent to
// the ticket will be returned in callback GetTicketForWebApiResponse_t
HAuthTicket Steam_User::GetAuthTicketForWebApi( const char *pchIdentity )
{
PRINT_DEBUG("%s", pchIdentity);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return auth_manager->getWebApiTicket(pchIdentity);
}
// 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 Steam_User::BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID )
{
PRINT_DEBUG("%i %llu", cbAuthTicket, steamID.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
return auth_manager->beginAuth(pAuthTicket, cbAuthTicket, steamID);
}
// Stop tracking started by BeginAuthSession - called when no longer playing game with this entity
void Steam_User::EndAuthSession( CSteamID steamID )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auth_manager->endAuth(steamID);
}
// Cancel auth ticket from GetAuthSessionTicket, called when no longer playing game with the entity you gave the ticket to
void Steam_User::CancelAuthTicket( HAuthTicket hAuthTicket )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auth_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 Steam_User::UserHasLicenseForApp( CSteamID steamID, AppId_t appID )
{
PRINT_DEBUG_ENTRY();
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 Steam_User::BIsBehindNAT()
{
PRINT_DEBUG_ENTRY();
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 Steam_User::AdvertiseGame( CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer )
{
PRINT_DEBUG_ENTRY();
std::lock_guard<std::recursive_mutex> lock(global_mutex);
Gameserver *server = new Gameserver();
server->set_id(steamIDGameServer.ConvertToUint64());
server->set_ip(unIPServer);
server->set_port(usPortServer);
server->set_query_port(usPortServer);
server->set_appid(settings->get_local_game_id().ToUint64());
if (settings->matchmaking_server_list_always_lan_type)
server->set_type(eLANServer);
else
server->set_type(eFriendsServer);
Common_Message msg;
msg.set_allocated_gameserver(server);
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
network->sendToAllIndividuals(&msg, true);
}
// 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 Steam_User::RequestEncryptedAppTicket( void *pDataToInclude, int cbDataToInclude )
{
PRINT_DEBUG("%i", cbDataToInclude);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
EncryptedAppTicketResponse_t data;
data.m_eResult = k_EResultOK;
DecryptedAppTicket ticket;
ticket.TicketV1.Reset();
ticket.TicketV2.Reset();
ticket.TicketV4.Reset();
ticket.TicketV1.TicketVersion = 1;
if (pDataToInclude) {
ticket.TicketV1.UserData.assign((uint8_t*)pDataToInclude, (uint8_t*)pDataToInclude + cbDataToInclude);
}
ticket.TicketV2.TicketVersion = 4;
ticket.TicketV2.SteamID = settings->get_local_steam_id().ConvertToUint64();
ticket.TicketV2.TicketIssueTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
ticket.TicketV2.TicketValidityEnd = ticket.TicketV2.TicketIssueTime + (21 * 24 * 60 * 60);
for (int i = 0; i < 140; ++i)
{
AppId_t appid;
bool available;
std::string name;
if (!settings->getDLC(appid, appid, available, name)) break;
ticket.TicketV4.AppIDs.emplace_back(appid);
}
ticket.TicketV4.HasVACStatus = true;
ticket.TicketV4.VACStatus = 0;
auto serialized = ticket.SerializeTicket();
SteamAppTicket_pb pb;
pb.set_ticket_version_no(1);
pb.set_crc_encryptedticket(0); // TODO: Find out how to compute the CRC
pb.set_cb_encrypteduserdata(cbDataToInclude);
pb.set_cb_encrypted_appownershipticket(serialized.size() - 16);
pb.mutable_encrypted_ticket()->assign(serialized.begin(), serialized.end()); // TODO: Find how to encrypt datas
encrypted_app_ticket = pb.SerializeAsString();
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
// retrieve a finished ticket
bool Steam_User::GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{
PRINT_DEBUG("%i", cbMaxTicket);
unsigned int ticket_size = encrypted_app_ticket.size();
if (!cbMaxTicket) {
if (!pcbTicket) return false;
*pcbTicket = ticket_size;
return true;
}
if (!pTicket) return false;
if (ticket_size > cbMaxTicket) return false;
encrypted_app_ticket.copy((char *)pTicket, cbMaxTicket);
if (pcbTicket) *pcbTicket = ticket_size;
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 Steam_User::GetGameBadgeLevel( int nSeries, bool bFoil )
{
PRINT_DEBUG_ENTRY();
return 0;
}
// gets the Steam Level of the user, as shown on their profile
int Steam_User::GetPlayerSteamLevel()
{
PRINT_DEBUG_ENTRY();
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 Steam_User::RequestStoreAuthURL( const char *pchRedirectURL )
{
PRINT_DEBUG_ENTRY();
return 0;
}
// gets whether the users phone number is verified
bool Steam_User::BIsPhoneVerified()
{
PRINT_DEBUG_ENTRY();
return true;
}
// gets whether the user has two factor enabled on their account
bool Steam_User::BIsTwoFactorEnabled()
{
PRINT_DEBUG_ENTRY();
return true;
}
// gets whether the users phone number is identifying
bool Steam_User::BIsPhoneIdentifying()
{
PRINT_DEBUG_ENTRY();
return false;
}
// gets whether the users phone number is awaiting (re)verification
bool Steam_User::BIsPhoneRequiringVerification()
{
PRINT_DEBUG_ENTRY();
return false;
}
STEAM_CALL_RESULT( MarketEligibilityResponse_t )
SteamAPICall_t Steam_User::GetMarketEligibility()
{
PRINT_DEBUG_ENTRY();
return 0;
}
// Retrieves anti indulgence / duration control for current user
STEAM_CALL_RESULT( DurationControl_t )
SteamAPICall_t Steam_User::GetDurationControl()
{
PRINT_DEBUG_ENTRY();
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 Steam_User::BSetDurationControlOnlineState( EDurationControlOnlineState eNewState )
{
PRINT_DEBUG_ENTRY();
return false;
}

File diff suppressed because it is too large Load Diff

View File

@ -20,10 +20,10 @@
Steam_Utils::Steam_Utils(Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, Steam_Overlay *overlay): Steam_Utils::Steam_Utils(Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, Steam_Overlay *overlay):
settings(settings), settings(settings),
callback_results(callback_results), callback_results(callback_results),
callbacks(callbacks), callbacks(callbacks),
overlay(overlay) overlay(overlay)
{ {
} }

View File

@ -1,71 +1,71 @@
#include "dll/ugc_remote_storage_bridge.h" #include "dll/ugc_remote_storage_bridge.h"
Ugc_Remote_Storage_Bridge::Ugc_Remote_Storage_Bridge(class Settings *settings) Ugc_Remote_Storage_Bridge::Ugc_Remote_Storage_Bridge(class Settings *settings)
{ {
this->settings = settings; this->settings = settings;
// subscribe to all mods initially // subscribe to all mods initially
subscribed = settings->modSet(); subscribed = settings->modSet();
} }
void Ugc_Remote_Storage_Bridge::add_ugc_query_result(UGCHandle_t file_handle, PublishedFileId_t fileid, bool handle_of_primary_file) Ugc_Remote_Storage_Bridge::~Ugc_Remote_Storage_Bridge()
{ {
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
steam_ugc_queries[file_handle].mod_id = fileid; steam_ugc_queries.clear();
steam_ugc_queries[file_handle].is_primary_file = handle_of_primary_file; }
}
void Ugc_Remote_Storage_Bridge::add_ugc_query_result(UGCHandle_t file_handle, PublishedFileId_t fileid, bool handle_of_primary_file)
bool Ugc_Remote_Storage_Bridge::remove_ugc_query_result(UGCHandle_t file_handle) {
{ std::lock_guard lock(global_mutex);
std::lock_guard lock(global_mutex);
steam_ugc_queries[file_handle].mod_id = fileid;
return !!steam_ugc_queries.erase(file_handle); steam_ugc_queries[file_handle].is_primary_file = handle_of_primary_file;
} }
std::optional<Ugc_Remote_Storage_Bridge::QueryInfo> Ugc_Remote_Storage_Bridge::get_ugc_query_result(UGCHandle_t file_handle) const bool Ugc_Remote_Storage_Bridge::remove_ugc_query_result(UGCHandle_t file_handle)
{ {
std::lock_guard lock(global_mutex); std::lock_guard lock(global_mutex);
auto it = steam_ugc_queries.find(file_handle); return !!steam_ugc_queries.erase(file_handle);
if (steam_ugc_queries.end() == it) return std::nullopt; }
return it->second;
} std::optional<Ugc_Remote_Storage_Bridge::QueryInfo> Ugc_Remote_Storage_Bridge::get_ugc_query_result(UGCHandle_t file_handle) const
{
void Ugc_Remote_Storage_Bridge::add_subbed_mod(PublishedFileId_t id) std::lock_guard lock(global_mutex);
{
subscribed.insert(id); auto it = steam_ugc_queries.find(file_handle);
} if (steam_ugc_queries.end() == it) return std::nullopt;
return it->second;
void Ugc_Remote_Storage_Bridge::remove_subbed_mod(PublishedFileId_t id) }
{
subscribed.erase(id); void Ugc_Remote_Storage_Bridge::add_subbed_mod(PublishedFileId_t id)
} {
subscribed.insert(id);
size_t Ugc_Remote_Storage_Bridge::subbed_mods_count() const }
{
return subscribed.size(); void Ugc_Remote_Storage_Bridge::remove_subbed_mod(PublishedFileId_t id)
} {
subscribed.erase(id);
bool Ugc_Remote_Storage_Bridge::has_subbed_mod(PublishedFileId_t id) const }
{
return !!subscribed.count(id); size_t Ugc_Remote_Storage_Bridge::subbed_mods_count() const
} {
return subscribed.size();
std::set<PublishedFileId_t>::iterator Ugc_Remote_Storage_Bridge::subbed_mods_itr_begin() const }
{
return subscribed.begin(); bool Ugc_Remote_Storage_Bridge::has_subbed_mod(PublishedFileId_t id) const
} {
return !!subscribed.count(id);
std::set<PublishedFileId_t>::iterator Ugc_Remote_Storage_Bridge::subbed_mods_itr_end() const }
{
return subscribed.end(); std::set<PublishedFileId_t>::iterator Ugc_Remote_Storage_Bridge::subbed_mods_itr_begin() const
} {
return subscribed.begin();
Ugc_Remote_Storage_Bridge::~Ugc_Remote_Storage_Bridge() }
{
std::lock_guard lock(global_mutex); std::set<PublishedFileId_t>::iterator Ugc_Remote_Storage_Bridge::subbed_mods_itr_end() const
{
steam_ugc_queries.clear(); return subscribed.end();
} }

View File

@ -254,7 +254,7 @@ public:
void AddAchievementNotification(nlohmann::json const& ach); void AddAchievementNotification(nlohmann::json const& ach);
}; };
#else #else // EMU_OVERLAY
class Steam_Overlay class Steam_Overlay
{ {
@ -288,6 +288,6 @@ public:
void AddAchievementNotification(nlohmann::json const& ach) {} void AddAchievementNotification(nlohmann::json const& ach) {}
}; };
#endif #endif // EMU_OVERLAY
#endif//__INCLUDED_STEAM_OVERLAY_H__ #endif //__INCLUDED_STEAM_OVERLAY_H__

View File

@ -6,8 +6,6 @@
// avoids confusing ImGui when another label has the same text "MyText" // avoids confusing ImGui when another label has the same text "MyText"
#include "overlay/steam_overlay.h" #include "overlay/steam_overlay.h"
#include "overlay/notification.h"
#include "overlay/steam_overlay_translations.h"
#include <thread> #include <thread>
#include <string> #include <string>
@ -17,14 +15,17 @@
#include <random> #include <random>
#include "InGameOverlay/ImGui/imgui.h" #include "InGameOverlay/ImGui/imgui.h"
#include "InGameOverlay/RendererDetector.h"
#include "dll/dll.h" #include "dll/dll.h"
#include "dll/settings_parser.h" #include "dll/settings_parser.h"
#include "InGameOverlay/RendererDetector.h" // translation
#include "overlay/steam_overlay_translations.h"
// fonts // fonts
#include "fonts/unifont.hpp" #include "fonts/unifont.hpp"
// builtin audio
#include "overlay/notification.h"
#define URL_WINDOW_NAME "URL Window" #define URL_WINDOW_NAME "URL Window"
@ -135,7 +136,7 @@ Steam_Overlay::Steam_Overlay(Settings* settings, Local_Storage *local_storage, S
} }
this->network->setCallback(CALLBACK_ID_STEAM_MESSAGES, settings->get_local_steam_id(), &Steam_Overlay::overlay_networking_callback, this); this->network->setCallback(CALLBACK_ID_STEAM_MESSAGES, settings->get_local_steam_id(), &Steam_Overlay::overlay_networking_callback, this);
run_every_runcb->add(&Steam_Overlay::overlay_run_callback, this); this->run_every_runcb->add(&Steam_Overlay::overlay_run_callback, this);
} }
Steam_Overlay::~Steam_Overlay() Steam_Overlay::~Steam_Overlay()
@ -143,7 +144,7 @@ Steam_Overlay::~Steam_Overlay()
if (settings->disable_overlay) return; if (settings->disable_overlay) return;
this->network->rmCallback(CALLBACK_ID_STEAM_MESSAGES, settings->get_local_steam_id(), &Steam_Overlay::overlay_networking_callback, this); this->network->rmCallback(CALLBACK_ID_STEAM_MESSAGES, settings->get_local_steam_id(), &Steam_Overlay::overlay_networking_callback, this);
run_every_runcb->remove(&Steam_Overlay::overlay_run_callback, this); this->run_every_runcb->remove(&Steam_Overlay::overlay_run_callback, this);
} }
void Steam_Overlay::request_renderer_detector() void Steam_Overlay::request_renderer_detector()
@ -1038,7 +1039,7 @@ void Steam_Overlay::build_notifications(int width, int height)
friend_actions_temp.push(it->frd->first); friend_actions_temp.push(it->frd->first);
// when we click "accept game invite" from someone else, we want to remove this notification immediately since it's no longer relevant // when we click "accept game invite" from someone else, we want to remove this notification immediately since it's no longer relevant
// this assignment will make the notification elapsed time insanely large // this assignment will make the notification elapsed time insanely large
it->start_time = std::chrono::milliseconds(0); it->start_time = {};
} }
} }
break; break;