diff --git a/src/CloudGaming/Api/CloudGaming.Api/Controllers/PlayGameController.cs b/src/CloudGaming/Api/CloudGaming.Api/Controllers/PlayGameController.cs index 458f577..9eb4163 100644 --- a/src/CloudGaming/Api/CloudGaming.Api/Controllers/PlayGameController.cs +++ b/src/CloudGaming/Api/CloudGaming.Api/Controllers/PlayGameController.cs @@ -149,7 +149,7 @@ public class PlayGameController : CloudGamingControllerBase /// [HttpPost] [Authorize] - public async Task> ReconPlayGameAsync([FromBody] ReconPlayGameSettings reconPlayGameSettings) + public async Task> ReconPlayGameAsync([FromBody] ReconPlayGameRequest reconPlayGameSettings) { PlayGameBLL playGameBLL = new PlayGameBLL(ServiceProvider, JYApi); return await playGameBLL.ReconPlayGame(reconPlayGameSettings); diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs index 8542795..38a8847 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs @@ -149,6 +149,17 @@ namespace CloudGaming.Code.AppExtend { return RedisConnection.GetRedis(appConfig.RedisConnectionString); } + + /// + /// + /// + /// + /// + public static IServer GetRedisServer(this AppConfig appConfig) + { + return RedisConnection.GetServer(appConfig.RedisConnectionString); + } + /// /// 获取Dao /// diff --git a/src/CloudGaming/Code/CloudGaming.Code/Contract/IJYApi.cs b/src/CloudGaming/Code/CloudGaming.Code/Contract/IJYApi.cs index 9767ca9..c836fa5 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Contract/IJYApi.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Contract/IJYApi.cs @@ -62,7 +62,7 @@ namespace CloudGaming.Code.Contract Task>> DisplayGrade([Body(BodySerializationMethod.UrlEncoded)] DisplayGradeSettings playGameQueue); /// - /// 切换视频等级 + /// 重连会话 /// /// /// @@ -84,7 +84,7 @@ namespace CloudGaming.Code.Contract /// /// [Post("/jyapi/myScList")] - Task>> MyScList([Body(BodySerializationMethod.UrlEncoded)] RequestBaseModel requestBase); + Task>> MyScList([Body(BodySerializationMethod.UrlEncoded)] JYRequestParameter parameter); /// /// 获取会话列表 /// diff --git a/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameBLL.cs b/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameBLL.cs index c3a11c5..a450663 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameBLL.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameBLL.cs @@ -7,6 +7,8 @@ using CloudGaming.DtoModel.PlayGame; using Newtonsoft.Json; +using Org.BouncyCastle.Asn1.Ocsp; + namespace CloudGaming.Code.Game; @@ -481,10 +483,7 @@ public class PlayGameBLL : CloudGamingBase /// public async Task> GetMyScList(string sn) { - var requestParmat = new RequestBaseModel() - { - Sn = sn, - }; + var requestParmat = new JYRequestParameter(sn, _UserId); var response = await JYApi.MyScList(requestParmat); if (response.IsSuccess) { @@ -497,27 +496,32 @@ public class PlayGameBLL : CloudGamingBase /// /// 重连会话 /// - /// - /// - /// - /// - /// + /// /// - public async Task> ReconPlayGame(ReconPlayGameSettings reconPlayGameSettings) + public async Task> ReconPlayGame(ReconPlayGameRequest reconPlayGameRequest) { - var setting = new ReconPlayGameSettings() + var setting = new ReconPlayGameSettings(reconPlayGameRequest.Sn, _UserId) { - ScId = reconPlayGameSettings.ScId, - DisplayGrade = reconPlayGameSettings.DisplayGrade, - Cpu = reconPlayGameSettings.Cpu, - ModelName = reconPlayGameSettings.ModelName, - Sn = reconPlayGameSettings.Sn, + ScId = reconPlayGameRequest.ScId, + DisplayGrade = reconPlayGameRequest.DisplayGrade, + Cpu = reconPlayGameRequest.Cpu, + ModelName = reconPlayGameRequest.ModelName, }; - var response = await JYApi.ReconPlayGame(setting); - if (response.IsSuccess) + var jyResponseData = await JYApi.ReconPlayGame(setting); + if (jyResponseData.IsSuccess) { - return new BaseResponse(ResponseCode.Success, "", response.Data) { }; + var gameInfo = await PlayGameExtend.PlayGameRecon(this, _UserId, reconPlayGameRequest.ScId); + var gameResponse1 = JsonConvert.DeserializeObject>(jyResponseData.ResponseContent); + if (gameResponse1 != null && gameResponse1.TryGetValue("data", out var xxx)) + { + if (gameInfo != null) + { + await gameInfo.SaveChangesAsync(this); + } + return new BaseResponse(ResponseCode.Success, "", new { GameId = gameInfo?.GameId, GameData = JsonConvert.SerializeObject(xxx) }); + } + } - throw response.ToMessageBox(); + throw jyResponseData.ToMessageBox(); } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameExtend.cs index b16e0e9..7368498 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameExtend.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameExtend.cs @@ -1,4 +1,7 @@ +using AgileConfig.Client; + using CloudGaming.Code.DataAccess; +using CloudGaming.Code.DataAccess.MultiTenantUtil; using CloudGaming.DtoModel.Account.User; using CloudGaming.DtoModel.Game; using CloudGaming.DtoModel.JY; @@ -8,13 +11,35 @@ using HuanMeng.DotNetCore.Redis; using Newtonsoft.Json; +using StackExchange.Redis; + using System.Runtime.CompilerServices; namespace CloudGaming.Code.Game { public static class PlayGameExtend { - private static string GetPlayGameKey(string gameId, int userId) => $"PlayGame:{gameId}:{userId}"; + + /// + /// 初始化app + /// + /// + /// + public static IHostApplicationBuilder AddPlayGameServer(this WebApplicationBuilder builder) + { + + builder.Services.AddHostedService(); + return builder; + + } + /// + /// + /// + /// + /// + /// + public static string GetPlayGameKeyPrefix { get; set; } = $"PlayGame"; + public static string GetPlayGameKey(string gameId, int userId) => $"PlayGame:{gameId}:{userId}"; /// /// 获取用户游戏缓存 /// @@ -91,7 +116,7 @@ namespace CloudGaming.Code.Game } /// - /// 初始化 + /// 结束游戏 /// /// /// @@ -104,10 +129,10 @@ namespace CloudGaming.Code.Game if (playGameUserInfo.GameStatus != PlayGameStatus.游戏结束) { playGameUserInfo.GameStatus = PlayGameStatus.游戏结束; - playGameUserInfo.GameUserOperation.Add(new PlayGameUserOperation() + playGameUserInfo.GameUserOperation.Add(new PlayGameUserOperation(jYApiRespnse) { OperationDateTime = DateTime.Now, - Content = "游戏结束" + Content = "游戏结束", }); playGameUserInfo.LastDateTime = DateTime.Now; } @@ -176,6 +201,15 @@ namespace CloudGaming.Code.Game { if (playGameUserInfo.GameStatus != PlayGameStatus.游戏中) { + + if (playGameUserInfo.GameStatus == PlayGameStatus.游戏掉线) + { + playGameUserInfo.GameUserOperation.Add(new PlayGameUserOperation() + { + OperationDateTime = DateTime.Now, + Content = "用户游戏掉线重连成功", + }); + } playGameUserInfo.GameStatus = PlayGameStatus.游戏中; } playGameUserInfo.PlayGameHeartbeatAt = DateTime.Now; @@ -279,6 +313,80 @@ namespace CloudGaming.Code.Game return true; } + /// + /// 游戏掉线 + /// + /// + /// + public static bool PlayGameUserNotAction(this PlayGameUserInfo playGameUserInfo) + { + if (playGameUserInfo.GameStatus != PlayGameStatus.游戏掉线) + { + playGameUserInfo.GameStatus = PlayGameStatus.游戏掉线; + playGameUserInfo.GameUserOperation.Add(new PlayGameUserOperation() + { + Content = $"用户游戏掉线,上一次心跳时间{playGameUserInfo.PlayGameHeartbeatAt?.ToString("yyyy-MM-dd HH:mm:ss")};", + OperationDateTime = DateTime.Now, + }); + } + return true; + } + + /// + /// + /// + /// + /// + /// + /// + public static async Task> GetPlayGameUserInfoList(IDatabase redis, IServer redisServer, string keyPattern) + { + var keys = await redisServer.ScanKeysAsync(keyPattern, 2000).ConfigureAwait(false); + + var tasks = keys.Select(async gameKey => + { + var gameInfo = await redis.StringGetAsync(gameKey).ConfigureAwait(false); + return gameInfo; + }); + + var gameInfoList = (await Task.WhenAll(tasks).ConfigureAwait(false)).Where(x => x != null).ToList(); + return gameInfoList; + } + + + /// + /// 游戏掉线 + /// + /// + /// + public static async Task PlayGameRecon(CloudGamingBase cloudGamingBase, int userId, int scId) + { + var list = await GetPlayGameUserInfoList(cloudGamingBase.RedisCache, cloudGamingBase.RedisServerCache, $"{GetPlayGameKeyPrefix}:*:{userId}"); + if (list != null && list.Count > 0) + { + var playGameUserInfo = list.Where(it => it.ScId == scId).FirstOrDefault(); + if (playGameUserInfo == null) + { + playGameUserInfo = list[0]; + } + if (playGameUserInfo != null) + { + if (playGameUserInfo.GameStatus != PlayGameStatus.游戏掉线) + { + playGameUserInfo.GameStatus = PlayGameStatus.游戏掉线; + playGameUserInfo.GameUserOperation.Add(new PlayGameUserOperation() + { + Content = $"用户游戏掉线,上一次心跳时间{playGameUserInfo.PlayGameHeartbeatAt?.ToString("yyyy-MM-dd HH:mm:ss")};", + OperationDateTime = DateTime.Now, + }); + } + } + return playGameUserInfo; + } + + return null; + } + /// /// 保存缓存到redis中 /// @@ -286,6 +394,19 @@ namespace CloudGaming.Code.Game /// /// public static async Task SaveChangesAsync(this PlayGameUserInfo playGameUserInfo, CloudGamingBase cloudGamingBase) + { + return await SaveChangesAsync(playGameUserInfo, cloudGamingBase.Dao, cloudGamingBase.RedisCache); + } + + + /// + /// 保存缓存到redis中 + /// + /// + /// + /// + /// + public static async Task SaveChangesAsync(this PlayGameUserInfo playGameUserInfo, DAO dao, IDatabase redisDatabase) { playGameUserInfo.LastDateTime = DateTime.Now; var redisGameKey = GetPlayGameKey(playGameUserInfo.GameId, playGameUserInfo.UserId); @@ -294,7 +415,7 @@ namespace CloudGaming.Code.Game if (playGameUserInfo.DiamondListId > 0) { //设置游玩记录 - var userGameListLog = await cloudGamingBase.Dao.DaoPhone.Context.T_User_GameList.FirstOrDefaultAsync(it => it.Id == playGameUserInfo.DiamondListId); + var userGameListLog = await dao.DaoPhone.Context.T_User_GameList.FirstOrDefaultAsync(it => it.Id == playGameUserInfo.DiamondListId); if (userGameListLog != null) { userGameListLog.Status = (int)PlayGameStatus.游戏结束; @@ -304,12 +425,12 @@ namespace CloudGaming.Code.Game userGameListLog.PlaySeconds = (int)t.TotalSeconds; userGameListLog.PlayTime = playTime; userGameListLog.UpdateTime = DateTime.Now; - await cloudGamingBase.Dao.DaoPhone.Context.SaveChangesAsync(); + await dao.DaoPhone.Context.SaveChangesAsync(); //设置游玩历史 var userId = playGameUserInfo.UserId; var gameId = playGameUserInfo.GameId; //设置用户游玩历史 - var playGameTime = await cloudGamingBase.Dao.DaoPhone.Context.T_User_PlayGameTime.Where(it => it.UserId == userId && it.GameId == gameId).FirstOrDefaultAsync(); + var playGameTime = await dao.DaoPhone.Context.T_User_PlayGameTime.Where(it => it.UserId == userId && it.GameId == gameId).FirstOrDefaultAsync(); if (playGameTime == null) { playGameTime = new T_User_PlayGameTime() @@ -323,26 +444,25 @@ namespace CloudGaming.Code.Game UpdateTime = DateTime.Now, UserId = userId, }; - await cloudGamingBase.Dao.DaoPhone.Context.T_User_PlayGameTime.AddAsync(playGameTime); + await dao.DaoPhone.Context.T_User_PlayGameTime.AddAsync(playGameTime); } playGameTime.PlayTime += playTime; playGameTime.UpdateTime = DateTime.Now; playGameTime.DiamondPlayTime += playTime; - await cloudGamingBase.Dao.DaoPhone.Context.SaveChangesAsync(); + await dao.DaoPhone.Context.SaveChangesAsync(); } } var playGameLog = playGameUserInfo.ToGamePlayGameLog(); if (playGameLog != null) { - await cloudGamingBase.Dao.DaoPhone.Context.T_Game_PlayGameLog.AddAsync(playGameLog); - await cloudGamingBase.Dao.DaoPhone.Context.SaveChangesAsync(); + await dao.DaoPhone.Context.T_Game_PlayGameLog.AddAsync(playGameLog); + await dao.DaoPhone.Context.SaveChangesAsync(); } - return await cloudGamingBase.RedisCache.KeyDeleteAsync(redisGameKey); + return await redisDatabase.KeyDeleteAsync(redisGameKey); } - return await cloudGamingBase.RedisCache.StringSetAsync(redisGameKey, playGameUserInfo, TimeSpan.FromMinutes(10)); + return await redisDatabase.StringSetAsync(redisGameKey, playGameUserInfo, TimeSpan.FromMinutes(10)); } - } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameProcessor.cs b/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameProcessor.cs new file mode 100644 index 0000000..7544f1a --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameProcessor.cs @@ -0,0 +1,119 @@ +using CloudGaming.Code.AppExtend; +using CloudGaming.Code.Contract; +using CloudGaming.Code.DataAccess; +using CloudGaming.Code.JY; +using CloudGaming.DtoModel.PlayGame; + +using Flurl.Util; + +using HuanMeng.DotNetCore.Processors; + +using Microsoft.Extensions.DependencyInjection; + +using Refit; + +using StackExchange.Redis; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using static SKIT.FlurlHttpClient.Wechat.TenpayV3.Models.CreatePayDevicePrinterPrintOrderRequest.Types.Table.Types; + +namespace CloudGaming.Code.Game; + +/// +/// 游戏服务 +/// +public class PlayGameProcessor(IServiceProvider serviceProvider) : ThreadProcessor +{ + protected override async void Proc_Do() + { + while (_isRunning) + { + // 多项目配置 + var appConfigs = AppConfigurationExtend.AppConfigs + .Select(it => it.Value) + .Where(it => it.DomainName != "default") + .ToList(); + + var tasks = appConfigs.Select(ProcessAppConfig).ToList(); + + // 等待所有任务完成 + await Task.WhenAll(tasks); + + // 使用异步延迟代替阻塞线程 + await Task.Delay(5); + } + } + + private async Task ProcessAppConfig(AppConfig appConfig) + { + var userNotActionDisconnect = DateTime.Now.AddSeconds(-appConfig.GameConfig.UserNotActionDisconnect); + var userNotActionEndGame = DateTime.Now.AddSeconds(-appConfig.GameConfig.UserNotActionEndGame); + + using var scope = serviceProvider.CreateScope(); + var dao = scope.ServiceProvider.GetDAO(appConfig); + var redis = appConfig.GetRedisDataBase(); + var redisServer = appConfig.GetRedisServer(); + + var keyPattern = $"{PlayGameExtend.GetPlayGameKeyPrefix}:*"; + List gameInfoList = await GetPlayGameUserInfoList(redis, redisServer, keyPattern).ConfigureAwait(false); + + // 处理未操作断线的玩家 + var userPlayGameNotActionEndGame = gameInfoList + .Where(it => it.GameStatus == PlayGameStatus.游戏掉线 && it.PlayGameHeartbeatAt < userNotActionEndGame) + .ToList(); + + foreach (var user in userPlayGameNotActionEndGame) + { + await EndGameAsync(user, appConfig, dao, redis).ConfigureAwait(false); + gameInfoList.Remove(user); + } + + // 处理未操作标记为掉线的玩家 + var userPlayGameNotAction = gameInfoList + .Where(it => it.GameStatus == PlayGameStatus.游戏中 && it.PlayGameHeartbeatAt < userNotActionDisconnect) + .ToList(); + + foreach (var user in userPlayGameNotAction) + { + //将用户标记为短线状态 + user.PlayGameUserNotAction(); + await user.SaveChangesAsync(dao, redis).ConfigureAwait(false); + } + } + + public static async Task> GetPlayGameUserInfoList(IDatabase redis, IServer redisServer, string keyPattern) + { + var keys = await redisServer.ScanKeysAsync(keyPattern, 2000).ConfigureAwait(false); + + var tasks = keys.Select(async gameKey => + { + var gameInfo = await redis.StringGetAsync(gameKey).ConfigureAwait(false); + return gameInfo; + }); + + var gameInfoList = (await Task.WhenAll(tasks).ConfigureAwait(false)).Where(x => x != null).ToList(); + return gameInfoList; + } + + private async Task EndGameAsync(PlayGameUserInfo user, AppConfig appConfig, DAO dao, IDatabase redis) + { + using HttpClient httpClient = new HttpClient(new JYApiAppConfigHandler(appConfig, user.Ip)); + var jyApi = RestService.For(httpClient); + + var playGameCommonSetting = new PlayGameCommonSetting + { + ScId = user.ScId, + Sn = user.Sn + }; + + var jyResponse = await jyApi.StopGame(playGameCommonSetting).ConfigureAwait(false); + + user.ExitPlayGame(jyResponse); + await user.SaveChangesAsync(dao, redis).ConfigureAwait(false); + } +} diff --git a/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameService.cs b/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameService.cs index e83bae9..ec1cfd7 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameService.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Game/PlayGameService.cs @@ -6,16 +6,29 @@ using System.Threading.Tasks; namespace CloudGaming.Code.Game; +/// +/// 玩游戏服务 +/// +/// public class PlayGameService(IServiceProvider serviceProvider) : IHostedService { + PlayGameProcessor? playGameProcessor = null; public Task StartAsync(CancellationToken cancellationToken) { - //AppConfigurationExtend.AppConfigs.Select(it => it.Value).Where(it => it.DomainName != "default").ToList(); - throw new NotImplementedException(); + AppConfigurationExtend.AppConfigs.Select(it => it.Value).Where(it => it.DomainName != "default").ToList(); + //throw new NotImplementedException(); + playGameProcessor = new PlayGameProcessor(serviceProvider) { }; + playGameProcessor.Run(); + return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { - throw new NotImplementedException(); + if (playGameProcessor != null) + { + playGameProcessor.Stop(); + playGameProcessor.Dispose(); + } + return Task.CompletedTask; } } diff --git a/src/CloudGaming/Code/CloudGaming.Code/JY/JYApiAppConfigHandler.cs b/src/CloudGaming/Code/CloudGaming.Code/JY/JYApiAppConfigHandler.cs new file mode 100644 index 0000000..dcebaab --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/JY/JYApiAppConfigHandler.cs @@ -0,0 +1,56 @@ + +using CloudGaming.AppConfigModel; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.Code.JY +{ + public class JYApiAppConfigHandler : JYApiHandler + { + protected AppConfig AppConfig { get; set; } + protected string Ip { get; set; } + public JYApiAppConfigHandler(AppConfig appConfig, string ip) : base(null) + { + AppConfig = appConfig; + Ip = ip; + } + + //protected new async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + //{ + // try + // { + // await SetRequest(request); + // var sw = Stopwatch.StartNew(); + // var response = await base.SendAsync(request, cancellationToken); + // sw.Stop(); + // if (response.IsSuccessStatusCode) + // { + // await AttachDebugInfoAsync(request, response, sw.Elapsed.TotalMilliseconds.ToString()); + // } + // return response; + // } + // catch (Exception ex) + // { + // // 可根据实际需要扩展异常处理逻辑 + // throw new HttpRequestException("发送请求时发生错误。", ex); + // } + //} + protected override async Task SetRequest(HttpRequestMessage request) + { + + + UpdateRequestUri(request, AppConfig.GameConfig.BsUrl); + + if (request.Content is FormUrlEncodedContent) + { + + await UpdateRequestContentAsync(request, AppConfig, Ip); + } + + } + } +} diff --git a/src/CloudGaming/Code/CloudGaming.Code/JY/JYApiHandler.cs b/src/CloudGaming/Code/CloudGaming.Code/JY/JYApiHandler.cs index d32926a..0ae3b43 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/JY/JYApiHandler.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/JY/JYApiHandler.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; + using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -10,11 +12,14 @@ namespace CloudGaming.Code.JY; /// public class JYApiHandler : DelegatingHandler { - private readonly IHttpContextAccessor _httpContextAccessor; + protected readonly IHttpContextAccessor _httpContextAccessor; - public JYApiHandler(IHttpContextAccessor httpContextAccessor) + public JYApiHandler(IHttpContextAccessor? httpContextAccessor) { - _httpContextAccessor = httpContextAccessor; + if (httpContextAccessor != null) + { + _httpContextAccessor = httpContextAccessor; + } } /// @@ -24,21 +29,7 @@ public class JYApiHandler : DelegatingHandler { try { - var httpContext = _httpContextAccessor.HttpContext; - if (httpContext == null) - throw new InvalidOperationException("无法获取HttpContext。"); - - var host = httpContext.Request.Host.Host; - var app = AppConfigurationExtend.GetAppConfig(host); - if (app != null) - { - UpdateRequestUri(request, app.GameConfig.BsUrl); - - if (request.Content is FormUrlEncodedContent) - { - await UpdateRequestContentAsync(request, app, httpContext); - } - } + await SetRequest(request); var sw = Stopwatch.StartNew(); var response = await base.SendAsync(request, cancellationToken); sw.Stop(); @@ -46,7 +37,6 @@ public class JYApiHandler : DelegatingHandler { await AttachDebugInfoAsync(request, response, sw.Elapsed.TotalMilliseconds.ToString()); } - return response; } catch (Exception ex) @@ -56,10 +46,36 @@ public class JYApiHandler : DelegatingHandler } } + /// + /// + /// + /// + /// + /// + protected virtual async Task SetRequest(HttpRequestMessage request) + { + var httpContext = _httpContextAccessor.HttpContext; + if (httpContext == null) + throw new InvalidOperationException("无法获取HttpContext。"); + + var host = httpContext.Request.Host.Host; + var app = AppConfigurationExtend.GetAppConfig(host); + if (app != null) + { + UpdateRequestUri(request, app.GameConfig.BsUrl); + + if (request.Content is FormUrlEncodedContent) + { + + await UpdateRequestContentAsync(request, app, httpContext.GetClientIpAddress()); + } + } + } + /// /// 更新请求的URI /// - private void UpdateRequestUri(HttpRequestMessage request, string baseUrl) + protected void UpdateRequestUri(HttpRequestMessage request, string baseUrl) { var newUri = new Uri(new Uri(baseUrl), request.RequestUri.PathAndQuery); request.RequestUri = newUri; @@ -68,12 +84,12 @@ public class JYApiHandler : DelegatingHandler /// /// 更新请求内容 /// - private async Task UpdateRequestContentAsync(HttpRequestMessage request, dynamic app, HttpContext httpContext) + protected async Task UpdateRequestContentAsync(HttpRequestMessage request, AppConfig app, string ip) { var formData = await request.Content.ReadAsStringAsync(); var parameters = ParseFormData(formData); - AddDefaultParameters(parameters, app, httpContext); + AddDefaultParameters(parameters, app, ip); var signKey = GenerateSignature(parameters, app.GameConfig.SignKey); var encryptedParameters = $"{ConvertDictionaryToPostData(parameters)}&sign={signKey}"; @@ -84,7 +100,7 @@ public class JYApiHandler : DelegatingHandler /// /// 添加默认参数 /// - private void AddDefaultParameters(SortedDictionary parameters, dynamic app, HttpContext httpContext) + protected void AddDefaultParameters(SortedDictionary parameters, AppConfig app, string ip) { if (!parameters.ContainsKey("channel_id")) { @@ -93,7 +109,7 @@ public class JYApiHandler : DelegatingHandler if (!parameters.ContainsKey("ip")) { - parameters["ip"] = httpContext.GetClientIpAddress(); + parameters["ip"] = ip; } if (!parameters.ContainsKey("time")) @@ -105,7 +121,7 @@ public class JYApiHandler : DelegatingHandler /// /// 生成签名 /// - private string GenerateSignature(SortedDictionary parameters, string signKey) + protected string GenerateSignature(SortedDictionary parameters, string signKey) { var dataString = string.Join("&", parameters.Select(kv => $"{kv.Key}={kv.Value}")) + $"&key={signKey}"; return MD5Encryption.ComputeMD5Hash(dataString).ToUpper(); @@ -114,7 +130,7 @@ public class JYApiHandler : DelegatingHandler /// /// 附加调试信息到响应 /// - private async Task AttachDebugInfoAsync(HttpRequestMessage request, HttpResponseMessage response, string totalMilliseconds) + protected async Task AttachDebugInfoAsync(HttpRequestMessage request, HttpResponseMessage response, string totalMilliseconds) { var responseContent = await response.Content.ReadAsStringAsync(); var responseJson = JsonConvert.DeserializeObject(responseContent); @@ -131,7 +147,7 @@ public class JYApiHandler : DelegatingHandler /// /// 序列化HTTP响应 /// - private string SerializeResponse(HttpResponseMessage response, string content) + protected string SerializeResponse(HttpResponseMessage response, string content) { return JsonConvert.SerializeObject(new { @@ -144,7 +160,7 @@ public class JYApiHandler : DelegatingHandler /// /// 解析表单数据 /// - private SortedDictionary ParseFormData(string formData) + protected SortedDictionary ParseFormData(string formData) { var parameters = new SortedDictionary(); var pairs = formData.Split('&'); @@ -164,7 +180,7 @@ public class JYApiHandler : DelegatingHandler /// /// 将字典转换为POST数据格式 /// - private string ConvertDictionaryToPostData(SortedDictionary dictionary) + protected string ConvertDictionaryToPostData(SortedDictionary dictionary) { return string.Join("&", dictionary.Select(kv => $"{kv.Key}={kv.Value}")); } diff --git a/src/CloudGaming/Model/CloudGaming.AppConfigModel/GameConfig.cs b/src/CloudGaming/Model/CloudGaming.AppConfigModel/GameConfig.cs index b4d3835..888ae3a 100644 --- a/src/CloudGaming/Model/CloudGaming.AppConfigModel/GameConfig.cs +++ b/src/CloudGaming/Model/CloudGaming.AppConfigModel/GameConfig.cs @@ -25,5 +25,21 @@ namespace CloudGaming.AppConfigModel /// 客户渠道地址 /// public string BsUrl { get; set; } + + /// + /// 用户多少秒后可以重连会话,必须小于结束游戏前 + /// + public int UserReconnectTime { get; set; } + + /// + /// 用户没有心跳后多少秒判断掉线 + /// + public int UserNotActionDisconnect { get; set; } + + + /// + /// 用户没有心跳后多少秒结束游戏 + /// + public int UserNotActionEndGame { get; set; } } } diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/DisplayGradeSettings.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/DisplayGradeSettings.cs index ceef83f..2b64972 100644 --- a/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/DisplayGradeSettings.cs +++ b/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/DisplayGradeSettings.cs @@ -1,3 +1,5 @@ +using CloudGaming.DtoModel.JY; + using System; using System.Collections.Generic; using System.Linq; @@ -10,11 +12,15 @@ namespace CloudGaming.DtoModel.PlayGame /// /// 切换视频 /// - public class DisplayGradeSettings : PlayGameCommonSetting + public class DisplayGradeSettings : JYRequestParameter// PlayGameCommonSetting { public DisplayGradeSettings() { } - + /// + /// 本次游戏连接会话 id + /// + [JsonPropertyName("sc_id")] + public int ScId { get; set; } /// /// 显示等级,可在后台配置,默认为 1 /// diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/PlayGameUserInfo.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/PlayGameUserInfo.cs index 467194a..6dd0c90 100644 --- a/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/PlayGameUserInfo.cs +++ b/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/PlayGameUserInfo.cs @@ -141,6 +141,11 @@ public class PlayGameUserInfo /// public int DiamondListId { get; set; } + /// + /// ip地址 + /// + public string Ip { get; set; } + /// /// 转化成数据库实体类 /// diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/ReconPlayGameRequest.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/ReconPlayGameRequest.cs new file mode 100644 index 0000000..666d6af --- /dev/null +++ b/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/ReconPlayGameRequest.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace CloudGaming.DtoModel.PlayGame; + + +/// +/// 重连会话 +/// +public class ReconPlayGameRequest : PlayGameCommonSetting +{ + public ReconPlayGameRequest() { } + + /// + /// 显示等级,可在后台配置,默认为 1 + /// + [JsonPropertyName("display_grade")] + public int? DisplayGrade { get; set; } = 1; // 默认值为 1 + + /// + /// 设备型号 + /// + [JsonPropertyName("model_name")] + public string ModelName { get; set; } + + /// + /// 芯片 + /// + [JsonPropertyName("cpu")] + public string Cpu { get; set; } +} diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/ReconPlayGameSettings.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/ReconPlayGameSettings.cs index ec2b032..2a96770 100644 --- a/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/ReconPlayGameSettings.cs +++ b/src/CloudGaming/Model/CloudGaming.DtoModel/PlayGame/ReconPlayGameSettings.cs @@ -1,3 +1,5 @@ +using CloudGaming.DtoModel.JY; + using System; using System.Collections.Generic; using System.Linq; @@ -10,13 +12,17 @@ namespace CloudGaming.DtoModel.PlayGame /// /// 重连会话 /// - public class ReconPlayGameSettings : PlayGameCommonSetting + public class ReconPlayGameSettings : JYRequestParameter// PlayGameCommonSetting { public ReconPlayGameSettings() { } + public ReconPlayGameSettings(string sn, int userId) : base(sn, userId) + { + } + /// - /// 会话 id + /// 本次游戏连接会话 id /// [JsonPropertyName("sc_id")] public int ScId { get; set; }