Skip to content

Commit 13d4fed

Browse files
committed
feat(bot): 添加好友场景测试流程
- Bot 客户端支持 friend 场景,自动执行加好友、列表、删好友流程 - BotRunOptions 新增 Scenario 参数,支持逗号分隔多场景 - 添加在线玩家注册表用于好友配对 - 预热好友相关 Proto 序列化类型
1 parent f4bcfe5 commit 13d4fed

3 files changed

Lines changed: 186 additions & 3 deletions

File tree

GameFrameX.Client/Bot/BotClient.cs

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
using GameFrameX.NetWork.Messages;
3131
using GameFrameX.Proto.Proto;
3232
using GameFrameX.Foundation.Logger;
33+
using System.Collections.Concurrent;
3334
using ErrorEventArgs = GameFrameX.SuperSocket.ClientEngine.ErrorEventArgs;
3435

3536
namespace GameFrameX.Client.Bot;
@@ -39,17 +40,34 @@ namespace GameFrameX.Client.Bot;
3940
/// </summary>
4041
public sealed class BotClient
4142
{
43+
private static readonly ConcurrentDictionary<string, long> OnlinePlayerIds = new(StringComparer.Ordinal);
44+
45+
private enum FriendScenarioStage
46+
{
47+
None = 0,
48+
WaitAdd = 1,
49+
WaitListAfterAdd = 2,
50+
WaitDelete = 3,
51+
WaitListAfterDelete = 4,
52+
Completed = 5,
53+
}
54+
4255
private readonly BotTcpClient m_TcpClient;
4356
private readonly string m_BotName;
4457
private readonly BotTcpClientEvent m_BotTcpClientEvent;
4558
private readonly BotRunOptions _options;
4659
private int _disconnectScheduled;
4760
private long _accountId;
61+
private long _playerId;
62+
private long _friendTargetPlayerId;
63+
private int _friendTargetResolveAttempts;
64+
private FriendScenarioStage _friendScenarioStage;
4865

4966
/// <summary>
5067
/// 初始化机器人客户端
5168
/// </summary>
5269
/// <param name="botName">机器人名称</param>
70+
/// <param name="options">运行参数</param>
5371
public BotClient(string botName, BotRunOptions options)
5472
{
5573
m_BotName = botName;
@@ -97,6 +115,15 @@ private void OnReceiveMsg(MessageObject messageObject)
97115
case RespPlayerLogin msg:
98116
OnPlayerLoginSuccess(msg);
99117
break;
118+
case RespFriendByAdd msg:
119+
OnFriendAddSuccess(msg);
120+
break;
121+
case RespDeleteFriend msg:
122+
OnDeleteFriendSuccess(msg);
123+
break;
124+
case RespFriendList msg:
125+
OnFriendListSuccess(msg);
126+
break;
100127
}
101128
}
102129

@@ -115,6 +142,10 @@ private void ClientConnectedCallback()
115142
/// </summary>
116143
private void ClientClosedCallback()
117144
{
145+
if (!string.IsNullOrWhiteSpace(m_BotName))
146+
{
147+
OnlinePlayerIds.TryRemove(m_BotName, out _);
148+
}
118149
}
119150

120151
/// <summary>
@@ -220,10 +251,135 @@ private void OnPlayerCreateSuccess(RespPlayerCreate msg)
220251
/// <param name="msg">登录成功的响应消息</param>
221252
private void OnPlayerLoginSuccess(RespPlayerLogin msg)
222253
{
223-
LogHelper.Info($"机器人-{m_BotName}登录成功,id:{msg.PlayerInfo.Id}");
254+
_playerId = msg.PlayerInfo.Id;
255+
OnlinePlayerIds[m_BotName] = _playerId;
256+
LogHelper.Info($"机器人-{m_BotName}登录成功,id:{_playerId}");
257+
if (_options.HasScenario("friend"))
258+
{
259+
StartFriendScenario();
260+
return;
261+
}
262+
224263
ScheduleDisconnectIfNeeded();
225264
}
226265

