Use XMPP Roster for BuddyList

This allows the same user to keep the buddy list across clients and
config resets. At the same time this allows for per lobby buddy lists
which is needed as the same nickname may refer to different people.

Use lower case comparison for buddy names. This is in line with the idea
of local part of jid should be seen as normalized nick and is used
server-side to compare user names.

The migration is kept simple. I.e. only attempted if the roster is
empty and the config indicates no migration was done before.

Fixes: #6824
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
This commit is contained in:
Ralph Sennhauser committed 2026-10-19 20:31:27 +02:00
1 parent 72e12ddd6a
commit 830c97c521
16 files changed
+313 -65

No files matched your search

+1 -1
View File
@@ -566,7 +566,7 @@ terms_of_service = "0" ; Version (hash) of the Terms of Service tha
privacy_policy = "0" ; Version (hash) of the Privacy Policy that the user has accepted
xpartamupp = "wfgbot29" ; Name of the server-side XMPP-account that manage games
echelon = "echelon29" ; Name of the server-side XMPP-account that manages ratings
buddies = "," ; Comma separated list of playernames that the current user has marked as buddies
buddies.migrated = false ; Whether a former local buddy list was already migrated to the server roster
rememberpassword = true ; Whether to store the encrypted password in the user config
[lobby.columns]
@@ -8,13 +8,6 @@ var g_DescriptionHighlight = "orange";
*/
var g_DefaultLobbyRating = 1200;
/**
* XEP-0172 doesn't restrict nicknames, but our lobby policy does.
* So use this human readable delimiter to separate buddy names in the config file.
*/
var g_BuddyListDelimiter = ",";
/**
* Returns the nickname without the lobby rating.
*/
@@ -24,11 +17,6 @@ function splitRatingFromNick(playerName)
return { "nick": result ? result[1] : playerName, "rating": result ? +result[2] : "" };
}
/**
* Array of playernames that the current user has marked as buddies.
*/
var g_Buddies = Engine.ConfigDB_GetValue("user", "lobby.buddies").split(g_BuddyListDelimiter);
/**
* Denotes which players are a lobby buddy of the current user.
*/
@@ -119,7 +107,7 @@ function formatPlayerInfo(playerDataArray, playerStates)
playerDescriptions[teamIdx].push(sprintf(playerDescription, {
"playerName":
coloredText(
(g_Buddies.indexOf(playerNick) != -1 ? g_BuddySymbol + " " : "") +
(Engine.IsLobbyBuddy(playerNick) ? g_BuddySymbol + " " : "") +
escapeText(playerData.Name),
(typeof getPlayerColor === 'function' ?
(isAI ? "white" : getPlayerColor(playerNick)) :
@@ -103,7 +103,8 @@ class PlayerAssignmentsController
{
const assignOption = Engine.ConfigDB_GetValue("user", this.ConfigAssignPlayers);
if (assignOption == "disabled" ||
assignOption == "buddies" && g_Buddies.indexOf(splitRatingFromNick(newAssignments[newGUID].name).nick) == -1)
assignOption == "buddies" &&
!Engine.IsLobbyBuddy(splitRatingFromNick(newAssignments[newGUID].name).nick))
return;
}
@@ -11,9 +11,10 @@ class BuddyButton
this.toggleBuddyButton = Engine.GetGUIObjectByName("toggleBuddyButton");
this.toggleBuddyButton.onPress = this.onPress.bind(this);
const rebuild = this.rebuild.bind(this);
xmppMessages.registerXmppMessageHandler("system", "connected", rebuild);
xmppMessages.registerXmppMessageHandler("system", "disconnected", rebuild);
const rebuildAndNotify = this.rebuildAndNotify.bind(this);
xmppMessages.registerXmppMessageHandler("system", "connected", rebuildAndNotify);
xmppMessages.registerXmppMessageHandler("system", "disconnected", rebuildAndNotify);
xmppMessages.registerXmppMessageHandler("roster", "change", rebuildAndNotify);
this.rebuild();
}
@@ -31,38 +32,33 @@ class BuddyButton
rebuild()
{
this.toggleBuddyButton.caption =
g_Buddies.indexOf(this.playerName) != -1 ?
this.UnmarkString :
this.MarkString;
const isBuddy = !!this.playerName && Engine.IsLobbyBuddy(this.playerName);
this.toggleBuddyButton.caption = isBuddy ? this.UnmarkString : this.MarkString;
this.toggleBuddyButton.enabled = Engine.IsXmppClientConnected() && !!this.playerName && this.playerName != g_Nickname;
}
rebuildAndNotify()
{
this.rebuild();
for (const handler of this.buddyChangedHandlers)
handler();
}
/**
* Toggle the buddy state of the selected player.
*/
onPress()
{
if (!this.playerName || this.playerName == g_Nickname || this.playerName.indexOf(g_BuddyListDelimiter) != -1)
if (!this.playerName || this.playerName == g_Nickname)
return;
const index = g_Buddies.indexOf(this.playerName);
if (index != -1)
g_Buddies.splice(index, 1);
if (Engine.IsLobbyBuddy(this.playerName))
Engine.LobbyRemoveBuddy(this.playerName);
else
g_Buddies.push(this.playerName);
Engine.ConfigDB_CreateAndSaveValue(
"user",
"lobby.buddies",
g_Buddies.filter(nick => nick).join(g_BuddyListDelimiter) || g_BuddyListDelimiter
);
this.rebuild();
for (const handler of this.buddyChangedHandlers)
handler();
Engine.LobbyAddBuddy(this.playerName);
}
}
@@ -187,7 +187,7 @@ class Game
playerRatingTotal += playerNickRating.rating || g_DefaultLobbyRating;
// Sort games with playing buddies above games with spectating buddies
if (hasBuddies < 2 && g_Buddies.indexOf(playerNickRating.nick) != -1)
if (hasBuddies < 2 && Engine.IsLobbyBuddy(playerNickRating.nick))
hasBuddies = player.Team == "observer" ? 1 : 2;
}
@@ -96,7 +96,7 @@ class PlayerList
parsePlayer(sortKey, player)
{
player.isBuddy = g_Buddies.indexOf(player.name) != -1;
player.isBuddy = Engine.IsLobbyBuddy(player.name);
switch (sortKey)
{
@@ -112,5 +112,8 @@ XmppMessages.prototype.MessageTypes = {
"profile",
"leaderboard",
"ratinglist"
],
"roster": [
"change"
]
};
@@ -1,6 +1,6 @@
[font="sans-bold-18"]0 A.D. Empires Ascendant Multiplayer Lobby Privacy Policy[/font]
[font="sans-bold-14"]Document Date:[/font] 2025-04-10
[font="sans-bold-14"]Document Date:[/font] 2026-10-07
[font="sans-bold-16"]Personal data processed by Wildfire Games and purposes of processing:[/font]
[font="sans-bold-14"]1. Player name[/font]
@@ -38,6 +38,10 @@ Wildfire Games stores match statistics to:
1. Verify the accuracy of the rating score and identify and enforce Terms of Service violations relating to that.
2. Compute and publish community statistics to improve the game and the service, for example to improve the balancing of the game or to make certain aspects of the game more appealing.
[font="sans-bold-14"]7. Buddies[/font]
Players can mark other players as buddies to distinguish them from all other players.
Wildfire Games stores each player's buddy list to ensure that it is available across different computers a player might be using.
[font="sans-bold-14"]Security of processing:[/font]
The transmission of personal data is secured using TLS encryption (GDPR 32).
Personal data is protected against unintentional loss in encrypted backups for additional time (GDPR 30.1.g, GDPR 32).
+3 -1
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games.
/* Copyright (C) 2026 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -62,6 +62,8 @@
#include <gloox/presence.h> // IWYU pragma: export
#include <gloox/registration.h> // IWYU pragma: export
#include <gloox/registrationhandler.h> // IWYU pragma: export
#include <gloox/rosterlistener.h> // IWYU pragma: export
#include <gloox/rostermanager.h> // IWYU pragma: export
#include <gloox/stanzaextension.h> // IWYU pragma: export
#include <gloox/tag.h> // IWYU pragma: export
#include <gloox/util.h> // IWYU pragma: export
+186 -5
View File
@@ -39,6 +39,7 @@
#include "scriptinterface/Request.h"
#include "scriptinterface/StructuredClone.h"
#include <algorithm>
#include <ctime>
#include <deque>
#include <iostream>
@@ -51,6 +52,7 @@
#include <js/Vector.h>
#include <map>
#include <optional>
#include <set>
#include <string>
#include <tuple>
#include <unicode/locid.h>
@@ -60,6 +62,8 @@
namespace
{
constexpr const char* ROSTER_GROUP_NAME_BUDDIES{"buddies"};
//debug
#if 1
#define DbgXMPP(x)
@@ -129,7 +133,7 @@ std::optional<gloox::MUCRoom> CreateMucRoom(gloox::MUCRoomHandler* handler, cons
class XmppClient::Impl : public gloox::ConnectionListener, public gloox::MUCRoomHandler,
public gloox::IqHandler, public gloox::RegistrationHandler, public gloox::MessageHandler,
public gloox::Jingle::SessionHandler, public gloox::LogHandler
public gloox::Jingle::SessionHandler, public gloox::LogHandler, public gloox::RosterListener
{
public:
// Basic
@@ -161,6 +165,7 @@ public:
gloox::CertStatus m_certStatus;
bool m_initialLoadComplete;
bool m_regOpt;
std::set<std::string> m_Buddies;
private:
/* Xmpp handlers */
@@ -216,6 +221,37 @@ private:
void handleSessionInitiation(gloox::Jingle::Session* session,
const gloox::Jingle::Session::Jingle* jingle);
/**
* Rebuild m_Buddies from the roster.
*/
void UpdateBuddies();
/**
* Move a pre-roster buddy list from the config into the roster, then drop
* it from the config. Does nothing once that migration has happened.
*
* Must only be called once the roster has been received, since an empty
* roster cannot be distinguished from one without buddies.
*/
void MigrateBuddyList();
/* Roster */
void handleItemAdded(const gloox::JID& jid) override;
void handleItemSubscribed(const gloox::JID& /*jid*/) override {}
void handleItemRemoved(const gloox::JID& jid) override;
void handleItemUpdated(const gloox::JID& jid) override;
void handleItemUnsubscribed(const gloox::JID& /*jid*/) override {}
void handleRoster(const gloox::Roster& /*roster*/) override;
void handleRosterPresence(const gloox::RosterItem& /*item*/, const std::string& /*resource*/, gloox::Presence::PresenceType /*presence*/, const std::string& /*msg*/) override {}
void handleSelfPresence(const gloox::RosterItem& /*item*/, const std::string& /*resource*/, gloox::Presence::PresenceType /*presence*/, const std::string& /*msg*/) override {}
bool handleSubscriptionRequest(const gloox::JID& /*jid*/, const std::string& /*msg*/) override { return false; }
bool handleUnsubscriptionRequest(const gloox::JID& /*jid*/, const std::string& /*msg*/) override { return false; }
void handleNonrosterPresence(const gloox::Presence& /*presence*/) override {}
void handleRosterError(const gloox::IQ& iq) override;
#if GLOOXVERSION >= 0x010100
void handleRosterItemExchange(const gloox::JID& /*from*/, const gloox::RosterX* /*items*/) override {}
#endif
template<typename... Args>
void CreateGUIMessage(
const std::string& type,
@@ -223,14 +259,16 @@ private:
const std::time_t time,
Args const&... args);
public:
struct SPlayer {
SPlayer(const gloox::Presence::PresenceType presence, const gloox::MUCRoomRole role,
const std::string& rating)
: m_Presence(presence), m_Role(role), m_Rating(rating)
const std::string& bareJid, const std::string& rating)
: m_Presence(presence), m_Role(role), m_BareJid(bareJid), m_Rating(rating)
{
}
gloox::Presence::PresenceType m_Presence;
gloox::MUCRoomRole m_Role;
std::string m_BareJid;
std::string m_Rating;
};
using PlayerMap = std::map<std::string, SPlayer>;
@@ -335,6 +373,8 @@ XmppClient::Impl::Impl(const Script::Interface* scriptInterface, const std::stri
m_client.registerMessageHandler(this);
m_client.rosterManager()->registerRosterListener(this, false);
m_registration.registerRegistrationHandler(this);
// Uncomment to see the raw stanzas
@@ -428,6 +468,7 @@ void XmppClient::Impl::onDisconnect(gloox::ConnectionError error)
m_PlayerMap.clear();
m_PlayerMapUpdate = true;
m_HistoricGuiMessages.clear();
m_Buddies.clear();
m_initialLoadComplete = false;
CreateGUIMessage(
@@ -1190,7 +1231,7 @@ void XmppClient::Impl::handleMUCParticipantPresence(gloox::MUCRoom*, const gloox
m_PlayerMap.emplace(
std::piecewise_construct,
std::forward_as_tuple(participant.newNick),
std::forward_as_tuple(presence.presence(), participant.role, std::move(m_PlayerMap.at(nick).m_Rating)));
std::forward_as_tuple(presence.presence(), participant.role, participant.jid ? participant.jid->bare() : "", std::move(m_PlayerMap.at(nick).m_Rating)));
else
LOGERROR("Nickname changed to an existing nick!");
@@ -1282,12 +1323,16 @@ void XmppClient::Impl::handleMUCParticipantPresence(gloox::MUCRoom*, const gloox
m_PlayerMap.emplace(
std::piecewise_construct,
std::forward_as_tuple(nick),
std::forward_as_tuple(presence.presence(), participant.role, std::string()));
std::forward_as_tuple(presence.presence(), participant.role,
participant.jid ? participant.jid->bare() : "",
std::string()));
}
else
{
it->second.m_Presence = presence.presence();
it->second.m_Role = participant.role;
if (participant.jid)
it->second.m_BareJid = participant.jid->bare();
}
}
@@ -1511,3 +1556,139 @@ void XmppClient::Impl::handleSessionInitiation(gloox::Jingle::Session*,
g_NetServer->SendHolePunchingMessage(candidate.ip, candidate.port);
}
// ----------------------------------------------------------------------------
//
// Roster
//
// ----------------------------------------------------------------------------
namespace {
// TODO: Helper only needed till split view is supported by all compilers.
std::vector<std::string> splitBuddyList(std::string buddyList)
{
std::vector<std::string> buddies{};
size_t posStart{0};
size_t posEnd{0};
while ((posEnd = buddyList.find(",", posStart)) != std::string::npos)
{
buddies.push_back(buddyList.substr(posStart, posEnd - posStart));
posStart = posEnd + 1;
}
buddies.push_back(buddyList.substr(posStart));
return buddies;
}
/**
* Whether the given roster item is one of the buddies tracked in the buddy list.
*/
bool IsBuddyRosterItem(const gloox::RosterItem& item)
{
const gloox::StringList groups = item.groups();
return !item.name().empty() &&
std::find(groups.begin(), groups.end(), ROSTER_GROUP_NAME_BUDDIES) != groups.end();
}
} // namespace
void XmppClient::AddBuddy(const std::string& nick)
{
DbgXMPP("Add buddy: " + nick);
const auto it = m_Impl->m_PlayerMap.find(nick);
if (it == m_Impl->m_PlayerMap.end())
LOGWARNING("XmppClient: Can't add unknown player to roster: {}", nick);
else if (it->second.m_BareJid.empty())
LOGWARNING("XmppClient: Anonymous player can't be added to roster: {}", nick);
else
m_Impl->m_client.rosterManager()->add(it->second.m_BareJid, nick, gloox::StringList{ROSTER_GROUP_NAME_BUDDIES});
}
void XmppClient::RemoveBuddy(const std::string& nick)
{
DbgXMPP("Remove buddy: " + nick);
const auto it = m_Impl->m_PlayerMap.find(nick);
if (it == m_Impl->m_PlayerMap.end())
LOGWARNING("XmppClient: Can't remove unknown player from roster: {}", nick);
else if (it->second.m_BareJid.empty())
LOGWARNING("XmppClient: Anonymous player can't be removed from roster: {}", nick);
else
m_Impl->m_client.rosterManager()->remove(it->second.m_BareJid);
}
bool XmppClient::IsBuddy(const std::string& nick) const
{
return m_Impl->m_Buddies.find(CStr(nick).LowerCase()) != m_Impl->m_Buddies.end();
}
std::set<std::string> XmppClient::GetBuddies() const
{
return m_Impl->m_Buddies;
}
void XmppClient::Impl::UpdateBuddies()
{
m_Buddies.clear();
for (const auto& [jid, item] : *m_client.rosterManager()->roster())
{
if (IsBuddyRosterItem(*item))
m_Buddies.insert(CStr(item->name()).LowerCase());
}
}
void XmppClient::Impl::MigrateBuddyList()
{
if (g_ConfigDB.Get("lobby.buddies.migrated", false))
return;
if (m_client.rosterManager()->roster()->empty())
{
const std::string buddyList{g_ConfigDB.Get("lobby.buddies", std::string{})};
for (const std::string& nick : splitBuddyList(buddyList))
{
if (nick.empty())
continue;
m_client.rosterManager()->add(nick + "@" + m_server, nick, gloox::StringList{ROSTER_GROUP_NAME_BUDDIES});
}
}
g_ConfigDB.RemoveValue(CFG_USER, "lobby.buddies");
g_ConfigDB.SetValueString(CFG_USER, "lobby.buddies.migrated", "true");
g_ConfigDB.WriteFile(CFG_USER);
}
void XmppClient::Impl::handleRoster(const gloox::Roster& /*roster*/)
{
MigrateBuddyList();
UpdateBuddies();
CreateGUIMessage("roster", "change", std::time(nullptr));
}
void XmppClient::Impl::handleItemAdded(const gloox::JID& /*jid*/)
{
UpdateBuddies();
CreateGUIMessage("roster", "change", std::time(nullptr));
}
void XmppClient::Impl::handleItemRemoved(const gloox::JID& jid)
{
// gloox (1.0.28) calles this before before updating the roster which means
// calling UpdateBuddies here would still leave the removed buddy in
// m_Buddies; drop just that entry instead.
if (const gloox::RosterItem* item = m_client.rosterManager()->getRosterItem(jid); item && IsBuddyRosterItem(*item))
m_Buddies.erase(CStr(item->name()).LowerCase());
CreateGUIMessage("roster", "change", std::time(nullptr));
}
void XmppClient::Impl::handleItemUpdated(const gloox::JID& /*jid*/)
{
UpdateBuddies();
CreateGUIMessage("roster", "change", std::time(nullptr));
}
void XmppClient::Impl::handleRosterError(const gloox::IQ& iq)
{
LOGERROR("{}", iq.tag()->xml());
}
+36
View File
@@ -20,6 +20,8 @@
#include <js/Value.h>
#include <memory>
#include <set>
#include <string>
namespace Script { class Interface; }
namespace Script { class Request; }
@@ -69,6 +71,40 @@ public:
void SendMUCMessage(const std::string& message);
void SendStunEndpointToHost(const std::string& ip, std::uint16_t port, const std::string& hostJID);
/* Roster */
/**
* Add the given nickname to the buddies of the current user.
*
* @param nick The nickname of a player in the lobby.
*/
void AddBuddy(const std::string& nick);
/**
* Remove the given nickname from the buddies of the current user.
*
* @param nick The nickname of a player in the lobby.
*/
void RemoveBuddy(const std::string& nick);
/**
* Whether the given nickname is a buddy of the current user.
*
* The buddies are stored in the XMPP roster of the connected lobby. Since
* a nickname only identifies a user within one lobby, this is meaningless
* without a connection and always returns false in that case.
*
* @param nick The nickname to look up, compared case-insensitively
*/
bool IsBuddy(const std::string& nick) const;
/**
* Get the nicknames of all buddies of the current user.
*
* @returns The (lowercase) nicknames of the buddies in the lobby's roster
*/
std::set<std::string> GetBuddies() const;
private:
class Impl;
const std::unique_ptr<Impl> m_Impl;
@@ -51,6 +51,21 @@ bool HasXmppClient()
return g_XmppClient;
}
/**
* Whether the given nickname is a buddy of the current user.
*
* @param nick The nickname to look up, compared case-insensitively
*/
static bool IsLobbyBuddy([[maybe_unused]] const std::string& nick)
{
#if CONFIG2_LOBBY
return g_XmppClient && g_XmppClient->IsBuddy(nick);
#else
// Without the lobby there is no roster to look the nickname up in.
return false;
#endif
}
void SetRankedGame(bool isRanked)
{
g_rankedGame = isRanked;
@@ -190,6 +205,7 @@ void RegisterScriptFunctions(const Script::Request& rq)
{
// Lobby functions
Script::Function::Register<&HasXmppClient>(rq, "HasXmppClient");
Script::Function::Register<&IsLobbyBuddy>(rq, "IsLobbyBuddy");
Script::Function::Register<&SetRankedGame>(rq, "SetRankedGame");
#if CONFIG2_LOBBY // Allow the lobby to be disabled
Script::Function::Register<&StartXmppClient>(rq, "StartXmppClient");
@@ -229,6 +245,10 @@ void RegisterScriptFunctions(const Script::Request& rq)
REGISTER_XMPP(GetRole, "LobbyGetPlayerRole");
REGISTER_XMPP(GetRating, "LobbyGetPlayerRating");
REGISTER_XMPP(GetSubject, "LobbyGetRoomSubject");
// Roster
REGISTER_XMPP(AddBuddy, "LobbyAddBuddy");
REGISTER_XMPP(RemoveBuddy, "LobbyRemoveBuddy");
#undef REGISTER_XMPP
Script::Function::Register<&EncryptPassword>(rq, "EncryptPassword");
+8 -14
View File
@@ -118,9 +118,10 @@ static CStr DebugName(CNetServerSession* session)
CNetServerWorker::CNetServerWorker(const bool continueSavedGame, std::uint16_t port,
const bool useLobbyAuth, std::string password, std::string controllerSecret,
std::string initAttributes) :
std::string initAttributes, std::set<std::string> buddies) :
m_ContinuesSavedGame{continueSavedGame},
m_LobbyAuth{useLobbyAuth},
m_Buddies{std::move(buddies)},
m_ControllerSecret{std::move(controllerSecret)},
m_Password{std::move(password)}
{
@@ -1078,17 +1079,9 @@ bool CNetServerWorker::OnAuthenticate(CNetServerSession* session, CFsmEvent<CNet
}
else if (observerLateJoin == "buddies")
{
std::wstringstream buddiesStream(wstring_from_utf8(
g_ConfigDB.Get("lobby.buddies", std::string{})));
CStrW buddy;
while (std::getline(buddiesStream, buddy, L','))
{
if (buddy == usernameWithoutRating)
{
isRejoining = true;
break;
}
}
const std::string name{usernameWithoutRating.ToUTF8().LowerCase()};
if (server.m_Buddies.count(name) > 0)
isRejoining = true;
}
}
@@ -1695,9 +1688,10 @@ void CNetServerWorker::SendHolePunchingMessage(const CStr& ipStr, std::uint16_t
CNetServer::CNetServer(const bool continueSavedGame, std::uint16_t port, const bool useLobbyAuth,
std::string password, std::string controllerSecret, std::string initAttributes) :
std::string password, std::string controllerSecret, std::string initAttributes,
std::set<std::string> buddies) :
m_Worker{continueSavedGame, port, useLobbyAuth, password, std::move(controllerSecret),
std::move(initAttributes)},
std::move(initAttributes), std::move(buddies)},
m_LobbyAuth{useLobbyAuth},
m_Password{std::move(password)}
{
+12 -2
View File
@@ -29,6 +29,7 @@
#include <js/Value.h>
#include <mutex>
#include <optional>
#include <set>
#include <string>
#include <thread>
#include <unordered_map>
@@ -135,7 +136,8 @@ private:
friend class CNetServer;
CNetServerWorker(const bool continuesSavedGame, std::uint16_t port, const bool useLobbyAuth,
std::string password, std::string controllerSecret, std::string initAttributes);
std::string password, std::string controllerSecret, std::string initAttributes,
std::set<std::string> buddies);
~CNetServerWorker();
bool CheckPassword(const std::string& password, const std::string& salt) const;
@@ -262,6 +264,13 @@ private:
*/
const bool m_LobbyAuth;
/**
* The nicknames (lowercase) of the host's buddies at the time the game was
* hosted, used to decide who may join after the game has started
* (lateobserver authentication).
*/
const std::set<std::string> m_Buddies;
std::unique_ptr<ENetHost, DestroyHost> m_Host;
std::vector<std::unique_ptr<CNetServerSession>> m_Sessions;
@@ -356,7 +365,8 @@ class CNetServer
NONCOPYABLE(CNetServer);
public:
CNetServer(const bool isSavedGame, std::uint16_t port, const bool useLobbyAuth = false,
std::string password = {}, std::string controllerSecret = {}, std::string initAttributes = {});
std::string password = {}, std::string controllerSecret = {}, std::string initAttributes = {},
std::set<std::string> buddies = {});
/**
* Call from the GUI to asynchronously notify all clients that they should start loading the game.
@@ -20,6 +20,7 @@
#include "JSInterface_Network.h"
#include "lib/code_generation.h"
#include "lib/config2.h"
#include "lib/debug.h"
#include "lib/utf8.h"
#include "lobby/XmppClient.h"
@@ -45,6 +46,7 @@
#include <js/TypeDecls.h>
#include <js/Value.h>
#include <optional>
#include <set>
#include <stdexcept>
#include <string>
#include <utility>
@@ -109,7 +111,18 @@ void StartNetworkHost(const CStrW& playerName, const std::uint16_t serverPort, c
HashCryptographically(password, hostJID + password + PS_SERIALIZATION_VERSION) : "";
const std::string secret = ps_generate_guid();
g_NetServer = new CNetServer(continueSavedGame, serverPort, hasLobby, hashedPassword, secret);
// Snapshot the host's buddies from the roster of the lobby they are
// connected to. Hosting without a lobby means there is no roster, so
// nobody is a buddy.
std::set<std::string> buddies;
#if CONFIG2_LOBBY
if (hasLobby)
buddies = g_XmppClient->GetBuddies();
#endif
g_NetServer = new CNetServer(continueSavedGame, serverPort, hasLobby, hashedPassword, secret,
{}, buddies);
// Generate a secret to identify the host client.
+1 -1
View File
@@ -55,7 +55,7 @@ void TriggerAllHooks(const std::multimap<CStr, std::function<void()>>& hooks, co
// These entries will not be printed to logfiles, so that logfiles can be shared without leaking personal or sensitive data
const std::unordered_set<std::string> g_UnloggedEntries = {
"lobby.password",
"lobby.buddies",
"lobby.buddies", // The buddy list of older versions, now stored in the server roster and removed on migration.
"userreport.id" // authentication token for GDPR personal data requests
};