Skip to content

Commit df2ebc0

Browse files
committed
Refactor game advertisement lifecycle
1 parent 1847765 commit df2ebc0

10 files changed

Lines changed: 182 additions & 97 deletions

File tree

src/Atlasd/Battlenet/Common.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public ShutdownEvent(string adminMessage, bool cancelled, DateTime eventDate, Ti
4242
public static ConcurrentDictionary<string, Channel> ActiveChannels;
4343
public static ConcurrentDictionary<byte[], Clan> ActiveClans;
4444
public static ConcurrentDictionary<Socket, ClientState> ActiveClientStates;
45-
public static ConcurrentDictionary<byte[], GameAd> ActiveGameAds;
45+
public static List<GameAd> ActiveGameAds;
4646
public static ConcurrentDictionary<string, GameState> ActiveGameStates;
4747
public static IPAddress DefaultAddress { get; private set; }
4848
public static int DefaultPort { get; private set; }
@@ -102,7 +102,7 @@ public static void Initialize()
102102
ActiveChannels = new ConcurrentDictionary<string, Channel>(StringComparer.OrdinalIgnoreCase);
103103
ActiveClans = new ConcurrentDictionary<byte[], Clan>();
104104
ActiveClientStates = new ConcurrentDictionary<Socket, ClientState>();
105-
ActiveGameAds = new ConcurrentDictionary<byte[], GameAd>();
105+
ActiveGameAds = new List<GameAd>();
106106
ActiveGameStates = new ConcurrentDictionary<string, GameState>(StringComparer.OrdinalIgnoreCase);
107107

108108
InitializeAds();

src/Atlasd/Battlenet/Protocols/Game/GameAd.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ public enum StateFlags : UInt32
5656
public UInt32 GamePort { get; private set; }
5757
public GameTypes GameType { get; private set; }
5858
public UInt32 GameVersion { get; private set; }
59-
public byte[] Name { get; private set; }
6059
public LocaleInfo Locale { get; private set; }
60+
public byte[] Name { get; private set; }
61+
public GameState Owner { get => Clients != null && Clients.Count > 0 ? Clients[0] : null; }
6162
public byte[] Password { get; private set; }
6263
public Product.ProductCode Product { get; private set; }
6364
public byte[] Statstring { get; private set; }
@@ -98,13 +99,6 @@ public bool RemoveClient(GameState client)
9899
{
99100
bool removed = false;
100101
lock (Clients) removed = Clients.Remove(client);
101-
if (Clients.Count == 0)
102-
{
103-
if (!Battlenet.Common.ActiveGameAds.TryRemove(Name, out _))
104-
{
105-
Logging.WriteLine(Logging.LogLevel.Error, Logging.LogType.GameAd, $"Failed to remove game ad [{Encoding.ASCII.GetString(Name)}] from active game ad cache");
106-
}
107-
}
108102
return removed;
109103
}
110104

src/Atlasd/Battlenet/Protocols/Game/GameState.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ public void Close()
156156
}
157157

158158
// Remove this GameAd
159-
StopGameAd(true);
159+
if (GameAd != null && GameAd.RemoveClient(this)) GameAd = null;
160160
}
161161

162162
public void Dispose() /* part of IDisposable */
@@ -385,11 +385,5 @@ public void SetLocale()
385385
}
386386
*/
387387
}
388-
389-
public void StopGameAd(bool leavingGame)
390-
{
391-
if (GameAd == null) return;
392-
if (leavingGame && GameAd.RemoveClient(this)) GameAd = null;
393-
}
394388
}
395389
}

src/Atlasd/Battlenet/Protocols/Game/Messages/SID_GETADVLISTEX.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,27 @@ public override bool Invoke(MessageContext context)
5858

5959
var gameAds = new List<GameAd>();
6060