266+
private void StartFriendScenario()
267+
{
268+
if (_playerId <= 0)
269+
{
270+
LogHelper.Error($"机器人-{m_BotName}好友场景启动失败,玩家ID非法:{_playerId}");
271+
ScheduleDisconnectIfNeeded();
272+
return;
273+
}
274+
275+
if (!TryResolveFriendTargetPlayerId())
276+
{
277+
_friendTargetResolveAttempts++;
278+
if (_friendTargetResolveAttempts > 5)
279+
{
280+
LogHelper.Error($"机器人-{m_BotName}好友场景启动失败,未找到可用好友目标。");
281+
ScheduleDisconnectIfNeeded();
282+
return;
283+
}
284+
285+
_ = Task.Run(async () =>
286+
{
287+
await Task.Delay(1000);
288+
StartFriendScenario();
289+
});
290+
return;
291+
}
292+
293+
_friendScenarioStage = FriendScenarioStage.WaitAdd;
294+
LogHelper.Info($"机器人-{m_BotName}开始执行好友场景,目标玩家:{_friendTargetPlayerId}");
295+
m_TcpClient.SendToServer(new ReqFriendByAdd { PlayerId = _friendTargetPlayerId });
296+
}
297+
298+
private void OnFriendAddSuccess(RespFriendByAdd msg)
299+
{
300+
if (_friendScenarioStage != FriendScenarioStage.WaitAdd)
301+
{
302+
return;
303+
}
304+
305+
if (!msg.Success || msg.ErrorCode != 0)
306+
{
307+
LogHelper.Error($"机器人-{m_BotName}好友场景-加好友失败,Success:{msg.Success}, ErrorCode:{msg.ErrorCode}");
308+
ScheduleDisconnectIfNeeded();
309+
return;
310+
}
311+
312+
_friendScenarioStage = FriendScenarioStage.WaitListAfterAdd;
313+
LogHelper.Info($"机器人-{m_BotName}好友场景-加好友成功,开始拉取好友列表。");
314+
m_TcpClient.SendToServer(new ReqFriendList());
315+
}
316+
317+
private void OnDeleteFriendSuccess(RespDeleteFriend msg)
318+
{
319+
if (_friendScenarioStage != FriendScenarioStage.WaitDelete)
320+
{
321+
return;
322+
}
323+
324+
if (!msg.Success || msg.ErrorCode != 0)
325+
{
326+
LogHelper.Error($"机器人-{m_BotName}好友场景-删好友失败,Success:{msg.Success}, ErrorCode:{msg.ErrorCode}");
327+
ScheduleDisconnectIfNeeded();
328+
return;
329+
}
330+
331+
_friendScenarioStage = FriendScenarioStage.WaitListAfterDelete;
332+
LogHelper.Info($"机器人-{m_BotName}好友场景-删好友成功,开始二次拉取好友列表。");
333+
m_TcpClient.SendToServer(new ReqFriendList());
334+
}
335+
336+
private void OnFriendListSuccess(RespFriendList msg)
337+
{
338+
if (_friendScenarioStage != FriendScenarioStage.WaitListAfterAdd
339+
&& _friendScenarioStage != FriendScenarioStage.WaitListAfterDelete)
340+
{
341+
return;
342+
}
343+
344+
if (msg.ErrorCode != 0)
345+
{
346+
LogHelper.Error($"机器人-{m_BotName}好友场景-拉取列表失败,ErrorCode:{msg.ErrorCode}");
347+
ScheduleDisconnectIfNeeded();
348+
return;
349+
}
350+
351+
if (_friendScenarioStage == FriendScenarioStage.WaitListAfterAdd)
352+
{
353+
_friendScenarioStage = FriendScenarioStage.WaitDelete;
354+
LogHelper.Info($"机器人-{m_BotName}好友场景-首次列表成功,数量:{msg.Friends?.Count ?? 0},开始删好友。");
355+
m_TcpClient.SendToServer(new ReqDeleteFriend { PlayerId = _friendTargetPlayerId });
356+
return;
357+
}
358+
359+
_friendScenarioStage = FriendScenarioStage.Completed;
360+
LogHelper.Info($"机器人-{m_BotName}好友场景执行完成,二次列表数量:{msg.Friends?.Count ?? 0}。");
361+
ScheduleDisconnectIfNeeded();
362+
}
363+
364+
private bool TryResolveFriendTargetPlayerId()
365+
{
366+
foreach (var entry in OnlinePlayerIds)
367+
{
368+
if (entry.Key == m_BotName)
369+
{
370+
continue;
371+
}
372+
373+
if (entry.Value > 0 && entry.Value != _playerId)
374+
{
375+
_friendTargetPlayerId = entry.Value;
376+
return true;
377+
}
378+
}
379+
380+
return false;
381+
}
382+
227383
private void ScheduleDisconnectIfNeeded()
228384
{
229385
if (!_options.EnableDisconnectLoop || _options.DisconnectAfterLoginSeconds <= 0)

GameFrameX.Client/Bot/BotRunOptions.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public sealed class BotRunOptions
1414
public bool EnableDisconnectLoop { get; init; } = true;
1515
public int DisconnectAfterLoginSeconds { get; init; } = 15;
1616
public int RunSeconds { get; init; } = 0;
17+
public string Scenario { get; init; } = "login";
1718

1819
public static BotRunOptions Parse(string[] args)
1920
{
@@ -51,9 +52,29 @@ public static BotRunOptions Parse(string[] args)
5152
EnableDisconnectLoop = ReadBool(values, "disconnect-loop", true),
5253
DisconnectAfterLoginSeconds = ReadInt(values, "disconnect-after-login-seconds", 15),
5354
RunSeconds = ReadInt(values, "run-seconds", 0),
55+
Scenario = ReadString(values, "scenario", "login"),
5456
};
5557
}
5658

