Skip to content

Commit e7efbbd

Browse files
committed
- Improvements to Steam sockets impl
- Game latency data for runahead is now pre-seeded with lobby latency histogram
1 parent 5d08bee commit e7efbbd

11 files changed

Lines changed: 261 additions & 112 deletions

File tree

GeneralsMD/Code/GameEngine/Include/GameNetwork/ConnectionManager.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,20 @@ class ConnectionManager
5858
virtual void reset(); ///< Take this instance back to the initial state.
5959
virtual void update(Bool isInGame); ///< Service the Connections being managed by this instance.
6060

61+
#if defined(GENERALS_ONLINE)
62+
void SeedLatencyData(int highestLatency)
63+
{
64+
for (int i = 0; i < MAX_SLOTS; ++i) {
65+
m_fpsAverages[i] = GENERALS_ONLINE_HIGH_FPS_LIMIT;
66+
}
67+
for (int i = 0; i < MAX_SLOTS; ++i) {
68+
m_latencyAverages[i] = highestLatency / 1000.f;
69+
}
70+
71+
m_frameMetrics.SeedLatencyData(highestLatency);
72+
}
73+
#endif
74+
6175
// End SubsystemInterface functions
6276

6377
void updateRunAhead(Int oldRunAhead, Int frameRate, Bool didSelfSlug, Int nextExecutionFrame); ///< Update the run ahead value. If we are the current packet router, issue the command.
Lines changed: 75 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,76 @@
1-
/*
2-
** Command & Conquer Generals Zero Hour(tm)
3-
** Copyright 2025 Electronic Arts Inc.
4-
**
5-
** This program is free software: you can redistribute it and/or modify
6-
** it under the terms of the GNU General Public License as published by
7-
** the Free Software Foundation, either version 3 of the License, or
8-
** (at your option) any later version.
9-
**
10-
** This program is distributed in the hope that it will be useful,
11-
** but WITHOUT ANY WARRANTY; without even the implied warranty of
12-
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-
** GNU General Public License for more details.
14-
**
15-
** You should have received a copy of the GNU General Public License
16-
** along with this program. If not, see <http://www.gnu.org/licenses/>.
17-
*/
18-
19-
////////////////////////////////////////////////////////////////////////////////
20-
// //
21-
// (c) 2001-2003 Electronic Arts Inc. //
22-
// //
23-
////////////////////////////////////////////////////////////////////////////////
24-
25-
/** FrameMetrics.h */
26-
27-
#pragma once
28-
29-
#ifndef __FRAMEMETRICS_H
30-
#define __FRAMEMETRICS_H
31-
32-
#include "Lib/BaseType.h"
33-
#include "GameNetwork/NetworkDefs.h"
34-
35-
class FrameMetrics {
36-
public:
37-
FrameMetrics();
38-
virtual ~FrameMetrics();
39-
40-
void init();
41-
void reset();
42-
43-
void doPerFrameMetrics(UnsignedInt frame);
44-
void processLatencyResponse(UnsignedInt frame);
45-
void addCushion(Int cushion);
46-
47-
Real getAverageLatency();
48-
Int getAverageFPS();
49-
Int getMinimumCushion();
50-
51-
protected:
52-
// These are used for keeping track of parameters to the run ahead equation.
53-
// frames per second history variables.
54-
Real *m_fpsList; ///< A record of how many game logic frames per second there were for the last 60 seconds.
55-
time_t m_lastFpsTimeThing; ///< The time when the last fps entry started being recorded.
56-
Int m_fpsListIndex; ///< Index into the array of the current fps list entry being measured.
57-
Real m_averageFps; ///< The current average logic fps, computed just like m_averageLatency below but with the fps numbers.
58-
59-
// round trip time to packet router variables.
60-
// The lists are indexed off the frame number of the frame info packet they are associated with.
61-
// The index used should be the frame number mod the array length.
62-
Real *m_latencyList; ///< A record of the round trip latencies of the frame info packets to the packet router. Values in seconds.
63-
time_t *m_pendingLatencies; ///< The latencies of frame info packets that are "in the air."
64-
Real m_averageLatency; ///< The current average latency, this is used to save calculation time.
65-
///< When a new latency value is received, the old one is subtracted out and the new
66-
///< one is added in.
67-
68-
// packet arrival cushion variables.
69-
// Keeps track of the cushion for the incoming commands.
70-
UnsignedInt m_cushionIndex; ///< The next index to use for the cushion list.
71-
Int m_minimumCushion; ///< The average cushion for the history.
72-
};
73-
1+
/*
2+
** Command & Conquer Generals Zero Hour(tm)
3+
** Copyright 2025 Electronic Arts Inc.
4+
**
5+
** This program is free software: you can redistribute it and/or modify
6+
** it under the terms of the GNU General Public License as published by
7+
** the Free Software Foundation, either version 3 of the License, or
8+
** (at your option) any later version.
9+
**
10+
** This program is distributed in the hope that it will be useful,
11+
** but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
** GNU General Public License for more details.
14+
**
15+
** You should have received a copy of the GNU General Public License
16+
** along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
////////////////////////////////////////////////////////////////////////////////
20+
// //
21+
// (c) 2001-2003 Electronic Arts Inc. //
22+
// //
23+
////////////////////////////////////////////////////////////////////////////////
24+
25+
/** FrameMetrics.h */
26+
27+
#pragma once
28+
29+
#ifndef __FRAMEMETRICS_H
30+
#define __FRAMEMETRICS_H
31+
32+
#include "Lib/BaseType.h"
33+
#include "GameNetwork/NetworkDefs.h"
34+
35+
class FrameMetrics {
36+
public:
37+
FrameMetrics();
38+
virtual ~FrameMetrics();
39+
40+
void init();
41+
void reset();
42+
43+
void doPerFrameMetrics(UnsignedInt frame);
44+
void processLatencyResponse(UnsignedInt frame);
45+
void addCushion(Int cushion);
46+
47+
Real getAverageLatency();
48+
Int getAverageFPS();
49+
Int getMinimumCushion();
50+
51+
void SeedLatencyData(int latency);
52+
53+
protected:
54+
// These are used for keeping track of parameters to the run ahead equation.
55+
// frames per second history variables.
56+
Real *m_fpsList; ///< A record of how many game logic frames per second there were for the last 60 seconds.
57+
time_t m_lastFpsTimeThing; ///< The time when the last fps entry started being recorded.
58+
Int m_fpsListIndex; ///< Index into the array of the current fps list entry being measured.
59+
Real m_averageFps; ///< The current average logic fps, computed just like m_averageLatency below but with the fps numbers.
60+
61+
// round trip time to packet router variables.
62+
// The lists are indexed off the frame number of the frame info packet they are associated with.
63+
// The index used should be the frame number mod the array length.
64+
Real *m_latencyList; ///< A record of the round trip latencies of the frame info packets to the packet router. Values in seconds.
65+
time_t *m_pendingLatencies; ///< The latencies of frame info packets that are "in the air."
66+
Real m_averageLatency; ///< The current average latency, this is used to save calculation time.
67+
///< When a new latency value is received, the old one is subtracted out and the new
68+
///< one is added in.
69+
70+
// packet arrival cushion variables.
71+
// Keeps track of the cushion for the incoming commands.
72+
UnsignedInt m_cushionIndex; ///< The next index to use for the cushion list.
73+
Int m_minimumCushion; ///< The average cushion for the history.
74+
};
75+
7476
#endif // __FRAMEMETRICS_H

GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/NetworkMesh.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class PlayerConnection
4343

4444
int SendGamePacket(void* pBuffer, uint32_t totalDataSize);
4545

46+
void UpdateLatencyHistogram();
47+
4648
bool IsIPV4();
4749
bool IsDirect()
4850
{
@@ -52,6 +54,21 @@ class PlayerConnection
5254

5355
int Recv(SteamNetworkingMessage_t** pMsg);
5456

57+
int GetHighestHistoricalLatency()
58+
{
59+
int highestLatency = 0;
60+
for (int latencyHistory : m_vecLatencyHistory)
61+
{
62+
if (latencyHistory > highestLatency)
63+
{
64+
highestLatency = latencyHistory;
65+
}
66+
}
67+
68+
return highestLatency;
69+
}
70+
71+
std::vector<int> m_vecLatencyHistory;
5572
std::string GetStats();
5673

5774
std::string GetConnectionType();
@@ -121,6 +138,22 @@ class NetworkMesh
121138
return highestLatency;
122139
}
123140

141+
Real getMaximumHistoricalLatency()
142+
{
143+
int highestLatency = 0;
144+
145+
for (auto& kvPair : m_mapConnections)
146+
{
147+
PlayerConnection& conn = kvPair.second;
148+
if (conn.GetLatency() > highestLatency)
149+
{
150+
highestLatency = conn.GetHighestHistoricalLatency();
151+
}
152+
}
153+
154+
return Real(highestLatency);
155+
}
156+
124157

