https://github.com/libsdl-org/Maelstrom/commit/9b4a7d3c7279cf59f2e1228114f9e36a934954ff
From 9b4a7d3c7279cf59f2e1228114f9e36a934954ff Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 6 Nov 2011 18:04:26 -0500
Subject: [PATCH] Implemented ping system to catch players that dropped out and
give a network health indicator.
---
Images/ping1.bmp | Bin 0 -> 2742 bytes
Images/ping2.bmp | Bin 0 -> 2742 bytes
Images/ping3.bmp | Bin 0 -> 2742 bytes
UI/lobby.xml | 102 ++++++++++++++++++-------
netlogic/gameinfo.cpp | 111 ++++++++++++++++++++++++---
netlogic/gameinfo.h | 47 +++++++++++-
netlogic/lobby.cpp | 173 +++++++++++++++++++++++++++++++++++++++---
netlogic/lobby.h | 4 +
netlogic/netplay.cpp | 2 +-
netlogic/protocol.h | 15 +++-
10 files changed, 399 insertions(+), 55 deletions(-)
create mode 100644 Images/ping1.bmp
create mode 100644 Images/ping2.bmp
create mode 100644 Images/ping3.bmp
diff --git a/Images/ping1.bmp b/Images/ping1.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..b9c84ffd70e2968381980e326b82876e7a44346b
GIT binary patch
literal 2742
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
Y<<}q7KdNr49T~<d90b;WrY#5iAD~)a+W-In
literal 0
HcmV?d00001
diff --git a/Images/ping2.bmp b/Images/ping2.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..40a242832f5972a8661e13a87e3762e3cf076efa
GIT binary patch
literal 2742
zcmbW1dr*{B6u`-u`lGU@f2t`f8&d&+fkq%hK!b{5hAFhrkV$05;SoCEB2XZ&l*a__
zlz_{76`_C(WD}MJ7iSk2me+2;E)EOAhy-KMx+@Qrh3(w$>~~prNt^C`-}%lx_nhB3
z_w{`g(Db|w)7(JX!n+yXP4GTr^EyZyn=}~jcnM#|0Agiz^{J0xWd$Ci(WqN|Ff=y4
z3YyV~#;3-bI~e{w7XS|>q&{cA^94uuXSX_K6qaBL^Y9gD<^+rG>gu|DtUQo&YUAF(
z?T2IE3yyxv|A5Uq_8A2v#gl**d}a}dKQ^p{Sr*OI%&T8U?9937UDEtveuKk_tT+9F
zHafU7e3ScHAZvJx!(i8~A0TxdJ$9)@!B=_{+Xjxc{IRE0xa|mM!&c|X*}qLw02VFG
zLTAdx%>qdqwE*G9@R$qt*OcojUf)<&$M9#hGP~%s4R&si01B(hPMn4yXU)#6JQQzU
zKX%aqDZC5nV?@dy<s(V$L%#fLZy!3oFXmgY69^kfb&SF;nHT$@opw<HH{jgS-uEpN
z`c$;-E)l*FneK6v1E&-o@Bs%?2vrbh)wSj$9Qe>4EgPCuw+clviE0EbC<z=<<ij+y
zusTlA0#1xf<$c2=L7DlF1Idi;YIO(Cmd|Q+pyL!knxn95ni`YQA_W90)x(>Q#Jgnj
zKRH`DI6g_UG%M60l`Zj-k$_zO^j~wxHx&<`b?ax30lGm5(7Y3J-cLL4n#FhL*17!D
z@NtnKtft++tSO|vE3&Eogh-hvR^^F0!4sIIVD<qB>TC70#{ewL1_Sczx`(#J<+*2H
z_<&pM!Ta5lFZQ`4*<U9+e5E_`YF}KdGFGUV&@I9#U8dzh@%Z3GXqPPjikbX#TC3Bo
zFh4S>=C)fF-yy5giznDy(dJhr`J(p5*Nr_P4e})M;N68KT(pThQ*+vT5J46h$rgO3
z0C3ja8h_a<!11f_#f1foeP}S0O8P%7xV)#l)#rkEe~mPv?z(t%3PyneQmq*c;_`#C
z&+GMiG@7TNn+`*|`Gr?J{o(p=k4Ux+iI(;%z)0Kt{QNz=p5{e+b(1KpsFtU^T{18k
z$}NX$f#-y*KeOg^Q@~%lu=Ju|@U~EneN>7=Z04r0#Lypj@<BCCVXidFdQX;ApA^V)
z<)iuC8eZ>cv2p?&C=e8!2;M}{VN|LbdF?=yZRDvP$1)r_**g-r&fk}LW|e){C@Ja~
zsFDv>$OdzSonaM%l-9mD@eur%vO9+Jdo%?-8tu|UQa^ympNVWVG*S{uW_O@BIy37W
zmGo|SvQz9Cr}$i#)I#^%YS(iOyJ3H;rTgk+2Y>5|XjYIT+M-BqA4->~@;iqgEE^ye
z;FLzlgw3R2G!QroP}2A{_TQv#Iey0Nbm^|#Dv$hnuQHME#rDsCkp^Fu9ToJ(TpJ1(
zbjJxguPSenXd^i}l!gkGM1hyd1rn|Rgup2+ZWQg}oOVtxcFwDDKG*2VYx1dR4g6UW
ze5oy}qF$s>>+jElC!T){C%IS-)j=UTdFg!C)S2mNxpJVjyI<JV*WN#%P~V)Jon<0S
zKOa*NeAsAf1^uZsts3!X7m$uIis_zO0nrQjB%fJ>A?#5f+Fd^f>xY4CZ;fbf$I&N9
z2(uVPi?y9%*B!Ee{EsfpOYZ_rL=|hS^$_JyXF5X|0yz0aFn>0hh<aKp|AmkH2V0N8
Fe*iMDTs;5)
literal 0
HcmV?d00001
diff --git a/Images/ping3.bmp b/Images/ping3.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..7ecfd82523886bb8c488ceb68966680609aaf1fa
GIT binary patch
literal 2742
zcmc&!T}YEr7+!*2Bz7sGDT1OI5>0dyr*5|SgCL?46CDa(Imqx2S&YoQkuJOt!i*$V
z-nAmZa7Jb^(|$}_ITI91(lCaFmRZg@d(V0Io$vT<sdpXD_xhaYJn!?q=j{7DTR3=B
zMu!68NqA!5IRVcR*>Q+6SsUy>KZXw_AnfdT51by)4$R%%-Szc9PUk1D7vL_E2j=nu
zkLRDYz_7CNa!5!@Xs9wc_;h>wO)SBu?~uVeNS5A!{@8hO@uje^;)H~TxVZCCQRif`
zQ&?hOLy14R1geLK0Gc7!GC%({JiIm~<&IiyOHc1lO6rJ=EDH-$!WLCxAmTuSoe2CH
z76F)8trLlfH!3RLm`v~M>fUKIL-FxVK|yDhmcG*x=!_97@R+pCCToB(;4n6}AeY~3
zZJp`qS?K7PEh-vMOl%7ZQn=k-7^sAUvlIX@IG)@*PIjL>10~ez2le%iuC6(=*{Rb#
ziH^QdQgRV^YC>`af6)iGm?0GqnPh?te3qAg>U2X|?QllMP(;KPm8uEeQkXOgn#C*z
z0xz)*K}<Ag4*oFQZja6OvAcWRZl6O2Bh{lbL>!RIhVMiYb9z6d^U2A%va)M?4GHgZ
zt(TYIT3uZS3~gXDG;7awaotMaA4qgKW@BPn<Z?@HZr}9u602gfeJ~gvwY1C?7Ft$T
zzM((sSU3?9l3zr8m%vS}zM7nTJvG&$R9fZo{+yhFnwqBt1%n2|OQUhBvC&~N&2)CY
zz#~8voaoPCa$Q0X<dwriL6+6(jE(J7DsQKy-Bqa`W@e6PwRXLJyt?|e(Ky-M>}Y6s
zySTVX8}(&DF17MCk4rRX$43ApfYH&<3WX&psV^&QAUk_FKi^(jI#yLRVKBTho2Ol_
z-=F~#J{La<p^(@u@Pdq-0x*`?+Vb@FPUYp@*JvJVwIh1{Xl?DYk&!PT02!LITm%rY
zXNWKvDfp9AmcG4B4gzCiV`FXY$L8iQT;eN78vz4fZv+VxmY_=?iHSlKQ=m!7B?ubw
zSMU}&Rzjryfl$e9;<<dpK0Yjw0nQ{a^jH3Of7Sp1e!v0vC1QUP8Gj!1{1)KD49wz{
zybsR<W?}{;0{^AI@<4x<f(OF790`yNtc4gOuS9{r3^eo;fd3WzCYl9^OvLsi&d39a
zJbpD0=~qm?AmpixgUs%*!1v?g_ly2Q6>=6@Nc3Wr7+`zI@DaA)I+-WB@sXZI{7=q%
P@)%3_Ig{Q52ZH|s2vxwF
literal 0
HcmV?d00001
diff --git a/UI/lobby.xml b/UI/lobby.xml
index 35787853..e2745581 100644
--- a/UI/lobby.xml
+++ b/UI/lobby.xml
@@ -25,7 +25,7 @@
<Area name="gamelist">
<Elements>
- <DialogLabel name="ping_label" text="Ping">
+ <DialogLabel name="ping_label">
<Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" x="398" y="46"/>
</DialogLabel>
@@ -42,9 +42,15 @@
<DialogLabel name="host" text="bwg101.corp.pacbell.net">
<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="join" x="120"/>
</DialogLabel>
- <DialogLabel name="ping" text="32ms">
- <Anchor anchorFrom="TOP" anchorTo="BOTTOM" anchor="ping_label" y="12"/>
- </DialogLabel>
+ <Image name="ping1" image="Images/ping1.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping_label" y="12"/>
+ </Image>
+ <Image name="ping2" image="Images/ping2.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
+ <Image name="ping3" image="Images/ping3.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
</Elements>
</Area>
@@ -61,9 +67,15 @@
<DialogLabel name="host" text="bwg101.corp.pacbell.net">
<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="join" x="120"/>
</DialogLabel>
- <DialogLabel name="ping" text="32ms">
- <Anchor anchorFrom="TOP" anchorTo="BOTTOM" anchor="ping_label" y="42"/>
- </DialogLabel>
+ <Image name="ping1" image="Images/ping1.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping_label" y="42"/>
+ </Image>
+ <Image name="ping2" image="Images/ping2.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
+ <Image name="ping3" image="Images/ping3.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
</Elements>
</Area>
@@ -80,9 +92,15 @@
<DialogLabel name="host" text="bwg101.corp.pacbell.net">
<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="join" x="120"/>
</DialogLabel>
- <DialogLabel name="ping" text="32ms">
- <Anchor anchorFrom="TOP" anchorTo="BOTTOM" anchor="ping_label" y="72"/>
- </DialogLabel>
+ <Image name="ping1" image="Images/ping1.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping_label" y="72"/>
+ </Image>
+ <Image name="ping2" image="Images/ping2.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
+ <Image name="ping3" image="Images/ping3.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
</Elements>
</Area>
@@ -99,9 +117,15 @@
<DialogLabel name="host" text="bwg101.corp.pacbell.net">
<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="join" x="120"/>
</DialogLabel>
- <DialogLabel name="ping" text="32ms">
- <Anchor anchorFrom="TOP" anchorTo="BOTTOM" anchor="ping_label" y="102"/>
- </DialogLabel>
+ <Image name="ping1" image="Images/ping1.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping_label" y="102"/>
+ </Image>
+ <Image name="ping2" image="Images/ping2.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
+ <Image name="ping3" image="Images/ping3.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
</Elements>
</Area>
@@ -118,17 +142,23 @@
<DialogLabel name="host" text="bwg101.corp.pacbell.net">
<Anchor anchorFrom="LEFT" anchorTo="RIGHT" anchor="join" x="120"/>
</DialogLabel>
- <DialogLabel name="ping" text="32ms">
- <Anchor anchorFrom="TOP" anchorTo="BOTTOM" anchor="ping_label" y="132"/>
- </DialogLabel>
+ <Image name="ping1" image="Images/ping1.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping_label" y="132"/>
+ </Image>
+ <Image name="ping2" image="Images/ping2.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
+ <Image name="ping3" image="Images/ping3.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
</Elements>
</Area>
</Elements>
</Area>
<Area name="gameinfo">
<Elements>
- <DialogLabel name="ping_label" text="Ping">
- <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" x="398" y="46"/>
+ <DialogLabel name="ping_label">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" x="398" y="50"/>
</DialogLabel>
<Area name="player1">
@@ -157,9 +187,15 @@
<DialogLabel name="host" text="bwg101.corp.pacbell.net">
<Anchor anchorFrom="TOPLEFT" anchorTo="BOTTOMLEFT" anchor="name" y="2"/>
</DialogLabel>
- <DialogLabel name="ping" text="32ms">
- <Anchor anchorFrom="TOP" anchorTo="BOTTOM" anchor="ping_label" y="12"/>
- </DialogLabel>
+ <Image name="ping1" image="Images/ping1.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping_label" y="12"/>
+ </Image>
+ <Image name="ping2" image="Images/ping2.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
+ <Image name="ping3" image="Images/ping3.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
</Elements>
</Area>
@@ -189,9 +225,15 @@
<DialogLabel name="host" text="192.168.0.101">
<Anchor anchorFrom="TOPLEFT" anchorTo="BOTTOMLEFT" anchor="name" y="2"/>
</DialogLabel>
- <DialogLabel name="ping" text="77ms">
- <Anchor anchorFrom="TOP" anchorTo="BOTTOM" anchor="ping_label" y="58"/>
- </DialogLabel>
+ <Image name="ping1" image="Images/ping1.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping_label" y="58"/>
+ </Image>
+ <Image name="ping2" image="Images/ping2.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
+ <Image name="ping3" image="Images/ping3.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
</Elements>
</Area>
@@ -221,9 +263,15 @@
<DialogLabel name="host" text="192.168.0.101">
<Anchor anchorFrom="TOPLEFT" anchorTo="BOTTOMLEFT" anchor="name" y="2"/>
</DialogLabel>
- <DialogLabel name="ping" text="18ms">
- <Anchor anchorFrom="TOP" anchorTo="BOTTOM" anchor="ping_label" y="104"/>
- </DialogLabel>
+ <Image name="ping1" image="Images/ping1.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping_label" y="104"/>
+ </Image>
+ <Image name="ping2" image="Images/ping2.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
+ <Image name="ping3" image="Images/ping3.bmp">
+ <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ping1"/>
+ </Image>
</Elements>
</Area>
</Elements>
diff --git a/netlogic/gameinfo.cpp b/netlogic/gameinfo.cpp
index bcb873de..48c23ca4 100644
--- a/netlogic/gameinfo.cpp
+++ b/netlogic/gameinfo.cpp
@@ -37,8 +37,9 @@ void
GameInfo::SetHostInfo(Uint32 gameID, const char *name)
{
this->gameID = gameID;
- players[0].playerID = gameID;
- SDL_strlcpy(players[0].name, name, sizeof(players[0].name));
+ players[HOST_PLAYER].playerID = gameID;
+ SDL_strlcpy(players[HOST_PLAYER].name, name,
+ sizeof(players[HOST_PLAYER].name));
}
void
@@ -48,10 +49,15 @@ GameInfo::CopyFrom(const GameInfo &rhs)
deathMatch = rhs.deathMatch;
for (int i = 0; i < MAX_PLAYERS; ++i) {
+ players[i].playerID = rhs.players[i].playerID;
+ SDL_strlcpy(players[i].name, rhs.players[i].name,
+ sizeof(players[i].name));
if (players[i].address != rhs.players[i].address) {
- // FIXME: reset the ping info
+ players[i].address = rhs.players[i].address;
+
+ // Reset the ping info
+ InitializePing(i);
}
- players[i] = rhs.players[i];
}
UpdateUI();
}
@@ -83,8 +89,8 @@ GameInfo::ReadFromPacket(DynamicPacket &packet)
// We want to get the public address of the server
// If we already have one, we assume that's the fastest interface
- if (!players[0].address.host) {
- players[0].address = packet.address;
+ if (!players[HOST_PLAYER].address.host) {
+ players[HOST_PLAYER].address = packet.address;
}
return true;
@@ -107,17 +113,27 @@ GameInfo::WriteToPacket(DynamicPacket &packet)
void
GameInfo::BindPlayerToUI(int index, UIElement *element)
{
+ char name[32];
GameInfoPlayer *player = &players[index];
+ if (player->UI.element == element) {
+ return;
+ }
+
+ player->UI.element = element;
player->UI.enabled = element->GetElement<UIElementCheckbox>("enabled");
player->UI.name = element->GetElement<UIElement>("name");
player->UI.host = element->GetElement<UIElement>("host");
- player->UI.ping = element->GetElement<UIElement>("ping");
player->UI.control = element->GetElement<UIElementRadioGroup>("control");
player->UI.keyboard = element->GetElement<UIElement>("keyboard");
player->UI.joystick = element->GetElement<UIElement>("joystick");
player->UI.network = element->GetElement<UIElement>("network");
+ for (int i = 0; i < NUM_PING_STATES; ++i) {
+ SDL_snprintf(name, sizeof(name), "ping%d", i);
+ player->UI.ping_states[i] = element->GetElement<UIElement>(name);
+ }
+
UpdateUI(player);
}
@@ -152,10 +168,6 @@ GameInfo::UpdateUI(GameInfoPlayer *player)
player->UI.host->Hide();
}
}
- if (player->UI.ping) {
- // FIXME: not yet implemented
- player->UI.ping->Hide();
- }
if (player->UI.control) {
if (player->playerID == localID) {
player->UI.control->SetValue(CONTROL_KEYBOARD);
@@ -184,4 +196,81 @@ GameInfo::UpdateUI(GameInfoPlayer *player)
player->UI.network->Hide();
}
}
+ for (int i = 0; i < NUM_PING_STATES; ++i) {
+ UIElement *element = player->UI.ping_states[i];
+ if (element) {
+ if (player->ping.status == i) {
+ element->Show();
+ } else {
+ element->Hide();
+ }
+ }
+ }
+}
+
+void
+GameInfo::UpdatePingTime(int index, Uint32 timestamp)
+{
+ Uint32 now;
+ Uint32 elapsed;
+ GameInfoPlayer *player;
+
+ now = SDL_GetTicks();
+ elapsed = (now - timestamp);
+
+ player = GetPlayer(index);
+ player->ping.lastPing = now;
+ player->ping.roundTripTime = elapsed;
+}
+
+void
+GameInfo::InitializePing(int index)
+{
+ if (IsNetworkPlayer(index)) {
+ GameInfoPlayer *player = GetPlayer(index);
+ player->ping.lastPing = SDL_GetTicks();
+ player->ping.roundTripTime = 0;
+ player->ping.status = PING_GOOD;
+ }
+}
+
+void
+GameInfo::UpdatePingStatus()
+{
+ Uint32 now;
+ Uint32 sinceLastPing;
+
+ now = SDL_GetTicks();
+ for (int i = 0; i < MAX_PLAYERS; ++i) {
+ GameInfoPlayer *player = GetPlayer(i);
+ if (!IsNetworkPlayer(i)) {
+ player->ping.status = PING_LOCAL;
+ continue;
+ }
+
+ sinceLastPing = int(now - player->ping.lastPing);
+ if (sinceLastPing < PING_INTERVAL) {
+ if (player->ping.roundTripTime <= 48) {
+//printf("Game 0x%8.8x: player 0x%8.8x round trip time %d (GOOD)\n", gameID, player->playerID, player->ping.roundTripTime);
+ player->ping.status = PING_GOOD;
+ } else if (player->ping.roundTripTime <= 64) {
+//printf("Game 0x%8.8x: player 0x%8.8x round trip time %d (OKAY)\n", gameID, player->playerID, player->ping.roundTripTime);
+ player->ping.status = PING_OKAY;
+ } else {
+//printf("Game 0x%8.8x: player 0x%8.8x round trip time %d (BAD)\n", gameID, player->playerID, player->ping.roundTripTime);
+ player->ping.status = PING_BAD;
+ }
+ } else if (sinceLastPing < 2*PING_INTERVAL) {
+//printf("Game 0x%8.8x: player 0x%8.8x since last ping %d (OKAY)\n", gameID, player->playerID, sinceLastPing);
+ player->ping.status = PING_OKAY;
+ } else if (sinceLastPing < PING_TIMEOUT) {
+//printf("Game 0x%8.8x: player 0x%8.8x since last ping %d (BAD)\n", gameID, player->playerID, sinceLastPing);
+ player->ping.status = PING_BAD;
+ } else {
+//printf("Game 0x%8.8x: player 0x%8.8x since last ping %d (TIMEDOUT)\n", gameID, player->playerID, sinceLastPing);
+ player->ping.status = PING_TIMEDOUT;
+ }
+
+ UpdateUI(player);
+ }
}
diff --git a/netlogic/gameinfo.h b/netlogic/gameinfo.h
index da505a4e..ec00323e 100644
--- a/netlogic/gameinfo.h
+++ b/netlogic/gameinfo.h
@@ -36,6 +36,15 @@ enum {
CONTROL_NETWORK,
};
+enum PING_STATUS {
+ PING_LOCAL,
+ PING_GOOD,
+ PING_OKAY,
+ PING_BAD,
+ PING_TIMEDOUT,
+ NUM_PING_STATES
+};
+
struct GameInfoPlayer
{
Uint32 playerID;
@@ -43,14 +52,21 @@ struct GameInfoPlayer
char name[MAX_NAMELEN+1];
struct {
+ Uint32 lastPing;
+ Uint32 roundTripTime;
+ PING_STATUS status;
+ } ping;
+
+ struct {
+ UIElement *element;
UIElementCheckbox *enabled;
UIElement *name;
UIElement *host;
- UIElement *ping;
UIElementRadioGroup *control;
UIElement *keyboard;
UIElement *joystick;
UIElement *network;
+ UIElement *ping_states[NUM_PING_STATES];
} UI;
};
@@ -78,7 +94,7 @@ class GameInfo
void WriteToPacket(DynamicPacket &packet);
GameInfoPlayer *GetHost() {
- return GetPlayer(0);
+ return GetPlayer(HOST_PLAYER);
}
GameInfoPlayer *GetPlayer(int index) {
return &players[index];
@@ -109,6 +125,16 @@ class GameInfo
return false;
}
+ bool IsNetworkPlayer(int index) {
+ if (!players[index].playerID) {
+ return false;
+ }
+ if (players[index].playerID == localID) {
+ return false;
+ }
+ return true;
+ }
+
bool IsFull() {
for (int i = 0; i < MAX_PLAYERS; ++i) {
if (!players[i].playerID) {
@@ -122,6 +148,23 @@ class GameInfo
void UpdateUI();
void UpdateUI(GameInfoPlayer *player);
+ void InitializePing() {
+ for (int i = 0; i < MAX_PLAYERS; ++i) {
+ InitializePing(i);
+ }
+ }
+ void InitializePing(int index);
+ void UpdatePingTime(int index, Uint32 timestamp);
+ void UpdatePingStatus();
+
+ PING_STATUS GetPingStatus(int index) {
+ if (IsNetworkPlayer(index)) {
+ return players[index].ping.status;
+ } else {
+ return PING_LOCAL;
+ }
+ }
+
public:
Uint32 gameID;
Uint8 deathMatch;
diff --git a/netlogic/lobby.cpp b/netlogic/lobby.cpp
index 959b5e3d..ac6818a9 100644
--- a/netlogic/lobby.cpp
+++ b/netlogic/lobby.cpp
@@ -35,7 +35,6 @@
// Update the game list every 3 seconds
//#define GLOBAL_SERVER_HOST "obelix.dreamhost.com"
#define GLOBAL_SERVER_HOST "localhost"
-#define GLOBAL_CHECK_INTERVAL 3000
LobbyDialogDelegate::LobbyDialogDelegate(UIPanel *panel) :
@@ -43,6 +42,7 @@ LobbyDialogDelegate::LobbyDialogDelegate(UIPanel *panel) :
{
m_state = STATE_NONE;
m_uniqueID = 0;
+ m_lastPing = 0;
m_lastRefresh = 0;
m_requestSequence = 1;
}
@@ -174,7 +174,7 @@ LobbyDialogDelegate::OnTick()
Uint32 now = SDL_GetTicks();
if (!m_lastRefresh ||
- (now - m_lastRefresh) > GLOBAL_CHECK_INTERVAL) {
+ (now - m_lastRefresh) > PING_INTERVAL) {
if (m_state == STATE_HOSTING) {
AdvertiseGame();
} else if (m_state == STATE_LISTING) {
@@ -193,6 +193,12 @@ LobbyDialogDelegate::OnTick()
ProcessPacket(m_packet);
m_packet.Reset();
}
+
+ // Do this after processing packets in case a pong was pending
+ if (!m_lastPing || (now - m_lastPing) > PING_INTERVAL) {
+ CheckPings();
+ m_lastPing = now;
+ }
}
void
@@ -261,7 +267,7 @@ LobbyDialogDelegate::UpdateUI()
for (int i = 0; (unsigned)i < SDL_arraysize(m_gameListElements); ++i) {
if (i < m_gameList.length()) {
m_gameListElements[i]->Show();
- m_gameList[i].BindPlayerToUI(0, m_gameListElements[i]);
+ m_gameList[i].BindPlayerToUI(HOST_PLAYER, m_gameListElements[i]);
} else {
m_gameListElements[i]->Hide();
}
@@ -310,11 +316,68 @@ LobbyDialogDelegate::SetState(LOBBY_STATE state)
// Update the UI for the new state
UpdateUI();
+ m_lastPing = 0;
+
// Send any packet requests immediately
// Comment this out to simulate initial packet loss
m_lastRefresh = 0;
}
+void
+LobbyDialogDelegate::CheckPings()
+{
+ // Check for ping timeouts
+ if (m_state == STATE_LISTING) {
+ bool removed = false;
+ int i = 0;
+ while (i < m_gameList.length()) {
+ GameInfo &game = m_gameList[i];
+ game.UpdatePingStatus();
+ if (game.GetPingStatus(HOST_PLAYER) == PING_TIMEDOUT) {
+//printf("Game timed out, removing from list\n");
+ m_gameList.remove(game);
+ removed = true;
+ } else {
+ ++i;
+ }
+ }
+ if (removed) {
+ UpdateUI();
+ }
+ } else if (m_state == STATE_HOSTING) {
+ m_game.UpdatePingStatus();
+ for (int i = 0; i < MAX_PLAYERS; ++i) {
+ if (m_game.GetPingStatus(i) == PING_TIMEDOUT) {
+//printf("Player timed out, removing from lobby\n");
+ SendKick(i);
+ }
+ }
+ } else if (m_state == STATE_JOINED) {
+ m_game.UpdatePingStatus();
+ if (m_game.GetPingStatus(HOST_PLAYER) == PING_TIMEDOUT) {
+//printf("Game timed out, leaving lobbyn");
+ SetState(STATE_LISTING);
+ }
+ }
+
+ if (m_state == STATE_HOSTING || m_state == STATE_JOINED) {
+
+ // Send pings to everyone who is still here
+ m_packet.StartLobbyMessage(LOBBY_PING);
+ m_packet.Write(m_game.gameID);
+ m_packet.Write(m_uniqueID);
+ m_packet.Write(SDL_GetTicks());
+
+ for (int i = 0; i < MAX_PLAYERS; ++i) {
+ if (m_game.IsNetworkPlayer(i)) {
+ m_packet.address = m_game.GetPlayer(i)->address;
+
+ SDLNet_UDP_Send(gNetFD, -1, &m_packet);
+ }
+ }
+ }
+}
+
void
LobbyDialogDelegate::AdvertiseGame()
{
@@ -350,6 +413,7 @@ LobbyDialogDelegate::GetGameList()
// Get game info for local games
m_packet.StartLobbyMessage(LOBBY_REQUEST_GAME_INFO);
+ m_packet.Write(SDL_GetTicks());
m_packet.address.host = INADDR_BROADCAST;
m_packet.address.port = SDL_SwapBE16(NETPLAY_PORT);
SDLNet_UDP_Send(gNetFD, -1, &m_packet);
@@ -359,6 +423,7 @@ void
LobbyDialogDelegate::GetGameInfo()
{
m_packet.StartLobbyMessage(LOBBY_REQUEST_GAME_INFO);
+ m_packet.Write(SDL_GetTicks());
m_packet.address = m_game.GetHost()->address;
SDLNet_UDP_Send(gNetFD, -1, &m_packet);
}
@@ -367,6 +432,7 @@ void
LobbyDialogDelegate::JoinGame(GameInfo &game)
{
m_game.CopyFrom(game);
+ m_game.InitializePing();
SetState(STATE_JOINING);
}
@@ -398,17 +464,23 @@ LobbyDialogDelegate::SendKick(int index)
{
GameInfoPlayer *player;
- player = m_game.GetPlayer(index);
- if (!player->playerID || player->playerID == m_uniqueID) {
+ if (!m_game.IsNetworkPlayer(index)) {
return;
}
+ player = m_game.GetPlayer(index);
m_packet.StartLobbyMessage(LOBBY_KICK);
m_packet.Write(m_game.gameID);
m_packet.Write(player->playerID);
m_packet.address = player->address;
SDLNet_UDP_Send(gNetFD, -1, &m_packet);
+
+ // Now remove them from the game list
+ SDL_zero(*player);
+
+ // Update our own UI
+ UpdateUI();
}
void
@@ -468,7 +540,9 @@ LobbyDialogDelegate::ProcessPacket(DynamicPacket &packet)
}
if (cmd == LOBBY_PING) {
- //ProcessPing(packet);
+ ProcessPing(packet);
+ } else if (cmd == LOBBY_PONG) {
+ ProcessPong(packet);
} else if (cmd == LOBBY_REQUEST_GAME_INFO) {
ProcessRequestGameInfo(packet);
} else if (cmd == LOBBY_REQUEST_JOIN) {
@@ -491,8 +565,9 @@ LobbyDialogDelegate::ProcessPacket(DynamicPacket &packet)
// These packets we handle in all the join states
if (cmd == LOBBY_PING) {
- // Somebody thinks we're still in a game lobby
- //RejectPing(packet);
+ ProcessPing(packet);
+ } else if (cmd == LOBBY_PONG) {
+ ProcessPong(packet);
} else if (cmd == LOBBY_GAME_INFO) {
ProcessGameInfo(packet);
} else if (cmd == LOBBY_KICK) {
@@ -500,6 +575,62 @@ LobbyDialogDelegate::ProcessPacket(DynamicPacket &packet)
}
}
+void
+LobbyDialogDelegate::ProcessPing(DynamicPacket &packet)
+{
+ Uint32 gameID;
+ Uint32 playerID;
+ Uint32 timestamp;
+
+ if (m_state != STATE_HOSTING && m_state != STATE_JOINED) {
+ return;
+ }
+ if (!packet.Read(gameID) || gameID != m_game.gameID) {
+ return;
+ }
+ if (!packet.Read(playerID) || !m_game.HasPlayer(playerID)) {
+ return;
+ }
+ if (!packet.Read(timestamp)) {
+ return;
+ }
+
+ m_reply.StartLobbyMessage(LOBBY_PONG);
+ m_reply.Write(gameID);
+ m_reply.Write(playerID);
+ m_reply.Write(timestamp);
+ m_reply.address = packet.address;
+
+ SDLNet_UDP_Send(gNetFD, -1, &m_reply);
+}
+
+void
+LobbyDialogDelegate::ProcessPong(DynamicPacket &packet)
+{
+ Uint32 gameID;
+ Uint32 playerID;
+ Uint32 timestamp;
+
+ if (m_state != STATE_HOSTING && m_state != STATE_JOINED) {
+ return;
+ }
+ if (!packet.Read(gameID) || gameID != m_game.gameID) {
+ return;
+ }
+ if (!packet.Read(playerID) || playerID != m_uniqueID) {
+ return;
+ }
+ if (!packet.Read(timestamp)) {
+ return;
+ }
+
+ for (int i = 0; i < MAX_PLAYERS; ++i) {
+ if (packet.address == m_game.players[i].address) {
+ m_game.UpdatePingTime(i, timestamp);
+ }
+ }
+}
+
void
LobbyDialogDelegate::ProcessNewGame(DynamicPacket &packet)
{
@@ -535,7 +666,14 @@ LobbyDialogDelegate::ProcessAnnouncePlayer(DynamicPacket &packet)
void
LobbyDialogDelegate::ProcessRequestGameInfo(DynamicPacket &packet)
{
+ Uint32 timestamp;
+
+ if (!packet.Read(timestamp)) {
+ return;
+ }
+
m_reply.StartLobbyMessage(LOBBY_GAME_INFO);
+ m_reply.Write(timestamp);
m_game.WriteToPacket(m_reply);
m_reply.address = packet.address;
@@ -581,15 +719,15 @@ LobbyDialogDelegate::ProcessRequestJoin(DynamicPacket &packet)
player->playerID = playerID;
player->address = packet.address;
SDL_strlcpy(player->name, name, sizeof(player->name));
+ m_game.InitializePing(slot);
// Let everybody know!
m_reply.StartLobbyMessage(LOBBY_GAME_INFO);
+ m_reply.Write((Uint32)0);
m_game.WriteToPacket(m_reply);
for (slot = 0; slot < MAX_PLAYERS; ++slot) {
- GameInfoPlayer *player = m_game.GetPlayer(slot);
- Uint32 playerID = player->playerID;
- if (playerID && playerID != m_uniqueID) {
- m_reply.address = player->address;
+ if (m_game.IsNetworkPlayer(slot)) {
+ m_reply.address = m_game.players[slot].address;
SDLNet_UDP_Send(gNetFD, -1, &m_reply);
}
}
@@ -622,8 +760,13 @@ LobbyDialogDelegate::ProcessRequestLeave(DynamicPacket &packet)
void
LobbyDialogDelegate::ProcessGameInfo(DynamicPacket &packet)
{
+ Uint32 timestamp;
GameInfo game;
+ if (!packet.Read(timestamp)) {
+ return;
+ }
+
if (!game.ReadFromPacket(packet)) {
return;
}
@@ -638,8 +781,13 @@ LobbyDialogDelegate::ProcessGameInfo(DynamicPacket &packet)
}
}
if (i == m_gameList.length()) {
+ game.InitializePing();
m_gameList.add(game);
}
+ if (timestamp) {
+ m_gameList[i].UpdatePingTime(HOST_PLAYER, timestamp);
+ m_gameList[i].UpdatePingStatus();
+ }
} else {
if (game.gameID != m_game.gameID) {
// Probably an old packet...
@@ -691,6 +839,7 @@ LobbyDialogDelegate::ProcessGameServerList(DynamicPacket &packet)
// Request game information from the servers
m_reply.StartLobbyMessage(LOBBY_REQUEST_GAME_INFO);
+ m_reply.Write(SDL_GetTicks());
if (!packet.Read(serverCount)) {
return;
diff --git a/netlogic/lobby.h b/netlogic/lobby.h
index 877bc346..9e6e3d62 100644
--- a/netlogic/lobby.h
+++ b/netlogic/lobby.h
@@ -57,6 +57,7 @@ class LobbyDialogDelegate : public UIDialogDelegate
void UpdateUI();
+ void CheckPings();
void AdvertiseGame();
void RemoveGame();
void GetGameList();
@@ -71,6 +72,8 @@ class LobbyDialogDelegate : public UIDialogDelegate
void PackAddresses(DynamicPacket &packet);
void ProcessPacket(DynamicPacket &packet);
+ void ProcessPing(DynamicPacket &packet);
+ void ProcessPong(DynamicPacket &packet);
void ProcessNewGame(DynamicPacket &packet);
void ProcessAnnouncePlayer(DynamicPacket &packet);
void ProcessRequestGameInfo(DynamicPacket &packet);
@@ -94,6 +97,7 @@ class LobbyDialogDelegate : public UIDialogDelegate
} m_state;
Uint32 m_uniqueID;
+ Uint32 m_lastPing;
Uint32 m_lastRefresh;
Uint32 m_requestSequence;
diff --git a/netlogic/netplay.cpp b/netlogic/netplay.cpp
index 6af5016e..b4a63107 100644
--- a/netlogic/netplay.cpp
+++ b/netlogic/netplay.cpp
@@ -172,7 +172,7 @@ int CheckPlayers(void)
}
/* Add ourselves if needed */
if ( gNumPlayers == 0 ) {
- AddLocalPlayer(0);
+ AddLocalPlayer(HOST_PLAYER);
gNumPlayers = 1;
FoundUs = 1;
}
diff --git a/netlogic/protocol.h b/netlogic/protocol.h
index 40185b26..08654dab 100644
--- a/netlogic/protocol.h
+++ b/netlogic/protocol.h
@@ -111,7 +111,7 @@ enum LobbyProtocol {
Uint32 gameID
Uint32 playerID
- Uint32 sequence
+ Uint32 timestamp
*/
LOBBY_PONG,
@@ -119,16 +119,19 @@ enum LobbyProtocol {
Uint32 gameID
Uint32 playerID
- Uint32 sequence
+ Uint32 timestamp
*/
LOBBY_REQUEST_GAME_INFO,
/* Sent by the joining game to get info for the game list
+
+ Uint32 timestamp
*/
LOBBY_GAME_INFO,
/* Sent by the hosting game, if there are slots open
+ Uint32 timestamp
Uint32 gameID
Uint8 deathMatch;
Uint32 player1_uniqueID;
@@ -195,6 +198,14 @@ enum LobbyProtocol {
*/
#define MAX_PLAYERS 3
+/* The index of the player hosting the game */
+#define HOST_PLAYER 0
+
+/* If the other side hasn't responded in 3 seconds, we'll drop them */
+#define PING_INTERVAL 1000
+#define
(Patch may be truncated, please check the link at the top of this post.)