61-
foreach (var _pair in Battlenet.Common.ActiveGameAds)
61+
lock (Battlenet.Common.ActiveGameAds)
6262
{
63-
var _ad = _pair.Value;
63+
foreach (var gameAd in Battlenet.Common.ActiveGameAds)
64+
{
65+
if (gameAd.Clients.Count == 0)
66+
{
67+
Battlenet.Common.ActiveGameAds.Remove(gameAd);
68+
continue;
69+
}
6470

65-
if (_ad.Clients.Count == 0) continue;
66-
if (_ad.Product != context.Client.GameState.Product) continue;
71+
if (gameAd.Product != context.Client.GameState.Product) continue;
6772

68-
if (viewingFilter == 0xFFFF || viewingFilter == 0x30)
69-
{
70-
if (gameType != 0 && gameType != (ushort)_ad.GameType) continue;
71-
if (subGameType != 0 && subGameType != _ad.SubGameType) continue;
72-
}
73-
else if (viewingFilter == 0xFF80) { }
73+
if (viewingFilter == 0xFFFF || viewingFilter == 0x30)
74+
{
75+
if (gameType != 0 && gameType != (ushort)gameAd.GameType) continue;
76+
if (subGameType != 0 && subGameType != gameAd.SubGameType) continue;
77+
}
78+
else if (viewingFilter == 0xFF80) { }
7479

75-
gameAds.Add(_ad);
80+
gameAds.Add(gameAd);
81+
}
7682
}
7783