125158
std::queue<QueuedGamePacket> m_queueQueuedGamePackets;
126159

@@ -164,4 +197,9 @@ class NetworkMesh
164197
ISignalingClient* m_pSignaling = nullptr;
165198

166199
HSteamListenSocket m_hListenSock = k_HSteamListenSocket_Invalid;
200+
201+
std::string m_strTurnUsername;
202+
std::string m_strTurnToken;
203+
std::string m_strTurnUsernameString;
204+
std::string m_strTurnTokenString;
167205
};

GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,8 @@ class NGMP_OnlineServices_LobbyInterface
411411
return m_LobbyTryingToJoin;
412412
}
413413

414-
std::string& GetLobbyTurnUsername() { return m_strTURNUsername; }
415-
std::string& GetLobbyTurnToken() { return m_strTURNToken; }
414+
std::string GetLobbyTurnUsername() { return m_strTURNUsername; }
415+
std::string GetLobbyTurnToken() { return m_strTURNToken; }
416416

417417
private:
418418
std::function<void(bool)> m_cb_CreateLobbyPendingCallback = nullptr;

GeneralsMD/Code/GameEngine/Include/GameNetwork/NetworkInterface.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ class NetworkInterface : public SubsystemInterface
9898
virtual Real getUnknownBytesPerSecond( void ) = 0;
9999
virtual Real getUnknownPacketsPerSecond( void ) = 0;
100100

101+
#if defined(GENERALS_ONLINE)
102+
virtual void SeedLatencyData(int highestLatency) = 0;
103+
#endif
104+
101105
virtual void updateLoadProgress( Int percent ) = 0;
102106
virtual void loadProgressComplete( void ) = 0;
103107
virtual void sendTimeOutGameStart( void ) = 0;

