Skip to content
2 changes: 2 additions & 0 deletions Core/GameEngine/Include/Common/UserPreferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ class UserPreferences : public PreferenceMap
Real getReal(AsciiString key, Real defaultValue) const;
Int getInt(AsciiString key, Int defaultValue) const;
AsciiString getAsciiString(AsciiString key, AsciiString defaultValue) const;
UnicodeString getUnicodeString(AsciiString key, UnicodeString defaultValue) const;

void setBool(AsciiString key, Bool val);
void setReal(AsciiString key, Real val);
void setInt(AsciiString key, Int val);
void setAsciiString(AsciiString key, AsciiString val);
void setUnicodeString(AsciiString key, UnicodeString val);

protected:
AsciiString m_filename;
Expand Down
34 changes: 24 additions & 10 deletions Core/GameEngine/Source/Common/UserPreferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,19 @@ AsciiString UserPreferences::getAsciiString(AsciiString key, AsciiString default
return it->second;
}

UnicodeString UserPreferences::getUnicodeString(AsciiString key, UnicodeString defaultValue) const
{
UserPreferences::const_iterator it = find(key);
if (it == end())
{
return defaultValue;
}

UnicodeString ret;
ret.translate(it->second);
return ret;
}

void UserPreferences::setBool(AsciiString key, Bool val)
{
(*this)[key] = boolAsStr(val);
Expand All @@ -280,6 +293,13 @@ void UserPreferences::setAsciiString(AsciiString key, AsciiString val)
(*this)[key] = val;
}

void UserPreferences::setUnicodeString(AsciiString key, UnicodeString val)
{
AsciiString str;
str.translate(val);
(*this)[key] = str;
}

//-----------------------------------------------------------------------------
// QuickMatchPreferences base class
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -515,22 +535,16 @@ void CustomMatchPreferences::setLastLadder(const AsciiString& addr, UnsignedShor
(*this)["LastLadderPort"] = strVal;
}

AsciiString CustomMatchPreferences::getLastLobbyName() const
UnicodeString CustomMatchPreferences::getLastLobbyName() const
{
CustomMatchPreferences::const_iterator it = find("LastLobbyName");
if (it == end())
{
return AsciiString::TheEmptyString;
}

AsciiString ret = it->second;
UnicodeString ret = getUnicodeString("LastLobbyName", UnicodeString::TheEmptyString);
ret.trim();
return ret;
}

void CustomMatchPreferences::setLastLobbyName(const AsciiString& name)
void CustomMatchPreferences::setLastLobbyName(const UnicodeString& name)
{
(*this)["LastLobbyName"] = name;
setUnicodeString("LastLobbyName", name);
}

AsciiString CustomMatchPreferences::getLastLadderAddr( void )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ class CustomMatchPreferences : public UserPreferences
CustomMatchPreferences();
virtual ~CustomMatchPreferences() override;

AsciiString getLastLobbyName() const;
void setLastLobbyName(const AsciiString& name);
UnicodeString getLastLobbyName() const;
void setLastLobbyName(const UnicodeString& name);

void setLastLadder(const AsciiString& addr, UnsignedShort port);
AsciiString getLastLadderAddr();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include "GameNetwork/GeneralsOnline/json.hpp"

// Optional field: dest keeps its default when the key is missing or null
template <typename T>
inline void JsonGetOptional(const nlohmann::json& j, const char* szKey, T& dest)
{
auto it = j.find(szKey);
if (it != j.end() && !it->is_null())
{
it->get_to(dest);
}
}

// Required field: throws when the key is missing or the wrong type
template <typename T>
inline void JsonGetRequired(const nlohmann::json& j, const char* szKey, T& dest)
{
j.at(szKey).get_to(dest);
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class PlayerConnection
m_State != EConnectionState::CONNECTION_DISCONNECTED;
}

// a closed connection has nothing to receive
bool CanRecv() const { return m_hSteamConnection != k_HSteamNetConnection_Invalid; }

int Recv(SteamNetworkingMessage_t** pMsg);

int GetHighestHistoricalLatency()
Expand Down Expand Up @@ -245,6 +248,9 @@ class NetworkMesh
m_vecPendingStateUpdates.emplace_back(userID, newState);
}

// drops a peer that left for good so nothing keeps polling it
void RemoveConnection(int64_t userID);