7884
return new SID_GETADVLISTEX().Invoke(new MessageContext(context.Client, MessageDirection.ServerToClient, new Dictionary<string, dynamic>(){

src/Atlasd/Battlenet/Protocols/Game/Messages/SID_LEAVEGAME.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ public override bool Invoke(MessageContext context)
3131
if (Buffer.Length != 0)
3232
throw new GameProtocolViolationException(context.Client, $"{MessageName(Id)} buffer must be 0 bytes, got {Buffer.Length}");
3333

34-
context.Client.GameState.StopGameAd(true);
34+
GameState gs = context.Client.GameState;
35+
if (gs.GameAd != null && gs.GameAd.RemoveClient(gs)) gs.GameAd = null;
36+
3537
return true;
3638
}
3739
}

src/Atlasd/Battlenet/Protocols/Game/Messages/SID_NOTIFYJOIN.cs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using Atlasd.Daemon;
33
using System;
44
using System.IO;
5+
using System.Linq;
6+
using System.Text;
57

68
namespace Atlasd.Battlenet.Protocols.Game.Messages
79
{
@@ -35,16 +37,24 @@ public override bool Invoke(MessageContext context)
3537

3638
var productId = r.ReadUInt32();
3739
var productVersion = r.ReadUInt32();
38-
var gameName = r.ReadString();
39-
var gamePassword = r.ReadString();
40+
var gameName = r.ReadByteString();
41+
var gamePassword = r.ReadByteString();
4042

41-
try
43+
if (context.Client.GameState.ActiveChannel != null)
44+
context.Client.GameState.ActiveChannel.RemoveUser(context.Client.GameState);
45+
46+
lock (Battlenet.Common.ActiveGameAds)
4247
{
43-
lock (context.Client.GameState.ActiveChannel)
44-
context.Client.GameState.ActiveChannel.RemoveUser(context.Client.GameState);
48+
foreach (var gameAd in Battlenet.Common.ActiveGameAds)
49+
{
50+
if (gameAd.Name.SequenceEqual(gameName))
51+
{
52+
if (gameAd.HasClient(context.Client.GameState) || gameAd.AddClient(context.Client.GameState))
53+
context.Client.GameState.GameAd = gameAd;
54+
break;
55+
}
56+
}
4557
}
46-
catch (ArgumentNullException) { }
47-
catch (NullReferenceException) { }
4858

4959
return true;
5060
}

src/Atlasd/Battlenet/Protocols/Game/Messages/SID_STARTADVEX.cs

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
using Atlasd.Battlenet.Exceptions;
22
using Atlasd.Daemon;
33
using System;
4+
using System.Collections.Generic;
45
using System.IO;
6+
using System.Linq;
57

68
namespace Atlasd.Battlenet.Protocols.Game.Messages
79
{
810
class SID_STARTADVEX : Message
911
{
12+
public enum Statuses : UInt32
13+
{
14+
Failed = 0,
15+
Success = 1,
16+
};
17+
1018
public SID_STARTADVEX()
1119
{
1220
Id = (byte)MessageIds.SID_STARTADVEX;
@@ -58,47 +66,63 @@ public override bool Invoke(MessageContext context)
5866
var gamePassword = r.ReadByteString();
5967
var gameStatstring = r.ReadByteString();
6068

61-
var gameAds = Battlenet.Common.ActiveGameAds.ToArray();
69+
Statuses status = Statuses.Failed;
6270
GameAd gameAd = null;
6371

64-
foreach (var _pair in gameAds)
72+
lock (Battlenet.Common.ActiveGameAds)
6573
{
66-
if (_pair.Value != null && _pair.Value.HasClient(context.Client.GameState))
74+
foreach (GameAd _ad in Battlenet.Common.ActiveGameAds)
75+
{
76+
if (_ad.Name.SequenceEqual(gameName))
77+
{
78+
gameAd = _ad;
79+
break;
80+
}
81+
}
82+
83+
if (gameAd == null)
6784
{
68-
gameAd = _pair.Value;
69-
break;
85+
gameAd = new GameAd(context.Client.GameState, gameName, gamePassword, gameStatstring, 6112, (GameAd.GameTypes)gameType, subGameType, context.Client.GameState.Version.VersionByte);
86+
gameAd.AddClient(context.Client.GameState);
87+
Battlenet.Common.ActiveGameAds.Add(gameAd);
7088
}
7189
}
7290

73-
if (gameAd == null)
91+
if (gameAd.HasClient(context.Client.GameState)) context.Client.GameState.GameAd = gameAd;
92+
93+
bool gameAdOwner = gameAd != null && gameAd.Owner == context.Client.GameState;
94+
if (!gameAdOwner)
7495
{
75-
gameAd = new GameAd(context.Client.GameState, gameName, gamePassword, gameStatstring, 6112, (GameAd.GameTypes)gameType, subGameType, context.Client.GameState.Version.VersionByte);
76-
context.Client.GameState.GameAd = gameAd;
77-
Battlenet.Common.ActiveGameAds.TryAdd(gameName, gameAd);
96+
status = Statuses.Failed;
97+
}
98+
else
99+
{
100+
status = Statuses.Success;
101+
gameAd.SetActiveStateFlags((GameAd.StateFlags)gameState);
102+
gameAd.SetElapsedTime(gameElapsedTime);
103+
gameAd.SetGameType((GameAd.GameTypes)gameType);
104+
gameAd.SetName(gameName);
105+
gameAd.SetPassword(gamePassword);
106+
gameAd.SetPort(6112);
107+
gameAd.SetStatstring(gameStatstring);
78108
}
79109

80-
gameAd.SetActiveStateFlags((GameAd.StateFlags)gameState);
81-
gameAd.SetElapsedTime(gameElapsedTime);
82-
gameAd.SetGameType((GameAd.GameTypes)gameType);
83-
gameAd.SetName(gameName);
84-
gameAd.SetPassword(gamePassword);
85-
gameAd.SetPort(6112);
86-
gameAd.SetStatstring(gameStatstring);
87-
88-
return new SID_STARTADVEX().Invoke(new MessageContext(context.Client, MessageDirection.ServerToClient));
110+
return new SID_STARTADVEX().Invoke(new MessageContext(context.Client, MessageDirection.ServerToClient, new Dictionary<string, dynamic>(){{ "status", status }}));
89111
}
90112
case MessageDirection.ServerToClient:
91113
{
114+
var status = context.Arguments.ContainsKey("status") ? (Statuses)context.Arguments["status"] : Statuses.Failed;
115+
92116
/**
93-
* (UINT32) Status (0x00 Failed, 0x01 Success)
117+
* (UINT32) Status
94118
*/
95119

96120
Buffer = new byte[4];
97121

98122
using var m = new MemoryStream(Buffer);
99123
using var w = new BinaryWriter(m);
100124

101-
w.Write((UInt32)1); // success = 1
125+
w.Write((UInt32)status);
102126

103127
Logging.WriteLine(Logging.LogLevel.Debug, Logging.LogType.Client_Game, context.Client.RemoteEndPoint, $"[{Common.DirectionToString(context.Direction)}] {MessageName(Id)} ({4 + Buffer.Length} bytes)");
104128
context.Client.Send(ToByteArray(context.Client.ProtocolType));

src/Atlasd/Battlenet/Protocols/Game/Messages/SID_STARTADVEX2.cs

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
using Atlasd.Battlenet.Exceptions;
22
using Atlasd.Daemon;
33
using System;
4+
using System.Collections.Generic;
45
using System.IO;
6+
using System.Linq;
57

68
namespace Atlasd.Battlenet.Protocols.Game.Messages
79
{
810
class SID_STARTADVEX2 : Message
911
{
12+
public enum Statuses : UInt32
13+
{
14+
Failed = 0,
15+
Success = 1,
16+
};
17+
1018
public SID_STARTADVEX2()
1119
{
1220
Id = (byte)MessageIds.SID_STARTADVEX2;
@@ -58,47 +66,63 @@ public override bool Invoke(MessageContext context)
5866
var gamePassword = r.ReadByteString();
5967
var gameStatstring = r.ReadByteString();
6068

61-
var gameAds = Battlenet.Common.ActiveGameAds.ToArray();
69+
Statuses status = Statuses.Failed;
6270
GameAd gameAd = null;
6371

64-
foreach (var _pair in gameAds)
72+
lock (Battlenet.Common.ActiveGameAds)
6573
{
66-
if (_pair.Value != null && _pair.Value.HasClient(context.Client.GameState))
74+
foreach (GameAd _ad in Battlenet.Common.ActiveGameAds)
75+
{
76+
if (_ad.Name.SequenceEqual(gameName))
77+
{
78+
gameAd = _ad;
79+
break;
80+
}
81+
}
82+
83+
if (gameAd == null)
6784
{
68-
gameAd = _pair.Value;
69-
break;
85+
gameAd = new GameAd(context.Client.GameState, gameName, gamePassword, gameStatstring, 6112, (GameAd.GameTypes)gameType, subGameType, context.Client.GameState.Version.VersionByte);
86+
gameAd.AddClient(context.Client.GameState);
87+
Battlenet.Common.ActiveGameAds.Add(gameAd);
7088
}
7189
}
7290

73-
if (gameAd == null)
91+
if (gameAd.HasClient(context.Client.GameState)) context.Client.GameState.GameAd = gameAd;
92+
93+
bool gameAdOwner = gameAd != null && gameAd.Owner == context.Client.GameState;
94+
if (!gameAdOwner)
7495
{
75-
gameAd = new GameAd(context.Client.GameState, gameName, gamePassword, gameStatstring, 6112, (GameAd.GameTypes)gameType, subGameType, context.Client.GameState.Version.VersionByte);
76-
context.Client.GameState.GameAd = gameAd;
77-
Battlenet.Common.ActiveGameAds.TryAdd(gameName, gameAd);
96+
status = Statuses.Failed;
97+
}
98+
else
99+
{
100+
status = Statuses.Success;
101+
gameAd.SetActiveStateFlags((GameAd.StateFlags)gameState);
102+
gameAd.SetElapsedTime(gameElapsedTime);
103+
gameAd.SetGameType((GameAd.GameTypes)gameType);
104+
gameAd.SetName(gameName);
105+
gameAd.SetPassword(gamePassword);
106+
gameAd.SetPort(6112);
107+
gameAd.SetStatstring(gameStatstring);
78108
}
79109

80-
gameAd.SetActiveStateFlags((GameAd.StateFlags)gameState);
81-
gameAd.SetElapsedTime(gameElapsedTime);
82-
gameAd.SetGameType((GameAd.GameTypes)gameType);
83-
gameAd.SetName(gameName);
84-
gameAd.SetPassword(gamePassword);
85-
gameAd.SetPort(portNumber);
86-
gameAd.SetStatstring(gameStatstring);
87-
88-
return new SID_STARTADVEX2().Invoke(new MessageContext(context.Client, MessageDirection.ServerToClient));
110+
return new SID_STARTADVEX2().Invoke(new MessageContext(context.Client, MessageDirection.ServerToClient, new Dictionary<string, dynamic>(){{ "status", status }}));
89111
}
90112
case MessageDirection.ServerToClient:
91113
{
114+
var status = context.Arguments.ContainsKey("status") ? (Statuses)context.Arguments["status"] : Statuses.Failed;
115+
92116
/**
93-
* (UINT32) Status (0x00 Failed, 0x01 Success)
117+
* (UINT32) Status
94118
*/
95119

96120
Buffer = new byte[4];
97121

98122
using var m = new MemoryStream(Buffer);
99123
using var w = new BinaryWriter(m);
100124

101-
w.Write((UInt32)1); // success = 1
125+
w.Write((UInt32)status);
102126

103127
Logging.WriteLine(Logging.LogLevel.Debug, Logging.LogType.Client_Game, context.Client.RemoteEndPoint, $"[{Common.DirectionToString(context.Direction)}] {MessageName(Id)} ({4 + Buffer.Length} bytes)");
104128
context.Client.Send(ToByteArray(context.Client.ProtocolType));

0 commit comments

Comments
 (0)