GeneralsMD/Code/GameEngine/Source/GameNetwork/ConnectionManager.cpp

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,25 +1280,8 @@ void ConnectionManager::updateRunAhead(Int oldRunAhead, Int frameRate, Bool didS
12801280

12811281
DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::updateRunAhead - minFps after adjustment is %d", minFps));
12821282
Int newRunAhead = 0;
1283-
if (TheNGMPGame == nullptr)
1284-
{
1285-
newRunAhead = (Int)((getMaximumLatency() / 2.0) * (Real)minFps);
1286-
NetworkLog(ELogVerbosity::LOG_DEBUG, "New run ahead is %d, formula is maxlat is %f (div 2: %f), minfps is %d", newRunAhead, getMaximumLatency(), getMaximumLatency() / 2.f, minFps);
1287-
}
1288-
else
1289-
{
1290-
// for NGMP use our data, its more accurate
1291-
NetworkMesh* pMesh = NGMP_OnlineServicesManager::GetInstance()->GetLobbyInterface()->GetNetworkMesh();
1292-
1293-
if (pMesh != nullptr)
1294-
{
1295-
float maxLatency = (float)pMesh->getMaximumLatency();
1296-
newRunAhead = (Int)ceil((maxLatency / (1000.f / TheNetwork->getFrameRate())));
1297-
1298-
NetworkLog(ELogVerbosity::LOG_RELEASE, "New run ahead is %d, formula is maxlat is %f, minfps is %d", newRunAhead, maxLatency, minFps);
1299-
}
1300-
1301-
}
1283+
newRunAhead = (Int)((getMaximumLatency() / 2.0) * (Real)minFps);
1284+
NetworkLog(ELogVerbosity::LOG_RELEASE, "New run ahead is %d, formula is maxlat is %f (div 2: %f), minfps is %d", newRunAhead, getMaximumLatency(), getMaximumLatency() / 2.f, minFps);
13021285

13031286
newRunAhead += (newRunAhead * TheGlobalData->m_networkRunAheadSlack) / 100; // Add in 10% of slack to the run ahead in case of network hiccups.
13041287

@@ -1411,7 +1394,29 @@ void ConnectionManager::updateRunAhead(Int oldRunAhead, Int frameRate, Bool didS
14111394
if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
14121395
msg->setID(GenerateNextCommandID());
14131396
}
1397+
1398+
#if defined(GENERALS_ONLINE)
1399+
if (TheNGMPGame != nullptr)
1400+
{
1401+
NetworkMesh* pMesh = NGMP_OnlineServicesManager::GetInstance()->GetLobbyInterface()->GetNetworkMesh();
1402+
1403+
if (pMesh != nullptr)
1404+
{
1405+
float maxLatency = (float)pMesh->getMaximumHistoricalLatency();
1406+
msg->setAverageLatency(maxLatency / 1000.f);
1407+
}
1408+
else
1409+
{
1410+
msg->setAverageLatency(m_frameMetrics.getAverageLatency());
1411+
}
1412+
}
1413+
else
1414+
{
1415+
msg->setAverageLatency(m_frameMetrics.getAverageLatency());
1416+
}
1417+
#else
14141418
msg->setAverageLatency(m_frameMetrics.getAverageLatency());
1419+
#endif
14151420

14161421
// see above for explanation.
14171422
// if (didSelfSlug) {

GeneralsMD/Code/GameEngine/Source/GameNetwork/FrameMetrics.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,6 @@ void FrameMetrics::init() {
8787
}
8888

8989
m_averageLatency = (Real)((Real)totalLatency / 1000.f) / (Real)connections.size();
90-
91-
m_averageLatency = (Real)0.2;
9290
}
9391
else
9492
{
@@ -175,4 +173,10 @@ Real FrameMetrics::getAverageLatency() {
175173

176174
Int FrameMetrics::getMinimumCushion() {
177175
return m_minimumCushion;
178-
}
176+
}
177+
178+
void FrameMetrics::SeedLatencyData(int latency)
179+
{
180+
m_averageFps = GENERALS_ONLINE_HIGH_FPS_LIMIT;
181+
m_averageLatency = latency / 1000.f;
182+
}

GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/NGMPGame.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,13 @@ void NGMPGame::launchGame(void)
392392
// Time to initialize TheNetwork for this game.
393393
TheNetwork = NetworkInterface::createNetwork();
394394
TheNetwork->init();
395+
396+
NetworkMesh* pMesh = NGMP_OnlineServicesManager::GetInstance()->GetLobbyInterface()->GetNetworkMesh();
397+
if (pMesh != nullptr)
398+
{
399+
400+
TheNetwork->SeedLatencyData(pMesh->getMaximumHistoricalLatency());
401+
}
395402

396403
// TODO_NGMP: Do we really care about these values anymore
397404
TheNetwork->setLocalAddress(getLocalIP(), 8888);

0 commit comments

Comments
 (0)