PlayerConnection* GetConnectionForUser(int64_t user_id)
{
if (m_mapConnections.contains(user_id))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,16 @@ class WebSocket
// lobby ID the check was started for; a stale reply (different lobby) is ignored
int64_t m_connectivityCheckLobbyID = -1;

// when the check was sent; Tick() gives up on it after a client-side timeout
int64_t m_connectivityCheckStartMs = -1;

void SendData_StartFullMeshConnectivityCheck(std::function<void(bool, std::list<std::pair<int64_t, int64_t>>, std::string)> cbOnConnectivityCheckComplete);

void ClearConnectivityCheckCallback()
{
m_cbOnConnectivityCheckComplete = nullptr;
m_connectivityCheckLobbyID = -1;
m_connectivityCheckStartMs = -1;
}

void Tick();
Expand All @@ -215,6 +219,13 @@ class WebSocket
m_pendingSignals.push(std::move(payload));
}

// signals queued for a lobby we've since left must not reach the next one
void ClearPendingSignals()
{
std::scoped_lock<std::mutex> lock(m_pendingSignalsMutex);
m_pendingSignals = std::queue<std::vector<uint8_t>>();
}

std::queue<std::vector<uint8_t>> DrainPendingSignals()
{
std::scoped_lock<std::mutex> lock(m_pendingSignalsMutex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,20 @@ struct LobbyMemberEntry : public NetworkMemberBase
int startpos = -1;
bool has_map = false;

uint16_t m_SlotIndex = 999999;
// marks a member the service sent without a usable slot
static constexpr uint16_t NO_SLOT_INDEX = UINT16_MAX;
uint16_t m_SlotIndex = NO_SLOT_INDEX;
uint16_t m_SlotState = SlotState::SLOT_OPEN;

std::string region;
std::string middlewareUserID;
int latency = 0;

bool HasValidSlot() const
{
return m_SlotIndex < MAX_SLOTS;
}

bool IsHuman() const
{
return user_id != -1 && m_SlotState == SlotState::SLOT_PLAYER;
Expand Down Expand Up @@ -384,6 +391,10 @@ class NGMP_OnlineServices_LobbyInterface

void UpdateRoomDataCache(std::function<void(bool)> fnCallback = nullptr);

// re-fetches the lobby when the roster could not be trusted, throttled
void RequestRosterRefresh();
int64_t m_lastRosterRefreshRequestMs = 0;

std::function<void(LobbyMemberEntry)> m_cbPlayerDoesntHaveMap = nullptr;
void RegisterForPlayerDoesntHaveMapCallback(std::function<void(LobbyMemberEntry)> cb)
{
Expand Down Expand Up @@ -472,7 +483,8 @@ class NGMP_OnlineServices_LobbyInterface

NetworkMesh* GetNetworkMeshForLobby() { return m_pLobbyMesh; }

void JoinLobby(LobbyEntry lobby, std::string strPassword);
// false when a join is already in progress and this request was dropped
bool JoinLobby(LobbyEntry lobby, std::string strPassword);

void LeaveCurrentLobby();
void ResetForMatchmakingRequeue();
Expand Down Expand Up @@ -551,6 +563,10 @@ class NGMP_OnlineServices_LobbyInterface
#endif

bool m_bAttemptingToJoinLobby = false;

// GET Lobby/{id} responses can arrive out of order; only apply ones newer than the last applied
std::atomic<uint64_t> m_LobbyUpdateRequestSeq = 0;
std::atomic<uint64_t> m_LobbyUpdateAppliedSeq = 0;
// Invalidates asynchronous callbacks left behind by an abandoned lobby join.
std::atomic<uint64_t> m_LobbyJoinGeneration = 0;
LobbyEntry m_LobbyTryingToJoin;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,12 +336,8 @@ void PopupHostGameInit( WindowLayout *layout, void *userData )
#if defined(GENERALS_ONLINE)
{
CustomMatchPreferences pref;
AsciiString lastLobbyName = pref.getLastLobbyName();
if (!lastLobbyName.isEmpty())
{
name.translate(lastLobbyName.str());
}
else
name = pref.getLastLobbyName();
if (name.isEmpty())
{
name.translate("Generals Online Lobby");
}
Expand Down Expand Up @@ -605,19 +601,8 @@ WindowMsgHandledType PopupHostGameSystem( GameWindow *window, UnsignedInt msg, W
#if defined(GENERALS_ONLINE)
// save last used lobby name to CustomPref.ini
{
char buffer[256];
const WideChar* w = name.str();
int i = 0;
for (; w[i] != 0 && i < 255; ++i)
{
buffer[i] = (char)(w[i] & 0xFF);
}
buffer[i] = 0;

AsciiString lobbyNameAscii = buffer;

CustomMatchPreferences pref;
pref.setLastLobbyName(lobbyNameAscii);
pref.setLastLobbyName(name);
pref.write();
}
#endif
Expand Down
Loading
Loading