Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions GameFrameX.Client/Bot/BotClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@

using GameFrameX.NetWork.Messages;
using GameFrameX.Proto.Proto;
using GameFrameX.Utility.Log;

using ErrorEventArgs = GameFrameX.SuperSocket.ClientEngine.ErrorEventArgs;

namespace GameFrameX.Bot;

public class BotClient
{
private readonly BotTcpClient m_TcpClient;
private readonly BotHttpClient m_HttpClient;
private readonly string m_BotName;
private readonly BotTcpClientEvent m_BotTcpClientEvent;
private const string m_LoginUrl = "http://127.0.0.1:29001/game/api/";

public BotClient(string botName)
{
m_BotName = botName;
m_BotTcpClientEvent.OnConnectedCallback += ClientConnectedCallback;
m_BotTcpClientEvent.OnClosedCallback += ClientClosedCallback;
m_BotTcpClientEvent.OnErrorCallback += ClientErrorCallback;
m_BotTcpClientEvent.OnReceiveMsgCallback += ClientReceiveCallback;
m_TcpClient = new BotTcpClient(m_BotTcpClientEvent);
m_HttpClient = new BotHttpClient();
}

public async Task EntryAsync()
{
try
{

await m_TcpClient.EntryAsync();
}
catch (Exception e)
{
LogHelper.Info($"EntryAsync Error: {e.Message}| Thread ID:{Thread.CurrentThread.ManagedThreadId} ");
}
}

private void OnReceiveMsg(MessageObject messageObject)
{
switch (messageObject)
{
case RespPlayerLogin msg:
OnPlayerLoginSuccess(msg);
break;
}
}

#region 连接状态回调

private void ClientConnectedCallback()
{
SendLoginMessage();
}

private void ClientClosedCallback()
{
}

private void ClientErrorCallback(ErrorEventArgs error)
{
}

private void ClientReceiveCallback(MessageObject outerMsg)
{
OnReceiveMsg(outerMsg);
}

#endregion

#region 消息发送


private async void SendLoginMessage()
{
try
{
//请求登录验证
var reqLogin = new ReqLogin
{
UserName = m_BotName,
Password = "12312",
// Platform = LoginPlatform.Custom,
// PlatformLoginMethod = LoginVerificationMethod.Password
};

string respLoginUrl = $"{m_LoginUrl}{nameof(ReqLogin)}";
var respLogin = await m_HttpClient.Post<RespLogin>(respLoginUrl, reqLogin);
if (respLogin.ErrorCode != 0)
{
LogHelper.Error("请求登录验证,错误信息:" + respLogin.ErrorCode);
return;
}

LogHelper.Info($"机器人-{m_BotName}账号验证成功,id:{ respLogin.Id}");

//请求角色列表
var reqPlayerList = new ReqPlayerList();
reqPlayerList.Id = respLogin.Id;
string reqPlayerListUrl = $"{m_LoginUrl}{nameof(ReqPlayerList)}";
var respPlayerList = await m_HttpClient.Post<RespPlayerList>(reqPlayerListUrl, reqPlayerList);

if (respPlayerList.ErrorCode != 0)
{
LogHelper.Error("请求角色列表,错误信息:" + respPlayerList.ErrorCode);
return;
}

PlayerInfo player;
if (respPlayerList.PlayerList.Count == 0)
{
LogHelper.Info("角色列表为空");

//请求创建角色
var reqCreatePlayer = new ReqPlayerCreate();
reqCreatePlayer.Id = respLogin.Id;

string reqCreatePlayerUrl = $"{m_LoginUrl}{nameof(ReqPlayerCreate)}";
var respPlayerCreator = await m_HttpClient.Post<RespPlayerCreate>(reqCreatePlayerUrl,
reqCreatePlayer);
if (respPlayerCreator.ErrorCode != 0)
{
LogHelper.Error("请求创建角色,错误信息:" + respPlayerCreator.ErrorCode);
return;
}

player = respPlayerCreator.PlayerInfo;
LogHelper.Info($"创建角色 Id:{player.Id}-昵称:{player.Name}-等级:{player.Level}-角色状态:{player.State}");
}
else
{
player = respPlayerList.PlayerList[0];
LogHelper.Info($"角色列表 Id:{player.Id}-昵称:{player.Name}-等级:{player.Level}-角色状态:{player.State}");
}

var reqPlayerLogin = new ReqPlayerLogin();
reqPlayerLogin.Id = player.Id;
// reqPlayerLogin.Name = m_BotName;
m_TcpClient.SendToServer(reqPlayerLogin);
}
catch (Exception e)
{
LogHelper.Error($"SendLoginMessage Error: {e.Message}| Thread ID:{Thread.CurrentThread.ManagedThreadId} ");
}
}

#endregion


#region 消息接收

private void OnPlayerLoginSuccess(RespPlayerLogin msg)
{
LogHelper.Info($"机器人-{m_BotName}登录成功,id:{msg.PlayerInfo.Id}");
}

#endregion
}
117 changes: 117 additions & 0 deletions GameFrameX.Client/Bot/BotHttpClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System.Net.Http.Headers;
using System.Text;
using GameFrameX.NetWork.Abstractions;
using GameFrameX.NetWork.Messages;
using GameFrameX.Utility.Log;

