using System.Net; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; using System.Xml.Linq; using Amazon.Runtime.Internal.Endpoints.StandardLibrary; using Google.Protobuf.WellKnownTypes; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.Hosting; using Newtonsoft.Json; using Org.BouncyCastle.Asn1.Ocsp; using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.UGQ.Models; using UGQDatabase.Models; namespace GameServer { public class UgqApiManager { HttpClient http_client = new(); string url_prefix = string.Empty; private static readonly object lock_object = new object(); private static bool is_task_running = false; private static Task? inspection_check_task = null; private static Int32 api_server_check_state = 0; //0이면 서버 정상 상태, -1이면 서버 죽거나 ip주소 바뀌어서 접속 안되는 상태 private const Int32 inspection_state = -1; private const Int32 active_state = 0; private const string connection_test_str = "connection-test"; public async Task onInit() { await Task.CompletedTask; http_client.BaseAddress = new Uri(GameServerApp.getServerLogic().getServerConfig().UgqApiServerConfig.ApiServerAddress); url_prefix = GameServerApp.getServerLogic().getServerConfig().UgqApiServerConfig.UrlInGamePrefix; } public async Task checkConnection() { (var result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Get, connection_test_str, new(), string.Empty); return (result); } public async Task<(Result, UgqBoardSearchResult?)> getPagedGradeUgqFromBoard(Dictionary dictionaries, LanguageType langType) { string url_str = $"quest-board?"; var url_builder_str = makeGetParamString(url_str, dictionaries); var headers = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(langType); (var result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Get, url_builder_str, headers, string.Empty); return (result, ret); } public async Task<(Result, UgqBoardItemDetail?)> getUgqDetailFromBoard(Dictionary dictionaries, string userGuid, LanguageType langType) { string url_str = $"quest-board-detail/{userGuid}?"; var url_builder_str = makeGetParamString(url_str, dictionaries); var headers = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(langType); (var result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Get, url_builder_str, headers, string.Empty); return (result, ret); } public async Task<(Result, UgqBoardSportlightResult?)> getPagedSpotLightUgqFromBoard(Dictionary dictionaries) { (var result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Get, "quest-board-spotlight", dictionaries, string.Empty); return (result, ret); } public async Task<(Result, UgqBoardSearchResult?)> getPagedBookmarkUgqFromBoard(Dictionary dictionaries, string userGuid) { string url_str = $"bookmark-quest-board/{userGuid}?"; var url_builder_str = makeGetParamString(url_str, dictionaries); (var result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Get, url_builder_str, dictionaries, string.Empty); return (result, ret); } public async Task ugqRegisterBookmark(Dictionary dict, UInt32 questId, string userGuid) { UgqBookmarkRequest request = new(); request.QuestId = questId; request.UserGuid = userGuid; (var result, var _) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, $"bookmark", dict, JsonConvert.SerializeObject(request)); return result; } public async Task ugqDeRegisterBookmark(Dictionary dict, UInt32 questId, string userGuid) { UgqUnbookmarkRequest request = new(); request.QuestId = questId; request.UserGuid = userGuid; (var result, var _) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, $"unbookmark", dict, JsonConvert.SerializeObject(request)); return result; } public async Task ugqRegisterLike(Dictionary dict, UInt32 questId, UInt32 questRevision, string userGuid) { UgqLikeRequest request = new(); request.QuestId = questId; request.Revision = questRevision; request.UserGuid = userGuid; (var result, var _) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, $"like", dict, JsonConvert.SerializeObject(request)); return result; } public async Task setUgqQuestAccepted(string userGuid, UInt32 questId, UInt32 QuestRevision, LanguageType langType) { var dict = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(langType); UgqQuestAcceptedRequest request = new(); request.QuestId = questId; request.Revision = QuestRevision; request.Reason = UGQAcceptReason.Player; string url_str = $"set-quest-accepted/{userGuid}"; (var result, var _) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, url_str, dict, JsonConvert.SerializeObject(request)); return result; } public async Task setTestUgqQuestCompleted(string userGuid, UInt32 questId, UInt32 QuestRevision, LanguageType langType) { var dict = UgqApiHelper.makeDictionaryForUgqQuestId(questId, QuestRevision); string url_str = $"set-test-completed/{userGuid}?"; var url_builder_str = makeGetParamString(url_str, dict); var headers = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(langType); (var result, var _) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, url_builder_str, headers, string.Empty); return result; } public async Task setTestUgqQuestAborted(string userGuid, UInt32 questId, UInt32 QuestRevision, LanguageType langType) { var dict = UgqApiHelper.makeDictionaryForUgqQuestId(questId, QuestRevision); string url_str = $"set-test-aborted/{userGuid}?"; var url_builder_str = makeGetParamString(url_str, dict); var headers = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(langType); (var result, var _) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, url_builder_str, headers, string.Empty); return result; } public async Task setUgqQuestCompleted(string userGuid, UInt32 questId, UInt32 QuestRevision, Int32 amount, LanguageType langType) { var dict = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(langType); UgqQuestCompletedRequest request = new(); request.QuestId = questId; request.Revision = QuestRevision; request.Amount = amount; string url_str = $"set-quest-completed/{userGuid}"; (var result, var _) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, url_str, dict, JsonConvert.SerializeObject(request)); return result; } public async Task setUgqQuestAborted(string userGuid, UInt32 questId, UInt32 QuestRevision, UGQAbortReason reason, LanguageType langType) { var dict = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(langType); UgqQuestAbortedRequest request = new(); request.QuestId = questId; request.Revision = QuestRevision; request.Reason = reason; string url_str = $"set-quest-aborted/{userGuid}"; (var result, var _) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, url_str, dict, JsonConvert.SerializeObject(request)); return result; } public async Task ugqDeregisterLike(Dictionary dict, UInt32 questId, UInt32 questRevision, string userGuid) { UgqUnlikeRequest request = new(); request.QuestId = questId; request.Revision = questRevision; request.UserGuid = userGuid; var req_param = JsonContent.Create(new { questId = questId, revision = questRevision, userGuid = userGuid }); (var result, var _) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, $"unlike", dict, JsonConvert.SerializeObject(request)); return result; } public async Task ugqReport(Dictionary dict, UInt32 questId, UInt32 questRevision, string userGuid, string reportText) { UgqReportRequest request = new(); request.QuestId = questId; request.Revision = questRevision; request.UserGuid = userGuid; request.Contents = reportText; var req_param = JsonContent.Create(new { questId = questId, revision = questRevision, userGuid = userGuid, contents = reportText }); (var result, var _) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, $"report", dict, JsonConvert.SerializeObject(request)); return result; } public async Task<(Result, GameQuestDataEntity?)> getUgqQuestData(string userGuid, UInt32 questId, UInt32 questRevision, LanguageType langType) { var dictionaries = UgqApiHelper.makeDictionaryForUgqQuestId(questId, questRevision); string url_str = $"game-quest-data?"; var url_builder_str = makeGetParamString(url_str, dictionaries); var headers = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(langType); (var result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Get, url_builder_str, headers, string.Empty); return (result, ret); } public async Task<(Result, List?)> getTestUgqIds(string userGuid, LanguageType languageType) { string url_str = $"user-quests-in-test-state/{userGuid}"; var dictionaries = UgqApiHelper.makeDictionaryFromLanguageType(languageType); (var result, var ret) = await sendUgqRequestToApiServer>(http_client, HttpMethod.Get, url_str, dictionaries, string.Empty); return (result, ret); } public async Task<(Result, GameQuestDataEntity?)> getUgqTestQuestData(string userGuid, UInt32 questId, UInt32 questRevision, LanguageType langType) { var dictionaries = UgqApiHelper.makeDictionaryForUgqQuestId(questId, questRevision); string url_str = $"test-game-quest-data/{userGuid}?"; var url_builder_str = makeGetParamString(url_str, dictionaries); var headers = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(langType); (var result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Get, url_builder_str, headers, string.Empty); return (result, ret); } public async Task<(Result, GameQuestDataEntity?)> getUgqLatestQuestData(UInt32 questId, LanguageType langType) { var dictionaries = UgqApiHelper.makeDictionaryForQuestId(questId, langType); string url_str = $"latest-revision-game-quest-data?"; var url_builder_str = makeGetParamString(url_str, dictionaries); var headers = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(langType); (var result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Get, url_builder_str, headers, string.Empty); return (result, ret); } public async Task<(Result, GameQuestDataSimple?)> getUgqQuestSimpleInfo(UInt32 questId, UInt32 questRevision, QuestContentState state) { var dictionaries = UgqApiHelper.makeDictionaryForUgqByState(questId, questRevision, state); string url_str = $"game-quest-data-simple?"; var url_builder_str = makeGetParamString(url_str, dictionaries); (var result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Get, url_builder_str, new(), string.Empty); return (result, ret); } public async Task<(Result, GameQuestDataEntity?)> requestBeginCreatorPoint(string userGuid, Int32 cost, Int32 questId, Int32 questRevision, LanguageType languageType) { var result = new Result(); if (cost == 0) { return (result, new()); } var dictionaries = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(languageType); string url_str = $"begin-creator-point"; BeginCreatorPointRequest request = new(); request.UserGuid = userGuid; request.Point = cost; //여기 제네릭 나중에 영수증 response로 처리 해야된다. (result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, url_str, dictionaries, JsonConvert.SerializeObject(request)); return (result, ret); } public async Task<(Result, GameQuestDataEntity?)> requestCommitCreatorPoint(string userGuid, string receipt, LanguageType languageType) { var dictionaries = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(languageType); string url_str = $"commit-creator-point"; CommitCreatorPointRequest request = new(); request.UserGuid = userGuid; request.ReceiptId = receipt; (var result, var ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, url_str, dictionaries, JsonConvert.SerializeObject(request)); return (result, ret); } public async Task<(Result, GameQuestDataEntity?)> requestCancelCreatorPoint(string userGuid, string receipt, string reason, LanguageType languageType) { var dictionaries = UgqApiHelper.makeDictionaryFromLanguageTypeWithMediaType(languageType); string url_str = $"cancel-creator-point"; CancelCreatorPointRequest request = new(); request.UserGuid = userGuid; request.ReceiptId = receipt; request.CancelReason = reason; (var result, GameQuestDataEntity? ret) = await sendUgqRequestToApiServer(http_client, HttpMethod.Post, url_str, dictionaries, JsonConvert.SerializeObject(request)); return (result, ret); } private async Task<(Result, T?)> sendUgqRequestToApiServer(HttpClient httpClient, HttpMethod method, string url, Dictionary headers, string reqBodyString) where T : class { var result = new Result(); var err_msg = string.Empty; //점검 상태면 여기서 리턴 if (api_server_check_state == inspection_state && !url.Equals(connection_test_str)) { err_msg = $"UgqApiServer Inspection State. Exception !!!"; result.setFail(ServerErrorCode.UgqApiServerHttpRequestException, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } HttpRequestMessage requestMessage = new HttpRequestMessage(method, $"{url_prefix}/{url}"); foreach (var header in headers) { requestMessage.Headers.Add(header.Key, header.Value); } if (reqBodyString != string.Empty) { var stringContent = new StringContent(reqBodyString, UnicodeEncoding.UTF8, "application/json"); requestMessage.Content = stringContent; } var content_string = string.Empty; try { var resMsg = await httpClient.SendAsync(requestMessage); if (resMsg.IsSuccessStatusCode == false) { if (resMsg.StatusCode == HttpStatusCode.BadRequest) { content_string = await resMsg.Content.ReadAsStringAsync(); if (string.IsNullOrWhiteSpace(content_string)) { err_msg = $"UgqApiServerRequestFailed Failed. content_string : resMsg.StatusCode : {resMsg.StatusCode}, reason : {resMsg.ReasonPhrase} !!!, url : {url}"; result.setFail(ServerErrorCode.UgqApiServerRequestFailed, err_msg); Log.getLogger().info(err_msg); } else { result = makeErrorResult(content_string); } return (result, null); } err_msg = $"UgqApiServerRequestFailed Failed. resMsg.StatusCode : content_string : {content_string}, {resMsg.StatusCode}, reason : {resMsg.ReasonPhrase} !!!, url : {url}"; result.setFail(ServerErrorCode.UgqApiServerRequestFailed, err_msg); Log.getLogger().info(err_msg); return (result, null); } content_string = await resMsg.Content.ReadAsStringAsync(); if (string.IsNullOrWhiteSpace(content_string)) { return (result, null); } } catch (Exception e) { err_msg = $"UgqApiServerRequestFailed Failed. Exception !!!, {e.Message}"; result.setFail(ServerErrorCode.UgqApiServerHttpRequestException, err_msg); Log.getLogger().error(result.toBasicString()); //서버죽었으므로 여기서 점검 상태로 변경 StartChangeInspectStateTask(); //await changeInspectState(); return (result, null); } var contect_template = JsonConvert.DeserializeObject(content_string); if (null == contect_template) { err_msg = $"UgqApiServerRequest Serialize Failed. content_string : {content_string} !!!, url : {url}"; result.setFail(ServerErrorCode.UgqApiServerConvertToObjectFailed, err_msg); Log.getLogger().info(err_msg); return (result, null); } return (result, contect_template); } private void StartChangeInspectStateTask() { lock (lock_object) { if (is_task_running) return; // 이미 실행 중인 작업이 있으면 아무것도 하지 않음 is_task_running = true; inspection_check_task = Task.Run(async () => { try { await changeInspectState(); } finally { is_task_running = false; // 작업이 완료된 후 상태 업데이트 } }); } } private async Task changeInspectState() { api_server_check_state = inspection_state; while (true) { // 1분 대기 await Task.Delay(TimeSpan.FromMinutes(1)); var result = new Result(); result = await checkConnection(); if (result.isFail() && result.getErrorCode() == ServerErrorCode.UgqApiServerHttpRequestException) { Log.getLogger().error($"ugq api server inpection recheck still error changed"); } else { //그렇지 않으면 정상 이니까 상태 변경 api_server_check_state = active_state; Log.getLogger().error($"ugq api server inpection request success !! api_server_check_state : {api_server_check_state}"); break; } } } private Result makeErrorResult(string content_string) { var result = new Result(); try { var response_result = JsonConvert.DeserializeObject(content_string); NullReferenceCheckHelper.throwIfNull(response_result, () => $"response_result is null !!!"); result.setFail((ServerErrorCode)response_result.ErrorCode, response_result.ErrorMessage); } catch (Exception e) { var err_msg = $"UgqApiServerRequest Serialize Failed. content_string : {e}, {content_string} !!!"; result.setFail(ServerErrorCode.UgqApiServerConvertToObjectFailed, err_msg); Log.getLogger().info(err_msg); return result; } return result; } private string makeGetParamString(string prefixUrl, Dictionary dictionaries) { StringBuilder url_builder = new StringBuilder(prefixUrl); int count = dictionaries.Count; int index = 0; foreach (var dict in dictionaries) { url_builder.Append($"{dict.Key}={dict.Value}"); if (index < count - 1) { url_builder.Append("&"); } index++; } return url_builder.ToString(); } } }