59+
public bool HasScenario(string scenarioName)
60+
{
61+
if (string.IsNullOrWhiteSpace(Scenario) || string.IsNullOrWhiteSpace(scenarioName))
62+
{
63+
return false;
64+
}
65+
66+
var parts = Scenario.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
67+
foreach (var part in parts)
68+
{
69+
if (string.Equals(part, scenarioName, StringComparison.OrdinalIgnoreCase))
70+
{
71+
return true;
72+
}
73+
}
74+
75+
return false;
76+
}
77+
5778
private static string ReadString(IDictionary<string, string> values, string key, string defaultValue)
5879
{
5980
return values.TryGetValue(key, out var value) ? value : defaultValue;

GameFrameX.Client/Program.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ static async Task Main(string[] args)
4848
WarmUpProtoSerializer();
4949

5050
LogHelper.Info(
51-
"Bot options: bot-count={botCount}, tcp={tcpHost}:{tcpPort}, disconnect-loop={disconnectLoop}, disconnect-after-login-seconds={disconnectAfterLoginSeconds}, run-seconds={runSeconds}",
52-
options.BotCount, options.TcpHost, options.TcpPort, options.EnableDisconnectLoop, options.DisconnectAfterLoginSeconds, options.RunSeconds);
51+
"Bot options: bot-count={botCount}, tcp={tcpHost}:{tcpPort}, scenario={scenario}, disconnect-loop={disconnectLoop}, disconnect-after-login-seconds={disconnectAfterLoginSeconds}, run-seconds={runSeconds}",
52+
options.BotCount, options.TcpHost, options.TcpPort, options.Scenario, options.EnableDisconnectLoop, options.DisconnectAfterLoginSeconds, options.RunSeconds);
5353

5454
var cts = new CancellationTokenSource();
5555
var botTasks = new List<Task>(options.BotCount);
@@ -89,5 +89,11 @@ private static void WarmUpProtoSerializer()
8989
_ = ProtoBufSerializerHelper.Serialize(new RespPlayerList());
9090
_ = ProtoBufSerializerHelper.Serialize(new RespPlayerCreate());
9191
_ = ProtoBufSerializerHelper.Serialize(new RespPlayerLogin());
92+
_ = ProtoBufSerializerHelper.Serialize(new ReqFriendByAdd());
93+
_ = ProtoBufSerializerHelper.Serialize(new RespFriendByAdd());
94+
_ = ProtoBufSerializerHelper.Serialize(new ReqDeleteFriend());
95+
_ = ProtoBufSerializerHelper.Serialize(new RespDeleteFriend());
96+
_ = ProtoBufSerializerHelper.Serialize(new ReqFriendList());
97+
_ = ProtoBufSerializerHelper.Serialize(new RespFriendList());
9298
}
9399
}

0 commit comments

Comments
 (0)