namespace GameFrameX.Bot;

public class BotHttpClient
{
private readonly HttpClient m_HttpClient = new();
private static readonly StringBuilder m_StringBuilder = new(256);

/// <summary>
/// 发送Post请求。
/// </summary>
/// <param name="url">目标服务器的URL地址。</param>
/// <param name="message">要发送的消息对象,必须继承自MessageObject。</param>
/// <typeparam name="T">返回的数据类型,必须继承自MessageObject并且实现IResponseMessage接口。</typeparam>
/// <returns>返回一个任务对象,该任务完成时将包含从服务器接收到的响应数据,数据类型为T。</returns>
/// <remarks>
/// 此方法用于向指定的URL发送POST请求,并接收响应。请求的消息体由参数message提供,而响应则会被解析为指定的泛型类型T。
/// </remarks>
public async Task<T> Post<T>(string url, MessageObject message) where T : MessageObject, IResponseMessage
{
var webBufferResult = await PostInner(url, message);
if (webBufferResult.IsSuccessStatusCode)
{
var messageObjectHttp =
SerializerHelper.Deserialize<MessageHttpObject>(await webBufferResult.Content.ReadAsByteArrayAsync());
if (messageObjectHttp != null && messageObjectHttp.Id != default)
{
var messageType = MessageProtoHelper.GetMessageTypeById(messageObjectHttp.Id);
if (messageType != typeof(T))
{
LogHelper.Error(
$"Response message type is invalid. Expected '{typeof(T).FullName}', actual '{messageType.FullName}'.");
return default!;
}

return SerializerHelper.Deserialize<T>(messageObjectHttp.Body);
}
}
else
{
throw new Exception($"Failed to post data. Status code: {webBufferResult.StatusCode}");
}

return default!;
}

private Task<HttpResponseMessage> PostInner(string url, MessageObject message, object userData = null!)
{
url = UrlHandler(url);
var id = MessageProtoHelper.GetMessageIdByType(message.GetType());
MessageHttpObject messageHttpObject = new MessageHttpObject
{
Id = id,
UniqueId = message.UniqueId,
Body = SerializerHelper.Serialize(message),
};
var sendData = SerializerHelper.Serialize(messageHttpObject);
var content = new ByteArrayContent(sendData);
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-protobuf");
content.Headers.ContentLength = sendData.Length;
return m_HttpClient.PostAsync(url, content);
}

/// <summary>
/// URL 标准化
/// </summary>
/// <param name="url"></param>
/// <param name="queryString"></param>
/// <returns></returns>
private string UrlHandler(string url, Dictionary<string, string> queryString = null!)
{
m_StringBuilder.Clear();
m_StringBuilder.Append((url));
if (queryString != null && queryString.Count > 0)
{
if (!EndsWithFast(url, "?"))
{
m_StringBuilder.Append("?");
}

foreach (var kv in queryString)
{
m_StringBuilder.AppendFormat("{0}={1}&", kv.Key, kv.Value);
}

url = m_StringBuilder.ToString(0, m_StringBuilder.Length - 1);
m_StringBuilder.Clear();
}

return url;
}

/// <summary>
/// 判断字符串是否以目标字符串结尾
/// </summary>
/// <param name="self"></param>
/// <param name="target">目标字符串</param>
/// <returns></returns>
private bool EndsWithFast(string self, string target)
{
int ap = self.Length - 1;
int bp = target.Length - 1;

while (ap >= 0 && bp >= 0 && self[ap] == target[bp])
{
ap--;
bp--;
}

return (bp < 0);
}
}
Loading