초기커밋
This commit is contained in:
314
GameServer/Global/AIChat/AIChatServerConnector.cs
Normal file
314
GameServer/Global/AIChat/AIChatServerConnector.cs
Normal file
@@ -0,0 +1,314 @@
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Signers;
|
||||
using ServerCommon;
|
||||
using System.Text;
|
||||
using ServerCore; using ServerBase;
|
||||
using System.Text.Json.Nodes;
|
||||
using Newtonsoft.Json;
|
||||
using MetaAssets;
|
||||
using META_ID = System.UInt32;
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
static public class AIChatServerConnector
|
||||
{
|
||||
static string server_jwt = string.Empty;
|
||||
static string? jwtAdminJson = null;
|
||||
static string? jwtUserJson = null;
|
||||
|
||||
static public async Task onInit()
|
||||
{
|
||||
var messageBytes = MakeMessage("admin");
|
||||
var signatureBytes = SignMessage(messageBytes);
|
||||
|
||||
AIChatJwt AIChatAdminJwt = new AIChatJwt();
|
||||
AIChatAdminJwt.messageBase64 = Convert.ToBase64String(messageBytes);
|
||||
AIChatAdminJwt.signatureBase64 = Convert.ToBase64String(signatureBytes);
|
||||
|
||||
jwtAdminJson = JsonConvert.SerializeObject(AIChatAdminJwt);
|
||||
|
||||
messageBytes = MakeMessage("user");
|
||||
signatureBytes = SignMessage(messageBytes);
|
||||
|
||||
AIChatJwt AIChatUserJwt = new AIChatJwt();
|
||||
AIChatUserJwt.messageBase64 = Convert.ToBase64String(messageBytes);
|
||||
AIChatUserJwt.signatureBase64 = Convert.ToBase64String(signatureBytes);
|
||||
|
||||
jwtUserJson = JsonConvert.SerializeObject(AIChatUserJwt);
|
||||
|
||||
await serverJwtIssue();
|
||||
|
||||
await RegisterGeneralNPC();
|
||||
}
|
||||
|
||||
static public string getUserJwtJson()
|
||||
{
|
||||
NullReferenceCheckHelper.throwIfNull(jwtUserJson, () => $"jwtUserJson is null !!!");
|
||||
return jwtUserJson;
|
||||
}
|
||||
|
||||
//서버의 JWT를 발행합니다.
|
||||
static private async Task<Result> serverJwtIssue()
|
||||
{
|
||||
(var result, string resMsg) = await sendAIChatServer("POST", $"/api/auth/jwt/issue", null, jwtAdminJson);
|
||||
if (result.isFail()) return result;
|
||||
|
||||
var jwt = JsonNode.Parse(resMsg)?["jwt"]?.ToString();
|
||||
if (jwt != null)
|
||||
{
|
||||
server_jwt = jwt;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//전체 채팅량 랭킹 (매개변수인 languageType은 res로 받는 캐릭터 이름이나 owner의 이름의 번역 차이임.)
|
||||
static public async Task<(Result, AIChatRanking?)> getAllRanking(LanguageType language, int page, int numPerPage)
|
||||
{
|
||||
(var result, string resMsg) = await sendAIChatServerWithServerJwt("GET", $"/api/ranking/all?lang={language.ToString().ToLower()}&page={page}&numPerPage={numPerPage}", null);
|
||||
if (result.isFail()) return (result, null);
|
||||
|
||||
try
|
||||
{
|
||||
var aichat_ranking = JsonConvert.DeserializeObject<AIChatRanking>(resMsg);
|
||||
return (result, aichat_ranking);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.getLogger().error($"Exception !!!, DeserializeObject : exception:{ex}, resMsg:{resMsg}");
|
||||
return (result, null);
|
||||
}
|
||||
}
|
||||
//최근 24시간 채팅량 랭킹 (매개변수인 languageType은 res로 받는 캐릭터 이름이나 owner의 이름의 번역 차이임.)
|
||||
static public async Task<(Result, AIChatRanking?)> getTodayRanking(LanguageType language, int page, int numPerPage)
|
||||
{
|
||||
(var result, string resMsg) = await sendAIChatServerWithServerJwt("GET", $"/api/ranking/today?lang={language.ToString().ToLower()}&page={page}&numPerPage={numPerPage}", null);
|
||||
if (result.isFail()) return (result, null);
|
||||
|
||||
try
|
||||
{
|
||||
var aichat_ranking = JsonConvert.DeserializeObject<AIChatRanking>(resMsg);
|
||||
return (result, aichat_ranking);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.getLogger().error($"Exception !!!, DeserializeObject : exception:{ex}, resMsg:{resMsg}");
|
||||
return (result, null);
|
||||
}
|
||||
}
|
||||
|
||||
//특정 캐릭터 조회
|
||||
static public async Task<(Result, AIChatCharacter?)> getCharacterByTargetGuid(string target_guid)
|
||||
{
|
||||
(var result, string resMsg) = await sendAIChatServerWithServerJwt("GET", $"/api/character/{target_guid}", null);
|
||||
if (result.isFail()) return (result, null);
|
||||
|
||||
try
|
||||
{
|
||||
var aichat_character = JsonConvert.DeserializeObject<AIChatCharacter>(resMsg);
|
||||
return (result, aichat_character);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.getLogger().error($"Exception !!!, DeserializeObject : exception:{ex}, resMsg:{resMsg}");
|
||||
return (result, null);
|
||||
}
|
||||
}
|
||||
|
||||
static private byte[] MakeMessage(string role)
|
||||
{
|
||||
var message = "{\"role\":\"" + role + "\"}";
|
||||
var messageBytes = Encoding.UTF8.GetBytes(message);
|
||||
return messageBytes;
|
||||
}
|
||||
|
||||
static private byte[] SignMessage(byte[] messageBytes)
|
||||
{
|
||||
var privateKey = Convert.FromBase64String(GameServerApp.getServerLogic().getServerConfig().AIChatConfig.PrivateKey);
|
||||
var privateKeyParams = new Ed25519PrivateKeyParameters(privateKey, 0);
|
||||
|
||||
// 서명합니다.
|
||||
var signer = new Ed25519Signer();
|
||||
signer.Init(true, privateKeyParams);
|
||||
signer.BlockUpdate(messageBytes, 0, messageBytes.Length);
|
||||
var signature = signer.GenerateSignature();
|
||||
return signature;
|
||||
}
|
||||
|
||||
static public async Task<(Result, string)> sendAIChatServerWithServerJwt(string httpMethod, string url, string? bodyJson)
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
if (server_logic.getServerConfig().OfflineMode == true)
|
||||
{
|
||||
result.setFail(ServerErrorCode.AiChatServerInactive);
|
||||
return (result, string.Empty);
|
||||
}
|
||||
|
||||
var fullUrl = makeAiChatServerURL(url);
|
||||
|
||||
(var IsSuccessStatusCode, var readMsg) = await HttpClientHelper.sendHttpRequest( httpMethod
|
||||
, fullUrl, server_jwt, bodyJson
|
||||
, "application/json"
|
||||
, "caliverse", "1.0" );
|
||||
if (IsSuccessStatusCode == false)
|
||||
{
|
||||
Log.getLogger().info($"sendAIChatServer Failed. fullUrl : {fullUrl}, return msg : {readMsg} !!!");
|
||||
|
||||
result = ErrorHelper(readMsg, fullUrl);
|
||||
if (result.ErrorCode == ServerErrorCode.AiChatServerTokenExpired)
|
||||
{
|
||||
result = await serverJwtIssue();
|
||||
if (result.isSuccess())
|
||||
{
|
||||
(IsSuccessStatusCode, readMsg) = await HttpClientHelper.sendHttpRequest( httpMethod
|
||||
, fullUrl, server_jwt, bodyJson
|
||||
, "application/json"
|
||||
, "caliverse", "1.0" );
|
||||
if (IsSuccessStatusCode == false)
|
||||
result.setFail(ServerErrorCode.AiChatServerTokenExpired);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (result, readMsg);
|
||||
}
|
||||
|
||||
static public async Task<(Result, string)> sendAIChatServer(string httpMethod, string url, string? jwt, string? bodyJson)
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
if (server_logic.getServerConfig().OfflineMode == true)
|
||||
{
|
||||
result.setFail(ServerErrorCode.AiChatServerInactive);
|
||||
return (result, string.Empty);
|
||||
}
|
||||
|
||||
var fullUrl = makeAiChatServerURL(url);
|
||||
|
||||
(var IsSuccessStatusCode, var readMsg) = await HttpClientHelper.sendHttpRequest( httpMethod
|
||||
, fullUrl, jwt, bodyJson
|
||||
, "application/json"
|
||||
, "caliverse", "1.0" );
|
||||
if (IsSuccessStatusCode == false)
|
||||
{
|
||||
Log.getLogger().info($"sendAIChatServer Failed. fullUrl : {fullUrl}, return msg : {readMsg} !!!");
|
||||
|
||||
result = ErrorHelper(readMsg, fullUrl);
|
||||
return (result, readMsg);
|
||||
}
|
||||
|
||||
return (result, readMsg);
|
||||
}
|
||||
|
||||
static public string makeAiChatServerURL(string url)
|
||||
{
|
||||
return $"{GameServerApp.getServerLogic().getServerConfig().AIChatConfig.BaseAddress}{url}";
|
||||
}
|
||||
|
||||
static private Result ErrorHelper(string errorMsg, string fullUrl)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
AIChatError? error = JsonConvert.DeserializeObject<AIChatError>(errorMsg);
|
||||
NullReferenceCheckHelper.throwIfNull(error, () => $"error is null !!!");
|
||||
|
||||
switch (error.code)
|
||||
{
|
||||
case 998: result.setFail(ServerErrorCode.AiChatServerBadrequest, err_msg); break;
|
||||
case 999: result.setFail(ServerErrorCode.AiChatServerForbidden, err_msg); break;
|
||||
case 1000: result.setFail(ServerErrorCode.AiChatServerUnauthorized, err_msg); break;
|
||||
case 1001: result.setFail(ServerErrorCode.AiChatServerUserNotFound, err_msg); break;
|
||||
case 1002: result.setFail(ServerErrorCode.AiChatServerCharacterNotFound, err_msg); break;
|
||||
case 1003: result.setFail(ServerErrorCode.AiChatServerReactionNotFound, err_msg); break;
|
||||
case 1004: result.setFail(ServerErrorCode.AiChatServerExampleDialongNotFound, err_msg); break;
|
||||
case 1005: result.setFail(ServerErrorCode.AiChatServerSessionNotFound, err_msg); break;
|
||||
case 1006: result.setFail(ServerErrorCode.AiChatServerModelNotFound, err_msg); break;
|
||||
case 1007: result.setFail(ServerErrorCode.AiChatServerNotEnoughPoint, err_msg); break;
|
||||
case 1008: result.setFail(ServerErrorCode.AiChatServerInvalidParameters, err_msg); break;
|
||||
case 1009: result.setFail(ServerErrorCode.AiChatServerSessionLimitExceeded, err_msg); break;
|
||||
case 1010: result.setFail(ServerErrorCode.AiChatServerInvalidSignature, err_msg); break;
|
||||
case 1011: result.setFail(ServerErrorCode.AiChatServerUserAlreadyExists, err_msg); break;
|
||||
case 1012: result.setFail(ServerErrorCode.AiChatServerRemoveFailed, err_msg); break;
|
||||
case 1013: result.setFail(ServerErrorCode.AiChatServerDuplicateGuid, err_msg); break;
|
||||
case 1014: result.setFail(ServerErrorCode.AiChatServerDuplicateId, err_msg); break;
|
||||
case 1015: result.setFail(ServerErrorCode.AiChatServerChatNotFound, err_msg); break;
|
||||
case 1016: result.setFail(ServerErrorCode.AiChatServerTokenExpired, err_msg); break;
|
||||
case 1017: result.setFail(ServerErrorCode.AiChatServerInternalServer, err_msg); break;
|
||||
case 1018: result.setFail(ServerErrorCode.AiChatServerInvalidMessage, err_msg); break;
|
||||
case 1019: result.setFail(ServerErrorCode.AiChatServerUserOnlyFeature, err_msg); break;
|
||||
case 1020: result.setFail(ServerErrorCode.AiChatServerOwnershipError, err_msg); break;
|
||||
|
||||
case 1024: result.setFail(ServerErrorCode.AiChatServerChargeOrderNotFoundError, err_msg); break;
|
||||
default:
|
||||
{
|
||||
err_msg = $"sendAIChatServer Failed. fullUrl : {fullUrl}, error code : {error.code}, error name : {error.name}, error message : {error.message} !!!";
|
||||
result.setFail(ServerErrorCode.AiChatServerReqFailed, err_msg);
|
||||
Log.getLogger().info(err_msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err_msg = $"AiChat Exception message : {ex.Message}";
|
||||
result.setFail(ServerErrorCode.AiChatServerReqFailed, err_msg);
|
||||
Log.getLogger().info(err_msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static private async Task<Result> RegisterGeneralNPC()
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
List<string> npclist = new();
|
||||
|
||||
var npcGeneralMetaDatas = MetaData.Instance.NpcGeneralMetaData;
|
||||
|
||||
foreach (var generalNpc in npcGeneralMetaDatas)
|
||||
{
|
||||
(result, var ai_chat_character) = await getCharacterByTargetGuid(generalNpc.Value.Npc_id.ToString());
|
||||
if (result.isSuccess())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
EGenderType gender = EGenderType.MALE;
|
||||
if(generalNpc.Value.Gender == EGenderType.ALL)
|
||||
gender = EGenderType.MALE;
|
||||
|
||||
AIChatRegisterCharacter to_register_ai_chat_server = new();
|
||||
to_register_ai_chat_server.guid = generalNpc.Value.Npc_id.ToString();
|
||||
to_register_ai_chat_server.ownerUserGuid = string.Empty;
|
||||
to_register_ai_chat_server.lang = generalNpc.Value.Language.ToString().ToLower();
|
||||
to_register_ai_chat_server.name = generalNpc.Value.Name;
|
||||
to_register_ai_chat_server.persona = UgcNpcHelper.toPersonaWithJsonString(generalNpc.Value.WorldScenario, generalNpc.Value.Description);
|
||||
to_register_ai_chat_server.firstMes = generalNpc.Value.FirstMes;
|
||||
to_register_ai_chat_server.shortDesc = generalNpc.Value.ShortDesc;
|
||||
to_register_ai_chat_server.tags = generalNpc.Value.Tags.Select(x => (META_ID)x).ToList();
|
||||
to_register_ai_chat_server.isOfficial = true;
|
||||
to_register_ai_chat_server.socialActionConfig = UgcNpcHelper.toSocialActionConfigWithAIChatServer((META_ID)generalNpc.Value.SocialAction_default, generalNpc.Value.SocialAction_habit.Select(x => (META_ID)x).ToList());
|
||||
to_register_ai_chat_server.attributes.gender = (int)gender;
|
||||
|
||||
string json = JsonConvert.SerializeObject(to_register_ai_chat_server);
|
||||
var bodyParam = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
(result, string resMsg) = await sendAIChatServerWithServerJwt("POST", $"/api/character", json);
|
||||
if (result.isFail())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
GameServer/Global/Billing/BillingSchema.cs
Normal file
81
GameServer/Global/Billing/BillingSchema.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
public class BillingTestAccount
|
||||
{
|
||||
public string email { get; set; } = string.Empty;
|
||||
public string password { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class BillingTestJwt
|
||||
{
|
||||
public string jwt { get; set; } = string.Empty;
|
||||
public int isNew { get; set; } = 0;
|
||||
public string email { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class BillingPurchaseRequest
|
||||
{
|
||||
public string account_id { get; set; } = string.Empty;
|
||||
public string order_id { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class BillingPurchaseInfos
|
||||
{
|
||||
public int status_code { get; set; } = 0;
|
||||
public string status_message { get; set; } = string.Empty;
|
||||
public List<BuilingProductOrder> data { get; set; } = new();
|
||||
}
|
||||
|
||||
public class BuilingProductOrder
|
||||
{
|
||||
public string order_id { get; set; } = string.Empty;
|
||||
public string product_meta_id { get; set; } = string.Empty;
|
||||
public int buyDateTime { get; set; } = 0;
|
||||
public string state { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class BillingPurchaseState
|
||||
{
|
||||
public int status_code { get; set; } = 0;
|
||||
public string status_message { get; set; } = string.Empty;
|
||||
public BuilingProductState data { get; set; } = new();
|
||||
}
|
||||
|
||||
public class BuilingProductState
|
||||
{
|
||||
public string state { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class BillingChangeState
|
||||
{
|
||||
public string account_id { get; set; } = string.Empty;
|
||||
public List<BillingStateList> state_list { get; set; } = new();
|
||||
}
|
||||
|
||||
public class BillingStateList
|
||||
{
|
||||
public string order_id { get; set; } = string.Empty;
|
||||
public string state { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class BillingStateRes
|
||||
{
|
||||
public int status_code { get; set; } = 0;
|
||||
public string status_message { get; set; } = string.Empty;
|
||||
public List<BillingResStateList> state_list { get; set; } = new();
|
||||
}
|
||||
|
||||
public class BillingResStateList
|
||||
{
|
||||
public string order_id { get; set; } = string.Empty;
|
||||
public string bulk_result { get; set; } = string.Empty;
|
||||
public string bulk_message { get; set; } = string.Empty;
|
||||
public string state { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
307
GameServer/Global/Billing/BillingServerConnector.cs
Normal file
307
GameServer/Global/Billing/BillingServerConnector.cs
Normal file
@@ -0,0 +1,307 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
public class BillingServerConnector
|
||||
{
|
||||
static public string makeBillingServerURL(string url)
|
||||
{
|
||||
return $"{GameServerApp.getServerLogic().getServerConfig().BillingConfig.BaseAddress}{url}";
|
||||
}
|
||||
|
||||
//테스트 유저의 JWT 발급
|
||||
static public async Task<(Result, string jwt)> TestUserJwtIssue()
|
||||
{
|
||||
var result = OnlineConnectConfigCheck();
|
||||
if (result.isFail())
|
||||
return (result, string.Empty);
|
||||
|
||||
var billing = new BillingTestAccount() { email = "calilab@caliverse.io", password = "calilab1234!" };
|
||||
var body = JsonConvert.SerializeObject(billing);
|
||||
|
||||
var fullUrl = makeBillingServerURL($"/v1/wallet/auth/login");
|
||||
|
||||
(bool isSuccess, string resMsg) = await HttpClientHelper.sendHttpRequest( "POST", fullUrl, null, body
|
||||
, "application/json"
|
||||
, "caliverse", "1.0" );
|
||||
if (isSuccess == false)
|
||||
{
|
||||
Log.getLogger().error($"Failed billing server action : TestUserJwtIssue, msg : {resMsg}");
|
||||
result.setFail(ServerErrorCode.BillingGetPurchaseInfoFailed);
|
||||
return (result, string.Empty);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var test_jwt = JsonConvert.DeserializeObject<BillingTestJwt>(resMsg);
|
||||
NullReferenceCheckHelper.throwIfNull(test_jwt, () => $"test_jwt is null !!!");
|
||||
|
||||
return (result, test_jwt.jwt);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.getLogger().error($"Exception !!!, DeserializeObject : exception:{ex}, resMsg:{resMsg}");
|
||||
return (result, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
//유저의 구매내역들 조회
|
||||
static public async Task<(Result, BillingPurchaseInfos?)> GetBillingPurchaseInfos(BillingPurchaseRequest billingPurchaseRequest, string jwt)
|
||||
{
|
||||
var result = OnlineConnectConfigCheck();
|
||||
if (result.isFail())
|
||||
return (result, null);
|
||||
|
||||
var body = JsonConvert.SerializeObject(billingPurchaseRequest);
|
||||
|
||||
var fullUrl = makeBillingServerURL($"/v1/shop/products");
|
||||
|
||||
(bool isSuccess, string resMsg) = await HttpClientHelper.sendHttpRequest("GET", fullUrl, jwt, body
|
||||
, "application/json"
|
||||
, "caliverse", "1.0");
|
||||
if (isSuccess == false)
|
||||
{
|
||||
Log.getLogger().error($"Failed billing server action : GetBillingPurchaseInfos, fullUrl : {fullUrl}, jwt : {jwt}, order_id : {billingPurchaseRequest.order_id}, web_account_id : {billingPurchaseRequest.account_id}, msg : {resMsg}");
|
||||
result.setFail(ServerErrorCode.BillingGetPurchaseInfoFailed);
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var res = JsonConvert.DeserializeObject<BillingPurchaseInfos>(resMsg);
|
||||
NullReferenceCheckHelper.throwIfNull(res, () => $"res is null !!!");
|
||||
if (res.status_code != 200)
|
||||
{
|
||||
Log.getLogger().error($"Failed billing server action : GetBillingPurchaseInfo, fullUrl : {fullUrl}, jwt : {jwt}, order_id : {billingPurchaseRequest.order_id}, web_account_id : {billingPurchaseRequest.account_id}, msg : {res.status_message}");
|
||||
result.setFail(ServerErrorCode.BillingGetPurchaseInfoFailed);
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
res.data.Sort((x, y) => {
|
||||
return x.buyDateTime.CompareTo(y.buyDateTime);
|
||||
});
|
||||
|
||||
return (result, res);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.getLogger().error($"Exception !!!, DeserializeObject : exception:{ex}, resMsg:{resMsg}");
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//유저의 특정 구매 상태 조회
|
||||
static public async Task<(Result, BillingPurchaseState?)> GetBillingPurchaseInfo(BillingPurchaseRequest billingPurchaseRequest, string jwt)
|
||||
{
|
||||
var result = OnlineConnectConfigCheck();
|
||||
if (result.isFail())
|
||||
return (result, null);
|
||||
|
||||
var body = JsonConvert.SerializeObject(billingPurchaseRequest);
|
||||
|
||||
var fullUrl = makeBillingServerURL($"/v1/shop/product");
|
||||
|
||||
(bool isSuccess, string resMsg) = await HttpClientHelper.sendHttpRequest("GET", fullUrl, jwt, body
|
||||
, "application/json"
|
||||
, "caliverse", "1.0");
|
||||
if (isSuccess == false)
|
||||
{
|
||||
Log.getLogger().error($"Failed billing server action : GetBillingPurchaseInfo, fullUrl : {fullUrl}, jwt : {jwt}, order_id : {billingPurchaseRequest.order_id}, web_account_id : {billingPurchaseRequest.account_id}, msg : {resMsg}");
|
||||
result.setFail(ServerErrorCode.BillingGetPurchaseInfoFailed);
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var res = JsonConvert.DeserializeObject<BillingPurchaseState>(resMsg);
|
||||
NullReferenceCheckHelper.throwIfNull(res, () => $"res is null !!!");
|
||||
if (res.status_code != 200)
|
||||
{
|
||||
Log.getLogger().error($"Failed billing server action : GetBillingPurchaseInfo, fullUrl : {fullUrl}, jwt : {jwt}, order_id : {billingPurchaseRequest.order_id}, web_account_id : {billingPurchaseRequest.account_id}, msg : {res.status_message}");
|
||||
result.setFail(ServerErrorCode.BillingGetPurchaseInfoFailed);
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
return (result, res);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.getLogger().error($"Exception !!!, DeserializeObject : exception:{ex}, resMsg:{resMsg}");
|
||||
return (result, null);
|
||||
}
|
||||
}
|
||||
|
||||
//유저의 구매 상태 업데이트
|
||||
static public async Task<(Result, string)> UpdateBillingState(BillingChangeState billingChangeState, string jwt)
|
||||
{
|
||||
var result = OnlineConnectConfigCheck();
|
||||
if (result.isFail())
|
||||
return (result, string.Empty);
|
||||
|
||||
var body = JsonConvert.SerializeObject(billingChangeState);
|
||||
|
||||
var fullUrl = makeBillingServerURL($"/v1/shop/update");
|
||||
|
||||
(bool isSuccess, string resMsg) = await HttpClientHelper.sendHttpRequest("PUT", fullUrl, jwt, body
|
||||
, "application/json"
|
||||
, "caliverse", "1.0");
|
||||
|
||||
string orders_string = string.Empty;
|
||||
string states_string = string.Empty;
|
||||
foreach (var stateInfo in billingChangeState.state_list)
|
||||
{
|
||||
orders_string += $"{stateInfo.order_id},";
|
||||
states_string += $"{stateInfo.state},";
|
||||
}
|
||||
|
||||
if (isSuccess == false)
|
||||
{
|
||||
Log.getLogger().error($"Failed billing server action : UpdateBillingState, fullUrl : {fullUrl}, jwt : {jwt}, web_account_id : {billingChangeState.account_id}, order_ids : {orders_string}, update state : {states_string}, msg : {resMsg}");
|
||||
result.setFail(ServerErrorCode.BillingUpdateStateFailed);
|
||||
return (result, resMsg);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var res = JsonConvert.DeserializeObject<BillingStateRes>(resMsg);
|
||||
NullReferenceCheckHelper.throwIfNull(res, () => $"res is null !!!");
|
||||
if (res.status_code != 200)
|
||||
{
|
||||
Log.getLogger().error($"Failed billing server action : UpdateBillingState, fullUrl : {fullUrl}, jwt : {jwt}, web_account_id : {billingChangeState.account_id}, order_id : {orders_string}, update state : {states_string}, msg : {res.status_message}");
|
||||
result.setFail(ServerErrorCode.BillingUpdateStateFailed);
|
||||
return (result, res.status_message);
|
||||
}
|
||||
|
||||
return (result, res.status_message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.getLogger().error($"Exception !!!, DeserializeObject : exception:{ex}, resMsg:{resMsg}");
|
||||
return (result, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
static public async Task<(Result, BillingStateType)> GetBillingState(string jwt, string account_id, string packageOrderId)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var billingPurchaseRequest = new BillingPurchaseRequest() { account_id = account_id.ToString(), order_id = packageOrderId };
|
||||
(result, var state) = await GetBillingPurchaseInfo(billingPurchaseRequest, jwt);
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result, BillingStateType.none);
|
||||
}
|
||||
NullReferenceCheckHelper.throwIfNull(state, () => $"state is null !!!");
|
||||
|
||||
if (EnumHelper.tryParse<BillingStateType>(state.data.state, out var stateType) == false)
|
||||
{
|
||||
err_msg = $"Enum Parse Failed. BillingStateType : {state.data.state}";
|
||||
result.setFail(ServerErrorCode.BillingStateTypeInvalid, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, BillingStateType.none);
|
||||
}
|
||||
|
||||
return (result, stateType);
|
||||
}
|
||||
//빌링 상태값 업데이트 가능 여부 확인
|
||||
static public async Task<(Result, bool isUpdated)> AbleToChangeBillingState(string jwt, string account_id, string packageOrderId, BillingStateType toBillingStateType)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var billingPurchaseRequest = new BillingPurchaseRequest() { account_id = account_id.ToString(), order_id = packageOrderId };
|
||||
(result, var state) = await GetBillingPurchaseInfo(billingPurchaseRequest, jwt);
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result, false);
|
||||
}
|
||||
NullReferenceCheckHelper.throwIfNull(state, () => $"state is null !!!");
|
||||
|
||||
if (EnumHelper.tryParse<BillingStateType>(state.data.state, out var stateType) == false)
|
||||
{
|
||||
err_msg = $"Enum Parse Failed. BillingStateType : {state.data.state}";
|
||||
result.setFail(ServerErrorCode.BillingStateTypeInvalid, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, false);
|
||||
}
|
||||
|
||||
switch (toBillingStateType)
|
||||
{
|
||||
case BillingStateType.received:
|
||||
if (stateType != BillingStateType.paid)
|
||||
{
|
||||
err_msg = $"Invalid BillingState. current BillingStateType : {stateType}, toBillingStateType {toBillingStateType}";
|
||||
result.setFail(ServerErrorCode.BillingStateTypeInvalid, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, false);
|
||||
}
|
||||
break;
|
||||
case BillingStateType.opening:
|
||||
if (stateType == BillingStateType.refund || stateType == BillingStateType.refund_request)
|
||||
{
|
||||
err_msg = $"Invalid BillingState. current BillingStateType : {stateType}, toBillingStateType {toBillingStateType}";
|
||||
result.setFail(ServerErrorCode.BillingStateTypeRefund, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, false);
|
||||
}
|
||||
else if (stateType == BillingStateType.canceled)
|
||||
{
|
||||
err_msg = $"Invalid BillingState. current BillingStateType : {stateType}, toBillingStateType {toBillingStateType}";
|
||||
result.setFail(ServerErrorCode.BillingStateTypeRefundComplete, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, false);
|
||||
}
|
||||
else if (stateType == BillingStateType.opening || stateType == BillingStateType.opened)
|
||||
{
|
||||
return (result, true);
|
||||
}
|
||||
break;
|
||||
case BillingStateType.opened:
|
||||
if (stateType == BillingStateType.opened)
|
||||
{
|
||||
return (result, true);
|
||||
}
|
||||
else if (stateType != BillingStateType.opening)
|
||||
{
|
||||
err_msg = $"Invalid BillingState. current BillingStateType : {stateType}, toBillingStateType {toBillingStateType}";
|
||||
result.setFail(ServerErrorCode.BillingStateTypeInvalid, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
err_msg = $"Invalid BillingState. current BillingStateType : {stateType}, toBillingStateType {toBillingStateType}";
|
||||
result.setFail(ServerErrorCode.BillingStateTypeInvalid, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, false);
|
||||
}
|
||||
|
||||
return (result, false);
|
||||
}
|
||||
|
||||
static private Result OnlineConnectConfigCheck()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var server_config = GameServerApp.getServerLogic().getServerConfig();
|
||||
var billing_address = server_config.BillingConfig.BaseAddress;
|
||||
|
||||
if (server_config.OfflineMode == true || billing_address == string.Empty)
|
||||
{
|
||||
result.setFail(ServerErrorCode.ServerOfflineModeEnable);
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
163
GameServer/Global/Building/BuildingManager.cs
Normal file
163
GameServer/Global/Building/BuildingManager.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using Amazon.S3.Model;
|
||||
using ServerCommon;
|
||||
using ServerCore; using ServerBase;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
public class BuildingManager
|
||||
{
|
||||
ConcurrentDictionary<int, Building> m_buildings = new();
|
||||
|
||||
|
||||
public async Task<Result> loadBuildings()
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var building_meta_ids = MapManager.Instance.getBuildingMetaIds();
|
||||
foreach (var building_meta_id in building_meta_ids)
|
||||
{
|
||||
var building = new Building();
|
||||
await building.onInit();
|
||||
|
||||
m_buildings.TryAdd(building_meta_id, building);
|
||||
|
||||
var building_action = building.getEntityAction<BuildingAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(building_action, () => $"building_action is null !!!");
|
||||
|
||||
result = await building_action.tryLoadBuildingFromDb(building_meta_id);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryLoadBuildingFromDb() !!! : {result.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var building_floor_agent_action = building.getEntityAction<BuildingFloorAgentAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(building_floor_agent_action, () => $"building_floor_agent_action is null !!!");
|
||||
|
||||
result = await building_floor_agent_action.tryLoadBuildingFloorFromDb(building_meta_id);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryLoadBuildingFloorFromDb() !!! : {result.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var building_profit_agent_action = building.getEntityAction<BuildingProfitAgentAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(building_profit_agent_action, () => $"building_profit_agent_action is null !!!");
|
||||
|
||||
result = await building_profit_agent_action.tryLoadBuildingProfitFromDb(building_meta_id);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryLoadBuildingProfitFromDb() !!! : {result.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var building_profit_history_agent_action = building.getEntityAction<BuildingProfitHistoryAgentAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(building_profit_history_agent_action, () => $"building_profit_history_agent_action is null !!!");
|
||||
|
||||
result = await building_profit_history_agent_action.tryLoadBuildingProfitHistoryFromDb(building_meta_id);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryLoadBuildingProfitHistoryFromDb() !!! : {result.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var building_rental_history_agent_action = building.getEntityAction<BuildingRentalHistoryAgentAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(building_rental_history_agent_action, () => $"building_rental_history_agent_action is null !!!");
|
||||
|
||||
result = await building_rental_history_agent_action.tryLoadBuildingRentalHistoryFromDb(building_meta_id);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryLoadBuildingRentalHistoryFromDb() !!! : {result.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool tryGetBuilding(int buildingMetaId, [MaybeNullWhen(false)] out Building building)
|
||||
{
|
||||
return m_buildings.TryGetValue(buildingMetaId, out building);
|
||||
}
|
||||
|
||||
public async Task<List<BuildingInfo>> tryGetBuildingInfos()
|
||||
{
|
||||
var building_infos = new List<BuildingInfo>();
|
||||
|
||||
foreach (var building in m_buildings.Values)
|
||||
{
|
||||
var building_info = await building.toBuildingInfo();
|
||||
building_infos.Add(building_info);
|
||||
}
|
||||
|
||||
return building_infos;
|
||||
}
|
||||
|
||||
public List<(int, int, int)> checkRentalFinish()
|
||||
{
|
||||
var rental_finish_building_floors = new List<(int, int, int)>();
|
||||
|
||||
foreach (var building in m_buildings.Values)
|
||||
{
|
||||
var building_floor_agent_action = building.getEntityAction<BuildingFloorAgentAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(building_floor_agent_action, () => $"building_floor_agent_action is null !!!");
|
||||
|
||||
var building_floors = building_floor_agent_action.getBuildingFloors();
|
||||
foreach (var building_floor in building_floors.Values)
|
||||
{
|
||||
var building_floor_action = building_floor.getEntityAction<BuildingFloorAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(building_floor_action, () => $"building_floor_action is null !!!");
|
||||
|
||||
if (building_floor_action.isRentalFinish())
|
||||
{
|
||||
var (land_meta_id, building_meta_id, floor) = building_floor_action.getAddress();
|
||||
|
||||
rental_finish_building_floors.Add((land_meta_id, (int)building_meta_id, floor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rental_finish_building_floors;
|
||||
}
|
||||
|
||||
public Result tryRemoveBuildingFloor(int buildingMetaId, int floor)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
if (!m_buildings.TryGetValue(buildingMetaId, out var building))
|
||||
{
|
||||
err_msg = $"Failed to TryGetValue() !!! : buildingMetaId:{buildingMetaId} : {this.getTypeName()}";
|
||||
result.setFail(ServerErrorCode.BuildingNotFound, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var building_floor_agent_action = building.getEntityAction<BuildingFloorAgentAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(building_floor_agent_action, () => $"building_floor_agent_action is null !!!");
|
||||
|
||||
building_floor_agent_action.removeBuildingFloor(floor);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
74
GameServer/Global/Channel/ChannelManager.cs
Normal file
74
GameServer/Global/Channel/ChannelManager.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
public class GameChannel
|
||||
{
|
||||
public Int32 channel = 0;
|
||||
public Int32 trafficlevel = 0;
|
||||
}
|
||||
|
||||
public class ChannelManager
|
||||
{
|
||||
public List<GameChannel> m_channels = new List<GameChannel>(1000);
|
||||
|
||||
public ChannelManager() { }
|
||||
|
||||
public async Task updateChannels()
|
||||
{
|
||||
m_channels.Clear();
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
(var result, var server_infos) = await server_logic.getServerInfosByServerType(ServerType.Channel, server_logic.getWorldId());
|
||||
|
||||
foreach (var channel in server_infos)
|
||||
{
|
||||
if (GameServerApp.getServerLogic().getWorldId() != channel.WorldId) continue;
|
||||
|
||||
GameChannel gameChannel = new GameChannel();
|
||||
gameChannel.channel = channel.Channel;
|
||||
|
||||
var curr_users = channel.Sessions + channel.Reservation + channel.ReturnCount;
|
||||
gameChannel.trafficlevel = getTrafficLevel(curr_users, channel.Capacity);
|
||||
|
||||
m_channels.Add(gameChannel);
|
||||
}
|
||||
}
|
||||
|
||||
public List<GameChannel> getWholeChannels()
|
||||
{
|
||||
return m_channels;
|
||||
}
|
||||
|
||||
public int getTrafficLevel(int CurUser, int MaxUser)
|
||||
{
|
||||
double trafficPercent = (double)CurUser / (double)MaxUser * (double)100;
|
||||
int trafficLevel = 0;
|
||||
|
||||
if (0 <= trafficPercent && trafficPercent < 80.0f)
|
||||
{
|
||||
trafficLevel = 0;
|
||||
}
|
||||
else if (80.0f <= trafficPercent && trafficPercent < 100.0f)
|
||||
{
|
||||
trafficLevel = 1;
|
||||
}
|
||||
else if (100.0f <= trafficPercent)
|
||||
{
|
||||
trafficLevel = 2;
|
||||
}
|
||||
|
||||
|
||||
return trafficLevel;
|
||||
}
|
||||
}
|
||||
70
GameServer/Global/Land/LandManager.cs
Normal file
70
GameServer/Global/Land/LandManager.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using Grpc.Core;
|
||||
using MetaAssets;
|
||||
using ServerCommon;
|
||||
using ServerCore; using ServerBase;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
public class LandManager
|
||||
{
|
||||
ConcurrentDictionary<int, Land> m_lands = new();
|
||||
|
||||
|
||||
public async Task<Result> loadLands()
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var land_meta_ids = MapManager.Instance.getLandMetaIds();
|
||||
foreach (var land_meta_id in land_meta_ids)
|
||||
{
|
||||
var land = new Land();
|
||||
await land.onInit();
|
||||
|
||||
m_lands.TryAdd(land_meta_id, land);
|
||||
|
||||
var land_action = land.getEntityAction<LandAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_action, () => $"land_action is null !!!");
|
||||
|
||||
result = await land_action.tryLoadLandFromDb(land_meta_id);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryLoadLandFromDb() !!! : {result.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool tryGetLand(int landMetaId, [MaybeNullWhen(false)] out Land land)
|
||||
{
|
||||
return m_lands.TryGetValue(landMetaId, out land);
|
||||
}
|
||||
|
||||
public async Task<List<LandInfo>> getLandInfos()
|
||||
{
|
||||
var land_infos = new List<LandInfo>();
|
||||
|
||||
foreach (var land in m_lands.Values)
|
||||
{
|
||||
var land_info = await land.toLandInfo();
|
||||
if (land_info.LandMetaId == 0)
|
||||
continue;
|
||||
|
||||
land_infos.Add(land_info);
|
||||
}
|
||||
|
||||
return land_infos;
|
||||
}
|
||||
}
|
||||
}
|
||||
204
GameServer/Global/LandAuction/Helper/LandAuctionNotifyHelper.cs
Normal file
204
GameServer/Global/LandAuction/Helper/LandAuctionNotifyHelper.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
using static ClientToGameMessage.Types;
|
||||
using static ServerMessage.Types;
|
||||
|
||||
|
||||
using META_ID = System.UInt32;
|
||||
using USER_GUID = System.String;
|
||||
using USER_NICKNAME = System.String;
|
||||
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
public static class LandAuctionNotifyHelper
|
||||
{
|
||||
public static bool send_GS2C_NTF_LAND_AUCTION_ALL_LOAD(Player player, List<LandAuctionSummary>? landAuctionSummaries)
|
||||
{
|
||||
if(null == landAuctionSummaries)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var ntf_packet = new ClientToGame();
|
||||
ntf_packet.Message = new();
|
||||
|
||||
var ntf_msg = new GS2C_NTF_LAND_AUCTION_ALL_LOAD();
|
||||
ntf_packet.Message.NtfLandAuctionAllLoad = ntf_msg;
|
||||
|
||||
ntf_msg.LandAuctionsSummaries.AddRange(landAuctionSummaries.ToList());
|
||||
|
||||
if (false == GameServerApp.getServerLogic().onSendPacket(player, ntf_packet))
|
||||
{
|
||||
Log.getLogger().warn($"Failed to onSendPacket() !!! : {ntf_packet.toBasicString()} - {player.toBasicString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool broadcast_GS2C_NTF_LAND_AUCTION_SUMMARY(LandAuctionSummary landAuctionSummary)
|
||||
{
|
||||
var log_msg = $"call send_GS2C_NTF_LAND_AUCTION_SUMMARY() : {landAuctionSummary.LandAuctionInfo.toBasicString()}";
|
||||
Log.getLogger().debug(log_msg);
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var users = server_logic.getPlayerManager().getUsers();
|
||||
if (0 >= users.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var ntf_packet = new ClientToGame();
|
||||
ntf_packet.Message = new();
|
||||
|
||||
var ntf_msg = new GS2C_NTF_LAND_AUCTION_SUMMARY();
|
||||
ntf_packet.Message.NtfLandAuctionSummary = ntf_msg;
|
||||
|
||||
ntf_msg.LandAuctionSummary = landAuctionSummary;
|
||||
|
||||
var players = users.Values.ToArray();
|
||||
if (false == GameServerApp.getServerLogic().onSendPacket(players, ntf_packet))
|
||||
{
|
||||
Log.getLogger().warn($"Failed to onSendPacket() !!! : {ntf_packet.toBasicString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool broadcast_GS2GS_NTF_LAND_AUCTION_HIGHEST_BIDDER_CHANGE( USER_GUID receiverUserGuid, BoolType hasReceivedRefundMail
|
||||
, META_ID landMetaId
|
||||
, CurrencyType currencyType, double highestBidPrice
|
||||
, USER_GUID highestBidUserGuid, USER_NICKNAME highestBidUserNickname )
|
||||
{
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
|
||||
var message = new ServerMessage();
|
||||
var ntf_msg = new GS2GS_NTF_LAND_AUCTION_HIGHEST_BIDDER_CHANGE();
|
||||
message.NtfLandAuctionHighestBidderChange = ntf_msg;
|
||||
|
||||
ntf_msg.ReceiverUserGuid = receiverUserGuid;
|
||||
ntf_msg.HasReceivedRefundMail = hasReceivedRefundMail;
|
||||
ntf_msg.LandMetaId = (Int32)landMetaId;
|
||||
|
||||
ntf_msg.CurrencyType = currencyType;
|
||||
ntf_msg.HighestBidPrice = highestBidPrice;
|
||||
ntf_msg.HighestBidUserGuid = highestBidUserGuid;
|
||||
ntf_msg.HighestBidUserNickname = highestBidUserNickname;
|
||||
|
||||
var rabbit_mq = server_logic.getRabbitMqConnector() as RabbitMQ4Game;
|
||||
NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit_mq is null !!!");
|
||||
|
||||
rabbit_mq.sendMessageToExchangeAllGame(message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool send_GS2C_NTF_LAND_AUCTION_HIGHEST_BIDDER_CHANGE( Player player
|
||||
, META_ID landMetaId
|
||||
, CurrencyType bidCurrencyType, double highestBidPrice
|
||||
, USER_GUID highestUserGuid, USER_NICKNAME highestUserNickname )
|
||||
{
|
||||
var log_msg = $"call send_GS2C_NTF_LAND_AUCTION_HIGHEST_BIDDER_CHANGE !!! : landMetaId:{landMetaId}, highestBidPrice:{highestBidPrice}, highestUserNickname:{highestUserNickname}";
|
||||
Log.getLogger().debug(log_msg);
|
||||
|
||||
var ntf_packet = new ClientToGame();
|
||||
ntf_packet.Message = new();
|
||||
|
||||
var ntf_msg = new GS2C_NTF_LAND_AUCTION_HIGHEST_BIDDER_CHANGE();
|
||||
ntf_packet.Message.NtfLandAuctionHighestBidderChange = ntf_msg;
|
||||
|
||||
ntf_msg.LandMetaId = (Int32)landMetaId;
|
||||
ntf_msg.CurrencyType = bidCurrencyType;
|
||||
ntf_msg.HighestBidPrice = highestBidPrice;
|
||||
ntf_msg.HighestBidUserGuid = highestUserGuid;
|
||||
ntf_msg.HighestBidUserNickname = highestUserNickname;
|
||||
|
||||
if (false == GameServerApp.getServerLogic().onSendPacket(player, ntf_packet))
|
||||
{
|
||||
Log.getLogger().warn($"Failed to onSendPacket() !!! : {ntf_packet.toBasicString()} - {player.toBasicString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool broadcast_GS2C_NTF_LAND_AUCTION_WINNING_BID(USER_NICKNAME winningUserNickname, META_ID landMetaId)
|
||||
{
|
||||
var log_msg = $"call broadcast_GS2C_NTF_LAND_AUCTION_WINNING_BID !!! : winningUserNickname:{winningUserNickname}, landMetaId:{landMetaId}";
|
||||
Log.getLogger().debug(log_msg);
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var users = server_logic.getPlayerManager().getUsers();
|
||||
var players = users.Values.ToArray();
|
||||
|
||||
if (players.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var ntf_packet = new ClientToGame();
|
||||
ntf_packet.Message = new ClientToGameMessage();
|
||||
|
||||
var ntf_msg = new GS2C_NTF_LAND_AUCTION_WINNING_BID();
|
||||
ntf_packet.Message.NtfLandAuctionWinningBid = ntf_msg;
|
||||
ntf_msg.WinningUserNickname = winningUserNickname;
|
||||
ntf_msg.LandMetaId = (int)landMetaId;
|
||||
|
||||
if (false == GameServerApp.getServerLogic().onSendPacket(players, ntf_packet))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void broadcast_GS2GS_NTF_LAND_AUCTION_WINNING_BID( USER_GUID winningUserGuid, USER_NICKNAME winningUserNickname
|
||||
, META_ID landMetaId, List<META_ID> buildingMetaIds
|
||||
, BoolType isNewRecvMail = BoolType.True)
|
||||
{
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
|
||||
var message = new ServerMessage();
|
||||
var ntf_msg = new GS2GS_NTF_LAND_AUCTION_WINNING_BID();
|
||||
message.NtfLandAuctionWinningBid = ntf_msg;
|
||||
|
||||
ntf_msg.WinningUserGuid = winningUserGuid;
|
||||
ntf_msg.WinningUserNickname = winningUserNickname;
|
||||
ntf_msg.LandMetaId = (int)landMetaId;
|
||||
ntf_msg.BuildingMetaIds.AddRange( Array.ConvertAll<META_ID, int>(buildingMetaIds.ToArray(), Convert.ToInt32).ToList());
|
||||
ntf_msg.IsNewRecvMail = isNewRecvMail;
|
||||
|
||||
var rabbit_mq = server_logic.getRabbitMqConnector() as RabbitMQ4Game;
|
||||
NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit_mq is null !!!");
|
||||
|
||||
rabbit_mq.sendMessageToExchangeAllGame(message);
|
||||
}
|
||||
|
||||
public static void broadcast_GS2GS_NTF_LAND_AUCTION_RESERVATION(List<META_ID> toAddActivitings)
|
||||
{
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
|
||||
var message = new ServerMessage();
|
||||
var ntf_msg = new GS2GS_NTF_LAND_AUCTION_RESERVATION();
|
||||
message.NtfLandAuctionReservation = ntf_msg;
|
||||
|
||||
ntf_msg.ToAddActivitings.AddRange(Array.ConvertAll<META_ID, int>(toAddActivitings.ToArray(), Convert.ToInt32).ToList());
|
||||
|
||||
var rabbit_mq = server_logic.getRabbitMqConnector() as RabbitMQ4Game;
|
||||
NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit_mq is null !!!");
|
||||
|
||||
rabbit_mq.sendMessageToExchangeAllGame(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
using META_ID = System.UInt32;
|
||||
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
public static class LandAuctionReservationHelper
|
||||
{
|
||||
//=========================================================================================
|
||||
// 예약된 랜드 경매 정보가 있는지 확인하고
|
||||
// 있다면 활성화중인 랜드 경매의 종료 상태를 확인하여
|
||||
// 종료 상태라면 LandAuctionActivityDoc.AuctionNumber를 예약된 정보로 변경 및 저장 한다.
|
||||
//=========================================================================================
|
||||
public static async Task<(Result, bool)> configureNextLandAuctionToDb(META_ID landMetaId)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var db_connector = server_logic.getDynamoDbClient();
|
||||
|
||||
var last_auction_number = 0;
|
||||
|
||||
(result, var found_activity_doc) = await LandAuctionDbHelper.readLandAuctionActivityDocFromDb(landMetaId, false);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to readLandAuctionActivityDocFromDb() !!! : {result.toBasicString()} - landMetaId:{landMetaId}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, false);
|
||||
}
|
||||
if (null != found_activity_doc)
|
||||
{
|
||||
// 현재 활성화중인 랜드 경매 종료 상태를 체크 한다.
|
||||
var curr_activity_attrib = found_activity_doc.getAttrib<LandAuctionActivityAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(curr_activity_attrib, () => $"curr_activity_attrib is null !!! - landMetaId:{landMetaId}");
|
||||
var activity_auction_number = curr_activity_attrib.AuctionNumber;
|
||||
ConditionValidCheckHelper.throwIfFalseWithCondition( () => 0 < activity_auction_number
|
||||
, () => $"Invalid AuctionNubmer !!! : 0 < activityAuctionNumber:{activity_auction_number}"
|
||||
+ $" - landMetaId:{landMetaId}");
|
||||
last_auction_number = activity_auction_number;
|
||||
|
||||
(result, var last_registry_doc) = await LandAuctionDbHelper.readLandAuctionRegistryDocFromDb(landMetaId, last_auction_number);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to readLandAuctionRegistryDocFromDb() !!! : {result.toBasicString()} - landMetaId:{landMetaId}, lastAuctionNumber:{last_auction_number}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, false);
|
||||
}
|
||||
NullReferenceCheckHelper.throwIfNull(last_registry_doc, () => $"last_registry_doc is null !!! - landMetaId:{landMetaId}, lastAuctionNumber:{last_auction_number}");
|
||||
var last_registry_attrib = last_registry_doc.getAttrib<LandAuctionRegistryAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(last_registry_attrib, () => $"last_registry_attrib is null !!! - landMetaId:{landMetaId}, lastAuctionNumber:{last_auction_number}");
|
||||
|
||||
if(LandAuctionResult.None == last_registry_attrib.LandAuctionResult)
|
||||
{
|
||||
if (true == LandAuctionManager.It.hasActivitingLandAuction(landMetaId))
|
||||
{
|
||||
// 아직 경매 처리 진행전 이다 !!!
|
||||
return (result, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 활성화 목록에 로딩을 해야 한다 !!!
|
||||
return (result, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 다음 예약된 경매가 있는지 체크 한다.
|
||||
// 있다면 LandAuctionActivityDoc.AuctionNumber에 설정 한다. !!!
|
||||
var next_auction_number = last_auction_number + 1;
|
||||
(result, var found_registry_doc) = await LandAuctionDbHelper.readLandAuctionRegistryDocFromDb(landMetaId, next_auction_number, false);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to readLandAuctionRegistryDocFromDb() !!! : {result.toBasicString()} - landMetaId:{landMetaId}, auctionNumber:{next_auction_number}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, false);
|
||||
}
|
||||
if (null == found_registry_doc)
|
||||
{
|
||||
return (result, false);
|
||||
}
|
||||
var registry_attrib = found_registry_doc.getAttrib<LandAuctionRegistryAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(registry_attrib, () => $"registry_attrib is null !!! - landMetaId:{landMetaId}, auctionNumber:{next_auction_number}");
|
||||
|
||||
result = registry_attrib.checkValidLandAuctionRegistry();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result, false);
|
||||
}
|
||||
|
||||
if (null == found_activity_doc)
|
||||
{
|
||||
var new_activity_doc = new LandAuctionActivityDoc(landMetaId);
|
||||
found_activity_doc = new_activity_doc;
|
||||
}
|
||||
|
||||
var activity_attrib = found_activity_doc.getAttrib<LandAuctionActivityAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(activity_attrib, () => $"activity_attrib is null !!! - landMetaId:{landMetaId}");
|
||||
|
||||
if (next_auction_number <= activity_attrib.AuctionNumber)
|
||||
{
|
||||
// 이미 next_auction_number이상으로 설정 되었다면 갱신할 필요가 없다 !!!
|
||||
return (result, false);
|
||||
}
|
||||
|
||||
activity_attrib.AuctionNumber = next_auction_number;
|
||||
|
||||
result = await db_connector.simpleUpsertDocumentWithDocType(found_activity_doc);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to simpleInsertDocumentWithDocType<LandAuctionActivityDoc> !!! : {result.toBasicString()}, auctionNumber:{next_auction_number} - landMetaId:{landMetaId}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, false);
|
||||
}
|
||||
|
||||
LandAuctionBusinessLogHelper.writeBusinessLogByLandAuctionActivity(found_activity_doc);
|
||||
|
||||
err_msg = $"Configure Next LandAuctionKey !!!, nextAuctionNumber:{next_auction_number} - landMetaId:{landMetaId}";
|
||||
Log.getLogger().debug(err_msg);
|
||||
|
||||
return (result, true);
|
||||
}
|
||||
|
||||
public static async Task<(Result, Int32)> getNextLandAuctionNumber(META_ID landMetaId)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var db_connector = server_logic.getDynamoDbClient();
|
||||
|
||||
var last_auction_number = 0;
|
||||
|
||||
(result, var found_activity_doc) = await LandAuctionDbHelper.readLandAuctionActivityDocFromDb(landMetaId, false);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to readLandAuctionActivityDocFromDb() !!! : {result.toBasicString()} - landMetaId:{landMetaId}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, 0);
|
||||
}
|
||||
if (null != found_activity_doc)
|
||||
{
|
||||
var activity_attrib = found_activity_doc.getAttrib<LandAuctionActivityAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(activity_attrib, () => $"activity_attrib is null !!! - landMetaId:{landMetaId}");
|
||||
var activity_auction_number = activity_attrib.AuctionNumber;
|
||||
ConditionValidCheckHelper.throwIfFalseWithCondition( () => 0 < activity_auction_number
|
||||
, () => $"Invalid AuctionNubmer !!! : 0 < activityAuctionNumber:{activity_auction_number}"
|
||||
+ $" - landMetaId:{landMetaId}" );
|
||||
last_auction_number = activity_auction_number;
|
||||
}
|
||||
|
||||
var next_auction_number = 0;
|
||||
var is_continue = true;
|
||||
|
||||
while (is_continue)
|
||||
{
|
||||
next_auction_number = last_auction_number + 1;
|
||||
|
||||
(result, var found_registry_doc) = await LandAuctionDbHelper.readLandAuctionRegistryDocFromDb(landMetaId, next_auction_number, false);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to readLandAuctionRegistryDocFromDb() !!! : {result.toBasicString()} - landMetaId:{landMetaId}, auctionNumber:{next_auction_number}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, 0);
|
||||
}
|
||||
|
||||
if (null == found_registry_doc)
|
||||
{
|
||||
is_continue = false;
|
||||
break;
|
||||
}
|
||||
|
||||
last_auction_number = next_auction_number;
|
||||
}
|
||||
|
||||
return (result, next_auction_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
584
GameServer/Global/LandAuction/LandAuctionManager.cs
Normal file
584
GameServer/Global/LandAuction/LandAuctionManager.cs
Normal file
@@ -0,0 +1,584 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Diagnostics;
|
||||
|
||||
|
||||
|
||||
using Amazon.S3.Model;
|
||||
using Axion.Collections.Concurrent;
|
||||
using NeoSmart.AsyncLock;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
using ServerCommon;
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
using META_ID = System.UInt32;
|
||||
using LAND_AUCTION_KEY = System.String;
|
||||
using REQUESTOR_ID = System.String;
|
||||
|
||||
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
public class LandAuctionManager : Singleton<LandAuctionManager>
|
||||
{
|
||||
private AsyncLock m_reserved_lock = new();
|
||||
|
||||
// 랜드 경매가 활성화중인 목록
|
||||
private ConcurrentDictionary<META_ID, LandAuction> m_activitings = new();
|
||||
|
||||
// 랜드 경매가 종료된 목록
|
||||
private ConcurrentDictionary<META_ID, LandAuction> m_records = new();
|
||||
|
||||
public LandAuctionManager()
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<Result> tryActivitingLandAuctions( REQUESTOR_ID requestorId
|
||||
, List<META_ID> toAddActivitings
|
||||
, string callTid )
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
Stopwatch? stopwatch = null;
|
||||
var event_tid = string.Empty;
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var server_config = server_logic.getServerConfig();
|
||||
|
||||
using ( var releaser = await m_reserved_lock.LockAsync() )
|
||||
{
|
||||
if(0 < toAddActivitings.Count)
|
||||
{
|
||||
// 1. 현재 활성화중인 랜드 경매 키를 읽어 온다.
|
||||
var activiting_keys = getActivitings().Keys.ToList();
|
||||
|
||||
// 2. 활성화 목록에 추가할 랜드 경매 키를 추출 한다.
|
||||
var to_add_reserved_keys = KeyComparer.getKeysOnlyInSecond(activiting_keys, toAddActivitings);
|
||||
|
||||
// 3. 신규 랜드 경매를 활성화 목록에 추가 한다.
|
||||
foreach (var land_meta_id in to_add_reserved_keys)
|
||||
{
|
||||
result = await addActivitingLandAuctionByReservationKey(land_meta_id);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to addActivitingLandAuctionByReservationKey() !!!, in onTaskTick() : {result.toBasicString()} - landMetaId:{land_meta_id}, {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 활성화중인 모든 랜드 경매를 업데이트 한다.
|
||||
var activitings = getActivitings().Values.ToList();
|
||||
foreach (var land_auction in activitings)
|
||||
{
|
||||
if (true == server_config.PerformanceCheckEnable)
|
||||
{
|
||||
event_tid = System.Guid.NewGuid().ToString("N");
|
||||
stopwatch = Stopwatch.StartNew();
|
||||
}
|
||||
|
||||
var land_auction_action = land_auction.getEntityAction<LandAuctionAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!!");
|
||||
|
||||
var fn_land_auction_check = async delegate ()
|
||||
{
|
||||
var err_msg = $"LandAuctionCheck in LandAuctionCheckTicker.onTaskTick() !!! - TID:{callTid} - {land_auction.toBasicString()}";
|
||||
Log.getLogger().debug(err_msg);
|
||||
|
||||
var result = new Result();
|
||||
|
||||
var land_meta_id = land_auction_action.getLandMetaId();
|
||||
|
||||
result = await land_auction_action.tryCheckLandAuction(land_meta_id, requestorId, true);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryCheckLandAuction() !!! : {result.toBasicString()} - {land_auction.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
err_msg = $"Successed LandAuctionAction.tryCheckLandAuctionByTicker() !!! - TID:{callTid} - {land_auction.toBasicString()}";
|
||||
Log.getLogger().debug(err_msg);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
result = await land_auction.runTransactionRunnerSafelyWithTransGuid( requestorId
|
||||
, TransactionIdType.PrivateContents, "LandAuctionCheck"
|
||||
, fn_land_auction_check);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to runTransactionRunnerSafely()!!! : {result.toBasicString()} - {land_auction.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
}
|
||||
|
||||
if (null != stopwatch)
|
||||
{
|
||||
var elapsed_msec = stopwatch.ElapsedMilliseconds;
|
||||
stopwatch.Stop();
|
||||
|
||||
if (1000 <= elapsed_msec)
|
||||
{
|
||||
Log.getLogger().debug( $"{this.getTypeName()} Performance alert !!! : Execution delayed !!!"
|
||||
+ $" - ETID:{event_tid}, ElapsedMSec:{elapsed_msec}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> tryConfigureAndAddReservedLandAuctioKeyAll(REQUESTOR_ID requstorId)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var reserved_land_meta_ids = new HashSet<META_ID>();
|
||||
|
||||
(var auctionable_land_meta_ids, var calculated_ttl_sec) = getAuctionableLandMetaIdsWithTtlSec();
|
||||
if(0 >= auctionable_land_meta_ids.Count)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// 1. 랜드 경매 예약 설정 WriteLock 권한을 획득 한다.
|
||||
var is_success = await LandAuctionCacheHelper.tryAcquireWriteLockWithLandAuctionReservation(requstorId, calculated_ttl_sec);
|
||||
if (true == is_success)
|
||||
{
|
||||
foreach (var land_meta_id in auctionable_land_meta_ids)
|
||||
{
|
||||
var is_continue = false;
|
||||
|
||||
// 1.1. 랜드 경매의 예약 가능 여부를 체크하고 예약 설정 한다.
|
||||
(result, var is_to_add_activiting) = await LandAuctionReservationHelper.configureNextLandAuctionToDb(land_meta_id);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to configureNextAuctionToDb() !!! : {result.toBasicString()} - landMetaId:{land_meta_id}";
|
||||
Log.getLogger().error(err_msg);
|
||||
is_continue = true;
|
||||
}
|
||||
if (false == is_to_add_activiting)
|
||||
{
|
||||
is_continue = true;
|
||||
}
|
||||
if (true == is_continue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 랜드 메타 id를 예약 목록에 등록 한다.
|
||||
if (false == reserved_land_meta_ids.Add(land_meta_id))
|
||||
{
|
||||
err_msg = $"Failed to Add() !!!, in tryConfigureAndAddReservedLandAuctionAll(), Already exist Reserved Key !!! : landMetaId:{land_meta_id}";
|
||||
Log.getLogger().error(err_msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
err_msg = $"LandAuction ReservedKeys to add Key !!! : landMetaId:{land_meta_id}";
|
||||
Log.getLogger().debug(err_msg);
|
||||
}
|
||||
await LandAuctionCacheHelper.tryReleaseWriteLockWithLandAuctionReservation(requstorId);
|
||||
|
||||
if (0 < reserved_land_meta_ids.Count)
|
||||
{
|
||||
LandAuctionNotifyHelper.broadcast_GS2GS_NTF_LAND_AUCTION_RESERVATION(reserved_land_meta_ids.ToList());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private (List<META_ID>, short) getAuctionableLandMetaIdsWithTtlSec()
|
||||
{
|
||||
var auctionable_land_meta_ids = new HashSet<META_ID>();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
foreach (var each in MetaData.Instance._LandTable)
|
||||
{
|
||||
var land_meta = each.Value;
|
||||
|
||||
var land_meta_id = (META_ID)land_meta.LandId;
|
||||
if (EditorType.USER != land_meta.Editor)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var result = OwnedLandHelper.checkLandWithoutOwner((META_ID)land_meta_id);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to checkLandWithoutOwner() !!! : {result.toBasicString()}, landMetaId:{land_meta_id}";
|
||||
Log.getLogger().debug(err_msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(false == auctionable_land_meta_ids.Add(land_meta_id))
|
||||
{
|
||||
err_msg = $"Failed to Add() !!!, in getAuctionableLandMetaIdsWithTtlSec() - landMetaId:{land_meta_id}, {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
short ttl_sec = 0;
|
||||
if(0 < auctionable_land_meta_ids.Count)
|
||||
{
|
||||
ttl_sec = (short)(auctionable_land_meta_ids.Count * 2);
|
||||
}
|
||||
|
||||
return (auctionable_land_meta_ids.ToList(), ttl_sec);
|
||||
}
|
||||
|
||||
public async Task<Result> addActivitingLandAuctionByReservationKey(META_ID landMetaId)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
if (false == m_activitings.TryGetValue(landMetaId, out var found_land_auction))
|
||||
{
|
||||
var new_land_auction = new LandAuction();
|
||||
result = await new_land_auction.onInit();
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to LandAuction.onInit() !!!, in add_land_auction() - landMetaId:{landMetaId}, {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
m_activitings[landMetaId] = new_land_auction;
|
||||
found_land_auction = new_land_auction;
|
||||
|
||||
var registry_attribute = new_land_auction.getEntityAttribute<LandAuctionRegistryAttribute>();
|
||||
NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!! - landMetaId:{landMetaId}, {toBasicString()}");
|
||||
registry_attribute.LandMetaId = landMetaId;
|
||||
}
|
||||
|
||||
var land_auction_action = found_land_auction.getEntityAction<LandAuctionAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - {found_land_auction.toBasicString()}");
|
||||
|
||||
err_msg = $"LandAuction Activitings <= ReservedKeys !!! : landMetaId:{landMetaId}, auctionNumber:{land_auction_action.getAuctionNumber()}";
|
||||
Log.getLogger().debug(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> tryLoadLandAuctionAll()
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
(result, var activity_docs) = await LandAuctionDbHelper.readLandAuctionActivityDocsFromDb();
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to readLandAuctionActivityDocsFromDb() !!! : {result.toBasicString()} - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
NullReferenceCheckHelper.throwIfNull(activity_docs, () => $"activity_docs is null !!! - {toBasicString()}");
|
||||
|
||||
foreach(var activity_doc in activity_docs)
|
||||
{
|
||||
var activity_attrib = activity_doc.getAttrib<LandAuctionActivityAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(activity_attrib, () => $"activity_attrib is null !!! - {toBasicString()}");
|
||||
|
||||
var land_meta_id = activity_attrib.LandMetaId;
|
||||
var auction_number = activity_attrib.AuctionNumber;
|
||||
|
||||
(var registry_result, _) = await tryLoadLandAuctionByMetaId(land_meta_id);
|
||||
if(registry_result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryLoadLandAuctionByMetaId() !!! : {registry_result.toBasicString()} - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
(result, var record_docs) = await LandAuctionDbHelper.readLandAuctionRecordDocsFromDb();
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to readLandAuctionRecordDocsFromDb() !!! : {result.toBasicString()} - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
NullReferenceCheckHelper.throwIfNull(record_docs, () => $"record_docs is null !!!");
|
||||
|
||||
foreach (var record_doc in record_docs)
|
||||
{
|
||||
var record_attrib = record_doc.getAttrib<LandAuctionRecordAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(record_attrib, () => $"record_attrib is null !!! - {toBasicString()}");
|
||||
|
||||
var land_meta_id = record_attrib.LandMetaId;
|
||||
var auction_number = record_attrib.AuctionNumber;
|
||||
|
||||
var found_land_auction = findRecordLandAuction(land_meta_id);
|
||||
if(null == found_land_auction)
|
||||
{
|
||||
err_msg = $"Not found record LandAuction !!!, in Record LandAuction : landMetaId:{land_meta_id}, auctionNumber:{auction_number} - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
var land_auction_action = found_land_auction.getEntityAction<LandAuctionAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - {toBasicString()}");
|
||||
|
||||
var record_result = await land_auction_action.tryLoadLandAuctionForRecord(auction_number);
|
||||
if (record_result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryLoadLandAuctionForRecord() !!! : {record_result.toBasicString()} - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void sendLandAuctionAllToUserGuid(Player player)
|
||||
{
|
||||
var err_msg = string.Empty;
|
||||
|
||||
if( 0 >= m_activitings.Count
|
||||
&& 0 >= m_records.Count )
|
||||
{
|
||||
err_msg = $"No loaded LandAuction !!! - {toBasicString()}, {player.toBasicString()}";
|
||||
Log.getLogger().warn(err_msg);
|
||||
return;
|
||||
}
|
||||
|
||||
var land_auction_summaries = getLandAuctionAll();
|
||||
|
||||
if (false == LandAuctionNotifyHelper.send_GS2C_NTF_LAND_AUCTION_ALL_LOAD(player, land_auction_summaries))
|
||||
{
|
||||
err_msg = $"Failed to send_GS2C_NTF_LAND_AUCTION_ALL_LOAD() !!! - {toBasicString()} - {player.toBasicString()}";
|
||||
Log.getLogger().warn(err_msg);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.getLogger().info($"Sent LandAuction All : Count:{land_auction_summaries.Count} - {toBasicString()}");
|
||||
}
|
||||
|
||||
public async Task<(Result, LandAuction?)> tryLoadLandAuctionByMetaId(META_ID landMetaId)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var land_auction = new LandAuction();
|
||||
result = land_auction.onInit().GetAwaiter().GetResult();
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to LandAuction.onInit() !!!, in add_land_auction() - landMetaId:{landMetaId}, {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
var land_auction_action = land_auction.getEntityAction<LandAuctionAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - landMetaId:{landMetaId}, {toBasicString()}");
|
||||
|
||||
result = await land_auction_action.tryLoadLandAuctionMetaWithLock(landMetaId);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryLoadLandAuctionMeta() !!! : {result.toBasicString()} - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
if (true == land_auction_action.isLandAuctionState(LandAuctionState.Ended))
|
||||
{
|
||||
m_records.TryAdd(landMetaId, land_auction);
|
||||
}
|
||||
else
|
||||
{
|
||||
var add_land_auction = delegate (META_ID landMetaId)
|
||||
{
|
||||
return land_auction;
|
||||
};
|
||||
|
||||
var found_land_auction = m_activitings.AddOrUpdate(landMetaId, add_land_auction, (key, value) => value);
|
||||
NullReferenceCheckHelper.throwIfNull(found_land_auction, () => $"found_land_auction is null !!! - landMetaId:{landMetaId}, {toBasicString()}");
|
||||
}
|
||||
|
||||
return (result, land_auction);
|
||||
}
|
||||
|
||||
public async Task<(Result, LandAuctionCheckResult?)> tryCheckActivitingLandAuctionByMetaId(META_ID landMetaId, REQUESTOR_ID requestorId)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var is_load_required_meta = true;
|
||||
|
||||
if (false == MetaData.Instance._LandTable.TryGetValue((Int32)landMetaId, out var land_meta_data))
|
||||
{
|
||||
err_msg = $"Failed to TryGetValue() !!! : landMetaId:{landMetaId} - requestorId:{requestorId}";
|
||||
result.setFail(ServerErrorCode.LandMetaDataNotFound, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
if (false == m_activitings.TryGetValue(landMetaId, out var found_land_auction))
|
||||
{
|
||||
err_msg = $"Failed to TryGetValue() !!! : landMetaId:{landMetaId} - requestorId:{requestorId}";
|
||||
result.setFail(ServerErrorCode.LandAuctionNotFound, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return (result, null);
|
||||
}
|
||||
NullReferenceCheckHelper.throwIfNull(found_land_auction, () => $"found_land_auction is null !!! - landMetaId:{landMetaId}, requestorId:{requestorId}");
|
||||
|
||||
var land_auction_action = found_land_auction.getEntityAction<LandAuctionAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - landMetaId:{landMetaId}, requestorId:{requestorId}, {toBasicString()}");
|
||||
|
||||
(result, var auction_check_result) = await land_auction_action.tryCheckActivitingLandAuctionWithTransactionRunner(landMetaId, requestorId, is_load_required_meta);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to tryCheckActivitingLandAuctionWithTransactionRunner() !!! : {result.toBasicString()} - landMetaId:{landMetaId}, requestorId{requestorId}, {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
return (result, auction_check_result);
|
||||
}
|
||||
|
||||
public void activitingToRecord(META_ID landMetaId, LandAuction landAuction)
|
||||
{
|
||||
var err_msg = string.Empty;
|
||||
|
||||
if(false == m_activitings.TryRemove(landMetaId, out _))
|
||||
{
|
||||
err_msg = $"Failed to TryRemove() !!!, in Activitings : {landAuction.toBasicString()} - {toBasicString()}";
|
||||
Log.getLogger().debug(err_msg);
|
||||
}
|
||||
|
||||
m_records[landMetaId] = landAuction;
|
||||
|
||||
var land_auction_action = landAuction.getEntityAction<LandAuctionAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - {landAuction.toBasicString()}");
|
||||
|
||||
err_msg = $"LandAuction Records <= Activitings !!! : landMetaId:{landMetaId}, auctionNumber:{land_auction_action.getAuctionNumber()}";
|
||||
Log.getLogger().debug(err_msg);
|
||||
}
|
||||
|
||||
public LandAuction? findRecordLandAuction(META_ID landMetaId)
|
||||
{
|
||||
m_records.TryGetValue(landMetaId, out var found_land_auction);
|
||||
return found_land_auction;
|
||||
}
|
||||
|
||||
public LandAuction? findActivitingLandAuction(META_ID landMetaId)
|
||||
{
|
||||
m_activitings.TryGetValue(landMetaId, out var found_land_auction);
|
||||
return found_land_auction;
|
||||
}
|
||||
|
||||
public bool hasActivitingLandAuction(META_ID landMetaId)
|
||||
{
|
||||
return m_activitings.ContainsKey(landMetaId);
|
||||
}
|
||||
|
||||
public async Task<List<LandAuctionCompact>> getLandAuctionHistoryAll()
|
||||
{
|
||||
var land_auction_compacts = new List<LandAuctionCompact>();
|
||||
|
||||
foreach (var each in m_records)
|
||||
{
|
||||
var land_auction = each.Value;
|
||||
var land_auction_action = land_auction.getEntityAction<LandAuctionAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - {toBasicString()}");
|
||||
|
||||
if (true == land_auction_action.isHistory())
|
||||
{
|
||||
var land_auction_compact = await land_auction_action.toLandAuctionCompact();
|
||||
if (null == land_auction_compact)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
land_auction_compacts.Add(land_auction_compact);
|
||||
}
|
||||
}
|
||||
|
||||
return land_auction_compacts;
|
||||
}
|
||||
|
||||
public List<LandAuctionSummary> getLandAuctionScheduleAll()
|
||||
{
|
||||
var land_auction_summaries = new List<LandAuctionSummary>();
|
||||
|
||||
foreach (var each in m_activitings)
|
||||
{
|
||||
var land_auction = each.Value;
|
||||
var land_auction_action = land_auction.getEntityAction<LandAuctionAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - {toBasicString()}");
|
||||
|
||||
if(true == land_auction_action.isScheduling())
|
||||
{
|
||||
land_auction_summaries.Add(land_auction_action.toLandAuctionSummary());
|
||||
}
|
||||
}
|
||||
|
||||
return land_auction_summaries;
|
||||
}
|
||||
|
||||
public List<LandAuctionSummary> getLandAuctionAll()
|
||||
{
|
||||
var land_auction_summaries = new List<LandAuctionSummary>();
|
||||
|
||||
foreach (var each in m_activitings)
|
||||
{
|
||||
var land_auction = each.Value;
|
||||
var land_auction_action = land_auction.getEntityAction<LandAuctionAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - {toBasicString()}");
|
||||
|
||||
if ( true == land_auction_action.isLandAuctionState(LandAuctionState.Scheduled )
|
||||
|| true == land_auction_action.isLandAuctionState(LandAuctionState.Started )
|
||||
|| true == land_auction_action.isLandAuctionState(LandAuctionState.Ended )
|
||||
)
|
||||
{
|
||||
land_auction_summaries.Add(land_auction_action.toLandAuctionSummary());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var each in m_records)
|
||||
{
|
||||
var land_auction = each.Value;
|
||||
var land_auction_action = land_auction.getEntityAction<LandAuctionAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - {toBasicString()}");
|
||||
|
||||
if(land_auction_action.isLandAuctionResult(LandAuctionResult.Successed))
|
||||
{
|
||||
land_auction_summaries.Add(land_auction_action.toLandAuctionSummary());
|
||||
}
|
||||
}
|
||||
|
||||
return land_auction_summaries;
|
||||
}
|
||||
|
||||
|
||||
public ConcurrentDictionary<META_ID, LandAuction> getRecords() => m_records;
|
||||
|
||||
public ConcurrentDictionary<META_ID, LandAuction> getActivitings() => m_activitings;
|
||||
|
||||
public string toBasicString()
|
||||
{
|
||||
return $"{GameServerApp.getServerLogic().toBasicString()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
67
GameServer/Global/NFT/NFTInfo.cs
Normal file
67
GameServer/Global/NFT/NFTInfo.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
namespace NFT
|
||||
{
|
||||
public class NFTInfo
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; } = string.Empty; // NFT Asset 내부 관리 DB의 Key (DB 저장 시 Incremental)
|
||||
|
||||
[JsonProperty("projectName")]
|
||||
public string ProjectName { get; set; } = string.Empty; // 프로젝트 고유 식별자
|
||||
|
||||
[JsonProperty("contractAddress")]
|
||||
public string ContractAddress { get; set; } = string.Empty; // 컨트랙트 주소
|
||||
|
||||
[JsonProperty("blockchainNetwork")]
|
||||
public string BlockchainNetwork { get; set; } = string.Empty; // 블록체인 네트워크 (메인넷 또는 테스트넷)
|
||||
|
||||
[JsonProperty("tokenId")]
|
||||
public Int32 TokenId { get; set; } = 0; // 토큰 ID
|
||||
|
||||
[JsonProperty("owner")]
|
||||
public string Owner { get; set; } = string.Empty; // 소유자
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; } = string.Empty; // NFT명
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; } = string.Empty; // 설명
|
||||
|
||||
[JsonProperty("imageUrl")]
|
||||
public string ImageUrl { get; set; } = string.Empty; // Image Resource Url
|
||||
|
||||
[JsonProperty("isStaked")]
|
||||
public Int32 IsStaked { get; set; } = 0; // Staking 상태: 1, 보유 상태: 0
|
||||
|
||||
[JsonProperty("attributes")]
|
||||
public List<Attribute> Attributes { get; set; } = new List<Attribute>(); // 속성 목록
|
||||
|
||||
[JsonProperty("unstakingDisabled")]
|
||||
public Int32 UnstakingDisabled { get; set; } = 0; // 인벤토리의 NFT를 외부지갑으로 전송가능: 1, 인벤토리의 NFT를 외부지갑으로 전송 불가능: 0
|
||||
}
|
||||
|
||||
public class Attribute
|
||||
{
|
||||
[JsonProperty("trait_type")]
|
||||
public string TraitType { get; set; } = string.Empty; // 속성명
|
||||
|
||||
[JsonProperty("value")]
|
||||
public string Value { get; set; } = string.Empty; // 속성값
|
||||
}
|
||||
}
|
||||
}
|
||||
107
GameServer/Global/NoticeChat/NoticeChat.cs
Normal file
107
GameServer/Global/NoticeChat/NoticeChat.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
public class NoticeChat : EntityBase
|
||||
{
|
||||
private NoticeChat(EntityBase parent)
|
||||
: base(EntityType.NoticeChat, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<Result> onInit()
|
||||
{
|
||||
addEntityAttribute(new NoticeChatAttribute(this));
|
||||
|
||||
return await base.onInit();
|
||||
}
|
||||
|
||||
public static async Task<(Result, NoticeChat?)> createTestNoticeChat(EntityBase owner, int chat_id, int messageType, string KoMessage, string EnMessage)
|
||||
{
|
||||
var notice_chat = new NoticeChat(owner);
|
||||
var result = await notice_chat.onInit();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
var err_msg = string.Empty;
|
||||
DateTime now = DateTime.UtcNow;
|
||||
|
||||
var notice_chat_attribute = notice_chat.getEntityAttribute<NoticeChatAttribute>();
|
||||
if (notice_chat_attribute == null)
|
||||
{
|
||||
err_msg = $"Failed to get notice chat attribute : {nameof(NoticeChatAttribute)}";
|
||||
result.setFail(ServerErrorCode.EntityAttributeIsNull, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
List<NoticeChatDetail> detailInfos = new();
|
||||
detailInfos.Add(new NoticeChatDetail() { ChatMessage = $"{chat_id} {KoMessage}", Languagetype = LanguageType.Ko });
|
||||
detailInfos.Add(new NoticeChatDetail() { ChatMessage = $"{chat_id} {EnMessage}", Languagetype = LanguageType.En });
|
||||
detailInfos.Add(new NoticeChatDetail() { ChatMessage = $"{chat_id} あなたの名前は何ですか", Languagetype = LanguageType.Ja });
|
||||
|
||||
notice_chat_attribute.ChatId = chat_id;
|
||||
notice_chat_attribute.NextNoticeTime = now;
|
||||
notice_chat_attribute.RepeatCount = 10;
|
||||
notice_chat_attribute.RepeatMinuteTime = 1;
|
||||
notice_chat_attribute.Sender = "[GM]그리노스";
|
||||
notice_chat_attribute.MessageType = messageType;
|
||||
notice_chat_attribute.DetailList = detailInfos;
|
||||
|
||||
notice_chat_attribute.newEntityAttribute();
|
||||
|
||||
return (result, notice_chat);
|
||||
}
|
||||
|
||||
public static async Task<(Result, NoticeChat?)> createMailFromDoc(EntityBase parent, NoticeChatDoc doc)
|
||||
{
|
||||
var system_mail = new NoticeChat(parent);
|
||||
var result = await system_mail.onInit();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var notice_chat_attribute = system_mail.getEntityAttribute<NoticeChatAttribute>();
|
||||
if (notice_chat_attribute == null)
|
||||
{
|
||||
err_msg = $"Failed to get notice chat attribute : {nameof(NoticeChatAttribute)}";
|
||||
result.setFail(ServerErrorCode.EntityAttributeIsNull, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
if (notice_chat_attribute.copyEntityAttributeFromDoc(doc) == false)
|
||||
{
|
||||
err_msg = $"Failed to copyEntityAttributeFromDoc !!! : doc_type {doc.GetType()} - {system_mail.getRootParent().toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
return (result, system_mail);
|
||||
}
|
||||
|
||||
public override string toBasicString()
|
||||
{
|
||||
return $"{this.getTypeName()} - {getRootParent().toBasicString()}";
|
||||
}
|
||||
|
||||
public override string toSummaryString()
|
||||
{
|
||||
return $"{this.getTypeName()} - {getRootParent().toBasicString()}";
|
||||
}
|
||||
}
|
||||
246
GameServer/Global/NoticeChat/NoticeChatManager.cs
Normal file
246
GameServer/Global/NoticeChat/NoticeChatManager.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
using ServerCommon;
|
||||
using System.Collections.Concurrent;
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
public class NoticeChatManager : EntityBase, IWithLogActor
|
||||
{
|
||||
ConcurrentDictionary<Int32 ,NoticeChat> m_notice_chats = new();
|
||||
|
||||
public NoticeChatManager()
|
||||
: base(EntityType.NoticeChatManager)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<Result> LoadDB()
|
||||
{
|
||||
var result = new Result();
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var dynamo_db_client = server_logic.getDynamoDbClient();
|
||||
ArgumentNullException.ThrowIfNull(server_logic, $"server_logic is null !!!");
|
||||
ArgumentNullException.ThrowIfNull(dynamo_db_client, $"dynamo_db_client is null !!!");
|
||||
|
||||
var doc = new NoticeChatDoc();
|
||||
var query_config = dynamo_db_client.makeQueryConfigForReadByPKOnly(doc.getPK());
|
||||
(result, var read_doc_list) = await dynamo_db_client.simpleQueryDocTypesWithQueryOperationConfig<NoticeChatDoc>(query_config);
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (var read_doc in read_doc_list)
|
||||
{
|
||||
(result, var notice_chat) = await NoticeChat.createMailFromDoc(this, read_doc);
|
||||
if (result.isFail() || notice_chat == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var notice_chat_attribute = notice_chat.getEntityAttribute<NoticeChatAttribute>();
|
||||
if (notice_chat_attribute == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 지정한 시간으로부터 repeat count만큼만 공지사항을 보낸다.
|
||||
while (true)
|
||||
{
|
||||
if (notice_chat_attribute.RepeatCount == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (notice_chat_attribute.NextNoticeTime > DateTime.UtcNow)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
notice_chat_attribute.RepeatCount -= 1;
|
||||
notice_chat_attribute.NextNoticeTime = notice_chat_attribute.NextNoticeTime.AddMinutes(notice_chat_attribute.RepeatMinuteTime);
|
||||
}
|
||||
|
||||
if (notice_chat_attribute.RepeatCount == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m_notice_chats.TryAdd(notice_chat_attribute.ChatId, notice_chat);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<Result> UpdateChat()
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var player_manager = server_logic.getPlayerManager();
|
||||
ArgumentNullException.ThrowIfNull(server_logic, $"server_logic is null !!!");
|
||||
ArgumentNullException.ThrowIfNull(player_manager, $"player_manager is null !!!");
|
||||
|
||||
List<NoticeChat> copy_notice_chats = CopyData();
|
||||
foreach (var notice_chat in copy_notice_chats)
|
||||
{
|
||||
var notice_chat_attribute = notice_chat.getEntityAttribute<NoticeChatAttribute>();
|
||||
if(notice_chat_attribute == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (notice_chat_attribute.NextNoticeTime <= DateTime.UtcNow)
|
||||
{
|
||||
if (notice_chat_attribute.MessageType != 10 && notice_chat_attribute.MessageType != 11)
|
||||
{
|
||||
err_msg = $"NoticeChatInfo messageType is invalid. - GMTOOL Data. messageType : {notice_chat_attribute.MessageType}";
|
||||
Log.getLogger().error(err_msg);
|
||||
m_notice_chats.TryRemove(notice_chat_attribute.ChatId, out var removed_chat);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (notice_chat_attribute.RepeatCount <= 0)
|
||||
{
|
||||
m_notice_chats.TryRemove(notice_chat_attribute.ChatId, out var removed_chat);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(EnumHelper.isDefined<ChatType>(notice_chat_attribute.MessageType) == false)
|
||||
{
|
||||
err_msg = $"is Not Defined EChatType !!! EChatType : {notice_chat_attribute.MessageType} - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
Dictionary<LanguageType, (string Sender, ChatType MessageType, string ChatMessage)> languageChatInfos = new();
|
||||
foreach (var detail in notice_chat_attribute.DetailList)
|
||||
{
|
||||
languageChatInfos.Add(detail.Languagetype, (notice_chat_attribute.Sender, (ChatType)notice_chat_attribute.MessageType, detail.ChatMessage));
|
||||
}
|
||||
|
||||
var receivers = player_manager.getUsers();
|
||||
foreach (var receiver in receivers)
|
||||
{
|
||||
var player = receiver.Value;
|
||||
ArgumentNullException.ThrowIfNull(player, $"player is null !!! - {player.toBasicString()}");
|
||||
|
||||
var user_create_or_load_action = player.getEntityAction<UserCreateOrLoadAction>();
|
||||
ArgumentNullException.ThrowIfNull(user_create_or_load_action, $"user_create_or_load_action is null !!! - {player.toBasicString()}");
|
||||
|
||||
if (false == user_create_or_load_action.isCompletedLoadUser())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var account_attribute = player.getEntityAttribute<AccountAttribute>();
|
||||
ArgumentNullException.ThrowIfNull(account_attribute, $"account_attribute is null !!! - {player.toBasicString()}");
|
||||
|
||||
if (languageChatInfos.TryGetValue(account_attribute.LanguageType, out var chat_info) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ChatNotifyHelper.send_S2C_NTF_CHAT(receiver.Value, chat_info.MessageType, chat_info.Sender, receiver.Value.getUserNickname(), PlayerStateType.None, chat_info.ChatMessage);
|
||||
}
|
||||
|
||||
notice_chat_attribute.RepeatCount -= 1;
|
||||
notice_chat_attribute.NextNoticeTime = notice_chat_attribute.NextNoticeTime.AddMinutes(notice_chat_attribute.RepeatMinuteTime);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
private List<NoticeChat> CopyData()
|
||||
{
|
||||
return new List<NoticeChat>(m_notice_chats.Values);
|
||||
}
|
||||
|
||||
public async Task<Result> CheatFuncSaveNoticeChat(int messageType, string KoMessage, string EnMessage)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var dynamo_db_client = server_logic.getDynamoDbClient();
|
||||
ArgumentNullException.ThrowIfNull(server_logic, $"server_logic is null !!!");
|
||||
ArgumentNullException.ThrowIfNull(dynamo_db_client, $"dynamo_db_client is null !!!");
|
||||
|
||||
var fn_cheat_notice_chat_write = async delegate ()
|
||||
{
|
||||
var result = new Result();
|
||||
int new_chat_id = 0;
|
||||
var notice_chat_list = CopyData();
|
||||
foreach (var noticechat in notice_chat_list)
|
||||
{
|
||||
var notice_chat_attrib = noticechat.getEntityAttribute<NoticeChatAttribute>();
|
||||
NullReferenceCheckHelper.throwIfNull(notice_chat_attrib, () => $"notice_chat_attrib is null !!!");
|
||||
int chat_id = notice_chat_attrib.ChatId;
|
||||
if (new_chat_id < chat_id)
|
||||
{
|
||||
new_chat_id = chat_id;
|
||||
}
|
||||
}
|
||||
|
||||
(result, var notice_chat) = await NoticeChat.createTestNoticeChat(this, new_chat_id, messageType, KoMessage, EnMessage);
|
||||
if (result.isFail() || notice_chat == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var batch = new QueryBatchEx<QueryRunnerWithDocument>(this, LogActionType.TestWriteNoticeChat
|
||||
, server_logic.getDynamoDbClient());
|
||||
{
|
||||
batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner());
|
||||
}
|
||||
|
||||
result = await QueryHelper.sendQueryAndBusinessLog(batch);
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
m_notice_chats.TryAdd(new_chat_id, notice_chat);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
result = await this.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "CheatNoticeChatWrite", fn_cheat_notice_chat_write);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to runTransactionRunnerSafely()!!! : {result.toBasicString()} - {this.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string toBasicString()
|
||||
{
|
||||
return $"{this.getTypeName()}";
|
||||
}
|
||||
|
||||
public override string toSummaryString()
|
||||
{
|
||||
return $"{this.getTypeName()}";
|
||||
}
|
||||
|
||||
public ILogActor toLogActor()
|
||||
{
|
||||
var server_logic = ServerLogicApp.getServerLogicApp();
|
||||
|
||||
var log_info = new NoticeChatActorLog();
|
||||
if (server_logic == null)
|
||||
return log_info;
|
||||
|
||||
log_info.initLogInfo(
|
||||
// 서버 정보
|
||||
server_logic.getServerConfig().getRegionId()
|
||||
, server_logic.getServerConfig().getWorldId()
|
||||
, server_logic.getServerType().toServerType()
|
||||
);
|
||||
|
||||
return log_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
GameServer/Global/NoticeChat/NoticeCheat.cs
Normal file
41
GameServer/Global/NoticeChat/NoticeCheat.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using ServerCommon;
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
[ChatCommandAttribute("addnoticechat", typeof(ChatCommandAddNoticeChat), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)]
|
||||
internal class ChatCommandAddNoticeChat : ChatCommandBase
|
||||
{
|
||||
public override async Task invoke(Player player, string token, string[] args)
|
||||
{
|
||||
Log.getLogger().info($"HandleAddNoticeChat");
|
||||
|
||||
if (args.Length < 3)
|
||||
{
|
||||
Log.getLogger().error($"Invalid Argument");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!int.TryParse(args[0], out var messageType))
|
||||
{
|
||||
Log.getLogger().error($"questcomplete param parsing Error args : {args[0]}");
|
||||
return;
|
||||
}
|
||||
|
||||
string KoMessage = args[1];
|
||||
string EnMessage = args[2];
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
ArgumentNullException.ThrowIfNull(server_logic);
|
||||
await server_logic.getNoticeChatManager().CheatFuncSaveNoticeChat(messageType, KoMessage, EnMessage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.getLogger().error($"AddNoticeChat cheat Failed. {ex}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
GameServer/Global/Rental/RentalManager.cs
Normal file
12
GameServer/Global/Rental/RentalManager.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
internal class RentalManager
|
||||
{
|
||||
}
|
||||
}
|
||||
89
GameServer/Global/SeasonPass/SeasonPassManager.cs
Normal file
89
GameServer/Global/SeasonPass/SeasonPassManager.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using MetaAssets;
|
||||
using ServerCommon;
|
||||
using ServerCore; using ServerBase;
|
||||
using META_ID = System.UInt32;
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
public class SeasonPassManager : EntityBase
|
||||
{
|
||||
private List<SeasonPassMetaData> seasonPassList = new();
|
||||
public META_ID currentSeasonPass { get; private set; } = 0;
|
||||
|
||||
public SeasonPassManager()
|
||||
: base(EntityType.SeasonPassManager)
|
||||
{
|
||||
}
|
||||
|
||||
public Result Init()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var current_time = DateTimeHelper.Current;
|
||||
|
||||
var season_pass_meta_datas = MetaData.Instance.SeasonPassMetaData;
|
||||
int new_season_pass_metaid = 0;
|
||||
|
||||
foreach (var meta_data in season_pass_meta_datas)
|
||||
{
|
||||
if (meta_data.Value.EndTime < current_time)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
seasonPassList.Add(meta_data.Value);
|
||||
|
||||
if (new_season_pass_metaid == 0 && meta_data.Value.StartTime < current_time && meta_data.Value.EndTime > current_time)
|
||||
{
|
||||
new_season_pass_metaid = meta_data.Value.Id;
|
||||
}
|
||||
}
|
||||
|
||||
currentSeasonPass = (META_ID)new_season_pass_metaid;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private META_ID getNewSeasonPassId()
|
||||
{
|
||||
var current_time = DateTimeHelper.Current;
|
||||
|
||||
int new_season_pass_metaid = 0;
|
||||
|
||||
var to_remove_season_passs_meta_datas = new List<SeasonPassMetaData>();
|
||||
|
||||
foreach (var meta_data in seasonPassList)
|
||||
{
|
||||
if (meta_data.EndTime < current_time)
|
||||
{
|
||||
to_remove_season_passs_meta_datas.Remove(meta_data);
|
||||
}
|
||||
|
||||
if (new_season_pass_metaid == 0 && meta_data.StartTime < current_time && meta_data.EndTime > current_time)
|
||||
{
|
||||
new_season_pass_metaid = meta_data.Id;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var meta_data in to_remove_season_passs_meta_datas)
|
||||
{
|
||||
seasonPassList.Remove(meta_data);
|
||||
}
|
||||
|
||||
return (META_ID)new_season_pass_metaid;
|
||||
}
|
||||
|
||||
public (bool,META_ID) updateNewSeason()
|
||||
{
|
||||
META_ID old_season_pass = currentSeasonPass;
|
||||
currentSeasonPass = getNewSeasonPassId();
|
||||
|
||||
if(old_season_pass != currentSeasonPass)
|
||||
{
|
||||
return (true, currentSeasonPass);
|
||||
}
|
||||
|
||||
return (false, currentSeasonPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
150
GameServer/Global/SystemMail/SystemMetaMail.cs
Normal file
150
GameServer/Global/SystemMail/SystemMetaMail.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
public class SystemMetaMail : EntityBase
|
||||
{
|
||||
private SystemMetaMail(EntityBase parent)
|
||||
: base(EntityType.SystemMail, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<Result> onInit()
|
||||
{
|
||||
addEntityAttribute(new SystemMetaMailAttribute(this));
|
||||
|
||||
return await base.onInit();
|
||||
}
|
||||
|
||||
public static async Task<(Result, SystemMetaMail?)> createTestSystemMail(EntityBase owner, int mail_sequence_num)
|
||||
{
|
||||
var system_mail = new SystemMetaMail(owner);
|
||||
var result = await system_mail.onInit();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
var err_msg = string.Empty;
|
||||
DateTime now = DateTime.UtcNow;
|
||||
|
||||
var system_meta_mail_attribute = system_mail.getEntityAttribute<SystemMetaMailAttribute>();
|
||||
NullReferenceCheckHelper.throwIfNull(system_meta_mail_attribute, () => $"system_meta_mail_attribute is null !!! - {owner.toBasicString()}");
|
||||
|
||||
List<ServerCommon.MailItem> data = new List<ServerCommon.MailItem>();
|
||||
data.Add(new ServerCommon.MailItem() { ItemId = 15231470, Count = 1 });
|
||||
data.Add(new ServerCommon.MailItem() { ItemId = 31020014, Count = 1 });
|
||||
|
||||
system_meta_mail_attribute.MailId = mail_sequence_num + 1;
|
||||
|
||||
List<OperationSystemMessage> titles = new();
|
||||
OperationSystemMessage title_ko = new();
|
||||
title_ko.LanguageType = LanguageType.Ko;
|
||||
title_ko.Text = "한국어 타이틀";
|
||||
titles.Add(title_ko);
|
||||
|
||||
OperationSystemMessage title_ja = new();
|
||||
title_ja.LanguageType = LanguageType.Ja;
|
||||
title_ja.Text = "日本語 タイトル";//メッセージ 送信者
|
||||
titles.Add(title_ja);
|
||||
|
||||
OperationSystemMessage title_en = new();
|
||||
title_en.LanguageType = LanguageType.En;
|
||||
title_en.Text = "English Title";
|
||||
titles.Add(title_en);
|
||||
|
||||
system_meta_mail_attribute.Title = titles;
|
||||
|
||||
|
||||
List<OperationSystemMessage> messages = new();
|
||||
OperationSystemMessage msg_ko = new();
|
||||
msg_ko.LanguageType = LanguageType.Ko;
|
||||
msg_ko.Text = "한국어 메시지";
|
||||
messages.Add(msg_ko);
|
||||
|
||||
OperationSystemMessage msg_ja = new();
|
||||
msg_ja.LanguageType = LanguageType.Ja;
|
||||
msg_ja.Text = "日本語 メッセージ";// 送信者
|
||||
messages.Add(msg_ja);
|
||||
|
||||
OperationSystemMessage msg_en = new();
|
||||
msg_en.LanguageType = LanguageType.En;
|
||||
msg_en.Text = "English Message";
|
||||
messages.Add(msg_en);
|
||||
|
||||
system_meta_mail_attribute.Text = messages;
|
||||
|
||||
|
||||
List<OperationSystemMessage> senders = new();
|
||||
OperationSystemMessage senders_ko = new();
|
||||
senders_ko.LanguageType = LanguageType.Ko;
|
||||
senders_ko.Text = "한국어 보낸이";
|
||||
senders.Add(senders_ko);
|
||||
|
||||
OperationSystemMessage senders_ja = new();
|
||||
senders_ja.LanguageType = LanguageType.Ja;
|
||||
senders_ja.Text = "日本語 送信者";//
|
||||
senders.Add(senders_ja);
|
||||
|
||||
OperationSystemMessage senders_en = new();
|
||||
senders_en.LanguageType = LanguageType.En;
|
||||
senders_en.Text = "English Sender";
|
||||
senders.Add(senders_en);
|
||||
|
||||
system_meta_mail_attribute.SenderNickName = messages;
|
||||
|
||||
system_meta_mail_attribute.StartTime = now;
|
||||
system_meta_mail_attribute.EndTime = now.AddDays(2);
|
||||
system_meta_mail_attribute.ItemList.AddRange(data);
|
||||
|
||||
system_meta_mail_attribute.newEntityAttribute();
|
||||
|
||||
return (result, system_mail);
|
||||
}
|
||||
|
||||
public static async Task<(Result, SystemMetaMail?)> createMailFromDoc(EntityBase parent, SystemMetaMailDoc doc)
|
||||
{
|
||||
var system_mail = new SystemMetaMail(parent);
|
||||
var result = await system_mail.onInit();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var system_mail_attribute = system_mail.getEntityAttribute<SystemMetaMailAttribute>();
|
||||
NullReferenceCheckHelper.throwIfNull(system_mail_attribute, () => $"system_mail_attribute is null !!! - {parent.toBasicString()}");
|
||||
|
||||
if (system_mail_attribute.copyEntityAttributeFromDoc(doc) == false)
|
||||
{
|
||||
err_msg = $"Failed to copyEntityAttributeFromDoc !!! : doc_type {doc.GetType()} - {system_mail.getRootParent()?.toBasicString()}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
return (result, system_mail);
|
||||
}
|
||||
|
||||
public override string toBasicString()
|
||||
{
|
||||
return $"{this.getTypeName()} - {getRootParent()?.toBasicString()}";
|
||||
}
|
||||
|
||||
public override string toSummaryString()
|
||||
{
|
||||
return $"{this.getTypeName()} - {getRootParent()?.toSummaryString()}";
|
||||
}
|
||||
}
|
||||
338
GameServer/Global/SystemMail/SystemMetaMailManager.cs
Normal file
338
GameServer/Global/SystemMail/SystemMetaMailManager.cs
Normal file
@@ -0,0 +1,338 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
public class SystemMetaMailManager : EntityBase, IWithLogActor
|
||||
{
|
||||
ConcurrentDictionary<Int32, SystemMetaMail> m_system_meta_mails = new();
|
||||
private bool m_is_all_load = false;
|
||||
|
||||
private HashSet<Int32> m_active_user_send_mail_ids = new ();
|
||||
public SystemMetaMailManager()
|
||||
: base(EntityType.SystemMailManager)
|
||||
{
|
||||
}
|
||||
|
||||
//서버 뜰때만 사용
|
||||
public async Task<Result> LoadDB()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var dynamo_db_client = server_logic.getDynamoDbClient();
|
||||
|
||||
var doc = new SystemMetaMailDoc();
|
||||
var query_config = dynamo_db_client.makeQueryConfigForReadByPKOnly(doc.getPK());
|
||||
(result, var read_doc_list) = await dynamo_db_client.simpleQueryDocTypesWithQueryOperationConfig<SystemMetaMailDoc>(query_config);
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var now = DateTimeHelper.Current;
|
||||
foreach (var read_doc in read_doc_list)
|
||||
{
|
||||
(result, var system_meta_mail) = await SystemMetaMail.createMailFromDoc(this, read_doc);
|
||||
if (result.isFail() || system_meta_mail == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var system_mail_attribute = system_meta_mail.getEntityAttribute<SystemMetaMailAttribute>();
|
||||
NullReferenceCheckHelper.throwIfNull(system_mail_attribute, () => $"system_mail_attribute is null !!!");
|
||||
|
||||
if (system_mail_attribute.EndTime < now) continue;
|
||||
|
||||
if(false == m_system_meta_mails.TryAdd(system_mail_attribute.MailId, system_meta_mail))
|
||||
{
|
||||
Log.getLogger().warn($"m_system_meta_mails try add error. mailId : {system_mail_attribute.MailId}");
|
||||
continue;
|
||||
}
|
||||
|
||||
//여기까진 정상적으로 온것이고 시간대가 활성화 시간대이면 발동시점에 첫 메일 보상처리를 위해 별도 데이터로 관리
|
||||
if (system_mail_attribute.StartTime <= now && now <= system_mail_attribute.EndTime)
|
||||
{
|
||||
m_active_user_send_mail_ids.Add(system_mail_attribute.MailId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_is_all_load = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> refreshDB()
|
||||
{
|
||||
var result = new Result();
|
||||
if (m_is_all_load == false) return result;
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var dynamo_db_client = server_logic.getDynamoDbClient();
|
||||
|
||||
var doc = new SystemMetaMailDoc();
|
||||
var query_config = dynamo_db_client.makeQueryConfigForReadByPKOnly(doc.getPK());
|
||||
(result, var read_doc_list) = await dynamo_db_client.simpleQueryDocTypesWithQueryOperationConfig<SystemMetaMailDoc>(query_config);
|
||||
if (result.isFail())
|
||||
{
|
||||
Log.getLogger().error("SystemMetaMailDoc read error");
|
||||
return result;
|
||||
}
|
||||
|
||||
var now = DateTimeHelper.Current;
|
||||
foreach (var read_doc in read_doc_list)
|
||||
{
|
||||
var attrib = read_doc.getAttrib<SystemMetaMailAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(attrib, () => $"attrib is null !!!");
|
||||
|
||||
if (false == m_system_meta_mails.ContainsKey(attrib.MailId))
|
||||
{
|
||||
if (attrib.EndTime < now) continue;
|
||||
|
||||
(result, var system_meta_mail) = await SystemMetaMail.createMailFromDoc(this, read_doc);
|
||||
if (result.isFail() || system_meta_mail == null)
|
||||
{
|
||||
Log.getLogger().error($"SystemMetaMailDoc create error, {result.toBasicString()}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var system_mail_attribute = system_meta_mail.getEntityAttribute<SystemMetaMailAttribute>();
|
||||
NullReferenceCheckHelper.throwIfNull(system_mail_attribute, () => $"system_mail_attribute is null !!!");
|
||||
|
||||
if (false == m_system_meta_mails.TryAdd(system_mail_attribute.MailId, system_meta_mail))
|
||||
{
|
||||
Log.getLogger().error($"SystemMetaMailDoc refresh Add error, mailid : {system_mail_attribute.MailId}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (attrib.EndTime < now)
|
||||
{
|
||||
if (false == m_system_meta_mails.TryRemove(attrib.MailId, out var removed_attribute))
|
||||
{
|
||||
Log.getLogger().error($"SystemMetaMailDoc refresh Add error, mailid : {attrib.MailId}");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.getLogger().info($"SystemMetaMailDoc is old : {JsonConvert.SerializeObject(removed_attribute)}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//24-10-23 Ticker에서 new Noti 안보내는걸로 처리
|
||||
public async Task<Result> notifyNewSystemMail()
|
||||
{
|
||||
var result = new Result();
|
||||
if (m_is_all_load == false) return result;
|
||||
|
||||
var now = DateTimeHelper.Current;
|
||||
bool need_noti = false;
|
||||
|
||||
foreach (var system_mail in m_system_meta_mails.Values)
|
||||
{
|
||||
var system_mail_attribute = system_mail.getEntityAttribute<SystemMetaMailAttribute>();
|
||||
if (system_mail_attribute == null) continue;
|
||||
|
||||
if (m_active_user_send_mail_ids.Contains(system_mail_attribute.MailId)) continue;
|
||||
if (now < system_mail_attribute.StartTime) continue;
|
||||
|
||||
//여기 오면 신규 메일이 있는거니까 new 처리 해준다.
|
||||
need_noti = true;
|
||||
m_active_user_send_mail_ids.Add(system_mail_attribute.MailId);
|
||||
}
|
||||
|
||||
if(need_noti) AllPlayerSystemMailNoti();
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
public List<SystemMetaMail> GetSystemMail(HashSet<Int32> receivedSystemMail)
|
||||
{
|
||||
var systemMailList = new List<SystemMetaMail>();
|
||||
var mails = CopyData();
|
||||
|
||||
foreach (var systemMail in mails)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var system_meta_mail_attribute = systemMail.getEntityAttribute<SystemMetaMailAttribute>();
|
||||
if (system_meta_mail_attribute == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (now < system_meta_mail_attribute.StartTime || system_meta_mail_attribute.EndTime < now) continue;
|
||||
|
||||
if (receivedSystemMail.Contains(system_meta_mail_attribute.MailId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (system_meta_mail_attribute.StartTime <= now && now <= system_meta_mail_attribute.EndTime )
|
||||
{
|
||||
systemMailList.Add(systemMail);
|
||||
}
|
||||
}
|
||||
|
||||
return systemMailList;
|
||||
}
|
||||
|
||||
public List<SystemMetaMail> GetSystemMail(Dictionary<Int32, DateTime> receivedSystemMails)
|
||||
{
|
||||
var systemMailList = new List<SystemMetaMail>();
|
||||
var mails = CopyData();
|
||||
|
||||
foreach (var systemMail in mails)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var system_meta_mail_attribute = systemMail.getEntityAttribute<SystemMetaMailAttribute>();
|
||||
if (system_meta_mail_attribute == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (now < system_meta_mail_attribute.StartTime || system_meta_mail_attribute.EndTime < now) continue;
|
||||
|
||||
if (receivedSystemMails.ContainsKey(system_meta_mail_attribute.MailId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
systemMailList.Add(systemMail);
|
||||
}
|
||||
|
||||
return systemMailList;
|
||||
}
|
||||
|
||||
private List<SystemMetaMail> CopyData()
|
||||
{
|
||||
return new List<SystemMetaMail>(m_system_meta_mails.Values.ToList());
|
||||
}
|
||||
|
||||
public async Task<Result> CheatFuncSaveSystemMetaMail()
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
var dynamo_db_client = server_logic.getDynamoDbClient();
|
||||
|
||||
var fn_cheat_system_meta_mail_whrite = async delegate ()
|
||||
{
|
||||
var result = new Result();
|
||||
int mailId = 0;
|
||||
var mailList = CopyData();
|
||||
foreach (var systemMail in mailList)
|
||||
{
|
||||
var system_mail_attribute = systemMail.getEntityAttribute<SystemMetaMailAttribute>();
|
||||
if (system_mail_attribute == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int mail_id = system_mail_attribute.MailId;
|
||||
if (mailId < mail_id)
|
||||
{
|
||||
mailId = mail_id;
|
||||
}
|
||||
}
|
||||
|
||||
(result, var system_mail) = await SystemMetaMail.createTestSystemMail(this, mailId);
|
||||
if (result.isFail() || system_mail == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var batch = new QueryBatchEx<QueryRunnerWithDocument>( this, LogActionType.TestWriteSystemMail
|
||||
, server_logic.getDynamoDbClient() );
|
||||
{
|
||||
batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner());
|
||||
}
|
||||
|
||||
result = await QueryHelper.sendQueryAndBusinessLog(batch);
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (false == m_system_meta_mails.TryAdd(mailId, system_mail))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
result = await this.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "CheatSystemMetaMailWrite", fn_cheat_system_meta_mail_whrite);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to runTransactionRunnerSafely()!!! : {result.toBasicString()} - {this.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void AllPlayerSystemMailNoti()
|
||||
{
|
||||
var server_logic = GameServerApp.getServerLogic();
|
||||
|
||||
var users = server_logic.getPlayerManager().getUsers();
|
||||
foreach( var user in users )
|
||||
{
|
||||
var mail_action = user.Value.getEntityAction<MailAction>();
|
||||
mail_action.NewReceivedSystemMail();
|
||||
}
|
||||
}
|
||||
|
||||
public override string toBasicString()
|
||||
{
|
||||
return $"{this.getTypeName()}";
|
||||
}
|
||||
|
||||
public override string toSummaryString()
|
||||
{
|
||||
return $"{this.getTypeName()}";
|
||||
}
|
||||
|
||||
public ILogActor toLogActor()
|
||||
{
|
||||
var server_logic = ServerLogicApp.getServerLogicApp();
|
||||
|
||||
var log_info = new SystemMailActorLog();
|
||||
if (server_logic == null)
|
||||
return log_info;
|
||||
|
||||
log_info.initLogInfo(
|
||||
// 서버 정보
|
||||
server_logic.getServerConfig().getRegionId()
|
||||
, server_logic.getServerConfig().getWorldId()
|
||||
, server_logic.getServerType().toServerType()
|
||||
);
|
||||
|
||||
return log_info;
|
||||
}
|
||||
|
||||
public bool isAllLoad()
|
||||
{
|
||||
return m_is_all_load;
|
||||
}
|
||||
}
|
||||
119
GameServer/Global/Ugq/UgqApiHelper.cs
Normal file
119
GameServer/Global/Ugq/UgqApiHelper.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using ServerCore; using ServerBase;
|
||||
using StackExchange.Redis;
|
||||
|
||||
|
||||
using UGQDatabase.Models;
|
||||
|
||||
|
||||
namespace GameServer
|
||||
{
|
||||
public static class UgqApiHelper
|
||||
{
|
||||
public static UgqUICategoryGradeType convertSearchCategoryTypeToUICategoryGradeType(UgqSearchCategoryType searchCategoryType)
|
||||
{
|
||||
switch (searchCategoryType)
|
||||
{
|
||||
case UgqSearchCategoryType.None:
|
||||
case UgqSearchCategoryType.SpotLight:
|
||||
return global::UgqUICategoryGradeType.None;
|
||||
|
||||
case UgqSearchCategoryType.GradeAmateur:
|
||||
return global::UgqUICategoryGradeType.Amateur;
|
||||
|
||||
case UgqSearchCategoryType.GradeRisingStar:
|
||||
return global::UgqUICategoryGradeType.RisingStar;
|
||||
|
||||
case UgqSearchCategoryType.GradeMaster:
|
||||
return global::UgqUICategoryGradeType.Master;
|
||||
|
||||
default:
|
||||
return global::UgqUICategoryGradeType.None;
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> makeDictionaryFromSearchQuestBoard(Int32 pageNumber, UgqUICategoryGradeType gradeType,
|
||||
UgqSearchType searchType, string searchText, UgqSortType sortType, LanguageType languageType, Int32 pageSize)
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
dict.Add("pageNumber", pageNumber.ToString());
|
||||
dict.Add("pageSize", pageSize.ToString());
|
||||
dict.Add("gradeType", gradeType.ToString());
|
||||
dict.Add("searchType", searchType.ToString());
|
||||
dict.Add("searchText", WebUtility.UrlEncode(searchText));
|
||||
dict.Add("sortType", sortType.ToString());
|
||||
dict.Add("accept-language", languageType.ToString());
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> makeDictionaryForUgqQuestId(UInt32 questId, UInt32 questRevision)
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
dict.Add("questId", questId.ToString());
|
||||
dict.Add("revision", questRevision.ToString());
|
||||
//dict.Add("accept-language", languageType.ToString());
|
||||
return dict;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> makeDictionaryForUgqByState(UInt32 questId, UInt32 questRevision, QuestContentState state)
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
dict.Add("questId", questId.ToString());
|
||||
dict.Add("revision", questRevision.ToString());
|
||||
dict.Add("state", state.ToString());
|
||||
return dict;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> makeDictionaryForQuestId(UInt32 questId, LanguageType languageType)
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
dict.Add("questId", questId.ToString());
|
||||
dict.Add("accept-language", languageType.ToString());
|
||||
return dict;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> makeDictionaryFromLanguageType(LanguageType languageType)
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
dict.Add("accept-language", languageType.ToString());
|
||||
return dict;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> makeDictionaryFromLanguageTypeWithMediaType(LanguageType languageType)
|
||||
{
|
||||
//languageType이 숫자로 되어 있어서 이걸 언어 스트링으로 바꿔줘야 되는데. 방법을 아직 모르겠다 ,우선 분기처리
|
||||
// 왜 int도 아니고 enum도 아니고, 숫자로 표시?
|
||||
var typestr = LanguageType.En.ToString();
|
||||
|
||||
if (0 == (int)languageType || (int)languageType == 2)
|
||||
{
|
||||
typestr = LanguageType.En.ToString();
|
||||
}
|
||||
if (1 == (int)languageType)
|
||||
{
|
||||
typestr = LanguageType.Ko.ToString();
|
||||
}
|
||||
if (4 == (int)languageType)
|
||||
{
|
||||
typestr = LanguageType.Ja.ToString();
|
||||
}
|
||||
|
||||
Dictionary<string, string> dict = new();
|
||||
dict.Add("accept-language", typestr);
|
||||
dict.Add("mediaType", "application/json");
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
508
GameServer/Global/Ugq/UgqApiManager.cs
Normal file
508
GameServer/Global/Ugq/UgqApiManager.cs
Normal file
@@ -0,0 +1,508 @@
|
||||
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<Result> checkConnection()
|
||||
{
|
||||
(var result, var ret) = await sendUgqRequestToApiServer<UgqBoardSearchResult>(http_client, HttpMethod.Get, connection_test_str, new(), string.Empty);
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
public async Task<(Result, UgqBoardSearchResult?)> getPagedGradeUgqFromBoard(Dictionary<string, string> 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<UgqBoardSearchResult>(http_client, HttpMethod.Get, url_builder_str, headers, string.Empty);
|
||||
return (result, ret);
|
||||
}
|
||||
|
||||
public async Task<(Result, UgqBoardItemDetail?)> getUgqDetailFromBoard(Dictionary<string, string> 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<UgqBoardItemDetail>(http_client, HttpMethod.Get, url_builder_str, headers, string.Empty);
|
||||
return (result, ret);
|
||||
}
|
||||
|
||||
public async Task<(Result, UgqBoardSportlightResult?)> getPagedSpotLightUgqFromBoard(Dictionary<string, string> dictionaries)
|
||||
{
|
||||
(var result, var ret) = await sendUgqRequestToApiServer<UgqBoardSportlightResult>(http_client, HttpMethod.Get, "quest-board-spotlight", dictionaries, string.Empty);
|
||||
return (result, ret);
|
||||
|
||||
}
|
||||
public async Task<(Result, UgqBoardSearchResult?)> getPagedBookmarkUgqFromBoard(Dictionary<string, string> dictionaries, string userGuid)
|
||||
{
|
||||
string url_str = $"bookmark-quest-board/{userGuid}?";
|
||||
var url_builder_str = makeGetParamString(url_str, dictionaries);
|
||||
(var result, var ret) = await sendUgqRequestToApiServer<UgqBoardSearchResult>(http_client, HttpMethod.Get, url_builder_str, dictionaries, string.Empty);
|
||||
return (result, ret);
|
||||
}
|
||||
|
||||
|
||||
public async Task<Result> ugqRegisterBookmark(Dictionary<string, string> dict, UInt32 questId, string userGuid)
|
||||
{
|
||||
UgqBookmarkRequest request = new();
|
||||
request.QuestId = questId;
|
||||
request.UserGuid = userGuid;
|
||||
(var result, var _) = await sendUgqRequestToApiServer<object>(http_client, HttpMethod.Post, $"bookmark", dict, JsonConvert.SerializeObject(request));
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> ugqDeRegisterBookmark(Dictionary<string, string> dict, UInt32 questId, string userGuid)
|
||||
{
|
||||
UgqUnbookmarkRequest request = new();
|
||||
request.QuestId = questId;
|
||||
request.UserGuid = userGuid;
|
||||
(var result, var _) = await sendUgqRequestToApiServer<object>(http_client, HttpMethod.Post, $"unbookmark", dict, JsonConvert.SerializeObject(request));
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> ugqRegisterLike(Dictionary<string, string> dict, UInt32 questId, UInt32 questRevision, string userGuid)
|
||||
{
|
||||
UgqLikeRequest request = new();
|
||||
request.QuestId = questId;
|
||||
request.Revision = questRevision;
|
||||
request.UserGuid = userGuid;
|
||||
(var result, var _) = await sendUgqRequestToApiServer<object>(http_client, HttpMethod.Post, $"like", dict, JsonConvert.SerializeObject(request));
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> 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<object>(http_client, HttpMethod.Post, url_str, dict, JsonConvert.SerializeObject(request));
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> 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<UgqBoardItemDetail>(http_client, HttpMethod.Post, url_builder_str, headers, string.Empty);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> 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<UgqBoardItemDetail>(http_client, HttpMethod.Post, url_builder_str, headers, string.Empty);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> 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<object>(http_client, HttpMethod.Post, url_str, dict, JsonConvert.SerializeObject(request));
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> 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<object>(http_client, HttpMethod.Post, url_str, dict, JsonConvert.SerializeObject(request));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<Result> ugqDeregisterLike(Dictionary<string, string> 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<object>(http_client, HttpMethod.Post, $"unlike", dict, JsonConvert.SerializeObject(request));
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Result> ugqReport(Dictionary<string, string> 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<object>(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<GameQuestDataEntity>(http_client, HttpMethod.Get, url_builder_str, headers, string.Empty);
|
||||
|
||||
return (result, ret);
|
||||
}
|
||||
public async Task<(Result, List<UgqQuestInTestState>?)> 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<List<UgqQuestInTestState>>(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<GameQuestDataEntity>(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<GameQuestDataEntity>(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<GameQuestDataSimple>(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<GameQuestDataEntity>(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<GameQuestDataEntity>(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<GameQuestDataEntity>(http_client, HttpMethod.Post, url_str, dictionaries, JsonConvert.SerializeObject(request));
|
||||
return (result, ret);
|
||||
}
|
||||
|
||||
private async Task<(Result, T?)> sendUgqRequestToApiServer<T>(HttpClient httpClient, HttpMethod method, string url, Dictionary<string, string> 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<T>(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<ApiErrorResponse>(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<string, string> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user