2024-05-03 01:29:57 +03:00
|
|
|
/* 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_matchmaking.h"
|
|
|
|
|
|
|
|
#define SEND_LOBBY_RATE 5.0
|
|
|
|
|
|
|
|
#define PENDING_JOIN_TIMEOUT 10.0
|
|
|
|
#define REQUEST_LOBBY_DATA_TIMEOUT 6.0
|
|
|
|
#define LOBBY_DELETED_TIMEOUT 2
|
|
|
|
|
|
|
|
#define LOBBY_CREATE_DELAY 0.07 //artificial delay for lobby creation
|
|
|
|
|
|
|
|
#define FILTER_MAX_DEFAULT 4096
|
|
|
|
|
|
|
|
#define LOBBY_SEARCH_TIMEOUT 0.2 //Tested on real steam
|
|
|
|
|
|
|
|
|
|
|
|
google::protobuf::Map<std::string,std::string>::const_iterator Steam_Matchmaking::caseinsensitive_find(const ::google::protobuf::Map< ::std::string, ::std::string >& map, std::string key)
|
|
|
|
{
|
|
|
|
auto x = map.begin();
|
|
|
|
while (x != map.end()) {
|
|
|
|
if (common_helpers::str_cmp_insensitive(key, x->first)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++x;
|
|
|
|
}
|
|
|
|
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
Lobby* Steam_Matchmaking::get_lobby(CSteamID id)
|
|
|
|
{
|
|
|
|
if (!id.IsLobby())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
auto lobby = std::find_if(lobbies.begin(), lobbies.end(), [&id](Lobby const& item) { return (item.room_id() & 0xFFFFFFFF) == (id.GetAccountID()); });
|
|
|
|
if (lobbies.end() == lobby)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return &(*lobby);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::send_lobby_data()
|
|
|
|
{
|
2024-05-06 21:16:45 +03:00
|
|
|
if (lobbies.size()) {
|
|
|
|
PRINT_DEBUG("lobbies %zu", lobbies.size());
|
|
|
|
}
|
2024-05-03 01:29:57 +03:00
|
|
|
|
|
|
|
for(auto & l: lobbies) {
|
|
|
|
if (get_lobby_member(&l, settings->get_local_steam_id()) && l.owner() == settings->get_local_steam_id().ConvertToUint64() && !l.deleted()) {
|
|
|
|
PRINT_DEBUG("lobby " "%" PRIu64 "", l.room_id());
|
|
|
|
Common_Message msg = Common_Message();
|
|
|
|
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
|
|
|
msg.set_allocated_lobby(new Lobby(l));
|
|
|
|
network->sendToAllIndividuals(&msg, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::trigger_lobby_dataupdate(CSteamID lobby, CSteamID member, bool success, double cb_timeout, bool send_changed_lobby)
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu %llu", lobby.ConvertToUint64(), member.ConvertToUint64());
|
|
|
|
LobbyDataUpdate_t data{};
|
|
|
|
data.m_ulSteamIDLobby = lobby.ConvertToUint64();
|
|
|
|
data.m_bSuccess = success;
|
|
|
|
data.m_ulSteamIDMember = member.ConvertToUint64();
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), cb_timeout, true);
|
|
|
|
|
|
|
|
// if this was a user data update, then trigger another callback for the lobby itself
|
|
|
|
if (lobby != member) {
|
|
|
|
data.m_ulSteamIDMember = lobby.ConvertToUint64();
|
|
|
|
//Is this really necessary?
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), cb_timeout, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
Lobby *l = get_lobby(lobby);
|
|
|
|
if (l && l->owner() == settings->get_local_steam_id().ConvertToUint64()) {
|
|
|
|
if (send_changed_lobby) {
|
|
|
|
PRINT_DEBUG("resending new data");
|
|
|
|
Common_Message msg = Common_Message();
|
|
|
|
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
|
|
|
msg.set_allocated_lobby(new Lobby(*l));
|
|
|
|
network->sendToAllIndividuals(&msg, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::trigger_lobby_member_join_leave(CSteamID lobby, CSteamID member, bool leaving, bool success, double cb_timeout)
|
|
|
|
{
|
|
|
|
LobbyChatUpdate_t data{};
|
|
|
|
data.m_ulSteamIDLobby = lobby.ConvertToUint64();
|
|
|
|
data.m_ulSteamIDUserChanged = member.ConvertToUint64();
|
|
|
|
data.m_ulSteamIDMakingChange = member.ConvertToUint64();
|
|
|
|
uint32 member_state_change = 0; //EChatMemberStateChange
|
|
|
|
|
|
|
|
if (!leaving) {
|
|
|
|
member_state_change |= k_EChatMemberStateChangeEntered;
|
|
|
|
} else {
|
|
|
|
member_state_change |= k_EChatMemberStateChangeLeft;
|
|
|
|
}
|
|
|
|
|
|
|
|
data.m_rgfChatMemberStateChange = member_state_change;
|
|
|
|
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), cb_timeout);
|
|
|
|
// trigger_lobby_dataupdate(lobby, member, success, cb_timeout);
|
|
|
|
trigger_lobby_dataupdate(lobby, lobby, success, cb_timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Steam_Matchmaking::send_owner_packet(CSteamID lobby_id, Lobby_Messages *message)
|
|
|
|
{
|
|
|
|
Lobby *lobby = get_lobby(lobby_id);
|
|
|
|
|
|
|
|
if (!lobby) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common_Message msg;
|
|
|
|
msg.set_allocated_lobby_messages(message);
|
|
|
|
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
|
|
|
msg.set_dest_id((uint64)lobby->owner());
|
|
|
|
msg.mutable_lobby_messages()->set_id(lobby_id.ConvertToUint64());
|
|
|
|
return network->sendTo(&msg, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Steam_Matchmaking::send_clients_packet(CSteamID lobby_id, Lobby_Messages *message)
|
|
|
|
{
|
|
|
|
Lobby *lobby = get_lobby(lobby_id);
|
|
|
|
|
|
|
|
if (!lobby) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common_Message msg;
|
|
|
|
msg.set_allocated_lobby_messages(message);
|
|
|
|
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
|
|
|
msg.mutable_lobby_messages()->set_id(lobby_id.ConvertToUint64());
|
|
|
|
return network->sendToAllIndividuals(&msg, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Steam_Matchmaking::send_lobby_members_packet(CSteamID lobby_id, Lobby_Messages *message)
|
|
|
|
{
|
|
|
|
Lobby *lobby = get_lobby(lobby_id);
|
|
|
|
|
|
|
|
if (!lobby) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common_Message msg;
|
|
|
|
msg.set_allocated_lobby_messages(message);
|
|
|
|
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
|
|
|
msg.mutable_lobby_messages()->set_id(lobby_id.ConvertToUint64());
|
|
|
|
|
|
|
|
for (auto & m : lobby->members()) {
|
|
|
|
msg.set_dest_id((uint64)m.id());
|
|
|
|
network->sendTo(&msg, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Steam_Matchmaking::change_owner(Lobby *lobby, CSteamID new_owner)
|
|
|
|
{
|
|
|
|
Lobby_Messages *message = new Lobby_Messages();
|
|
|
|
message->set_type(Lobby_Messages::CHANGE_OWNER);
|
|
|
|
message->set_idata(new_owner.ConvertToUint64());
|
|
|
|
lobby->set_owner(new_owner.ConvertToUint64());
|
|
|
|
send_owner_packet((uint64)lobby->room_id(), message);
|
|
|
|
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)lobby->room_id(), true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::send_gameservercreated_cb(uint64 room_id, uint64 server_id, uint32 ip, uint16 port)
|
|
|
|
{
|
|
|
|
LobbyGameCreated_t data;
|
|
|
|
data.m_ulSteamIDLobby = room_id;
|
|
|
|
data.m_ulSteamIDGameServer = server_id;
|
|
|
|
data.m_unIP = ip;
|
|
|
|
data.m_usPort = port;
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::on_self_enter_leave_lobby(CSteamID id, int type, bool leaving)
|
|
|
|
{
|
|
|
|
if (type == k_ELobbyTypeInvisible) return;
|
|
|
|
|
|
|
|
if (!leaving) {
|
|
|
|
settings->set_lobby(id);
|
|
|
|
} else {
|
|
|
|
settings->set_lobby(k_steamIDNil);
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO: handle cases where in two lobbies of type not invisible
|
|
|
|
//steam says a user can only be in one regular lobby but we all know how well documented steam is
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::steam_matchmaking_callback(void *object, Common_Message *msg)
|
|
|
|
{
|
|
|
|
// PRINT_DEBUG_ENTRY();
|
|
|
|
|
|
|
|
Steam_Matchmaking *steam_matchmaking = (Steam_Matchmaking *)object;
|
|
|
|
steam_matchmaking->Callback(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::steam_matchmaking_run_every_runcb(void *object)
|
|
|
|
{
|
|
|
|
// PRINT_DEBUG_ENTRY();
|
|
|
|
|
|
|
|
Steam_Matchmaking *steam_matchmaking = (Steam_Matchmaking *)object;
|
|
|
|
steam_matchmaking->RunCallbacks();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Steam_Matchmaking::add_member_to_lobby(Lobby *lobby, CSteamID id)
|
|
|
|
{
|
|
|
|
if (get_lobby_member(lobby, id)) return false; // player already exists
|
|
|
|
|
|
|
|
Lobby_Member *member = lobby->add_members();
|
|
|
|
member->set_id(id.ConvertToUint64());
|
|
|
|
PRINT_DEBUG("added lobby member %llu", (uint64)id.ConvertToUint64());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Steam_Matchmaking::leave_lobby(Lobby *lobby, CSteamID id)
|
|
|
|
{
|
|
|
|
auto member = std::find_if(lobby->mutable_members()->begin(), lobby->mutable_members()->end(), [&id](Lobby_Member const& item) { return item.id() == id.ConvertToUint64(); });
|
|
|
|
if (member != lobby->mutable_members()->end()) {
|
|
|
|
lobby->mutable_members()->erase(member);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Lobby_Member* Steam_Matchmaking::get_lobby_member(Lobby *lobby, CSteamID user_id)
|
|
|
|
{
|
|
|
|
if (!lobby) return NULL;
|
|
|
|
|
|
|
|
auto member = std::find_if(lobby->mutable_members()->begin(), lobby->mutable_members()->end(), [&user_id](Lobby_Member const& item) { return item.id() == user_id.ConvertToUint64(); });
|
|
|
|
if (lobby->mutable_members()->end() == member)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return &(*member);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Steam_Matchmaking::Steam_Matchmaking(class Settings *settings, class Local_Storage *local_storage, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
|
|
|
{
|
|
|
|
this->settings = settings;
|
|
|
|
this->local_storage = local_storage;
|
|
|
|
this->network = network;
|
|
|
|
this->callback_results = callback_results;
|
|
|
|
this->callbacks = callbacks;
|
|
|
|
this->run_every_runcb = run_every_runcb;
|
|
|
|
|
|
|
|
this->filter_max_results = FILTER_MAX_DEFAULT;
|
|
|
|
search_call_api_id = 0;
|
|
|
|
searching = false;
|
|
|
|
|
|
|
|
this->network->setCallback(CALLBACK_ID_LOBBY, settings->get_local_steam_id(), &Steam_Matchmaking::steam_matchmaking_callback, this);
|
|
|
|
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Matchmaking::steam_matchmaking_callback, this);
|
|
|
|
this->run_every_runcb->add(&Steam_Matchmaking::steam_matchmaking_run_every_runcb, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
Steam_Matchmaking::~Steam_Matchmaking()
|
|
|
|
{
|
|
|
|
this->network->rmCallback(CALLBACK_ID_LOBBY, settings->get_local_steam_id(), &Steam_Matchmaking::steam_matchmaking_callback, this);
|
|
|
|
this->network->rmCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Matchmaking::steam_matchmaking_callback, this);
|
|
|
|
this->run_every_runcb->remove(&Steam_Matchmaking::steam_matchmaking_run_every_runcb, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// game server favorites storage
|
|
|
|
// saves basic details about a multiplayer game server locally
|
|
|
|
|
|
|
|
// returns the number of favorites servers the user has stored
|
|
|
|
int Steam_Matchmaking::GetFavoriteGameCount()
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
std::string file_path = local_storage->get_current_save_directory() + "7" + PATH_SEPARATOR + Local_Storage::remote_storage_folder + PATH_SEPARATOR + "serverbrowser_favorites.txt";
|
|
|
|
unsigned long long file_size = file_size_(file_path);
|
|
|
|
if (file_size) {
|
|
|
|
std::string list{};
|
|
|
|
list.resize(file_size);
|
|
|
|
Local_Storage::get_file_data(file_path, (char *)&list[0], file_size, 0);
|
|
|
|
auto list_lines = std::count(list.begin(), list.end(), '\n');
|
|
|
|
list_lines += (!list.empty() && list.back() != '\n');
|
|
|
|
return list_lines;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns the details of the game server
|
|
|
|
// iGame is of range [0,GetFavoriteGameCount())
|
|
|
|
// *pnIP, *pnConnPort are filled in the with IP:port of the game server
|
|
|
|
// *punFlags specify whether the game server was stored as an explicit favorite or in the history of connections
|
|
|
|
// *pRTime32LastPlayedOnServer is filled in the with the Unix time the favorite was added
|
|
|
|
bool Steam_Matchmaking::GetFavoriteGame( int iGame, AppId_t *pnAppID, uint32 *pnIP, uint16 *pnConnPort, uint16 *pnQueryPort, uint32 *punFlags, uint32 *pRTime32LastPlayedOnServer )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// adds the game server to the local list; updates the time played of the server if it already exists in the list
|
|
|
|
int Steam_Matchmaking::AddFavoriteGame( AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQueryPort, uint32 unFlags, uint32 rTime32LastPlayedOnServer )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%u %u %hu %hu %u %u", nAppID, nIP, nConnPort, nQueryPort, unFlags, rTime32LastPlayedOnServer);
|
|
|
|
|
|
|
|
std::string file_path;
|
|
|
|
unsigned long long file_size;
|
|
|
|
|
|
|
|
if (unFlags == 1) {
|
|
|
|
file_path = local_storage->get_current_save_directory() + "7" + PATH_SEPARATOR + Local_Storage::remote_storage_folder + PATH_SEPARATOR + "serverbrowser_favorites.txt";
|
|
|
|
file_size = file_size_(file_path);
|
|
|
|
}
|
|
|
|
else if (unFlags == 2) {
|
|
|
|
file_path = local_storage->get_current_save_directory() + "7" + PATH_SEPARATOR + Local_Storage::remote_storage_folder + PATH_SEPARATOR + "serverbrowser_history.txt";
|
|
|
|
file_size = file_size_(file_path);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char ip[4];
|
|
|
|
ip[0] = nIP & 0xFF;
|
|
|
|
ip[1] = (nIP >> 8) & 0xFF;
|
|
|
|
ip[2] = (nIP >> 16) & 0xFF;
|
|
|
|
ip[3] = (nIP >> 24) & 0xFF;
|
|
|
|
char newip[24];
|
|
|
|
snprintf(newip, sizeof(newip), "%d.%d.%d.%d:%d\n", ip[3], ip[2], ip[1], ip[0], nConnPort);
|
|
|
|
std::string newip_string;
|
|
|
|
newip_string.append(newip);
|
|
|
|
|
|
|
|
if (file_size) {
|
|
|
|
std::string list{};
|
|
|
|
list.resize(file_size);
|
|
|
|
Local_Storage::get_file_data(file_path, (char *)&list[0], file_size, 0);
|
|
|
|
auto list_lines = std::count(list.begin(), list.end(), '\n');
|
|
|
|
list_lines += (!list.empty() && list.back() != '\n');
|
|
|
|
|
|
|
|
std::size_t find_ip = list.find(newip_string);
|
|
|
|
if (find_ip == std::string::npos) {
|
|
|
|
list.append(newip_string);
|
|
|
|
list.append("\n");
|
|
|
|
|
|
|
|
std::size_t file_directory = file_path.rfind("/");
|
|
|
|
std::string directory_path;
|
|
|
|
std::string file_name;
|
|
|
|
if (file_directory != std::string::npos) {
|
|
|
|
directory_path = file_path.substr(0, file_directory);
|
|
|
|
file_name = file_path.substr(file_directory);
|
|
|
|
}
|
|
|
|
Local_Storage::store_file_data(directory_path, file_name, (char *)list.data(), list.size());
|
|
|
|
|
|
|
|
return ++list_lines;
|
|
|
|
}
|
|
|
|
|
|
|
|
return list_lines;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
newip_string.append("\n");
|
|
|
|
|
|
|
|
std::size_t file_directory = file_path.rfind("/");
|
|
|
|
std::string directory_path;
|
|
|
|
std::string file_name;
|
|
|
|
if (file_directory != std::string::npos) {
|
|
|
|
directory_path = file_path.substr(0, file_directory);
|
|
|
|
file_name = file_path.substr(file_directory);
|
|
|
|
}
|
|
|
|
Local_Storage::store_file_data(directory_path, file_name, (char *)newip_string.data(), newip_string.size());
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// removes the game server from the local storage; returns true if one was removed
|
|
|
|
bool Steam_Matchmaking::RemoveFavoriteGame( AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQueryPort, uint32 unFlags )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
|
|
|
|
std::string file_path;
|
|
|
|
unsigned long long file_size;
|
|
|
|
|
|
|
|
if (unFlags == 1) {
|
|
|
|
file_path = local_storage->get_current_save_directory() + "7" + PATH_SEPARATOR + Local_Storage::remote_storage_folder + "serverbrowser_favorites.txt";
|
|
|
|
file_size = file_size_(file_path);
|
|
|
|
}
|
|
|
|
else if (unFlags == 2) {
|
|
|
|
file_path = local_storage->get_current_save_directory() + "7" + PATH_SEPARATOR + Local_Storage::remote_storage_folder + "serverbrowser_history.txt";
|
|
|
|
file_size = file_size_(file_path);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file_size) {
|
|
|
|
std::string list{};
|
|
|
|
list.resize(file_size);
|
|
|
|
Local_Storage::get_file_data(file_path, (char *)&list[0], file_size, 0);
|
|
|
|
|
|
|
|
unsigned char ip[4];
|
|
|
|
ip[0] = nIP & 0xFF;
|
|
|
|
ip[1] = (nIP >> 8) & 0xFF;
|
|
|
|
ip[2] = (nIP >> 16) & 0xFF;
|
|
|
|
ip[3] = (nIP >> 24) & 0xFF;
|
|
|
|
char newip[24];
|
|
|
|
snprintf((char *)newip, sizeof(newip), "%d.%d.%d.%d:%d\n", ip[3], ip[2], ip[1], ip[0], nConnPort);
|
|
|
|
std::string newip_string;
|
|
|
|
newip_string.append(newip);
|
|
|
|
|
|
|
|
std::size_t list_ip = list.find(newip_string);
|
|
|
|
if (list_ip != std::string::npos) {
|
|
|
|
list.erase(list_ip, newip_string.length());
|
|
|
|
|
|
|
|
std::size_t file_directory = file_path.rfind("/");
|
|
|
|
std::string directory_path;
|
|
|
|
std::string file_name;
|
|
|
|
if (file_directory != std::string::npos) {
|
|
|
|
directory_path = file_path.substr(0, file_directory);
|
|
|
|
file_name = file_path.substr(file_directory);
|
|
|
|
}
|
|
|
|
Local_Storage::store_file_data(directory_path, file_name, (char *)list.data(), list.size());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////
|
|
|
|
// Game lobby functions
|
|
|
|
|
|
|
|
// Get a list of relevant lobbies
|
|
|
|
// this is an asynchronous request
|
|
|
|
// results will be returned by LobbyMatchList_t callback & call result, with the number of lobbies found
|
|
|
|
// this will never return lobbies that are full
|
|
|
|
// to add more filter, the filter calls below need to be call before each and every RequestLobbyList() call
|
|
|
|
// use the CCallResult<> object in steam_api.h to match the SteamAPICall_t call result to a function in an object, e.g.
|
|
|
|
/*
|
|
|
|
class CMyLobbyListManager
|
|
|
|
{
|
|
|
|
CCallResult<CMyLobbyListManager, LobbyMatchList_t> m_CallResultLobbyMatchList;
|
|
|
|
void FindLobbies()
|
|
|
|
{
|
|
|
|
// SteamMatchmaking()->AddRequestLobbyListFilter*() functions would be called here, before RequestLobbyList();
|
|
|
|
|
|
|
|
m_CallResultLobbyMatchList.Set( hSteamAPICall, this, &CMyLobbyListManager::OnLobbyMatchList );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnLobbyMatchList( LobbyMatchList_t *pLobbyMatchList, bool bIOFailure )
|
|
|
|
{
|
|
|
|
// lobby list has be retrieved from Steam back-end, use results
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
//
|
|
|
|
STEAM_CALL_RESULT( LobbyMatchList_t )
|
|
|
|
SteamAPICall_t Steam_Matchmaking::RequestLobbyList()
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
|
|
|
|
filtered_lobbies.clear();
|
|
|
|
lobby_last_search = std::chrono::high_resolution_clock::now();
|
|
|
|
filter_values_copy = filter_values;
|
|
|
|
filter_max_results_copy = filter_max_results;
|
|
|
|
filter_values.clear();
|
|
|
|
filter_max_results = FILTER_MAX_DEFAULT;
|
|
|
|
searching = true;
|
|
|
|
if (search_call_api_id) callback_results->rmCallBack(search_call_api_id, NULL);
|
|
|
|
search_call_api_id = callback_results->reserveCallResult();
|
|
|
|
|
|
|
|
return search_call_api_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::RequestLobbyList_OLD()
|
|
|
|
{
|
|
|
|
RequestLobbyList();
|
|
|
|
}
|
|
|
|
|
|
|
|
// filters for lobbies
|
|
|
|
// this needs to be called before RequestLobbyList() to take effect
|
|
|
|
// these are cleared on each call to RequestLobbyList()
|
|
|
|
void Steam_Matchmaking::AddRequestLobbyListStringFilter( const char *pchKeyToMatch, const char *pchValueToMatch, ELobbyComparison eComparisonType )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("'%s'=='%s' %i", pchKeyToMatch, pchValueToMatch, eComparisonType);
|
|
|
|
if (!pchValueToMatch) return;
|
|
|
|
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
struct Filter_Values fv;
|
|
|
|
fv.key = std::string(pchKeyToMatch);
|
|
|
|
fv.value_string = std::string(pchValueToMatch);
|
|
|
|
fv.is_int = false;
|
|
|
|
fv.eComparisonType = eComparisonType;
|
|
|
|
filter_values.push_back(fv);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// numerical comparison
|
|
|
|
void Steam_Matchmaking::AddRequestLobbyListNumericalFilter( const char *pchKeyToMatch, int nValueToMatch, ELobbyComparison eComparisonType )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("'%s'==%i %i", pchKeyToMatch, nValueToMatch, eComparisonType);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
struct Filter_Values fv;
|
|
|
|
fv.key = std::string(pchKeyToMatch);
|
|
|
|
fv.value_int = nValueToMatch;
|
|
|
|
fv.is_int = true;
|
|
|
|
fv.eComparisonType = eComparisonType;
|
|
|
|
filter_values.push_back(fv);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns results closest to the specified value. Multiple near filters can be added, with early filters taking precedence
|
|
|
|
void Steam_Matchmaking::AddRequestLobbyListNearValueFilter( const char *pchKeyToMatch, int nValueToBeCloseTo )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("'%s'==%u", pchKeyToMatch, nValueToBeCloseTo);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns only lobbies with the specified number of slots available
|
|
|
|
void Steam_Matchmaking::AddRequestLobbyListFilterSlotsAvailable( int nSlotsAvailable )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%i", nSlotsAvailable);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// sets the distance for which we should search for lobbies (based on users IP address to location map on the Steam backed)
|
|
|
|
void Steam_Matchmaking::AddRequestLobbyListDistanceFilter( ELobbyDistanceFilter eLobbyDistanceFilter )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%i", eLobbyDistanceFilter);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// sets how many results to return, the lower the count the faster it is to download the lobby results & details to the client
|
|
|
|
void Steam_Matchmaking::AddRequestLobbyListResultCountFilter( int cMaxResults )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%i", cMaxResults);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
filter_max_results = cMaxResults;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Steam_Matchmaking::AddRequestLobbyListCompatibleMembersFilter( CSteamID steamIDLobby )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::AddRequestLobbyListFilter( const char *pchKeyToMatch, const char *pchValueToMatch )
|
|
|
|
{
|
|
|
|
AddRequestLobbyListStringFilter(pchKeyToMatch, pchValueToMatch, k_ELobbyComparisonEqual);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::AddRequestLobbyListNumericalFilter( const char *pchKeyToMatch, int nValueToMatch, int nComparisonType )
|
|
|
|
{
|
|
|
|
AddRequestLobbyListNumericalFilter(pchKeyToMatch, nValueToMatch, (ELobbyComparison) nComparisonType );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::AddRequestLobbyListSlotsAvailableFilter()
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_TODO();
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns the CSteamID of a lobby, as retrieved by a RequestLobbyList call
|
|
|
|
// should only be called after a LobbyMatchList_t callback is received
|
|
|
|
// iLobby is of the range [0, LobbyMatchList_t::m_nLobbiesMatching)
|
|
|
|
// the returned CSteamID::IsValid() will be false if iLobby is out of range
|
|
|
|
CSteamID Steam_Matchmaking::GetLobbyByIndex( int iLobby )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%i", iLobby);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
CSteamID id = k_steamIDNil;
|
|
|
|
if (0 <= iLobby && iLobby < filtered_lobbies.size()) id = filtered_lobbies[iLobby];
|
|
|
|
PRINT_DEBUG("found lobby %llu", id.ConvertToUint64());
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a lobby on the Steam servers.
|
|
|
|
// If private, then the lobby will not be returned by any RequestLobbyList() call; the CSteamID
|
|
|
|
// of the lobby will need to be communicated via game channels or via InviteUserToLobby()
|
|
|
|
// this is an asynchronous request
|
|
|
|
// results will be returned by LobbyCreated_t callback and call result; lobby is joined & ready to use at this point
|
|
|
|
// a LobbyEnter_t callback will also be received (since the local user is joining their own lobby)
|
|
|
|
STEAM_CALL_RESULT( LobbyCreated_t )
|
|
|
|
SteamAPICall_t Steam_Matchmaking::CreateLobby( ELobbyType eLobbyType, int cMaxMembers )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("type: %i max_members: %i", eLobbyType, cMaxMembers);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
struct Pending_Creates p_c{};
|
|
|
|
p_c.api_id = callback_results->reserveCallResult();
|
|
|
|
p_c.eLobbyType = eLobbyType;
|
|
|
|
p_c.cMaxMembers = cMaxMembers;
|
|
|
|
p_c.created = std::chrono::high_resolution_clock::now();
|
|
|
|
pending_creates.push_back(p_c);
|
|
|
|
return p_c.api_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
SteamAPICall_t Steam_Matchmaking::CreateLobby( ELobbyType eLobbyType )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("old");
|
|
|
|
return CreateLobby(eLobbyType, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::CreateLobby_OLD( ELobbyType eLobbyType )
|
|
|
|
{
|
|
|
|
CreateLobby(eLobbyType);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::CreateLobby( bool bPrivate )
|
|
|
|
{
|
|
|
|
CreateLobby(bPrivate ? k_ELobbyTypePrivate : k_ELobbyTypePublic);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Joins an existing lobby
|
|
|
|
// this is an asynchronous request
|
|
|
|
// results will be returned by LobbyEnter_t callback & call result, check m_EChatRoomEnterResponse to see if was successful
|
|
|
|
// lobby metadata is available to use immediately on this call completing
|
|
|
|
STEAM_CALL_RESULT( LobbyEnter_t )
|
|
|
|
SteamAPICall_t Steam_Matchmaking::JoinLobby( CSteamID steamIDLobby )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu", steamIDLobby.ConvertToUint64());
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
|
|
|
|
auto pj = std::find_if(pending_joins.begin(), pending_joins.end(), [&steamIDLobby](Pending_Joins const& item) {return item.lobby_id == steamIDLobby;});
|
|
|
|
if (pj != pending_joins.end()) {
|
|
|
|
PRINT_DEBUG("already found in pending joins list");
|
|
|
|
return pj->api_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
Pending_Joins pending_join{};
|
|
|
|
pending_join.api_id = callback_results->reserveCallResult();
|
|
|
|
pending_join.lobby_id = steamIDLobby;
|
|
|
|
pending_join.joined = std::chrono::high_resolution_clock::now();
|
|
|
|
pending_joins.push_back(pending_join);
|
|
|
|
|
|
|
|
Lobby_Messages *message = new Lobby_Messages();
|
|
|
|
message->set_type(Lobby_Messages::JOIN);
|
|
|
|
pending_join.message_sent = send_owner_packet(steamIDLobby, message);
|
|
|
|
|
|
|
|
PRINT_DEBUG("added new entry to pending joins");
|
|
|
|
return pending_join.api_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::JoinLobby_OLD( CSteamID steamIDLobby )
|
|
|
|
{
|
|
|
|
JoinLobby(steamIDLobby);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Leave a lobby; this will take effect immediately on the client side
|
|
|
|
// other users in the lobby will be notified by a LobbyChatUpdate_t callback
|
|
|
|
void Steam_Matchmaking::LeaveLobby( CSteamID steamIDLobby )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
PRINT_DEBUG("pass mutex");
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (lobby) {
|
|
|
|
if (!lobby->deleted()) {
|
|
|
|
on_self_enter_leave_lobby((uint64)lobby->room_id(), lobby->type(), true);
|
|
|
|
self_lobby_member_data.erase(lobby->room_id());
|
|
|
|
if (lobby->owner() != settings->get_local_steam_id().ConvertToUint64()) {
|
|
|
|
PRINT_DEBUG("not owner");
|
|
|
|
leave_lobby(&(*lobby), settings->get_local_steam_id());
|
|
|
|
Lobby_Messages *message = new Lobby_Messages();
|
|
|
|
message->set_type(Lobby_Messages::LEAVE);
|
|
|
|
send_owner_packet(steamIDLobby, message);
|
|
|
|
} else {
|
|
|
|
PRINT_DEBUG("owner");
|
|
|
|
Lobby_Messages *message = new Lobby_Messages();
|
|
|
|
message->set_type(Lobby_Messages::LEAVE);
|
|
|
|
|
|
|
|
if (lobby->members().size() > 1) {
|
|
|
|
leave_lobby(&(*lobby), settings->get_local_steam_id());
|
|
|
|
change_owner(&(*lobby), (uint64)lobby->members(0).id());
|
|
|
|
send_owner_packet(steamIDLobby, message);
|
|
|
|
} else {
|
|
|
|
send_clients_packet(steamIDLobby, message);
|
|
|
|
lobby->set_deleted(true);
|
|
|
|
lobby->set_time_deleted(std::chrono::duration_cast<std::chrono::duration<uint64>>(std::chrono::system_clock::now().time_since_epoch()).count());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRINT_DEBUG("Done");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Invite another user to the lobby
|
|
|
|
// the target user will receive a LobbyInvite_t callback
|
|
|
|
// will return true if the invite is successfully sent, whether or not the target responds
|
|
|
|
// returns false if the local user is not connected to the Steam servers
|
|
|
|
// if the other user clicks the join link, a GameLobbyJoinRequested_t will be posted if the user is in-game,
|
|
|
|
// or if the game isn't running yet the game will be launched with the parameter +connect_lobby <64-bit lobby id>
|
|
|
|
bool Steam_Matchmaking::InviteUserToLobby( CSteamID steamIDLobby, CSteamID steamIDInvitee )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby) return false;
|
|
|
|
|
|
|
|
Common_Message msg;
|
|
|
|
Friend_Messages *friend_messages = new Friend_Messages();
|
|
|
|
friend_messages->set_type(Friend_Messages::LOBBY_INVITE);
|
|
|
|
friend_messages->set_lobby_id(steamIDLobby.ConvertToUint64());
|
|
|
|
msg.set_allocated_friend_messages(friend_messages);
|
|
|
|
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
|
|
|
msg.set_dest_id(steamIDInvitee.ConvertToUint64());
|
|
|
|
return network->sendTo(&msg, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Lobby iteration, for viewing details of users in a lobby
|
|
|
|
// only accessible if the lobby user is a member of the specified lobby
|
|
|
|
// persona information for other lobby members (name, avatar, etc.) will be asynchronously received
|
|
|
|
// and accessible via ISteamFriends interface
|
|
|
|
|
|
|
|
// returns the number of users in the specified lobby
|
|
|
|
int Steam_Matchmaking::GetNumLobbyMembers( CSteamID steamIDLobby )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu", steamIDLobby.ConvertToUint64());
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
int ret = 0;
|
|
|
|
if (lobby) ret = lobby->members().size();
|
|
|
|
|
|
|
|
PRINT_DEBUG("count=%i", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns the CSteamID of a user in the lobby
|
|
|
|
// iMember is of range [0,GetNumLobbyMembers())
|
|
|
|
// note that the current user must be in a lobby to retrieve CSteamIDs of other users in that lobby
|
|
|
|
CSteamID Steam_Matchmaking::GetLobbyMemberByIndex( CSteamID steamIDLobby, int iMember )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu %i", steamIDLobby.ConvertToUint64(), iMember);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
CSteamID id = k_steamIDNil;
|
|
|
|
if (lobby && !lobby->deleted() && lobby->members().size() > iMember && iMember >= 0) id = (uint64)lobby->members(iMember).id();
|
|
|
|
PRINT_DEBUG("found member: %llu", id.ConvertToUint64());
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get data associated with this lobby
|
|
|
|
// takes a simple key, and returns the string associated with it
|
|
|
|
// "" will be returned if no value is set, or if steamIDLobby is invalid
|
|
|
|
const char* Steam_Matchmaking::GetLobbyData( CSteamID steamIDLobby, const char *pchKey )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu '%s'", steamIDLobby.ConvertToUint64(), pchKey);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
if (!pchKey) return "";
|
|
|
|
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
const char *ret = "";
|
|
|
|
if (lobby) {
|
|
|
|
auto result = caseinsensitive_find(lobby->values(), pchKey);
|
|
|
|
if (result != lobby->values().end()) ret = result->second.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
PRINT_DEBUG("returned '%s'", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sets a key/value pair in the lobby metadata
|
|
|
|
// each user in the lobby will be broadcast this new value, and any new users joining will receive any existing data
|
|
|
|
// this can be used to set lobby names, map, etc.
|
|
|
|
// to reset a key, just set it to ""
|
|
|
|
// other users in the lobby will receive notification of the lobby data change via a LobbyDataUpdate_t callback
|
|
|
|
bool Steam_Matchmaking::SetLobbyData( CSteamID steamIDLobby, const char *pchKey, const char *pchValue )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("[%llu] '%s'='%s'", steamIDLobby.ConvertToUint64(), pchKey, pchValue);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
if (!pchKey) return false;
|
|
|
|
if (!pchValue) pchValue = "";
|
|
|
|
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby || lobby->deleted()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool changed = true;
|
|
|
|
//callback is always triggered when setlobbydata is called from non owner however no data is actually changed.
|
|
|
|
if (lobby->owner() == settings->get_local_steam_id().ConvertToUint64()) {
|
|
|
|
auto result = caseinsensitive_find(lobby->values(), pchKey);
|
|
|
|
if (result == lobby->values().end()) {
|
|
|
|
(*lobby->mutable_values())[pchKey] = pchValue;
|
|
|
|
} else {
|
|
|
|
if (result->second == std::string(pchValue)) changed = false;
|
|
|
|
(*lobby->mutable_values())[result->first] = pchValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (changed)
|
|
|
|
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true, 0.005, changed);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns the number of metadata keys set on the specified lobby
|
|
|
|
int Steam_Matchmaking::GetLobbyDataCount( CSteamID steamIDLobby )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu", steamIDLobby.ConvertToUint64());
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
int size = 0;
|
|
|
|
if (lobby) size = lobby->values().size();
|
|
|
|
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns a lobby metadata key/values pair by index, of range [0, GetLobbyDataCount())
|
|
|
|
bool Steam_Matchmaking::GetLobbyDataByIndex( CSteamID steamIDLobby, int iLobbyData, char *pchKey, int cchKeyBufferSize, char *pchValue, int cchValueBufferSize )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu [%i] key size=%i, value size=%i", steamIDLobby.ConvertToUint64(), iLobbyData, cchKeyBufferSize, cchValueBufferSize);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
if (lobby && lobby->values().size() > iLobbyData && iLobbyData >= 0) {
|
|
|
|
auto lobby_data = lobby->values().begin();
|
|
|
|
for (int i = 0; i < iLobbyData; ++i) ++lobby_data;
|
|
|
|
if (pchKey && cchKeyBufferSize > 0) {
|
|
|
|
strncpy(pchKey, lobby_data->first.c_str(), cchKeyBufferSize - 1);
|
|
|
|
pchKey[cchKeyBufferSize - 1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pchValue && cchValueBufferSize > 0) {
|
|
|
|
strncpy(pchValue, lobby_data->second.c_str(), cchValueBufferSize - 1);
|
|
|
|
pchValue[cchValueBufferSize - 1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRINT_DEBUG("ret '%s'='%s'", pchKey, pchValue);
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// removes a metadata key from the lobby
|
|
|
|
bool Steam_Matchmaking::DeleteLobbyData( CSteamID steamIDLobby, const char *pchKey )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("'%s'", pchKey);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby || lobby->owner() != settings->get_local_steam_id().ConvertToUint64() || lobby->deleted()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
lobby->mutable_values()->erase(pchKey);
|
|
|
|
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Gets per-user metadata for someone in this lobby
|
|
|
|
const char* Steam_Matchmaking::GetLobbyMemberData( CSteamID steamIDLobby, CSteamID steamIDUser, const char *pchKey )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("'%s' %llu %llu", pchKey, steamIDLobby.ConvertToUint64(), steamIDUser.ConvertToUint64());
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
if (!pchKey) return "";
|
|
|
|
|
|
|
|
struct Lobby_Member *member = get_lobby_member(get_lobby(steamIDLobby), steamIDUser);
|
|
|
|
const char *ret = "";
|
|
|
|
if (member) {
|
|
|
|
if (steamIDUser == settings->get_local_steam_id()) {
|
|
|
|
auto result = self_lobby_member_data.find(steamIDLobby.ConvertToUint64());
|
|
|
|
if (result != self_lobby_member_data.end()) {
|
|
|
|
auto value = caseinsensitive_find(result->second, std::string(pchKey));
|
|
|
|
if (value != result->second.end()) {
|
|
|
|
ret = value->second.c_str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
auto result = caseinsensitive_find(member->values(), std::string(pchKey));
|
|
|
|
if (result == member->values().end()) return "";
|
|
|
|
ret = result->second.c_str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRINT_DEBUG("res '%s'", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sets per-user metadata (for the local user implicitly)
|
|
|
|
void Steam_Matchmaking::SetLobbyMemberData( CSteamID steamIDLobby, const char *pchKey, const char *pchValue )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu '%s'='%s'", steamIDLobby.ConvertToUint64(), pchKey, pchValue);
|
|
|
|
if (!pchKey) return;
|
|
|
|
char empty_string[] = "";
|
|
|
|
if (!pchValue) pchValue = empty_string;
|
|
|
|
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby || lobby->deleted()) return;
|
|
|
|
|
|
|
|
Lobby_Member *member = get_lobby_member(lobby, settings->get_local_steam_id());
|
|
|
|
if (member) {
|
|
|
|
if (lobby->owner() == settings->get_local_steam_id().ConvertToUint64()) {
|
|
|
|
auto result = caseinsensitive_find(member->values(), std::string(pchKey));
|
|
|
|
if (result == member->values().end()) {
|
|
|
|
(*member->mutable_values())[pchKey] = pchValue;
|
|
|
|
} else {
|
|
|
|
(*member->mutable_values())[result->first] = pchValue;
|
|
|
|
}
|
|
|
|
trigger_lobby_dataupdate(steamIDLobby, (uint64)member->id(), true);
|
|
|
|
} else {
|
|
|
|
Lobby_Messages *message = new Lobby_Messages();
|
|
|
|
message->set_type(Lobby_Messages::MEMBER_DATA);
|
|
|
|
(*message->mutable_map())[pchKey] = pchValue;
|
|
|
|
send_owner_packet(steamIDLobby, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
auto result = self_lobby_member_data.find(steamIDLobby.ConvertToUint64());
|
|
|
|
if (result != self_lobby_member_data.end()) {
|
|
|
|
auto value = caseinsensitive_find(result->second, std::string(pchKey));
|
|
|
|
if (value != result->second.end()) {
|
|
|
|
self_lobby_member_data[steamIDLobby.ConvertToUint64()][value->first] = pchValue;
|
|
|
|
} else {
|
|
|
|
self_lobby_member_data[steamIDLobby.ConvertToUint64()][pchKey] = pchValue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self_lobby_member_data[steamIDLobby.ConvertToUint64()][pchKey] = pchValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Broadcasts a chat message to the all the users in the lobby
|
|
|
|
// users in the lobby (including the local user) will receive a LobbyChatMsg_t callback
|
|
|
|
// returns true if the message is successfully sent
|
|
|
|
// pvMsgBody can be binary or text data, up to 4k
|
|
|
|
// if pvMsgBody is text, cubMsgBody should be strlen( text ) + 1, to include the null terminator
|
|
|
|
bool Steam_Matchmaking::SendLobbyChatMsg( CSteamID steamIDLobby, const void *pvMsgBody, int cubMsgBody )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu %i", steamIDLobby.ConvertToUint64(), cubMsgBody);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby || lobby->deleted()) return false;
|
|
|
|
|
|
|
|
Lobby_Messages *message = new Lobby_Messages();
|
|
|
|
message->set_type(Lobby_Messages::CHAT_MESSAGE);
|
|
|
|
message->set_bdata(pvMsgBody, cubMsgBody);
|
|
|
|
return send_lobby_members_packet(steamIDLobby, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get a chat message as specified in a LobbyChatMsg_t callback
|
|
|
|
// iChatID is the LobbyChatMsg_t::m_iChatID value in the callback
|
|
|
|
// *pSteamIDUser is filled in with the CSteamID of the member
|
|
|
|
// *pvData is filled in with the message itself
|
|
|
|
// return value is the number of bytes written into the buffer
|
|
|
|
int Steam_Matchmaking::GetLobbyChatEntry( CSteamID steamIDLobby, int iChatID, STEAM_OUT_STRUCT() CSteamID *pSteamIDUser, void *pvData, int cubData, EChatEntryType *peChatEntryType )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu %i %p %p %i %p", steamIDLobby.ConvertToUint64(), iChatID, pSteamIDUser, pvData, cubData, peChatEntryType);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
if (iChatID >= chat_entries.size() || iChatID < 0 || cubData < 0) return 0;
|
|
|
|
if (chat_entries[iChatID].lobby_id != steamIDLobby) return 0;
|
|
|
|
if (pSteamIDUser) *pSteamIDUser = chat_entries[iChatID].user_id;
|
|
|
|
if (peChatEntryType) *peChatEntryType = chat_entries[iChatID].type;
|
|
|
|
if (pvData) {
|
|
|
|
if (chat_entries[iChatID].message.size() <= cubData) {
|
|
|
|
cubData = chat_entries[iChatID].message.size();
|
|
|
|
memcpy(pvData, chat_entries[iChatID].message.data(), cubData);
|
|
|
|
PRINT_DEBUG(" Returned chat of len: %i", cubData);
|
|
|
|
return cubData;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Refreshes metadata for a lobby you're not necessarily in right now
|
|
|
|
// you never do this for lobbies you're a member of, only if your
|
|
|
|
// this will send down all the metadata associated with a lobby
|
|
|
|
// this is an asynchronous call
|
|
|
|
// returns false if the local user is not connected to the Steam servers
|
|
|
|
// results will be returned by a LobbyDataUpdate_t callback
|
|
|
|
// if the specified lobby doesn't exist, LobbyDataUpdate_t::m_bSuccess will be set to false
|
|
|
|
bool Steam_Matchmaking::RequestLobbyData( CSteamID steamIDLobby )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu", steamIDLobby.ConvertToUint64());
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
|
|
|
|
struct Data_Requested requested{};
|
|
|
|
requested.lobby_id = steamIDLobby;
|
|
|
|
requested.requested = std::chrono::high_resolution_clock::now();
|
|
|
|
data_requested.push_back(requested);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sets the game server associated with the lobby
|
|
|
|
// usually at this point, the users will join the specified game server
|
|
|
|
// either the IP/Port or the steamID of the game server has to be valid, depending on how you want the clients to be able to connect
|
|
|
|
void Steam_Matchmaking::SetLobbyGameServer( CSteamID steamIDLobby, uint32 unGameServerIP, uint16 unGameServerPort, CSteamID steamIDGameServer )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu %llu %hhu.%hhu.%hhu.%hhu:%hu",
|
|
|
|
steamIDLobby.ConvertToUint64(), steamIDGameServer.ConvertToUint64(), ((unsigned char *)&unGameServerIP)[3], ((unsigned char *)&unGameServerIP)[2], ((unsigned char *)&unGameServerIP)[1], ((unsigned char *)&unGameServerIP)[0], unGameServerPort
|
|
|
|
);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (lobby) {
|
|
|
|
if (lobby->deleted()) return;
|
|
|
|
|
|
|
|
lobby->mutable_gameserver()->set_id(steamIDGameServer.ConvertToUint64());
|
|
|
|
lobby->mutable_gameserver()->set_ip(unGameServerIP);
|
|
|
|
lobby->mutable_gameserver()->set_port(unGameServerPort);
|
|
|
|
lobby->mutable_gameserver()->set_num_update(lobby->gameserver().num_update() + 1);
|
|
|
|
|
|
|
|
send_gameservercreated_cb(lobby->room_id(), lobby->gameserver().id(), lobby->gameserver().ip(), lobby->gameserver().port());
|
|
|
|
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns the details of a game server set in a lobby - returns false if there is no game server set, or that lobby doesn't exist
|
|
|
|
bool Steam_Matchmaking::GetLobbyGameServer( CSteamID steamIDLobby, uint32 *punGameServerIP, uint16 *punGameServerPort, STEAM_OUT_STRUCT() CSteamID *psteamIDGameServer )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CSteamID server_id((uint64)lobby->gameserver().id());
|
|
|
|
if (server_id.IsValid() || lobby->gameserver().port()) {
|
|
|
|
if (psteamIDGameServer) *psteamIDGameServer = server_id;
|
|
|
|
if (punGameServerIP) *punGameServerIP = lobby->gameserver().ip();
|
|
|
|
if (punGameServerPort) *punGameServerPort = lobby->gameserver().port();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// set the limit on the # of users who can join the lobby
|
|
|
|
bool Steam_Matchmaking::SetLobbyMemberLimit( CSteamID steamIDLobby, int cMaxMembers )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu %i", steamIDLobby.ConvertToUint64(), cMaxMembers);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby || lobby->owner() != settings->get_local_steam_id().ConvertToUint64() || lobby->deleted()) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
lobby->set_member_limit(cMaxMembers);
|
|
|
|
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns the current limit on the # of users who can join the lobby; returns 0 if no limit is defined
|
|
|
|
int Steam_Matchmaking::GetLobbyMemberLimit( CSteamID steamIDLobby )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu", steamIDLobby.ConvertToUint64());
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
int limit = 0;
|
|
|
|
if (lobby) limit = lobby->member_limit();
|
|
|
|
|
|
|
|
PRINT_DEBUG(" limit %i", limit);
|
|
|
|
return limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::SetLobbyVoiceEnabled( CSteamID steamIDLobby, bool bVoiceEnabled )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
}
|
|
|
|
|
|
|
|
// updates which type of lobby it is
|
|
|
|
// only lobbies that are k_ELobbyTypePublic or k_ELobbyTypeInvisible, and are set to joinable, will be returned by RequestLobbyList() calls
|
|
|
|
bool Steam_Matchmaking::SetLobbyType( CSteamID steamIDLobby, ELobbyType eLobbyType )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%i", eLobbyType);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby || lobby->owner() != settings->get_local_steam_id().ConvertToUint64() || lobby->deleted()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lobby->type() != eLobbyType) {
|
|
|
|
//maybe rename those functions?
|
|
|
|
if (lobby->type() == k_ELobbyTypeInvisible) on_self_enter_leave_lobby(steamIDLobby, eLobbyType, false);
|
|
|
|
if (eLobbyType == k_ELobbyTypeInvisible) on_self_enter_leave_lobby(steamIDLobby, lobby->type(), true);
|
|
|
|
|
|
|
|
lobby->set_type(eLobbyType);
|
|
|
|
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// sets whether or not a lobby is joinable - defaults to true for a new lobby
|
|
|
|
// if set to false, no user can join, even if they are a friend or have been invited
|
|
|
|
bool Steam_Matchmaking::SetLobbyJoinable( CSteamID steamIDLobby, bool bLobbyJoinable )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%u", bLobbyJoinable);
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby || lobby->owner() != settings->get_local_steam_id().ConvertToUint64() || lobby->deleted()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lobby->joinable() != bLobbyJoinable) {
|
|
|
|
lobby->set_joinable(bLobbyJoinable);
|
|
|
|
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns the current lobby owner
|
|
|
|
// you must be a member of the lobby to access this (Mr_Goldberg note: This is a lie)
|
|
|
|
// there always one lobby owner - if the current owner leaves, another user will become the owner
|
|
|
|
// it is possible (bur rare) to join a lobby just as the owner is leaving, thus entering a lobby with self as the owner
|
|
|
|
CSteamID Steam_Matchmaking::GetLobbyOwner( CSteamID steamIDLobby )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu", steamIDLobby.ConvertToUint64());
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby || lobby->deleted()) return k_steamIDNil;
|
|
|
|
|
|
|
|
//TODO: might be better to require the lobby info to be at least requested first.
|
|
|
|
return (uint64)lobby->owner();
|
|
|
|
}
|
|
|
|
|
|
|
|
// asks the Steam servers for a list of lobbies that friends are in
|
|
|
|
// returns results by posting one RequestFriendsLobbiesResponse_t callback per friend/lobby pair
|
|
|
|
// if no friends are in lobbies, RequestFriendsLobbiesResponse_t will be posted but with 0 results
|
|
|
|
// filters don't apply to lobbies (currently)
|
|
|
|
bool Steam_Matchmaking::RequestFriendsLobbies()
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
RequestFriendsLobbiesResponse_t data = {};
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
float Steam_Matchmaking::GetLobbyDistance( CSteamID steamIDLobby )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG("%llu", steamIDLobby.ConvertToUint64());
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// changes who the lobby owner is
|
|
|
|
// you must be the lobby owner for this to succeed, and steamIDNewOwner must be in the lobby
|
|
|
|
// after completion, the local user will no longer be the owner
|
|
|
|
bool Steam_Matchmaking::SetLobbyOwner( CSteamID steamIDLobby, CSteamID steamIDNewOwner )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
Lobby *lobby = get_lobby(steamIDLobby);
|
|
|
|
if (!lobby || lobby->owner() != settings->get_local_steam_id().ConvertToUint64() || lobby->deleted()) return false;
|
|
|
|
Lobby_Member *member = get_lobby_member(lobby, steamIDNewOwner);
|
|
|
|
if (member) {
|
|
|
|
change_owner(&(*lobby), (uint64)member->id());
|
|
|
|
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// link two lobbies for the purposes of checking player compatibility
|
|
|
|
// you must be the lobby owner of both lobbies
|
|
|
|
bool Steam_Matchmaking::SetLinkedLobby( CSteamID steamIDLobby, CSteamID steamIDLobbyDependent )
|
|
|
|
{
|
|
|
|
PRINT_DEBUG_ENTRY();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Steam_Matchmaking::remove_lobbies()
|
|
|
|
{
|
|
|
|
uint64 current_time = std::chrono::duration_cast<std::chrono::duration<uint64>>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
|
|
auto g = std::begin(lobbies);
|
|
|
|
while (g != std::end(lobbies)) {
|
|
|
|
if (g->members().size() == 0 || (g->deleted() && (g->time_deleted() + LOBBY_DELETED_TIMEOUT < current_time))) {
|
|
|
|
PRINT_DEBUG("LOBBY " "%" PRIu64 "", g->room_id());
|
|
|
|
self_lobby_member_data.erase(g->room_id());
|
|
|
|
g = lobbies.erase(g);
|
|
|
|
} else {
|
|
|
|
++g;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::create_pending_lobbies()
|
|
|
|
{
|
|
|
|
auto p_c = std::begin(pending_creates);
|
|
|
|
while (p_c != std::end(pending_creates)) {
|
|
|
|
if (check_timedout(p_c->created, LOBBY_CREATE_DELAY)) {
|
|
|
|
Lobby lobby{};
|
|
|
|
CSteamID lobby_id = generate_steam_id_lobby();
|
|
|
|
lobby.set_room_id(lobby_id.ConvertToUint64());
|
|
|
|
lobby.set_joinable(true);
|
|
|
|
lobby.set_member_limit(p_c->cMaxMembers);
|
|
|
|
lobby.set_type(p_c->eLobbyType);
|
|
|
|
lobby.set_owner(settings->get_local_steam_id().ConvertToUint64());
|
|
|
|
lobby.set_appid(settings->get_local_game_id().AppID());
|
|
|
|
add_member_to_lobby(&lobby, settings->get_local_steam_id());
|
|
|
|
lobbies.push_back(lobby);
|
|
|
|
|
|
|
|
if (settings->disable_lobby_creation) {
|
|
|
|
LobbyCreated_t data;
|
|
|
|
data.m_eResult = k_EResultFail;
|
|
|
|
data.m_ulSteamIDLobby = 0;
|
|
|
|
callback_results->addCallResult(p_c->api_id, data.k_iCallback, &data, sizeof(data));
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
} else {
|
|
|
|
LobbyCreated_t data;
|
|
|
|
data.m_eResult = k_EResultOK;
|
|
|
|
data.m_ulSteamIDLobby = lobby.room_id();
|
|
|
|
callback_results->addCallResult(p_c->api_id, data.k_iCallback, &data, sizeof(data));
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
|
|
|
|
{
|
|
|
|
LobbyEnter_t data2{};
|
|
|
|
data2.m_ulSteamIDLobby = lobby.room_id();
|
|
|
|
data2.m_rgfChatPermissions = 0; //Unused - Always 0
|
|
|
|
if (p_c->eLobbyType == k_ELobbyTypePrivate)
|
|
|
|
data2.m_bLocked = true;
|
|
|
|
else
|
|
|
|
data2.m_bLocked = false;
|
|
|
|
data2.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess;
|
|
|
|
callbacks->addCBResult(data2.k_iCallback, &data2, sizeof(data2));
|
|
|
|
}
|
|
|
|
|
|
|
|
on_self_enter_leave_lobby(lobby_id, p_c->eLobbyType, false);
|
|
|
|
trigger_lobby_dataupdate(lobby_id, lobby_id, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
p_c = pending_creates.erase(p_c);
|
|
|
|
} else {
|
|
|
|
++p_c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::run_background()
|
|
|
|
{
|
|
|
|
remove_lobbies();
|
|
|
|
create_pending_lobbies();
|
|
|
|
|
|
|
|
if (check_timedout(last_sent_lobbies, SEND_LOBBY_RATE)) {
|
|
|
|
send_lobby_data();
|
|
|
|
last_sent_lobbies = std::chrono::high_resolution_clock::now();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Steam_Matchmaking::RunCallbacks()
|
|
|
|
{
|
|
|
|
run_background();
|
|
|
|
|
|
|
|
if (searching) {
|
|
|
|
PRINT_DEBUG("for lobbies %zu", lobbies.size());
|
|
|
|
for(auto & l: lobbies) {
|
|
|
|
bool use = l.joinable() && (l.type() == k_ELobbyTypePublic || l.type() == k_ELobbyTypeInvisible || l.type() == k_ELobbyTypeFriendsOnly) && !l.deleted();
|
|
|
|
PRINT_DEBUG("use lobby: %u, filters: %zu, joinable: %u, type: %u, deleted: %u", use, filter_values_copy.size(), l.joinable(), l.type(), l.deleted());
|
|
|
|
for (auto & f : filter_values_copy) {
|
|
|
|
PRINT_DEBUG("'%s':'%s'/%i %u %i", f.key.c_str(), f.value_string.c_str(), f.value_int, f.is_int, f.eComparisonType);
|
|
|
|
auto value = caseinsensitive_find(l.values(), f.key);
|
|
|
|
if (value != l.values().end()) {
|
|
|
|
//TODO: eComparisonType
|
|
|
|
if (!f.is_int) {
|
|
|
|
PRINT_DEBUG("Compare Values %s %s", value->second.c_str(), f.value_string.c_str());
|
|
|
|
if (f.eComparisonType == k_ELobbyComparisonEqual) {
|
|
|
|
if (value->second == f.value_string) {
|
|
|
|
PRINT_DEBUG("Equal (non-int)");
|
|
|
|
//use = use;
|
|
|
|
} else {
|
|
|
|
PRINT_DEBUG("Not Equal (non-int)");
|
|
|
|
use = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PRINT_DEBUG("TODO UNSUPPORTED compare type (non-int) %i", (int)f.eComparisonType);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
PRINT_DEBUG("%s", value->second.c_str());
|
|
|
|
int compare_to = 0;
|
|
|
|
//TODO: check if this is how real steam behaves
|
|
|
|
if (value->second.size()) {
|
|
|
|
compare_to = std::stoll(value->second, 0, 0);
|
|
|
|
}
|
|
|
|
PRINT_DEBUG("Compare Values %i %i", compare_to, f.value_int);
|
|
|
|
if (f.eComparisonType == k_ELobbyComparisonEqual) {
|
|
|
|
if (compare_to == f.value_int) {
|
|
|
|
PRINT_DEBUG("Equal (int)");
|
|
|
|
//use = use;
|
|
|
|
} else {
|
|
|
|
PRINT_DEBUG("Not Equal (int)");
|
|
|
|
use = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PRINT_DEBUG("TODO UNSUPPORTED compare type (int) %i", (int)f.eComparisonType);
|
|
|
|
}
|
|
|
|
} catch (...) {
|
|
|
|
//Same case as if the key is not in the lobby?
|
|
|
|
use = false;
|
|
|
|
}
|
|
|
|
//TODO: add more comparisons
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PRINT_DEBUG("Compare Key not in lobby");
|
|
|
|
if (f.eComparisonType == k_ELobbyComparisonEqual) {
|
|
|
|
//If the key is not in the lobby do we take it into account?
|
|
|
|
use = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRINT_DEBUG("Lobby " "%" PRIu64 " use %u", l.room_id(), use);
|
|
|
|
if (use) PUSH_BACK_IF_NOT_IN(filtered_lobbies, (uint64)l.room_id());
|
|
|
|
if (filtered_lobbies.size() >= filter_max_results_copy) {
|
|
|
|
PRINT_DEBUG("returning lobby search results, count=%zu", filtered_lobbies.size());
|
|
|
|
searching = false;
|
|
|
|
LobbyMatchList_t data{};
|
|
|
|
data.m_nLobbiesMatching = filtered_lobbies.size();
|
|
|
|
callback_results->addCallResult(search_call_api_id, data.k_iCallback, &data, sizeof(data));
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
search_call_api_id = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (searching && check_timedout(lobby_last_search, LOBBY_SEARCH_TIMEOUT)) {
|
|
|
|
PRINT_DEBUG("LOBBY_SEARCH_TIMEOUT %zu", filtered_lobbies.size());
|
|
|
|
LobbyMatchList_t data{};
|
|
|
|
data.m_nLobbiesMatching = filtered_lobbies.size();
|
|
|
|
callback_results->addCallResult(search_call_api_id, data.k_iCallback, &data, sizeof(data));
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
searching = false;
|
|
|
|
search_call_api_id = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto g = std::begin(pending_joins);
|
|
|
|
while (g != std::end(pending_joins)) {
|
|
|
|
if (!g->message_sent) {
|
|
|
|
PRINT_DEBUG("resending join lobby");
|
|
|
|
Lobby_Messages *message = new Lobby_Messages();
|
|
|
|
message->set_type(Lobby_Messages::JOIN);
|
|
|
|
g->message_sent = send_owner_packet(g->lobby_id, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
Lobby *lobby = get_lobby(g->lobby_id);
|
|
|
|
if (lobby && lobby->deleted()) {
|
|
|
|
PRINT_DEBUG("lobby deleted %llu", g->lobby_id.ConvertToUint64());
|
|
|
|
LobbyEnter_t data{};
|
|
|
|
data.m_ulSteamIDLobby = lobby->room_id();
|
|
|
|
data.m_rgfChatPermissions = 0; //Unused - Always 0
|
|
|
|
data.m_bLocked = false;
|
|
|
|
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseDoesntExist;
|
|
|
|
callback_results->addCallResult(g->api_id, data.k_iCallback, &data, sizeof(data));
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
g = pending_joins.erase(g);
|
|
|
|
} else if (get_lobby_member(lobby, settings->get_local_steam_id())) {
|
|
|
|
PRINT_DEBUG("lobby joined %llu", g->lobby_id.ConvertToUint64());
|
|
|
|
LobbyEnter_t data{};
|
|
|
|
data.m_ulSteamIDLobby = lobby->room_id();
|
|
|
|
data.m_rgfChatPermissions = 0; //Unused - Always 0
|
|
|
|
data.m_bLocked = false;
|
|
|
|
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess;
|
|
|
|
callback_results->addCallResult(g->api_id, data.k_iCallback, &data, sizeof(data));
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
g = pending_joins.erase(g);
|
|
|
|
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)lobby->room_id(), true);
|
|
|
|
} else if (check_timedout(g->joined, PENDING_JOIN_TIMEOUT)) {
|
|
|
|
PRINT_DEBUG("pending join timeout %llu", g->lobby_id.ConvertToUint64());
|
|
|
|
LobbyEnter_t data{};
|
|
|
|
data.m_ulSteamIDLobby = g->lobby_id.ConvertToUint64();
|
|
|
|
data.m_rgfChatPermissions = 0; //Unused - Always 0
|
|
|
|
data.m_bLocked = false;
|
|
|
|
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseDoesntExist;
|
|
|
|
callback_results->addCallResult(g->api_id, data.k_iCallback, &data, sizeof(data));
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
g = pending_joins.erase(g);
|
|
|
|
} else {
|
|
|
|
++g;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto dr = std::begin(data_requested);
|
|
|
|
while (dr != std::end(data_requested)) {
|
|
|
|
if (get_lobby(dr->lobby_id)) {
|
|
|
|
trigger_lobby_dataupdate(dr->lobby_id, dr->lobby_id, true);
|
|
|
|
dr = data_requested.erase(dr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check_timedout(dr->requested, REQUEST_LOBBY_DATA_TIMEOUT)) {
|
|
|
|
trigger_lobby_dataupdate(dr->lobby_id, dr->lobby_id, false);
|
|
|
|
dr = data_requested.erase(dr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
++dr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Steam_Matchmaking::Callback(Common_Message *msg)
|
|
|
|
{
|
|
|
|
if (msg->has_lobby()) {
|
|
|
|
PRINT_DEBUG("GOT A LOBBY appid: %u " "%" PRIu64 "", msg->lobby().appid(), msg->lobby().owner());
|
|
|
|
if (msg->lobby().owner() != settings->get_local_steam_id().ConvertToUint64() && msg->lobby().appid() == settings->get_local_game_id().AppID()) {
|
|
|
|
Lobby *lobby = get_lobby((uint64)msg->lobby().room_id());
|
|
|
|
if (!lobby) {
|
|
|
|
size_t old_size = lobbies.size();
|
|
|
|
lobbies.resize(old_size + 1);
|
|
|
|
lobbies[old_size].set_room_id(msg->lobby().room_id());
|
|
|
|
lobby = &(lobbies[old_size]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!lobby->deleted()) {
|
|
|
|
if (!protobuf_message_equal(*lobby, msg->lobby())) {
|
|
|
|
bool we_are_in_lobby = !!get_lobby_member(lobby, settings->get_local_steam_id());
|
|
|
|
if (we_are_in_lobby) trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)lobby->room_id(), true);
|
|
|
|
|
|
|
|
for (auto & m : lobby->members()) {
|
|
|
|
int count = 0;
|
|
|
|
Lobby_Member *member = get_lobby_member(msg->mutable_lobby(), (uint64)m.id());
|
|
|
|
|
|
|
|
if (we_are_in_lobby) {
|
|
|
|
if (!member) {
|
|
|
|
trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)m.id(), true, true, 0.2);
|
|
|
|
} else if (!protobuf_message_equal(*member, m)) {
|
|
|
|
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)m.id(), true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool joined = false;
|
|
|
|
for (auto & m : msg->lobby().members()) {
|
|
|
|
Lobby_Member *member = get_lobby_member(lobby, (uint64)m.id());
|
|
|
|
if (!member) {
|
|
|
|
if (m.id() == settings->get_local_steam_id().ConvertToUint64()) {
|
|
|
|
CSteamID id((uint64)lobby->room_id());
|
|
|
|
auto pd = pending_joins.begin();
|
|
|
|
while (pd != pending_joins.end()) {
|
|
|
|
if (pd->lobby_id == id) {
|
|
|
|
bool success = true;
|
|
|
|
LobbyEnter_t data;
|
|
|
|
data.m_ulSteamIDLobby = lobby->room_id();
|
|
|
|
data.m_rgfChatPermissions = 0; //Unused - Always 0
|
|
|
|
data.m_bLocked = false;
|
|
|
|
data.m_EChatRoomEnterResponse = success ? k_EChatRoomEnterResponseSuccess : k_EChatRoomEnterResponseError;
|
|
|
|
callback_results->addCallResult(pd->api_id, data.k_iCallback, &data, sizeof(data));
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
pd = pending_joins.erase(pd);
|
|
|
|
joined = true;
|
|
|
|
} else {
|
|
|
|
++pd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (joined) {
|
|
|
|
on_self_enter_leave_lobby((uint64)lobby->room_id(), lobby->type(), false);
|
|
|
|
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)lobby->room_id(), true);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (we_are_in_lobby) trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)m.id(), false, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (joined) {
|
|
|
|
for (auto & m : msg->lobby().members()) {
|
|
|
|
if (m.id() != settings->get_local_steam_id().ConvertToUint64()) {
|
|
|
|
//TODO: is this good?
|
|
|
|
//trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)m.id(), false, true);
|
|
|
|
if (m.values().size()) {
|
|
|
|
//TODO: check if this is what steam does
|
|
|
|
//trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)m.id(), true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((joined && msg->lobby().gameserver().num_update()) || (we_are_in_lobby && (lobby->gameserver().num_update() != msg->lobby().gameserver().num_update()))) {
|
|
|
|
send_gameservercreated_cb(lobby->room_id(), msg->lobby().gameserver().id(), msg->lobby().gameserver().ip(), msg->lobby().gameserver().port());
|
|
|
|
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)lobby->room_id(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
*lobby = msg->lobby();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (msg->has_lobby_messages()) {
|
|
|
|
PRINT_DEBUG("LOBBY MESSAGE %u " "%" PRIu64 "", msg->lobby_messages().type(), msg->lobby_messages().id());
|
|
|
|
Lobby *lobby = get_lobby((uint64)msg->lobby_messages().id());
|
|
|
|
if (lobby && !lobby->deleted()) {
|
|
|
|
bool we_are_in_lobby = !!get_lobby_member(lobby, settings->get_local_steam_id());
|
|
|
|
if (lobby->owner() == settings->get_local_steam_id().ConvertToUint64()) {
|
|
|
|
if (msg->lobby_messages().type() == Lobby_Messages::JOIN) {
|
|
|
|
PRINT_DEBUG("LOBBY MESSAGE: JOIN, lobby=%llu from=%llu", (uint64)lobby->room_id(), (uint64)msg->source_id());
|
|
|
|
if (add_member_to_lobby(lobby, (uint64)msg->source_id())) {
|
|
|
|
trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)msg->source_id(), false, true, 0.01);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->lobby_messages().type() == Lobby_Messages::MEMBER_DATA) {
|
|
|
|
PRINT_DEBUG("LOBBY MESSAGE: MEMBER_DATA");
|
|
|
|
Lobby_Member *member = get_lobby_member(lobby, (uint64)msg->source_id());
|
|
|
|
if (member) {
|
|
|
|
for (auto const &p : msg->lobby_messages().map()) {
|
|
|
|
PRINT_DEBUG("member data '%s'='%s'", p.first.c_str(), p.second.c_str());
|
|
|
|
auto result = caseinsensitive_find(member->values(), p.first);
|
|
|
|
if (result == member->values().end()) {
|
|
|
|
(*member->mutable_values())[p.first] = p.second;
|
|
|
|
} else {
|
|
|
|
(*member->mutable_values())[result->first] = p.second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)member->id(), true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->lobby_messages().type() == Lobby_Messages::LEAVE) {
|
|
|
|
PRINT_DEBUG("LOBBY MESSAGE: LEAVE " "%" PRIu64 "", msg->source_id());
|
|
|
|
leave_lobby(lobby, (uint64)msg->source_id());
|
|
|
|
if (we_are_in_lobby) trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)msg->source_id(), true, true, 0.2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->lobby_messages().type() == Lobby_Messages::CHANGE_OWNER) {
|
|
|
|
PRINT_DEBUG("LOBBY MESSAGE: CHANGE OWNER");
|
|
|
|
lobby->set_owner(msg->lobby_messages().idata());
|
|
|
|
if (we_are_in_lobby) trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)lobby->room_id(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->lobby_messages().type() == Lobby_Messages::CHAT_MESSAGE) {
|
|
|
|
PRINT_DEBUG("LOBBY MESSAGE: CHAT MESSAGE");
|
|
|
|
if (we_are_in_lobby) {
|
|
|
|
struct Chat_Entry entry{};
|
|
|
|
entry.type = k_EChatEntryTypeChatMsg;
|
|
|
|
entry.message = msg->lobby_messages().bdata();
|
|
|
|
entry.lobby_id = CSteamID((uint64)msg->lobby_messages().id());
|
|
|
|
entry.user_id = CSteamID((uint64)msg->source_id());
|
|
|
|
LobbyChatMsg_t data{};
|
|
|
|
data.m_ulSteamIDLobby = msg->lobby_messages().id();
|
|
|
|
data.m_ulSteamIDUser = msg->source_id();
|
|
|
|
data.m_eChatEntryType = entry.type;
|
|
|
|
data.m_iChatID = chat_entries.size();
|
|
|
|
chat_entries.push_back(entry);
|
|
|
|
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->has_low_level()) {
|
|
|
|
if (msg->low_level().type() == Low_Level::CONNECT) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
|
|
|
for (auto & l: lobbies) {
|
|
|
|
if (leave_lobby(&(l), (uint64)msg->source_id()))
|
|
|
|
trigger_lobby_member_join_leave((uint64)l.room_id(), (uint64)msg->source_id(), true, true, 0.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
remove_lobbies();
|
|
|
|
}
|