From 8f18415f2553a5d425db42f03aced858b0ffc128 Mon Sep 17 00:00:00 2001 From: bcjang Date: Fri, 28 Nov 2025 16:54:56 +0900 Subject: [PATCH] 1128 --- BrokerApiCore/ApiModels/AdminModel.cs | 41 + BrokerApiCore/BrokerApiCore.csproj | 125 +- BrokerApiCore/BrokerApiCoreService.cs | 17 + .../PlanetItemExchangeBusinessLog.cs | 3 +- .../PlanetUserAuthBusinessLog.cs | 4 +- BrokerApiCore/BrokerServerLogic.cs | 263 +- BrokerApiCore/Common/PlanetTokenValidator.cs | 30 + .../Entity/Actions/BrokerMailSendAction.cs | 62 +- BrokerApiCore/Entity/Helpers/MailHelper.cs | 38 + .../SqlContext/MetaverseBrokerDbContext.cs | 2 +- BrokerApiCore/Services/JwtGenerator.cs | 5 +- BrokerApiCore/Services/PlanetService.cs | 24 +- .../ProductOrderCompletionStrategy.cs | 6 +- BrokerApiCore/Services/UserAuthService.cs | 11 +- .../BrokerApiCore.csproj.nuget.dgspec.json | 441 +- .../obj/BrokerApiCore.csproj.nuget.g.props | 8 +- .../obj/BrokerApiCore.csproj.nuget.g.targets | 5 +- .../obj/Debug/BrokerApiCore.assets.cache | Bin 111234 -> 91915 bytes ...okerApiCore.csproj.AssemblyReference.cache | Bin 60502 -> 43581 bytes BrokerApiCore/obj/project.assets.json | 2065 ++----- BrokerApiCore/obj/project.nuget.cache | 84 +- BrokerApiCore/obj/project.packagespec.json | 2 +- .../obj/rider.project.model.nuget.info | 2 +- BrokerApiCore/obj/rider.project.restore.info | 2 +- BrokerApiServer/BrokerApiServer.csproj | 106 +- BrokerApiServer/BrokerServerService.cs | 111 + BrokerApiServer/BrokerServiceExtensions.cs | 73 + .../Common/RequireAdminAuthAttribute.cs | 8 +- .../Common/RequirePlanetAuthAttribute.cs | 4 +- .../Common/RequireUserJwtAuthAttribute.cs | 5 +- .../Common/ResultExceptionFilter.cs | 5 +- .../Controllers/AccountController.cs | 51 - .../Controllers/AdminController.cs | 52 +- .../Controllers/CurrencyController.cs | 166 - .../Controllers/PlanetController.cs | 4 +- .../Controllers/PlanetUserController.cs | 19 +- BrokerApiServer/Controllers/UserController.cs | 93 - BrokerApiServer/Directory.Build.props | 4 +- .../Extensions/AppBuilderExtensions.cs | 179 +- .../ForceStopServerPacketHandler.cs | 32 +- .../StopServerPacketHandler.cs | 31 +- BrokerApiServer/Program.cs | 55 +- .../Properties/launchSettings.json | 5 +- BrokerApiTest/BrokerApiTest.csproj | 79 +- BrokerApiTest/Config/ServerConfig-Dev.json | 162 - BrokerApiTest/Config/ServerConfig-QA.json | 150 - BrokerApiTest/Config/ServerConfig-Stage.json | 151 - BrokerApiTest/Config/ServerConfig.json | 216 - BrokerApiTest/Config/nlog.config | 95 - .../CurrencyControllerTests.cs | 90 - .../DevCurrencyControllerTests.cs | 2 - .../DevPlanetUserControllerTests.cs | 279 + .../ControllerTests/PlanetControllerTests.cs | 22 +- .../PlanetUserControllerTests.cs | 33 +- .../QAPlanetUserControllerTests.cs | 2 - .../StagePlanetUserControllerTests.cs | 3 - BrokerApiTest/Directory.Build.props | 8 + BrokerApiTest/DocQuery/DynamoDbClientTests.cs | 10 - BrokerApiTest/DocQuery/UserTestAttrib.cs | 1 - .../DocQuery/UserTestDeleteOneAction.cs | 7 +- BrokerApiTest/DocQuery/UserTestDoc.cs | 4 +- .../DocQuery/UserTestEntityAttribute.cs | 4 +- .../DocQuery/UserTestFindOneAction.cs | 6 +- .../DocQuery/UserTestInsertAction.cs | 4 +- .../DocQuery/UserTestUpdateOneAction.cs | 4 +- BrokerApiTest/EntityTests/EntityMailTests.cs | 81 +- BrokerApiTest/EntityTests/EntityTests.cs | 2 - BrokerApiTest/Etc/EnumHelperTest.cs | 2 +- BrokerApiTest/Helper/BrokerTestServer.cs | 41 +- BrokerApiTest/Helper/Helpers.cs | 2 +- BrokerApiTest/Helper/TestUserHelper.cs | 1 - BrokerApiTest/Jwt/JwtBasicTests.cs | 109 +- .../BrokerApiTest.csproj.nuget.dgspec.json | 319 +- .../Debug/net8.0/BrokerApiTest.assets.cache | Bin 133027 -> 133359 bytes ...okerApiTest.csproj.AssemblyReference.cache | Bin 83075 -> 82564 bytes BrokerApiTest/obj/project.assets.json | 62 +- BrokerApiTest/obj/project.nuget.cache | 3 +- BrokerApiTest/obj/project.packagespec.json | 2 +- .../obj/rider.project.model.nuget.info | 2 +- BrokerApiTest/obj/rider.project.restore.info | 2 +- GameServer/0. Base/BaseGetSet.cs | 10 +- .../AppearanceCustomizeCheat.cs | 2 +- .../BeaconShop/Action/BeaconShopAction.cs | 115 +- .../BeaconShop/MongoDb/BeaconShopMongoDoc.cs | 2 + .../MongoDb/BeaconShopRepository.cs | 88 + ...conShopRecentRegisterItemsPacketHandler.cs | 71 + .../BeaconShopSearchItemPacketHandler.cs | 23 +- .../Calium/Action/CaliumConverterAction.cs | 2 +- .../PacketHandler/ClaimRewardPacketHandler.cs | 5 +- .../Contents/Craft/Action/CraftAction.cs | 1571 +++--- GameServer/Contents/Craft/CraftCheat.cs | 484 +- .../Contents/Farming/Helper/FarmingHelper.cs | 2 +- .../FarmingCancelPacketHandler.cs | 25 +- .../GameMode/Action/GameGameObjectAction.cs | 44 - .../Action/GameModeBestRecordAction.cs | 182 + ...eModeObjectInteractionHandlerActionBase.cs | 189 + .../Action/GameModePlayRegulationAction.cs | 583 ++ .../Action/GameModePositionSetAction.cs | 53 + .../Action/GameModePostUpdateAction.cs | 42 + .../Action/GameModePreUpdateAction.cs | 27 + .../Action/GameModeStateCheckAction.cs | 44 + .../Cheat/CheatGameModeBestRecordTest.cs | 96 + .../GameMode/Cheat/CheatGameModeHostNoti.cs | 34 + .../GameMode/Cheat/CheatGameModeUserKick.cs | 24 + .../DBQGameModePlayRegulationReadAll.cs | 109 + .../GameMode/Helper/GameModeHelper.cs | 478 +- .../GameMode/Helper/GameNotifyHelper.cs | 183 +- .../GameMode/InteractionObject/GameObject.cs | 12 +- ...eObjectInteractionDataCommonHandlerBase.cs | 40 + .../IGameObjectInteractionDataHandler.cs | 8 + .../Log/GameModePenaltyBusinessLog.cs | 28 + .../GameModeInstanceRoom.cs} | 42 +- .../JoinManage/GameModeInitHandlerBase.cs | 18 +- .../JoinManage/GameModeJoinHandlerBase.cs | 3 +- .../GameModeJoinSuccessHandlerBase.cs | 15 +- .../Manage/JoinManage/IGameModeInitHandler.cs | 8 +- .../JoinManage/IGameModeJoinSuccessHandler.cs | 6 +- .../LeaveManage/GameModeDestroyHandlerBase.cs | 15 +- .../GameModeDisconnectHandlerBase.cs | 46 + .../LeaveManage/GameModeLeaveHandlerBase.cs | 36 +- .../LeaveManage/IGameModeDisconnectHandler.cs | 6 + .../IPreparationForLeavingGameHandler.cs | 6 + .../PreparationForLeavingGameBase.cs | 67 + .../Matching/IMatchingReservationHandler.cs | 8 + .../MatchingReservationHandlerBase.cs | 64 + .../Manage/PlayManage/GameModeBase.cs | 507 +- .../PlayManage/GameModeLifeCycleTicker.cs | 4 - .../Manage/PlayManage/GameModeManager.cs | 91 +- .../GameObjectnteractionDataBase.cs | 14 + .../Manage/PlayManage/IDeadHandler.cs | 3 +- .../GameMode/Manage/PlayManage/IGameMode.cs | 12 +- .../IGameModeLoadCompleteHandler.cs | 6 + .../GameMode/Manage/PlayManage/IGameObject.cs | 6 - .../IGameObjectInteractionHandler.cs | 10 + .../IGameObjectnteractionDataHandler.cs | 6 + .../Manage/PlayManage/IRespawnHandler.cs | 7 + .../Manage/StateManage/GameModeStateBase.cs | 175 +- .../Manage/StateManage/GameModeStateNone.cs | 46 + .../GameModeTransitionStrategyBase.cs | 66 + .../Manage/StateManage/IGameModeState.cs | 14 +- .../StateManage/IGameModeStateManager.cs | 10 + .../IGameModeTransitionStrategy.cs | 8 + .../Manage/StateManage/StateChangeHistory.cs | 11 + .../Manage/BattleInstanceManager.cs | 225 +- .../Manage/BattleInstanceRespawn.cs | 10 - .../Manage/BattleInstanceRoomHandler.cs | 379 -- .../Mode-Battle/Manage/GameModeTPS.cs | 27 - .../Action/BattleInstanceAction.cs | 181 +- .../Action/BattleInstanceStateUpdateAction.cs | 399 +- .../Action/BattleInstanceUpdateAction.cs | 302 +- .../Action/BattleLocationAction.cs | 18 +- .../Action/BattleObjectInteractAction.cs | 7 +- .../Action/BattleObjectRewardAction.cs | 151 +- .../Action/BattleZoneMoveAction.cs | 182 - .../FfaGameObjectPodStorageInteractAction.cs | 57 - .../Action/TpsFfaBuffInteractAction.cs | 101 + .../TpsFfaBuffInteractionDataHandler.cs | 12 + .../Action/TpsFfaCombatPodInteractAction.cs | 102 + .../TpsFfaCombatPodInteractionDataHandler.cs | 15 + .../Action/TpsFfaLoadCompleteAction.cs | 38 + .../Action/TpsFfaObjectUpdateAction.cs | 211 + .../Action/TpsFfaPickupPodInteractAction.cs | 100 + .../TpsFfaPickupPodInteractionDataHandler.cs | 14 + .../Action/TpsFfaPodStorageInteractAction.cs | 177 + .../TpsFfaPodStorageInteractionDataHandler.cs | 15 + .../Action/TpsFfaSetUserReadyPosAction.cs | 129 + .../Action/TpsFfaWeaponInteractAction.cs | 100 + .../TpsFfaWeaponInteractionDataHandler.cs | 13 + .../ModeFreeForAll/Cheat/BattleCheat.cs | 61 +- .../ModeFreeForAll/Doc/BattleEventDoc.cs | 56 +- .../Entity/BattleInstanceRoom.cs | 78 - .../Entity/BattleInstanceSnapshotAttribute.cs | 18 +- .../Entity/BattleInstanceSnapshotDoc.cs | 3 +- .../Entity/SystemBattleEvent.cs | 13 +- .../Entity/TpsFfaChargeRewardData.cs | 14 + .../ModeFreeForAll/Helper/BattleConstant.cs | 6 +- .../Helper/BattleInstancePlayModeHelper.cs | 28 +- .../BattleObjectInteractionLogicHandler.cs | 4 +- .../ModeFreeForAll/Helper/BattleRoomHelper.cs | 75 +- .../Helper/BattleRoomNotifyHelper.cs | 94 +- .../ModeFreeForAll/Helper/TpsFfaHelper.cs | 98 + .../Log/BattlePodCombatRewardBusinessLog.cs | 2 +- .../Log/BattleRespawnBusinessLog.cs | 2 +- .../Log/BattleRoundUpdateBusinessLog.cs | 2 +- .../Log/BattleSnapShotBusinessLog.cs | 17 +- .../Manage/GameModeTPSFreeForAll.cs | 393 +- .../Manage/TPSFreeForAllDestroyHandler.cs | 3 +- .../Manage/TPSFreeForAllInitHandler.cs | 27 +- .../Manage/TPSFreeForAllJoinHandler.cs | 12 +- .../Manage/TPSFreeForAllJoinSuccessHandler.cs | 163 +- .../Manage/TPSFreeForAllLeaveHandler.cs | 10 +- .../Manage/TPSFreeForAllTransitionStrategy.cs | 171 + .../TpsFfaMatchingReservationHandler.cs | 15 + .../Manage/TpsFreeForAllDisconnectHandler.cs | 38 + ...sFreeForAllPreparationForLeavingHandler.cs | 30 + .../ModeFreeForAll/Meta/TpsFfaMetaData.cs | 46 + .../BattleObjectInteractionPacketHandler.cs | 50 +- .../BattlePlayerDeadPacketHandler.cs | 6 +- .../BattlePlayerRespawnPacketHandler.cs | 16 +- .../JoinBattleInstancePacketHandler.cs | 227 +- .../LeaveBattleInstancePacketHandler.cs | 96 +- ...onForLeavingBattleInstancePacketHandler.cs | 26 +- .../Redis/BattleInstanceRoomStorage.cs | 565 -- .../State/TpsFfaStateCreated.cs | 47 + .../ModeFreeForAll/State/TpsFfaStateReady.cs | 59 + .../State/TpsFfaStateRoundEndAll.cs | 62 + .../State/TpsFfaStateRoundWait.cs | 59 + .../State/TpsFfaStateRounding.cs | 97 + .../ModeFreeForAll/State/TpsFfaStateWait.cs | 51 + .../Tickers/BattleEventCheckTicker.cs | 390 +- .../Tickers/BattleEventNotifyTicker.cs | 3 +- .../Manage/GameModeTPSTeamDeathMatch.cs | 60 +- .../Manage/TPSTeamDeathMatchDestroyHandler.cs | 1 + .../Manage/TPSTeamDeathMatchInitHandler.cs | 1 + .../Manage/TPSTeamDeathMatchJoinHandler.cs | 1 + .../TPSTeamDeathMatchJoinSuccessHandler.cs | 1 + .../Manage/TPSTeamDeathMatchLeaveHandler.cs | 5 +- .../TPSTeamDeathMatchTransitionStrategy.cs | 11 + ...mDeathMatchPreparationForLeavingHandler.cs | 29 + .../Mode-Running/Manage/GameModeRun.cs | 33 +- .../Manage/GameModeRunAdventure.cs | 50 +- .../Manage/RunAdventureDestroyHandler.cs | 1 + .../Manage/RunAdventureInitHandler.cs | 1 + .../Manage/RunAdventureJoinHandler.cs | 1 + .../Manage/RunAdventureJoinSuccessHandler.cs | 1 + .../Manage/RunAdventureLeaveHandler.cs | 5 +- ...unAdventurePreparationForLeavingHandler.cs | 29 + .../Manage/RunAdventureTransitionStrategy.cs | 12 + .../Actions/RunPracticeBuffInteractAction.cs | 108 + .../RunPracticeBuffInteractionDataHandler.cs | 12 + .../RunPracticeCheckPointInteractAction.cs | 66 + ...racticeCheckPointInteractionDataHandler.cs | 12 + .../Actions/RunPracticeDeadAction.cs | 35 + .../RunPracticeGoalLineInteractionAction.cs | 77 + ...nPracticeGoalLineInteractionDataHandler.cs | 12 + .../Actions/RunPracticeLoadCompleteAction.cs | 44 + .../Actions/RunPracticeObjectUpdateAction.cs | 80 + .../Actions/RunPracticePlayAction.cs | 64 + .../RunPracticeReadyPosAssignAction.cs | 6 + .../Actions/RunPracticeRespawnAction.cs | 45 + .../ModePractice/Helper/RunPracticeHelper.cs | 61 + .../Manage/GameModeRunPractice.cs | 143 + .../Manage/RunPracticeDestroyHandler.cs | 21 + .../Manage/RunPracticeDisconnectHandler.cs | 32 + .../Manage/RunPracticeInitHandler.cs | 17 + .../Manage/RunPracticeJoinHandler.cs | 31 + .../Manage/RunPracticeJoinSuccessHandler.cs | 103 + .../Manage/RunPracticeLeaveHandler.cs | 37 + ...RunPracticePreparationForLeavingHandler.cs | 29 + .../Manage/RunPracticeTransitionStrategy.cs | 141 + .../ModePractice/Meta/RunPracticeMetaData.cs | 43 + .../State/RunPracticeStateCreated.cs | 45 + .../State/RunPracticeStateDestroyed.cs | 47 + .../State/RunPracticeStatePlay.cs | 63 + .../State/RunPracticeStateReady.cs | 83 + .../State/RunPracticeStateResult.cs | 53 + .../State/RunPracticeStateSummary.cs | 101 + .../State/RunPracticeStateWait.cs | 57 + .../RaceGameObjectSavePointInteractAction.cs | 43 - .../ModeRace/Actions/RaceStateCheckAction.cs | 63 - .../Actions/RunRaceBuffInteractAction.cs | 111 + .../RunRaceBuffInteractionDataHandler.cs | 15 + .../RunRaceCheckPointInteractAction.cs | 88 + ...RunRaceCheckPointInteractionDataHandler.cs | 15 + .../ModeRace/Actions/RunRaceDeadAction.cs | 67 + .../RunRaceGoalLineInteractionAction.cs | 131 + .../RunRaceGoalLineInteractionDataHandler.cs | 15 + .../Actions/RunRaceLoadCompleteAction.cs | 38 + .../Actions/RunRaceObjectUpdateAction.cs | 89 + .../ModeRace/Actions/RunRacePlayAction.cs | 294 + .../Actions/RunRaceReadyPosAssignAction.cs | 94 + .../ModeRace/Actions/RunRaceRespawnAction.cs | 75 + .../ModeRace/Actions/RunRaceRewardAction.cs | 541 ++ .../ModeRace/Helper/RunRaceHelper.cs | 83 +- .../ModeRace/Helper/RunRaceNotifyHelper.cs | 30 + .../RunRaceCheckPointAbusingBusinessLog.cs | 25 + .../Log/RunRaceFinishRewardBusinessLog.cs | 24 + .../Log/RunRaceRespawnRewardBusinessLog.cs | 22 + .../Log/RunRaceRewardSummaryBusinessLog.cs | 24 + .../Log/RunRaceUnfinishRewardBusinessLog.cs | 23 + .../ModeRace/Manage/GameModeRunRace.cs | 206 +- .../ModeRace/Manage/GameModeRunRaceData.cs | 21 - .../ModeRace/Manage/RunRaceDestroyHandler.cs | 9 +- .../Manage/RunRaceDisconnectHandler.cs | 40 + .../ModeRace/Manage/RunRaceInitHandler.cs | 9 +- .../ModeRace/Manage/RunRaceJoinHandler.cs | 1 + .../Manage/RunRaceJoinSuccessHandler.cs | 54 +- .../ModeRace/Manage/RunRaceLeaveHandler.cs | 21 +- .../RunRaceMatchingReservationHandler.cs | 24 + .../RunRacePreparationForLeavingHandler.cs | 31 + .../Manage/RunRaceTransitionStrategy.cs | 143 + .../ModeRace/Meta/RunRaceMetaData.cs | 61 + .../ModeRace/Reward/RewardRunRace.cs | 25 + .../ModeRace/State/RaceStateDestroyed.cs | 34 - .../ModeRace/State/RaceStateEnd.cs | 33 - .../ModeRace/State/RaceStateReady.cs | 38 - .../ModeRace/State/RaceStateStart.cs | 45 - .../ModeRace/State/RunRaceStateCreated.cs | 49 + .../ModeRace/State/RunRaceStateDestroyed.cs | 47 + .../ModeRace/State/RunRaceStateEnd.cs | 47 + .../ModeRace/State/RunRaceStatePlay.cs | 107 + .../ModeRace/State/RunRaceStateReady.cs | 66 + .../ModeRace/State/RunRaceStateResult.cs | 56 + .../State/RunRaceStateRewardSummary.cs | 129 + .../ModeRace/State/RunRaceStateWait.cs | 96 + .../PacketHandler/GameLeavePacketHandler.cs | 258 + .../GameModeLoadCompletePacketHandler.cs | 79 + .../GameObjectInteractionPacketHandler.cs | 119 +- .../GamePlayerDeadPacketHandler.cs | 93 + .../GamePlayerRespawnPacketHandler.cs | 83 + ...tionForLeavingGameInstancePacketHandler.cs | 81 + .../State/GameModeStateLoadingWait.cs | 54 + .../GameZone/Action/GameZoneAction.cs | 138 +- .../GameZone/Action/GameZoneMoveAction.cs | 304 +- .../GameZone/Helper/TaxiBusinessLogHelper.cs | 19 +- .../ContentsMovePacketHandler.cs | 114 + .../DestroyWeaponObjectPacketHandler.cs | 120 + .../LeaveInstancePacketHandler.cs | 234 +- .../MoveToBeaconPacketHandler.cs | 174 + .../PacketHandler/TaxiPacketHandler.cs | 29 +- .../UseMountPropPacketHandler.cs | 14 +- .../UseRewardPropPacketHandler.cs | 27 +- .../ItemFunction/Action/ItemBuyAction.cs | 126 +- .../Action/LargePacketHandleAction.cs | 182 + .../PacketHandler/LargePacketHandler.cs | 56 + .../Location/Action/LocationAction.cs | 33 +- .../Loginout/Action/GameLoginAction.cs | 12 +- .../Loginout/Action/GameLogoutAction.cs | 27 +- .../PacketHandler/GameLogoutPacketHandler.cs | 2 + .../Match/CheatCommand/ChatCommandMatch.cs | 139 + .../Contents/Match/Doc/Match_CS_Sequence.md | 57 + .../Match/Doc/게임 매칭 CS 시퀀스 .png | Bin 0 -> 86968 bytes GameServer/Contents/Match/IMatchStrategy.cs | 7 + .../Match/MatchJoinGameRoomReserve.cs | 241 + GameServer/Contents/Match/MatchManager.Log.cs | 124 + GameServer/Contents/Match/MatchManager.cs | 310 ++ .../Contents/Match/MatchRoomEventHandler.cs | 111 + .../PacketHandler/MatchCancelPacketHandler.cs | 93 + .../Match/PacketHandler/MatchNotifyHelper.cs | 28 + .../MatchReservePacketHandler.cs | 92 + .../MyHome/Action/MyhomeAgentAction.cs | 9 - .../DeleteMyhomePacketHandler.cs | 9 + .../Quest/Action/QuestTaskUpdateAction.cs | 10 +- .../PacketHandler/QuestRewardPacketHandler.cs | 2 + .../QuestUGQ/Action/UgqAssignAction.cs | 2 +- GameServer/Contents/Reward/RewardBase.cs | 13 - GameServer/Contents/Reward/RewardHelper.cs | 53 + .../SeasonPass/Action/SeasonPassAction.cs | 7 +- GameServer/Contents/Shop/Action/ShopAction.cs | 10 +- .../Shop/Action/ShopSoldProductAction.cs | 24 +- GameServer/Contents/Shop/Helper/ShopHelper.cs | 4 + .../RePurchaseItemPacketHandler.cs | 13 +- .../PacketHandler/SellItemPacketHandler.cs | 5 +- .../Action/UserCreateOrLoadAction.cs | 35 +- .../Creation/Normal/DefaultUserCreator.cs | 14 + .../Event/Creation/Tester/TestUserCreator.cs | 14 + .../Event/Loading/UserLoader.cs | 59 +- .../PacketHandler/ChatCommandEventAction.cs | 213 + .../PacketHandler/ChatCommandWorldEvent.cs | 157 + .../WorldEventContributionHandler.cs | 37 + .../Action/BuildingFloorAction.cs | 4 +- .../Action/BuildingFloorAgentAction.cs | 9 +- .../Action/CaliumStorageAction.cs | 3 + .../CaliumStorage/CaliumStorageHelper.cs | 11 +- .../Action/CustomDefinedUiAction.cs | 4 +- .../Farming/Action/FarmingEffectAction.cs | 10 +- GameServer/Entity/Inventory/Base/BagInven.cs | 4 +- .../Item/Owner/Myhome/MyhomeItemAction.cs | 21 +- GameServer/Entity/Match/MatchAction.cs | 41 + GameServer/Entity/Player/FakePlayer.cs | 70 + .../Entity/Player/Helper/PlayerHelper.cs | 4 +- GameServer/Entity/Player/Player.cs | 85 +- GameServer/Entity/Player/PlayerCheat.cs | 135 + GameServer/Entity/Rank/Action/RankAction.cs | 156 + .../Entity/Rank/Action/RankAgentAction.cs | 309 + GameServer/Entity/Rank/Helper/RankHelper.cs | 79 + GameServer/Entity/Rank/Rank.cs | 38 + .../Entity/Ranker/Action/RankerAction.cs | 382 ++ .../Entity/Ranker/Action/RankerAgentAction.cs | 89 + .../Ranker/Helper/RankerBusinessLogHelper.cs | 22 + .../Entity/Ranker/Helper/RankerHelper.cs | 227 + GameServer/Entity/Ranker/Ranker.cs | 46 + GameServer/Entity/Ranker/RankerCheat.cs | 53 + .../Entity/Ranking/Action/RankingAction.cs | 576 ++ .../Helper/RankingBusinessLogHelper.cs | 33 + .../Ranking/Helper/RankingCacheHelper.cs | 127 + .../Entity/Ranking/Helper/RankingHelper.cs | 232 + .../Ranking/Helper/RankingNotifyHelper.cs | 54 + .../PacketHandler/RankingInfoPacketHandler.cs | 87 + GameServer/Entity/Ranking/Ranking.cs | 97 + GameServer/Entity/Ranking/RankingCheat.cs | 242 + .../Action/RankingScheduleAction.cs | 111 + .../Helper/RankingScheduleHelper.cs | 138 + .../Helper/RankingScheduleNotifyHelper.cs | 80 + .../Entity/RankingSchedule/RankingSchedule.cs | 46 + .../RankingSchedule/RankingScheduleCheat.cs | 94 + .../Event/Action/GameEventCollectorAction.cs | 28 + .../Event/EventDefines/GameEventDefines.cs | 103 + .../GameEventFfaOnPickUpObject.cs | 18 + .../EventInvokers/GameEventInvokerBase.cs | 32 + .../GameEventInvokerClaimRewarded.cs | 10 + .../GameEventInvokerCraftingCompleted.cs | 15 + .../GameEventInvokerFarmingCompleted.cs | 11 + .../GameEventInvokerQuestRewarded.cs | 13 + .../GameEventInvokerRewadPropRewarded.cs | 11 + .../GameEventInvokerSeasonPassRewarded.cs | 10 + .../GameEventInvokerTaxiArrived.cs | 14 + .../GameEventRunRaceCompleteTime.cs | 18 + .../Event/EventInvokers/IGameEventInvoker.cs | 12 + GameServer/Event/EventManager.cs | 10 +- .../GameEventRankRunRaceFinishTime.cs | 6 + .../Event/EventRunners/GameEventRunnerBase.cs | 6 + .../Event/EventRunners/IGameEventRunner.cs | 6 + GameServer/Event/GameEventAnalyzer.cs | 67 + GameServer/Event/GameEventCollector.cs | 57 + GameServer/Event/GameEventHelper.cs | 15 + GameServer/Event/GameEventTriggerManager.cs | 17 + .../Event/GlobalGameEventInvokerManager.cs | 32 + GameServer/Event/Manage/GlobalGameEvent.cs | 12 + GameServer/GameServer.csproj | 104 +- GameServer/GameServerApp.cs | 22 +- GameServer/GameServerLogic.cs | 311 +- .../Global/Banner/Action/BannerAction.cs | 52 + GameServer/Global/Banner/Banner.cs | 38 + GameServer/Global/Banner/BannerManager.cs | 112 + .../Global/Banner/Helper/BannerHelper.cs | 40 + .../Banner/Helper/BannerNotifyHelper.cs | 34 + .../EventActionScoreHelper.cs | 78 + .../EventActionScoreManager.cs | 35 + .../EventActionScoreProcessor.Log.cs | 164 + .../EventActionScoreProcessor.cs | 44 + .../EventActionScoreResult.cs | 76 + .../Interfaces/IEventActionScoreHelper.cs | 11 + .../Interfaces/IEventToScoreHandler.cs | 12 + .../Interfaces/IRankingEventScheduleInfo.cs | 9 + .../Interfaces/IWorldEventScheduleInfo.cs | 9 + .../Interfaces/IWorldEventScoreRepo.cs | 24 + .../RankingEventActionScore.cs | 83 + .../RankingEventEventScheduleInfo.cs | 16 + .../EventActionScore/WorldEventActionScore.cs | 82 + .../WorldEventScheduleInfo.cs | 13 + .../EventActionScore/WorldEventScoreRepo.cs | 295 + GameServer/Global/Ranking/RankingManager.cs | 186 + .../RankingSchedule/RankingScheduleManager.cs | 188 + GameServer/Global/Rental/RentalManager.cs | 12 - .../Global/WorldEvent/WorldEventManager.cs | 107 + .../WorldEvent/WorldEventScheduleManager.cs | 163 + GameServer/Map/Helper/MapHelper.cs | 17 + GameServer/Map/Helper/MapNotifyHelper.cs | 40 +- GameServer/Map/Map.cs | 196 +- GameServer/Map/MapManager.cs | 50 +- ...angeMannequinDisplayItemMQPacketHandler.cs | 2 +- .../GameMatch/AckMatchCancelHandler.cs | 23 + .../GameMatch/AckMatchReserveHandler.cs | 21 + .../GameMatch/AckMatchRoomInfoHandler.cs | 22 + .../NtfMatchGameJoinReserveHandler.cs | 43 + .../GameMatch/NtfMatchResultHandler.cs | 105 + .../GameMatch/NtfMatchStatusHandler.cs | 79 + .../NtfModifyRankingInfoMQPackerHandler.cs | 57 + ...tfQuestTaskForceCompleteMQPacketHandler.cs | 203 + .../NtfRefreshRankMQPacketHandler.cs | 56 + .../NtfUpdateBannerMQPacketHandler.cs | 39 + ...NtfUpdateRankingScheduleMQPackerHandler.cs | 94 + ...ReqUpdateRankingIntervalMQPacketHandler.cs | 66 + GameServer/Network/GameLoginListener.cs | 2 + GameServer/Properties/launchSettings.json | 18 +- GameServer/Room/InstanceRoom.cs | 118 +- GameServer/Room/InstanceRoomHandler.cs | 8 +- GameServer/Room/InstanceRoomManager.cs | 39 +- GameServer/Ticker/GameEventCheckTicker.cs | 21 + GameServer/Ticker/MatchServerListTicker.cs | 51 + .../Ticker/RankingScheduleUpdateTicker.cs | 66 + GameServer/Ticker/RankingUpdateTicker.cs | 67 + .../Ticker/WorldEventScheduleUpdateTicker.cs | 53 + GameServer/z.Backup/ClientSession.cs | 2 +- GameServer/z.Backup/ClientSession_Friend.cs | 65 - LoginServer/LoginServerApp.cs | 13 +- LoginServer/LoginServerLogic.cs | 2 +- LoginServer/Manager/LoginQueueManager.cs | 79 +- MatchServer/Common/IMatchMq.cs | 8 + MatchServer/Common/ISeqWorkQue.cs | 62 + MatchServer/Common/ISeqWorker.cs | 26 + MatchServer/Common/LazyServiceExtensions.cs | 19 + MatchServer/Common/PeriodicWorker.cs | 51 + MatchServer/Common/SeqWorkQue.cs | 515 ++ MatchServer/Common/SequenceWorkQue.cs | 141 + MatchServer/Common/SequenceWorker.cs | 85 + .../NtfMatchChangeGameStateHandler.cs | 21 + .../PacketHandler/NtfMatchGameJoinHandler.cs | 20 + .../PacketHandler/NtfMatchGameQuitHandler.cs | 21 + .../PacketHandler/NtfMatchTestCheatHandler.cs | 20 + .../ReqGameMatchCancelHandler.cs | 20 + .../ReqGameMatchReserveHandler.cs | 21 + .../PacketHandler/ReqMatchGameInfoHandler.cs | 20 + .../Controllers/PacketRecvHandlerEx.cs | 9 + MatchServer/Controllers/RabbitMq4Match.cs | 88 + MatchServer/Directory.Build.props | 8 + .../Infra/Config/IServerConfigService.cs | 34 + MatchServer/Infra/Config/ServerConfig.cs | 714 +++ .../Infra/Config/ServerConfigService.cs | 40 + MatchServer/MatchServer.csproj | 42 + MatchServer/MatchServerExtensions.cs | 89 + MatchServer/MatchServerLogic.cs | 325 ++ MatchServer/MatchServerService.cs | 124 + .../ForceStopServerPacketHandler.cs | 33 + .../StopServerPacketHandler.cs | 27 + MatchServer/Program.cs | 9 + .../BusinessLog/LogActor/MatchLogActor.cs | 35 + .../Services/Factories/InstanceRoomRepo.cs | 206 + .../Factories/MatchGroupProcessorFactory.cs | 65 + .../Factories/MatchProcessorFactory.cs | 17 + .../Services/Factories/MatchRoomFactory.cs | 23 + MatchServer/Services/GameEventCheckWorker.cs | 35 + .../Services/Interfaces/IMatchExecutor.cs | 6 + .../Interfaces/IMatchGroupProcessorFactory.cs | 8 + .../Interfaces/IMatchMakingProcessor.cs | 9 + .../Interfaces/IMatchMessageFactory.cs | 12 + .../Interfaces/IMatchMessageSender.cs | 9 + .../Services/Interfaces/IMatchMetaManager.cs | 46 + .../Interfaces/IMatchProcessorFactory.cs | 7 + .../Services/Interfaces/IMatchRoomFactory.cs | 6 + MatchServer/Services/MatchGroupManager.cs | 290 + .../Services/MatchGroupProcessor.Log.cs | 246 + MatchServer/Services/MatchGroupProcessor.cs | 499 ++ MatchServer/Services/MatchMessageHandler.cs | 71 + MatchServer/Services/MatchMetaManager.cs | 188 + MatchServer/Services/MatchProcessor.cs | 173 + MatchServer/Services/MatchService.Cmd.cs | 79 + MatchServer/Services/MatchService.cs | 593 ++ MatchServer/Services/MatchServiceNew.Cmd.cs | 79 + MatchServer/Services/MatchServiceNew.cs | 368 ++ .../Repositories/MatchRoomRepository.cs | 136 + .../Repositories/MatchUserRepository.cs | 92 + .../ValueObjects/MatchExecutionResult.cs | 13 + .../ValueObjects/ProcessedUsersResult.cs | 180 + Protocol/Protocol.csproj | 150 +- ...b43cf4488ddfd990500fad3e71f32d00d.svn-base | 72 + ...7b64c0f53f1b1d7a3fc6dfa13a3ea1b27.svn-base | 527 ++ ...f5fad84aa036940bc896af50a39927477.svn-base | 73 + ...3ea9a553172ab1a4514c3b6f995651e51.svn-base | 76 + ...b5fa598b020b6889e479359461fcaec40.svn-base | 74 + ...c25085873df744f902fc55d169f3c058d.svn-base | 470 ++ ...eeaa48562e6aa5d862154ce91ecb0c56e.svn-base | 555 ++ ...6b74b5fb74e0defd00ecb0c5f09d92372.svn-base | 565 ++ ...c522f11fb55c98f64915423fecbd4854b.svn-base | 564 ++ ...e4f470cbc2510f47f620cef6acfb9c314.svn-base | 591 ++ ...5ba2f4797a1c3ae30a8e1e0560b411b03.svn-base | 567 ++ ...5c8e6689660c94d86ba0ade4ef23568b1.svn-base | 560 ++ ...e11e33594095c5428d39963250e646fe0.svn-base | 70 + Protocol/client-proto/.svn/wc.db | Bin 122880 -> 122880 bytes Protocol/client-proto/Packet.proto | 318 +- Protocol/client-proto/Protocol.proto | 15 +- Protocol/client_proto_build.bat | 2 +- Protocol/client_proto_build.sh | 64 + Protocol/out-Proto/DefineResult.cs | 1587 +++++- Protocol/out-Proto/DefineResult.cs.mine | 4786 ++++++++++++++++ Protocol/out-Proto/DefineResult.cs.r128445 | 4895 ++++++++++++++++ Protocol/out-Proto/DefineResult.cs.r131478 | 4950 +++++++++++++++++ Protocol/pidl_build.sh | 39 + Protocol/proto/ClientToGame.proto | 1349 +++-- Protocol/proto/Define_Common.proto | 538 +- Protocol/proto/Define_Result.proto | 221 +- Protocol/proto/Game_Define.proto | 283 +- Protocol/proto/ServerMessage.proto | 257 +- Protocol/proto_build.bat | 2 +- Protocol/proto_build.sh | 98 + Server-Build.sln | 30 +- ServerBase/0. Base/BaseGetSet.cs | 61 +- ServerBase/1. Define/DatabaseDefine.cs | 3 + ServerBase/BusinessLog/BusinessLogger.cs | 3 + ServerBase/Cache/CacheBase.cs | 2 + .../ServerMetricsCacheRequest.cs | 5 + ServerBase/Config/ServerConfig.cs | 36 +- .../Config/ServerConfigMetaverseBroker.cs | 56 +- ServerBase/DB/DynamoDb/AttribBase.cs | 4 +- ServerBase/DB/DynamoDb/DynamoDbClient.cs | 2 + ServerBase/DB/DynamoDb/DynamoDbDocBase.cs | 4 +- .../DB/DynamoDb/DynamoDbItemRequestBase.cs | 4 + .../DynamoDbQueryExceptionNotifier.cs | 3 + ServerBase/DB/MongoDb/MongoDbConnector.cs | 4 + ServerBase/DB/MySqlDb/MySqlDbConnector.cs | 2 + ServerBase/DB/QueryBatch.cs | 5 + ServerBase/DB/QueryExecutorBase.cs | 4 + ServerBase/DB/QueryRunnerWithDocument.cs | 4 + ServerBase/DB/QueryRunnerWithItemRequest.cs | 4 + ServerBase/Entity/Action/EntityActionBase.cs | 3 + .../Entity/Aggregation/EntityAggregator.cs | 3 +- .../Entity/Attribute/EntityAttributeBase.cs | 34 +- .../Entity/Delta/EntityDeltaRecorder.cs | 3 + ServerBase/Entity/EntityBase.cs | 3 + ServerBase/Entity/State/EntityHFSMBase.cs | 2 + ServerBase/Entity/State/EntityStateBase.cs | 3 + ServerBase/Entity/Task/EntityTicker.cs | 3 + .../EntityTransactionRunnerWithScopLock.cs | 3 + .../Entity/Transaction/TransactionRunner.cs | 6 + ServerBase/Etc/ModuleContext.cs | 4 + ServerBase/Etc/ResultValue.cs | 2 +- ServerBase/Event/SimpleEventTriggerBase.cs | 2 +- ServerBase/Helper/DataCopyHelper.cs | 2 +- ServerBase/Helper/DynamoDbClientHelper.cs | 34 + ServerBase/Helper/DynamoDbDocBaseHelper.cs | 22 +- ServerBase/Helper/HttpClientHelper.cs | 6 +- ServerBase/Helper/ProgramVersionHelper.cs | 5 +- ServerBase/Helper/ResultHelper.cs | 16 +- ServerBase/Helper/ServerConfigHelper.cs | 2 +- ServerBase/Helper/ServerHelper.cs | 4 +- ServerBase/Helper/TransactionRunnerHelper.cs | 5 +- ServerBase/Logic/ServerLogicBase.cs | 81 +- ServerBase/Manager/ServerMetricsManager.cs | 2 + ServerBase/MessageQueue/RabbitMqConnector.cs | 26 +- ServerBase/Meta/MetaAssets/ContentLoader.cs | 51 - .../Meta/MetaAssets/ContentTableBase.cs | 11 - .../Meta/MetaValidation/MetaValidator.cs | 24 +- ServerBase/Monitor/Monitor.cs | 4 + .../Monitor/NamedPipe/NamedPipeMonitor.cs | 41 +- .../Network/Handler/LargePacketHandler.cs | 251 - ServerBase/Network/Packet/PacketHandler.cs | 17 +- ServerBase/Network/Packet/PacketReceiver.cs | 3 + ServerBase/Network/Packet/PacketSender.cs | 3 + ServerBase/ProudNet/Session/SessionBase.cs | 68 +- ServerBase/Redis/RedisConnector.cs | 4 + ServerBase/Redis/RedisRequestBase.cs | 3 + ServerBase/Redis/RedisRequestPrivateBase.cs | 3 + ServerBase/Redis/RedisRequestSharedBase.cs | 2 + .../Redis/RedisRequestWithLambdaBase.cs | 4 +- .../Redis/RedisWithLuaScriptExecutor.cs | 2 + ServerBase/S3/S3Connector.cs | 2 + ServerBase/ServerBase.csproj | 93 +- .../ServerMetrics/ServerMetricsHandler.cs | 10 +- ServerBase/Ticker/TimeEventForMinuteTicker.cs | 4 + ServerBase/User/UserManagerBase.cs | 7 +- .../Battle/BattleObjectInteractionLogInfo.cs | 2 +- .../Battle/BattleObjectStateUpdateLogInfo.cs | 2 +- .../Battle/BattlePodCombatRewardLogInfo.cs | 4 +- .../Battle/BattleRespawnLogInfo.cs | 4 +- .../Battle/BattleRoundingUpdateLogInfo.cs | 16 +- .../Battle/BattleSnapshotLogInfo.cs | 6 +- .../Domain/GameModePlayerRegulationLogInfo.cs | 32 + .../BusinessLog/Domain/MatchLogInfo.cs | 77 + .../BusinessLog/Domain/MatchUserLogInfo.cs | 33 + .../BusinessLog/Domain/RankerLogInfo.cs | 55 + .../Domain/RankingEventActionScoreLog.cs | 86 + .../BusinessLog/Domain/RankingLogInfo.cs | 44 + .../BusinessLog/Domain/TaxiLogInfo.cs | 22 +- .../1. Define/BusinessLog/Enum/LogEnum.cs | 177 +- .../GameModeMatchRegulationLogInfo.cs | 32 + .../GameModeObject.cs} | 42 +- .../GameMode/GameModePenaltyLogInfo.cs | 42 + .../GameObjectInteractionLogInfoBase.cs | 79 + .../GameObjectInteractionLogInfoEmpty.cs | 13 + .../GameMode/IGameObjectInteractionLogInfo.cs | 6 + .../GameModeRunPracticePlayLogInfo.cs | 37 + .../RunPracticeBuffInteractionLogInfo.cs | 23 + ...RunPracticeCheckPointInteractionLogInfo.cs | 24 + .../RunPracticeGoalLineInteractionLogInfo.cs | 23 + ...GameModeRunRaceCheckPointAbusingLogInfo.cs | 38 + .../GameModeRunRaceRewardSummayLogInfo.cs | 91 + .../RunRace/RunRaceBuffInteractionLogInfo.cs | 24 + .../RunRaceCheckPointInteractionLogInfo.cs | 26 + .../RunRace/RunRaceFinishRewardLogInfo.cs | 28 + .../RunRaceGoalLineInteractionLogInfo.cs | 26 + .../RunRace/RunRaceRespawnRewardLogInfo.cs | 28 + .../RunRace/RunRaceUnFinishRewardLogInfo.cs | 24 + .../TpsFfa/TpsFfaBuffInteractionLogInfo.cs | 21 + .../TpsFfaCombatPodInteractionLogInfo.cs | 29 + .../TpsFfaPickupPodInteractionLogInfo.cs | 24 + .../TpsFfaPodStorageInteractionLogInfo.cs | 44 + .../TpsFfa/TpsFfaWeaponInteractionLogInfo.cs | 22 + ServerCommon/1. Define/Constant.cs | 24 +- ServerCommon/1. Define/DatabaseDefine.cs | 2 + ServerCommon/1. Define/TypeDefine.cs | 19 +- .../BusinessLog/LogActor/RankingActorLog.cs | 51 + .../LogHelper/BusinessLogInvoker.cs | 61 + .../LogInvoker/BeaconCreateBusinessLog.cs | 1 - .../LogInvoker/CaliumBusinessLog.cs | 2 +- .../GameModeMatchRegulationBusinessLog.cs | 23 + .../GameModePlayerRegulationBusinessLog.cs | 24 + .../GameObjectInteractionBusinessLogBase.cs | 22 + .../LogInvoker/RankerBusinessLog.cs | 31 + .../LogInvoker/RankingBusinessLog.cs | 30 + .../RunPracticeBuffInteractionBuisinessLog.cs | 22 + ...acticeCheckPointInteractionBuisinessLog.cs | 22 + ...PracticeGoalLineInteractionBuisinessLog.cs | 24 + .../LogInvoker/RunPracticePlayBusinessLog.cs | 22 + .../RunRaceBuffInteractionBuisinessLog.cs | 22 + ...unRaceCheckPointInteractionBuisinessLog.cs | 22 + .../RunRaceGoalLineInteractionBuisinessLog.cs | 22 + .../TpsFfaBuffInteractionBuisinessLog.cs | 25 + .../TpsFfaCombatPodInteractionBuisinessLog.cs | 23 + .../TpsFfaPickupPodInteractionBuisinessLog.cs | 25 + ...TpsFfaPodStorageInteractionBuisinessLog.cs | 25 + .../TpsFfaWeaponInteractionBuisinessLog.cs | 23 + ServerCommon/Cache/LocationCacheRequest.cs | 11 +- ServerCommon/Cache/RankingCacheRequest.cs | 230 + ServerCommon/Contents/GameEvent/GameEvent.cs | 21 + .../GameEvent/GameEventDatabaseLoader.cs | 70 + .../Contents/GameEvent/GameEventManager.cs | 562 ++ .../Contents/GameEvent/GameEventManagerOld.cs | 292 + .../GameMode/GameModeRewardConditionBase.cs | 23 + .../GameMode/IGameModeRewardCondition.cs | 6 + .../RunRaceMinimumRespawnRewardCondition.cs | 15 + .../GameMode/RunRaceRankRewardCondition.cs | 15 + .../RunRaceUnfinishRankRewardCondition.cs | 14 + .../TpsFfaPickupPodRewardCondition.cs | 11 + .../TpsFfaPodStorageRewardCondition.cs | 16 + .../GameModeMatchSettingsLoader.cs | 55 + ServerCommon/Contents/Match/GameRoomInfo.cs | 40 + ServerCommon/Contents/Match/MagleveHasher.cs | 177 + ServerCommon/Contents/Match/MatchCheatCmd.cs | 37 + ServerCommon/Contents/Match/MatchGuard.cs | 60 + ServerCommon/Contents/Match/MatchPolicy.cs | 34 + ServerCommon/Contents/Match/MatchPoolInfo.cs | 47 + .../Contents/Match/MatchReserveUser.cs | 65 + ServerCommon/Contents/Match/MatchRoom.cs | 172 + ServerCommon/Contents/Match/MatchRouter.cs | 45 + ServerCommon/Contents/Match/MatchTeam.cs | 46 + ServerCommon/Contents/Match/RabbitMqRpc.cs | 106 + .../Contents/Match/ResultException.cs | 29 + .../ReservationMessageManager.cs | 7 +- .../ReservationUserManager.cs | 3 + .../ReturnUserToServer/ReturnUserManager.cs | 3 + ServerCommon/Doc/Global/BannerDoc.cs | 94 + ServerCommon/Doc/Global/CaliumStorageDoc.cs | 7 +- .../GameModeMatchSettingsDoc.cs | 80 + .../GameMatchSettings/GameModeSettingsDoc.cs | 53 + .../GameMatchSettings/GameModeTeamDoc.cs | 39 + ServerCommon/Doc/Global/RankDoc.cs | 101 + ServerCommon/Doc/Global/RankerDoc.cs | 99 + ServerCommon/Doc/Global/RankerSnapshotDoc.cs | 108 + ServerCommon/Doc/Global/RankingDoc.cs | 69 + ServerCommon/Doc/Global/RankingScheduleDoc.cs | 101 + ServerCommon/Doc/Global/RankingSnapshotDoc.cs | 90 + ServerCommon/Doc/Global/UgcNpcRankDoc.cs | 12 +- .../Doc/Global/UserNicknameRegistryDoc.cs | 2 +- .../Doc/Global/WorldEventGlobalScoreDoc.cs | 81 + .../Doc/Global/WorldEventScheduleDoc.cs | 120 + ServerCommon/Doc/Global/WorldEventScoreDoc.cs | 83 + .../OwnerContents/GameModeBestRecordDoc.cs | 129 + .../GameModePlayRegulationDoc.cs | 114 + ServerCommon/Doc/OwnerContents/QuestDoc.cs | 8 +- .../Doc/OwnerContents/QuestMailDoc.cs | 6 - ServerCommon/Doc/UserBase/LocationDoc.cs | 3 + .../EchoSystem/Models/CaliumRollUpResponse.cs | 9 +- .../Models/ConverterSyncResponse.cs | 2 + .../Entity/Attribute/AccountAttribute.cs | 39 +- .../Entity/Attribute/BannerAttribute.cs | 277 + .../Attribute/FarmingEffectAttribute.cs | 4 +- .../FarmingEffectLocationInTargetAttribute.cs | 2 +- .../Attribute/FarmingEffectOwnerAttribute.cs | 2 +- .../Entity/Attribute/GameEventAttribute.cs | 110 + .../Attribute/GameModeBestRecordAttribute.cs | 221 + .../GameModePlayRegulationAttribute.cs | 323 ++ .../Entity/Attribute/LocationAttribute.cs | 29 +- .../Entity/Attribute/RankAttribute.cs | 275 + .../Entity/Attribute/RankerAttribute.cs | 266 + .../Entity/Attribute/RankingAttribute.cs | 246 + .../Attribute/RankingScheduleAttribute.cs | 282 + .../Entity/Attribute/UserAttribute.cs | 7 +- .../Attribute/WorldEventScoreAttribute.cs | 224 + .../WorldEventTotalScoreAttribute.cs | 218 + .../DbQuery/DBQEntityReadToAttribute.cs | 2 +- ServerCommon/Entity/Player/UserBase.cs | 4 + ServerCommon/Helper/ConfigHelper.cs | 3 +- ServerCommon/Helper/EntityHelper.cs | 4 +- ServerCommon/Helper/InventoryRuleHelper.cs | 3 + .../Helper/LoadBalanceServerHelper.cs | 18 +- ServerCommon/Helper/MetaHelper.cs | 4 + .../Helper/ServerConnectionSwitchHelper.cs | 2 + ServerCommon/Helper/ServerMetricsHelper.cs | 4 + ServerCommon/Helper/StringRuleHelper.cs | 3 + ServerCommon/Helper/TypeHelper.cs | 2 + ServerCommon/Meta/GameConfigMeta.cs | 1241 +++-- ServerCommon/MetaAssets/ContentLoader.cs | 79 +- ServerCommon/MetaAssets/ContentTableBase.cs | 16 +- ServerCommon/MetaAssets/ItemMetaHelper.cs | 2 + ServerCommon/MetaAssets/MetaTable.cs | 423 +- .../MetaAssets/MetaTable/BattleObjectData.cs | 4 +- .../MetaAssets/MetaTable/ContentsMenuData.cs | 99 + .../MetaAssets/MetaTable/CraftingData.cs | 8 + .../MetaTable/EventActionScoreData.cs | 99 + .../MetaTable/GameModeCommonData.cs | 83 + .../MetaTable/GameModeConditionData.cs | 71 + .../MetaAssets/MetaTable/GameModeData.cs | 99 + .../MetaTable/GameModeInstanceData.cs | 71 + .../MetaAssets/MetaTable/GameModeMatchData.cs | 92 + .../MetaTable/GameModeRewardData.cs | 75 + .../GameModeRunRaceCheckPointMonitorData.cs | 63 + .../MetaTable/GameModeRunRaceData.cs | 75 + .../MetaAssets/MetaTable/GameModeStartData.cs | 67 + .../MetaAssets/MetaTable/GameModeTeamData.cs | 73 + .../MetaTable/GameModeTpsFfaData.cs | 83 + .../MetaAssets/MetaTable/RankingData.cs | 83 + .../MetaAssets/MetaTable/ShopProductData.cs | 4 + ServerCommon/MetaAssets/MetaTable/TaxiData.cs | 4 + .../MetaData/GameMode/GameModeAllMeta.cs | 42 + .../GameMode/GameModeConditionRewardMeta.cs | 27 + .../MetaData/GameMode/GameModeMeta.cs | 6 + .../MetaData/GameMode/GameModeMetaHelper.cs | 152 + .../MetaData/GameMode/IGameModeDetailMeta.cs | 8 + .../MetaData/GameMode/RunAdventureMeta.cs | 11 + .../MetaData/GameMode/RunPracticeMeta.cs | 14 + ServerCommon/MetaData/GameMode/RunRaceMeta.cs | 175 + ServerCommon/MetaData/GameMode/TPSFfaMeta.cs | 78 + ServerCommon/MetaData/GameMode/TPSTdmMeta.cs | 11 + ServerCommon/MetaData/MapData.cs | 4 +- ServerCommon/MetaData/MapDataTable.cs | 11 +- ServerCommon/MetaData/MetaData.cs | 44 +- ServerCommon/MetaData/MetaEnums.cs | 160 +- .../MetaTableValidation/CommonValidator.cs | 5 + .../ItemMetaDataValidator.cs | 29 + ServerCommon/MetaData/QuestAssignData.cs | 2 + ServerCommon/ProudNet/ProudNetListener.cs | 4 +- ServerCommon/Redis/GameInstanceRoomInfo.cs | 7 + ServerCommon/Redis/GameInstanceRoomStorage.cs | 258 + ServerCommon/Redis/InstanceRoomStorage.cs | 53 +- ServerCommon/ServerCommon.csproj | 113 +- ServerCore/AWS/AwsHelper.cs | 5 +- ServerCore/Comparer/KeyComparer.cs | 20 +- ServerCore/Config/ConfigManager.cs | 3 + ServerCore/Container/MultiIndexDictionary.cs | 4 + ServerCore/Dump/DotNetDumpHelper.cs | 3 + ServerCore/DynamoDB/DynamoDbConnectorBase.cs | 5 + ServerCore/HttpClient/SharedHttpClient.cs | 3 + ServerCore/Log/CloudWatchLog.cs | 4 + ServerCore/Log/NLog.cs | 156 +- ServerCore/Math/MathHelper.cs | 2 + ServerCore/Math/QuaternionHelper.cs | 4 + ServerCore/Math/TransformHelper.cs | 5 + ServerCore/MongoDB/MongoDbConnectorBase.cs | 2 + ServerCore/MongoDB/MongoDbRepository.cs | 2 + ServerCore/RabbitMQ/RabbitMqConnectorBase.cs | 4 + ServerCore/Random/RandomHelper.cs | 2 +- ServerCore/Redis/RedisConnectorBase.cs | 5 + .../Redis/RedisWithLuaScriptExecutorBase.cs | 4 + .../Redis/RedisWithLuaScriptExecutorHelper.cs | 3 + ServerCore/S3/S3ConnectorBase.cs | 4 + ServerCore/ServerCore.csproj | 9 - ServerCore/Task/TaskSerializer.cs | 80 +- ServerCore/TypeHelper/JsonHelper.cs | 19 + ServerCore/TypeHelper/StringHelper.cs | 2 +- UGQApiServer/Program.cs | 3 +- 841 files changed, 67879 insertions(+), 12265 deletions(-) create mode 100644 BrokerApiCore/BrokerApiCoreService.cs create mode 100644 BrokerApiCore/Common/PlanetTokenValidator.cs create mode 100644 BrokerApiCore/Entity/Helpers/MailHelper.cs create mode 100644 BrokerApiServer/BrokerServerService.cs create mode 100644 BrokerApiServer/BrokerServiceExtensions.cs delete mode 100644 BrokerApiServer/Controllers/AccountController.cs delete mode 100644 BrokerApiServer/Controllers/CurrencyController.cs delete mode 100644 BrokerApiServer/Controllers/UserController.cs delete mode 100644 BrokerApiTest/Config/ServerConfig-Dev.json delete mode 100644 BrokerApiTest/Config/ServerConfig-QA.json delete mode 100644 BrokerApiTest/Config/ServerConfig-Stage.json delete mode 100644 BrokerApiTest/Config/ServerConfig.json delete mode 100644 BrokerApiTest/Config/nlog.config delete mode 100644 BrokerApiTest/ControllerTests/CurrencyControllerTests.cs create mode 100644 BrokerApiTest/ControllerTests/DevPlanetUserControllerTests.cs create mode 100644 BrokerApiTest/Directory.Build.props create mode 100644 GameServer/Contents/BeaconShop/PacketHandler/BeaconShopRecentRegisterItemsPacketHandler.cs delete mode 100644 GameServer/Contents/GameMode/Action/GameGameObjectAction.cs create mode 100644 GameServer/Contents/GameMode/Action/GameModeBestRecordAction.cs create mode 100644 GameServer/Contents/GameMode/Action/GameModeObjectInteractionHandlerActionBase.cs create mode 100644 GameServer/Contents/GameMode/Action/GameModePlayRegulationAction.cs create mode 100644 GameServer/Contents/GameMode/Action/GameModePositionSetAction.cs create mode 100644 GameServer/Contents/GameMode/Action/GameModePostUpdateAction.cs create mode 100644 GameServer/Contents/GameMode/Action/GameModePreUpdateAction.cs create mode 100644 GameServer/Contents/GameMode/Action/GameModeStateCheckAction.cs create mode 100644 GameServer/Contents/GameMode/Cheat/CheatGameModeBestRecordTest.cs create mode 100644 GameServer/Contents/GameMode/Cheat/CheatGameModeHostNoti.cs create mode 100644 GameServer/Contents/GameMode/Cheat/CheatGameModeUserKick.cs create mode 100644 GameServer/Contents/GameMode/DbQuery/DBQGameModePlayRegulationReadAll.cs create mode 100644 GameServer/Contents/GameMode/InteractionObject/GameObjectInteractionDataCommonHandlerBase.cs create mode 100644 GameServer/Contents/GameMode/InteractionObject/IGameObjectInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Log/GameModePenaltyBusinessLog.cs rename GameServer/Contents/GameMode/{Mode-Battle/Manage/BattleInstanceRoom.cs => Manage/GameModeInstanceRoom.cs} (53%) create mode 100644 GameServer/Contents/GameMode/Manage/LeaveManage/GameModeDisconnectHandlerBase.cs create mode 100644 GameServer/Contents/GameMode/Manage/LeaveManage/IGameModeDisconnectHandler.cs create mode 100644 GameServer/Contents/GameMode/Manage/LeaveManage/IPreparationForLeavingGameHandler.cs create mode 100644 GameServer/Contents/GameMode/Manage/LeaveManage/PreparationForLeavingGameBase.cs create mode 100644 GameServer/Contents/GameMode/Manage/Matching/IMatchingReservationHandler.cs create mode 100644 GameServer/Contents/GameMode/Manage/Matching/MatchingReservationHandlerBase.cs create mode 100644 GameServer/Contents/GameMode/Manage/PlayManage/GameObjectnteractionDataBase.cs create mode 100644 GameServer/Contents/GameMode/Manage/PlayManage/IGameModeLoadCompleteHandler.cs delete mode 100644 GameServer/Contents/GameMode/Manage/PlayManage/IGameObject.cs create mode 100644 GameServer/Contents/GameMode/Manage/PlayManage/IGameObjectInteractionHandler.cs create mode 100644 GameServer/Contents/GameMode/Manage/PlayManage/IGameObjectnteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Manage/PlayManage/IRespawnHandler.cs create mode 100644 GameServer/Contents/GameMode/Manage/StateManage/GameModeStateNone.cs create mode 100644 GameServer/Contents/GameMode/Manage/StateManage/GameModeTransitionStrategyBase.cs create mode 100644 GameServer/Contents/GameMode/Manage/StateManage/IGameModeStateManager.cs create mode 100644 GameServer/Contents/GameMode/Manage/StateManage/IGameModeTransitionStrategy.cs create mode 100644 GameServer/Contents/GameMode/Manage/StateManage/StateChangeHistory.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRespawn.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRoomHandler.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Battle/Manage/GameModeTPS.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleZoneMoveAction.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/FfaGameObjectPodStorageInteractAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaBuffInteractAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaBuffInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaCombatPodInteractAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaCombatPodInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaLoadCompleteAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaObjectUpdateAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPickupPodInteractAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPickupPodInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPodStorageInteractAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPodStorageInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaSetUserReadyPosAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaWeaponInteractAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaWeaponInteractionDataHandler.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceRoom.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/TpsFfaChargeRewardData.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/TpsFfaHelper.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllTransitionStrategy.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFfaMatchingReservationHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFreeForAllDisconnectHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFreeForAllPreparationForLeavingHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Meta/TpsFfaMetaData.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateCreated.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateReady.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRoundEndAll.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRoundWait.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRounding.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateWait.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchTransitionStrategy.cs create mode 100644 GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TpsTeamDeathMatchPreparationForLeavingHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventurePreparationForLeavingHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureTransitionStrategy.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeBuffInteractAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeBuffInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeCheckPointInteractAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeCheckPointInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeDeadAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeGoalLineInteractionAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeGoalLineInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeLoadCompleteAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeObjectUpdateAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticePlayAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeReadyPosAssignAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeRespawnAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Helper/RunPracticeHelper.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/GameModeRunPractice.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeDestroyHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeDisconnectHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeInitHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeJoinHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeJoinSuccessHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeLeaveHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticePreparationForLeavingHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeTransitionStrategy.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/Meta/RunPracticeMetaData.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateCreated.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateDestroyed.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStatePlay.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateReady.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateResult.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateSummary.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateWait.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RaceGameObjectSavePointInteractAction.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RaceStateCheckAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceBuffInteractAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceBuffInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceCheckPointInteractAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceCheckPointInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceDeadAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceGoalLineInteractionAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceGoalLineInteractionDataHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceLoadCompleteAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceObjectUpdateAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRacePlayAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceReadyPosAssignAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceRespawnAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceRewardAction.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Helper/RunRaceNotifyHelper.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceCheckPointAbusingBusinessLog.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceFinishRewardBusinessLog.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceRespawnRewardBusinessLog.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceRewardSummaryBusinessLog.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceUnfinishRewardBusinessLog.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/GameModeRunRaceData.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceDisconnectHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceMatchingReservationHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRacePreparationForLeavingHandler.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceTransitionStrategy.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Meta/RunRaceMetaData.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/Reward/RewardRunRace.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateDestroyed.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateEnd.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateReady.cs delete mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateStart.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateCreated.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateDestroyed.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateEnd.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStatePlay.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateReady.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateResult.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateRewardSummary.cs create mode 100644 GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateWait.cs create mode 100644 GameServer/Contents/GameMode/PacketHandler/GameLeavePacketHandler.cs create mode 100644 GameServer/Contents/GameMode/PacketHandler/GameModeLoadCompletePacketHandler.cs create mode 100644 GameServer/Contents/GameMode/PacketHandler/GamePlayerDeadPacketHandler.cs create mode 100644 GameServer/Contents/GameMode/PacketHandler/GamePlayerRespawnPacketHandler.cs create mode 100644 GameServer/Contents/GameMode/PacketHandler/PreparationForLeavingGameInstancePacketHandler.cs create mode 100644 GameServer/Contents/GameMode/State/GameModeStateLoadingWait.cs create mode 100644 GameServer/Contents/GameZone/PacketHandler/ContentsMovePacketHandler.cs create mode 100644 GameServer/Contents/GameZone/PacketHandler/DestroyWeaponObjectPacketHandler.cs create mode 100644 GameServer/Contents/GameZone/PacketHandler/MoveToBeaconPacketHandler.cs create mode 100644 GameServer/Contents/LargePacket/Action/LargePacketHandleAction.cs create mode 100644 GameServer/Contents/LargePacket/PacketHandler/LargePacketHandler.cs create mode 100644 GameServer/Contents/Match/CheatCommand/ChatCommandMatch.cs create mode 100644 GameServer/Contents/Match/Doc/Match_CS_Sequence.md create mode 100644 GameServer/Contents/Match/Doc/게임 매칭 CS 시퀀스 .png create mode 100644 GameServer/Contents/Match/IMatchStrategy.cs create mode 100644 GameServer/Contents/Match/MatchJoinGameRoomReserve.cs create mode 100644 GameServer/Contents/Match/MatchManager.Log.cs create mode 100644 GameServer/Contents/Match/MatchManager.cs create mode 100644 GameServer/Contents/Match/MatchRoomEventHandler.cs create mode 100644 GameServer/Contents/Match/PacketHandler/MatchCancelPacketHandler.cs create mode 100644 GameServer/Contents/Match/PacketHandler/MatchNotifyHelper.cs create mode 100644 GameServer/Contents/Match/PacketHandler/MatchReservePacketHandler.cs create mode 100644 GameServer/Contents/Reward/RewardHelper.cs create mode 100644 GameServer/Contents/WorldEvent/PacketHandler/ChatCommandEventAction.cs create mode 100644 GameServer/Contents/WorldEvent/PacketHandler/ChatCommandWorldEvent.cs create mode 100644 GameServer/Contents/WorldEvent/PacketHandler/WorldEventContributionHandler.cs create mode 100644 GameServer/Entity/Match/MatchAction.cs create mode 100644 GameServer/Entity/Player/FakePlayer.cs create mode 100644 GameServer/Entity/Rank/Action/RankAction.cs create mode 100644 GameServer/Entity/Rank/Action/RankAgentAction.cs create mode 100644 GameServer/Entity/Rank/Helper/RankHelper.cs create mode 100644 GameServer/Entity/Rank/Rank.cs create mode 100644 GameServer/Entity/Ranker/Action/RankerAction.cs create mode 100644 GameServer/Entity/Ranker/Action/RankerAgentAction.cs create mode 100644 GameServer/Entity/Ranker/Helper/RankerBusinessLogHelper.cs create mode 100644 GameServer/Entity/Ranker/Helper/RankerHelper.cs create mode 100644 GameServer/Entity/Ranker/Ranker.cs create mode 100644 GameServer/Entity/Ranker/RankerCheat.cs create mode 100644 GameServer/Entity/Ranking/Action/RankingAction.cs create mode 100644 GameServer/Entity/Ranking/Helper/RankingBusinessLogHelper.cs create mode 100644 GameServer/Entity/Ranking/Helper/RankingCacheHelper.cs create mode 100644 GameServer/Entity/Ranking/Helper/RankingHelper.cs create mode 100644 GameServer/Entity/Ranking/Helper/RankingNotifyHelper.cs create mode 100644 GameServer/Entity/Ranking/PacketHandler/RankingInfoPacketHandler.cs create mode 100644 GameServer/Entity/Ranking/Ranking.cs create mode 100644 GameServer/Entity/Ranking/RankingCheat.cs create mode 100644 GameServer/Entity/RankingSchedule/Action/RankingScheduleAction.cs create mode 100644 GameServer/Entity/RankingSchedule/Helper/RankingScheduleHelper.cs create mode 100644 GameServer/Entity/RankingSchedule/Helper/RankingScheduleNotifyHelper.cs create mode 100644 GameServer/Entity/RankingSchedule/RankingSchedule.cs create mode 100644 GameServer/Entity/RankingSchedule/RankingScheduleCheat.cs create mode 100644 GameServer/Event/Action/GameEventCollectorAction.cs create mode 100644 GameServer/Event/EventDefines/GameEventDefines.cs create mode 100644 GameServer/Event/EventInvokers/GameEventFfaOnPickUpObject.cs create mode 100644 GameServer/Event/EventInvokers/GameEventInvokerBase.cs create mode 100644 GameServer/Event/EventInvokers/GameEventInvokerClaimRewarded.cs create mode 100644 GameServer/Event/EventInvokers/GameEventInvokerCraftingCompleted.cs create mode 100644 GameServer/Event/EventInvokers/GameEventInvokerFarmingCompleted.cs create mode 100644 GameServer/Event/EventInvokers/GameEventInvokerQuestRewarded.cs create mode 100644 GameServer/Event/EventInvokers/GameEventInvokerRewadPropRewarded.cs create mode 100644 GameServer/Event/EventInvokers/GameEventInvokerSeasonPassRewarded.cs create mode 100644 GameServer/Event/EventInvokers/GameEventInvokerTaxiArrived.cs create mode 100644 GameServer/Event/EventInvokers/GameEventRunRaceCompleteTime.cs create mode 100644 GameServer/Event/EventInvokers/IGameEventInvoker.cs create mode 100644 GameServer/Event/EventRunners/GameEventRankRunRaceFinishTime.cs create mode 100644 GameServer/Event/EventRunners/GameEventRunnerBase.cs create mode 100644 GameServer/Event/EventRunners/IGameEventRunner.cs create mode 100644 GameServer/Event/GameEventAnalyzer.cs create mode 100644 GameServer/Event/GameEventCollector.cs create mode 100644 GameServer/Event/GameEventHelper.cs create mode 100644 GameServer/Event/GameEventTriggerManager.cs create mode 100644 GameServer/Event/GlobalGameEventInvokerManager.cs create mode 100644 GameServer/Event/Manage/GlobalGameEvent.cs create mode 100644 GameServer/Global/Banner/Action/BannerAction.cs create mode 100644 GameServer/Global/Banner/Banner.cs create mode 100644 GameServer/Global/Banner/BannerManager.cs create mode 100644 GameServer/Global/Banner/Helper/BannerHelper.cs create mode 100644 GameServer/Global/Banner/Helper/BannerNotifyHelper.cs create mode 100644 GameServer/Global/EventActionScore/EventActionScoreHelper.cs create mode 100644 GameServer/Global/EventActionScore/EventActionScoreManager.cs create mode 100644 GameServer/Global/EventActionScore/EventActionScoreProcessor.Log.cs create mode 100644 GameServer/Global/EventActionScore/EventActionScoreProcessor.cs create mode 100644 GameServer/Global/EventActionScore/EventActionScoreResult.cs create mode 100644 GameServer/Global/EventActionScore/Interfaces/IEventActionScoreHelper.cs create mode 100644 GameServer/Global/EventActionScore/Interfaces/IEventToScoreHandler.cs create mode 100644 GameServer/Global/EventActionScore/Interfaces/IRankingEventScheduleInfo.cs create mode 100644 GameServer/Global/EventActionScore/Interfaces/IWorldEventScheduleInfo.cs create mode 100644 GameServer/Global/EventActionScore/Interfaces/IWorldEventScoreRepo.cs create mode 100644 GameServer/Global/EventActionScore/RankingEventActionScore.cs create mode 100644 GameServer/Global/EventActionScore/RankingEventEventScheduleInfo.cs create mode 100644 GameServer/Global/EventActionScore/WorldEventActionScore.cs create mode 100644 GameServer/Global/EventActionScore/WorldEventScheduleInfo.cs create mode 100644 GameServer/Global/EventActionScore/WorldEventScoreRepo.cs create mode 100644 GameServer/Global/Ranking/RankingManager.cs create mode 100644 GameServer/Global/RankingSchedule/RankingScheduleManager.cs delete mode 100644 GameServer/Global/Rental/RentalManager.cs create mode 100644 GameServer/Global/WorldEvent/WorldEventManager.cs create mode 100644 GameServer/Global/WorldEvent/WorldEventScheduleManager.cs create mode 100644 GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchCancelHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchReserveHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchRoomInfoHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchGameJoinReserveHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchResultHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchStatusHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/NtfModifyRankingInfoMQPackerHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/NtfQuestTaskForceCompleteMQPacketHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/NtfRefreshRankMQPacketHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/NtfUpdateBannerMQPacketHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/NtfUpdateRankingScheduleMQPackerHandler.cs create mode 100644 GameServer/MessageQueue/PacketHandler/ReqUpdateRankingIntervalMQPacketHandler.cs create mode 100644 GameServer/Ticker/GameEventCheckTicker.cs create mode 100644 GameServer/Ticker/MatchServerListTicker.cs create mode 100644 GameServer/Ticker/RankingScheduleUpdateTicker.cs create mode 100644 GameServer/Ticker/RankingUpdateTicker.cs create mode 100644 GameServer/Ticker/WorldEventScheduleUpdateTicker.cs delete mode 100644 GameServer/z.Backup/ClientSession_Friend.cs create mode 100644 MatchServer/Common/IMatchMq.cs create mode 100644 MatchServer/Common/ISeqWorkQue.cs create mode 100644 MatchServer/Common/ISeqWorker.cs create mode 100644 MatchServer/Common/LazyServiceExtensions.cs create mode 100644 MatchServer/Common/PeriodicWorker.cs create mode 100644 MatchServer/Common/SeqWorkQue.cs create mode 100644 MatchServer/Common/SequenceWorkQue.cs create mode 100644 MatchServer/Common/SequenceWorker.cs create mode 100644 MatchServer/Controllers/PacketHandler/NtfMatchChangeGameStateHandler.cs create mode 100644 MatchServer/Controllers/PacketHandler/NtfMatchGameJoinHandler.cs create mode 100644 MatchServer/Controllers/PacketHandler/NtfMatchGameQuitHandler.cs create mode 100644 MatchServer/Controllers/PacketHandler/NtfMatchTestCheatHandler.cs create mode 100644 MatchServer/Controllers/PacketHandler/ReqGameMatchCancelHandler.cs create mode 100644 MatchServer/Controllers/PacketHandler/ReqGameMatchReserveHandler.cs create mode 100644 MatchServer/Controllers/PacketHandler/ReqMatchGameInfoHandler.cs create mode 100644 MatchServer/Controllers/PacketRecvHandlerEx.cs create mode 100644 MatchServer/Controllers/RabbitMq4Match.cs create mode 100644 MatchServer/Directory.Build.props create mode 100644 MatchServer/Infra/Config/IServerConfigService.cs create mode 100644 MatchServer/Infra/Config/ServerConfig.cs create mode 100644 MatchServer/Infra/Config/ServerConfigService.cs create mode 100644 MatchServer/MatchServer.csproj create mode 100644 MatchServer/MatchServerExtensions.cs create mode 100644 MatchServer/MatchServerLogic.cs create mode 100644 MatchServer/MatchServerService.cs create mode 100644 MatchServer/NamedPipePacketHandler/ForceStopServerPacketHandler.cs create mode 100644 MatchServer/NamedPipePacketHandler/StopServerPacketHandler.cs create mode 100644 MatchServer/Program.cs create mode 100644 MatchServer/Services/BusinessLog/LogActor/MatchLogActor.cs create mode 100644 MatchServer/Services/Factories/InstanceRoomRepo.cs create mode 100644 MatchServer/Services/Factories/MatchGroupProcessorFactory.cs create mode 100644 MatchServer/Services/Factories/MatchProcessorFactory.cs create mode 100644 MatchServer/Services/Factories/MatchRoomFactory.cs create mode 100644 MatchServer/Services/GameEventCheckWorker.cs create mode 100644 MatchServer/Services/Interfaces/IMatchExecutor.cs create mode 100644 MatchServer/Services/Interfaces/IMatchGroupProcessorFactory.cs create mode 100644 MatchServer/Services/Interfaces/IMatchMakingProcessor.cs create mode 100644 MatchServer/Services/Interfaces/IMatchMessageFactory.cs create mode 100644 MatchServer/Services/Interfaces/IMatchMessageSender.cs create mode 100644 MatchServer/Services/Interfaces/IMatchMetaManager.cs create mode 100644 MatchServer/Services/Interfaces/IMatchProcessorFactory.cs create mode 100644 MatchServer/Services/Interfaces/IMatchRoomFactory.cs create mode 100644 MatchServer/Services/MatchGroupManager.cs create mode 100644 MatchServer/Services/MatchGroupProcessor.Log.cs create mode 100644 MatchServer/Services/MatchGroupProcessor.cs create mode 100644 MatchServer/Services/MatchMessageHandler.cs create mode 100644 MatchServer/Services/MatchMetaManager.cs create mode 100644 MatchServer/Services/MatchProcessor.cs create mode 100644 MatchServer/Services/MatchService.Cmd.cs create mode 100644 MatchServer/Services/MatchService.cs create mode 100644 MatchServer/Services/MatchServiceNew.Cmd.cs create mode 100644 MatchServer/Services/MatchServiceNew.cs create mode 100644 MatchServer/Services/Repositories/MatchRoomRepository.cs create mode 100644 MatchServer/Services/Repositories/MatchUserRepository.cs create mode 100644 MatchServer/Services/ValueObjects/MatchExecutionResult.cs create mode 100644 MatchServer/Services/ValueObjects/ProcessedUsersResult.cs create mode 100644 Protocol/client-proto/.svn/pristine/01/01089b1b43cf4488ddfd990500fad3e71f32d00d.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/16/168d7827b64c0f53f1b1d7a3fc6dfa13a3ea1b27.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/1e/1ef74f5f5fad84aa036940bc896af50a39927477.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/4f/4f5ec643ea9a553172ab1a4514c3b6f995651e51.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/73/73a39a2b5fa598b020b6889e479359461fcaec40.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/7a/7a71755c25085873df744f902fc55d169f3c058d.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/7d/7d09c1feeaa48562e6aa5d862154ce91ecb0c56e.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/86/86272cd6b74b5fb74e0defd00ecb0c5f09d92372.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/8e/8e9a15cc522f11fb55c98f64915423fecbd4854b.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/a4/a452b6ce4f470cbc2510f47f620cef6acfb9c314.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/b3/b3475cc5ba2f4797a1c3ae30a8e1e0560b411b03.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/d4/d4f06695c8e6689660c94d86ba0ade4ef23568b1.svn-base create mode 100644 Protocol/client-proto/.svn/pristine/db/dbb908ee11e33594095c5428d39963250e646fe0.svn-base create mode 100644 Protocol/client_proto_build.sh create mode 100644 Protocol/out-Proto/DefineResult.cs.mine create mode 100644 Protocol/out-Proto/DefineResult.cs.r128445 create mode 100644 Protocol/out-Proto/DefineResult.cs.r131478 create mode 100644 Protocol/pidl_build.sh create mode 100644 Protocol/proto_build.sh delete mode 100644 ServerBase/Meta/MetaAssets/ContentLoader.cs delete mode 100644 ServerBase/Meta/MetaAssets/ContentTableBase.cs delete mode 100644 ServerBase/Network/Handler/LargePacketHandler.cs create mode 100644 ServerCommon/1. Define/BusinessLog/Domain/GameModePlayerRegulationLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/Domain/MatchLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/Domain/MatchUserLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/Domain/RankerLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/Domain/RankingEventActionScoreLog.cs create mode 100644 ServerCommon/1. Define/BusinessLog/Domain/RankingLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/GameModeMatchRegulationLogInfo.cs rename ServerCommon/1. Define/BusinessLog/{Battle/BattleInstancesObjects.cs => GameMode/GameModeObject.cs} (80%) create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/GameModePenaltyLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/GameObjectInteractionLogInfoBase.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/GameObjectInteractionLogInfoEmpty.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/IGameObjectInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/GameModeRunPracticePlayLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeBuffInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeCheckPointInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeGoalLineInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunRace/GameModeRunRaceCheckPointAbusingLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunRace/GameModeRunRaceRewardSummayLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceBuffInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceCheckPointInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceFinishRewardLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceGoalLineInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceRespawnRewardLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceUnFinishRewardLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaBuffInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaCombatPodInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaPickupPodInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaPodStorageInteractionLogInfo.cs create mode 100644 ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaWeaponInteractionLogInfo.cs create mode 100644 ServerCommon/BusinessLog/LogActor/RankingActorLog.cs create mode 100644 ServerCommon/BusinessLog/LogHelper/BusinessLogInvoker.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/GameModeMatchRegulationBusinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/GameModePlayerRegulationBusinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/GameObjectInteractionBusinessLogBase.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/RankerBusinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/RankingBusinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/RunPracticeBuffInteractionBuisinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/RunPracticeCheckPointInteractionBuisinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/RunPracticeGoalLineInteractionBuisinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/RunPracticePlayBusinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/RunRaceBuffInteractionBuisinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/RunRaceCheckPointInteractionBuisinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/RunRaceGoalLineInteractionBuisinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/TpsFfaBuffInteractionBuisinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/TpsFfaCombatPodInteractionBuisinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/TpsFfaPickupPodInteractionBuisinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/TpsFfaPodStorageInteractionBuisinessLog.cs create mode 100644 ServerCommon/BusinessLog/LogInvoker/TpsFfaWeaponInteractionBuisinessLog.cs create mode 100644 ServerCommon/Cache/RankingCacheRequest.cs create mode 100644 ServerCommon/Contents/GameEvent/GameEvent.cs create mode 100644 ServerCommon/Contents/GameEvent/GameEventDatabaseLoader.cs create mode 100644 ServerCommon/Contents/GameEvent/GameEventManager.cs create mode 100644 ServerCommon/Contents/GameEvent/GameEventManagerOld.cs create mode 100644 ServerCommon/Contents/GameMode/GameModeRewardConditionBase.cs create mode 100644 ServerCommon/Contents/GameMode/IGameModeRewardCondition.cs create mode 100644 ServerCommon/Contents/GameMode/RunRaceMinimumRespawnRewardCondition.cs create mode 100644 ServerCommon/Contents/GameMode/RunRaceRankRewardCondition.cs create mode 100644 ServerCommon/Contents/GameMode/RunRaceUnfinishRankRewardCondition.cs create mode 100644 ServerCommon/Contents/GameMode/TpsFfaPickupPodRewardCondition.cs create mode 100644 ServerCommon/Contents/GameMode/TpsFfaPodStorageRewardCondition.cs create mode 100644 ServerCommon/Contents/GameModeSettings/GameModeMatchSettingsLoader.cs create mode 100644 ServerCommon/Contents/Match/GameRoomInfo.cs create mode 100644 ServerCommon/Contents/Match/MagleveHasher.cs create mode 100644 ServerCommon/Contents/Match/MatchCheatCmd.cs create mode 100644 ServerCommon/Contents/Match/MatchGuard.cs create mode 100644 ServerCommon/Contents/Match/MatchPolicy.cs create mode 100644 ServerCommon/Contents/Match/MatchPoolInfo.cs create mode 100644 ServerCommon/Contents/Match/MatchReserveUser.cs create mode 100644 ServerCommon/Contents/Match/MatchRoom.cs create mode 100644 ServerCommon/Contents/Match/MatchRouter.cs create mode 100644 ServerCommon/Contents/Match/MatchTeam.cs create mode 100644 ServerCommon/Contents/Match/RabbitMqRpc.cs create mode 100644 ServerCommon/Contents/Match/ResultException.cs create mode 100644 ServerCommon/Doc/Global/BannerDoc.cs create mode 100644 ServerCommon/Doc/Global/GameMatchSettings/GameModeMatchSettingsDoc.cs create mode 100644 ServerCommon/Doc/Global/GameMatchSettings/GameModeSettingsDoc.cs create mode 100644 ServerCommon/Doc/Global/GameMatchSettings/GameModeTeamDoc.cs create mode 100644 ServerCommon/Doc/Global/RankDoc.cs create mode 100644 ServerCommon/Doc/Global/RankerDoc.cs create mode 100644 ServerCommon/Doc/Global/RankerSnapshotDoc.cs create mode 100644 ServerCommon/Doc/Global/RankingDoc.cs create mode 100644 ServerCommon/Doc/Global/RankingScheduleDoc.cs create mode 100644 ServerCommon/Doc/Global/RankingSnapshotDoc.cs create mode 100644 ServerCommon/Doc/Global/WorldEventGlobalScoreDoc.cs create mode 100644 ServerCommon/Doc/Global/WorldEventScheduleDoc.cs create mode 100644 ServerCommon/Doc/Global/WorldEventScoreDoc.cs create mode 100644 ServerCommon/Doc/OwnerContents/GameModeBestRecordDoc.cs create mode 100644 ServerCommon/Doc/OwnerContents/GameModePlayRegulationDoc.cs create mode 100644 ServerCommon/Entity/Attribute/BannerAttribute.cs create mode 100644 ServerCommon/Entity/Attribute/GameEventAttribute.cs create mode 100644 ServerCommon/Entity/Attribute/GameModeBestRecordAttribute.cs create mode 100644 ServerCommon/Entity/Attribute/GameModePlayRegulationAttribute.cs create mode 100644 ServerCommon/Entity/Attribute/RankAttribute.cs create mode 100644 ServerCommon/Entity/Attribute/RankerAttribute.cs create mode 100644 ServerCommon/Entity/Attribute/RankingAttribute.cs create mode 100644 ServerCommon/Entity/Attribute/RankingScheduleAttribute.cs create mode 100644 ServerCommon/Entity/Attribute/WorldEventScoreAttribute.cs create mode 100644 ServerCommon/Entity/Attribute/WorldEventTotalScoreAttribute.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/ContentsMenuData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/EventActionScoreData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeCommonData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeConditionData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeInstanceData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeMatchData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeRewardData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeRunRaceCheckPointMonitorData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeRunRaceData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeStartData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeTeamData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/GameModeTpsFfaData.cs create mode 100644 ServerCommon/MetaAssets/MetaTable/RankingData.cs create mode 100644 ServerCommon/MetaData/GameMode/GameModeAllMeta.cs create mode 100644 ServerCommon/MetaData/GameMode/GameModeConditionRewardMeta.cs create mode 100644 ServerCommon/MetaData/GameMode/GameModeMeta.cs create mode 100644 ServerCommon/MetaData/GameMode/GameModeMetaHelper.cs create mode 100644 ServerCommon/MetaData/GameMode/IGameModeDetailMeta.cs create mode 100644 ServerCommon/MetaData/GameMode/RunAdventureMeta.cs create mode 100644 ServerCommon/MetaData/GameMode/RunPracticeMeta.cs create mode 100644 ServerCommon/MetaData/GameMode/RunRaceMeta.cs create mode 100644 ServerCommon/MetaData/GameMode/TPSFfaMeta.cs create mode 100644 ServerCommon/MetaData/GameMode/TPSTdmMeta.cs create mode 100644 ServerCommon/Redis/GameInstanceRoomInfo.cs create mode 100644 ServerCommon/Redis/GameInstanceRoomStorage.cs diff --git a/BrokerApiCore/ApiModels/AdminModel.cs b/BrokerApiCore/ApiModels/AdminModel.cs index 3acfd75..ff568db 100644 --- a/BrokerApiCore/ApiModels/AdminModel.cs +++ b/BrokerApiCore/ApiModels/AdminModel.cs @@ -20,6 +20,47 @@ public class AdminSapphireChangeResponse public long SapphireCurrentAmount { get; set; } } +public class AdminPlanetProviderInfoCreateRequest +{ + +} + +public class AdminPlanetProviderInfoCreateResponse +{ + +} + +public class AdminPlanetProviderInfoUpdateRequest +{ + +} + +public class AdminPlanetProviderInfoUpdateResponse +{ + +} + +public class AdminPlanetProviderInfoDeleteRequest +{ + +} + +public class AdminPlanetProviderInfoDeleteResponse +{ + +} + +public class AdminPlanetProviderInfoGetRequest +{ + +} + +public class AdminPlanetProviderInfoGetResponse +{ + +} + + [SwaggerSchema( "교환 주문 목록 요청
- SsoAccountId 또는 UserGuid 중 하나를 선택해서 유저 구분
- seasonId는 필수로 입력 (관련 내용은 사업부에 문의)")] public class AdminItemExchangeOrderListRequest diff --git a/BrokerApiCore/BrokerApiCore.csproj b/BrokerApiCore/BrokerApiCore.csproj index 6a2fbba..12bf2c7 100644 --- a/BrokerApiCore/BrokerApiCore.csproj +++ b/BrokerApiCore/BrokerApiCore.csproj @@ -1,65 +1,60 @@ - - - net8.0 - enable - enable - false - false - true - false - true - $(NoWarn);1591 - - - - - - - - - - - - - - - full - ..\..\bin\Debug\ - - - - full - ..\..\bin\Release\ - - - - full - true - ..\..\bin\Shipping\ - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - + + + net8.0 + enable + enable + false + false + true + false + true + $(NoWarn);1591 + + + + + + + + + + + + + + full + ..\..\bin\Debug\ + + + full + ..\..\bin\Release\ + + + full + true + ..\..\bin\Shipping\ + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + \ No newline at end of file diff --git a/BrokerApiCore/BrokerApiCoreService.cs b/BrokerApiCore/BrokerApiCoreService.cs new file mode 100644 index 0000000..e08a386 --- /dev/null +++ b/BrokerApiCore/BrokerApiCoreService.cs @@ -0,0 +1,17 @@ +// using ServerBase; +// +// namespace BrokerApiCore; +// +// public class BrokerApiCoreService +// { +// private readonly ServerType m_server_type = ServerType.BrokerApi; +// private readonly ServerConfigMetaverseBroker m_server_config; +// +// public BrokerApiCoreService(ServerConfigMetaverseBroker serverConfig) +// { +// m_server_config = serverConfig; +// } +// +// private string m_server_name => nameof(ServerType.BrokerApi); +// +// } diff --git a/BrokerApiCore/BrokerBusinessLog/PlanetItemExchangeBusinessLog.cs b/BrokerApiCore/BrokerBusinessLog/PlanetItemExchangeBusinessLog.cs index 121ab3d..32063a2 100644 --- a/BrokerApiCore/BrokerBusinessLog/PlanetItemExchangeBusinessLog.cs +++ b/BrokerApiCore/BrokerBusinessLog/PlanetItemExchangeBusinessLog.cs @@ -10,8 +10,9 @@ public class PlanetItemExchangeBusinessLog : ILogInvokerEx { private readonly PlanetItemExchangeLogData m_log_data; + // LogDomainType.BrokerApi => LogDomainType.PlanetItemExchange 로 변경 public PlanetItemExchangeBusinessLog(LogActionEx logAction, PlanetItemExchangeLogData logData) - : base(LogDomainType.BrokerApi, logAction) + : base(LogDomainType.PlanetItemExchange, logAction) { m_log_data = new PlanetItemExchangeLogData(this, logData); } diff --git a/BrokerApiCore/BrokerBusinessLog/PlanetUserAuthBusinessLog.cs b/BrokerApiCore/BrokerBusinessLog/PlanetUserAuthBusinessLog.cs index a64c25a..39f7603 100644 --- a/BrokerApiCore/BrokerBusinessLog/PlanetUserAuthBusinessLog.cs +++ b/BrokerApiCore/BrokerBusinessLog/PlanetUserAuthBusinessLog.cs @@ -10,9 +10,9 @@ namespace BrokerApiCore; public class PlanetUserAuthBusinessLog : ILogInvokerEx { private readonly PlanetUserAuthLogData m_log_data; - + // LogDomainType.BrokerApi => LogDomainType.PlanetUserAuth 로 변경 public PlanetUserAuthBusinessLog(PlanetUserAuthLogData logData, LogActionType logActionType) - : base(LogDomainType.BrokerApi, new LogActionEx(logActionType)) + : base(LogDomainType.PlanetUserAuth, new LogActionEx(logActionType)) { m_log_data = new PlanetUserAuthLogData(this, logData); } diff --git a/BrokerApiCore/BrokerServerLogic.cs b/BrokerApiCore/BrokerServerLogic.cs index d2fbaa8..610d60e 100644 --- a/BrokerApiCore/BrokerServerLogic.cs +++ b/BrokerApiCore/BrokerServerLogic.cs @@ -1,142 +1,149 @@ -using NLog.Config; - +using Microsoft.Extensions.Configuration; using ServerBase; using ServerCommon; +using ServerCommon.BusinessLogDomain; + using ServerCore; + using MODULE_ID = System.UInt32; +using ServerMetricsHelper = ServerBase.ServerMetricsHelper; namespace BrokerApiCore; -public sealed class BrokerServerLogic : IServerLogic, IWithLogActor, IInitializer +public class BrokerServerLogic : ServerLogicBase, IWithPacketNamespaceVerifier, IWithConfiguration, + IWithServerMetrics, IWithLogActor { - private ServerProgramVersion m_server_program_version = new ServerProgramVersion(); - private readonly DynamoDbClient m_dynamo_db_client = new(); - private readonly ServerConfigMetaverseBroker m_server_config; - private readonly ServerType m_server_type = ServerType.BrokerApi; - private readonly string m_server_name = ServerType.BrokerApi.ToString(); - private bool m_is_initialized = false; + private readonly string m_server_config_path = "./"; - public BrokerServerLogic(ServerConfigMetaverseBroker serverConfig) + public BrokerServerLogic(ServerConfig serverConfig) : base(serverConfig) { - m_server_config = serverConfig; } - public IModule getModule(MODULE_ID moduleId) - { - return null; - } + public Action OnServerStart { get; set; } - public Result registerEntityTicker(EntityTicker entityTicker) - { - return null; - } - - - public async Task onInit() - { - if (m_is_initialized) - { - return new Result(); - } - m_is_initialized = true; - - Log.initLog(ServerType.BrokerApi.ToString(), "Developer", onNLogConfigurationChanged); - onInitBusinessLog(); - var result = await onInitDynamoDb(); - return result; - } - - private async Task onInitDynamoDb() + public Result mergeConfiguration(IConfiguration configuration) { var result = new Result(); + var key_options = configuration; + + // var port = ushort.Parse(key_options["port"] ?? "0"); + var config_file = key_options["config"]; var server_config = getServerConfig(); - - (var error_code, var to_load_table_names) = ServerConfigHelper.getDynamoDbTableNamesWithServiceType(server_config.ServiceType); - if (error_code.isFail()) - { - var err_msg = $"Failed to DynamoDbClient.getDynamoDbTableNameWithServiceType() !!! : {server_config.toBasicString()} - {toBasicString()}"; - result.setFail(error_code, err_msg); - Log.getLogger().error(err_msg); - - return result; - } - - var dynamo_db_client = getDynamoDbClient(); - result = dynamo_db_client.connectToDb( to_load_table_names - , server_config.AWS.LocalDynamoDB, server_config.Dynamodb - , server_config.AWS.AccessKey, server_config.AWS.SecretKey, server_config.AWS.Region ); - if (result.isFail()) - { - Log.getLogger().error($"Failed to connectToDb !!! "); - return result; - } - - if (false == await dynamo_db_client.createDBIfNotExists(false)) - { - var err_msg = $"Failed to create DB Table !!! - {toBasicString()}"; - result.setFail(ServerErrorCode.DynamoDbTableCreateFailed, err_msg); - Log.getLogger().fatal(result.toBasicString()); - return result; - } - - Log.getLogger().info($"Success ServerLogicBase.onInitDatabase() - {toBasicString()}"); + server_config.setConfigFilePath($"{m_server_config_path + ServerConfig.ConfigDirectory + "/" + config_file}"); return result; } - private void onInitBusinessLog() + protected override string onGetDumpFilename() { - BusinessLogger.setup(new NLogAppender() - , new JsonText() - , LogTransToOutputType.TransToMultyLine); - - Log.getLogger().info($"Success ServerLogicBase.onInitBusinessLog() - {toBasicString()}"); + return getServerName() + "_" + getServerConfig().getAppParamPort(); } - private void onNLogConfigurationChanged(object? sender, LoggingConfigurationChangedEventArgs e) + public override async Task onInit() { var result = new Result(); var err_msg = string.Empty; - var server_config = getServerConfig(); - if (server_config.AWS.CloudWatchLog.Enable) + var businesslog_refresh_date = MetaHelper.GameConfigMeta.BusinessLogRefreshTime; + DailyTimeEventManager.Instance.tryAddTask("BrokerBusinessLogRefresh", businesslog_refresh_date, onBusinessLogRefresh); + + result = await base.onInit(); + if (result.isFail()) { - result = setupCloudwatchLog(server_config); - if (result.isFail()) - { - err_msg = $"Failed to setupCloudwatchLog() !!! in onNLogConfigurationChanged() : {result.toBasicString()} - {toBasicString()}"; - Log.getLogger().fatal(err_msg); - } - } - - return; - Result setupCloudwatchLog(ServerConfig serverConfig) - { - var result = new Result(); - var err_msg = string.Empty; - - if(false == CloudWatchLog.isOpened()) - { - return result; - } - - if (false == CloudWatchLog.setup( serverConfig.AWS.CloudWatchLog.CloudWatchLogGroup - , serverConfig.AWS.CloudWatchLog.CloudWatchLogNamePattern - , serverConfig.AWS.CloudWatchLog.CloudWatchLogLevel - , serverConfig.AWS.Region - , serverConfig.AWS.AccessKey - , serverConfig.AWS.SecretKey - , serverConfig.AWS.CloudWatchLog.CloudWatchLogLayout) ) - { - err_msg = $"Failed to CloudWatchLog.setup() !!! - {toBasicString()}"; - result.setFail(ServerErrorCode.NlogWithAwsCloudWatchSetupFailed, err_msg); - Log.getLogger().fatal(result.toBasicString()); - - return result; - } - + err_msg = $"Failed to onInit() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); return result; } + + return result; + } + + // 비즈니스 로그가 일일 아카이빙 되도록 강제로 매일 로그를 남긴다. + private async Task onBusinessLogRefresh() + { + await Task.CompletedTask; + + var log_invokers = new List(1); + var empty_business_refresh_with_log_actor = new EmptyBusinessWithLogActor(); + DailyRefreshBusinessLog log = new(); + log_invokers.Add(log); + BusinessLogger.collectLogs(new LogActionEx(LogActionType.TestBusinessLog), + empty_business_refresh_with_log_actor, log_invokers); + Log.getLogger().info("Broker EmptyBusinessLog write"); + } + + + protected override Result onCreateServerName() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_type_name = getServerType(); + var server_type = server_type_name.toServerType(); + if (false == server_type.isValidServerType()) + { + err_msg = $"ServerType invalid !!! : {server_type.ToString()}"; + result.setFail(ServerErrorCode.ServerTypeInvalid, err_msg); + return result; + } + + var server_config = getServerConfig(); + var listen_port = server_config.toClientListenPort(); + if (listen_port <= 0) + { + err_msg = + $"Client listen port invalid !!! : appParamPort:{listen_port}, configListenPort:{server_config.ClientListenPort} - {server_type.ToString()}"; + result.setFail(ServerErrorCode.ClientListenPortInvalid, err_msg); + return result; + } + + base.setServerName(server_type.toServerName(server_config.toClientListenIP(), listen_port, 0, 0)); + return result; + } + + protected override Result onLoadMetaDatas() + { + return ServerCommon.ServerLogicHelper.loadMetaDatas(this); + } + + protected override async Task onRegisterModuleAll() + { + var result = new Result(); + + var config = getServerConfig(); + var load_config_info = config.getLoadedConfig(); + NullReferenceCheckHelper.throwIfNull(load_config_info, + () => $"load_config_info is null !!! - {toBasicString()}"); + + { + result = await tryCreateAndRegisterModule(() => + { + var config_param = new DynamoDbClient.ConfigParam(); + result = config_param.tryReadFromJsonOrDefault(load_config_info); + if (result.isFail()) + { + return (null, result); + } + + var module_context = new ModuleContext((MODULE_ID)ModuleId.DynamoDbConnector + , 0, 0, config_param); + var created_module = new DynamoDbClient(module_context); + return (created_module, result); + }); + } + return result; + } + + public override async Task onCreateTickerAll() + { + return await Task.FromResult(new Result()); + } + + protected override async Task onStartServer() + { + var result = await base.onStartServer(); + OnServerStart?.Invoke(); + return result; } public ILogActor toLogActor() @@ -151,33 +158,43 @@ public sealed class BrokerServerLogic : IServerLogic, IWithLogActor, IInitialize return log_info; } - public string toBasicString() + // public string toBasicString() + // { + // return $"{m_server_name}({m_server_type})"; + // } + + // public string getServerType() + // { + // return m_server_type.ToString(); + // } + + // public string getServerName() + // { + // return m_server_name; + // } + public bool isValidPacketNamespace(string toCheckNamespace, IPacketCommand packetCommand) { - return $"{m_server_name}({m_server_type})"; + return true; } - public string getServerType() + public Task setupServerMetrics() { - return m_server_type.ToString(); + return Task.FromResult(new Result()); } - public string getServerName() + public bool isSetupCompleted() { - return m_server_name; + return ServerMetricsHelper.isSetupCompleted(this); } - public ServerConfig getServerConfig() + public ServerMetricsCacheRequest getServerMetricsCacheRequest() { - return m_server_config; + return ServerBase.ServerMetricsHelper.getServerMetricsCacheRequest(this); } - public RedisConnector getRedisConnector() + public Task syncServerInfoToCache() { - throw new NotImplementedException(); + return Task.FromResult(new Result()); } - public DynamoDbClient getDynamoDbClient() - { - return m_dynamo_db_client; - } } diff --git a/BrokerApiCore/Common/PlanetTokenValidator.cs b/BrokerApiCore/Common/PlanetTokenValidator.cs new file mode 100644 index 0000000..74c74ed --- /dev/null +++ b/BrokerApiCore/Common/PlanetTokenValidator.cs @@ -0,0 +1,30 @@ + +using Microsoft.IdentityModel.JsonWebTokens; +using System.Security.Claims; +using BrokerApiCore; + +namespace BrokerApiCore; +public class PlanetTokenValidator +{ + public (string, string) validate(ClaimsPrincipal? principal) + { + Guard.Against.isNull(principal, ServerErrorCode.InvalidPlanetJwt, () => "jwt parsing error"); + + var exp_claim = principal.FindFirst(JwtRegisteredClaimNames.Exp)?.Value; + Guard.Against.isNull(principal, ServerErrorCode.InvalidPlanetJwt, () => "no JwtRegisteredClaimNames.Exp value"); + + var exp_time = DateTimeOffset.FromUnixTimeSeconds(long.Parse(exp_claim ?? string.Empty)); + Guard.Against.isFalse(exp_time > DateTimeOffset.UtcNow, ServerErrorCode.ExpiredPlanetJwt, + () => "Jwt has expired"); + + var planet_id = principal.FindFirstValue(JwtRegisteredClaimNames.Sid); + Guard.Against.isNullOrEmptyOrWhiteSpace(planet_id, ServerErrorCode.InvalidPlanetJwt, + () => "jwt parsing error no sub"); + + var planet_server_type = principal.FindFirstValue(JwtRegisteredClaimNames.Typ); + Guard.Against.isNullOrEmptyOrWhiteSpace(planet_server_type, ServerErrorCode.InvalidPlanetJwt, + () => "jwt parsing error no typ"); + + return (planet_id, planet_server_type); + } +} diff --git a/BrokerApiCore/Entity/Actions/BrokerMailSendAction.cs b/BrokerApiCore/Entity/Actions/BrokerMailSendAction.cs index 59b5086..7d01dfc 100644 --- a/BrokerApiCore/Entity/Actions/BrokerMailSendAction.cs +++ b/BrokerApiCore/Entity/Actions/BrokerMailSendAction.cs @@ -1,6 +1,10 @@ using System.Text; + using ServerBase; -using ServerCommon; using ServerCore; + +using ServerCommon; + +using ServerCore; namespace BrokerApiCore; @@ -35,7 +39,6 @@ public class BrokerMailSendAction : EntityActionBase private void setMailAttribute(MailSendOption option) { DateTime now = DateTimeHelper.Current; - var expire_date = now.AddSeconds(MetaHelper.GameConfigMeta.SystemMailStoragePeriod); string mail_guid = makeMailGuid(now); var mail_attribute = getOwner().getEntityAttributeNotNull(); @@ -47,7 +50,7 @@ public class BrokerMailSendAction : EntityActionBase mail_attribute.Title = option.Title; mail_attribute.Text = option.Text; mail_attribute.CreateTime = now; - mail_attribute.ExpireTime = expire_date; + mail_attribute.ExpireTime = option.ExpireDate; mail_attribute.IsSystemMail = option.IsSystemMail; mail_attribute.IsRead = false; mail_attribute.IsGetItem = false; @@ -137,18 +140,46 @@ public class BrokerMailSendAction : EntityActionBase logData.ItemList = mailAttrib.ItemList; } + // public MailSendOption createSystemSendMailOptionByMeta( + // ProductMetaData productMeta, + // SystemMailMetaData mailMeta, + // string receiverUserGuid, + // string receiverNickName, int amount) + // { + // DateTime now = DateTimeHelper.Current; + // var expire_date = now.AddSeconds(MetaHelper.GameConfigMeta.SystemMailStoragePeriod); + // // if (productMeta.Storage_Period_First != 0) + // // { + // // expire_date = DateTime.UtcNow.AddMinutes(productMeta.Storage_Period_First); + // // } + // var mail_send_option = new MailSendOption + // { + // ReceiverUserGuid = receiverUserGuid, + // ReceiverNickName = receiverNickName, + // IsSystemMail = true, + // IsTextByMetaData = true, + // Title = mailMeta.Mail_Title, + // Text = mailMeta.Mail_Desc, + // ExpireDate = expire_date, + // ItemList = getMailItems(productMeta, amount).ToList(), + // PackageOrderId = string.Empty, + // }; + // return mail_send_option; + // } + public MailSendOption createSystemSendMailOptionByMeta( ProductMetaData productMeta, SystemMailMetaData mailMeta, + IEnumerable mailItems, string receiverUserGuid, - string receiverNickName, int amount) + string receiverNickName) { DateTime now = DateTimeHelper.Current; var expire_date = now.AddSeconds(MetaHelper.GameConfigMeta.SystemMailStoragePeriod); - // if (productMeta.Storage_Period_First != 0) - // { - // expire_date = DateTime.UtcNow.AddMinutes(productMeta.Storage_Period_First); - // } + if (productMeta.Storage_Period_First != 0) + { + expire_date = DateTime.UtcNow.AddMinutes(productMeta.Storage_Period_First); + } var mail_send_option = new MailSendOption { ReceiverUserGuid = receiverUserGuid, @@ -158,7 +189,7 @@ public class BrokerMailSendAction : EntityActionBase Title = mailMeta.Mail_Title, Text = mailMeta.Mail_Desc, ExpireDate = expire_date, - ItemList = getMailItems(productMeta, amount).ToList(), + ItemList = mailItems.ToList(), PackageOrderId = string.Empty, }; return mail_send_option; @@ -170,12 +201,15 @@ public class BrokerMailSendAction : EntityActionBase { IEnumerable total_items = Enumerable.Range(0, amount).Select(i => { - return productMetaData.First_List.Select(itemMeta => new ServerCommon.MailItem() + return productMetaData.First_List.Select(itemMeta => { - ItemId = (UInt32)itemMeta.Id, - Count = itemMeta.Value, - ProductId = (UInt32)productMetaData.Id, - isRepeatProduct = false + return new ServerCommon.MailItem() + { + ItemId = (UInt32)itemMeta.Id, + Count = itemMeta.Value, + ProductId = (UInt32)productMetaData.Id, + isRepeatProduct = false + }; }); }).SelectMany(x => x); return total_items; diff --git a/BrokerApiCore/Entity/Helpers/MailHelper.cs b/BrokerApiCore/Entity/Helpers/MailHelper.cs new file mode 100644 index 0000000..4b9ceee --- /dev/null +++ b/BrokerApiCore/Entity/Helpers/MailHelper.cs @@ -0,0 +1,38 @@ +using MetaAssets; + +namespace BrokerApiCore; + +public static class MailHelper +{ + public static IEnumerable getMailItems(BrokerMetaTableRef metaTableRefRef, ProductMetaData productMeta, int amount) + { + var product_meta_id = productMeta.Id; + metaTableRefRef.MetaTable.ProductMetaTable.ProductMetaDataListbyId.TryGetValue(product_meta_id, + out var product_meta); + Guard.Against.isNull(product_meta, ServerErrorCode.MetaIdInvalid, ()=>"product meta not found"); + var mail_items = product_meta.First_List.SelectMany(x => + { + metaTableRefRef.MetaTable.ItemMetaTable.ItemMetaDataListbyId.TryGetValue(x.Id, out var item_meta_data); + Guard.Against.isNull(item_meta_data, ServerErrorCode.MetaIdInvalid, () => "item meta not found"); + var apply_amount = 0; + var items = new List(); + while (apply_amount < amount) + { + var current_amount = apply_amount + item_meta_data.StackMaxCount < amount + ? item_meta_data.StackMaxCount + : amount - apply_amount; + var mail_item = new ServerCommon.MailItem + { + ProductId = Convert.ToUInt32(product_meta_id), + ItemId = Convert.ToUInt32(item_meta_data.ItemId), + Count = x.Value * current_amount + }; + apply_amount += Convert.ToInt32(mail_item.Count); + items.Add(mail_item); + } + return items; + }); + return mail_items; + } +} + diff --git a/BrokerApiCore/Repository/SqlContext/MetaverseBrokerDbContext.cs b/BrokerApiCore/Repository/SqlContext/MetaverseBrokerDbContext.cs index f3d360c..b2895fb 100644 --- a/BrokerApiCore/Repository/SqlContext/MetaverseBrokerDbContext.cs +++ b/BrokerApiCore/Repository/SqlContext/MetaverseBrokerDbContext.cs @@ -16,7 +16,7 @@ public class MetaverseBrokerDbContext: DbContext public MetaverseBrokerDbContext(DbContextOptions options) : base(options) { - // base.Database.EnsureCreated(); + base.Database.EnsureCreated(); } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/BrokerApiCore/Services/JwtGenerator.cs b/BrokerApiCore/Services/JwtGenerator.cs index 56ce60a..10bf753 100644 --- a/BrokerApiCore/Services/JwtGenerator.cs +++ b/BrokerApiCore/Services/JwtGenerator.cs @@ -20,10 +20,9 @@ public class JwtGenerator public JwtOption JwtOption => m_jwt_option; // Access Token 생성 - public string generateAccessToken(string planetId, string planetServerType, string? refreshToken = null) + public string generateAccessToken(string planetId, string planetServerType, DateTime dateTime ) { - // todo: 토큰 유효기간 설정 - var issued_at = new DateTime(2025, 3, 1, 0, 0, 0, DateTimeKind.Utc); + var issued_at = dateTime.ToUniversalTime(); // new DateTime(2025, 3, 1, 0, 0, 0, DateTimeKind.Utc); var expires = issued_at.AddYears(1); var security_key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(m_jwt_option.Secret)); var credentials = new SigningCredentials(security_key, SecurityAlgorithms.HmacSha256); diff --git a/BrokerApiCore/Services/PlanetService.cs b/BrokerApiCore/Services/PlanetService.cs index 43be094..a683eb2 100644 --- a/BrokerApiCore/Services/PlanetService.cs +++ b/BrokerApiCore/Services/PlanetService.cs @@ -1,12 +1,10 @@ using System.Security.Claims; using Microsoft.IdentityModel.JsonWebTokens; - using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; namespace BrokerApiCore; - public class PlanetService { private readonly PlanetInfoRepo m_planet_info_repo; @@ -29,7 +27,7 @@ public class PlanetService Guard.Against.isFalse(planet_info.SecretKey == planetSecretKey, ServerErrorCode.PlanetSecretKeyDoesNotMatched, () => "플래닛에 제공한 엑세스키와 맞지 않음"); - var access_token = m_jwt_generator.generateAccessToken(planetId, planet_info.ServerType); + var access_token = m_jwt_generator.generateAccessToken(planetId, planet_info.ServerType, planet_info.UpdatedAt); // 비즈니스 로그 m_log_actor = new PlanetProviderLogActor(planetId); @@ -47,24 +45,6 @@ public class PlanetService return ("new_earth", "caliverse"); } - var principal = m_jwt_parser.parseToken(jwt); - Guard.Against.isNull(principal, ServerErrorCode.InvalidPlanetJwt, () => "jwt parsing error"); - - var exp_claim = principal.FindFirst(JwtRegisteredClaimNames.Exp)?.Value; - Guard.Against.isNull(principal, ServerErrorCode.InvalidPlanetJwt, () => "no JwtRegisteredClaimNames.Exp value"); - - var exp_time = DateTimeOffset.FromUnixTimeSeconds(long.Parse(exp_claim ?? string.Empty)); - Guard.Against.isFalse(exp_time > DateTimeOffset.UtcNow, ServerErrorCode.ExpiredPlanetJwt, - () => "Jwt has expired"); - - var planet_id = principal.FindFirstValue(JwtRegisteredClaimNames.Sid); - Guard.Against.isNullOrEmptyOrWhiteSpace(planet_id, ServerErrorCode.InvalidPlanetJwt, - () => "jwt parsing error no sub"); - - var planet_server_type = principal.FindFirstValue(JwtRegisteredClaimNames.Typ); - Guard.Against.isNullOrEmptyOrWhiteSpace(planet_server_type, ServerErrorCode.InvalidPlanetJwt, - () => "jwt parsing error no typ"); - - return (planet_id, planet_server_type); + return new PlanetTokenValidator().validate(m_jwt_parser.parseToken(jwt)); } } diff --git a/BrokerApiCore/Services/PlanetitemExchange/Strategies/ProductOrderCompletionStrategy.cs b/BrokerApiCore/Services/PlanetitemExchange/Strategies/ProductOrderCompletionStrategy.cs index 90cb8c0..136da93 100644 --- a/BrokerApiCore/Services/PlanetitemExchange/Strategies/ProductOrderCompletionStrategy.cs +++ b/BrokerApiCore/Services/PlanetitemExchange/Strategies/ProductOrderCompletionStrategy.cs @@ -44,9 +44,10 @@ public class ProductOrderCompletionStrategy : IOrderCompletionStrategy Guard.Against.resultFail(init_result, init_result.ErrorCode, ()=>"BrokerMail onInit error"); var mail_send_action = broker_mail.getEntityActionNotNull(); - var mail_option = mail_send_action.createSystemSendMailOptionByMeta(product_meta, mail_meta, + var mail_option = mail_send_action.createSystemSendMailOptionByMeta( + product_meta, mail_meta, MailHelper.getMailItems(m_meta_table_ref, product_meta, orderCreated.ExchangeMetaAmount), m_planet_user_entity.UserGuid, - m_planet_user_entity.Nickname, orderCreated.ExchangeMetaAmount); + m_planet_user_entity.Nickname); //============================================================================================== // rdb 트랜잭션 시작 @@ -133,3 +134,4 @@ public class ProductOrderCompletionStrategy : IOrderCompletionStrategy return mail_data; } } + diff --git a/BrokerApiCore/Services/UserAuthService.cs b/BrokerApiCore/Services/UserAuthService.cs index 1cd7b28..c457d15 100644 --- a/BrokerApiCore/Services/UserAuthService.cs +++ b/BrokerApiCore/Services/UserAuthService.cs @@ -28,9 +28,14 @@ public class UserAuthService public PlanetUserEntity PlanetUserEntity => m_planet_user_entity; - private ServerConfigMetaverseBroker? getServerConfig() + // private ServerConfigMetaverseBroker? getServerConfig() + // { + // return m_server_logic.getServerConfig() as ServerConfigMetaverseBroker; + // } + + private ServerConfig? getServerConfig() { - return m_server_logic.getServerConfig() as ServerConfigMetaverseBroker; + return m_server_logic.getServerConfig(); } public UserAuthService(SsoAccountRepo ssoAccountRepo, IServerLogic serverLogic) @@ -171,7 +176,7 @@ public class UserAuthService { accessToken = Convert.ToUInt64(id); } - catch (Exception e) + catch { accessToken = 0; } diff --git a/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.dgspec.json b/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.dgspec.json index ec6a865..2ec57f3 100644 --- a/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.dgspec.json +++ b/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.dgspec.json @@ -55,18 +55,28 @@ "version": "[8.0.2, )", "versionCentrallyManaged": true }, + "Microsoft.EntityFrameworkCore": { + "target": "Package", + "version": "[8.0.13, )", + "versionCentrallyManaged": true + }, "Microsoft.EntityFrameworkCore.Design": { "include": "Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive", "suppressParent": "All", "target": "Package", - "version": "[8.0.2, )", + "version": "[8.0.13, )", + "versionCentrallyManaged": true + }, + "Microsoft.EntityFrameworkCore.Relational": { + "target": "Package", + "version": "[8.0.13, )", "versionCentrallyManaged": true }, "Microsoft.EntityFrameworkCore.Tools": { "include": "Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive", "suppressParent": "All", "target": "Package", - "version": "[8.0.2, )", + "version": "[8.0.13, )", "versionCentrallyManaged": true }, "NLog.Web.AspNetCore": { @@ -76,7 +86,7 @@ }, "Pomelo.EntityFrameworkCore.MySql": { "target": "Package", - "version": "[8.0.2, )", + "version": "[8.0.3, )", "versionCentrallyManaged": true }, "Swashbuckle.AspNetCore.Annotations": { @@ -88,17 +98,10 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "8.2.2", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", + "AutoMapper": "15.0.1", "AWS.Logger.NLog": "3.3.4", "AWSSDK.Core": "3.7.402.46", "AWSSDK.DynamoDBv2": "3.7.407", @@ -114,6 +117,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -121,6 +125,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -136,10 +141,12 @@ "Microsoft.AspNetCore.OpenApi": "8.0.6", "Microsoft.Data.Sqlite": "8.0.6", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.EntityFrameworkCore.Design": "8.0.2", - "Microsoft.EntityFrameworkCore.Tools": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", + "Microsoft.EntityFrameworkCore.Design": "8.0.13", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.EntityFrameworkCore.Tools": "8.0.13", "Microsoft.Extensions.Caching.StackExchangeRedis": "8.0.6", - "Microsoft.Extensions.Configuration": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", @@ -148,8 +155,8 @@ "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", "Microsoft.Extensions.Http": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0", "Microsoft.NET.Test.Sdk": "17.12.0", "Microsoft.OpenApi": "1.6.22", @@ -159,18 +166,27 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", - "Pomelo.EntityFrameworkCore.MySql": "8.0.2", + "Pomelo.EntityFrameworkCore.MySql": "8.0.3", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -183,8 +199,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "8.0.0", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -270,17 +289,10 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "8.2.2", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", + "AutoMapper": "15.0.1", "AWS.Logger.NLog": "3.3.4", "AWSSDK.Core": "3.7.402.46", "AWSSDK.DynamoDBv2": "3.7.407", @@ -296,6 +308,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -303,6 +316,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -318,10 +332,12 @@ "Microsoft.AspNetCore.OpenApi": "8.0.6", "Microsoft.Data.Sqlite": "8.0.6", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.EntityFrameworkCore.Design": "8.0.2", - "Microsoft.EntityFrameworkCore.Tools": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", + "Microsoft.EntityFrameworkCore.Design": "8.0.13", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.EntityFrameworkCore.Tools": "8.0.13", "Microsoft.Extensions.Caching.StackExchangeRedis": "8.0.6", - "Microsoft.Extensions.Configuration": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", @@ -330,8 +346,8 @@ "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", "Microsoft.Extensions.Http": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0", "Microsoft.NET.Test.Sdk": "17.12.0", "Microsoft.OpenApi": "1.6.22", @@ -341,18 +357,27 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", - "Pomelo.EntityFrameworkCore.MySql": "8.0.2", + "Pomelo.EntityFrameworkCore.MySql": "8.0.3", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -365,8 +390,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "8.0.0", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -445,17 +473,10 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "8.2.2", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", + "AutoMapper": "15.0.1", "AWS.Logger.NLog": "3.3.4", "AWSSDK.Core": "3.7.402.46", "AWSSDK.DynamoDBv2": "3.7.407", @@ -471,6 +492,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -478,6 +500,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -493,10 +516,12 @@ "Microsoft.AspNetCore.OpenApi": "8.0.6", "Microsoft.Data.Sqlite": "8.0.6", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.EntityFrameworkCore.Design": "8.0.2", - "Microsoft.EntityFrameworkCore.Tools": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", + "Microsoft.EntityFrameworkCore.Design": "8.0.13", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.EntityFrameworkCore.Tools": "8.0.13", "Microsoft.Extensions.Caching.StackExchangeRedis": "8.0.6", - "Microsoft.Extensions.Configuration": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", @@ -505,8 +530,8 @@ "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", "Microsoft.Extensions.Http": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0", "Microsoft.NET.Test.Sdk": "17.12.0", "Microsoft.OpenApi": "1.6.22", @@ -516,18 +541,27 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", - "Pomelo.EntityFrameworkCore.MySql": "8.0.2", + "Pomelo.EntityFrameworkCore.MySql": "8.0.3", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -540,8 +574,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "8.0.0", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -623,20 +660,20 @@ "frameworks": { "net8.0": { "targetAlias": "net8.0", + "dependencies": { + "Ulid": { + "target": "Package", + "version": "[1.3.4, )", + "versionCentrallyManaged": true + } + }, "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "8.2.2", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", + "AutoMapper": "15.0.1", "AWS.Logger.NLog": "3.3.4", "AWSSDK.Core": "3.7.402.46", "AWSSDK.DynamoDBv2": "3.7.407", @@ -652,6 +689,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -659,6 +697,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -674,10 +713,12 @@ "Microsoft.AspNetCore.OpenApi": "8.0.6", "Microsoft.Data.Sqlite": "8.0.6", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.EntityFrameworkCore.Design": "8.0.2", - "Microsoft.EntityFrameworkCore.Tools": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", + "Microsoft.EntityFrameworkCore.Design": "8.0.13", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.EntityFrameworkCore.Tools": "8.0.13", "Microsoft.Extensions.Caching.StackExchangeRedis": "8.0.6", - "Microsoft.Extensions.Configuration": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", @@ -686,8 +727,8 @@ "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", "Microsoft.Extensions.Http": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0", "Microsoft.NET.Test.Sdk": "17.12.0", "Microsoft.OpenApi": "1.6.22", @@ -697,18 +738,27 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", - "Pomelo.EntityFrameworkCore.MySql": "8.0.2", + "Pomelo.EntityFrameworkCore.MySql": "8.0.3", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -721,8 +771,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "8.0.0", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -824,51 +877,6 @@ "version": "[1.3.2, )", "versionCentrallyManaged": true }, - "AutoMapper": { - "target": "Package", - "version": "[14.0.0, )", - "versionCentrallyManaged": true - }, - "AutoMapper.AspNetCore.OData.EFCore": { - "target": "Package", - "version": "[7.0.1, )", - "versionCentrallyManaged": true - }, - "AutoMapper.Collection": { - "target": "Package", - "version": "[11.0.0, )", - "versionCentrallyManaged": true - }, - "AutoMapper.Collection.EntityFrameworkCore": { - "target": "Package", - "version": "[11.0.0, )", - "versionCentrallyManaged": true - }, - "AutoMapper.Contrib.Autofac.DependencyInjection": { - "target": "Package", - "version": "[9.0.0, )", - "versionCentrallyManaged": true - }, - "AutoMapper.Data": { - "target": "Package", - "version": "[9.0.0, )", - "versionCentrallyManaged": true - }, - "AutoMapper.EF6": { - "target": "Package", - "version": "[3.0.1, )", - "versionCentrallyManaged": true - }, - "AutoMapper.Extensions.EnumMapping": { - "target": "Package", - "version": "[4.1.0, )", - "versionCentrallyManaged": true - }, - "AutoMapper.Extensions.ExpressionMapping": { - "target": "Package", - "version": "[8.0.0, )", - "versionCentrallyManaged": true - }, "Axion.ConcurrentHashSet": { "target": "Package", "version": "[1.0.0, )", @@ -916,12 +924,12 @@ }, "Microsoft.Extensions.Configuration": { "target": "Package", - "version": "[9.0.4, )", + "version": "[8.0.0, )", "versionCentrallyManaged": true }, "Microsoft.Extensions.Options": { "target": "Package", - "version": "[9.0.4, )", + "version": "[8.0.2, )", "versionCentrallyManaged": true }, "MongoDB.Analyzer": { @@ -1013,17 +1021,10 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "8.2.2", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", + "AutoMapper": "15.0.1", "AWS.Logger.NLog": "3.3.4", "AWSSDK.Core": "3.7.402.46", "AWSSDK.DynamoDBv2": "3.7.407", @@ -1039,6 +1040,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -1046,6 +1048,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -1061,10 +1064,12 @@ "Microsoft.AspNetCore.OpenApi": "8.0.6", "Microsoft.Data.Sqlite": "8.0.6", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.EntityFrameworkCore.Design": "8.0.2", - "Microsoft.EntityFrameworkCore.Tools": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", + "Microsoft.EntityFrameworkCore.Design": "8.0.13", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.EntityFrameworkCore.Tools": "8.0.13", "Microsoft.Extensions.Caching.StackExchangeRedis": "8.0.6", - "Microsoft.Extensions.Configuration": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", @@ -1073,8 +1078,8 @@ "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", "Microsoft.Extensions.Http": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0", "Microsoft.NET.Test.Sdk": "17.12.0", "Microsoft.OpenApi": "1.6.22", @@ -1084,18 +1089,27 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", - "Pomelo.EntityFrameworkCore.MySql": "8.0.2", + "Pomelo.EntityFrameworkCore.MySql": "8.0.3", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -1108,8 +1122,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "8.0.0", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -1188,17 +1205,10 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "8.2.2", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", + "AutoMapper": "15.0.1", "AWS.Logger.NLog": "3.3.4", "AWSSDK.Core": "3.7.402.46", "AWSSDK.DynamoDBv2": "3.7.407", @@ -1214,6 +1224,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -1221,6 +1232,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -1236,10 +1248,12 @@ "Microsoft.AspNetCore.OpenApi": "8.0.6", "Microsoft.Data.Sqlite": "8.0.6", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.EntityFrameworkCore.Design": "8.0.2", - "Microsoft.EntityFrameworkCore.Tools": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", + "Microsoft.EntityFrameworkCore.Design": "8.0.13", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.EntityFrameworkCore.Tools": "8.0.13", "Microsoft.Extensions.Caching.StackExchangeRedis": "8.0.6", - "Microsoft.Extensions.Configuration": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", @@ -1248,8 +1262,8 @@ "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", "Microsoft.Extensions.Http": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0", "Microsoft.NET.Test.Sdk": "17.12.0", "Microsoft.OpenApi": "1.6.22", @@ -1259,18 +1273,27 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", - "Pomelo.EntityFrameworkCore.MySql": "8.0.2", + "Pomelo.EntityFrameworkCore.MySql": "8.0.3", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -1283,8 +1306,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "8.0.0", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -1377,17 +1403,10 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "8.2.2", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", + "AutoMapper": "15.0.1", "AWS.Logger.NLog": "3.3.4", "AWSSDK.Core": "3.7.402.46", "AWSSDK.DynamoDBv2": "3.7.407", @@ -1403,6 +1422,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -1410,6 +1430,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -1425,10 +1446,12 @@ "Microsoft.AspNetCore.OpenApi": "8.0.6", "Microsoft.Data.Sqlite": "8.0.6", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.EntityFrameworkCore.Design": "8.0.2", - "Microsoft.EntityFrameworkCore.Tools": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", + "Microsoft.EntityFrameworkCore.Design": "8.0.13", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.EntityFrameworkCore.Tools": "8.0.13", "Microsoft.Extensions.Caching.StackExchangeRedis": "8.0.6", - "Microsoft.Extensions.Configuration": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", @@ -1437,8 +1460,8 @@ "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", "Microsoft.Extensions.Http": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0", "Microsoft.NET.Test.Sdk": "17.12.0", "Microsoft.OpenApi": "1.6.22", @@ -1448,18 +1471,27 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", - "Pomelo.EntityFrameworkCore.MySql": "8.0.2", + "Pomelo.EntityFrameworkCore.MySql": "8.0.3", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -1472,8 +1504,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "8.0.0", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -1556,17 +1591,10 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "8.2.2", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", + "AutoMapper": "15.0.1", "AWS.Logger.NLog": "3.3.4", "AWSSDK.Core": "3.7.402.46", "AWSSDK.DynamoDBv2": "3.7.407", @@ -1582,6 +1610,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -1589,6 +1618,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -1604,10 +1634,12 @@ "Microsoft.AspNetCore.OpenApi": "8.0.6", "Microsoft.Data.Sqlite": "8.0.6", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.EntityFrameworkCore.Design": "8.0.2", - "Microsoft.EntityFrameworkCore.Tools": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", + "Microsoft.EntityFrameworkCore.Design": "8.0.13", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.EntityFrameworkCore.Tools": "8.0.13", "Microsoft.Extensions.Caching.StackExchangeRedis": "8.0.6", - "Microsoft.Extensions.Configuration": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", @@ -1616,8 +1648,8 @@ "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", "Microsoft.Extensions.Http": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0", "Microsoft.NET.Test.Sdk": "17.12.0", "Microsoft.OpenApi": "1.6.22", @@ -1627,18 +1659,27 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", - "Pomelo.EntityFrameworkCore.MySql": "8.0.2", + "Pomelo.EntityFrameworkCore.MySql": "8.0.3", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -1651,8 +1692,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "8.0.0", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -1748,17 +1792,10 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "8.2.2", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", + "AutoMapper": "15.0.1", "AWS.Logger.NLog": "3.3.4", "AWSSDK.Core": "3.7.402.46", "AWSSDK.DynamoDBv2": "3.7.407", @@ -1774,6 +1811,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -1781,6 +1819,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -1796,10 +1835,12 @@ "Microsoft.AspNetCore.OpenApi": "8.0.6", "Microsoft.Data.Sqlite": "8.0.6", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.EntityFrameworkCore.Design": "8.0.2", - "Microsoft.EntityFrameworkCore.Tools": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", + "Microsoft.EntityFrameworkCore.Design": "8.0.13", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.EntityFrameworkCore.Tools": "8.0.13", "Microsoft.Extensions.Caching.StackExchangeRedis": "8.0.6", - "Microsoft.Extensions.Configuration": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", @@ -1808,8 +1849,8 @@ "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", "Microsoft.Extensions.Http": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0", "Microsoft.NET.Test.Sdk": "17.12.0", "Microsoft.OpenApi": "1.6.22", @@ -1819,18 +1860,27 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", - "Pomelo.EntityFrameworkCore.MySql": "8.0.2", + "Pomelo.EntityFrameworkCore.MySql": "8.0.3", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -1843,8 +1893,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "8.0.0", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, diff --git a/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.g.props b/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.g.props index 5f34edd..f93526f 100644 --- a/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.g.props +++ b/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.g.props @@ -14,13 +14,11 @@ - - - + + C:\Users\user\.nuget\packages\mongodb.analyzer\1.5.0 - C:\Users\user\.nuget\packages\entityframework\6.5.1 C:\Users\user\.nuget\packages\awssdk.core\3.7.402.46 C:\Users\user\.nuget\packages\awssdk.securitytoken\3.7.401.89 C:\Users\user\.nuget\packages\awssdk.s3\3.7.416.15 @@ -29,6 +27,6 @@ C:\Users\user\.nuget\packages\awssdk.cloudwatchlogs\3.7.305.15 C:\Users\user\.nuget\packages\awssdk.ec2\3.7.330.4 C:\Users\user\.nuget\packages\microsoft.codeanalysis.analyzers\3.3.3 - C:\Users\user\.nuget\packages\microsoft.entityframeworkcore.tools\8.0.2 + C:\Users\user\.nuget\packages\microsoft.entityframeworkcore.tools\8.0.13 \ No newline at end of file diff --git a/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.g.targets b/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.g.targets index 81dbdb8..6988ff0 100644 --- a/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.g.targets +++ b/BrokerApiCore/obj/BrokerApiCore.csproj.nuget.g.targets @@ -3,9 +3,8 @@ - - - + + \ No newline at end of file diff --git a/BrokerApiCore/obj/Debug/BrokerApiCore.assets.cache b/BrokerApiCore/obj/Debug/BrokerApiCore.assets.cache index 34c90ebca3ceaf30ce23dea9451ac0fad9b754fd..0240c3268d00b0df2786420d7d36334ead788b5e 100644 GIT binary patch literal 91915 zcmd5_34k0&bv3$WTaqQ&mLcb^y=8@byXrc5U&(>=)K z4cANlZlVs#fmff4c32_uSnWY6gVYXvOg3RsNm#C$@?mb`PeelZkzaInuC$`X3-uwB|EEbUTUpOi~l zH`{DMCC6vw;#hC^wZJbli_`cfPM3>5zv&@wXMdL5N04t!O3Gu7%~eR(gXe zyHX^8LM{NoU|0+N+a2Hi*q~MH^I7Z*a=&~x*K9c@$Ju;x`o)=Mxi#0SpYm(`;`_aw zyRu&V80R@gd92b?zf3*d3Z&`z$C6zUlKC^TnODojW<97+w!9*GOVz_upB;DggC728 ztdB!c)`*ab*RD==XMO=5QaeX*H!ZR&&Y~^|_z?-d2mZxT#g%*B#6XjY7 zqd#HnbG+BRaT&vjGWMu4ys`RBv*_>lYd)7PhWa&(dM&Q6>jpKnkEwEP$}3C+t!AM} z*aL;yP_byjmATpwMk=Tzak~`wp_Zu97;HV+JTGRjm*D!!Zot+XF}{KJT6N<)5*CnI zrI#{F(&g@Rd_(ox6!ey;O^)|_+bpkJwYgTgnD|~VV^-^NeXgrQ^7Asjnh_sEY00k@ z=ML6R`szh{yTwbF{fNZn&?u@Mz#`I-J<;c9<2XKVm44WtZJ|9=zk64Jk5YU%Qp7Xa z41>9#b!c4~~lj5EA6>s2nKwmNaJBj3IdWB%FRt#DNAS)E7 z%QYWuZ_pbUuaqaoF=^O=X1JS3#)#xlNJ8?{oPhpajzlBpMx-l{u0*;&yB;ue@u<7- zo9OGBq`}dc#S8(ZRw^`01H|~mm`6PU2vvV8*j@!T#Fj=VV!H_`KieAEE4D)>wuiyf z6TAi6&0s@puSO!a*B}+0(q_-oSa9_4mg$+`FK1-L|5~IY`S^!5y~7*o8}Np>rghydOZ^H+Jf|iT)YhPuy!HE^h+?6Qy*e_1JXUYm?q~U?fhuwBN)l42Qj)4 zDc>A7a|+VVw|(XzP)J>$P~3!ccP??0+IKr!sQQAB)U}Dv%}9lOd~74 z1Y;}G(QFtK#pYb2<)I_IwT2#ed={-Bhe2?dx@aMFABrb-{Yb=a0BO>} zu0DftwO9yR72i{=>r>4_V|s3U0183BmRm7aq{AVp0>Pvb!XQ`@gdrq?uno!a2^Z_t zDth1wMuJA6NefQ{-maLntn%TI^d68(rrW`UnC?I#raO`LWy@Wv1;t7kuNm(bb671W z=|Cg3U|2npEOwztVsRT1vA7+{(ffyMU|w~m(kfRA4M;T*Gm=t`)LVF)-XS79RA@-H zcYqDCy%UMpJ{IYOL$aA_1MPx!dvF!amj=H=qd_Cx2*~zEWRmCOz=L=`9*KDFMjFfJ zIaRMuq5U+P^;UgiW)cJ4z>Zi}s?_}<#VAuqetS?p@f$`Wej`YZo?@!mD0+pUfvu8imV!A7Z>2CaXv`2+Wy`@5< zoZ=sU1^wgdRjHS>yHS=r?@94Igx{|ANUt4@gWpMtD`_B#8+~={pZehxza#jar#;5} z9Bm+GYk2FUDem`nA}zW{DEXGAr`ReAS-VG5{KoM60ms))wQFrt@jK;OHn*f>DZa zTwWN7W-Ejsp0tXdW_L3CU9+t{D!at`M8-<e5v={4bVql2h*8tN zifT>3u<(}{`o$ZLvkr4sTQyEGMq19Gw>^#YQ0|r&^U)Cb1I0%Wt3$~zLg-)`T>JN0 zW>~bkLa-gPW`%AD(ESGmynY>Ca>QpX@(~E8(z4q+ubh2FW#hieeW2d#c4@HQ!Dlk8o zs@EodJC}sx?mBL1n0t$D2(ez-S*_~zQr$HMDSR#b?rIG`iH}Sq0>9YwTj8!lrxi+u z7T7AJ{IZvvNufQB-}yJh;+O1|qE+*(R?^YN6yHJ60cB4}B5taEg2BbUDr{i~ibydD@p;;Bg z=13RtY?b$n6xL_rcUK)gbw^k$I9*jBcyX7NmE5y-4Z?bZT2z+Yk0u~5L3(pHA0Q)1 z8Y;w{U(QCQ5hgTRF^~x4F9lLsd(++W%kX!zi*cgSELY2|@@ZI4=2N;xqYSdu{d5Y> z%klf^dEua!Ra0EpDf*Rkd&AO!SsysaxelpZwx;$92KGu^=?%XM>6zUDBYkhypHP-o zR9iWbk+yfjpHRkD2}SDFDQG`~-??95vd-dOBPS^Rg^CvSniQO$#qZPI)s$LhFV-u; zI2PICjq_wTmVAF|-JPhST2DK&0WymD70jf~q66 zBK{WU{Z?G*#r+&o?$L0zTpJv~^6gX^W>a(H*qlKZfYn^Gy)Yv

iDM9P*7k493(= zz72_h{5+Cl?_68mOprMW*bYe0P82Sg{bCMgbHRfZZu528z{Iv~qFz0B{|a4luOwOYAV59RXD&~Q(tOK78_o0DzyFi|T}_nv{lj-r+V zzmEaDp8`-un(nx{RVzJHX#zg>h(rWAV&aSjEKLPV*Fe9{nQRxN2?H=%3BA= zh*mQHaEkeFgEv;HWq^u_-UlEVUARXB!k-o=F^ z{)01QH;4b}Zl>+K7#8#+L@z;nj6r-HR|4_7NO|6RsT?%wL1BWLpT(e|fBZ0gmLoBx zWc&#*qR#bBBx3w~NF$DlhyH~oCc4ywtWdTjgHNJBV(=*>V(|M&4ud%|&hz5wgfbPR zS>iSfPyJB^lYXTP^~99Hpy+)X{D}W&kcj^uAUQTv6@Tcy!lc2*8>+6fXAqy$XTeAE z{X_iTlU=cOOtLZRqaBI5B{7fS0k-F4wf+$o|HrtJSf4|3=p2o5!zZhrzzZ-C(K_c$ ztv)$fE|#%)LXBsqZo4#MjYiaCOnp3}mOwtwK)%30{sdRY5?-TT^(%GHv>8{Wfm_YZ z67%lSFo$Vx@DL5<63`bJ&?OA$OSn2rgPH}b&a|ozYWsxk-cIv}d{hx0y4PT)@MUKE z6=wUVxH=SvfV>=FZq;b|L4dtlxNb$Qn_hwPf`E;cdM-u$Gv@W@%D` zidSO8UUA_klEGJ*fyWH~64$%38*dOytHwLfH-J)sUMx@?g|T?q8w;k%=0SWTUt>03 zXEuL@tK-{5SA({Drid|j%7@yF4iIg3r#BeY9DOijM738_9VIHs_Z!Ujo6PsGaebOY zymr2_i=s_;gE52&hv=@ z3tQ2Nnc^uJ%}JA!TCEPFcjd^k1N~!!PBk%+N7|Xhc_gTBGpO${sDHxMF;}*L!fmpL z$4ovSps|o%g8AnZn18`<$J$3Wm?=2pb%+OkJrdG)Q;_}@za7IFJ5xwCtX!?mv`85m z$K*ABT{M$rFAAxI9n{nk$oCk?_i?4x^KVEFivtEC_MklRw{p2^Uu|SOavLiCJM{37 zuztX>)-tSr$JMcV#;}s>Lg7M-N2XhCOL`0`xl+X|EwGo8aDK>e^lHn0;J0Ip)o|)w zq=%@t6pi!ZEgd}K#p;LXdVa)g{}WewIsb*^u-|63u(O4u(rJ6cILZW4${#a3-4^~E zzn|^ujze#q<_y+&t1#il=#9-BGNUG6Wm36Rh34_VLcBJ)ot86sCM>YxRe;JZqfBHZCb{OyS z`-vc7g&w*>qfqg%FjlJ1(u`89h|xF;MTVBC(x|Xh&pEL`MmZ%iiey0AxJLFE{C4b_ zF*4<>1^uOtg$G1Rz_h^nF)J6Xy%8zR4MgKzz!3B&yb!;SIg}2wznIXhxDsOLFu`yG zzYWcBQGS`HQOhK+M|xJxS4-QXLnWUu%{d|+W>P8hVwQOcuJpk!#r2MCnP)2H5|q?I z_=XBOkK?W^co`SGoC`i5*Mr#w&lRc_j)MU=d9lz*qxlo@knR3O4PL-pE@Un%aCJE6 zJQTD_O0V}C^=1o7J)&`FL8#j9$Mev;S@2oOd{!}^i*TiXmm@j4)vjIX8l?&PAn4y! zNc8tAr2OnUbDCSs0kBKT6Jl9;p{>X?~nzNFPtK$UElIl<#P z=5al)4nG^_(W$GPV77>vEymTc%+Jj7xX1|xOPIk@T+fkfoZ!BUxi81n@zt?-Ib7mI z`R8-_7vNgR?mKk(-MYdFt`{=b6}UPU`#X2z5q$PApJBIjW;Yc~t8=={2(EjX>pol^ zBfpvJ{J8B1L4}?;;B}ASGHbc2tG#zAIHj+lCCQ^9Kr9H;CFj=pG0#wv-^!G?>;V%R+Jn&ZLxt? zBC-r-FyzEEq~cNVa2sji{leKM5mZJ%SdDR)hTS!|&SZ;ZgP;_uP9bm*8G!_0)gmM> zb3p?DNSCO{xI?7DPVF^uX&RDdS{UMv9SH!@DVt{553J-c{wW@MKLWIZ*j5Vi3Z zZo6bw@yIOT2{3X2@7ylj2@rDO&NbB+%adYBlUEPBT(A>RPZBLqle4+;n*Pkx>)~`F%Ia#y4~i$1N3ZFDmnn&TffKVxKB@4W95h3@5!frRJq^N z-Q|%JJpEdg@=&LHE0Yf7iY(>i9p0m{K7KoN@6!Frdmb%1`AvFst@%-MTqeTJ7Q)!s zl&DrHDz)nY6Dqp1O5KTvdb$_liSMyjZi-xWZt=Y&qm>6{P>E$kHB)h8h-@JoQ|WrE zxsh7jLn+4ZdkbW@@g zC{0C=_(6F}Ri&sX6JhKsNu^$sjo=t|p49ulQP9P9;o5&t8tbM@ZKsYK&(-EqrB@ae zx7AJ7R<#zuvS=^#h9eIBIXIBZLcFs{x#a5;VkUTywJ_0oIXDOhEy^NaWL&(#`G|m#@7BsBVK)S zPb>}dy11|;VZsn7{Xs_N-h$IsainsN!nFKL`i4=>V2#Cp)9n-}VvWLZo%R_Ys}{V&~Gi(7S%wlq<{qbJ(wsDI46#(3VPAB`g?pS977VhGQdDBDz@K&{nJT zV14RAY)DE^Hzl4Lj5>+vq685PF6ri)#7$)iNw-(4+tNyr?p_++P1_FAqp)yc*GD%O zX-#>Z#Ws$J)~aVo+cmnslnu@3?~U;eY`Xy%5o~f9Wf9S;l*JU?TcNZTXc)?_iS8^L zV;dy;v2<+g0I3)B_gaGi4c))SaNiR>s-8mJ=%9<+hqcs{r3ukp4V#L{UL@9~5sosl z7a_7MEo8V&792#fIhzDV5=6C<_uBT%Oip4eS>7Uraim_QTN|0mXyQNwYa)UwY)0V* z6@GbFx8L@5w_45e#7xUCMdRBQ1~Pnur+sX6hXrjgP#mDRB;jXkdVDn8?k04m@{i3m z{1RqCEhY0cT)r4n#rPW9Oi)QzFj13uxX04_XmIicx1>cXguvL6V8we1U7A2l^ zAgD{LP%5+v^!R$6C3`cUNVU}Fy>+74%!Nf1ZTs9rbtO|JwxH3Ol`x6KSph_{-b503 z=mqHMMe?ccE2AuJo4k}|5pBxSALqxrr}T4WyvI3;Jy5zuTRDUA&>_DW%fxFdc4fT* z+S$4kOHWV4bsetjaJ>xI^++3#E=SskbOn+at__;KDzAS~7g*o|S6d3a5(P>%$`F($ zoKvLAy$bmVsIA;hcqWE!15huXf~3l(MUkwsH=}IF6idxmQpH~FfP4*}Z!ioPTSIef zU87|0Oz1visnKik?0VObp`b-C>5E(>S@N3R`gJJs21^er`r@t1G)?2Ag*?JZmUyN@ zug5d|cixrROVyw_uYi<)zq}Z3Cg+TFd!`y4!f`IP=LNgXkc!spuSV+m_PpRrl}>!; zeS2Q;xQ==BZhKxZ>&^DOVDJcR&kOE7*`62WpTq5W!BuY0pQ{M9g8i-BKs{U?mW>@p zrWI`Zm`y*f4l}rk`G`m>SPwGmAzU36GR1Hyywgaug4uRvwgcDsh(Iff-o-`VhO1)` z2(8Y#h&(IU-NEec#MQOo-Es6+!DkQiIR|DVg6m%9`Usni2t<##*@(c>bF&e_tEXlo zg3n>`#(QiwBDjtUuCA{rGKx{#Fg9x_wN0fcJi+Iv;L|g+5y6iZOGtO3@26|C5mBBr z8_6I3st`wj?p3r_fsy8fBs0wk9j>y~D7;c#noCtOA-Zpmi0vN{2QrbRM?Nv_UC*Fx4IVTq3r2! z{Don;K03#Wqq|nau*kHclkn1v7lh=}EoTZ>;3OO}7$7(&ksS6wW;k-|vE7Noj7H{> zU@8oT>L!1X9$7hwlDm0(i2~%Fl*CC3$od_2*S->L&OW-TX!~mfwnt@DRe|+Efz{>h z>TZ4V^e5rMkfRCG(yV;Hw6v?=@1rb=s;Ipgfik~gqxf13u}Rez?CwuYoBRUVEF+^% zv4sqb4eM>9bjb|SXRp5G_z~xxV zGJXu5MnM&;!Wbc_0*k4@RaT}xjSS8 zVK}?!qyoUhEcDxOb+{{6Iap|psHB3y+nK>TaGl?1q=LyiJ7CgT6jH(DUCiX&xH{IU z;{Z?Lq|zM`RPcEZ^La0>y^jPc7{8AhzaLkJUt%p8f;6_mL}ZYxc#mx22_vXDi&RiP zC?Gh7xdepyjr=L<b)EbX zJO$rRGhebSb8Mz{AGuTT`>f#S7$~DqR%ahlQvmop1NZ{2j`f`$gwhm1zQ{mm4(G6O z=sJX^VE1Ka_Z3_nEjLFvOHuGwxnQ!YnXfRGqWG_I@n6T)(LfQ`u*>k3g5Nio-#2k} zY=i{DQeAG(#+K^21NRAwN;!;@JtZwbe+S8-Pg}s?c2^M%MU6btj?n_jcSU_TdXI25 z41W4?An9%+7zzO2V*uaB)v;^G0K|ImJVh=PaDKpWax7{voaCG^oNRR$u~5MHA;X|G z1V>ksZgaXx11S-d>1PdR9|C^VH zgaV3;Na%=1l!k)Uw0Vm}C=iixIrWz`3wEqmGolt0DFn+db{BzAu$B=B=RfkGV6=f7 z0Dbd0WsE#bx`{X_Sj&imcVsVKW<(kk1jI(4*v8{2JE>4zqg>p-hgx?(oIM=Bi({@ zE0Tw_6{!!YA87z-5UD3}l>D=jV+gpihj@yt(j@yxXBFAL&tmL=@xU%H96VFMG$0Cs&k3;H-9Oc$o$?()I-&q|b0K+6*4UOXpJjv|pLV@N%D zCC!<$lH(X~Wyx_I&q;}Yfpv#ixNqA1uJQ<0kc?wc5r8&FdNSRScH_C_AN9@&XJ(++vc zERR$tdn1ospX@}QlMZ=Ku{=_(?2SBjy|NQ|Dh_$7ERR$#dn1oszwAVwnnRvC%Olmy z-pFIuGdq!|;gIJ+mPe|ay^+VRZ+0S2(;-j5@<_Eazf(;o<4>mE*@;Lkhe$Il(otM# zNG0{NH?PR9e|93zY2eEmQfKj;hE%DB_C_AN9@>dKXB_g(u{=^8b)Ac5Q%AE^9)~*G ziBJzYgnAkaCAHFC3T4$wI}z&X4xygGLP_QH^}PJ(tp{>2@mR^Bq#YfTfg5 zZ_lN4>c5>z`9g=3FJdXBD%^7^omz3HQvQ@f$``YgQbF#yluli_Qz>8Kkn*K0rBs`H zE~Qh0?o`T`Ii&n)mQpIyZ|e1UnMbegRM3|@1bqbyDpl;BzfPyN-KmtXbV&IsmQpI- zJ(tp{gLf+Bs~uAQ3`;51@wfE4(dN_4I~Dac4pD!WMU_hWITF>SuXifyYaOD#jzyKK z`>VQptF!UxHeH)0xWu~zaLJBl4X&iKzuqC<8(2K4v-ey)yUyOJcyDxw_a+ukD(gKL z&#tU@D&CtN;=P5%lUjPu#j|Vaor?EXhj>57;z{+q=i=Gb^G?Nk*dgBASUjnh_gp-? zUf!vAKkpFl?JS;D$a^lHT_NvOymvUn`vn$HYT`W?&#sAgD&9LC;{76vCspv*^m^RO zRKYuy@LdiG-^~(A-Mi;6(5`!TD&8+S#QS9yPb%F#7tgMAcPiemIK=x^7Efy1`J*Kz z_FHCi>tzmYyHgq8(8f6XD|`&dS)UsC{``?5`b3&C8i2)Ch@ zUsaLOe04Z_6cK~Y6jz?EIO(G09|}q6q;v`nP5&-OB0YH{66wiTBGKN_`;l%!x*5qs z`T&x{F#@L`d*sQ7Q`I^PW-j1d1@;7o&Kf5;n~+R!KG+c)<;aN0>hYoRsIgk9&@2t0 zepK*!9N*_r%o2bXa5jTI!MPfV;9P@bg7fQulnlJjA4rf!*2kC zBa$=MO{Zs_3otU6I=!eHEMR^UU>v~(7)+kC&;hO~?j|G?x(@^0yq%>k_@57c#GlSmC;m4hnfU)U_!k@<-fR$*PI=lTNcn%M z&uFNx-`ly%@EPf&QB<4_`E|=+?u6Il7=OgUJgp+rgZ` z>_8$gJCRIaJ`OPZvfFs678EOG9G!&0e0v-ebsL6;=%iQKkY%uWVtu=X_3whUV-W8; z7`>>8JHVc5;!Y%j^H?NPO?(1y9OvAZW~z-iPynKAi@1NJWE2_=8p4yO+sdkW9C#C! z$0HGz-AE=ZzXvRiP%2X$2hJCDvIl$#$1oD%7(p`O_#|*RdcY|PJubYp19cv+EdseX zOes9LSJuv6pdeKHkO0z#c&L*YBYuFUJI(vc2YZe4+an|=4cAXm<7il0>=Y6TDP?=$8DxBY%}H- zd@P0MxCPH20gpqio{o;(R+Ga4uYV{W*|wwo#AkA&LP(9Ke}9aE>2v=auGB$&9@lgS zbqns@ibT`YFCg8XT`?zTF|78>frO@9IR0BU)BC`S_?|!_zE40(^Cfn5t{{G{Z)h~Rh3@4*gb?evs zB|xC+ra%P*^~?C3u5S67zk=tEXh})P9Ah34`C_EWXrY9VgUX9Kd;nkw*f;}w60T`r z)DHd>$q}e529#KIbL@Z~GJ67xx?2s_1X1sF{uzF!A>x6ev2Sx3DVo-Uw26GS9tzNA8sN(PkFcr^&3yMe4gI`>~nhfiNII@Ci*^C zBN4_4r1bYmUC1|)9xaEX2>3<7mGDaz_C^G!b;R4~7=C=5t5iSyRHaW^ zD*f*Oy{pLH^L;FgXfRWZoua!?d!M#o{}!-20#JztGrz~i2)Jd2dy?UvvcUa&z;#4Z zGsBe!q^RZ*jXp^QWN$1Y4$ht|Pt7z{qmu2S!eDjXRxM!v0buh-S&kQ2Ft$j}^Ly-! zsPtM&j=DvTf9$$_C*(N$V`&8TH7V>33-)gVd;U#I>pP%x5#onkHK2VcLbNU+%Y!Ld znig5U16lHWh{bSI?dc>=r|C)pb*zoR9i(u#EV%y(xF2Q5-UumXQc|3@Nb%2*BEQ-w zYJYx@#SzG78M*E>&sdQE3y?pmkIfM>$zyY9+N)=kAf)H4Ln6T6!|yCT=lgi>h!<-d!9@JWoFA_|ct_MQnKKYzJ$gMI zf2RSHCj4(m`4?4W75C`8AR)suQZhW#BEt_LLsuhkFV7Paay%;~$FnVR{5#}$)SNdY z@INPo|G5_YKLq}+My~lhk4WHtUJCc~Ex7*!xE;Hx#*ekr^NIwt7o?!Q&;sp8faVBY zYlH@am#K1XO7$V`gV|v6Lex_GeGvoHqs~uR0RATcb~P(vg!6XZk*M5@8T%!;QfK{Q z3-KL)@@@p(x?qL;EndboL+MWX+PM7a;?5u?r7oTnsU z%JY<{WxU)1^Cy6rTkl8)NlwkSx|9Y#*XG)*|+Pbfp5guUFWQ;>hg0{MSCX$ncmaqpHBtf7N@O$z4ET44SkVCLSj zNy0p<^PEH@cr8PH9j?>}UT=X)3SWLb$bxmg&wCPb$n%~^jyGE5cnsul#IKJBy`&hO zw&M2Y`zC-Q#aVxGZ?=G30HU6r7bPn0EsW`{xKhRaoCVWDV9Kp67|)a9y_$GqQ6Bes zLY+W$)SoH`t>&E679Iv5sr;ukF-mIB0tV^&+Q)jf(f4qNjEEqFth8}P7^uXocDZ-|JGf%sTNEa$Gv zzz+XKCSuF+BV9LgjsAQ*chym6$7zW=dKcHxyK$wK@=Hi*Ff=c{03aOJ)HS~zR13{k zxJO>87f-=9Ra6n}Jt}+rd+$dU~t&ybbjQQsEhY7j`uQ-U$fv? z0UW(Q5=;Pl9|L;`|Q@B!1{Jw?prC{uEA5uq`iOHv)Zsb2QP2l=8Ggch!gf8_I#k$sJn2o;%jX&37Z~85SO9JSK*!2{ z4^Dy;K)=X9_3GA_EI=;@P=^CT*C)jZIA3NsU%{1L?4MfTYy=#KLZ|GjoE*l+5eS={Z(M^=vy$K=<+l?f#(~H=bMb@uPt~s0ngL2`xXNpPjt!3uDRW^ zc#Ndjdxj1!n)1D)K87=Iue9}mvA)3jH;nfL zGE3Zl&$#~qSL!hS(SrLL;C2`RSxzx5oaK>1UZlbIp_d6)D_6)3@Wqg=c>%3A*}?tEd_GB1LO`o z@BF2=<{EyffhE5n7tUiuK1$>g>dq9@T@I+X;kmt{Im4~G>ne#|g*2map{7>@;vd>u^eZRl0~R-sgA71TEoEd_{=FzY$d13As7ojz)*;Vt=nxM_?gQyXOH8 z)vkuT&jNWr?z{Bgu$mG_=)z_hcC&>kM6a}sj$3M`v|juzu3qC^m#0`Ksy9zcVw>~WSl1~7%pRm%bDT%7KVqx zaGuT^7Thmj?iVum6&CJCz}*oi=%Juh3Y{+-^=1nzXFI&etBazN-H+#L@k}Ug6zlV_kChCmt#Sha*bQJgd zAk7%A$B>RAIafe(^!+9o*sGNSqd3O_Ek}ABxDWVDa-6_@lH&=uYB^lKxp@bMNS2ve z`HX%ckuX@)NeyboQ9=up9wnY=5$Jvv=m8dJ99NgYbH@US1~y;vJjo)@lUbgpusqbz z9DbQBPGVqb8wI16BV-Z!Twg5=TfElO%G8jD(nI zLaFeZx{=Wa5x1^{AkmnqTUC0@EE;4Wb*aSIea1eCD=qQt^Sp8VcMR)~!8og9!&ox_ zl*BKOLZ}#SdBp%0d?!(w`Zt9?=xrPIEBe8W#`2Fe zXr(StBm);6s@JAav5_Q;`zcPuMY2;M>*CdI!BjyA3$GQUh1T@J8o%XDnBr{3E;RI< zt!)`d-&uuv*5O8}@m`rc!~F2Ogo!|T^IOE6Ou#P3oSXiV6Y zgrmWdFe2G?TrJcx&4g6OwU$ST2y}z)q7w8}@McTF@yb`9yVRJNid>AkBB_1(fA(6N^7giHi{H475mK0OFfFoV+l=-OreiS zDBD7W>RJ+Rv#${q$N{`=jr)4hE_TnfrfFxU7)DgMYqqrq!=m53Lln?aMgnqioFfh% zwOECik{y$Yin3oDKal>iuu)R)FMM6+_u=!~KG5ISHz?a+o~*>M*S0qE97+*lFe<^bq86(O<#d|p-bH-72Mew7m<^g$&V|RB@Qrfuy!(x zdusW(GV>6FZANJf3=*wr#3hug0AejBCPJ|g{pwaH7ZE!E1&VRRTwp>5^Ei?tBVjjx ze$*DB4V}i0XcH5x3Ai^Qh%U`ooS&aPE3v!T_?D7+MyD~YD;6rZxoXXvjo6sK9HI~B zRQoIS3B=`@a(+4OS*ZB+Mkx-oS`DpElZIm@q9V-GLY_w6?2=qhH&KwYL({bKEvi@H zV#0G&q8&2Iq6sj)i6chE?G8$WnuuEK!jYdCUu9&Bwbv?4bxvc~%f&;GqBx=U94wJe z)|v~=b!6pWe1sll)_Cl>2<>$-+9YAZH+aa1IU_@=B9fH)_2wv4lLF~x8&bXU6)eUK z$QzKe1ZNh)DO!CJXH>99)9iyKG@2TEd~<)2D=V?P(grH@W4$#;t;8O$tK-55BW5{E zXudiypug&PvLnB4rX@I+#U&(nZ5Cq^i7uh(PtwwixtwGUD(*tPFti6lwpwrLyPPGW z>ArkQ;w&ET%;Bul{=LFC=wje+Y#A1 zwHg_ZN*?!uOF~B>J@2O$Z=s}8)~aVoCnCM_6=h96rff^u@dp)yuj^9L8wrn~jkFz7 z!LnN5jlnS&-Gg_$R-c?K7t4i;S_0dtn>{T>Wl*B_#9 zT56*%S}JhW(E^mE)}ecO6=(5wZ>M=xE=5S0S0sE?mV`bm8@VD#oWyg=v23h{gEh*2 zbDgB(dql8wZ0rDeQ!N|l8&KVAh1NY(;JLdE-9OV zigI6Lx-Z%iRjQmw=OCwIZ*>&1FFjhw@GcmauHtoUm z#7yxN)|TapSL|;9H3AEQk)o zU?Ebf&n{9Fw^q{}@uFTx$vUX}LLuuxK}1+qapt2wO(jqJdVyAC(T|AydQHw2lNQUz zZQM!7QnGVU;a70sW)sCBgPN9^fr{Lmrf4$|*}=sY(P^NEgC?Y#MFgWQ50$*0OKvv~ z(BrjgwPA~BC!!**5+jc+O)mvSEF#mjVR($y{0dD2(3CYJ+Edi|+enGgqT`iv38NiO zJ6tPx(p`Fd{u2w;O1O8k1be=y0=YpKKP1|fv60p&jJOsnBQC9J5|H>gsKsJ)Vsn_b zqM$pg!24DjH3o?ls$>bFp;#Qk-bNTxKxZYci^!;9DQ~E+Js2EBd-K?Cy%YEfSHp&ky;ZUrIlZ)kkM0D&=5$q};?(0M6)IJV_tK8Z;$12&t*s*vtfd z5L=3Y?U0O!1$7$hl3AmI7b5@CVfbLqH2np|z#v=6&n4TIlAjC6LeMQJ`36RQzDnEz z6YzL7!Cu)5LASs+Ni^BaC(&dxA0yB$m~0v(D!1}6DqGJpPQ1f~pj%LG85o@`)FgjJw$H;!reNrOOEjU#-FlxvJGy6;4Vn1>|$p~}{8Wp`GAU<7nsCzD< zZpu9WJ;82CNV-^E4AM1ca&g>ZKaV}ZZXxIvG-nKqS~;7#{bckcBhW2qHTBx0s75|} zJeOFvI1VaLuw15zm+~K0=J`H0W}fehW9F@KX5P|UmqK!T-_K_(CB%VgqU{qjksJ8g z`(sl9P#w;apS@c)^0W8OCUS#(_TJf4fM@TdP2>iC_DiC_P*LwfM@Tr zP2>iC_Ac90fM@TyP2>iC_MY2RfM@T(P2_s(OniTt9k{8C{>*;dl*1sUy$3fH;MqHJ z6S;w(y(2dj;Mx0g6Zu}Ak&0S%^yj8h26azu(aubHtemZ#xv3Q9Z0*xcdG2nP+1Zne ziEN@%-{tJoZ8;4noc+74tXsV!rANF%G%`|0Bku8D4b(~t*l+nVZoNufWkRK z*vi`F94u@(4Je%BhOMk!&LPB>(}2P`n%K(P%V|L29Hne!?Q#xSww$Bw?ZWh?Np`GzrDb}Js& zh-NF0@wf&zTXt>8C&qUBIA<$lz&^m)vKaWbm_1NI@!mc_u=K9<=E8L$sxwk!s| z_R-5$$bfy=vSl&wwU1Y}LI&&ul`V^buYH8F6*6ESnrvANeC=bBt&joxU}Vc;;A**TP_bn>*k0R? z(Zp8L4#zNJ%V$Mk9Y1U(F#_wrVavCa{jo1GI!Ue-{)@x3y@t(&&^iqo(!QoA7JW~uxfIk)z`O*$y9WGePb+{izTu81r6Vd#IT{cQE-vD(!qn3r3e z4wVQ77r`s6E`pR}Rmz!fMZ#lmt-%p*O}mHPT7&=G28)Xtc-~-n6uKp`TNr`6&O(@U z+FGrs)LVp2r>X{ZC-x(wdm*0qo?x>!CXXD*!h1`<{5|!jOTA6!yUu z3SsIRJD^ck?bszWv6x+Aivz?P?2ohow!0i&_ejD{aEd7m%jy;goJVvYBwQ!N+9=d5 zL6V6uBTq?mXMs3Qz!uMkZg`?*~&go^!+5?&6f8gh`OtIh&gI#77A&zae$ zDG7u;RD?w$#5DWZBy(xQpMe}Qq&&Ig3yN#UC0gmz zEiseOf*|RtC3#XC^Uv2nooUiApm2 zwy_O05tv)FCP)inE>1fFp}3vK+R+4wMl8@Gqcre|t0?LIBRZj^MPwSL@YGEbCLDQ4 ze8DzJscQf!Ha z+8;*oOUO-yV690yqR_3ixRpMW>n5IsBtW#2QTBC)>>;N%FO?Mu50tgBEw!gRy~~t4 zLCU{`O1mQUAn62hZQ`lLp(E+L!p<6@yM$&KxnW$Q3Y2ab1Y$#Y*W@b^7M}8hSf*SP zkn!>i|Ao*CqTNB^0^x_SDYEG`_#qIoYx8&utTi|RtZp;%zfc?bQ@IzVjrP}#wHxgd z;TF?<(G4iY2XwbO=tP^am1?kdzusa+zB{&@Bak+ucwvuzouwouVH5L}Y-vk6AOf|+ zh7~>}Iv9rhW|DM-VNLt3P0_8`X(uvR4{?VU#AIl#BcZ3FnrkzDVQig2O@(SJZU>pR z$ChO1>p4QXo>5rX#)QOC+hLoFB8m-BO~it7m~rhwW#J@(kzZ*q#Kz-jXqWEb!BkX; ziHCS>s(!X{pvSsl+9|ZNkKx;nG8sJt zu4CxgRJu7OTm-OC!2yJ|OO0sz7D}qjtC?j6iEf_>k|?O*#j*fBGDlgA&IQY~8^Ow? zr@)GYC%_%e@H-2(&X#MP>N!#7Zo;x|(wuFXS99A6KaNTGxGe<6$LnkoFs~WFcS<{4 zL@&23oOV=zW5hpf6H6CYmuq0{Gk zndX|#ihXjd09CP<$Wco5+jjaGMLEYijc>Y9^EBmL%B8KrFi-L0!+@nKMCrQZE%LTe gp+(mw@2kk4%k*69{4h)#1?}WNhp}9Cfe5Sl|B9Wu4*&oF literal 111234 zcmd5_2Y?(`c^0-Mxk<7l+p=6_OD=NL=!&Xsigb4>wpFZ?5#=a*yK{GI?d`62_9UID zri1Cd_YitZ=rsfa1VRXeKte(YJ&-~ODU_7&fAhb2|IEHOZ+1^-BRtIQ%>Um1{qMbb z^)Cl+*}dcJ1q&8D`N!{TePwpruMfX?&Au0%^6)Qz;pR_#?VYdu`A;@)x^!{*`cuAj z&;I)@SOvP-p&Q0-kHS_oHiQ2k3+gkIVSB7uDjzLPhS6B*cobER2IX2~rgFU0E>G1O zlhN22fL&I6j!sZuMfXwHPf#l2pLdu6s>nr>9aj}54D*wnW*uMmb6 z_3+MP1I_|CjSFBQ?&BvDLcZkPvkl9!$b*A@!KOT+M~2+eE@EjH5@b6YM{I{8@g3qmq~Tt4&ZYPr>j8WZiHjNVf9@YH9Uu6{7U|E%S)FG-r} z?p5zgqlD~wmvV6|!~H4uySE_?QE@c`FS2_IBMa+oG@FT5X*xXKXdNv#TA?Vj?Ll8K z;3=~amBTKb!w}_Q1$Yh@-E!PtUmW0xHu|+{qaLAR>l4+9&e*PQ@49_gpX2uI8wwD`3$4ks2xrR z?eIi9xC7a^KI&BS8;uhkrbLQ&-dDVu+W~#W^zT`?JWa0@&DP6Ny98vV@>I1RqU{Z$ zH>_31$FOGDie|WjNJfcdUra*s(~^MxU5JaC-bJ`B#&rp6q8*ry0 z4R9SQT%FXea?z+pB-m}tu5O%`b}rkQElu!=?G0VAt%JQ{yGhrJWV;_c1HoI=#*NJOCT4qo+{<2R z^S8NV`CE6l1b?}qMPfey*MVaE%dLZ(q?&(3eh}RBW zzArG|(z9%1bqXnVuLM)Mb3{yc;<~kvfa$HAPJXm;BpAt^8)7tstJoG{?hZ~T-_DI2 zfkJMT5Q<@3w-gdLy?N2e7CR+^kKC#tJ|nnF#rU|kEG#TjI~9Vv+#ewByKvp%;eLV^ zk621#!=!~Vc&Zd(!>oy*_JcsOyBXONyIXJ(yIXNhxZJ?NRj<@A zlWvw;v~NEUY&WcBkq^bBw}MnMJqRYm^fp|?^bjsjTdRb%a1!(LN(lDNY7Jab&M0hM z)P`;U*f0|hC&ZHbD7X>#!?=k1?YQpt%=|jpXQzGL!O6_~B)kU$j)Zpvzjx&~ib_2y z*Q)pi(D<=Gs*8jc*u(W`NWGFQ9>OdhiaV9S!|3v`Q0FVb^h~W?#pD20PV*$oS2o;e zqp1h`h!B$j$@byQc8u9R0{0^xwlmXBGeH4Y@om%00J9-lVGV;!Oh-h=@<`_SDCYTS z+$;G!QI2vaZETDDuw*mKR^sTLHQIT{TtomUF@SLfP{!R;);$BrVOs&)(R{fl8;wbH zD$Q1--58&l!0cyWtEppZOuJ)>gG?dC3|Y(x7IPAJ&nG?EYL?~7l1@8RXR-zJa^19O z_OY7?#d5NFC2$z-`4%^<7cx923l zfvgoy=;dx@(kMcSj`=V=3mt)@DedhVKddzA>Ng9mg?BrRHcbs>i#v^) zkHh7eqozRN)L^FEp1}&cAezx6frRn+42&n>_j5hd7jvd2cJint!)Y3y==3Q;k}eTKuuoA*#?N5JdW-Iv`0bnfn!>15OOy3R zq|Rp0QL6DE9xyTcMTa%e-`AI{ebR7-MbiBvht&|#*=s+G;oXBfwU=k(^614FbeU-r zp;HBv?DVG*jaZI5MXPxZL!m=Xg7REk&nWzvS+Lm74aEw1UsAs|VAv< zq(hw4Rf;cSjJgzGjNfI~-B;J5G8!QVRN zsd0Cus!y$T&-mI5zSrTm@93-mKGIW-!g4EYXXXI~za-Ljg}xT0mtWQEGicv{-(B?M z=DV#pj`;ljkmqpkjf_zDk#EB9*Y?szEeJb@M*r7ugX`)8-ptr_pY?nA-R_}(7xSO= zB+yy;a@r$4p0_X{{lWh}eixs^n{PT0h|+~v>|>YkT!r`64A!^dcTXKYbw}7-I#$IB z4f=dSp{8JAQfGv{C(giy_w5zSwZ=FGrpYeKGgiLY%?TyGcW3au2fsas6W0E7s@-lD1hfaF62f~k z5Z;I19(`pi1T4cz*DiN8V7gH32q?`w@x8vEf#}Zf5AeIENdzNFxBgg%&^O#^Sgv+| zZU*BY;&-cuPOdRdI&aNJqlTUD%vshxvq&-JbwDLJhy=3kKEQB3h&#;_KZL87x=SlE z`U**FEsDB3kAdjU|HJtG>R$Q-Mv~TEHGOfxHm(t-=2@cS5Xe8m$n_}kQT*6EPT#xS!7%43t566}EJx%ZmBBROYk#Jlo;HTzwp9U0KpKEA;gue?{ zVYrb5MU(NCTrqJBP5;OiX?o?}khRbuRX+~yNA+1c~1M~15sZ7hl z|4N4KpW}DoK_zCZIv_qLz#)rsNgT_qIv#yGAmsll1JTFIe}Uf~%~c^k+7=z7(P8~S zQta|j!t{XD4l_q-Ut`{1$DPXlFL8NPpbhV+iQPRli*=U#XiOr@{Ts|uSNC7xcj0nB zUab!f;HYV`iW898F&reLyT{2BDPP-#KB7__;uI3dHyOyca3|USx_cn;R5?6`@hDO= zoMh}Fr5RK5F^G76Jff&2kiTId-)10xi~FsfZh-aw$4N1sG#b;4S>=Bx-bzM)$Be$i zjQ$??gT)wCS{UA_S}kBvOIsxyne#G{t@eZN04$)oKgb& zMF!ZvZI&kXbb!tX~G zU|ufO%V8}(lfsCZ*Di+ysbu}%%=%Zj(-;41T%J>CCAYEhfF$9aGh{d8^ilKYf#NDI(qWEK-C)Cd%ua!5JbyD|Cd`^opd>7;Q&isOHV1Em<9<(D- zwj|~Oyuh)zEY{OG`!jH-Vm%Xg&z!4SZHAjS3fw&t zhls4aNnx4HYDhy^*c(w5C@XL%Nvro##FfnJT;_Ei?w*e}!kVI4#e6TNT&1}18_D2& zX0V6La20;voZon(Xi7ESfxZFc3iM(#?hveHtHEeAMfz#t8@Yhltj3+n>_Xf<-zI7U zZTCbOWA0>#nI$?vwB2pNV5(mjC8`lkb%>}W-;0><#mx5-+#ll+uamFrqSQLMQ*q6P zLv&Z!b@Lj=bu}0JrTFc$Ip#vbD{<)ct=mMU=ddIMI! z%94MpL`PlHs7NFA-b@wb8QCFb@=UBl8(p%#fm!Qs>qh+c9BDA?^k#j$`yG%zc*l_j zmqz;dfVaP)Xe{B}#BlT$`2F$QGa1xy8bLB)R_!&l%c3D12+TZL^so zP|HNygG-TP?oQ@S?K(A719H;#uvA@qfqy#%HLei+`zUfl$r z2Q!}|%;zDv)4xk_`Lt1mj}Fx@&a)$zW@VgOAN{)?7yZ2fmuD=6i`L#ebqIFbnccFk z*mdWjL$JG%+1-S@XP?sRfL46kcE7WNFI7F2Djn8%_Kh@OGGQTixA`791dj(Yj|buI zG2>?*-Fo5>%uZuw3vu_HeKNBm9ykPpMa*C^?)Q@C4Z;0%=6(k5p1y&_E8uZMat$!S!tBx&(L6QDpZ9;et=dd?x(T*$tkt=~vMBT5z3Wu2tMUGZ{PA zlVbob5FHhW?i0grfn{1?nHPg^!K)#7`Py06hTekDoq~_&D^fL%9ebc28&p^q;FEqKx7`Zo8N0JO=ZH)+L zC#p;8*?DHc*dpsq2}-d09)BvN1a;q>W(Y9yG~?bq#SkFmDaL(qdLf|5(+kuOsXQ#W zz~~CfK(-DsId!G zB0Eoz8NCyFbB*;)U^!VQ>L5k!b@xe0mWd>^u|?08if8bcNsrdbM@H2{II$$E^$x1U z5eGYzB-&-d<02ibW}lI+2t7N`x)h@~XX!VbdO80#o(75POnwS~fb{bM{@LNCk6xa^ zFGmOI*SY$a^m9iqRymx{(Ys|C4{`Kt^-h=bHhR09cI9z=N`BQ2Di>_@bVaAzGkUzP z^KrH@5ZM`tr!e}pEaS$Do-Iv#*-~VgR;8Y0yhO#%Xo_dP5_)$2?)0;OHkjjY;0pVz z^mL5!SnD7(YNkZBL{U+T37|?%PnM}CW`;-4Vm#&<&-yatY7S?fr5P>VGlOcWBGiEL zMoMI-VnRgsHE5eTerAYdRf@zof1!69?0t>H1q-FP(3(PZBlP+b^V;I8gecBUQ^f8G z=*4B`MQYBa{I*k=1tJppr--QKHtM+)Y7>npu68iunU$F-tJ-cZGRq zj@}|t%Ti4zUJGQ9XWGB+m98~eStwG@zY!@>Jwt7c#wq{(FO%L2p<8IL{_B9-eUhw#G9@8TZlVKN;WJT8>LJ@EX4P3 z$^qJ^iEkhx5*?PEsDr<~cGGs$*#>r@`L7@$rbZgC^S5*1X9?nciM( zeaqUZL||`tOwb|xrPl0>Wv!bf+mjN(CVM6v$Rnj|Vd{hu+PNDmP@)mdBYiBNt&=Ma ziZo%$NHW7VO(}|Ttt+?0n2>T3Z6}>mLKUP8`Y6pBSI)7X0g;FyFrI(w{(YrM%Mw(QD0W#cP)D%KY`GJ^?~Hi)8^qW#4$;|3fXDiOgs`!X>^ z=;7J=Az2yGgQfaGYzst>mf?{!)S+i9)iXM^JRD9pNg2PcX4*@YZZ29DSxGk3`enub zuraEPWfF@Rm8(!S)P3REN^PC3Nr>oN`GAyz=p>b1tj@eJ79aHXvea9P^#u`~lOlp; z1U+1ldZ=s`=<#y(Seg^i)AQq}StWmZ6=yCK@af@cdOfnrp~Ox^E7iNSk~uwJ%o^eJ z_r*p7rBhRz2rlz!PZQCyjA}DIyIL!Ukj2fhFyKStj5<_QSCi%&G%?P4HlhF~ReB z{fH*>P_`thiH!njnHeO=S%pxnUpW%Ct4kisLoHG z>0^dq1g<6Ml`coKB8L0PcB#S}L|5sQ`C4U^lP>Z^dnu*tMHJU^W(kE`>x#%P%90Cp zD0;8kX_6ggKCIM)v6WPlh-wYzqI7q())4Bsf-M*Xt0j`vHj*en?@n*ek#F@Fm?Y`Y z%aIu}4AJAz+cS-B3;kSbG*AvgLxmpE0s5esa>%a3v30N!Iy1olo#;Ojdz??i{ZY8z zjr*fjtTI@<{(%gc5W@8$g2;;B=uNYlAad2+4d{e_m{c4KV$ zrDZ0wE&_KNE}P7E;Q3X)0XL0|43_fbJ&N~NJ9=iJy}eOs;A@<;MNgTM!GR5D4bSo4 zf;IVPUZ7Y&r+yyGwNkuctvog)hhU^%Mfkjbo)_b5jWWi=5v)O>V=kP(W!F)9drZl$zPXfv& zhO!xVpB;_^OR}9#g^iJ~Qzvpqata}O8T_duLh(MltmmzaYbEZU12QPE^rj{zis9Z+ z6BF$2RW&iem%ds0dQQ5UnBX!0YGQ)f{HTcu2KRxQnBYDyYGNY)dr?hHaFuFe9?kU9 z$qK|I*ze|#U=Qw|PQL2^OoGkL%;px{J?iBrCGe79y^mS%$K9i;S&mQpx(&D_m>pzh zx8Z&=VhM??AHrEb6nD=c5bJ375JO0?dpNTj!`;)~Qy{v})iebb+FhYK zp=2cBo@VOV&GzC{$mFtr9eMN~9syZchX&)>707W4?hkHd$Q^<8v)HFqY zQcbhCAL^2c3XNrl326}i2!_vKhO`pCuVcjsSkGcu z_uyVwwKy|0m!T8XX^U-|L}q57^gslJ=P(4a!uA=Nn!2!HNIS&M^9OC&3X=!0vQv7V zKv!Iw*M{z-#Do#$_5wympR&*8fG9V!x1s~Bl%yZwykav+ZMrJ5)LtY??XLW-vs`H6 zYfCvg5{;`9RJ{8x;k-n^@n{9x;mC6c)ztm&pHpWS63ojO%=x$%_t7i%#L5y)XQS^H znycl%Lg4JJeWh^DJ=Tk8`zsh5b>F{}F3a!eZ)9XNZv9Tja1of_%$TX~@pzxJR(Zu7t9EXF z?@K0(;!Iy?w8>8JEewP{;No*-(FqD9=`N6t-crXCURbUU@T~%4PaX9HRN|URsFl<( zTMT7d2hWA`?E>fTOpF%M=-w&t_0$1UIY!Fc#N1=E2wd+LxIFuo){mjvm@IiT!7HV!2q>RrD4)Z<_;ByQaUU7yTYA+%x)UWY_;BEqX+Ie zg6G$m=htz6aM4|xE@GhwR^MP&RGo#(7dp>#i-95le3Jo?Z?rA}#8c6pVxI^AGWN-> zp2993^F%O^F;6@`VyBrw!B{7P$#=OJzk|DvNq50U1d|^ylON*lIe;>Loa2oWf8YX}JZWR)~yc)QXc8twLlkqg8ksDD=mAj8Y-^$tV?` z!@WpYfC;4rczc~w00RN$zH|8_K-||Ze*_fi^2akjjHlIbcvUP=fnXv76?nR+goz0` z&})DK0YC;Q@a*p-0Olhwfq*0f6ZkfG6HLO|4)q{@orHh{0-Fp-a4!TR5S(Qo0*_t- z7m7S*5`WJj0D*ub0}yy-e;Q6tfd>Rj8F;|6@5e0DJ6!SBQEveU1PmE)z%!O=7+nV% z5bR{2fkXL=ZDvnS*kW&Nj)4ONA{jWqb27?^$j1aC)nV|) z!lvth0fMy*7;y3f1qeo~@r}~DhR&)A%9vOe_YxpLu$BP=im~R}6^qDj0s{!vGBAM0 zs42tf9hkIC<9%|<;<`VhFNHX9)V9a-uiX0OZTX*m+=#$-9JT&7V%#0OO4 zvI>H62KduI>ACh|{GP_gUdPqI)x>ott`@EcR~y$1u4A~4<2r$B7T3JUQ3>ZH$6dgc zC&y#(p5%BeE|TMMxaLI;xX;~}X#R9xACCvFJUO0#_aw&?agiK%lH--Yl_$rm@Sfy& zH7=6lHMr(Qj`rNT-q!+Go*b{kdy?bzxJZsS;PNfcdTU#EO>ID|`ne5=Zv?tLY2JkQ zB+Z*~ku<-DYd)o!+i>z0pv#ly_wk;jc`GiG=54r&D~YD(jmoWm5A#NLg=?KY%5VcT zeSACc<;n97yeE0&^l^UVanB#Sk>{NrdEUkH$Z6#K$m5jKk~Te zncc|qL61BiVtM3rbAIG;&o{e~=ffU(KEm?IY3EZtF=1VEgZ%L)ci!2JNFVix^f4Cc z<+#(3O4HBzsUr9Mvm1Fn4t#k->L>7?hEzEXogaDJ^U!YO`J_jlPq935I{Nf^X>y+F zXg5NA+9T8-u~2eWI-f#0XQkZ;^%;*)f6PM3$?41IQd5D6?9}@_RRm+OPFcr*ns&Ay zhC5AJKkJe5b1bEtvd+7d-q~xnQvQiY%FnZuaw0qLQhMjK-AefdkCb0zDdqHb-lg=; zbi0-EPd!q8iKUd2-g%eOJOAxg$}fAQ{43O%} z{m3KUk6Ao9FQ0eu-1G8o#rugzynkfznr#yo=|aiFYgB zKY7IaXBJOR!CySz<6iC*yjuzX#UtUrvV?N(J?|Cho_lvI-p@Va{TquXC*AWdo_o^W zt$4rii1+U-o}6tL_u!X0?>}C6Ugnu?cPryBJu?0W%P6PW_mYgxX?C|V{-;OA|6&>C z{F*#Qdi+KxPrfkwYlqXN@)RO(kM#$GLE_tUE5*VgXTOL5m&AGxDzzF#r=Wiq;-Y!- zMYw2h`x0DqcJ$x4nz-)7)yDNJTs!l%k15CtV(z$u@d&nSz=qgTFdkxi87>>!UxTej zt=hzv$q&{exUU5_;=T?Salagwjr(uF-D3f1xC^7;fc>OQDAz2ggP?z`RvjO!hix64 zh~xW+*jF5~bC+_|BEVh&a0K>BTm<$iTsE-31z3+`69E?feQJn~iD-0V{ry=Fe>+Un z9TP@Rwwm?8K$xz^MVPL^WyAD8z~no$vVwx?Ln=&yD#)ThZT^u(fnE!+68v=z@c#?& zK0lr*@O6p<7|~L~newHWD(N^qJ)-nBZ<#`h>sbo&e@#^oIHdSLNa3;25<(OU zNF`v%fK&vd&jEu<+!Ihlzz8R{j19`qMh3;g z97}>R9!$fzMb8rDCZlH&)GZFErvR$&Y~F!236eGE=vo4|jIKqvw>fa13fvw$#Ga#X z2{_xqo@#s* z247DRW2ns5OK=W0er$k|Y_-4`Rz$-)j}1sLr-3~%|?y(apjB*r2tUP5P-$F2*4078-O!FR4r5^4Mgsa!cwa|6@{&1)v~UhO~JPG z>?6iQk;g|BIYku$xh$U3frikL%@?5?#$`iyCWvk+gf1G4>!^Q=z3q+;O8#epAMrm6 z7x5p#W#fMqh)Tuyvjwg)x8~;3A8gxhX)!wBs&djNQJoDGglY*cLbVH*4b|Bo@|eAz zwCH4_FO<>A2;N=?yd{9=nGtjyy-dKk8EmPPZox$`ZpCFQrKKS97;B!C=w<@iK0qO8 z`*9Jp1GsF^mVwCA1@##HOn^BE<^<+8Tmu9!`j58y^J$^T&rUJft55mE8RhCMrjV$qapQ5vX-IdsKg%TV7(HoJu{-7L(z$5 z^>8kUG298xBOGwf1sqQpugc7H(|CwS>?z?0o)%;X+KOjm!ypqbwgi?(G8Xc=Nmw3@ z%hs6A0~U{ez|4BEm#}#PSP4MDbHO++0#?Rl19m=$JayJHupBrQ2b@)a%`G zfg=Q8DwYXcR4kLYY{hZ`h&)}=Bn2)LE}>{nf^KOb*n)MP{-$Vpw3`UUHVDT!6sV?v zf>2d)5vn_I*-)(pk;mPG9Tg_o^u1%z-EOpIi(`!Yf?aVk*(DqWLW#KMKzt#Hj_3DA z4#cfchq^Bg_fV2XVx7)ltvj$@1gxI^LSdzjAt*Jg8C-G%QdO?Dxpp+G5=$e4rRl(O zF|hc)gCuZX+?P8Vo(i1T(WT#+!O?QyxCA(gd@&;G7(8PXKxb1JZl_&vXE~4uCuk$qb;>(XU4j&j3vMqX9(S2R;=247evY zGS?FnJd@{$vJc#V_cQr@pbKigRiFbZTQLd5Swr-|gLos}-*dk?KrUjdl#gDVISWC? zGDY9#5u_jQi${J-HIw2R0uDj!5@eM%2RsrC;=OP4!L%OXWir4_OifUEh2IxoUO3R- z*EcAaBnJDi3C|YnA33b%teram&*IPX9^9$Fcs8zV+oy4O6Ny!{0cF;eYCe(|4$2aO zRrDawVTcr-iXcAM0dX@R`uqu+lC@wg9>2;0I2DjhpnqNl{qr5@w*bAz*M2_uN*Glq z>najoqC%1GphqmQhgB+?P3YcmfHvw$%pigF(G3_R~vo}dj zWHXaBgJV9Y&Ouc(6+j9t%9U**950AMe!Cg-dJ ziWX^J77SCGZa@@w)4oOg>WnO}ameyO$l}w3@VEXs2KyCt@Y)RS*Ew)M2)KPpLQ5$b&nRK7@ zdk&d~AXD*aj;%6ONVS4rvZ;uD@C{p&)0|-d!GFr-FAWAG_>niw*@gF>h$Gf-LCoNb zf)*2ZQNnNG64pKE?>kC(H-Hx3ec>Irc)1-3p+G;*EHD9r-*yEC}o*_bo&0zmS2loBI?g?aJE!??8i56|(0}S(nxYL;OAqUI@J+^_~ zqDTvX=P^J%N`Ke^@E`#G4o8_55`Bav(xc)>9TMHvW6PLJ6lwwUW0`{gxC7=Pz$`pH zN(W;pjpH~8=lL*;h%nPA7!%s{#eISSe-d|U%Aaxo9tGgSo|)5tPf8SPQO2JJM4(xq zA^(vB^5JfpLK?E}gbNq1r62xG2Ie0-VBQXxg%6X`Fy|BnTa@T$8S3Y7r$+E64yX?X z)Z)`M2iB7vC0oex`HUQ2aL924a@@O7v<1j7GDzKg|I`8UAplwUa&m4dr3P2u3Gc)M2by?9U+yc&5GI0Lf0q0?WQ+R?E!rZUOMC3{Yzf z{)GeJ!vWA^>?X->gMl0!SjOpvRQyu=L1Jf8Vd8{=J zrY@pugsJP*W!pSOz%UL-={w4$b7VcD5j|7nH zjsP8nJ*s9C zr~-#auRIm%93eWX=@gFquBc@J{WAmm7Y6pP4q$fxm`4*J1Iz@OFO1G4kr02Lf%tC@ zh(`g@vx-VVJh^e}MP>gY1Nh$^fNKEknbV~JcNf=Q0QzMH(0@1pO#{&U#=RGy{xbvW zzZ{_I0Og5{s!FL`s+YqWb)e)S04jghdDH%CyAcctQrXh}n<4!Qcj`WW?SRw(B#$~` z3@LG00L9Yq&KYubAyg4%#~$Mn^ut6i!TpB8{g%P~j{{t@V5_QBqh=#2jn|-u>KJeO z#}uq#Ln#6MF9Xtp&Hp(7-3dUR=;g|Md2?X3|6=oH(Q+1G&_JFG?uR>7(5-xlZlZmw{sZIa@+~d3J07M zfa96aDUDb%{e%{LL^hSe;S5#f=v1lI9OCy9!%D_*E@L>)fngRHJe^L2C3dq46R(sv z8O5F7NXF+g<2_s#s~n8)0^^(WKc*;}QXkVm-vGElTH$Hu5F+SSgVAV;3~R)Uk+;+@6}v=mpbr14tRb3e=G$k0h@(_#URE5 zdw+Pmjh+0)M5^*H%Ya|&0RMP^_Z{5Yz)#QA+SO()JQ22XlNfLdz{1 z`h6zxK_r%qWF6za9CvE8S2%D#0k}PSg3<9(G&Md`K3apVUNR;IrFy*qKVj{SeQy#wqM0oJo!>;Q|$CXwHc__IiNlXP(6DP9ATUk`zwuUXWVPD^cZeT%SyeTLFmo1 z4Gs`b1_;kwBmtq_kVQdtdU~c!Gw2*hhI+52mH>kcz>N-oPXR#BiCY42y4k2>=vMpN zsV^io+N8eg*$BH&0S&k+X{-%rDH-&Y;;rHMczs?uj1l-lHz zYzD3&6#kr&HEDJvX=30QH?>~-(*XkiEzk|%85Xc|N9>vO-ZNfxg2pz?G176z$`d=O zrWc}Z)lpHlEF)A+nVgz}hWe}w)O$QopN;pPRj6zd%zLU4sV&ALIQPV~66A9-AfM|2 z`MjP$wq{V6(|Mnjsr*ARtpxe}49FLFK)#UPcU|f2*=AU2VplRMROyF_{2-A_s4vPu zeX$4XOYq*Or<2rreWE%!(^A7@T1y=^!zCJts1YQyzEoqCNmH89`{JY|gI=Z#aECC6 zI}P)j9DUeJfjul#%;eNB!19TWp5PEFNE~+2d{FuEz;q=3w1$ZqOQ+5i%VtKlg^|6? zQo6JS{c=3_*i~?$N_9!`cVngn_f;KgPa2@m?KBFLqbiXs$ZZT)D{F1XZ(Gm$3c&Sj ziD|g45)R8IQTx+4dvUhYVJ5=>05S>b28N{j&l??(UI|DZ9V~{FRs>Mqt)hUe$2-(3 zxb*f`sB3M{#(lf;aTNtOA5YDYZ(^AG82$bZn6Cm%&q`UtYy`>fgDSVg;7)uedk+L= z(?NU@4`4W2CG3F?IIjjAk7Fx_gEkK(pJVPW@emWp%6kyQ(3Q8t0pm4*QEVhJ$7aEF z*e)sOo32hooSf}rS^}qsyVrs#{#&4Xjo0Bvwz12R`Sp133v#Usi-!c7SCDThb}vV} zM^R}arwlnAJ_D7G{mj8PfJ!Hec4ww&ha5%w29l~!pBJ&ex1dxmV^f4qc3N1AQRybD z8qxH$&$nQQB1a!hI z-ky{#ln=27)GXBkblsK4@d!J=q79gNdos67+QPvkDHI+ksf2tpL)P>5-=n69XVS!z z2E(`Dxo?3R54?2rh`mp&%i=KxwlkPkxTiyOa2Bzgj=dxhTzbNrXg*WYRZ zDSL&t;k`%ao2x$sHVUx0-f5XJ8q1Z!83nY%E?VZTT#j1#bRT}(%JJ=hcDT@|ub`1) zWIm#sGm06jQBG7=8pwVJkarX>qbZCtab;q6upVX|sgQGaT}BD%5R=L>I>2T0Siqwx z;XwzqcNT`m--6g^b9Rlo?ThU|QGK^DG(9LDazJ|*pm`j*&z7cZu?M^{cnKBiG_?El zP`n&&v@`p-qs;vN+{{_g?*bAWF)zX2QdFx1cB0CtmO4J+=lcpVpw;jM-y`!A!#hB`xXsi1W_ zxYNI^g4TP1%=Wpx56_Q5n)l=W2e|$aSC{DHo+72NWs`tL;Vkm_s~DkWz3R_2`mFZW z2Y||k{)2c<=s$$JM(>;LFg6+{Hk4Y^TQ&vyNgZpxV_^`>6VR}1R#QP#uOjj=o#RR2 z;vB}}lw+WwR->i(Fi_#Y1s}oxQLFtZ?j*&>aF;O`K91ic#V2qlDL#qIqmrQ^lMpB7 zC<4+u1o#wy;J-wGPveJ8fIq@>65unqlK_8=s~Z8fZJARYoY=O-A;4z=1pg%hd=5Ws z0{jV{lK`K`odoy-t|=%9KiSZ;ZnNQo+Q%OPFaVHb% zj>VNO@9g608xH(`Wx@Xvk54B4AQJ(+i>;#4dCtB(E?=`K7Z{H=x*P z5$O}Fe?P<@^!FZI2a9ICzch`r6sUV;@=u;;nEXgE*;AB>atNAX5IYG?OYmcn`9;W_ zwzL|ER+apb0rL|@m z#NU}zKNG2_g8m7=m+qh09kvfbAqd+wdn@N5luT#zmX(s|!Ps;~gFVwke_76nOmvgQ zaWIyxXYw1w8#zPKQ#_Ij_+dxT50&MnK8g`?2GfiV#t&7bj&t=x-cD8L1)PY&XIy4h zZqzmHsF!N95pAMm3aEhNL3!S#l0#Nt5BP^&YaYXo)jv7+mfr)WFzRfesF*cu4> z?DV&?`M$Kyz1W)bfu>SIk4!@_&J$UixNwVNZL%xu*%>;@;TmfW*u@10hMUZ0Q4XLIoz3=`mZr=37a+zRBeZm@hZuE-CmkM$OFvAyu+PIR_ znEjl7)dIL;Indc!nbdq5BM3`IgxEJ;2#3`{U!NU;7{Wvn!nEFzapDP@v)|&+IGZFc zArmf(YWJ!RY0=)(B-v!7F|In3O}50Am|Up_Y3pK%!&%(F5h(7~D4f-WQcO-uLjqFh zFIs(~@hj5_6G7_Jj0rgQ)B4U_J33^_;et-OI6TtjmKU;Pro^juoWM{)y=8f()xu0- z4^;g|!!~*+^cw-^V44{U-kyLmTeedzasq6-ZrG2B6IvIvi*@_PEOldsKo!8w#!S6D zJ5-9=P%o6Lq=f_MO8c*73@W*clrmaQ2k20jycIg@Q1!sH5*fh#^g4?Wf|Qc3=!|l+ zC=Q8pO-JJXjh)1l$2!a7OsBKGSZ0}2l=&G+EE!Y`!p!J8$568|c}0ZFc-LDghTv;j%GJrr2gMFWf>^r~4JteEK(L2uXD$o`X>~ zoj5xakdV9MuqA4;H$F452m|TXpkFsMDwGTbWq!U2Bq0W@nD3%E)`RX*=|i;>QXW@0 zUd7rk9ED+!t2rmD6g_3U){(I}$Jd3KldB=7ESIP(sjCWLqOPk^)k9EbGj>}#^9!w5g*Y`bB=01eE2u=`3Tn zxN?BAy{Cj%m={L-E6TX%KGS&imLG4Cq#1cL-5Y&<*IR4FLP%L%Yk1ly@U+)@_21bIQt8TllIN~pze^S6BA3h z)ED)V0!Zs;xsoV=Qfq4j1!!-_Oc8s(Em%0pgF3}2THhd^L~Erq{QwzYb~YI9Gt?wSGiQX!B=YbWW>h)dXt{kxBO^Ik^~O%IdOIR%-3tXa+}0RqXH9EATp_r=ZRpEoms@b5a=}Xf*XM zwKF5oP{I|dgojJf(MWG7IFl+WO1xYpmY##K4T{pWeV`qrl)?G&)2xdhl}o?w>^g9m z)2Jv&Tmr=}sC=1~*R%Y0s07_$k(HyJzCW-evR>mL!(;Lz zq5BxB@_b6lY;8UXoIU6f!Ji*{hFHoP38Z%cKMi`|q|rS6hWKBcJhFID(BF0{sf8{U zB-Z94qdvQHgKZrXoqhwzm58#wCc!8bJuvthyK6R=R@0w;-7-_K1#x;cCM>PbV@B#g zKCyJh*(P+fuVeIOd{?nuv<9E1Ccu~Le8fhG*+7X6l2hMiTy%y6gHV#x*e@sWQf9>Z zxJO9aslR+FBTBCtcwSFNzTBayMi!%Sl0v|hIy+&QtNSd0BWt(2jFV|2T73{ClIT}d zj8|#$e0x!Scq4PkPeGYq!kH(!peY?>y^}BUv=dTR*JiVlN816d>adCnE7VT(PQEQ) zcPe6XQI?6Yn7mfZ!gI(do3$P9v$l*|(+wT7V%^Y_60oWPujZUnda25)PtV;%#&2c9$Toa$Ml+nz?%1<7N=<`r8&}Jz~XEZ>6qb}X!%cDOYYo<_QE zx8>VAOee0@OTy7q`qUHt3dYr`XYAll>`mDYzv6JKdy29Rm2%mHRxYEC9qvO-oL717 z)TxISD9zm6b79t2y1n=+f`M)M`MCX5dYtbSbDX#1J zV>uGLU8xockA%uGhce?kV@DK)ZF1dmM)LfOp+<8Sj;2es)7ofK=hM%_je|4ewQ4jq zTy3FGZM0?=PEhNERu8$`Q7%|UXU3`ZtuIHydeF^!RXS;s@u^6^cnD6{W?JDX<;Wsi zB`hS{`bt;`$f=+^r4m{gg~bYSJxs#QR+C%eQ$cr1Xp?B7RZOCZRxw7PJ7uC}k*L}( z#;DpZ#t3w$RNEFtca(}Tx}#K#5$I02BN@d-LqFOm#^`9H7$eY~a=wnO3)RgcU2CQg$Mvn5LhMcj-6^dZ3!`?y zrrs{t)IoPjyQR14MKRtrwE-J(#lGO{nYfT_r)%QH{D+fyv5$?J7yIIvd3%hRx3zkJ zklfw(55=>Qq^qZy66_;fV%x|q{M`MqEdvbobL8jlmaY8Uy|aznBA>f=wq+2wJ82ua zg`c~Vwq+2w`)V7xg`c~xwq+2wyKEb|g`d02wq+2wdu|)Kg`d0Uwq+2wJ8&DhK1mke zUv39(%cDQHAGf8jNa^muZ5hCfe~w?FyCb*qbNA;q@)2H;ico!Qbj zIXgRZTQ1Dm*{9pm+|nttwBjgODq+r9`~rkt?t5-l55r(t^S}O1bjd?j5jP zDGzqmOR=%bmEi-54P&n4PCUL5&6OeJ@eOXSR}u?f_Xy?6WWYT% zxsq7;y2m6}CIjxl$d$yx*F6fkG8u3WKdvOZJNviXamSU<=G+0tmC7Qud!%t?GT47kogWpe9l&Sbiiv{?KPSfs7vKgf;wpV7~g3U^c@%4oc z`(}_|?RbUFtFYc?n__~0rGxNL5>A+?mMbdt^>AeFmFkHxLMEyhk9j882=c`GIDVF9 zv~pu^LA@wNlC}~S7JH%# z!Ix&W5~kdQ;=f1{cRF}2>>C`GxI%HAD2(Z(){YTVXUEnS_y}v`)NvJFX#)~}KD<$t zhMmgJP*~1E9OyWW=c0pZ1V1WDUii4V}(!S6h86PM<#6qY6XnUCL6;$17n)D(qeZOKO>FP!y&XBp-onw z)T*^9=1^%vCd%6YDIl}Rzz{3MNkeU zsqHNtoJ7FMHSSDYJPyUi6&@Z;MVZ)mh}X74Gf^Bpg=Pp{g_fq+kjPuaAn3aEt|!+0 z+Pa|A=to-?biP^7Jmr+p{B7)ek{nX@JtUVlD6)?39HVa)bIBUrXj>g=S)-ed*yhl& z<1E!`p_0|=c&)8sbWKx3rYLdbi?0gjV1bGDrrWySV(SdH7O-#$MZU3&Hg`(Mg|Q4l z@9csSvk{XdYZyZbbr+ORr5n65Ug-?Pr7Z--*Xf`nmbyR7A<?i&HsKBY!X0Xet1{EaAdC=iAXsA(U8u9_q&OoQylpd0!N;SI#x z5hh7FMk&WIHjTk=h)Jnq#${Ru`VjMJI8vTQ&vlulK3$W&d+19N$_8x`x^bnd*&+}Q zNG)Y0`pr&D*Ca`*km$42Ro*eWuGU;SlJx60*}lP&MK1TGRvkQ&p(bUZN+Uy( z(!tA}a+gQ0BxU|En`Gkyke|&+f+qiqNKjerPm^3ywb%1Y z5kYj#W53fBbAiJo$Ji4&33f%vdOH}^5ki580ESc6j@|Jx@zU6>99_kI_E8{M%uD8 z=1W93S~Q{(x*f0xB(MvSS;MAAY8ob_u>8vi5$ik0X+;OlKi~U3zxR3Gm#^oeu+1wF zi%?cTKmbaCUy5kh7a966MWTirZ36p_?jzaS^sGQ=+^1#_?{X>FK}({59Xiq{M8)vM z5(i&Pl%fjLOpHc#SZE#0a4%$hJihNQ^shpA?)n56a~ZA)}0# z)zX?+_*@f<&cIH40*sV|DyiJOGHYq>L2F@gQJLwV7V$yPZ%sDpVXc)011Ux`%p%N; zh8$}Mq&xoO*3EJu$R-#3Wp=$yDY_pViOs@Zn;xTfp6-W3*jz(}jJxMI2Gdn(LA(hZ zGaibo<&ufjt?r(FE}{k=9uEUowG5h>bQrH}g3~n=WYO_lBzRrE93IpYz?@Ld5n>>U zR4UC3gAg~ApA$Fys5o$*z(2E%$9PU^=P()eOiv>tN!zg8nEtbL}%gg|=_ZUd33nr<#)>nF2f9pt`g2YwvT#B%UFzz-` z*!^spj$Sn!wUh9)p5)VTYv-9@a7Z~*k^|ERvS(Hw?qQ02UOn0~0ETu7hBv0c{q{EKN?XD@ zpErcEX9sT!(OPh>jMkMig=P~?X{lrrQ_SZe&mlwzt5P|{i z*5faC>*?*ycgMbsHeLAIdrYZ0siH3~qMbSPJ!6-(Rc4fIdE$Y`#N>!~+}GAQex$;^ z?d_^TfotN*vr8T4nm72Kc6?{vFFrrAZfq(|+-&GRtmr>gKfiQX(neqHPJTn&y!=Ds z{_gv}Y$<+D2cr#ii&}Pos3Uy|FX&HV5sWfd0Z3M+GYR&3Hz=WWK{um%ti>CqHHdp2ow+_kYz$635iJ%5ex33Z2>tIEB2?g zTCZ2VqM}y4qR?7fTU^^}>)P8&U25ghYI_x_-#L@XOcD_LegAy&51Bddd(Ly7_dV}& zrtNF%%ir2K*kM&xR#uo0Ja-=f=JT&7fF7}T}mH5@rV;)Vl;VfNG*<^Yy%o>sFud~*KEZegKqqj-PqLhc(+Tb$*p z`*D}{-R^ml*Vz1ZSMM2z>gucU9_udM%xkp!?qEb%?(XL&-u&q^JC@+86Yt3KA6M74 zEMC)hE8?dg-U!RDTY6U=8*-y+ctmNDU2ktDt4;6r^zXqF9{q4`LCD@`Ha?qd!j6CC zbpGC+Wwy7QSYbb54iKL2s@R_CR>@bfX%xD#LAiuhr%;zAz`X%+>?k)*v{1kk%Q<`r zo6YCT#2|RI7wFv)I0ccQ5^FSsR>a}s?EXY#kb?{tZsqoI86+2uXf?wy<^qCKr`1&6 z>R$Ov>CQ>>tG#D>`i*rgj@WMh&W;C{YL#z%QakeZ?d@~yLbS2iSqIm5D?Lga9zL%3 zoYr}Lqoguz^6cV0p&NXHN;0eCC6$f4N*jrJUkQh9Y-P>OO*xj@Ik9tIK-bMhe;)sD z)9IpvPjVJ1on$=QqH~@5pL*Xb{$|*Pvx?~vqqdWDoM<{bR@m(}1e?JCWEkP_!T!Q< zR|yd#kjl7Ho{*4p*ld86c!OJ;0&5sPaIsDU9YU(s61Z9>){AjMjvhqOkS30$zfd$y z(rIQ8`Xr>M`E4A70YFS3o7s8;!X*D-T8osM`-%(-9#g;JV z{FU$h`F{Ai#F8OR>t(+@2|GXGXrp6?;&J`u_@37LKmOC{Hb4E3yj#B=eZu*B%V$3#n zahrc1X)S;7JR&ky`FK=8;cp`iN40D5M8^bq>NRZkfLOtVX1*H{6)ok-*f+@|B0}_cffc+w~V=h&f>9q<8j@X6i0GAOOqD)4VNh=j)^C+i`8Pq2Y`Unrw zPp0*!8ub~*uT^%en03Q}4^s(lwt<(&MMm6+h?R=cDFy-fo*y>nFFm9Je~g z=f8`68^SN`6dX}HSY5N};QsOMdfD!K!GBCSnf=LwEe(fWD>xD`Ki9V5!=S?)iB(tg z`l)F?UnG8!eyzG}o=xBxGWvh+BR%MIcy%b2Wq}gXz;_jL1OlFf&k;xj!e}lJ9xeWC zh7m$85b;c-yG}1IlZmx5E)4i4)1{;e7?b|{V2{to*HLlcVlIM#i*{b%KlVeB(T1m( z4>AFghXN#NpYvPH9Fth6C(2M3*WpB2xe8fLQC1@2<1j+LApN8;Dk)F}B_*iBU_BuT z*NaFS2AjfRw46=UFxyul`NUf!AKAdbO>i^RzBtvdZ4NR`^`=xW)MyDEqTNV1ED8S- z;k05u+J9fS=UYR~G@3ylh?`o=QW+MQuRui=Z9)D#2L7PvL1F=Kz!K|uaAd10=$bcZ zN(uCp5lW&&tS4lIRISn|l&}V6<2*jN*z7YgnHo!`#~3eOTCupWnmlnH{l&KiW3#QAy=n=XyW&JjMksQdLJ#i{usPvuXd@gwgDhWh9H=?!x{m@O@+d@ye9-f8(elGt0wfCLWAb9+B+NE?>hA1vdl zK1v!f)yeS$a0$qX9ZpDY*^^a_2YTq{-)6>4oMO}Bd zO=uSoT88DEb&FE!NS7VZ1y2weTN;}`Az4FzA0n(=G@%*?U9=`ySk%yA3L?LE@53bd-bEW z->1*K;a-uo?ODs&9sZYwZXTDzJ$n3(OVRhs_ZKFeB}Vz~Q*}%p$FKRbD)Q&5)Uw>g zirQaQohLVh9z0Yv{QmnNT)SYq@QUZ$fBegKhv(Zx5na@MnP#aBSRt8s)9-j=QCLyV*v=n18 z&bZsbSFfR(HDC}8(BbEaT0c-Qvu;H3AcBMrC49in3ZlOS6U%{X8K?B75SEU+ZU z!&(#r_NEU9?O6e=FZj3oqDdkGKu=D9HS8b8XMBqlqX*QyVKE?aMmkmtJGDrt8w8&X zs$Sqij?a)`V^8}&Pm6 zFTm4}jKkqjYXp4n2?QAz0bAq*ol4eW=UByv8QnB=PR6&a+@Mwt18wwq@ zlimUrc$V*gAh9lcJSi5CJBk231>Wer%On#OLx%+FX4iqG5+71+3;b`N83EM!CDi^5 z6Z)~-OEIxwvQTO=7eo;(2jK+ADWxzJ5+S+Xp0IzFD@w|>V)FU~^>cjE1JQK*d|kX~ z|66;dda;BA8A8sx&)1omw+vcLKX5j`Y?x6ilT#31=y@Hc;e3n-zqcV1{Pn(MtP7xkH%U zm(Cr7VAwtVVO5pn1`y4SV^#*5!bDoFuP~loiv=2r?7f*|!dSwe#M)5gU^2`6;?eXh zUB%PPX91ff&Lj_Ouk@L@23!G+neJk#gilY@SIBo(Ymh0SpP7>DKhyA*L3g3~1Bzi1 z-B3!v455!b^_Dj3ms?4zrDCYHFg#z>u=jX}c2aIH#Ss-lbPed&G)hL~x7XSyfBR&gMv%;k z)-aA*gRMoAVH^#~8%T@^spO^jvQcDEeI86=bEa;1Wh(SJ03nGpERZ5oF*KYRvylSg zX}JQF>xUyacL1q@Ve}P7co`t5)UeX0svHp)bj%o@WI&RE28>k=eW>^PUVo>Z3~2tA zi$W;oeYZo9%@9Cv1J zN~#|tfjRwTUdTsHg`$kd#dVcBJ)t5Nz?xMj5}3B)@Ik~(mq$uxlhP@qG=ovLe%7J& z-=TD8LQ;PP^8y?^te=1~pDAbkbx@Iw5?`y6Hs1z7Z}L>B%hb4*kQ*Y+dR7MY#U%#gNxs5BzyX@|sTXtiPYm{?`3pj~CJimH+ScOyO z(rfqiX?<6pj;;#1nRfa4{p2S7_9xHIueg}0Z~#lfGupl?NkpJ!*3>%7&(W>C1lZ20(3pyDl5D5l z8~?+fR$y!#ug3o3)4WrAzx&TNe8n!;y?xI2?)J@lP`|p@W`S~f`o_i)2jcEDfA`Jx zjvLLF1fIdx4J>W^lO^kGPW`*Wt^Txq#mtDiT$jfCLmu3|8{Gb3_uU?uDBTHk?A{nY zS2A`NF*e}ocSS+Q%wn@{MULU5q?3ccEHalxFi6ihwhtLRO9}QV=R_G z;1-zh!p8-$Iv0Tb0U^#3HcuWUkCt;dvM4sle8(4ne9|*!U53C%^O z6~mBNg2T%)7-P`c5hhcwlE_!g!kyFKqK*)4STjkZ6Z+7knGYL-+Lv2*+K_cPi6B%* zNXxbG$_bZhD>Zs`iB_z6y%N`G)q41ygpeVpM6e{;M!6(YxlE%R>5g;;+wOdx!Wv0g z@Gq#N4g7yQV624$s{R-7A;AMUA?(Ld{PLoChM(nC3i2{qhlow4naN}Z7^{c=`T@n{ z-z=e+$Qy0%UpSbMF{B>jl8`*0=gHI+@Jj^!tpEW^gb9&+ZE}@O8}_}zEfkz#)JpxO UjbMfwL)8ExMrZ8ZG1=yS08wMeV*mgE diff --git a/BrokerApiCore/obj/project.assets.json b/BrokerApiCore/obj/project.assets.json index 4e3dc8f..cfc2618 100644 --- a/BrokerApiCore/obj/project.assets.json +++ b/BrokerApiCore/obj/project.assets.json @@ -15,161 +15,6 @@ "lib/net7.0/AsyncStateMachine.dll": {} } }, - "Autofac/8.2.0": { - "type": "package", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "8.0.1" - }, - "compile": { - "lib/net8.0/Autofac.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net8.0/Autofac.dll": { - "related": ".xml" - } - } - }, - "AutoMapper/14.0.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Options": "8.0.0" - }, - "compile": { - "lib/net8.0/AutoMapper.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net8.0/AutoMapper.dll": { - "related": ".xml" - } - } - }, - "AutoMapper.AspNetCore.OData.EFCore/7.0.1": { - "type": "package", - "dependencies": { - "AutoMapper.Extensions.ExpressionMapping": "[8.0.0, 9.0.0)", - "LogicBuilder.Expressions.Utils": "7.0.0", - "Microsoft.AspNetCore.OData": "9.1.1", - "Microsoft.EntityFrameworkCore": "8.0.11" - }, - "compile": { - "lib/net8.0/AutoMapper.AspNetCore.OData.EFCore.dll": {} - }, - "runtime": { - "lib/net8.0/AutoMapper.AspNetCore.OData.EFCore.dll": {} - }, - "frameworkReferences": [ - "Microsoft.AspNetCore.App" - ] - }, - "AutoMapper.Collection/11.0.0": { - "type": "package", - "dependencies": { - "AutoMapper": "[14.0.0, 15.0.0)" - }, - "compile": { - "lib/net8.0/AutoMapper.Collection.dll": {} - }, - "runtime": { - "lib/net8.0/AutoMapper.Collection.dll": {} - } - }, - "AutoMapper.Collection.EntityFrameworkCore/11.0.0": { - "type": "package", - "dependencies": { - "AutoMapper.Collection": "[11.0.0, 12.0.0)", - "AutoMapper.Extensions.ExpressionMapping": "[8.0.0, 9.0.0)", - "Microsoft.EntityFrameworkCore": "9.0.2" - }, - "compile": { - "lib/net8.0/AutoMapper.Collection.EntityFrameworkCore.dll": {} - }, - "runtime": { - "lib/net8.0/AutoMapper.Collection.EntityFrameworkCore.dll": {} - } - }, - "AutoMapper.Contrib.Autofac.DependencyInjection/9.0.0": { - "type": "package", - "dependencies": { - "AutoMapper": "14.0.0", - "Autofac": "8.2.0" - }, - "compile": { - "lib/net8.0/AutoMapper.Contrib.Autofac.DependencyInjection.dll": { - "related": ".pdb" - } - }, - "runtime": { - "lib/net8.0/AutoMapper.Contrib.Autofac.DependencyInjection.dll": { - "related": ".pdb" - } - } - }, - "AutoMapper.Data/9.0.0": { - "type": "package", - "dependencies": { - "AutoMapper": "[14.0.0, 15.0.0)" - }, - "compile": { - "lib/net8.0/AutoMapper.Data.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net8.0/AutoMapper.Data.dll": { - "related": ".xml" - } - } - }, - "AutoMapper.EF6/3.0.1": { - "type": "package", - "dependencies": { - "AutoMapper": "10.1.1", - "DelegateDecompiler.EntityFramework": "0.34.0", - "EntityFramework": "6.5.1" - }, - "compile": { - "lib/netstandard2.1/AutoMapper.EF6.dll": {} - }, - "runtime": { - "lib/netstandard2.1/AutoMapper.EF6.dll": {} - } - }, - "AutoMapper.Extensions.EnumMapping/4.1.0": { - "type": "package", - "dependencies": { - "AutoMapper": "[14.0.0, 15.0.0)" - }, - "compile": { - "lib/net8.0/AutoMapper.Extensions.EnumMapping.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net8.0/AutoMapper.Extensions.EnumMapping.dll": { - "related": ".xml" - } - } - }, - "AutoMapper.Extensions.ExpressionMapping/8.0.0": { - "type": "package", - "dependencies": { - "AutoMapper": "[14.0.0, 15.0.0)" - }, - "compile": { - "lib/net8.0/AutoMapper.Extensions.ExpressionMapping.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net8.0/AutoMapper.Extensions.ExpressionMapping.dll": { - "related": ".xml" - } - } - }, "AWS.Logger.Core/3.3.3": { "type": "package", "dependencies": { @@ -364,31 +209,6 @@ } } }, - "DelegateDecompiler/0.34.0": { - "type": "package", - "dependencies": { - "Mono.Reflection": "2.0.0" - }, - "compile": { - "lib/netstandard2.1/DelegateDecompiler.dll": {} - }, - "runtime": { - "lib/netstandard2.1/DelegateDecompiler.dll": {} - } - }, - "DelegateDecompiler.EntityFramework/0.34.0": { - "type": "package", - "dependencies": { - "DelegateDecompiler": "0.34.0", - "EntityFramework": "6.3.0" - }, - "compile": { - "lib/netstandard2.1/DelegateDecompiler.EntityFramework.dll": {} - }, - "runtime": { - "lib/netstandard2.1/DelegateDecompiler.EntityFramework.dll": {} - } - }, "DnsClient/1.6.1": { "type": "package", "dependencies": { @@ -427,36 +247,6 @@ "lib/netcoreapp2.0/DumpExtensions.dll": {} } }, - "EntityFramework/6.5.1": { - "type": "package", - "dependencies": { - "Microsoft.CSharp": "4.7.0", - "System.CodeDom": "6.0.0", - "System.ComponentModel.Annotations": "5.0.0", - "System.Configuration.ConfigurationManager": "6.0.1", - "System.Data.SqlClient": "4.8.6" - }, - "compile": { - "lib/netstandard2.1/EntityFramework.SqlServer.dll": { - "related": ".xml" - }, - "lib/netstandard2.1/EntityFramework.dll": { - "related": ".SqlServer.xml;.xml" - } - }, - "runtime": { - "lib/netstandard2.1/EntityFramework.SqlServer.dll": { - "related": ".xml" - }, - "lib/netstandard2.1/EntityFramework.dll": { - "related": ".SqlServer.xml;.xml" - } - }, - "build": { - "buildTransitive/net6.0/EntityFramework.props": {}, - "buildTransitive/net6.0/EntityFramework.targets": {} - } - }, "Google.Protobuf/3.27.1": { "type": "package", "compile": { @@ -653,29 +443,6 @@ } } }, - "LogicBuilder.Expressions.Utils/7.0.0": { - "type": "package", - "dependencies": { - "LogicBuilder.Structures": "7.0.0", - "Microsoft.CSharp": "4.7.0", - "System.Reflection.Emit": "4.7.0" - }, - "compile": { - "lib/netstandard2.0/LogicBuilder.Expressions.Utils.dll": {} - }, - "runtime": { - "lib/netstandard2.0/LogicBuilder.Expressions.Utils.dll": {} - } - }, - "LogicBuilder.Structures/7.0.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/LogicBuilder.Structures.dll": {} - }, - "runtime": { - "lib/netstandard2.0/LogicBuilder.Structures.dll": {} - } - }, "Microsoft.AspNetCore.Authentication.JwtBearer/8.0.2": { "type": "package", "dependencies": { @@ -695,29 +462,10 @@ "Microsoft.AspNetCore.App" ] }, - "Microsoft.AspNetCore.OData/9.1.1": { - "type": "package", - "dependencies": { - "Microsoft.OData.Core": "[8.2.2, 9.0.0)", - "Microsoft.OData.Edm": "[8.2.2, 9.0.0)", - "Microsoft.OData.ModelBuilder": "[2.0.0, 3.0.0)", - "Microsoft.Spatial": "[8.2.2, 9.0.0)" - }, - "compile": { - "lib/net8.0/Microsoft.AspNetCore.OData.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net8.0/Microsoft.AspNetCore.OData.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Bcl.AsyncInterfaces/8.0.0": { + "Microsoft.Bcl.AsyncInterfaces/6.0.0": { "type": "package", "compile": { - "lib/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.dll": { + "lib/netstandard2.1/_._": { "related": ".xml" } }, @@ -989,15 +737,6 @@ } } }, - "Microsoft.CSharp/4.7.0": { - "type": "package", - "compile": { - "ref/netcoreapp2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/_._": {} - } - }, "Microsoft.Diagnostics.NETCore.Client/0.2.621003": { "type": "package", "dependencies": { @@ -1014,13 +753,13 @@ } } }, - "Microsoft.EntityFrameworkCore/9.0.2": { + "Microsoft.EntityFrameworkCore/8.0.13": { "type": "package", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "9.0.2", - "Microsoft.EntityFrameworkCore.Analyzers": "9.0.2", - "Microsoft.Extensions.Caching.Memory": "9.0.2", - "Microsoft.Extensions.Logging": "9.0.2" + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.13", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.13", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" }, "compile": { "lib/net8.0/Microsoft.EntityFrameworkCore.dll": { @@ -1036,7 +775,7 @@ "buildTransitive/net8.0/Microsoft.EntityFrameworkCore.props": {} } }, - "Microsoft.EntityFrameworkCore.Abstractions/9.0.2": { + "Microsoft.EntityFrameworkCore.Abstractions/8.0.13": { "type": "package", "compile": { "lib/net8.0/Microsoft.EntityFrameworkCore.Abstractions.dll": { @@ -1049,16 +788,22 @@ } } }, - "Microsoft.EntityFrameworkCore.Analyzers/9.0.2": { - "type": "package" + "Microsoft.EntityFrameworkCore.Analyzers/8.0.13": { + "type": "package", + "compile": { + "lib/netstandard2.0/_._": {} + }, + "runtime": { + "lib/netstandard2.0/_._": {} + } }, - "Microsoft.EntityFrameworkCore.Design/8.0.2": { + "Microsoft.EntityFrameworkCore.Design/8.0.13": { "type": "package", "dependencies": { "Humanizer.Core": "2.14.1", "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", - "Microsoft.EntityFrameworkCore.Relational": "8.0.2", - "Microsoft.Extensions.DependencyModel": "8.0.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.Extensions.DependencyModel": "8.0.2", "Mono.TextTemplating": "2.2.1" }, "compile": { @@ -1075,10 +820,10 @@ "build/net8.0/Microsoft.EntityFrameworkCore.Design.props": {} } }, - "Microsoft.EntityFrameworkCore.Relational/8.0.2": { + "Microsoft.EntityFrameworkCore.Relational/8.0.13": { "type": "package", "dependencies": { - "Microsoft.EntityFrameworkCore": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" }, "compile": { @@ -1092,10 +837,10 @@ } } }, - "Microsoft.EntityFrameworkCore.Tools/8.0.2": { + "Microsoft.EntityFrameworkCore.Tools/8.0.13": { "type": "package", "dependencies": { - "Microsoft.EntityFrameworkCore.Design": "8.0.2" + "Microsoft.EntityFrameworkCore.Design": "8.0.13" }, "compile": { "lib/net8.0/_._": {} @@ -1104,10 +849,10 @@ "lib/net8.0/_._": {} } }, - "Microsoft.Extensions.Caching.Abstractions/9.0.2": { + "Microsoft.Extensions.Caching.Abstractions/8.0.0": { "type": "package", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.2" + "Microsoft.Extensions.Primitives": "8.0.0" }, "compile": { "lib/net8.0/Microsoft.Extensions.Caching.Abstractions.dll": { @@ -1120,17 +865,17 @@ } }, "build": { - "buildTransitive/net8.0/_._": {} + "buildTransitive/net6.0/_._": {} } }, - "Microsoft.Extensions.Caching.Memory/9.0.2": { + "Microsoft.Extensions.Caching.Memory/8.0.1": { "type": "package", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "9.0.2", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2", - "Microsoft.Extensions.Logging.Abstractions": "9.0.2", - "Microsoft.Extensions.Options": "9.0.2", - "Microsoft.Extensions.Primitives": "9.0.2" + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" }, "compile": { "lib/net8.0/Microsoft.Extensions.Caching.Memory.dll": { @@ -1143,14 +888,14 @@ } }, "build": { - "buildTransitive/net8.0/_._": {} + "buildTransitive/net6.0/_._": {} } }, - "Microsoft.Extensions.Configuration/9.0.4": { + "Microsoft.Extensions.Configuration/8.0.0": { "type": "package", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.4", - "Microsoft.Extensions.Primitives": "9.0.4" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" }, "compile": { "lib/net8.0/Microsoft.Extensions.Configuration.dll": { @@ -1163,13 +908,13 @@ } }, "build": { - "buildTransitive/net8.0/_._": {} + "buildTransitive/net6.0/_._": {} } }, - "Microsoft.Extensions.Configuration.Abstractions/9.0.4": { + "Microsoft.Extensions.Configuration.Abstractions/8.0.0": { "type": "package", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.4" + "Microsoft.Extensions.Primitives": "8.0.0" }, "compile": { "lib/net8.0/Microsoft.Extensions.Configuration.Abstractions.dll": { @@ -1182,7 +927,7 @@ } }, "build": { - "buildTransitive/net8.0/_._": {} + "buildTransitive/net6.0/_._": {} } }, "Microsoft.Extensions.Configuration.Binder/8.0.0": { @@ -1273,10 +1018,10 @@ "buildTransitive/net6.0/Microsoft.Extensions.Configuration.UserSecrets.targets": {} } }, - "Microsoft.Extensions.DependencyInjection/9.0.2": { + "Microsoft.Extensions.DependencyInjection/8.0.1": { "type": "package", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" }, "compile": { "lib/net8.0/Microsoft.Extensions.DependencyInjection.dll": { @@ -1289,10 +1034,10 @@ } }, "build": { - "buildTransitive/net8.0/_._": {} + "buildTransitive/net6.0/_._": {} } }, - "Microsoft.Extensions.DependencyInjection.Abstractions/9.0.4": { + "Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2": { "type": "package", "compile": { "lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { @@ -1305,15 +1050,11 @@ } }, "build": { - "buildTransitive/net8.0/_._": {} + "buildTransitive/net6.0/_._": {} } }, - "Microsoft.Extensions.DependencyModel/8.0.0": { + "Microsoft.Extensions.DependencyModel/8.0.2": { "type": "package", - "dependencies": { - "System.Text.Encodings.Web": "8.0.0", - "System.Text.Json": "8.0.0" - }, "compile": { "lib/net8.0/_._": { "related": ".xml" @@ -1450,12 +1191,12 @@ "buildTransitive/net6.0/_._": {} } }, - "Microsoft.Extensions.Logging/9.0.2": { + "Microsoft.Extensions.Logging/8.0.1": { "type": "package", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "9.0.2", - "Microsoft.Extensions.Logging.Abstractions": "9.0.2", - "Microsoft.Extensions.Options": "9.0.2" + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" }, "compile": { "lib/net8.0/Microsoft.Extensions.Logging.dll": { @@ -1468,14 +1209,13 @@ } }, "build": { - "buildTransitive/net8.0/_._": {} + "buildTransitive/net6.0/_._": {} } }, - "Microsoft.Extensions.Logging.Abstractions/9.0.2": { + "Microsoft.Extensions.Logging.Abstractions/8.0.3": { "type": "package", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2", - "System.Diagnostics.DiagnosticSource": "9.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" }, "compile": { "lib/net8.0/Microsoft.Extensions.Logging.Abstractions.dll": { @@ -1488,27 +1228,14 @@ } }, "build": { - "buildTransitive/net8.0/Microsoft.Extensions.Logging.Abstractions.targets": {} + "buildTransitive/net6.0/Microsoft.Extensions.Logging.Abstractions.targets": {} } }, - "Microsoft.Extensions.ObjectPool/6.0.3": { - "type": "package", - "compile": { - "lib/net6.0/Microsoft.Extensions.ObjectPool.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net6.0/Microsoft.Extensions.ObjectPool.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Options/9.0.4": { + "Microsoft.Extensions.Options/8.0.2": { "type": "package", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.4", - "Microsoft.Extensions.Primitives": "9.0.4" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" }, "compile": { "lib/net8.0/Microsoft.Extensions.Options.dll": { @@ -1521,7 +1248,7 @@ } }, "build": { - "buildTransitive/net8.0/Microsoft.Extensions.Options.targets": {} + "buildTransitive/net6.0/Microsoft.Extensions.Options.targets": {} } }, "Microsoft.Extensions.Options.ConfigurationExtensions/8.0.0": { @@ -1547,7 +1274,7 @@ "buildTransitive/net6.0/_._": {} } }, - "Microsoft.Extensions.Primitives/9.0.4": { + "Microsoft.Extensions.Primitives/8.0.0": { "type": "package", "compile": { "lib/net8.0/Microsoft.Extensions.Primitives.dll": { @@ -1560,7 +1287,7 @@ } }, "build": { - "buildTransitive/net8.0/_._": {} + "buildTransitive/net6.0/_._": {} } }, "Microsoft.IdentityModel.Abstractions/8.9.0": { @@ -1660,59 +1387,13 @@ } } }, - "Microsoft.OData.Core/8.2.2": { + "Microsoft.NETCore.Platforms/5.0.0": { "type": "package", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.ObjectPool": "6.0.3", - "Microsoft.OData.Edm": "[8.2.2]", - "Microsoft.Spatial": "[8.2.2]" - }, "compile": { - "lib/net8.0/Microsoft.OData.Core.dll": { - "related": ".xml" - } + "lib/netstandard1.0/_._": {} }, "runtime": { - "lib/net8.0/Microsoft.OData.Core.dll": { - "related": ".xml" - } - } - }, - "Microsoft.OData.Edm/8.2.2": { - "type": "package", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.6.0" - }, - "compile": { - "lib/net8.0/Microsoft.OData.Edm.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net8.0/Microsoft.OData.Edm.dll": { - "related": ".xml" - } - } - }, - "Microsoft.OData.ModelBuilder/2.0.0": { - "type": "package", - "dependencies": { - "Microsoft.OData.Edm": "[8.0.0, 9.0.0)", - "Microsoft.Spatial": "[8.0.0, 9.0.0)", - "System.ComponentModel.Annotations": "4.6.0" - }, - "compile": { - "lib/net8.0/Microsoft.OData.ModelBuilder.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net8.0/Microsoft.OData.ModelBuilder.dll": { - "related": ".xml" - } + "lib/netstandard1.0/_._": {} } }, "Microsoft.OpenApi/1.6.22": { @@ -1728,19 +1409,6 @@ } } }, - "Microsoft.Spatial/8.2.2": { - "type": "package", - "compile": { - "lib/net8.0/Microsoft.Spatial.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net8.0/Microsoft.Spatial.dll": { - "related": ".xml" - } - } - }, "Microsoft.Win32.Registry/5.0.0": { "type": "package", "dependencies": { @@ -1764,28 +1432,6 @@ } } }, - "Microsoft.Win32.SystemEvents/6.0.0": { - "type": "package", - "compile": { - "lib/net6.0/Microsoft.Win32.SystemEvents.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net6.0/Microsoft.Win32.SystemEvents.dll": { - "related": ".xml" - } - }, - "build": { - "buildTransitive/netcoreapp3.1/_._": {} - }, - "runtimeTargets": { - "runtimes/win/lib/net6.0/Microsoft.Win32.SystemEvents.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, "MongoDB.Analyzer/1.5.0": { "type": "package" }, @@ -1828,19 +1474,6 @@ } } }, - "Mono.Reflection/2.0.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Mono.Reflection.dll": { - "related": ".pdb" - } - }, - "runtime": { - "lib/netstandard2.0/Mono.Reflection.dll": { - "related": ".pdb" - } - } - }, "Mono.TextTemplating/2.2.1": { "type": "package", "dependencies": { @@ -2104,10 +1737,10 @@ } } }, - "Pomelo.EntityFrameworkCore.MySql/8.0.2": { + "Pomelo.EntityFrameworkCore.MySql/8.0.3": { "type": "package", "dependencies": { - "Microsoft.EntityFrameworkCore.Relational": "[8.0.2, 8.0.999]", + "Microsoft.EntityFrameworkCore.Relational": "[8.0.13, 8.0.999]", "MySqlConnector": "2.3.5" }, "compile": { @@ -2138,41 +1771,6 @@ } } }, - "runtime.native.System.Data.SqlClient.sni/4.7.0": { - "type": "package", - "dependencies": { - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0" - } - }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "runtimeTargets": { - "runtimes/win-arm64/native/sni.dll": { - "assetType": "native", - "rid": "win-arm64" - } - } - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "runtimeTargets": { - "runtimes/win-x64/native/sni.dll": { - "assetType": "native", - "rid": "win-x64" - } - } - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "runtimeTargets": { - "runtimes/win-x86/native/sni.dll": { - "assetType": "native", - "rid": "win-x86" - } - } - }, "SharpCompress/0.30.1": { "type": "package", "compile": { @@ -2327,20 +1925,15 @@ "lib/netcoreapp2.0/_._": {} } }, - "System.CodeDom/6.0.0": { + "System.CodeDom/4.4.0": { "type": "package", "compile": { - "lib/net6.0/System.CodeDom.dll": { + "ref/netstandard2.0/_._": { "related": ".xml" } }, "runtime": { - "lib/net6.0/System.CodeDom.dll": { - "related": ".xml" - } - }, - "build": { - "buildTransitive/netcoreapp3.1/_._": {} + "lib/netstandard2.0/System.CodeDom.dll": {} } }, "System.Collections.Immutable/6.0.0": { @@ -2362,19 +1955,6 @@ "buildTransitive/netcoreapp3.1/_._": {} } }, - "System.ComponentModel.Annotations/5.0.0": { - "type": "package", - "compile": { - "ref/netstandard2.1/System.ComponentModel.Annotations.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.1/System.ComponentModel.Annotations.dll": { - "related": ".xml" - } - } - }, "System.Composition/6.0.0": { "type": "package", "dependencies": { @@ -2479,55 +2059,7 @@ "buildTransitive/netcoreapp3.1/_._": {} } }, - "System.Configuration.ConfigurationManager/6.0.1": { - "type": "package", - "dependencies": { - "System.Security.Cryptography.ProtectedData": "6.0.0", - "System.Security.Permissions": "6.0.0" - }, - "compile": { - "lib/net6.0/System.Configuration.ConfigurationManager.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net6.0/System.Configuration.ConfigurationManager.dll": { - "related": ".xml" - } - }, - "build": { - "buildTransitive/netcoreapp3.1/_._": {} - } - }, - "System.Data.SqlClient/4.8.6": { - "type": "package", - "dependencies": { - "Microsoft.Win32.Registry": "4.7.0", - "System.Security.Principal.Windows": "4.7.0", - "runtime.native.System.Data.SqlClient.sni": "4.7.0" - }, - "compile": { - "ref/netcoreapp2.1/System.Data.SqlClient.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp2.1/System.Data.SqlClient.dll": { - "related": ".xml" - } - }, - "runtimeTargets": { - "runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Diagnostics.DiagnosticSource/9.0.2": { + "System.Diagnostics.DiagnosticSource/8.0.0": { "type": "package", "compile": { "lib/net8.0/System.Diagnostics.DiagnosticSource.dll": { @@ -2539,44 +2071,8 @@ "related": ".xml" } }, - "contentFiles": { - "contentFiles/any/any/_._": { - "buildAction": "None", - "codeLanguage": "any", - "copyToOutput": false - } - }, "build": { - "buildTransitive/net8.0/_._": {} - } - }, - "System.Drawing.Common/6.0.0": { - "type": "package", - "dependencies": { - "Microsoft.Win32.SystemEvents": "6.0.0" - }, - "compile": { - "lib/net6.0/System.Drawing.Common.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net6.0/System.Drawing.Common.dll": { - "related": ".xml" - } - }, - "build": { - "buildTransitive/netcoreapp3.1/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/net6.0/System.Drawing.Common.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/net6.0/System.Drawing.Common.dll": { - "assetType": "runtime", - "rid": "win" - } + "buildTransitive/net6.0/_._": {} } }, "System.IdentityModel.Tokens.Jwt/8.9.0": { @@ -2637,15 +2133,6 @@ "buildTransitive/net6.0/_._": {} } }, - "System.Reflection.Emit/4.7.0": { - "type": "package", - "compile": { - "ref/netcoreapp2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/_._": {} - } - }, "System.Reflection.Metadata/6.0.1": { "type": "package", "dependencies": { @@ -2681,68 +2168,27 @@ "buildTransitive/netcoreapp3.1/_._": {} } }, - "System.Security.AccessControl/6.0.0": { - "type": "package", - "compile": { - "lib/net6.0/System.Security.AccessControl.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net6.0/System.Security.AccessControl.dll": { - "related": ".xml" - } - }, - "build": { - "buildTransitive/netcoreapp3.1/_._": {} - }, - "runtimeTargets": { - "runtimes/win/lib/net6.0/System.Security.AccessControl.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.ProtectedData/6.0.0": { - "type": "package", - "compile": { - "lib/net6.0/System.Security.Cryptography.ProtectedData.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net6.0/System.Security.Cryptography.ProtectedData.dll": { - "related": ".xml" - } - }, - "build": { - "buildTransitive/netcoreapp3.1/_._": {} - }, - "runtimeTargets": { - "runtimes/win/lib/net6.0/System.Security.Cryptography.ProtectedData.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Permissions/6.0.0": { + "System.Security.AccessControl/5.0.0": { "type": "package", "dependencies": { - "System.Security.AccessControl": "6.0.0", - "System.Windows.Extensions": "6.0.0" + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" }, "compile": { - "lib/net6.0/System.Security.Permissions.dll": { + "ref/netstandard2.0/System.Security.AccessControl.dll": { "related": ".xml" } }, "runtime": { - "lib/net6.0/System.Security.Permissions.dll": { + "lib/netstandard2.0/System.Security.AccessControl.dll": { "related": ".xml" } }, - "build": { - "buildTransitive/netcoreapp3.1/_._": {} + "runtimeTargets": { + "runtimes/win/lib/netcoreapp2.0/System.Security.AccessControl.dll": { + "assetType": "runtime", + "rid": "win" + } } }, "System.Security.Principal.Windows/5.0.0": { @@ -2850,26 +2296,17 @@ "buildTransitive/net6.0/_._": {} } }, - "System.Windows.Extensions/6.0.0": { + "Ulid/1.3.4": { "type": "package", - "dependencies": { - "System.Drawing.Common": "6.0.0" - }, "compile": { - "lib/net6.0/System.Windows.Extensions.dll": { + "lib/net8.0/Ulid.dll": { "related": ".xml" } }, "runtime": { - "lib/net6.0/System.Windows.Extensions.dll": { + "lib/net8.0/Ulid.dll": { "related": ".xml" } - }, - "runtimeTargets": { - "runtimes/win/lib/net6.0/System.Windows.Extensions.dll": { - "assetType": "runtime", - "rid": "win" - } } }, "YamlDotNet/16.3.0": { @@ -2979,7 +2416,8 @@ "Protocol": "1.0.0", "ServerBase": "1.0.0", "ServerCore": "1.0.0", - "UGQDatabase": "1.0.0" + "UGQDatabase": "1.0.0", + "Ulid": "1.3.4" }, "compile": { "bin/placeholder/ServerCommon.dll": {} @@ -2999,15 +2437,6 @@ "AWSSDK.S3": "3.7.416.15", "AWSSDK.SecurityToken": "3.7.401.89", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", "Axion.ConcurrentHashSet": "1.0.0", "BCrypt.Net-Next": "4.0.3", "BouncyCastle.Cryptography": "2.5.1", @@ -3017,8 +2446,8 @@ "Google.Protobuf": "3.27.1", "JWT": "11.0.0", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.Extensions.Configuration": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Options": "8.0.2", "MongoDB.Analyzer": "1.5.0", "MongoDB.Bson": "3.3.0", "MongoDB.Driver": "3.3.0", @@ -3077,156 +2506,6 @@ "lib/netstandard2.0/AsyncStateMachine.dll" ] }, - "Autofac/8.2.0": { - "sha512": "T+4+W4byzyUOarCbIcFRbxpYKC+cndfQm/+VeWpB60P2MCN0JMsewUhZqvH5Ooe936HQjn5uHvEY9tq6BfbiIw==", - "type": "package", - "path": "autofac/8.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "README.md", - "autofac.8.2.0.nupkg.sha512", - "autofac.nuspec", - "icon.png", - "lib/net6.0/Autofac.dll", - "lib/net6.0/Autofac.xml", - "lib/net7.0/Autofac.dll", - "lib/net7.0/Autofac.xml", - "lib/net8.0/Autofac.dll", - "lib/net8.0/Autofac.xml", - "lib/netstandard2.0/Autofac.dll", - "lib/netstandard2.0/Autofac.xml", - "lib/netstandard2.1/Autofac.dll", - "lib/netstandard2.1/Autofac.xml" - ] - }, - "AutoMapper/14.0.0": { - "sha512": "OC+1neAPM4oCCqQj3g2GJ2shziNNhOkxmNB9cVS8jtx4JbgmRzLcUOxB9Tsz6cVPHugdkHgCaCrTjjSI0Z5sCQ==", - "type": "package", - "path": "automapper/14.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "README.md", - "automapper.14.0.0.nupkg.sha512", - "automapper.nuspec", - "icon.png", - "lib/net8.0/AutoMapper.dll", - "lib/net8.0/AutoMapper.xml" - ] - }, - "AutoMapper.AspNetCore.OData.EFCore/7.0.1": { - "sha512": "VfJFmLdPjdKX1gOlUx9+n1hdi9tFadsEDd28iSsTy26SdOe8AuV9fWozVMXg6663qBbw/7LiaPP19VgRlfFkOA==", - "type": "package", - "path": "automapper.aspnetcore.odata.efcore/7.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "README.md", - "automapper.aspnetcore.odata.efcore.7.0.1.nupkg.sha512", - "automapper.aspnetcore.odata.efcore.nuspec", - "icon.png", - "lib/net8.0/AutoMapper.AspNetCore.OData.EFCore.dll", - "lib/net9.0/AutoMapper.AspNetCore.OData.EFCore.dll" - ] - }, - "AutoMapper.Collection/11.0.0": { - "sha512": "26CXTuPsIbfk121na1sWt/QcJ4EkXtlWy6R+LJFvsxiF80d2srbUgL0WgZn9mDyrJo4Hvvyqr/WFwNrVOKMiHQ==", - "type": "package", - "path": "automapper.collection/11.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "automapper.collection.11.0.0.nupkg.sha512", - "automapper.collection.nuspec", - "icon.png", - "lib/net8.0/AutoMapper.Collection.dll" - ] - }, - "AutoMapper.Collection.EntityFrameworkCore/11.0.0": { - "sha512": "+IHCGSLfdc2pNXEUF2Kz8P1I1anCqPlEGWswMzTz1tZ2eRod9bY609TS55dg2NlS8e9CKebIoT3e/c4m90jCoQ==", - "type": "package", - "path": "automapper.collection.entityframeworkcore/11.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "automapper.collection.entityframeworkcore.11.0.0.nupkg.sha512", - "automapper.collection.entityframeworkcore.nuspec", - "icon.png", - "lib/net8.0/AutoMapper.Collection.EntityFrameworkCore.dll" - ] - }, - "AutoMapper.Contrib.Autofac.DependencyInjection/9.0.0": { - "sha512": "vomQXQ4vqWUhl5tpbUbD3200BtlVJxdAUdCMa08a/vzRL6S9MJb2mN6EplaXXA3FLH1a3lPT1KpiHu6AlooQ/w==", - "type": "package", - "path": "automapper.contrib.autofac.dependencyinjection/9.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "automapper.contrib.autofac.dependencyinjection.9.0.0.nupkg.sha512", - "automapper.contrib.autofac.dependencyinjection.nuspec", - "icon.png", - "lib/net8.0/AutoMapper.Contrib.Autofac.DependencyInjection.dll", - "lib/net8.0/AutoMapper.Contrib.Autofac.DependencyInjection.pdb" - ] - }, - "AutoMapper.Data/9.0.0": { - "sha512": "OI0+nHZaVGZCys2Nz7YV1I5ZjKjuTM2NXlAUmd2Nx68s4qX5m8piUfSapVjxInoQDf5PJ7Mox7a1lEZHpSHYpg==", - "type": "package", - "path": "automapper.data/9.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "README.md", - "automapper.data.9.0.0.nupkg.sha512", - "automapper.data.nuspec", - "lib/net8.0/AutoMapper.Data.dll", - "lib/net8.0/AutoMapper.Data.xml" - ] - }, - "AutoMapper.EF6/3.0.1": { - "sha512": "4w8gqn6NixT69TmH5Iup2nFoC6bYtjwh/QozOvOEbnu6imnegZE2DE4EqEG6Fis1dKKJ6WT9NCbiWC4NZv7Abw==", - "type": "package", - "path": "automapper.ef6/3.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "automapper.ef6.3.0.1.nupkg.sha512", - "automapper.ef6.nuspec", - "lib/net462/AutoMapper.EF6.dll", - "lib/netstandard2.1/AutoMapper.EF6.dll" - ] - }, - "AutoMapper.Extensions.EnumMapping/4.1.0": { - "sha512": "4i6FVLOequZNLgU6CRFbT900tBHb6JxyJty7Q8Rf7wMiwIBy/PkjsOT7Vz6svacFykghEy7PURSxpO9Fl2OUsA==", - "type": "package", - "path": "automapper.extensions.enummapping/4.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "README.md", - "automapper.extensions.enummapping.4.1.0.nupkg.sha512", - "automapper.extensions.enummapping.nuspec", - "icon.png", - "lib/net8.0/AutoMapper.Extensions.EnumMapping.dll", - "lib/net8.0/AutoMapper.Extensions.EnumMapping.xml" - ] - }, - "AutoMapper.Extensions.ExpressionMapping/8.0.0": { - "sha512": "fR55QEOt8GiaVQPuEpyWJAzZxFSInCIYg6E1EoqQByWnjzQnQ5zHwQvMMF3E+AdtvAzbG7Intm08/8pK8eUTvQ==", - "type": "package", - "path": "automapper.extensions.expressionmapping/8.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "README.md", - "automapper.extensions.expressionmapping.8.0.0.nupkg.sha512", - "automapper.extensions.expressionmapping.nuspec", - "icon.png", - "lib/net8.0/AutoMapper.Extensions.ExpressionMapping.dll", - "lib/net8.0/AutoMapper.Extensions.ExpressionMapping.xml" - ] - }, "AWS.Logger.Core/3.3.3": { "sha512": "JEXjiwIwAws5YJZDVwSqUoZIjMxsAuaZOQFZfLk6lYTg9NLrFY1dHafy6qifz/8ZQSlBiuSs+MAAHlBqHF9Cfw==", "type": "package", @@ -3576,36 +2855,6 @@ "lib/netstandard2.0/CommandLine.xml" ] }, - "DelegateDecompiler/0.34.0": { - "sha512": "blUx3XMP4lJynw2y34NrnHzrshF2YGOPirxVUhHnAT09L/LwQc8Q6szmNGFi9hLd9AcM4nVAmXIX2DDyqCfwLg==", - "type": "package", - "path": "delegatedecompiler/0.34.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "README.md", - "delegatedecompiler.0.34.0.nupkg.sha512", - "delegatedecompiler.nuspec", - "lib/net40/DelegateDecompiler.dll", - "lib/net45/DelegateDecompiler.dll", - "lib/netstandard2.0/DelegateDecompiler.dll", - "lib/netstandard2.1/DelegateDecompiler.dll" - ] - }, - "DelegateDecompiler.EntityFramework/0.34.0": { - "sha512": "U55TkUKhrAsssd+cgXsEZqYXdT20nJBlEOBdLInsCRt1UghtWhYNZqbnR9oPIV4phrtgmkqj/6kXhfdczYNn4g==", - "type": "package", - "path": "delegatedecompiler.entityframework/0.34.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "README.md", - "delegatedecompiler.entityframework.0.34.0.nupkg.sha512", - "delegatedecompiler.entityframework.nuspec", - "lib/net45/DelegateDecompiler.EntityFramework.dll", - "lib/netstandard2.1/DelegateDecompiler.EntityFramework.dll" - ] - }, "DnsClient/1.6.1": { "sha512": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", "type": "package", @@ -3657,68 +2906,6 @@ "lib/netstandard2.0/DumpExtensions.dll" ] }, - "EntityFramework/6.5.1": { - "sha512": "sQRP2lWg1i3aAGWqdliAM8zrGx7LHMUk+9/MoxUjwfTZYGMXvZ2JYZTlyTm1PqDxvn3c9E3U76TWDON7Y5+CVA==", - "type": "package", - "path": "entityframework/6.5.1", - "hasTools": true, - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "README.md", - "build/EntityFramework.DefaultItems.props", - "build/EntityFramework.props", - "build/EntityFramework.targets", - "build/Microsoft.Data.Entity.Build.Tasks.dll", - "build/net6.0/EntityFramework.props", - "build/net6.0/EntityFramework.targets", - "buildTransitive/EntityFramework.props", - "buildTransitive/EntityFramework.targets", - "buildTransitive/net6.0/EntityFramework.props", - "buildTransitive/net6.0/EntityFramework.targets", - "content/net40/App.config.install.xdt", - "content/net40/App.config.transform", - "content/net40/Web.config.install.xdt", - "content/net40/Web.config.transform", - "entityframework.6.5.1.nupkg.sha512", - "entityframework.nuspec", - "lib/net40/EntityFramework.SqlServer.dll", - "lib/net40/EntityFramework.SqlServer.xml", - "lib/net40/EntityFramework.dll", - "lib/net40/EntityFramework.xml", - "lib/net45/EntityFramework.SqlServer.dll", - "lib/net45/EntityFramework.SqlServer.xml", - "lib/net45/EntityFramework.dll", - "lib/net45/EntityFramework.xml", - "lib/netstandard2.1/EntityFramework.SqlServer.dll", - "lib/netstandard2.1/EntityFramework.SqlServer.xml", - "lib/netstandard2.1/EntityFramework.dll", - "lib/netstandard2.1/EntityFramework.xml", - "tools/EntityFramework6.PS2.psd1", - "tools/EntityFramework6.PS2.psm1", - "tools/EntityFramework6.psd1", - "tools/EntityFramework6.psm1", - "tools/about_EntityFramework6.help.txt", - "tools/init.ps1", - "tools/install.ps1", - "tools/net40/any/ef6.exe", - "tools/net40/any/ef6.pdb", - "tools/net40/win-arm64/ef6.exe", - "tools/net40/win-arm64/ef6.pdb", - "tools/net40/win-x86/ef6.exe", - "tools/net40/win-x86/ef6.pdb", - "tools/net45/any/ef6.exe", - "tools/net45/any/ef6.pdb", - "tools/net45/win-arm64/ef6.exe", - "tools/net45/win-arm64/ef6.pdb", - "tools/net45/win-x86/ef6.exe", - "tools/net45/win-x86/ef6.pdb", - "tools/net6.0/any/ef6.dll", - "tools/net6.0/any/ef6.pdb", - "tools/net6.0/any/ef6.runtimeconfig.json" - ] - }, "Google.Protobuf/3.27.1": { "sha512": "7IVz9TzhYCZ8qY0rPhXUnyJSXYdshUqmmxmTI763XmDDSJJFnyfKH43FFcMJu/CZgBcE98xlFztrKwhzcRkiPg==", "type": "package", @@ -4000,32 +3187,6 @@ "lib/netstandard2.0/JWT.xml" ] }, - "LogicBuilder.Expressions.Utils/7.0.0": { - "sha512": "nTlL/5nGPHZ0s987Kta4lFnzU79Px/9R9rFhJi6AxCMHZUz5BUX4Aab1A29iBTLmIMu2DKyQM8iN8q1NK3+CUA==", - "type": "package", - "path": "logicbuilder.expressions.utils/7.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "README.md", - "lib/netstandard2.0/LogicBuilder.Expressions.Utils.dll", - "logicbuilder.expressions.utils.7.0.0.nupkg.sha512", - "logicbuilder.expressions.utils.nuspec" - ] - }, - "LogicBuilder.Structures/7.0.0": { - "sha512": "KDfzBB7cYk2ras9Nb3xYXuUKs+rvje0vVyGzkXPQCpKsSbugFi2fXz+0MIQ108K+f/mOde7ZBECkkmYObyPf2Q==", - "type": "package", - "path": "logicbuilder.structures/7.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "README.md", - "lib/netstandard2.0/LogicBuilder.Structures.dll", - "logicbuilder.structures.7.0.0.nupkg.sha512", - "logicbuilder.structures.nuspec" - ] - }, "Microsoft.AspNetCore.Authentication.JwtBearer/8.0.2": { "sha512": "7qJkk5k5jabATZZrMIQgpUB9yjDNAAApSqw+8d0FEyK1AJ4j+wv1qOMl2byUr837xbK+MjehtPnQ32yZ5Gtzlw==", "type": "package", @@ -4041,40 +3202,23 @@ "microsoft.aspnetcore.authentication.jwtbearer.nuspec" ] }, - "Microsoft.AspNetCore.OData/9.1.1": { - "sha512": "if1N+q3dTgUbUZ4KjLXCHYDQLKEEv0gpx8dLaSRp2StrYuWcRVCKjncI7fPcndFkt/nEJcIYzNKxtws4L5OO8Q==", + "Microsoft.Bcl.AsyncInterfaces/6.0.0": { + "sha512": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==", "type": "package", - "path": "microsoft.aspnetcore.odata/9.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "images/odata.png", - "lib/net8.0/Microsoft.AspNetCore.OData.dll", - "lib/net8.0/Microsoft.AspNetCore.OData.xml", - "microsoft.aspnetcore.odata.9.1.1.nupkg.sha512", - "microsoft.aspnetcore.odata.nuspec" - ] - }, - "Microsoft.Bcl.AsyncInterfaces/8.0.0": { - "sha512": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", - "type": "package", - "path": "microsoft.bcl.asyncinterfaces/8.0.0", + "path": "microsoft.bcl.asyncinterfaces/6.0.0", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", "LICENSE.TXT", - "PACKAGE.md", "THIRD-PARTY-NOTICES.TXT", - "buildTransitive/net461/Microsoft.Bcl.AsyncInterfaces.targets", - "buildTransitive/net462/_._", - "lib/net462/Microsoft.Bcl.AsyncInterfaces.dll", - "lib/net462/Microsoft.Bcl.AsyncInterfaces.xml", + "lib/net461/Microsoft.Bcl.AsyncInterfaces.dll", + "lib/net461/Microsoft.Bcl.AsyncInterfaces.xml", "lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.dll", "lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.xml", "lib/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.dll", "lib/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.xml", - "microsoft.bcl.asyncinterfaces.8.0.0.nupkg.sha512", + "microsoft.bcl.asyncinterfaces.6.0.0.nupkg.sha512", "microsoft.bcl.asyncinterfaces.nuspec", "useSharedDesignerContext.txt" ] @@ -4540,75 +3684,6 @@ "microsoft.codeanalysis.workspaces.common.nuspec" ] }, - "Microsoft.CSharp/4.7.0": { - "sha512": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==", - "type": "package", - "path": "microsoft.csharp/4.7.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/Microsoft.CSharp.dll", - "lib/netcoreapp2.0/_._", - "lib/netstandard1.3/Microsoft.CSharp.dll", - "lib/netstandard2.0/Microsoft.CSharp.dll", - "lib/netstandard2.0/Microsoft.CSharp.xml", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/uap10.0.16299/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "microsoft.csharp.4.7.0.nupkg.sha512", - "microsoft.csharp.nuspec", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/Microsoft.CSharp.dll", - "ref/netcore50/Microsoft.CSharp.xml", - "ref/netcore50/de/Microsoft.CSharp.xml", - "ref/netcore50/es/Microsoft.CSharp.xml", - "ref/netcore50/fr/Microsoft.CSharp.xml", - "ref/netcore50/it/Microsoft.CSharp.xml", - "ref/netcore50/ja/Microsoft.CSharp.xml", - "ref/netcore50/ko/Microsoft.CSharp.xml", - "ref/netcore50/ru/Microsoft.CSharp.xml", - "ref/netcore50/zh-hans/Microsoft.CSharp.xml", - "ref/netcore50/zh-hant/Microsoft.CSharp.xml", - "ref/netcoreapp2.0/_._", - "ref/netstandard1.0/Microsoft.CSharp.dll", - "ref/netstandard1.0/Microsoft.CSharp.xml", - "ref/netstandard1.0/de/Microsoft.CSharp.xml", - "ref/netstandard1.0/es/Microsoft.CSharp.xml", - "ref/netstandard1.0/fr/Microsoft.CSharp.xml", - "ref/netstandard1.0/it/Microsoft.CSharp.xml", - "ref/netstandard1.0/ja/Microsoft.CSharp.xml", - "ref/netstandard1.0/ko/Microsoft.CSharp.xml", - "ref/netstandard1.0/ru/Microsoft.CSharp.xml", - "ref/netstandard1.0/zh-hans/Microsoft.CSharp.xml", - "ref/netstandard1.0/zh-hant/Microsoft.CSharp.xml", - "ref/netstandard2.0/Microsoft.CSharp.dll", - "ref/netstandard2.0/Microsoft.CSharp.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/uap10.0.16299/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, "Microsoft.Diagnostics.NETCore.Client/0.2.621003": { "sha512": "22YdSeB53xk4Pd1Xz92HigUy8ySJMzd8hpEZHlFWeaefzYox4VJnW+gJEzGrxbSvl3oGCpiIFKqh8the0xhzSA==", "type": "package", @@ -4625,10 +3700,10 @@ "microsoft.diagnostics.netcore.client.nuspec" ] }, - "Microsoft.EntityFrameworkCore/9.0.2": { - "sha512": "P90ZuybgcpW32y985eOYxSoZ9IiL0UTYQlY0y1Pt1iHAnpZj/dQHREpSpry1RNvk8YjAeoAkWFdem5conqB9zQ==", + "Microsoft.EntityFrameworkCore/8.0.13": { + "sha512": "4wyLeg64sZgJcER83fkitlKuySY3OzwcOZSZcGV1TNskmThupfhcVlsQht7bNCfNpXTCZVNIOwAZChk6/OFdXQ==", "type": "package", - "path": "microsoft.entityframeworkcore/9.0.2", + "path": "microsoft.entityframeworkcore/8.0.13", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -4637,14 +3712,14 @@ "buildTransitive/net8.0/Microsoft.EntityFrameworkCore.props", "lib/net8.0/Microsoft.EntityFrameworkCore.dll", "lib/net8.0/Microsoft.EntityFrameworkCore.xml", - "microsoft.entityframeworkcore.9.0.2.nupkg.sha512", + "microsoft.entityframeworkcore.8.0.13.nupkg.sha512", "microsoft.entityframeworkcore.nuspec" ] }, - "Microsoft.EntityFrameworkCore.Abstractions/9.0.2": { - "sha512": "oVSjNSIYHsk0N66eqAWgDcyo9etEFbUswbz7SmlYR6nGp05byHrJAYM5N8U2aGWJWJI6WvIC2e4TXJgH6GZ6HQ==", + "Microsoft.EntityFrameworkCore.Abstractions/8.0.13": { + "sha512": "a6O+v8CMStumpVoQyN0aUpx5P3/qYZP+5791sGrzJhIM+QsniQdSaJVdUSneL40M+FpBgtiC8ggqzNo2x/gVlw==", "type": "package", - "path": "microsoft.entityframeworkcore.abstractions/9.0.2", + "path": "microsoft.entityframeworkcore.abstractions/8.0.13", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -4652,64 +3727,68 @@ "PACKAGE.md", "lib/net8.0/Microsoft.EntityFrameworkCore.Abstractions.dll", "lib/net8.0/Microsoft.EntityFrameworkCore.Abstractions.xml", - "microsoft.entityframeworkcore.abstractions.9.0.2.nupkg.sha512", + "microsoft.entityframeworkcore.abstractions.8.0.13.nupkg.sha512", "microsoft.entityframeworkcore.abstractions.nuspec" ] }, - "Microsoft.EntityFrameworkCore.Analyzers/9.0.2": { - "sha512": "w4jzX7XI+L3erVGzbHXpx64A3QaLXxqG3f1vPpGYYZGpxOIHkh7e4iLLD7cq4Ng1vjkwzWl5ZJp0Kj/nHsgFYg==", + "Microsoft.EntityFrameworkCore.Analyzers/8.0.13": { + "sha512": "ENny33QADFiRxge5ikJBs2p7JB4MQx/ZSPKPNwt2eJPO0lVUGSjTiJeunGGggkVHiaUy0yY4f625LQtNlErIsw==", "type": "package", - "path": "microsoft.entityframeworkcore.analyzers/9.0.2", + "path": "microsoft.entityframeworkcore.analyzers/8.0.13", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", "analyzers/dotnet/cs/Microsoft.EntityFrameworkCore.Analyzers.dll", "docs/PACKAGE.md", - "microsoft.entityframeworkcore.analyzers.9.0.2.nupkg.sha512", + "lib/netstandard2.0/_._", + "microsoft.entityframeworkcore.analyzers.8.0.13.nupkg.sha512", "microsoft.entityframeworkcore.analyzers.nuspec" ] }, - "Microsoft.EntityFrameworkCore.Design/8.0.2": { - "sha512": "lpSEopadyq4VjgErVbKXznlzmrdR+1zG4jjJlumgnDTz6Ov60qZkBn8uTfPYk0PUZ3wn+GNFOi3ouSTK4JKEIA==", + "Microsoft.EntityFrameworkCore.Design/8.0.13": { + "sha512": "lQ+ck65jBgUc4VdQFzmeE7rofw3VCW4vkLZKp3ntz1GONw8Upsvhg+bpRiMjz4HvArxoGaUZz2XQ6vg5Im0uUQ==", "type": "package", - "path": "microsoft.entityframeworkcore.design/8.0.2", + "path": "microsoft.entityframeworkcore.design/8.0.13", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", + "PACKAGE.md", "build/net8.0/Microsoft.EntityFrameworkCore.Design.props", "lib/net8.0/Microsoft.EntityFrameworkCore.Design.dll", "lib/net8.0/Microsoft.EntityFrameworkCore.Design.xml", - "microsoft.entityframeworkcore.design.8.0.2.nupkg.sha512", + "microsoft.entityframeworkcore.design.8.0.13.nupkg.sha512", "microsoft.entityframeworkcore.design.nuspec" ] }, - "Microsoft.EntityFrameworkCore.Relational/8.0.2": { - "sha512": "NoGfcq2OPw0z8XAPf74YFwGlTKjedWdsIEJqq4SvKcPjcu+B+/XDDNrDRxTvILfz4Ug8POSF49s1jz1JvUqTAg==", + "Microsoft.EntityFrameworkCore.Relational/8.0.13": { + "sha512": "uQR2iTar+6ZEjEHTwgH0/7ySSRme4R9sDiupfG3w/eBub3365fPw/MjhsuOMQkdq9YzLM7veH4Qt/K9OqL26Qg==", "type": "package", - "path": "microsoft.entityframeworkcore.relational/8.0.2", + "path": "microsoft.entityframeworkcore.relational/8.0.13", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", + "PACKAGE.md", "lib/net8.0/Microsoft.EntityFrameworkCore.Relational.dll", "lib/net8.0/Microsoft.EntityFrameworkCore.Relational.xml", - "microsoft.entityframeworkcore.relational.8.0.2.nupkg.sha512", + "microsoft.entityframeworkcore.relational.8.0.13.nupkg.sha512", "microsoft.entityframeworkcore.relational.nuspec" ] }, - "Microsoft.EntityFrameworkCore.Tools/8.0.2": { - "sha512": "PWy3X3Z1fnWlbU6pQMSnBvMwqERoKsriJ688TMl1xT2NyqcSk6/dX22eI5eV+qYXYmYna72Dq2u0P8tNZ6AYtg==", + "Microsoft.EntityFrameworkCore.Tools/8.0.13": { + "sha512": "mbCcyX6ImsCcK2OU2qEQKLWaHn91Z7P/KlQSKCZt+rxRMM+d94jV3SR3KKSchA4TzAehZaDKeQlLxuToEX15gQ==", "type": "package", - "path": "microsoft.entityframeworkcore.tools/8.0.2", + "path": "microsoft.entityframeworkcore.tools/8.0.13", "hasTools": true, "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", + "docs/PACKAGE.md", "lib/net8.0/_._", - "microsoft.entityframeworkcore.tools.8.0.2.nupkg.sha512", + "microsoft.entityframeworkcore.tools.8.0.13.nupkg.sha512", "microsoft.entityframeworkcore.tools.nuspec", "tools/EntityFrameworkCore.PS2.psd1", "tools/EntityFrameworkCore.PS2.psm1", @@ -4724,10 +3803,10 @@ "tools/netcoreapp2.0/any/ef.runtimeconfig.json" ] }, - "Microsoft.Extensions.Caching.Abstractions/9.0.2": { - "sha512": "a7QhA25n+BzSM5r5d7JznfyluMBGI7z3qyLlFviZ1Eiqv6DdiK27sLZdP/rpYirBM6UYAKxu5TbmfhIy13GN9A==", + "Microsoft.Extensions.Caching.Abstractions/8.0.0": { + "sha512": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", "type": "package", - "path": "microsoft.extensions.caching.abstractions/9.0.2", + "path": "microsoft.extensions.caching.abstractions/8.0.0", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -4737,25 +3816,27 @@ "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.Caching.Abstractions.targets", "buildTransitive/net462/_._", - "buildTransitive/net8.0/_._", + "buildTransitive/net6.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.Caching.Abstractions.targets", "lib/net462/Microsoft.Extensions.Caching.Abstractions.dll", "lib/net462/Microsoft.Extensions.Caching.Abstractions.xml", + "lib/net6.0/Microsoft.Extensions.Caching.Abstractions.dll", + "lib/net6.0/Microsoft.Extensions.Caching.Abstractions.xml", + "lib/net7.0/Microsoft.Extensions.Caching.Abstractions.dll", + "lib/net7.0/Microsoft.Extensions.Caching.Abstractions.xml", "lib/net8.0/Microsoft.Extensions.Caching.Abstractions.dll", "lib/net8.0/Microsoft.Extensions.Caching.Abstractions.xml", - "lib/net9.0/Microsoft.Extensions.Caching.Abstractions.dll", - "lib/net9.0/Microsoft.Extensions.Caching.Abstractions.xml", "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll", "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.xml", - "microsoft.extensions.caching.abstractions.9.0.2.nupkg.sha512", + "microsoft.extensions.caching.abstractions.8.0.0.nupkg.sha512", "microsoft.extensions.caching.abstractions.nuspec", "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.Caching.Memory/9.0.2": { - "sha512": "AlEfp0DMz8E1h1Exi8LBrUCNmCYcGDfSM4F/uK1D1cYx/R3w0LVvlmjICqxqXTsy7BEZaCf5leRZY2FuPEiFaw==", + "Microsoft.Extensions.Caching.Memory/8.0.1": { + "sha512": "HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==", "type": "package", - "path": "microsoft.extensions.caching.memory/9.0.2", + "path": "microsoft.extensions.caching.memory/8.0.1", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -4765,25 +3846,27 @@ "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.Caching.Memory.targets", "buildTransitive/net462/_._", - "buildTransitive/net8.0/_._", + "buildTransitive/net6.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.Caching.Memory.targets", "lib/net462/Microsoft.Extensions.Caching.Memory.dll", "lib/net462/Microsoft.Extensions.Caching.Memory.xml", + "lib/net6.0/Microsoft.Extensions.Caching.Memory.dll", + "lib/net6.0/Microsoft.Extensions.Caching.Memory.xml", + "lib/net7.0/Microsoft.Extensions.Caching.Memory.dll", + "lib/net7.0/Microsoft.Extensions.Caching.Memory.xml", "lib/net8.0/Microsoft.Extensions.Caching.Memory.dll", "lib/net8.0/Microsoft.Extensions.Caching.Memory.xml", - "lib/net9.0/Microsoft.Extensions.Caching.Memory.dll", - "lib/net9.0/Microsoft.Extensions.Caching.Memory.xml", "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll", "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.xml", - "microsoft.extensions.caching.memory.9.0.2.nupkg.sha512", + "microsoft.extensions.caching.memory.8.0.1.nupkg.sha512", "microsoft.extensions.caching.memory.nuspec", "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.Configuration/9.0.4": { - "sha512": "KIVBrMbItnCJDd1RF4KEaE8jZwDJcDUJW5zXpbwQ05HNYTK1GveHxHK0B3SjgDJuR48GRACXAO+BLhL8h34S7g==", + "Microsoft.Extensions.Configuration/8.0.0": { + "sha512": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", "type": "package", - "path": "microsoft.extensions.configuration/9.0.4", + "path": "microsoft.extensions.configuration/8.0.0", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -4793,25 +3876,27 @@ "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.Configuration.targets", "buildTransitive/net462/_._", - "buildTransitive/net8.0/_._", + "buildTransitive/net6.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.Configuration.targets", "lib/net462/Microsoft.Extensions.Configuration.dll", "lib/net462/Microsoft.Extensions.Configuration.xml", + "lib/net6.0/Microsoft.Extensions.Configuration.dll", + "lib/net6.0/Microsoft.Extensions.Configuration.xml", + "lib/net7.0/Microsoft.Extensions.Configuration.dll", + "lib/net7.0/Microsoft.Extensions.Configuration.xml", "lib/net8.0/Microsoft.Extensions.Configuration.dll", "lib/net8.0/Microsoft.Extensions.Configuration.xml", - "lib/net9.0/Microsoft.Extensions.Configuration.dll", - "lib/net9.0/Microsoft.Extensions.Configuration.xml", "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll", "lib/netstandard2.0/Microsoft.Extensions.Configuration.xml", - "microsoft.extensions.configuration.9.0.4.nupkg.sha512", + "microsoft.extensions.configuration.8.0.0.nupkg.sha512", "microsoft.extensions.configuration.nuspec", "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.Configuration.Abstractions/9.0.4": { - "sha512": "0LN/DiIKvBrkqp7gkF3qhGIeZk6/B63PthAHjQsxymJfIBcz0kbf4/p/t4lMgggVxZ+flRi5xvTwlpPOoZk8fg==", + "Microsoft.Extensions.Configuration.Abstractions/8.0.0": { + "sha512": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "type": "package", - "path": "microsoft.extensions.configuration.abstractions/9.0.4", + "path": "microsoft.extensions.configuration.abstractions/8.0.0", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -4821,17 +3906,19 @@ "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.Configuration.Abstractions.targets", "buildTransitive/net462/_._", - "buildTransitive/net8.0/_._", + "buildTransitive/net6.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.Configuration.Abstractions.targets", "lib/net462/Microsoft.Extensions.Configuration.Abstractions.dll", "lib/net462/Microsoft.Extensions.Configuration.Abstractions.xml", + "lib/net6.0/Microsoft.Extensions.Configuration.Abstractions.dll", + "lib/net6.0/Microsoft.Extensions.Configuration.Abstractions.xml", + "lib/net7.0/Microsoft.Extensions.Configuration.Abstractions.dll", + "lib/net7.0/Microsoft.Extensions.Configuration.Abstractions.xml", "lib/net8.0/Microsoft.Extensions.Configuration.Abstractions.dll", "lib/net8.0/Microsoft.Extensions.Configuration.Abstractions.xml", - "lib/net9.0/Microsoft.Extensions.Configuration.Abstractions.dll", - "lib/net9.0/Microsoft.Extensions.Configuration.Abstractions.xml", "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll", "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.xml", - "microsoft.extensions.configuration.abstractions.9.0.4.nupkg.sha512", + "microsoft.extensions.configuration.abstractions.8.0.0.nupkg.sha512", "microsoft.extensions.configuration.abstractions.nuspec", "useSharedDesignerContext.txt" ] @@ -4973,10 +4060,10 @@ "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.DependencyInjection/9.0.2": { - "sha512": "ZffbJrskOZ40JTzcTyKwFHS5eACSWp2bUQBBApIgGV+es8RaTD4OxUG7XxFr3RIPLXtYQ1jQzF2DjKB5fZn7Qg==", + "Microsoft.Extensions.DependencyInjection/8.0.1": { + "sha512": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "type": "package", - "path": "microsoft.extensions.dependencyinjection/9.0.2", + "path": "microsoft.extensions.dependencyinjection/8.0.1", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -4986,27 +4073,29 @@ "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.DependencyInjection.targets", "buildTransitive/net462/_._", - "buildTransitive/net8.0/_._", + "buildTransitive/net6.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.targets", "lib/net462/Microsoft.Extensions.DependencyInjection.dll", "lib/net462/Microsoft.Extensions.DependencyInjection.xml", + "lib/net6.0/Microsoft.Extensions.DependencyInjection.dll", + "lib/net6.0/Microsoft.Extensions.DependencyInjection.xml", + "lib/net7.0/Microsoft.Extensions.DependencyInjection.dll", + "lib/net7.0/Microsoft.Extensions.DependencyInjection.xml", "lib/net8.0/Microsoft.Extensions.DependencyInjection.dll", "lib/net8.0/Microsoft.Extensions.DependencyInjection.xml", - "lib/net9.0/Microsoft.Extensions.DependencyInjection.dll", - "lib/net9.0/Microsoft.Extensions.DependencyInjection.xml", "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.dll", "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.xml", "lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.dll", "lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.xml", - "microsoft.extensions.dependencyinjection.9.0.2.nupkg.sha512", + "microsoft.extensions.dependencyinjection.8.0.1.nupkg.sha512", "microsoft.extensions.dependencyinjection.nuspec", "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.DependencyInjection.Abstractions/9.0.4": { - "sha512": "UI0TQPVkS78bFdjkTodmkH0Fe8lXv9LnhGFKgKrsgUJ5a5FVdFRcgjIkBVLbGgdRhxWirxH/8IXUtEyYJx6GQg==", + "Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2": { + "sha512": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==", "type": "package", - "path": "microsoft.extensions.dependencyinjection.abstractions/9.0.4", + "path": "microsoft.extensions.dependencyinjection.abstractions/8.0.2", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -5016,27 +4105,29 @@ "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.DependencyInjection.Abstractions.targets", "buildTransitive/net462/_._", - "buildTransitive/net8.0/_._", + "buildTransitive/net6.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.Abstractions.targets", "lib/net462/Microsoft.Extensions.DependencyInjection.Abstractions.dll", "lib/net462/Microsoft.Extensions.DependencyInjection.Abstractions.xml", + "lib/net6.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll", + "lib/net6.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml", + "lib/net7.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll", + "lib/net7.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml", "lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll", "lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml", - "lib/net9.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll", - "lib/net9.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml", "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll", "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml", "lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.Abstractions.dll", "lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.Abstractions.xml", - "microsoft.extensions.dependencyinjection.abstractions.9.0.4.nupkg.sha512", + "microsoft.extensions.dependencyinjection.abstractions.8.0.2.nupkg.sha512", "microsoft.extensions.dependencyinjection.abstractions.nuspec", "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.DependencyModel/8.0.0": { - "sha512": "NSmDw3K0ozNDgShSIpsZcbFIzBX4w28nDag+TfaQujkXGazBm+lid5onlWoCBy4VsLxqnnKjEBbGSJVWJMf43g==", + "Microsoft.Extensions.DependencyModel/8.0.2": { + "sha512": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==", "type": "package", - "path": "microsoft.extensions.dependencymodel/8.0.0", + "path": "microsoft.extensions.dependencymodel/8.0.2", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -5058,7 +4149,7 @@ "lib/net8.0/Microsoft.Extensions.DependencyModel.xml", "lib/netstandard2.0/Microsoft.Extensions.DependencyModel.dll", "lib/netstandard2.0/Microsoft.Extensions.DependencyModel.xml", - "microsoft.extensions.dependencymodel.8.0.0.nupkg.sha512", + "microsoft.extensions.dependencymodel.8.0.2.nupkg.sha512", "microsoft.extensions.dependencymodel.nuspec", "useSharedDesignerContext.txt" ] @@ -5239,10 +4330,10 @@ "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.Logging/9.0.2": { - "sha512": "loV/0UNpt2bD+6kCDzFALVE63CDtqzPeC0LAetkdhiEr/tTNbvOlQ7CBResH7BQBd3cikrwiBfaHdyHMFUlc2g==", + "Microsoft.Extensions.Logging/8.0.1": { + "sha512": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", "type": "package", - "path": "microsoft.extensions.logging/9.0.2", + "path": "microsoft.extensions.logging/8.0.1", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -5252,27 +4343,29 @@ "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.Logging.targets", "buildTransitive/net462/_._", - "buildTransitive/net8.0/_._", + "buildTransitive/net6.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.Logging.targets", "lib/net462/Microsoft.Extensions.Logging.dll", "lib/net462/Microsoft.Extensions.Logging.xml", + "lib/net6.0/Microsoft.Extensions.Logging.dll", + "lib/net6.0/Microsoft.Extensions.Logging.xml", + "lib/net7.0/Microsoft.Extensions.Logging.dll", + "lib/net7.0/Microsoft.Extensions.Logging.xml", "lib/net8.0/Microsoft.Extensions.Logging.dll", "lib/net8.0/Microsoft.Extensions.Logging.xml", - "lib/net9.0/Microsoft.Extensions.Logging.dll", - "lib/net9.0/Microsoft.Extensions.Logging.xml", "lib/netstandard2.0/Microsoft.Extensions.Logging.dll", "lib/netstandard2.0/Microsoft.Extensions.Logging.xml", "lib/netstandard2.1/Microsoft.Extensions.Logging.dll", "lib/netstandard2.1/Microsoft.Extensions.Logging.xml", - "microsoft.extensions.logging.9.0.2.nupkg.sha512", + "microsoft.extensions.logging.8.0.1.nupkg.sha512", "microsoft.extensions.logging.nuspec", "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.Logging.Abstractions/9.0.2": { - "sha512": "dV9s2Lamc8jSaqhl2BQSPn/AryDIH2sSbQUyLitLXV0ROmsb+SROnn2cH939JFbsNrnf3mIM3GNRKT7P0ldwLg==", + "Microsoft.Extensions.Logging.Abstractions/8.0.3": { + "sha512": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", "type": "package", - "path": "microsoft.extensions.logging.abstractions/9.0.2", + "path": "microsoft.extensions.logging.abstractions/8.0.3", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -5324,45 +4417,28 @@ "analyzers/dotnet/roslyn4.4/cs/zh-Hant/Microsoft.Extensions.Logging.Generators.resources.dll", "buildTransitive/net461/Microsoft.Extensions.Logging.Abstractions.targets", "buildTransitive/net462/Microsoft.Extensions.Logging.Abstractions.targets", - "buildTransitive/net8.0/Microsoft.Extensions.Logging.Abstractions.targets", + "buildTransitive/net6.0/Microsoft.Extensions.Logging.Abstractions.targets", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.Logging.Abstractions.targets", "buildTransitive/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.targets", "lib/net462/Microsoft.Extensions.Logging.Abstractions.dll", "lib/net462/Microsoft.Extensions.Logging.Abstractions.xml", + "lib/net6.0/Microsoft.Extensions.Logging.Abstractions.dll", + "lib/net6.0/Microsoft.Extensions.Logging.Abstractions.xml", + "lib/net7.0/Microsoft.Extensions.Logging.Abstractions.dll", + "lib/net7.0/Microsoft.Extensions.Logging.Abstractions.xml", "lib/net8.0/Microsoft.Extensions.Logging.Abstractions.dll", "lib/net8.0/Microsoft.Extensions.Logging.Abstractions.xml", - "lib/net9.0/Microsoft.Extensions.Logging.Abstractions.dll", - "lib/net9.0/Microsoft.Extensions.Logging.Abstractions.xml", "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll", "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.xml", - "microsoft.extensions.logging.abstractions.9.0.2.nupkg.sha512", + "microsoft.extensions.logging.abstractions.8.0.3.nupkg.sha512", "microsoft.extensions.logging.abstractions.nuspec", "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.ObjectPool/6.0.3": { - "sha512": "IbQUEZr/LxxpPxkXDmKaemMeQNPjdHfk87HtTsI18a3RVgad0NOJSRaJ20hcesqL45PLcpQHR8xrPP7wZKbFQQ==", + "Microsoft.Extensions.Options/8.0.2": { + "sha512": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "type": "package", - "path": "microsoft.extensions.objectpool/6.0.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "THIRD-PARTY-NOTICES.TXT", - "lib/net461/Microsoft.Extensions.ObjectPool.dll", - "lib/net461/Microsoft.Extensions.ObjectPool.xml", - "lib/net6.0/Microsoft.Extensions.ObjectPool.dll", - "lib/net6.0/Microsoft.Extensions.ObjectPool.xml", - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll", - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.xml", - "microsoft.extensions.objectpool.6.0.3.nupkg.sha512", - "microsoft.extensions.objectpool.nuspec" - ] - }, - "Microsoft.Extensions.Options/9.0.4": { - "sha512": "fiFI2+58kicqVZyt/6obqoFwHiab7LC4FkQ3mmiBJ28Yy4fAvy2+v9MRnSvvlOO8chTOjKsdafFl/K9veCPo5g==", - "type": "package", - "path": "microsoft.extensions.options/9.0.4", + "path": "microsoft.extensions.options/8.0.2", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -5386,20 +4462,22 @@ "analyzers/dotnet/roslyn4.4/cs/zh-Hant/Microsoft.Extensions.Options.SourceGeneration.resources.dll", "buildTransitive/net461/Microsoft.Extensions.Options.targets", "buildTransitive/net462/Microsoft.Extensions.Options.targets", - "buildTransitive/net8.0/Microsoft.Extensions.Options.targets", + "buildTransitive/net6.0/Microsoft.Extensions.Options.targets", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.Options.targets", "buildTransitive/netstandard2.0/Microsoft.Extensions.Options.targets", "lib/net462/Microsoft.Extensions.Options.dll", "lib/net462/Microsoft.Extensions.Options.xml", + "lib/net6.0/Microsoft.Extensions.Options.dll", + "lib/net6.0/Microsoft.Extensions.Options.xml", + "lib/net7.0/Microsoft.Extensions.Options.dll", + "lib/net7.0/Microsoft.Extensions.Options.xml", "lib/net8.0/Microsoft.Extensions.Options.dll", "lib/net8.0/Microsoft.Extensions.Options.xml", - "lib/net9.0/Microsoft.Extensions.Options.dll", - "lib/net9.0/Microsoft.Extensions.Options.xml", "lib/netstandard2.0/Microsoft.Extensions.Options.dll", "lib/netstandard2.0/Microsoft.Extensions.Options.xml", "lib/netstandard2.1/Microsoft.Extensions.Options.dll", "lib/netstandard2.1/Microsoft.Extensions.Options.xml", - "microsoft.extensions.options.9.0.4.nupkg.sha512", + "microsoft.extensions.options.8.0.2.nupkg.sha512", "microsoft.extensions.options.nuspec", "useSharedDesignerContext.txt" ] @@ -5434,10 +4512,10 @@ "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.Primitives/9.0.4": { - "sha512": "SPFyMjyku1nqTFFJ928JAMd0QnRe4xjE7KeKnZMWXf3xk+6e0WiOZAluYtLdbJUXtsl2cCRSi8cBquJ408k8RA==", + "Microsoft.Extensions.Primitives/8.0.0": { + "sha512": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", "type": "package", - "path": "microsoft.extensions.primitives/9.0.4", + "path": "microsoft.extensions.primitives/8.0.0", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -5447,17 +4525,19 @@ "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.Primitives.targets", "buildTransitive/net462/_._", - "buildTransitive/net8.0/_._", + "buildTransitive/net6.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.Primitives.targets", "lib/net462/Microsoft.Extensions.Primitives.dll", "lib/net462/Microsoft.Extensions.Primitives.xml", + "lib/net6.0/Microsoft.Extensions.Primitives.dll", + "lib/net6.0/Microsoft.Extensions.Primitives.xml", + "lib/net7.0/Microsoft.Extensions.Primitives.dll", + "lib/net7.0/Microsoft.Extensions.Primitives.xml", "lib/net8.0/Microsoft.Extensions.Primitives.dll", "lib/net8.0/Microsoft.Extensions.Primitives.xml", - "lib/net9.0/Microsoft.Extensions.Primitives.dll", - "lib/net9.0/Microsoft.Extensions.Primitives.xml", "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll", "lib/netstandard2.0/Microsoft.Extensions.Primitives.xml", - "microsoft.extensions.primitives.9.0.4.nupkg.sha512", + "microsoft.extensions.primitives.8.0.0.nupkg.sha512", "microsoft.extensions.primitives.nuspec", "useSharedDesignerContext.txt" ] @@ -5604,44 +4684,22 @@ "microsoft.identitymodel.tokens.nuspec" ] }, - "Microsoft.OData.Core/8.2.2": { - "sha512": "VY3sZzBOxePvRuCeBlEiJ2LNxjr8ReOKj6YWXi5i8vG3TLWo+CmIc9fiE8WDoe90NcCaBnwK6Ub2fWnpNEbLxA==", + "Microsoft.NETCore.Platforms/5.0.0": { + "sha512": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==", "type": "package", - "path": "microsoft.odata.core/8.2.2", + "path": "microsoft.netcore.platforms/5.0.0", "files": [ ".nupkg.metadata", ".signature.p7s", - "lib/net8.0/Microsoft.OData.Core.dll", - "lib/net8.0/Microsoft.OData.Core.xml", - "microsoft.odata.core.8.2.2.nupkg.sha512", - "microsoft.odata.core.nuspec" - ] - }, - "Microsoft.OData.Edm/8.2.2": { - "sha512": "cHMsoV059EPNLD+SJFxIM8rKLUA3kKifflfUQy+7hWKZpmw+YhLi7qeWSSA9UDRVd0sqApFU8gdqHjordJoTIg==", - "type": "package", - "path": "microsoft.odata.edm/8.2.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net8.0/Microsoft.OData.Edm.dll", - "lib/net8.0/Microsoft.OData.Edm.xml", - "microsoft.odata.edm.8.2.2.nupkg.sha512", - "microsoft.odata.edm.nuspec" - ] - }, - "Microsoft.OData.ModelBuilder/2.0.0": { - "sha512": "QzySAMGhLCMyLNHSTIYKFjJXetoDeGkRS/7JEjm7eMJlyu1Qv4MfL1CXQFg2uijizNu2jQojT0mdCpawXbyv8Q==", - "type": "package", - "path": "microsoft.odata.modelbuilder/2.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "images/odata.png", - "lib/net8.0/Microsoft.OData.ModelBuilder.dll", - "lib/net8.0/Microsoft.OData.ModelBuilder.xml", - "microsoft.odata.modelbuilder.2.0.0.nupkg.sha512", - "microsoft.odata.modelbuilder.nuspec" + "Icon.png", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/netstandard1.0/_._", + "microsoft.netcore.platforms.5.0.0.nupkg.sha512", + "microsoft.netcore.platforms.nuspec", + "runtime.json", + "useSharedDesignerContext.txt", + "version.txt" ] }, "Microsoft.OpenApi/1.6.22": { @@ -5659,19 +4717,6 @@ "microsoft.openapi.nuspec" ] }, - "Microsoft.Spatial/8.2.2": { - "sha512": "iG0pF7o7bR9/J+WcSNGEW0DzewO8Te/T+5FJ9EIuXcPkVE96cqc6Cf57ygqlQZj5jmf+D/gOtBJjPc4iyeI0Aw==", - "type": "package", - "path": "microsoft.spatial/8.2.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net8.0/Microsoft.Spatial.dll", - "lib/net8.0/Microsoft.Spatial.xml", - "microsoft.spatial.8.2.2.nupkg.sha512", - "microsoft.spatial.nuspec" - ] - }, "Microsoft.Win32.Registry/5.0.0": { "sha512": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", "type": "package", @@ -5716,35 +4761,6 @@ "version.txt" ] }, - "Microsoft.Win32.SystemEvents/6.0.0": { - "sha512": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A==", - "type": "package", - "path": "microsoft.win32.systemevents/6.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "buildTransitive/netcoreapp2.0/Microsoft.Win32.SystemEvents.targets", - "buildTransitive/netcoreapp3.1/_._", - "lib/net461/Microsoft.Win32.SystemEvents.dll", - "lib/net461/Microsoft.Win32.SystemEvents.xml", - "lib/net6.0/Microsoft.Win32.SystemEvents.dll", - "lib/net6.0/Microsoft.Win32.SystemEvents.xml", - "lib/netcoreapp3.1/Microsoft.Win32.SystemEvents.dll", - "lib/netcoreapp3.1/Microsoft.Win32.SystemEvents.xml", - "lib/netstandard2.0/Microsoft.Win32.SystemEvents.dll", - "lib/netstandard2.0/Microsoft.Win32.SystemEvents.xml", - "microsoft.win32.systemevents.6.0.0.nupkg.sha512", - "microsoft.win32.systemevents.nuspec", - "runtimes/win/lib/net6.0/Microsoft.Win32.SystemEvents.dll", - "runtimes/win/lib/net6.0/Microsoft.Win32.SystemEvents.xml", - "runtimes/win/lib/netcoreapp3.1/Microsoft.Win32.SystemEvents.dll", - "runtimes/win/lib/netcoreapp3.1/Microsoft.Win32.SystemEvents.xml", - "useSharedDesignerContext.txt" - ] - }, "MongoDB.Analyzer/1.5.0": { "sha512": "ujrUe6NeLGW9q8ABseYY/NFx7pc1G/LshtVfLMONV02+fEy0jUDsXLHU7YvuARjJhJI7Mgz88tfvCncccUmerw==", "type": "package", @@ -5803,21 +4819,6 @@ "packageIcon.png" ] }, - "Mono.Reflection/2.0.0": { - "sha512": "gipqt3dO1NsgM9wfUsQeNVLYmSskDfaN+muNQHLJYkCuYxx2bIp6gAA8j2Rp+Ey6YNHloZv+BJY56r9fl3tfew==", - "type": "package", - "path": "mono.reflection/2.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net40/Mono.Reflection.dll", - "lib/net40/Mono.Reflection.pdb", - "lib/netstandard2.0/Mono.Reflection.dll", - "lib/netstandard2.0/Mono.Reflection.pdb", - "mono.reflection.2.0.0.nupkg.sha512", - "mono.reflection.nuspec" - ] - }, "Mono.TextTemplating/2.2.1": { "sha512": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==", "type": "package", @@ -6181,10 +5182,10 @@ "pipelines.sockets.unofficial.nuspec" ] }, - "Pomelo.EntityFrameworkCore.MySql/8.0.2": { - "sha512": "XjnlcxVBLnEMbyEc5cZzgZeDyLvAniACZQ04W1slWN0f4rmfNzl98gEMvHnFH0fMDF06z9MmgGi/Sr7hJ+BVnw==", + "Pomelo.EntityFrameworkCore.MySql/8.0.3": { + "sha512": "gOHP6v/nFp5V/FgHqv9mZocGqCLGofihEX9dTbLhiXX3H7SJHmGX70GIPUpiqLT+1jIfDxg1PZh9MTUKuk7Kig==", "type": "package", - "path": "pomelo.entityframeworkcore.mysql/8.0.2", + "path": "pomelo.entityframeworkcore.mysql/8.0.3", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -6192,7 +5193,7 @@ "icon.png", "lib/net8.0/Pomelo.EntityFrameworkCore.MySql.dll", "lib/net8.0/Pomelo.EntityFrameworkCore.MySql.xml", - "pomelo.entityframeworkcore.mysql.8.0.2.nupkg.sha512", + "pomelo.entityframeworkcore.mysql.8.0.3.nupkg.sha512", "pomelo.entityframeworkcore.mysql.nuspec" ] }, @@ -6213,69 +5214,6 @@ "rabbitmq.client.nuspec" ] }, - "runtime.native.System.Data.SqlClient.sni/4.7.0": { - "sha512": "9kyFSIdN3T0qjDQ2R0HRXYIhS3l5psBzQi6qqhdLz+SzFyEy4sVxNOke+yyYv8Cu8rPER12c3RDjLT8wF3WBYQ==", - "type": "package", - "path": "runtime.native.system.data.sqlclient.sni/4.7.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "runtime.native.system.data.sqlclient.sni.4.7.0.nupkg.sha512", - "runtime.native.system.data.sqlclient.sni.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "sha512": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==", - "type": "package", - "path": "runtime.win-arm64.runtime.native.system.data.sqlclient.sni/4.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.win-arm64.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512", - "runtime.win-arm64.runtime.native.system.data.sqlclient.sni.nuspec", - "runtimes/win-arm64/native/sni.dll", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "sha512": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==", - "type": "package", - "path": "runtime.win-x64.runtime.native.system.data.sqlclient.sni/4.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.win-x64.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512", - "runtime.win-x64.runtime.native.system.data.sqlclient.sni.nuspec", - "runtimes/win-x64/native/sni.dll", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "sha512": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==", - "type": "package", - "path": "runtime.win-x86.runtime.native.system.data.sqlclient.sni/4.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.win-x86.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512", - "runtime.win-x86.runtime.native.system.data.sqlclient.sni.nuspec", - "runtimes/win-x86/native/sni.dll", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, "SharpCompress/0.30.1": { "sha512": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", "type": "package", @@ -6508,27 +5446,25 @@ "version.txt" ] }, - "System.CodeDom/6.0.0": { - "sha512": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==", + "System.CodeDom/4.4.0": { + "sha512": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==", "type": "package", - "path": "system.codedom/6.0.0", + "path": "system.codedom/4.4.0", "files": [ ".nupkg.metadata", ".signature.p7s", - "Icon.png", "LICENSE.TXT", "THIRD-PARTY-NOTICES.TXT", - "buildTransitive/netcoreapp2.0/System.CodeDom.targets", - "buildTransitive/netcoreapp3.1/_._", "lib/net461/System.CodeDom.dll", - "lib/net461/System.CodeDom.xml", - "lib/net6.0/System.CodeDom.dll", - "lib/net6.0/System.CodeDom.xml", "lib/netstandard2.0/System.CodeDom.dll", - "lib/netstandard2.0/System.CodeDom.xml", - "system.codedom.6.0.0.nupkg.sha512", + "ref/net461/System.CodeDom.dll", + "ref/net461/System.CodeDom.xml", + "ref/netstandard2.0/System.CodeDom.dll", + "ref/netstandard2.0/System.CodeDom.xml", + "system.codedom.4.4.0.nupkg.sha512", "system.codedom.nuspec", - "useSharedDesignerContext.txt" + "useSharedDesignerContext.txt", + "version.txt" ] }, "System.Collections.Immutable/6.0.0": { @@ -6554,96 +5490,6 @@ "useSharedDesignerContext.txt" ] }, - "System.ComponentModel.Annotations/5.0.0": { - "sha512": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==", - "type": "package", - "path": "system.componentmodel.annotations/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net461/System.ComponentModel.Annotations.dll", - "lib/netcore50/System.ComponentModel.Annotations.dll", - "lib/netstandard1.4/System.ComponentModel.Annotations.dll", - "lib/netstandard2.0/System.ComponentModel.Annotations.dll", - "lib/netstandard2.1/System.ComponentModel.Annotations.dll", - "lib/netstandard2.1/System.ComponentModel.Annotations.xml", - "lib/portable-net45+win8/_._", - "lib/win8/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net461/System.ComponentModel.Annotations.dll", - "ref/net461/System.ComponentModel.Annotations.xml", - "ref/netcore50/System.ComponentModel.Annotations.dll", - "ref/netcore50/System.ComponentModel.Annotations.xml", - "ref/netcore50/de/System.ComponentModel.Annotations.xml", - "ref/netcore50/es/System.ComponentModel.Annotations.xml", - "ref/netcore50/fr/System.ComponentModel.Annotations.xml", - "ref/netcore50/it/System.ComponentModel.Annotations.xml", - "ref/netcore50/ja/System.ComponentModel.Annotations.xml", - "ref/netcore50/ko/System.ComponentModel.Annotations.xml", - "ref/netcore50/ru/System.ComponentModel.Annotations.xml", - "ref/netcore50/zh-hans/System.ComponentModel.Annotations.xml", - "ref/netcore50/zh-hant/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/System.ComponentModel.Annotations.dll", - "ref/netstandard1.1/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/de/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/es/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/fr/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/it/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/ja/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/ko/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/ru/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/zh-hans/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/zh-hant/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/System.ComponentModel.Annotations.dll", - "ref/netstandard1.3/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/de/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/es/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/fr/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/it/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/ja/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/ko/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/ru/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/zh-hans/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/zh-hant/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/System.ComponentModel.Annotations.dll", - "ref/netstandard1.4/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/de/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/es/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/fr/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/it/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/ja/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/ko/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/ru/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/zh-hans/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/zh-hant/System.ComponentModel.Annotations.xml", - "ref/netstandard2.0/System.ComponentModel.Annotations.dll", - "ref/netstandard2.0/System.ComponentModel.Annotations.xml", - "ref/netstandard2.1/System.ComponentModel.Annotations.dll", - "ref/netstandard2.1/System.ComponentModel.Annotations.xml", - "ref/portable-net45+win8/_._", - "ref/win8/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.componentmodel.annotations.5.0.0.nupkg.sha512", - "system.componentmodel.annotations.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, "System.Composition/6.0.0": { "sha512": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==", "type": "package", @@ -6776,121 +5622,10 @@ "useSharedDesignerContext.txt" ] }, - "System.Configuration.ConfigurationManager/6.0.1": { - "sha512": "jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==", + "System.Diagnostics.DiagnosticSource/8.0.0": { + "sha512": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", "type": "package", - "path": "system.configuration.configurationmanager/6.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "buildTransitive/netcoreapp2.0/System.Configuration.ConfigurationManager.targets", - "buildTransitive/netcoreapp3.1/_._", - "lib/net461/System.Configuration.ConfigurationManager.dll", - "lib/net461/System.Configuration.ConfigurationManager.xml", - "lib/net6.0/System.Configuration.ConfigurationManager.dll", - "lib/net6.0/System.Configuration.ConfigurationManager.xml", - "lib/netstandard2.0/System.Configuration.ConfigurationManager.dll", - "lib/netstandard2.0/System.Configuration.ConfigurationManager.xml", - "runtimes/win/lib/net461/System.Configuration.ConfigurationManager.dll", - "runtimes/win/lib/net461/System.Configuration.ConfigurationManager.xml", - "system.configuration.configurationmanager.6.0.1.nupkg.sha512", - "system.configuration.configurationmanager.nuspec", - "useSharedDesignerContext.txt" - ] - }, - "System.Data.SqlClient/4.8.6": { - "sha512": "2Ij/LCaTQRyAi5lAv7UUTV9R2FobC8xN9mE0fXBZohum/xLl8IZVmE98Rq5ugQHjCgTBRKqpXRb4ORulRdA6Ig==", - "type": "package", - "path": "system.data.sqlclient/4.8.6", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net451/System.Data.SqlClient.dll", - "lib/net46/System.Data.SqlClient.dll", - "lib/net461/System.Data.SqlClient.dll", - "lib/net461/System.Data.SqlClient.xml", - "lib/netcoreapp2.1/System.Data.SqlClient.dll", - "lib/netcoreapp2.1/System.Data.SqlClient.xml", - "lib/netstandard1.2/System.Data.SqlClient.dll", - "lib/netstandard1.2/System.Data.SqlClient.xml", - "lib/netstandard1.3/System.Data.SqlClient.dll", - "lib/netstandard1.3/System.Data.SqlClient.xml", - "lib/netstandard2.0/System.Data.SqlClient.dll", - "lib/netstandard2.0/System.Data.SqlClient.xml", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net451/System.Data.SqlClient.dll", - "ref/net46/System.Data.SqlClient.dll", - "ref/net461/System.Data.SqlClient.dll", - "ref/net461/System.Data.SqlClient.xml", - "ref/netcoreapp2.1/System.Data.SqlClient.dll", - "ref/netcoreapp2.1/System.Data.SqlClient.xml", - "ref/netstandard1.2/System.Data.SqlClient.dll", - "ref/netstandard1.2/System.Data.SqlClient.xml", - "ref/netstandard1.2/de/System.Data.SqlClient.xml", - "ref/netstandard1.2/es/System.Data.SqlClient.xml", - "ref/netstandard1.2/fr/System.Data.SqlClient.xml", - "ref/netstandard1.2/it/System.Data.SqlClient.xml", - "ref/netstandard1.2/ja/System.Data.SqlClient.xml", - "ref/netstandard1.2/ko/System.Data.SqlClient.xml", - "ref/netstandard1.2/ru/System.Data.SqlClient.xml", - "ref/netstandard1.2/zh-hans/System.Data.SqlClient.xml", - "ref/netstandard1.2/zh-hant/System.Data.SqlClient.xml", - "ref/netstandard1.3/System.Data.SqlClient.dll", - "ref/netstandard1.3/System.Data.SqlClient.xml", - "ref/netstandard1.3/de/System.Data.SqlClient.xml", - "ref/netstandard1.3/es/System.Data.SqlClient.xml", - "ref/netstandard1.3/fr/System.Data.SqlClient.xml", - "ref/netstandard1.3/it/System.Data.SqlClient.xml", - "ref/netstandard1.3/ja/System.Data.SqlClient.xml", - "ref/netstandard1.3/ko/System.Data.SqlClient.xml", - "ref/netstandard1.3/ru/System.Data.SqlClient.xml", - "ref/netstandard1.3/zh-hans/System.Data.SqlClient.xml", - "ref/netstandard1.3/zh-hant/System.Data.SqlClient.xml", - "ref/netstandard2.0/System.Data.SqlClient.dll", - "ref/netstandard2.0/System.Data.SqlClient.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll", - "runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.xml", - "runtimes/unix/lib/netstandard1.3/System.Data.SqlClient.dll", - "runtimes/unix/lib/netstandard2.0/System.Data.SqlClient.dll", - "runtimes/unix/lib/netstandard2.0/System.Data.SqlClient.xml", - "runtimes/win/lib/net451/System.Data.SqlClient.dll", - "runtimes/win/lib/net46/System.Data.SqlClient.dll", - "runtimes/win/lib/net461/System.Data.SqlClient.dll", - "runtimes/win/lib/net461/System.Data.SqlClient.xml", - "runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll", - "runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.xml", - "runtimes/win/lib/netstandard1.3/System.Data.SqlClient.dll", - "runtimes/win/lib/netstandard2.0/System.Data.SqlClient.dll", - "runtimes/win/lib/netstandard2.0/System.Data.SqlClient.xml", - "runtimes/win/lib/uap10.0.16299/System.Data.SqlClient.dll", - "runtimes/win/lib/uap10.0.16299/System.Data.SqlClient.xml", - "system.data.sqlclient.4.8.6.nupkg.sha512", - "system.data.sqlclient.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Diagnostics.DiagnosticSource/9.0.2": { - "sha512": "z5CMQNLzk8UKnTEHRKb4nq03CCDWBMEF2gfP3oPKZn4F8wip6LFZCP5rF90DREHqdNddScIGAfszXJSjh4drSw==", - "type": "package", - "path": "system.diagnostics.diagnosticsource/9.0.2", + "path": "system.diagnostics.diagnosticsource/8.0.0", "files": [ ".nupkg.metadata", ".signature.p7s", @@ -6899,65 +5634,23 @@ "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/System.Diagnostics.DiagnosticSource.targets", "buildTransitive/net462/_._", - "buildTransitive/net8.0/_._", + "buildTransitive/net6.0/_._", "buildTransitive/netcoreapp2.0/System.Diagnostics.DiagnosticSource.targets", - "content/ILLink/ILLink.Descriptors.LibraryBuild.xml", - "contentFiles/any/net462/ILLink/ILLink.Descriptors.LibraryBuild.xml", - "contentFiles/any/net8.0/ILLink/ILLink.Descriptors.LibraryBuild.xml", - "contentFiles/any/net9.0/ILLink/ILLink.Descriptors.LibraryBuild.xml", - "contentFiles/any/netstandard2.0/ILLink/ILLink.Descriptors.LibraryBuild.xml", "lib/net462/System.Diagnostics.DiagnosticSource.dll", "lib/net462/System.Diagnostics.DiagnosticSource.xml", + "lib/net6.0/System.Diagnostics.DiagnosticSource.dll", + "lib/net6.0/System.Diagnostics.DiagnosticSource.xml", + "lib/net7.0/System.Diagnostics.DiagnosticSource.dll", + "lib/net7.0/System.Diagnostics.DiagnosticSource.xml", "lib/net8.0/System.Diagnostics.DiagnosticSource.dll", "lib/net8.0/System.Diagnostics.DiagnosticSource.xml", - "lib/net9.0/System.Diagnostics.DiagnosticSource.dll", - "lib/net9.0/System.Diagnostics.DiagnosticSource.xml", "lib/netstandard2.0/System.Diagnostics.DiagnosticSource.dll", "lib/netstandard2.0/System.Diagnostics.DiagnosticSource.xml", - "system.diagnostics.diagnosticsource.9.0.2.nupkg.sha512", + "system.diagnostics.diagnosticsource.8.0.0.nupkg.sha512", "system.diagnostics.diagnosticsource.nuspec", "useSharedDesignerContext.txt" ] }, - "System.Drawing.Common/6.0.0": { - "sha512": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==", - "type": "package", - "path": "system.drawing.common/6.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "buildTransitive/netcoreapp2.0/System.Drawing.Common.targets", - "buildTransitive/netcoreapp3.1/_._", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net461/System.Drawing.Common.dll", - "lib/net461/System.Drawing.Common.xml", - "lib/net6.0/System.Drawing.Common.dll", - "lib/net6.0/System.Drawing.Common.xml", - "lib/netcoreapp3.1/System.Drawing.Common.dll", - "lib/netcoreapp3.1/System.Drawing.Common.xml", - "lib/netstandard2.0/System.Drawing.Common.dll", - "lib/netstandard2.0/System.Drawing.Common.xml", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "runtimes/unix/lib/net6.0/System.Drawing.Common.dll", - "runtimes/unix/lib/net6.0/System.Drawing.Common.xml", - "runtimes/unix/lib/netcoreapp3.1/System.Drawing.Common.dll", - "runtimes/unix/lib/netcoreapp3.1/System.Drawing.Common.xml", - "runtimes/win/lib/net6.0/System.Drawing.Common.dll", - "runtimes/win/lib/net6.0/System.Drawing.Common.xml", - "runtimes/win/lib/netcoreapp3.1/System.Drawing.Common.dll", - "runtimes/win/lib/netcoreapp3.1/System.Drawing.Common.xml", - "system.drawing.common.6.0.0.nupkg.sha512", - "system.drawing.common.nuspec", - "useSharedDesignerContext.txt" - ] - }, "System.IdentityModel.Tokens.Jwt/8.9.0": { "sha512": "7Pu9UjF1+so0s8zgzcIxSxbRQoiM2DMdwazVGmNptX3O6gDfMyeWZBd/Zn6VueDteN0ZTHw2acsf6+mAe8UpMw==", "type": "package", @@ -7058,60 +5751,6 @@ "system.reactive.nuspec" ] }, - "System.Reflection.Emit/4.7.0": { - "sha512": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==", - "type": "package", - "path": "system.reflection.emit/4.7.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Reflection.Emit.dll", - "lib/netcoreapp2.0/_._", - "lib/netstandard1.1/System.Reflection.Emit.dll", - "lib/netstandard1.1/System.Reflection.Emit.xml", - "lib/netstandard1.3/System.Reflection.Emit.dll", - "lib/netstandard2.0/System.Reflection.Emit.dll", - "lib/netstandard2.0/System.Reflection.Emit.xml", - "lib/netstandard2.1/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcoreapp2.0/_._", - "ref/netstandard1.1/System.Reflection.Emit.dll", - "ref/netstandard1.1/System.Reflection.Emit.xml", - "ref/netstandard1.1/de/System.Reflection.Emit.xml", - "ref/netstandard1.1/es/System.Reflection.Emit.xml", - "ref/netstandard1.1/fr/System.Reflection.Emit.xml", - "ref/netstandard1.1/it/System.Reflection.Emit.xml", - "ref/netstandard1.1/ja/System.Reflection.Emit.xml", - "ref/netstandard1.1/ko/System.Reflection.Emit.xml", - "ref/netstandard1.1/ru/System.Reflection.Emit.xml", - "ref/netstandard1.1/zh-hans/System.Reflection.Emit.xml", - "ref/netstandard1.1/zh-hant/System.Reflection.Emit.xml", - "ref/netstandard2.0/System.Reflection.Emit.dll", - "ref/netstandard2.0/System.Reflection.Emit.xml", - "ref/netstandard2.1/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Reflection.Emit.dll", - "runtimes/aot/lib/netcore50/System.Reflection.Emit.xml", - "system.reflection.emit.4.7.0.nupkg.sha512", - "system.reflection.emit.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, "System.Reflection.Metadata/6.0.1": { "sha512": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", "type": "package", @@ -7160,97 +5799,51 @@ "useSharedDesignerContext.txt" ] }, - "System.Security.AccessControl/6.0.0": { - "sha512": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==", + "System.Security.AccessControl/5.0.0": { + "sha512": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", "type": "package", - "path": "system.security.accesscontrol/6.0.0", + "path": "system.security.accesscontrol/5.0.0", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", "LICENSE.TXT", "THIRD-PARTY-NOTICES.TXT", - "buildTransitive/netcoreapp2.0/System.Security.AccessControl.targets", - "buildTransitive/netcoreapp3.1/_._", + "lib/net46/System.Security.AccessControl.dll", "lib/net461/System.Security.AccessControl.dll", "lib/net461/System.Security.AccessControl.xml", - "lib/net6.0/System.Security.AccessControl.dll", - "lib/net6.0/System.Security.AccessControl.xml", + "lib/netstandard1.3/System.Security.AccessControl.dll", "lib/netstandard2.0/System.Security.AccessControl.dll", "lib/netstandard2.0/System.Security.AccessControl.xml", + "lib/uap10.0.16299/_._", + "ref/net46/System.Security.AccessControl.dll", + "ref/net461/System.Security.AccessControl.dll", + "ref/net461/System.Security.AccessControl.xml", + "ref/netstandard1.3/System.Security.AccessControl.dll", + "ref/netstandard1.3/System.Security.AccessControl.xml", + "ref/netstandard1.3/de/System.Security.AccessControl.xml", + "ref/netstandard1.3/es/System.Security.AccessControl.xml", + "ref/netstandard1.3/fr/System.Security.AccessControl.xml", + "ref/netstandard1.3/it/System.Security.AccessControl.xml", + "ref/netstandard1.3/ja/System.Security.AccessControl.xml", + "ref/netstandard1.3/ko/System.Security.AccessControl.xml", + "ref/netstandard1.3/ru/System.Security.AccessControl.xml", + "ref/netstandard1.3/zh-hans/System.Security.AccessControl.xml", + "ref/netstandard1.3/zh-hant/System.Security.AccessControl.xml", + "ref/netstandard2.0/System.Security.AccessControl.dll", + "ref/netstandard2.0/System.Security.AccessControl.xml", + "ref/uap10.0.16299/_._", + "runtimes/win/lib/net46/System.Security.AccessControl.dll", "runtimes/win/lib/net461/System.Security.AccessControl.dll", "runtimes/win/lib/net461/System.Security.AccessControl.xml", - "runtimes/win/lib/net6.0/System.Security.AccessControl.dll", - "runtimes/win/lib/net6.0/System.Security.AccessControl.xml", - "runtimes/win/lib/netstandard2.0/System.Security.AccessControl.dll", - "runtimes/win/lib/netstandard2.0/System.Security.AccessControl.xml", - "system.security.accesscontrol.6.0.0.nupkg.sha512", + "runtimes/win/lib/netcoreapp2.0/System.Security.AccessControl.dll", + "runtimes/win/lib/netcoreapp2.0/System.Security.AccessControl.xml", + "runtimes/win/lib/netstandard1.3/System.Security.AccessControl.dll", + "runtimes/win/lib/uap10.0.16299/_._", + "system.security.accesscontrol.5.0.0.nupkg.sha512", "system.security.accesscontrol.nuspec", - "useSharedDesignerContext.txt" - ] - }, - "System.Security.Cryptography.ProtectedData/6.0.0": { - "sha512": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ==", - "type": "package", - "path": "system.security.cryptography.protecteddata/6.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "buildTransitive/netcoreapp2.0/System.Security.Cryptography.ProtectedData.targets", - "buildTransitive/netcoreapp3.1/_._", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net461/System.Security.Cryptography.ProtectedData.dll", - "lib/net461/System.Security.Cryptography.ProtectedData.xml", - "lib/net6.0/System.Security.Cryptography.ProtectedData.dll", - "lib/net6.0/System.Security.Cryptography.ProtectedData.xml", - "lib/netstandard2.0/System.Security.Cryptography.ProtectedData.dll", - "lib/netstandard2.0/System.Security.Cryptography.ProtectedData.xml", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "runtimes/win/lib/net461/System.Security.Cryptography.ProtectedData.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.ProtectedData.xml", - "runtimes/win/lib/net6.0/System.Security.Cryptography.ProtectedData.dll", - "runtimes/win/lib/net6.0/System.Security.Cryptography.ProtectedData.xml", - "runtimes/win/lib/netstandard2.0/System.Security.Cryptography.ProtectedData.dll", - "runtimes/win/lib/netstandard2.0/System.Security.Cryptography.ProtectedData.xml", - "system.security.cryptography.protecteddata.6.0.0.nupkg.sha512", - "system.security.cryptography.protecteddata.nuspec", - "useSharedDesignerContext.txt" - ] - }, - "System.Security.Permissions/6.0.0": { - "sha512": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==", - "type": "package", - "path": "system.security.permissions/6.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "buildTransitive/netcoreapp2.0/System.Security.Permissions.targets", - "buildTransitive/netcoreapp3.1/_._", - "lib/net461/System.Security.Permissions.dll", - "lib/net461/System.Security.Permissions.xml", - "lib/net5.0/System.Security.Permissions.dll", - "lib/net5.0/System.Security.Permissions.xml", - "lib/net6.0/System.Security.Permissions.dll", - "lib/net6.0/System.Security.Permissions.xml", - "lib/netcoreapp3.1/System.Security.Permissions.dll", - "lib/netcoreapp3.1/System.Security.Permissions.xml", - "lib/netstandard2.0/System.Security.Permissions.dll", - "lib/netstandard2.0/System.Security.Permissions.xml", - "runtimes/win/lib/net461/System.Security.Permissions.dll", - "runtimes/win/lib/net461/System.Security.Permissions.xml", - "system.security.permissions.6.0.0.nupkg.sha512", - "system.security.permissions.nuspec", - "useSharedDesignerContext.txt" + "useSharedDesignerContext.txt", + "version.txt" ] }, "System.Security.Principal.Windows/5.0.0": { @@ -7484,27 +6077,26 @@ "useSharedDesignerContext.txt" ] }, - "System.Windows.Extensions/6.0.0": { - "sha512": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==", + "Ulid/1.3.4": { + "sha512": "6IaGquwjjfW+BoHSV844y12Uy2kxbboYNmsibxr2lotcSPAA3LKy1CKcAQ8JOdAdL4xMoDNXA1oxG41w7fbr6Q==", "type": "package", - "path": "system.windows.extensions/6.0.0", + "path": "ulid/1.3.4", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net6.0/System.Windows.Extensions.dll", - "lib/net6.0/System.Windows.Extensions.xml", - "lib/netcoreapp3.1/System.Windows.Extensions.dll", - "lib/netcoreapp3.1/System.Windows.Extensions.xml", - "runtimes/win/lib/net6.0/System.Windows.Extensions.dll", - "runtimes/win/lib/net6.0/System.Windows.Extensions.xml", - "runtimes/win/lib/netcoreapp3.1/System.Windows.Extensions.dll", - "runtimes/win/lib/netcoreapp3.1/System.Windows.Extensions.xml", - "system.windows.extensions.6.0.0.nupkg.sha512", - "system.windows.extensions.nuspec", - "useSharedDesignerContext.txt" + "lib/net6.0/Ulid.dll", + "lib/net6.0/Ulid.xml", + "lib/net7.0/Ulid.dll", + "lib/net7.0/Ulid.xml", + "lib/net8.0/Ulid.dll", + "lib/net8.0/Ulid.xml", + "lib/netstandard2.0/Ulid.dll", + "lib/netstandard2.0/Ulid.xml", + "lib/netstandard2.1/Ulid.dll", + "lib/netstandard2.1/Ulid.xml", + "ulid.1.3.4.nupkg.sha512", + "ulid.nuspec" ] }, "YamlDotNet/16.3.0": { @@ -7592,10 +6184,12 @@ "projectFileDependencyGroups": { "net8.0": [ "Microsoft.AspNetCore.Authentication.JwtBearer >= 8.0.2", - "Microsoft.EntityFrameworkCore.Design >= 8.0.2", - "Microsoft.EntityFrameworkCore.Tools >= 8.0.2", + "Microsoft.EntityFrameworkCore >= 8.0.13", + "Microsoft.EntityFrameworkCore.Design >= 8.0.13", + "Microsoft.EntityFrameworkCore.Relational >= 8.0.13", + "Microsoft.EntityFrameworkCore.Tools >= 8.0.13", "NLog.Web.AspNetCore >= 5.3.11", - "Pomelo.EntityFrameworkCore.MySql >= 8.0.2", + "Pomelo.EntityFrameworkCore.MySql >= 8.0.3", "ServerCommon >= 1.0.0", "Swashbuckle.AspNetCore.Annotations >= 7.2.0" ] @@ -7654,18 +6248,28 @@ "version": "[8.0.2, )", "versionCentrallyManaged": true }, + "Microsoft.EntityFrameworkCore": { + "target": "Package", + "version": "[8.0.13, )", + "versionCentrallyManaged": true + }, "Microsoft.EntityFrameworkCore.Design": { "include": "Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive", "suppressParent": "All", "target": "Package", - "version": "[8.0.2, )", + "version": "[8.0.13, )", + "versionCentrallyManaged": true + }, + "Microsoft.EntityFrameworkCore.Relational": { + "target": "Package", + "version": "[8.0.13, )", "versionCentrallyManaged": true }, "Microsoft.EntityFrameworkCore.Tools": { "include": "Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive", "suppressParent": "All", "target": "Package", - "version": "[8.0.2, )", + "version": "[8.0.13, )", "versionCentrallyManaged": true }, "NLog.Web.AspNetCore": { @@ -7675,7 +6279,7 @@ }, "Pomelo.EntityFrameworkCore.MySql": { "target": "Package", - "version": "[8.0.2, )", + "version": "[8.0.3, )", "versionCentrallyManaged": true }, "Swashbuckle.AspNetCore.Annotations": { @@ -7687,17 +6291,10 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "8.2.2", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", - "AutoMapper": "14.0.0", - "AutoMapper.AspNetCore.OData.EFCore": "7.0.1", - "AutoMapper.Collection": "11.0.0", - "AutoMapper.Collection.EntityFrameworkCore": "11.0.0", - "AutoMapper.Contrib.Autofac.DependencyInjection": "9.0.0", - "AutoMapper.Data": "9.0.0", - "AutoMapper.EF6": "3.0.1", - "AutoMapper.Extensions.EnumMapping": "4.1.0", - "AutoMapper.Extensions.ExpressionMapping": "8.0.0", + "AutoMapper": "15.0.1", "AWS.Logger.NLog": "3.3.4", "AWSSDK.Core": "3.7.402.46", "AWSSDK.DynamoDBv2": "3.7.407", @@ -7713,6 +6310,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -7720,6 +6318,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -7735,10 +6334,12 @@ "Microsoft.AspNetCore.OpenApi": "8.0.6", "Microsoft.Data.Sqlite": "8.0.6", "Microsoft.Diagnostics.NETCore.Client": "0.2.621003", - "Microsoft.EntityFrameworkCore.Design": "8.0.2", - "Microsoft.EntityFrameworkCore.Tools": "8.0.2", + "Microsoft.EntityFrameworkCore": "8.0.13", + "Microsoft.EntityFrameworkCore.Design": "8.0.13", + "Microsoft.EntityFrameworkCore.Relational": "8.0.13", + "Microsoft.EntityFrameworkCore.Tools": "8.0.13", "Microsoft.Extensions.Caching.StackExchangeRedis": "8.0.6", - "Microsoft.Extensions.Configuration": "9.0.4", + "Microsoft.Extensions.Configuration": "8.0.0", "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", "Microsoft.Extensions.Configuration.UserSecrets": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.0", @@ -7747,8 +6348,8 @@ "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", "Microsoft.Extensions.Http": "8.0.0", "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.4", - "Microsoft.Extensions.Options": "9.0.4", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2", "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0", "Microsoft.NET.Test.Sdk": "17.12.0", "Microsoft.OpenApi": "1.6.22", @@ -7758,18 +6359,27 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", - "Pomelo.EntityFrameworkCore.MySql": "8.0.2", + "Pomelo.EntityFrameworkCore.MySql": "8.0.3", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -7782,8 +6392,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "8.0.0", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, diff --git a/BrokerApiCore/obj/project.nuget.cache b/BrokerApiCore/obj/project.nuget.cache index aaaee82..a0d7dae 100644 --- a/BrokerApiCore/obj/project.nuget.cache +++ b/BrokerApiCore/obj/project.nuget.cache @@ -1,20 +1,10 @@ { "version": 2, - "dgSpecHash": "1KN19Ey1X4g=", + "dgSpecHash": "OxiZmBRLTm0=", "success": true, "projectFilePath": "D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiCore\\BrokerApiCore.csproj", "expectedPackageFiles": [ "C:\\Users\\user\\.nuget\\packages\\asyncstatemachine\\1.3.2\\asyncstatemachine.1.3.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\autofac\\8.2.0\\autofac.8.2.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\automapper\\14.0.0\\automapper.14.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\automapper.aspnetcore.odata.efcore\\7.0.1\\automapper.aspnetcore.odata.efcore.7.0.1.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\automapper.collection\\11.0.0\\automapper.collection.11.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\automapper.collection.entityframeworkcore\\11.0.0\\automapper.collection.entityframeworkcore.11.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\automapper.contrib.autofac.dependencyinjection\\9.0.0\\automapper.contrib.autofac.dependencyinjection.9.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\automapper.data\\9.0.0\\automapper.data.9.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\automapper.ef6\\3.0.1\\automapper.ef6.3.0.1.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\automapper.extensions.enummapping\\4.1.0\\automapper.extensions.enummapping.4.1.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\automapper.extensions.expressionmapping\\8.0.0\\automapper.extensions.expressionmapping.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\aws.logger.core\\3.3.3\\aws.logger.core.3.3.3.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\aws.logger.nlog\\3.3.4\\aws.logger.nlog.3.3.4.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\awssdk.cloudwatchlogs\\3.7.305.15\\awssdk.cloudwatchlogs.3.7.305.15.nupkg.sha512", @@ -28,12 +18,9 @@ "C:\\Users\\user\\.nuget\\packages\\bcrypt.net-next\\4.0.3\\bcrypt.net-next.4.0.3.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\bouncycastle.cryptography\\2.5.1\\bouncycastle.cryptography.2.5.1.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\commandlineparser\\2.9.1\\commandlineparser.2.9.1.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\delegatedecompiler\\0.34.0\\delegatedecompiler.0.34.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\delegatedecompiler.entityframework\\0.34.0\\delegatedecompiler.entityframework.0.34.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\dnsclient\\1.6.1\\dnsclient.1.6.1.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\dotnet.multimap\\2.2.1\\dotnet.multimap.2.2.1.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\dumpextensions\\2.0.0\\dumpextensions.2.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\entityframework\\6.5.1\\entityframework.6.5.1.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\google.protobuf\\3.27.1\\google.protobuf.3.27.1.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\grpc.aspnetcore\\2.63.0\\grpc.aspnetcore.2.63.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\grpc.aspnetcore.server\\2.63.0\\grpc.aspnetcore.server.2.63.0.nupkg.sha512", @@ -46,65 +33,55 @@ "C:\\Users\\user\\.nuget\\packages\\grpc.reflection\\2.63.0\\grpc.reflection.2.63.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\humanizer.core\\2.14.1\\humanizer.core.2.14.1.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\jwt\\11.0.0\\jwt.11.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\logicbuilder.expressions.utils\\7.0.0\\logicbuilder.expressions.utils.7.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\logicbuilder.structures\\7.0.0\\logicbuilder.structures.7.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.aspnetcore.authentication.jwtbearer\\8.0.2\\microsoft.aspnetcore.authentication.jwtbearer.8.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.aspnetcore.odata\\9.1.1\\microsoft.aspnetcore.odata.9.1.1.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.bcl.asyncinterfaces\\8.0.0\\microsoft.bcl.asyncinterfaces.8.0.0.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.bcl.asyncinterfaces\\6.0.0\\microsoft.bcl.asyncinterfaces.6.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.bcl.memory\\9.0.0\\microsoft.bcl.memory.9.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.codeanalysis.analyzers\\3.3.3\\microsoft.codeanalysis.analyzers.3.3.3.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.codeanalysis.common\\4.5.0\\microsoft.codeanalysis.common.4.5.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.codeanalysis.csharp\\4.5.0\\microsoft.codeanalysis.csharp.4.5.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.codeanalysis.csharp.workspaces\\4.5.0\\microsoft.codeanalysis.csharp.workspaces.4.5.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.codeanalysis.workspaces.common\\4.5.0\\microsoft.codeanalysis.workspaces.common.4.5.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.csharp\\4.7.0\\microsoft.csharp.4.7.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.diagnostics.netcore.client\\0.2.621003\\microsoft.diagnostics.netcore.client.0.2.621003.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore\\9.0.2\\microsoft.entityframeworkcore.9.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore.abstractions\\9.0.2\\microsoft.entityframeworkcore.abstractions.9.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore.analyzers\\9.0.2\\microsoft.entityframeworkcore.analyzers.9.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore.design\\8.0.2\\microsoft.entityframeworkcore.design.8.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore.relational\\8.0.2\\microsoft.entityframeworkcore.relational.8.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore.tools\\8.0.2\\microsoft.entityframeworkcore.tools.8.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.caching.abstractions\\9.0.2\\microsoft.extensions.caching.abstractions.9.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.caching.memory\\9.0.2\\microsoft.extensions.caching.memory.9.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.configuration\\9.0.4\\microsoft.extensions.configuration.9.0.4.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.configuration.abstractions\\9.0.4\\microsoft.extensions.configuration.abstractions.9.0.4.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore\\8.0.13\\microsoft.entityframeworkcore.8.0.13.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore.abstractions\\8.0.13\\microsoft.entityframeworkcore.abstractions.8.0.13.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore.analyzers\\8.0.13\\microsoft.entityframeworkcore.analyzers.8.0.13.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore.design\\8.0.13\\microsoft.entityframeworkcore.design.8.0.13.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore.relational\\8.0.13\\microsoft.entityframeworkcore.relational.8.0.13.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.entityframeworkcore.tools\\8.0.13\\microsoft.entityframeworkcore.tools.8.0.13.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.caching.abstractions\\8.0.0\\microsoft.extensions.caching.abstractions.8.0.0.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.caching.memory\\8.0.1\\microsoft.extensions.caching.memory.8.0.1.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.configuration\\8.0.0\\microsoft.extensions.configuration.8.0.0.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.configuration.abstractions\\8.0.0\\microsoft.extensions.configuration.abstractions.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.configuration.binder\\8.0.0\\microsoft.extensions.configuration.binder.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.configuration.fileextensions\\8.0.0\\microsoft.extensions.configuration.fileextensions.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.configuration.json\\8.0.0\\microsoft.extensions.configuration.json.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.configuration.usersecrets\\8.0.0\\microsoft.extensions.configuration.usersecrets.8.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.dependencyinjection\\9.0.2\\microsoft.extensions.dependencyinjection.9.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.dependencyinjection.abstractions\\9.0.4\\microsoft.extensions.dependencyinjection.abstractions.9.0.4.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.dependencymodel\\8.0.0\\microsoft.extensions.dependencymodel.8.0.0.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.dependencyinjection\\8.0.1\\microsoft.extensions.dependencyinjection.8.0.1.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.dependencyinjection.abstractions\\8.0.2\\microsoft.extensions.dependencyinjection.abstractions.8.0.2.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.dependencymodel\\8.0.2\\microsoft.extensions.dependencymodel.8.0.2.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.diagnostics\\8.0.0\\microsoft.extensions.diagnostics.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.diagnostics.abstractions\\8.0.0\\microsoft.extensions.diagnostics.abstractions.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.fileproviders.abstractions\\8.0.0\\microsoft.extensions.fileproviders.abstractions.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.fileproviders.physical\\8.0.0\\microsoft.extensions.fileproviders.physical.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.filesystemglobbing\\8.0.0\\microsoft.extensions.filesystemglobbing.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.http\\8.0.0\\microsoft.extensions.http.8.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.logging\\9.0.2\\microsoft.extensions.logging.9.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.logging.abstractions\\9.0.2\\microsoft.extensions.logging.abstractions.9.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.objectpool\\6.0.3\\microsoft.extensions.objectpool.6.0.3.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.options\\9.0.4\\microsoft.extensions.options.9.0.4.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.logging\\8.0.1\\microsoft.extensions.logging.8.0.1.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.logging.abstractions\\8.0.3\\microsoft.extensions.logging.abstractions.8.0.3.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.options\\8.0.2\\microsoft.extensions.options.8.0.2.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.options.configurationextensions\\8.0.0\\microsoft.extensions.options.configurationextensions.8.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.primitives\\9.0.4\\microsoft.extensions.primitives.9.0.4.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.extensions.primitives\\8.0.0\\microsoft.extensions.primitives.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.identitymodel.abstractions\\8.9.0\\microsoft.identitymodel.abstractions.8.9.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.identitymodel.jsonwebtokens\\8.9.0\\microsoft.identitymodel.jsonwebtokens.8.9.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.identitymodel.logging\\8.9.0\\microsoft.identitymodel.logging.8.9.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.identitymodel.protocols\\7.1.2\\microsoft.identitymodel.protocols.7.1.2.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.identitymodel.protocols.openidconnect\\7.1.2\\microsoft.identitymodel.protocols.openidconnect.7.1.2.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.identitymodel.tokens\\8.9.0\\microsoft.identitymodel.tokens.8.9.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.odata.core\\8.2.2\\microsoft.odata.core.8.2.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.odata.edm\\8.2.2\\microsoft.odata.edm.8.2.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.odata.modelbuilder\\2.0.0\\microsoft.odata.modelbuilder.2.0.0.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\microsoft.netcore.platforms\\5.0.0\\microsoft.netcore.platforms.5.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.openapi\\1.6.22\\microsoft.openapi.1.6.22.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.spatial\\8.2.2\\microsoft.spatial.8.2.2.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\microsoft.win32.registry\\5.0.0\\microsoft.win32.registry.5.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\microsoft.win32.systemevents\\6.0.0\\microsoft.win32.systemevents.6.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\mongodb.analyzer\\1.5.0\\mongodb.analyzer.1.5.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\mongodb.bson\\3.3.0\\mongodb.bson.3.3.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\mongodb.driver\\3.3.0\\mongodb.driver.3.3.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\mono.reflection\\2.0.0\\mono.reflection.2.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\mono.texttemplating\\2.2.1\\mono.texttemplating.2.2.1.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\mysqlconnector\\2.4.0\\mysqlconnector.2.4.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\neosmart.asynclock\\3.2.1\\neosmart.asynclock.3.2.1.nupkg.sha512", @@ -123,12 +100,8 @@ "C:\\Users\\user\\.nuget\\packages\\nlog.web.aspnetcore\\5.3.11\\nlog.web.aspnetcore.5.3.11.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\otp.net\\1.4.0\\otp.net.1.4.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\pipelines.sockets.unofficial\\2.2.8\\pipelines.sockets.unofficial.2.2.8.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\pomelo.entityframeworkcore.mysql\\8.0.2\\pomelo.entityframeworkcore.mysql.8.0.2.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\pomelo.entityframeworkcore.mysql\\8.0.3\\pomelo.entityframeworkcore.mysql.8.0.3.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\rabbitmq.client\\6.8.1\\rabbitmq.client.6.8.1.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\runtime.native.system.data.sqlclient.sni\\4.7.0\\runtime.native.system.data.sqlclient.sni.4.7.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\runtime.win-arm64.runtime.native.system.data.sqlclient.sni\\4.4.0\\runtime.win-arm64.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\runtime.win-x64.runtime.native.system.data.sqlclient.sni\\4.4.0\\runtime.win-x64.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\runtime.win-x86.runtime.native.system.data.sqlclient.sni\\4.4.0\\runtime.win-x86.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\sharpcompress\\0.30.1\\sharpcompress.0.30.1.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\snappier\\1.0.0\\snappier.1.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\ssh.net\\2025.0.0\\ssh.net.2025.0.0.nupkg.sha512", @@ -140,35 +113,28 @@ "C:\\Users\\user\\.nuget\\packages\\swashbuckle.aspnetcore.swagger\\7.2.0\\swashbuckle.aspnetcore.swagger.7.2.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\swashbuckle.aspnetcore.swaggergen\\7.2.0\\swashbuckle.aspnetcore.swaggergen.7.2.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.buffers\\4.5.1\\system.buffers.4.5.1.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.codedom\\6.0.0\\system.codedom.6.0.0.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\system.codedom\\4.4.0\\system.codedom.4.4.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.collections.immutable\\6.0.0\\system.collections.immutable.6.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.componentmodel.annotations\\5.0.0\\system.componentmodel.annotations.5.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.composition\\6.0.0\\system.composition.6.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.composition.attributedmodel\\6.0.0\\system.composition.attributedmodel.6.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.composition.convention\\6.0.0\\system.composition.convention.6.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.composition.hosting\\6.0.0\\system.composition.hosting.6.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.composition.runtime\\6.0.0\\system.composition.runtime.6.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.composition.typedparts\\6.0.0\\system.composition.typedparts.6.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.configuration.configurationmanager\\6.0.1\\system.configuration.configurationmanager.6.0.1.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.data.sqlclient\\4.8.6\\system.data.sqlclient.4.8.6.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.diagnostics.diagnosticsource\\9.0.2\\system.diagnostics.diagnosticsource.9.0.2.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.drawing.common\\6.0.0\\system.drawing.common.6.0.0.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\system.diagnostics.diagnosticsource\\8.0.0\\system.diagnostics.diagnosticsource.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.identitymodel.tokens.jwt\\8.9.0\\system.identitymodel.tokens.jwt.8.9.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.io.pipelines\\6.0.3\\system.io.pipelines.6.0.3.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.memory\\4.5.5\\system.memory.4.5.5.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.reactive\\6.0.0\\system.reactive.6.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.reflection.emit\\4.7.0\\system.reflection.emit.4.7.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.reflection.metadata\\6.0.1\\system.reflection.metadata.6.0.1.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.runtime.compilerservices.unsafe\\6.0.0\\system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.security.accesscontrol\\6.0.0\\system.security.accesscontrol.6.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.security.cryptography.protecteddata\\6.0.0\\system.security.cryptography.protecteddata.6.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.security.permissions\\6.0.0\\system.security.permissions.6.0.0.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\system.security.accesscontrol\\5.0.0\\system.security.accesscontrol.5.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.security.principal.windows\\5.0.0\\system.security.principal.windows.5.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.text.encoding.codepages\\6.0.0\\system.text.encoding.codepages.6.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.text.encodings.web\\8.0.0\\system.text.encodings.web.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.text.json\\8.0.0\\system.text.json.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.threading.channels\\7.0.0\\system.threading.channels.7.0.0.nupkg.sha512", - "C:\\Users\\user\\.nuget\\packages\\system.windows.extensions\\6.0.0\\system.windows.extensions.6.0.0.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\ulid\\1.3.4\\ulid.1.3.4.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\yamldotnet\\16.3.0\\yamldotnet.16.3.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\zstdsharp.port\\0.7.3\\zstdsharp.port.0.7.3.nupkg.sha512" ], diff --git a/BrokerApiCore/obj/project.packagespec.json b/BrokerApiCore/obj/project.packagespec.json index 62d9947..12e3bfe 100644 --- a/BrokerApiCore/obj/project.packagespec.json +++ b/BrokerApiCore/obj/project.packagespec.json @@ -1 +1 @@ -"restore":{"projectUniqueName":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiCore\\BrokerApiCore.csproj","projectName":"BrokerApiCore","projectPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiCore\\BrokerApiCore.csproj","outputPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiCore\\obj\\","projectStyle":"PackageReference","centralPackageVersionsManagementEnabled":true,"originalTargetFrameworks":["net8.0"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"net8.0":{"targetAlias":"net8.0","projectReferences":{"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\ServerCommon\\ServerCommon.csproj":{"projectPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\ServerCommon\\ServerCommon.csproj"}}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"}}"frameworks":{"net8.0":{"targetAlias":"net8.0","dependencies":{"Microsoft.AspNetCore.Authentication.JwtBearer":{"target":"Package","version":"[8.0.2, )","versionCentrallyManaged":true},"Microsoft.EntityFrameworkCore.Design":{"include":"Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive","suppressParent":"All","target":"Package","version":"[8.0.2, )","versionCentrallyManaged":true},"Microsoft.EntityFrameworkCore.Tools":{"include":"Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive","suppressParent":"All","target":"Package","version":"[8.0.2, )","versionCentrallyManaged":true},"NLog.Web.AspNetCore":{"target":"Package","version":"[5.3.11, )","versionCentrallyManaged":true},"Pomelo.EntityFrameworkCore.MySql":{"target":"Package","version":"[8.0.2, )","versionCentrallyManaged":true},"Swashbuckle.AspNetCore.Annotations":{"target":"Package","version":"[7.2.0, )","versionCentrallyManaged":true}},"centralPackageVersions":{"Asp.Versioning.Mvc":"8.1.0","Asp.Versioning.Mvc.ApiExplorer":"8.1.0","AspNetCore.Swagger.Fluent.Annotations":"1.0.4","AsyncStateMachine":"1.3.2","AutoMapper":"14.0.0","AutoMapper.AspNetCore.OData.EFCore":"7.0.1","AutoMapper.Collection":"11.0.0","AutoMapper.Collection.EntityFrameworkCore":"11.0.0","AutoMapper.Contrib.Autofac.DependencyInjection":"9.0.0","AutoMapper.Data":"9.0.0","AutoMapper.EF6":"3.0.1","AutoMapper.Extensions.EnumMapping":"4.1.0","AutoMapper.Extensions.ExpressionMapping":"8.0.0","AWS.Logger.NLog":"3.3.4","AWSSDK.Core":"3.7.402.46","AWSSDK.DynamoDBv2":"3.7.407","AWSSDK.EC2":"3.7.330.4","AWSSDK.ElasticLoadBalancingV2":"3.7.303.15","AWSSDK.OpenSearchService":"3.7.404.81","AWSSDK.S3":"3.7.416.15","AWSSDK.SecurityToken":"3.7.401.89","Axion.ConcurrentHashSet":"1.0.0","BCrypt.Net-Next":"4.0.3","BouncyCastle.Cryptography":"2.5.1","CommandLineParser":"2.9.1","CommunityToolkit.Diagnostics":"8.2.2","CommunityToolkit.Mvvm":"8.2.2","Costura.Fody":"5.7.0","Csv":"2.0.93","DocumentFormat.OpenXml":"2.20.0","DotNet.MultiMap":"2.2.1","DumpExtensions":"2.0.0","ExcelDataReader":"3.6.0","ExcelDataReader.DataSet":"3.6.0","ExcelNumberFormat":"1.1.0","Google.Protobuf":"3.27.1","Grpc.AspNetCore":"2.63.0","Grpc.AspNetCore.Server.Reflection":"2.63.0","Grpc.Tools":"2.64.0","JWT":"11.0.0","MaterialDesignColors":"3.1.1-ci850","MaterialDesignThemes":"5.1.1-ci850","MaxMind.Db":"4.1.0","MaxMind.GeoIP2":"5.2.0","MediatR":"12.3.0","Microsoft.AspNetCore.Authentication.JwtBearer":"8.0.2","Microsoft.AspNetCore.Mvc.Testing":"8.0.2","Microsoft.AspNetCore.OpenApi":"8.0.6","Microsoft.Data.Sqlite":"8.0.6","Microsoft.Diagnostics.NETCore.Client":"0.2.621003","Microsoft.EntityFrameworkCore.Design":"8.0.2","Microsoft.EntityFrameworkCore.Tools":"8.0.2","Microsoft.Extensions.Caching.StackExchangeRedis":"8.0.6","Microsoft.Extensions.Configuration":"9.0.4","Microsoft.Extensions.Configuration.Abstractions":"8.0.0","Microsoft.Extensions.Configuration.UserSecrets":"8.0.0","Microsoft.Extensions.DependencyInjection":"8.0.0","Microsoft.Extensions.DependencyInjection.Abstractions":"8.0.1","Microsoft.Extensions.Hosting":"8.0.0","Microsoft.Extensions.Hosting.Abstractions":"8.0.0","Microsoft.Extensions.Http":"8.0.0","Microsoft.Extensions.Logging":"8.0.0","Microsoft.Extensions.Logging.Abstractions":"9.0.4","Microsoft.Extensions.Options":"9.0.4","Microsoft.Extensions.Options.ConfigurationExtensions":"8.0.0","Microsoft.NET.Test.Sdk":"17.12.0","Microsoft.OpenApi":"1.6.22","Microsoft.Xaml.Behaviors.Wpf":"1.1.122","MongoDB.Analyzer":"1.5.0","MongoDB.Bson":"3.3.0","MongoDB.Driver":"3.3.0","MongoDB.Driver.Core":"2.30.0","Moq":"4.20.72","MySqlConnector":"2.4.0","NeoSmart.AsyncLock":"3.2.1","Newtonsoft.Json":"13.0.3","Nito.AsyncEx":"5.1.2","NJsonSchema.CodeGeneration.CSharp":"10.9.0","NLog":"5.4.0","NLog.Web.AspNetCore":"5.3.11","Otp.NET":"1.4.0","p4api.net":"2023.1.248.4623","Polly":"8.5.1","Pomelo.EntityFrameworkCore.MySql":"8.0.2","RabbitMQ.Client":"6.8.1","SharpSvn":"1.14003.272","sqlite-net-pcl":"1.9.172","SSH.NET":"2025.0.0","StackExchange.Redis":"2.8.31","StackExchange.Redis.Extensions.Core":"11.0.0","StackExchange.Redis.Extensions.Newtonsoft":"11.0.0","StackExchange.Redis.MultiplexerPool":"2.2.0","Swashbuckle.AspNetCore":"7.2.0","Swashbuckle.AspNetCore.Annotations":"7.2.0","Swashbuckle.AspNetCore.Filters":"8.0.2","Swashbuckle.AspNetCore.SwaggerUI":"7.2.0","System.IdentityModel.Tokens.Jwt":"8.9.0","xunit":"2.9.3","xunit.assert":"2.9.2","xunit.runner.visualstudio":"3.0.1","YamlDotNet":"16.3.0"},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"Microsoft.NETCore.App":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json"}} \ No newline at end of file +"restore":{"projectUniqueName":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiCore\\BrokerApiCore.csproj","projectName":"BrokerApiCore","projectPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiCore\\BrokerApiCore.csproj","outputPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiCore\\obj\\","projectStyle":"PackageReference","centralPackageVersionsManagementEnabled":true,"originalTargetFrameworks":["net8.0"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"net8.0":{"targetAlias":"net8.0","projectReferences":{"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\ServerCommon\\ServerCommon.csproj":{"projectPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\ServerCommon\\ServerCommon.csproj"}}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"}}"frameworks":{"net8.0":{"targetAlias":"net8.0","dependencies":{"Microsoft.AspNetCore.Authentication.JwtBearer":{"target":"Package","version":"[8.0.2, )","versionCentrallyManaged":true},"Microsoft.EntityFrameworkCore":{"target":"Package","version":"[8.0.13, )","versionCentrallyManaged":true},"Microsoft.EntityFrameworkCore.Design":{"include":"Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive","suppressParent":"All","target":"Package","version":"[8.0.13, )","versionCentrallyManaged":true},"Microsoft.EntityFrameworkCore.Relational":{"target":"Package","version":"[8.0.13, )","versionCentrallyManaged":true},"Microsoft.EntityFrameworkCore.Tools":{"include":"Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive","suppressParent":"All","target":"Package","version":"[8.0.13, )","versionCentrallyManaged":true},"NLog.Web.AspNetCore":{"target":"Package","version":"[5.3.11, )","versionCentrallyManaged":true},"Pomelo.EntityFrameworkCore.MySql":{"target":"Package","version":"[8.0.3, )","versionCentrallyManaged":true},"Swashbuckle.AspNetCore.Annotations":{"target":"Package","version":"[7.2.0, )","versionCentrallyManaged":true}},"centralPackageVersions":{"Asp.Versioning.Mvc":"8.1.0","Asp.Versioning.Mvc.ApiExplorer":"8.1.0","Aspire.Hosting.AppHost":"8.2.2","AspNetCore.Swagger.Fluent.Annotations":"1.0.4","AsyncStateMachine":"1.3.2","AutoMapper":"15.0.1","AWS.Logger.NLog":"3.3.4","AWSSDK.Core":"3.7.402.46","AWSSDK.DynamoDBv2":"3.7.407","AWSSDK.EC2":"3.7.330.4","AWSSDK.ElasticLoadBalancingV2":"3.7.303.15","AWSSDK.OpenSearchService":"3.7.404.81","AWSSDK.S3":"3.7.416.15","AWSSDK.SecurityToken":"3.7.401.89","Axion.ConcurrentHashSet":"1.0.0","BCrypt.Net-Next":"4.0.3","BouncyCastle.Cryptography":"2.5.1","CommandLineParser":"2.9.1","CommunityToolkit.Diagnostics":"8.2.2","CommunityToolkit.Mvvm":"8.2.2","Costura.Fody":"5.7.0","coverlet.collector":"6.0.4","Csv":"2.0.93","DocumentFormat.OpenXml":"2.20.0","DotNet.MultiMap":"2.2.1","DumpExtensions":"2.0.0","ExcelDataReader":"3.6.0","ExcelDataReader.DataSet":"3.6.0","ExcelNumberFormat":"1.1.0","FluentAssertions":"8.3.0","Google.Protobuf":"3.27.1","Grpc.AspNetCore":"2.63.0","Grpc.AspNetCore.Server.Reflection":"2.63.0","Grpc.Tools":"2.64.0","JWT":"11.0.0","MaterialDesignColors":"3.1.1-ci850","MaterialDesignThemes":"5.1.1-ci850","MaxMind.Db":"4.1.0","MaxMind.GeoIP2":"5.2.0","MediatR":"12.3.0","Microsoft.AspNetCore.Authentication.JwtBearer":"8.0.2","Microsoft.AspNetCore.Mvc.Testing":"8.0.2","Microsoft.AspNetCore.OpenApi":"8.0.6","Microsoft.Data.Sqlite":"8.0.6","Microsoft.Diagnostics.NETCore.Client":"0.2.621003","Microsoft.EntityFrameworkCore":"8.0.13","Microsoft.EntityFrameworkCore.Design":"8.0.13","Microsoft.EntityFrameworkCore.Relational":"8.0.13","Microsoft.EntityFrameworkCore.Tools":"8.0.13","Microsoft.Extensions.Caching.StackExchangeRedis":"8.0.6","Microsoft.Extensions.Configuration":"8.0.0","Microsoft.Extensions.Configuration.Abstractions":"8.0.0","Microsoft.Extensions.Configuration.UserSecrets":"8.0.0","Microsoft.Extensions.DependencyInjection":"8.0.0","Microsoft.Extensions.DependencyInjection.Abstractions":"8.0.1","Microsoft.Extensions.Hosting":"8.0.0","Microsoft.Extensions.Hosting.Abstractions":"8.0.0","Microsoft.Extensions.Http":"8.0.0","Microsoft.Extensions.Logging":"8.0.0","Microsoft.Extensions.Logging.Abstractions":"8.0.3","Microsoft.Extensions.Options":"8.0.2","Microsoft.Extensions.Options.ConfigurationExtensions":"8.0.0","Microsoft.NET.Test.Sdk":"17.12.0","Microsoft.OpenApi":"1.6.22","Microsoft.Xaml.Behaviors.Wpf":"1.1.122","MongoDB.Analyzer":"1.5.0","MongoDB.Bson":"3.3.0","MongoDB.Driver":"3.3.0","MongoDB.Driver.Core":"2.30.0","Moq":"4.20.72","MSTest.TestAdapter":"3.8.3","MSTest.TestFramework":"3.8.3","MySqlConnector":"2.4.0","NeoSmart.AsyncLock":"3.2.1","Newtonsoft.Json":"13.0.3","Nito.AsyncEx":"5.1.2","NJsonSchema.CodeGeneration.CSharp":"10.9.0","NLog":"5.4.0","NLog.Extensions.Logging":"5.3.11","NLog.Web.AspNetCore":"5.3.11","NSubstituteAutoSubstitute":"1.1.0","OneOf":"3.0.271","Otp.NET":"1.4.0","p4api.net":"2023.1.248.4623","Polly":"8.5.1","Pomelo.EntityFrameworkCore.MySql":"8.0.3","RabbitMQ.Client":"6.8.1","RedLock.net":"2.3.2","Sentry.AspNetCore":"5.10.0","Sentry.Extensions.Logging":"5.10.0","Sentry.NLog":"5.10.0","SharpSvn":"1.14003.272","sqlite-net-pcl":"1.9.172","SSH.NET":"2025.0.0","StackExchange.Redis":"2.8.31","StackExchange.Redis.Extensions.Core":"11.0.0","StackExchange.Redis.Extensions.Newtonsoft":"11.0.0","StackExchange.Redis.MultiplexerPool":"2.2.0","Swashbuckle.AspNetCore":"7.2.0","Swashbuckle.AspNetCore.Annotations":"7.2.0","Swashbuckle.AspNetCore.Filters":"8.0.2","Swashbuckle.AspNetCore.SwaggerUI":"7.2.0","System.IdentityModel.Tokens.Jwt":"8.9.0","System.IO.Hashing":"8.0.0","Ulid":"1.3.4","xunit":"2.9.3","xunit.assert":"2.9.3","xunit.extensibility.core":"2.9.3","xunit.runner.visualstudio":"3.0.1","YamlDotNet":"16.3.0"},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"Microsoft.NETCore.App":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json"}} \ No newline at end of file diff --git a/BrokerApiCore/obj/rider.project.model.nuget.info b/BrokerApiCore/obj/rider.project.model.nuget.info index c69393a..24b7b8d 100644 --- a/BrokerApiCore/obj/rider.project.model.nuget.info +++ b/BrokerApiCore/obj/rider.project.model.nuget.info @@ -1 +1 @@ -17447875961277534 \ No newline at end of file +17538446637724083 \ No newline at end of file diff --git a/BrokerApiCore/obj/rider.project.restore.info b/BrokerApiCore/obj/rider.project.restore.info index de1d89a..24b7b8d 100644 --- a/BrokerApiCore/obj/rider.project.restore.info +++ b/BrokerApiCore/obj/rider.project.restore.info @@ -1 +1 @@ -17460517968336276 \ No newline at end of file +17538446637724083 \ No newline at end of file diff --git a/BrokerApiServer/BrokerApiServer.csproj b/BrokerApiServer/BrokerApiServer.csproj index dbec9e9..ef6934b 100644 --- a/BrokerApiServer/BrokerApiServer.csproj +++ b/BrokerApiServer/BrokerApiServer.csproj @@ -1,5 +1,4 @@ - - + Exe net8.0 @@ -8,108 +7,55 @@ true true Debug;Release;Shipping - true 8600,8602,8603,8604 true + true true UTF-8 - - - - - - - - - + + + + + + + 1591 full - + $(DefineConstants);SEQUENCE - 1591 full - + $(DefineConstants);SEQUENCE - 1591 full true - + $(DefineConstants);SEQUENCE - + + + - + - all - runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers; buildtransitive - all - runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - - - - - + + + + - - - <_ContentIncludedByDefault Remove="_Config\nlog.config" /> - <_ContentIncludedByDefault Remove="_Config\ServerConfig-Local.json" /> - <_ContentIncludedByDefault Remove="_Config\ServerConfig.json" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/BrokerApiServer/BrokerServerService.cs b/BrokerApiServer/BrokerServerService.cs new file mode 100644 index 0000000..eb96bc5 --- /dev/null +++ b/BrokerApiServer/BrokerServerService.cs @@ -0,0 +1,111 @@ +using CommandLine; + +using ControlCenter.NamedPipe.Model; + +using ServerBase; + +using ServerCore; + +namespace BrokerApiServer; + +using BrokerApiCore; + +public class BrokerServerService +{ + public class CmdOptions + { + [Option("config", Default = "ServerConfig.json")] + public string Config { get; set; } = "ServerConfig.json"; + + [Option('p', "port", Default = 12000, Required = true, HelpText = "Server Port")] + public int Port { get; init; } = 12000; + + [Option('s', "swagger", Default = false, Required = false, HelpText = "Show Swagger Mode")] + public bool UseSwagger { get; init; } = false; + } + + private BrokerServerLogic? _serverLogic; + private CancellationTokenSource _onStartCts = new(); + public BrokerServerLogic ServerLogic => _serverLogic!; + public CmdOptions Options { get; private set; } = new(); + + public bool IsRunning { get; private set; } = false; + + public async Task> GetServerInfosByServerType(ServerType serverType) + { + var (result, serverInfos) = await ServerLogic.getServerInfosByServerType(serverType); + return result.isFail() ? [] : serverInfos; + } + + public async Task Run(string[] args) + { + var result = new Result(); + + Log.getLogger().info($"GameServer start"); + + var name = nameof(BrokerServerService); + + ParserResult? parser_result = Parser.Default.ParseArguments(args); + Options = parser_result?.Value ?? new CmdOptions(); + try + { + await RunServer(Options); + } + catch (Exception e) + { + var err_msg = $"Exception !!!, {name}.runServer() : message:{e}"; + result.setFail(ServerErrorCode.TryCatchException, err_msg); + await _onStartCts.CancelAsync(); + } + finally + { + if (result.isFail()) + { + Log.getLogger().error($"{name} finally error !!! - {result.toBasicString()}"); + } + } + + Log.getLogger().info($"{name} terminated"); + Log.shutdown(); + return 0; + } + + private async Task RunServer(CmdOptions argCmdOptions) + { + var result = new Result(); + + var key_options = new Dictionary { { "config", argCmdOptions.Config }, }; + + var configuration = new ConfigurationBuilder().AddInMemoryCollection(key_options!).Build(); + NullReferenceCheckHelper.throwIfNull(configuration, () => $"configuration is null !!!"); + var server_config = new ServerConfig(); + server_config.setAppParamPort((ushort)argCmdOptions.Port); + + _serverLogic = new BrokerServerLogic(server_config); + _serverLogic.setServerType(nameof(ServerType.BrokerApi)); + _serverLogic.setConfiguration(configuration); + _serverLogic.OnServerStart = () => { _onStartCts.Cancel(); }; + + var namedOptionBuilder = new NamedPipeClientOptionBuilder(); + namedOptionBuilder.setIP(NetworkHelper.getEthernetLocalIPv4()) + .setPort(argCmdOptions.Port) + .setType(nameof(ServerType.BrokerApi)) + .setServiceCategory(nameof(ServiceCategory.Caliverse)); + + result = await _serverLogic.onRunServer(namedOptionBuilder); + if (result.isFail()) + { + return result; + } + + return result; + } + + public async Task WaitOnStart() + { + while (!_onStartCts.IsCancellationRequested) + { + await Task.Delay(10); + } + } +} diff --git a/BrokerApiServer/BrokerServiceExtensions.cs b/BrokerApiServer/BrokerServiceExtensions.cs new file mode 100644 index 0000000..5659083 --- /dev/null +++ b/BrokerApiServer/BrokerServiceExtensions.cs @@ -0,0 +1,73 @@ +using ServerBase; +using ServerCore; +using System.Reflection; +using static ControlCenter.NamedPipeHost.Extensions.ExtensionsForClient; + +namespace BrokerApiServer; +public static class BrokerServiceExtensions +{ + public static void AddBrokerServerService(this IServiceCollection services, string[] args) + { + var server = new BrokerServerService(); + _ = server.Run(args); + server.WaitOnStart().Wait(); + + var broker_server_config = AppBuilderExtensions.initBrokerServerConfig(); + + services.AddSingleton(server); + services.AddSingleton(server.ServerLogic); + services.AddSingleton(x => server.ServerLogic.getServerConfig()); + services.AddSingleton(x => server.ServerLogic.getDynamoDbClient()); + services.addCoreServices(broker_server_config); + services.addInfraServices(broker_server_config); + + services.AddHttpContextAccessor(); + services.AddEndpointsApiExplorer(); + services.AddControllers(options => { options.Filters.Add(new ResultExceptionFilter()); }); + services.AddHealthChecks(); + services.AddSwaggerGen(SwaggerSettingHelper.setSwaggerGen); + + var useControlCenter = broker_server_config.ServiceType != nameof(ServiceType.Dev); + if (useControlCenter) + { + var assemblies = new List(AppDomain.CurrentDomain.GetAssemblies()); + services.AddNamedPipelineClientManager(assemblies, ServerControlCenter.ServerType.BrokerApi, + nameof(ServiceCategory.Caliverse), NetworkHelper.getEthernetLocalIPv4(), server.Options.Port); + } + } + + public static void UseBrokerServerService(this WebApplication app) + { + var server = app.Services.GetRequiredService(); + app.UseCors("Everything"); + app.UseHttpsRedirection(); + app.UseMiddleware(); + app.UseAuthorization(); + app.UseRouting(); + app.MapControllers(); + if (server.Options.UseSwagger) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } + + app.MapHealthChecks("/healthcheck"); + app.metadataMangerInit(); + app.validateRepoConnections(); + + var useControlCenter = server.ServerLogic.getServerConfig().ServiceType != nameof(ServiceType.Dev); + if (useControlCenter) + { + app.UseNamedPipelineClientManager(); + } + + // 호스트 시작 종료 콜백 처리 + var logger = Log.getLogger(); + var serviceProvider = app.Services; + serviceProvider.GetRequiredService().ApplicationStarted.Register(() => + { + logger.Info($"Broker Api Server Service Started"); + }); + serviceProvider.GetRequiredService().ApplicationStopped.Register(Log.shutdown); + } +} diff --git a/BrokerApiServer/Common/RequireAdminAuthAttribute.cs b/BrokerApiServer/Common/RequireAdminAuthAttribute.cs index f0b84c1..a0e22b4 100644 --- a/BrokerApiServer/Common/RequireAdminAuthAttribute.cs +++ b/BrokerApiServer/Common/RequireAdminAuthAttribute.cs @@ -1,12 +1,8 @@ -using Microsoft.AspNetCore.Mvc.Filters; - - -using System.IdentityModel.Tokens.Jwt; +using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; - +using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.IdentityModel.Tokens; - using ServerCore; namespace BrokerApiServer; diff --git a/BrokerApiServer/Common/RequirePlanetAuthAttribute.cs b/BrokerApiServer/Common/RequirePlanetAuthAttribute.cs index fd61d87..6b3f73d 100644 --- a/BrokerApiServer/Common/RequirePlanetAuthAttribute.cs +++ b/BrokerApiServer/Common/RequirePlanetAuthAttribute.cs @@ -1,5 +1,5 @@ -using Microsoft.AspNetCore.Mvc.Filters; -using BrokerApiCore; +using BrokerApiCore; +using Microsoft.AspNetCore.Mvc.Filters; namespace BrokerApiServer; diff --git a/BrokerApiServer/Common/RequireUserJwtAuthAttribute.cs b/BrokerApiServer/Common/RequireUserJwtAuthAttribute.cs index 9432403..e036727 100644 --- a/BrokerApiServer/Common/RequireUserJwtAuthAttribute.cs +++ b/BrokerApiServer/Common/RequireUserJwtAuthAttribute.cs @@ -1,6 +1,5 @@ -using Microsoft.AspNetCore.Mvc.Filters; - -using BrokerApiCore; +using BrokerApiCore; +using Microsoft.AspNetCore.Mvc.Filters; namespace BrokerApiServer; diff --git a/BrokerApiServer/Common/ResultExceptionFilter.cs b/BrokerApiServer/Common/ResultExceptionFilter.cs index 9fff12d..12baca4 100644 --- a/BrokerApiServer/Common/ResultExceptionFilter.cs +++ b/BrokerApiServer/Common/ResultExceptionFilter.cs @@ -1,8 +1,7 @@ -using Microsoft.AspNetCore.Mvc; +using BrokerApiCore; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; - using ServerCore; -using BrokerApiCore; namespace BrokerApiServer; diff --git a/BrokerApiServer/Controllers/AccountController.cs b/BrokerApiServer/Controllers/AccountController.cs deleted file mode 100644 index df9469c..0000000 --- a/BrokerApiServer/Controllers/AccountController.cs +++ /dev/null @@ -1,51 +0,0 @@ -// using Microsoft.AspNetCore.Mvc; -// -// using Swashbuckle.AspNetCore.Annotations; -// -// using BrokerCore.ApiModels; -// using BrokerCore.Common; -// -// namespace BrokerApiServer.Controllers; -// -// using BrokerCore.Services; -// -// using Common; -// -// [Route("api/v1/account")] -// [ApiController, SwaggerTag("**PlanetUser 항목으로 이전 후 삭제 예정** (3월 말 예정)")] -// // 플래닛에서 b2b 유저 로그인하는 컨트롤러 -// public class AccountController : PlanetAuthControllerBase -// { -// private readonly UserAuthService m_user_auth_service; -// -// public AccountController(UserAuthService userAuthService) -// { -// m_user_auth_service = userAuthService; -// } -// -// [SwaggerIgnore] -// [HttpPost, Route("login"), RequirePlanetAuth] -// [Produces("application/json")] -// [ProducesResponseType(typeof(LoginResponse), StatusCodes.Status200OK)] -// [ProducesResponseType(typeof(ApiErrorResponse), StatusCodes.Status400BadRequest)] -// [SwaggerOperation(Summary = "웹 포털 런처 토큰으로 칼리버스 유저 인증 처리 (삭제 예정 - 3월말 런칭에 불포함)", -// Description = "플래닛의 클라이언트 구동 시 항상 유저 인증 후 실행해야 함")] -// public async Task login([FromBody] LoginRequest request) -// { -// Guard.Against.isNull(request, ServerErrorCode.InvalidRequest, "Request is empty"); -// Guard.Against.isNullOrEmptyOrWhiteSpace(request.WebPortalToken, ServerErrorCode.InvalidRequest, -// "WebPortalToken does not exist"); -// -// var result = await m_user_auth_service.authByWebPortalToken(request.WebPortalToken, this.PlanetId); -// Guard.Against.resultFail(result); -// return Ok(new LoginResponse { UserGuid = m_user_auth_service.UserGuid, Nickname = m_user_auth_service.Nickname, }); -// } -// -// [SwaggerIgnore] -// [HttpPost, Route("dummy")] -// public async Task dummy([FromBody] DummyRequest request) -// { -// await Task.CompletedTask; -// return Ok(new DummyResponse { Dummy = request.Dummy }); -// } -// } diff --git a/BrokerApiServer/Controllers/AdminController.cs b/BrokerApiServer/Controllers/AdminController.cs index 0dc8b19..8f4d2a2 100644 --- a/BrokerApiServer/Controllers/AdminController.cs +++ b/BrokerApiServer/Controllers/AdminController.cs @@ -1,13 +1,10 @@ -using BrokerApiServer; +using BrokerApiCore; + using Microsoft.AspNetCore.Mvc; using ServerBase; using ServerCommon; using Swashbuckle.AspNetCore.Annotations; - - -using BrokerApiCore; namespace BrokerApiServer.Controllers; - [Route("api/v1/admin")] [ApiController] // 운영자만 접근 가능한 컨트롤러 @@ -118,4 +115,49 @@ public class AdminController : ControllerBase }); return Ok(new PlanetItemExchangeOrderListResponse { Orders = order_dtos, TotalCount = total_count }); } + + + [HttpPost("planet_provider_create"), RequireAdminAuth] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AdminPlanetProviderInfoCreateResponse))] + [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] + [SwaggerOperation(Summary = "서비스 제공자 생성 [Bearer 인증 필요]")] + public async Task planetProviderCreate(AdminPlanetProviderInfoCreateRequest request) + { + await Task.CompletedTask; + return Ok(new { }); + } + + [HttpPost("planet_provider_delete"), RequireAdminAuth] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AdminPlanetProviderInfoDeleteResponse))] + [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] + [SwaggerOperation(Summary = "서비스 제공자 삭제 [Bearer 인증 필요]")] + public async Task planetProviderDelete(AdminPlanetProviderInfoDeleteRequest request) + { + await Task.CompletedTask; + return Ok(new { }); + } + + [HttpPost("planet_provider_get"), RequireAdminAuth] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AdminPlanetProviderInfoGetResponse))] + [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] + [SwaggerOperation(Summary = "서비스 제공자 정보 조회 [Bearer 인증 필요]")] + public async Task planetProviderRead(AdminPlanetProviderInfoGetRequest request) + { + await Task.CompletedTask; + return Ok(new { }); + } + + [HttpPost("planet_provider_update"), RequireAdminAuth] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AdminPlanetProviderInfoUpdateResponse))] + [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] + [SwaggerOperation(Summary = "서비스 제공자 정보 수정 [Bearer 인증 필요]")] + public async Task planetProviderUpdate(AdminPlanetProviderInfoUpdateRequest request) + { + await Task.CompletedTask; + return Ok(new { }); + } } diff --git a/BrokerApiServer/Controllers/CurrencyController.cs b/BrokerApiServer/Controllers/CurrencyController.cs deleted file mode 100644 index 78e6fb6..0000000 --- a/BrokerApiServer/Controllers/CurrencyController.cs +++ /dev/null @@ -1,166 +0,0 @@ -// using Asp.Versioning; -// -// using BrokerCore.ApiModels; -// using BrokerCore.Common; -// using BrokerCore.Services; -// using BrokerCore.DbEntity; -// -// using Microsoft.AspNetCore.Mvc; -// -// using Swashbuckle.AspNetCore.Annotations; -// -// namespace BrokerApiServer.Controllers; -// -// using Common; -// -// [ApiVersion("1.0")] -// [Route("api/v1")] -// [ApiController, SwaggerTag("**PlanetUser 항목으로 이전 후 삭제 예정** (3월 말 예정)")] -// // 플래닛에서 b2b로 통신하는 컨트롤러 -// public sealed class CurrencyController : PlanetAuthControllerBase -// { -// private readonly SapphireExchangeService m_exchange_service; -// -// public CurrencyController( -// SapphireExchangeService exchangeService) -// { -// m_exchange_service = exchangeService; -// } -// -// [SwaggerIgnore] -// [HttpPost("balance/sapphire"), RequirePlanetAuth] -// [Produces("application/json")] -// [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(SapphireResponse))] -// [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] -// [SwaggerOperation(Summary = "사파이어 현재 수량 조회")] -// public async Task getSapphireBalance(SapphireRequest request) -// { -// await m_exchange_service.authUser(request.UserGuid, this.PlanetId); -// -// var current_sapphire_amount = await m_exchange_service.getCurrentSapphire(); -// return Ok(new SapphireResponse { Amount = current_sapphire_amount }); -// } -// -// [SwaggerIgnore] -// [HttpPost("exchange/order/list"), RequirePlanetAuth] -// [Produces("application/json")] -// [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ExchangeOrderListResponse))] -// [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] -// [SwaggerOperation(Summary = "사파이어 교환 주문 목록 조회")] -// public async Task getOrders( -// [SwaggerRequestBody] ExchangeOrderListRequest request, -// CancellationToken cancellationToken) -// { -// var planet_id = this.PlanetId; -// var user_guid = request.UserGuid; -// await m_exchange_service.authUser(user_guid, this.PlanetId); -// -// ExchangeOrderStatus? order_status = request switch -// { -// { Option: FindOption.Completed } => ExchangeOrderStatus.Completed, -// { Option: FindOption.Pending } => ExchangeOrderStatus.Pending, -// _ => null // 전체 조회 -// }; -// var orders = -// await m_exchange_service.findOrderList(planet_id, user_guid, order_status, request.PageIndex, -// request.PageSize, "desc", cancellationToken); -// var mapped_orders = orders.Select(x => new ExchangeOrder -// { -// OrderId = x.OrderId, -// SapphireAmount = x.SapphireReducedDelta, -// SapphireReduceAmount = x.SapphireReducedDelta, -// EmeraldAmount = x.PlanetMoneyIncDelta, -// CreatedAt = x.CreatedAt, -// CompletedAt = x.CompletedAt, -// Status = x.OrderStatus -// }); -// return Ok(new ExchangeOrderListResponse { Orders = mapped_orders }); -// } -// -// // [HttpPost("exchange/order/list_for_user"), RequireUserJwtAuth] -// // [Produces("application/json")] -// // [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ExchangeOrderListResponse))] -// // [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] -// // [SwaggerOperation(Summary = "사파이어 교환 주문 목록 조회")] -// // public async Task getOrdersForUser( -// // [SwaggerRequestBody] ExchangeOrderListRequest request, -// // CancellationToken cancellationToken) -// // { -// // var planet_id = this.PlanetId; -// // var user_guid = request.UserGuid; -// // await m_exchange_service.authUser(user_guid, planet_id); -// // -// // ExchangeOrderStatus? order_status = request switch -// // { -// // { Option: FindOption.Completed } => ExchangeOrderStatus.Completed, -// // { Option: FindOption.Pending } => ExchangeOrderStatus.Pending, -// // _ => null // 전체 조회 -// // }; -// // var orders = -// // await m_exchange_service.findOrderList(planet_id, user_guid, order_status, request.PageIndex, -// // request.PageSize, request.SortOrder, cancellationToken); -// // var mapped_orders = orders.Select(x => new ExchangeOrder -// // { -// // OrderId = x.OrderId, -// // SapphireAmount = x.SapphireReducedDelta, -// // SapphireReduceAmount = x.SapphireReducedDelta, -// // EmeraldAmount = x.PlanetMoneyIncDelta, -// // CreatedAt = x.CreatedAt, -// // CompletedAt = x.CompletedAt, -// // Status = x.OrderStatus -// // }); -// // return Ok(new ExchangeOrderListResponse { Orders = mapped_orders }); -// // } -// -// [SwaggerIgnore] -// [HttpPost("exchange/order/create"), RequirePlanetAuth] -// [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ExchangeOrderResponse))] -// [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] -// [SwaggerOperation(Summary = "사파이어 교환 주문 체결 (사파이어 차감)")] -// public async Task createOrder( -// ExchangeOrderRequest request, -// CancellationToken cancellationToken) -// { -// var user_guid = request.UserGuid; -// await m_exchange_service.authUser(user_guid, this.PlanetId); -// // 메타버스 클라이언트에서 정상적으로 -// Guard.Against.isTrue(m_exchange_service.isUserLoggedIn(), -// ServerErrorCode.MetaverseClientOnConnected, -// "메타버스에 접속 중인 상태. 메타버스에서 정상적으로 로그아웃 한 후에 다시 시도 바람."); -// -// var (order, current_sapphire_balance) = -// await m_exchange_service.createOrder(this.PlanetId, this.PlanetServerType, request.Sapphire, -// request.Emerald); -// -// var response = new ExchangeOrderResponse -// { -// OrderId = order.OrderId, -// SapphireAmount = order.SapphireReducedDelta, -// EmeraldAmount = order.PlanetMoneyIncDelta, -// CurrentSapphireBalance = current_sapphire_balance, -// }; -// return Ok(response); -// } -// -// [SwaggerIgnore] -// [HttpPost("exchange/order/complete"), RequirePlanetAuth] -// [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ExchangeOrderCompleteResponse))] -// [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] -// [SwaggerOperation(Summary = "사파이어 교환 완료 (완료된 이후에 에메랄드 지급")] -// public async Task completeOrder( -// ExchangeOrderCompleteRequest request, -// CancellationToken cancellationToken) -// { -// await m_exchange_service.authUser(request.UserGuid, this.PlanetId); -// -// var order = await m_exchange_service.completeOrder(request.OrderId, cancellationToken); -// var response = new ExchangeOrderCompleteResponse -// { -// OrderId = order.OrderId, -// SapphireAmount = order.SapphireReducedDelta, -// EmeraldAmount = order.PlanetMoneyIncDelta, -// Status = order.OrderStatus, -// }; -// return Ok(response); -// } -// } diff --git a/BrokerApiServer/Controllers/PlanetController.cs b/BrokerApiServer/Controllers/PlanetController.cs index 6f6a744..dca6d6b 100644 --- a/BrokerApiServer/Controllers/PlanetController.cs +++ b/BrokerApiServer/Controllers/PlanetController.cs @@ -1,6 +1,6 @@ -using Microsoft.AspNetCore.Mvc; +using BrokerApiCore; +using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; -using BrokerApiCore; namespace BrokerApiServer.Controllers; diff --git a/BrokerApiServer/Controllers/PlanetUserController.cs b/BrokerApiServer/Controllers/PlanetUserController.cs index f777463..7b3c361 100644 --- a/BrokerApiServer/Controllers/PlanetUserController.cs +++ b/BrokerApiServer/Controllers/PlanetUserController.cs @@ -1,9 +1,8 @@ using System.ComponentModel; +using BrokerApiCore; using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; -using BrokerApiCore; - namespace BrokerApiServer; [Route("api/v1/planet/user")] @@ -34,6 +33,7 @@ public class PlanetUserController : PlanetAuthControllerBase var result = await m_user_auth_service.authByWebPortalToken(request.WebPortalToken, this.PlanetId); Guard.Against.resultFail(result); + return Ok(new LoginResponse { UserGuid = m_user_auth_service.UserGuid, Nickname = m_user_auth_service.Nickname, @@ -54,21 +54,6 @@ public class PlanetUserController : PlanetAuthControllerBase return Ok(new CurrencyBalanceResponse { CaliverseCurrencyType = request.CaliverseCurrencyType, Amount = current_sapphire_amount }); } - // [SwaggerIgnore] - // [HttpPost("sapphire"), RequirePlanetAuth] - // [Produces("application/json")] - // [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CurrencyBalanceResponse))] - // [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] - // [SwaggerOperation(Summary = "!! 삭제 예정 => 칼리버스 측 재화 현재 수량 조회로 대체 [Bearer 인증 필요]")] - // public async Task getSapphireBalance(CurrencyBalanceRequest request) - // { - // var sso_account_id = string.Empty; - // await m_exchange_service.validateAndGetUser(sso_account_id, request.UserGuid, this.PlanetId); - // - // var current_sapphire_amount = await m_exchange_service.getCurrentSapphire(); - // return Ok(new CurrencyBalanceResponse { Amount = current_sapphire_amount }); - // } - [HttpPost("exchange/order/list"), RequirePlanetAuth] [Produces("application/json")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(PlanetItemExchangeOrderListResponse))] diff --git a/BrokerApiServer/Controllers/UserController.cs b/BrokerApiServer/Controllers/UserController.cs deleted file mode 100644 index 843520d..0000000 --- a/BrokerApiServer/Controllers/UserController.cs +++ /dev/null @@ -1,93 +0,0 @@ -// -// using Asp.Versioning; -// -// using BrokerCore.ApiModels; -// using BrokerCore.Common; -// using BrokerCore.DbEntity; -// using BrokerCore.Services; -// -// using Microsoft.AspNetCore.Mvc; -// -// using ServerCommon; -// -// using Swashbuckle.AspNetCore.Annotations; -// -// namespace BrokerApiServer.Controllers; -// -// [ApiVersion("1.0")] -// [Route("api/v1/user")] -// [ApiController, SwaggerTag("**PlanetUser 항목으로 이전 후 삭제 예정** (3월 말 예정)")] -// // 특별한 인증 없이 런처 토큰만으로도 사용 가능한 컨트롤러 -// public class UserController : ControllerBase -// { -// private readonly SapphireExchangeService m_exchange_service; -// private readonly IServerLogic m_server_logic; -// -// public UserController( -// IServerLogic serverLogic, -// SapphireExchangeService exchangeService) -// { -// m_server_logic = serverLogic; -// m_exchange_service = exchangeService; -// } -// -// [SwaggerIgnore] -// [HttpPost("balance/sapphire"), RequireUserJwtAuth] -// [Produces("application/json")] -// [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserSapphireResponse))] -// [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] -// [SwaggerOperation(Summary = "유저 사파이어 현재 수량 조회 (유저 sso jwt bearer header 필요)", -// Description = "웹 런처에서 발급한 토큰을 header의 authorization에 bearer 형식으로 넣어주세요.")] -// public async Task getSapphireBalance(UserSapphireRequest _) -// { -// var dynamo_db_client = m_server_logic.getDynamoDbClient(); -// var user_guid = this.HttpContext.Items["user_guid"] as string ?? string.Empty; -// CurrencyControlHelper.setDbConnector(dynamo_db_client); -// var (result, current_sapphire_amount_double) = -// await CurrencyControlHelper.getMoneyByUserGuid(user_guid, CurrencyType.Sapphire); -// Guard.Against.resultFail(result); -// -// var current_sapphire_amount = Convert.ToInt64(current_sapphire_amount_double); -// return Ok(new UserSapphireResponse { SapphireAmount = current_sapphire_amount }); -// } -// -// [SwaggerIgnore] -// [HttpPost("exchange/order/list"), RequireUserJwtAuth] -// [Produces("application/json")] -// [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserExchangeOrderListResponse))] -// [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] -// [SwaggerOperation(Summary = "유저 사파이어 교환 주문 목록 조회 (유저 sso jwt bearer header 필요)", -// Description = "웹 런처에서 발급한 토큰을 header의 authorization에 bearer 형식으로 넣어주세요.\n 이기몹의 PlanetId는 igm26_iggymob 입니다.")] -// public async Task getOrdersForUser( -// [SwaggerRequestBody] UserExchangeOrderListRequest request, -// CancellationToken cancellationToken) -// { -// var planet_id = request.PlanetId; -// var user_guid = this.HttpContext.Items["user_guid"]?.ToString() ?? string.Empty; -// Guard.Against.isNullOrEmptyOrWhiteSpace(user_guid, -// ServerErrorCode.AccountNotFound, "해당 유저를 찾을 수 없음"); -// await m_exchange_service.authUser(user_guid, string.Empty); -// -// ExchangeOrderStatus? order_status = request switch -// { -// { Option: FindOption.Completed } => ExchangeOrderStatus.Completed, -// { Option: FindOption.Pending } => ExchangeOrderStatus.Pending, -// _ => null // 전체 조회 -// }; -// var orders = -// await m_exchange_service.findOrderList(planet_id, user_guid, order_status, -// request.PageIndex, request.PageSize, request.SortOrder, -// cancellationToken); -// var mapped_orders = orders.Select(x => new ExchangeOrder -// { -// OrderId = x.OrderId, -// SapphireAmount = x.SapphireReducedDelta, -// SapphireReduceAmount = x.SapphireReducedDelta, -// EmeraldAmount = x.PlanetMoneyIncDelta, -// CreatedAt = x.CreatedAt, -// CompletedAt = x.CompletedAt, -// Status = x.OrderStatus -// }); -// return Ok(new UserExchangeOrderListResponse { Orders = mapped_orders }); -// } -// } diff --git a/BrokerApiServer/Directory.Build.props b/BrokerApiServer/Directory.Build.props index 181094c..6e1e8e4 100644 --- a/BrokerApiServer/Directory.Build.props +++ b/BrokerApiServer/Directory.Build.props @@ -1,8 +1,8 @@ - + false false ..\..\obj\AnyCPU\$(MSBuildProjectName)\ ..\..\bin\ - \ No newline at end of file + diff --git a/BrokerApiServer/Extensions/AppBuilderExtensions.cs b/BrokerApiServer/Extensions/AppBuilderExtensions.cs index 267c5e4..5e7b897 100644 --- a/BrokerApiServer/Extensions/AppBuilderExtensions.cs +++ b/BrokerApiServer/Extensions/AppBuilderExtensions.cs @@ -1,11 +1,10 @@ using System.Text; +using BrokerApiCore; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; - using ServerBase; using ServerCore; -using BrokerApiCore; namespace BrokerApiServer; @@ -13,19 +12,8 @@ public static class AppBuilderExtensions { public static void initGlobalNlog() { - // nlog 설정 - var nlog_config_path = "./Config/nlog.config"; - var full_path = Path.GetFullPath(nlog_config_path); - if (!File.Exists(full_path)) - { - full_path = Path.GetFullPath(nlog_config_path, AppContext.BaseDirectory); - if (!File.Exists(full_path)) - { - throw new ApplicationException($"nlog.config 파일을 찾을 수 없음 => {full_path}"); - } - } - - Log.NLogFileName = full_path; + Log.initLog(nameof(ServerType.BrokerApi), "Developer"); // nlog 설정 + Log.getLogger().info("NLog Init"); } public static ServerConfigMetaverseBroker initBrokerServerConfig() @@ -42,35 +30,18 @@ public static class AppBuilderExtensions throw new ApplicationException("ServerConfig.json 파일을 찾을 수 없음"); } } + server_config.setConfigFilePath(full_path); var result = server_config.tryLoadConfig().GetAwaiter().GetResult(); Guard.Against.resultFail(result); return server_config; } - public static void addBrokerServerLogic(this IServiceCollection services, - ServerConfigMetaverseBroker serverConfig) - { - var broker_logic = new BrokerServerLogic(serverConfig); - var result = broker_logic.onInit().GetAwaiter().GetResult(); - if (result.isFail()) - { - Log.getLogger().error($"BrokerServerLogic Init Failed => {result}"); - throw new ApplicationException($"BrokerServerLogic Init Failed => {result}"); - } - ServerLogicApp.setServerLogicApp(broker_logic); - services.AddSingleton(broker_logic); - } - public static void addCoreServices(this IServiceCollection services, ServerConfigMetaverseBroker serverConfig) { services.AddSingleton(serverConfig); - var broker_logic = new BrokerServerLogic(serverConfig); - var result = broker_logic.onInit().GetAwaiter().GetResult(); - services.AddSingleton(broker_logic); - services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { @@ -80,7 +51,9 @@ public static class AppBuilderExtensions ValidateAudience = false, ValidateLifetime = true, ValidateIssuerSigningKey = true, - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(serverConfig.MetaverseBroker?.JwtSecretKey ?? String.Empty)) + IssuerSigningKey = + new SymmetricSecurityKey( + Encoding.UTF8.GetBytes(serverConfig.MetaverseBroker?.JwtSecretKey ?? String.Empty)) }; }); @@ -90,7 +63,7 @@ public static class AppBuilderExtensions // 메타데이터 관련 서비스 등록 services.AddSingleton(); - services.AddScoped( provider => + services.AddScoped(provider => { var meta_loader = provider.GetRequiredService(); return new BrokerMetaTableRef(meta_loader.getMetaTable()); @@ -104,14 +77,8 @@ public static class AppBuilderExtensions }; services.AddSingleton(jwt_option); - services.AddScoped(provider => - { - return new JwtGenerator(jwt_option); - }); - services.AddScoped(provider => - { - return new JwtParser(jwt_option); - }); + services.AddScoped(provider => { return new JwtGenerator(jwt_option); }); + services.AddScoped(provider => { return new JwtParser(jwt_option); }); services.AddScoped(); @@ -128,6 +95,8 @@ public static class AppBuilderExtensions services.AddScoped(); services.AddScoped(); + services.AddSingleton(); + services.AddDbContext(options => { var connection_string = serverConfig.SsoAccountDb; @@ -148,29 +117,19 @@ public static class AppBuilderExtensions { var connection_string = serverConfig.MetaverseBroker?.MetaverseBrokerDb; - options.UseMySql(connection_string, ServerVersion.AutoDetect(connection_string), my_sql_options => + options.UseMySql(connection_string, ServerVersion.AutoDetect(connection_string), mySqlOptions => { - my_sql_options.EnableRetryOnFailure( + mySqlOptions.EnableRetryOnFailure( maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null ); - my_sql_options.CommandTimeout(60); - my_sql_options.MaxBatchSize(20); + mySqlOptions.CommandTimeout(60); + mySqlOptions.MaxBatchSize(20); }); }); } - // todo: isLocal 삭제 예정 - public static void addAppServices(this WebApplicationBuilder builder) - { - initGlobalNlog(); - var server_config = initBrokerServerConfig(); - builder.Services.addBrokerServerLogic(server_config); - builder.Services.addCoreServices(server_config); - builder.Services.addInfraServices(server_config); - } - public static void brokerServerLogicInit(this IApplicationBuilder app) { var server_logic = app.ApplicationServices.GetRequiredService() as BrokerServerLogic; @@ -191,20 +150,29 @@ public static class AppBuilderExtensions public static void validateRepoConnections(this IApplicationBuilder app) { - var env = app.ApplicationServices.GetRequiredService(); // db 접속 유무 검사 using var scope = app.ApplicationServices.CreateScope(); var services = scope.ServiceProvider; - validateSsoAccountDb().Wait(); - validateMetaverseBrokerDb().Wait(); - migrateMetaverseBrokerDbForDevelopment().Wait(); + try + { + validateSsoAccountDb().Wait(); + validateMetaverseBrokerDb().Wait(); + // 마이그레이션은 코드에서 호출하지 말고, 별도의 migration script를 통해서 수행한다. + migrateMetaverseBrokerDbForDevelopment().Wait(); + } + catch (Exception ex) + { + Log.getLogger().error($"[Check Tunneling] Repo Connections Validation Failed => {ex.Message}"); + throw new ApplicationException($"Repo Connections Validation Failed => {ex.Message}"); + } + return; async Task validateSsoAccountDb() { - var context = services.GetRequiredService(); try { + var context = services.GetRequiredService(); if (await context.Database.CanConnectAsync()) { Log.getLogger().info($"Sso Account DB Connection OK => {context.Database.GetConnectionString()}"); @@ -217,17 +185,17 @@ public static class AppBuilderExtensions { Log.getLogger() .error( - $"Sso Account DB Connection Failed => {ex.Message}, {context.Database.GetConnectionString()}"); + $"Sso Account DB Connection Failed => {ex.Message}"); } - throw new ApplicationException($"Failed to connect Sso Account Db !!!"); + // throw new ApplicationException($"Failed to connect Sso Account Db !!!"); } async Task validateMetaverseBrokerDb() { - var context = services.GetRequiredService(); try { + var context = services.GetRequiredService(); if (await context.Database.CanConnectAsync()) { Log.getLogger() @@ -237,15 +205,16 @@ public static class AppBuilderExtensions if (migrations.Any()) { Log.getLogger().error($"{context.Database.ToString()} 마이그레션이 필요함 !!!"); - migrations.Select( x => x.ToString()).ToList().ForEach(x => Log.getLogger().warn(x)); + migrations.Select(x => x.ToString()).ToList().ForEach(x => Log.getLogger().warn(x)); } + return; } } catch (Exception ex) { Log.getLogger() - .error($"Metaverse-Broker DB 접속 예외 발생 => {ex.Message}, {context.Database.GetConnectionString()}"); + .error($"Metaverse-Broker DB 접속 예외 발생 => {ex.Message}"); } throw new ApplicationException($"Failed to connect Broker Db !!!"); @@ -268,4 +237,80 @@ public static class AppBuilderExtensions } } } + + public static void addDynamoDb(this IServiceCollection services) + { + services.AddSingleton(x => + { + var dynamo_db_client = new DynamoDbClient(); + return dynamo_db_client; + // var result = new Result(); + // var config = x.GetRequiredService(); + // + // (var error_code, var table_names) = + // ServerConfigHelper.getDynamoDbTableNamesWithServiceType(config.ServiceType); + // if (error_code.isFail()) + // { + // var err_msg = + // $"Failed to DynamoDbClient.getDynamoDbTableNameWithServiceType() !!! : {config.toBasicString()}"; + // result.setFail(error_code, err_msg); + // Log.getLogger().error(err_msg); + // Guard.Against.resultFail(result); + // } + // + // var dynamo_db_client = new DynamoDbClient(); + // var dynamodb_url = config.Dynamodb; + // var region = config.AWS.Region; + // var access_key = config.AWS.AccessKey; + // var secret_key = config.AWS.SecretKey; + // var use_local_db = config.AWS.LocalDynamoDB; + // if (result.isFail()) + // { + // Log.getLogger().error($"Failed to connectToDb !!! "); + // Guard.Against.resultFail(result); + // } + // + // dynamo_db_client.connectToDb(table_names, use_local_db, dynamodb_url, access_key, secret_key, region); + // return dynamo_db_client; + }); + } + + public static void useDynamoDb(this IApplicationBuilder app) + { + var result = new Result(); + var config = app.ApplicationServices.GetRequiredService(); + + (var error_code, var table_names) = + ServerConfigHelper.getDynamoDbTableNamesWithServiceType(config.ServiceType); + if (error_code.isFail()) + { + var err_msg = + $"Failed to DynamoDbClient.getDynamoDbTableNameWithServiceType() !!! : {config.toBasicString()}"; + result.setFail(error_code, err_msg); + Log.getLogger().error(err_msg); + Guard.Against.resultFail(result); + } + + var dynamo_db_client = app.ApplicationServices.GetRequiredService(); + var dynamodb_url = config.Dynamodb; + var region = config.AWS.Region; + var access_key = config.AWS.AccessKey; + var secret_key = config.AWS.SecretKey; + var use_local_db = config.AWS.LocalDynamoDB; + result = dynamo_db_client.connectToDb(table_names, use_local_db, dynamodb_url, access_key, secret_key, region); + if (result.isFail()) + { + Log.getLogger().error($"Failed to connectToDb !!! "); + Guard.Against.resultFail(result); + } + + var task = dynamo_db_client.createDBIfNotExists(false); + task.Wait(); + if (false == task.Result) + { + var err_msg = $"Failed to create DB Table !!!"; + result.setFail(ServerErrorCode.DynamoDbTableCreateFailed, err_msg); + Guard.Against.resultFail(result); + } + } } diff --git a/BrokerApiServer/NamedPipePacketHandler/ForceStopServerPacketHandler.cs b/BrokerApiServer/NamedPipePacketHandler/ForceStopServerPacketHandler.cs index 9a2f16a..8f04839 100644 --- a/BrokerApiServer/NamedPipePacketHandler/ForceStopServerPacketHandler.cs +++ b/BrokerApiServer/NamedPipePacketHandler/ForceStopServerPacketHandler.cs @@ -1,31 +1,33 @@ using ControlCenter.NamedPipe; using ControlCenter.NamedPipeHost.Manager; +using ServerBase; using ServerControlCenter; using ServerCore; -namespace BrokerApiServer; +namespace MatchServer; public class ForceStopServerMessageReceiver : NamedPipeReceiver { - private readonly ServerInfoManager m_info_manager; + // private readonly ServerInfoManager m_info_manager; - public ForceStopServerMessageReceiver(ServerInfoManager infoManager) - { - m_info_manager = infoManager; - } + public ForceStopServerMessageReceiver() + { + // m_info_manager = infoManager; + } - public override async Task Handle(A2S_REQ_FORCE_STOP_SERVER message, string message_id) - { - Log.getLogger().debug($"{nameof(ForceStopServerMessageReceiver)}: Receive - message_id[{message_id}] / message[{message}]"); + public override async Task Handle(A2S_REQ_FORCE_STOP_SERVER message, string message_id) + { + Log.getLogger().debug($"{nameof(ForceStopServerMessageReceiver)}: Receive - message_id[{message_id}] / message[{message}]"); - m_info_manager.setServerStatus(ServerStatus.Stop); + await NamedPipeMonitor.ChangeServerStatus(ServerStatus.Stop); + // m_info_manager.setServerStatus(ServerStatus.Stop); - // 정보 전달 대기 - await Task.Delay(1_000); + // 정보 전달 대기 + await Task.Delay(1_000); - // process 종료 - Environment.Exit(0); - } + // process 종료 + Environment.Exit(0); + } } diff --git a/BrokerApiServer/NamedPipePacketHandler/StopServerPacketHandler.cs b/BrokerApiServer/NamedPipePacketHandler/StopServerPacketHandler.cs index 919325f..77fbf6f 100644 --- a/BrokerApiServer/NamedPipePacketHandler/StopServerPacketHandler.cs +++ b/BrokerApiServer/NamedPipePacketHandler/StopServerPacketHandler.cs @@ -5,25 +5,26 @@ using ServerCore; namespace BrokerApiServer; +using ControlCenter.NamedPipe; + +using ServerBase; + public class StopServerMessageReceiver : NamedPipeReceiver { - private readonly ServerInfoManager m_info_manager; + public StopServerMessageReceiver() + { + } - public StopServerMessageReceiver(ServerInfoManager infoManager) - { - m_info_manager = infoManager; - } + public override async Task Handle(A2S_REQ_STOP_SERVER message, string message_id) + { + Log.getLogger().debug($"{nameof(StopServerMessageReceiver)}: Receive - message_id[{message_id}] / message[{message}]"); - public override async Task Handle(A2S_REQ_STOP_SERVER message, string message_id) - { - Log.getLogger().debug($"{nameof(StopServerMessageReceiver)}: Receive - message_id[{message_id}] / message[{message}]"); + await NamedPipeMonitor.ChangeServerStatus(ServerStatus.Stop); - m_info_manager.setServerStatus(ServerStatus.Stop); + // 정보 전달 대기 + await Task.Delay(1_000); - // 정보 전달 대기 - await Task.Delay(1_000); - - // process 종료 - Environment.Exit(0); - } + // process 종료 + Environment.Exit(0); + } } diff --git a/BrokerApiServer/Program.cs b/BrokerApiServer/Program.cs index 5e1435d..88db0a0 100644 --- a/BrokerApiServer/Program.cs +++ b/BrokerApiServer/Program.cs @@ -1,63 +1,18 @@ -using System.Reflection; using BrokerApiServer; using CommandLine; -using ControlCenter.NamedPipeHost.Extensions; -using ServerCore; -//============================================================================= -// 인자 설정 -//============================================================================= var port = 12000; -var use_swagger = false; -var parsed_arguments = Parser.Default.ParseArguments(args); -if (parsed_arguments is Parsed parsed) + +var parsed_arguments = Parser.Default.ParseArguments(args); +if (parsed_arguments is Parsed parsed) { port = parsed.Value.Port; - use_swagger = parsed.Value.UseSwagger ? true : false; } var cmd_options = new string[] { "--urls", $"http://*:{port}" }; - var builder = WebApplication.CreateBuilder(cmd_options); -builder.addAppServices(); -builder.Services.AddHttpContextAccessor(); -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddControllers(options => { options.Filters.Add(new ResultExceptionFilter()); }); -builder.Services.AddHealthChecks(); -builder.Services.AddSwaggerGen(SwaggerSettingHelper.setSwaggerGen); - -var assemblies = new List(AppDomain.CurrentDomain.GetAssemblies()); -builder.Services.AddNamedPipelineClientManager(assemblies, ServerControlCenter.ServerType.BrokerApi, - ServiceCategory.Caliverse.ToString(), NetworkHelper.getEthernetLocalIPv4(), port); +builder.Services.AddBrokerServerService(args); var app = builder.Build(); -app.UseCors("Everything"); -app.UseHttpsRedirection(); -app.UseMiddleware(); -app.UseAuthorization(); -app.UseRouting(); -app.MapControllers(); -if (use_swagger) -{ - app.UseSwagger(); - app.UseSwaggerUI(); -} - -app.MapHealthChecks("/healthcheck"); - -app.validateRepoConnections(); -app.brokerServerLogicInit(); - -// todo: 스키마 변경 시 자동으로 앱에서 마이그레이션 하지 않고, dotnet ef 툴을 사용할 것 -// app.metadataMangerInit(); - -app.Services.GetRequiredService().ApplicationStarted.Register(() => -{ - Log.getLogger().info($"Env : {app.Environment.EnvironmentName}"); - Log.getLogger().info($"BrokerApiServer started {app.Urls}"); -}); -app.Services.GetRequiredService().ApplicationStopped.Register(Log.shutdown); - -app.UseNamedPipelineClientManager(); - +app.UseBrokerServerService(); app.Run(); diff --git a/BrokerApiServer/Properties/launchSettings.json b/BrokerApiServer/Properties/launchSettings.json index d16a905..00aaabe 100644 --- a/BrokerApiServer/Properties/launchSettings.json +++ b/BrokerApiServer/Properties/launchSettings.json @@ -2,14 +2,15 @@ "profiles": { "http": { "commandName": "Project", - "commandLineArgs": "-p 12000 -s --named-pipe true", + "commandLineArgs": "-p 12000 -s", "launchBrowser": true, "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, - "applicationUrl": "http://localhost:12000" + "applicationUrl": "http://localhost:12000", + "workingDirectory": "../../bin/$(Configuration)" } } } diff --git a/BrokerApiTest/BrokerApiTest.csproj b/BrokerApiTest/BrokerApiTest.csproj index fd95c54..f4adcf8 100644 --- a/BrokerApiTest/BrokerApiTest.csproj +++ b/BrokerApiTest/BrokerApiTest.csproj @@ -1,54 +1,51 @@ - - - - net8.0 - enable - enable + + + net8.0 + enable + enable Debug;Release - false - true + false + true false - BrokerTest + BrokerTest + true Library + false - 1591 full - 1591 full - - - - - - - - PreserveNewest - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - + + + + + + PreserveNewest + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/BrokerApiTest/Config/ServerConfig-Dev.json b/BrokerApiTest/Config/ServerConfig-Dev.json deleted file mode 100644 index a0ce71b..0000000 --- a/BrokerApiTest/Config/ServerConfig-Dev.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "LogDir": "./Logs", - "DumpDir": "./", - "LocalServer": true, - "SingleThreaded": false, - "ServiceType": "Dev", - "StandaloneMode": true, - "OfflineMode": false, - "DefaultMaxUser": 500, - "ClientListen": { - "Ip": "", - "Port": 0 - }, - - "SessionKeepAliveTimeSec": 3600, - "MinWorkerThreadCount": 100, - "MinIoThreadCount": 0, - - "AccountLoginBlockEnable": false, - - "SsoAccountDb": "Server=localhost;Port=13306;User ID=external_ro;Password=bQNEXbRWQTtV6bwlqktGyBiuf2KqYF;Database=caliverse", - "SsoAccountAuthJwtSecretKey": "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78", - - "AccountNftDb": "Server=localhost;Port=13306;User ID=external_ro;Password=bQNEXbRWQTtV6bwlqktGyBiuf2KqYF;Database=caliverse", - - "Redis": "127.0.0.1:6379,password=KT-i5#i%-%LxKfZ5YJj6,AsyncTimeout=30000,SyncTimeout=30000,ssl=false,abortConnect=false", - - "Dynamodb": "http://localhost:8000", - - "MongoDb": { - "ConnectionString": "mongodb://devmetaversemongoconnect:dev#$943cali@localhost:27017", - "DatabaseName": "Metaverse", - "MinConnectionPoolSize": 0, - "MaxConnectionPoolSize": 100, - "WaitQueueTimeoutSecs": 120 - }, - - "AWS": { - "Enable": true, - "LocalDynamoDB": false, - "AccessKey": "AKIA4G3CB4Z5T6JUPHJN", - "SecretKey": "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", - "Region": "us-west-2", - "MilestoneName": "MS5", - - - "CloudWatchLog": { - "Enable": false, - "LogGroup": "MetaverseLog-Dev", - "LogNamePattern": "Developer", - "LogLevel": "Debug", - "Layout": { - "logTime": "${date:universalTime=true:format=yyyy-MM-ddTHH\\:mm\\:ss.fffZ}", - "server": "${event-properties:server}", - "ip/port": "${event-properties:ip/port}", - "threadId": "${threadid}", - "level": "${level:upperCase=true}", - "message": "${message}", - "function": "${event-properties:memberName}", - "filePath": "${event-properties:filePath}", - "lineNumber": "${event-properties:lineNumber}" - } - }, - - "S3": { - "ServiceFolderName": "Dev", - "MyhomeUgcInfoBucketName": "metaverse-myhomeugc-test", - "BeaconAppProfileBucketName": "metaverse-beacon-appprofile" - } - }, - - - "ClientProgramVersionCheck": false, - "ClientMinimumRequiredLogicVersion": 0, - "ProgramVersionPath": ".\\Version\\", - "ProgramVersion": { - "MetaSchemaVersion": "MetaSchemaVersion.json", - "MetaDataVersion": "MetaDataVersion.json", - "PacketVersion": "PacketVersion.json", - "ResourceVersion": "ServerResourceVersion.json", - "ConfigVersion": "ServerConfigVersion.json", - "LogicVersion": "ServerBinaryVersion.json", - "DbSchemaVersion": "DbSchemaVersion.json" - }, - "CheatCommandAlwaysAllow": true, - - - "AuthRule": { - "ClientStandaloneAllow": true, - "ClientBySsoAccountAuthWithLauncherAllow": false, - "PlatformTypeAllows": "" - }, - - "LoadBalancingRule": { - "AuthToGameRule": { - "Rule": "WeightedRoundRobin", - "MinRate": 0, - "MaxRate": 100, - "UserLanguageBased": true - } - }, - - "ServerApiUrlCatalog": [ - { "BillingApiServerUrl": { "Ko": "https://caliverse.io/shop" } }, - { "ChatAiApiServerUrl": { "Ko": "https://ai-dev-api.caliverse.io" } }, - { "S3ResourceImageUrl": { "Ko": "https://d3s9natejb9ydz.cloudfront.net/Dev" } } - ], - - "NftRule": { - "NftDBAccess": false, - "CPNftForOwnerAllGetUrl": "https://dev-api.caliverse.io/v1/nft/game" - }, - - "EchoSystem": { - "BaseAddress": "https://eco-system-dev-rollup-admin-api.caliverse.io", - "SlackAddress": "https://hooks.slack.com/services/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" - }, - - "AIChat": { - "BaseAddress": "https://ai-dev-api.caliverse.io", - "PrivateKey": "PPt+3OLhgLnXCGXrezRL4ZmtA3FmCZtjOgDPvx1MMAY/0VcXFDQQfBRnfR4c2FmzGILuPjue/xtNyLUFOe3EYg==" - }, - - "Billing": { - "BaseAddress": "https://dev-api.caliverse.io" - }, - - "Ugq": { - "ApiServerAddress": "https://dev-ugqapi.caliverse.io:11000", - //"ApiServerAddress": "http://localhost:1000", - "UrlInGamePrefix": "/api/v1/InGame" - }, - - "Rabbitmq": { - "HostName": "localhost", - "Port": 5672, - "UserName": "admin", - "Password": "admin", - "SSL": false - }, - - "GameConfig": { - "ReservationWaitTimeMSec": 1200000, - "LoginCacheExpiryTimeMSec": 3600000, - "ServerSwitchCacheExpiryTimeMSec": 3600000 - }, - - "ControlAgentEnable": false, - - "PerformanceCheckEnable": false, - - "BattleSystemEnable": true, - - "MetaverseBroker": { - "JwtSecretKey": "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78", - "ExpireMinutes": 1440, - "SsoAccountDb": "Server=dev-caliverse-db.cluster-ro-czac0we0qoyx.us-west-2.rds.amazonaws.com;Port=3306;User ID=external_ro;Password=bQNEXbRWQTtV6bwlqktGyBiuf2KqYF;Database=caliverse", - "MetaverseBrokerDb": "Server=metaverse-broker-dev.cdn6gxjy33pu.us-west-2.rds.amazonaws.com;Port=3306;User ID=broker_user;Password=Apxkqjtmqmfhzj;Database=metaverse-broker", - "MetaverseBrokerDbLocal": "Server=localhost;Port=3307;User ID=broker_user;Password=qmfhzjdbwj;Database=metaverse-broker" - } -} - diff --git a/BrokerApiTest/Config/ServerConfig-QA.json b/BrokerApiTest/Config/ServerConfig-QA.json deleted file mode 100644 index 1d42fe9..0000000 --- a/BrokerApiTest/Config/ServerConfig-QA.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "LogDir": "./Logs", - "DumpDir": "./", - "LocalServer": false, - "SingleThreaded": false, - "ServiceType": "Qa", - "OfflineMode": false, - "DefaultMaxUser": 600, - "ClientListen": { - "Ip": "", - "Port": 0 - }, - - - "SessionKeepAliveTimeSec": 3600, - "MinWorkerThreadCount": 200, - "MinIoThreadCount": 0, - - "AccountLoginBlockEnable": false, - - "SsoAccountDb": "Server=qa-caliverse-db.cluster-ro-czac0we0qoyx.us-west-2.rds.amazonaws.com;Port=3306;User ID=external_ro;Password=k0RantM9gOAg5ATecBTFXzbCYDnvXi;Database=caliverse", - "SsoAccountAuthJwtSecretKey": "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78", - - "AccountNftDb": "Server=qa-caliverse-db.cluster-ro-czac0we0qoyx.us-west-2.rds.amazonaws.com;Port=3306;User ID=external_ro;Password=k0RantM9gOAg5ATecBTFXzbCYDnvXi;Database=caliverse", - - "Redis": "clustercfg.metaverse-qa-cluster.ocif0u.usw2.cache.amazonaws.com:6379,password=wiUaVvNwX4PhBj&8,ssl=true,abortConnect=false", - "Dynamodb": "", - - "MongoDb": { - "ConnectionString": "mongodb://qrwugqmongoconnect:qrw#$943cali@ip-172-40-141-201.us-west-2.compute.internal:27017/Metaverse", - "DatabaseName": "Metaverse", - "MinConnectionPoolSize": 0, - "MaxConnectionPoolSize": 100, - "WaitQueueTimeoutSecs": 120 - }, - - "AWS": { - "Enable": true, - "LocalDynamoDB": false, - "AccessKey": "AKIA4G3CB4Z5T6JUPHJN", - "SecretKey": "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", - "Region": "us-west-2", - "MilestoneName": "MS5", - - "CloudWatchLog": { - "Enable": true, - "LogGroup": "MetaverseLog-QA", - "LogNamePattern": "Developer", - "LogLevel": "Debug", - "Layout": { - "logTime": "${date:universalTime=true:format=yyyy-MM-ddTHH\\:mm\\:ss.fffZ}", - "server": "${event-properties:server}", - "ip/port": "${event-properties:ip/port}", - "threadId": "${threadid}", - "level": "${level:upperCase=true}", - "message": "${message}", - "function": "${event-properties:memberName}", - "filePath": "${event-properties:filePath}", - "lineNumber": "${event-properties:lineNumber}" - } - }, - - "S3": { - "ServiceFolderName": "Qa", - "MyhomeUgcInfoBucketName": "metaverse-myhomeugc-qa", - "BeaconAppProfileBucketName": "metaverse-beacon-appprofile" - } - }, - - - "ClientProgramVersionCheck": false, - "ClientMinimumRequiredLogicVersion": 0, - "ProgramVersionPath": ".\\Version\\", - "ProgramVersion": { - "MetaSchemaVersion": "MetaSchemaVersion.json", - "MetaDataVersion": "MetaDataVersion.json", - "PacketVersion": "PacketVersion.json", - "ResourceVersion": "ServerResourceVersion.json", - "ConfigVersion": "ServerConfigVersion.json", - "LogicVersion": "ServerBinaryVersion.json", - "DbSchemaVersion": "DbSchemaVersion.json" - }, - "CheatCommandAlwaysAllow": true, - - - "AuthRule": { - "ClientStandaloneAllow": true, - "ClientBySsoAccountAuthWithLauncherAllow": true, - "PlatformTypeAllows": "WindowsPc" - }, - - "ServerApiUrlCatalog": [ - { "BillingApiServerUrl": { "Ko": "https://qa.caliverse.io/shop" } }, - { "ChatAiApiServerUrl": { "Ko": "https://ai-qa-api.caliverse.io" } }, - { "S3ResourceImageUrl": { "Ko": "https://d3s9natejb9ydz.cloudfront.net/QA" } } - ], - - "NftRule": { - "NftDBAccess": true, - "CPNftForOwnerAllGetUrl": "https://qa-api.caliverse.io/v1/nft/game" - }, - - "AIChat": { - "BaseAddress": "https://ai-qa-api.caliverse.io", - "PrivateKey": "PPt+3OLhgLnXCGXrezRL4ZmtA3FmCZtjOgDPvx1MMAY/0VcXFDQQfBRnfR4c2FmzGILuPjue/xtNyLUFOe3EYg==" - }, - - "EchoSystem": { - "BaseAddress": "https://eco-system-qa-rollup-admin-api.caliverse.io", - "SlackAddress": "https://hooks.slack.com/services/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" - }, - - "Billing": { - "BaseAddress": "https://qa-api.caliverse.io" - }, - - "Ugq": { - "ApiServerAddress": "http://internal-Metaverse-QA-UGQAPI-Game-1559632298.us-west-2.elb.amazonaws.com:11201", - "UrlInGamePrefix": "/api/v1/InGame" - }, - - "Rabbitmq": { - "HostName": "b-d7c76a76-156d-4d55-8614-d4ce122a47c3.mq.us-west-2.amazonaws.com", - "Port": 5671, - "UserName": "serveruser", - "Password": "Zkfflqjtm!33&*(", - "SSL": true - }, - - "GameConfig": { - "ReservationWaitTimeMSec": 120000, - "LoginCacheExpiryTimeMSec": 3600000, - "ServerSwitchCacheExpiryTimeMSec": 300000 - }, - - "ControlAgentEnable": true, - - "PerformanceCheckEnable": false, - - "BattleSystemEnable": true, - - "MetaverseBroker": { - "JwtSecretKey": "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78", - "ExpireMinutes": 1440, - "SsoAccountDb": "Server=qa-caliverse-db.cluster-ro-czac0we0qoyx.us-west-2.rds.amazonaws.com;Port=3306;User ID=external_ro;Password=k0RantM9gOAg5ATecBTFXzbCYDnvXi;Database=caliverse", - "MetaverseBrokerDb": "Server=metaverse-broker-qa.cdn6gxjy33pu.us-west-2.rds.amazonaws.com;Port=3306;User ID=broker_user;Password=06M67cKVhHDpVipisA2g;Database=metaverse-broker", - "MetaverseBrokerDbLocal": "Server=localhost;Port=3307;User ID=root;Password=root;Database=metaverse-broker" - } -} - diff --git a/BrokerApiTest/Config/ServerConfig-Stage.json b/BrokerApiTest/Config/ServerConfig-Stage.json deleted file mode 100644 index 643edda..0000000 --- a/BrokerApiTest/Config/ServerConfig-Stage.json +++ /dev/null @@ -1,151 +0,0 @@ -{ - "LogDir": "./Logs", - "DumpDir": "./", - "LocalServer": false, - "SingleThreaded": false, - "ServiceType": "Stage", - "OfflineMode": false, - "DefaultMaxUser": 600, - "ClientListen": { - "Ip": "", - "Port": 0 - }, - - - "SessionKeepAliveTimeSec": 600, - "MinWorkerThreadCount": 200, - "MinIoThreadCount": 0, - - "AccountLoginBlockEnable": false, - - "SsoAccountDb": "Server=stage-caliverse-db.cluster-ro-czac0we0qoyx.us-west-2.rds.amazonaws.com;Port=3306;User ID=caliverse;Password=XjEDXb8fi9ZXP5PaxDCxPWeXK03mzk;Database=caliverse", - "SsoAccountAuthJwtSecretKey": "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78", - - "AccountNftDb": "Server=stage-caliverse-db.cluster-ro-czac0we0qoyx.us-west-2.rds.amazonaws.com;Port=3306;User ID=caliverse;Password=XjEDXb8fi9ZXP5PaxDCxPWeXK03mzk;Database=caliverse", - - "Redis": "clustercfg.metaverse-stage-cluster.ocif0u.usw2.cache.amazonaws.com:6379,password=wiUaVvNwX4PhBj&8,ssl=true,abortConnect=false", - "Dynamodb": "", - - "MongoDb": { - "ConnectionString": "mongodb+srv://stageugqapirw:uY3xQzVURk59S7Vu@stage-ugqapi.7d0do.mongodb.net/?retryWrites=true&w=majority&appName=stage-ugqapi", - "DatabaseName": "Metaverse", - "MinConnectionPoolSize": 0, - "MaxConnectionPoolSize": 100, - "WaitQueueTimeoutSecs": 120 - }, - - "AWS": { - "Enable": true, - "LocalDynamoDB": false, - "AccessKey": "AKIA4G3CB4Z5T6JUPHJN", - "SecretKey": "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", - "Region": "us-west-2", - "MilestoneName": "MS5", - - "CloudWatchLog": { - "Enable": true, - "LogGroup": "MetaverseLog-Stage", - "LogNamePattern": "Developer", - "LogLevel": "Debug", - "Layout": { - "logTime": "${date:universalTime=true:format=yyyy-MM-ddTHH\\:mm\\:ss.fffZ}", - "server": "${event-properties:server}", - "ip/port": "${event-properties:ip/port}", - "threadId": "${threadid}", - "level": "${level:upperCase=true}", - "message": "${message}", - "function": "${event-properties:memberName}", - "filePath": "${event-properties:filePath}", - "lineNumber": "${event-properties:lineNumber}" - } - }, - - "S3": { - "ServiceFolderName": "Stage", - "MyhomeUgcInfoBucketName": "metaverse-myhomeugc-stage", - "BeaconAppProfileBucketName": "metaverse-beacon-appprofile" - } - }, - - - "ClientProgramVersionCheck": false, - "ClientMinimumRequiredLogicVersion": 0, - "ProgramVersionPath": ".\\Version\\", - "ProgramVersion": { - "MetaSchemaVersion": "MetaSchemaVersion.json", - "MetaDataVersion": "MetaDataVersion.json", - "PacketVersion": "PacketVersion.json", - "ResourceVersion": "ServerResourceVersion.json", - "ConfigVersion": "ServerConfigVersion.json", - "LogicVersion": "ServerBinaryVersion.json", - "DbSchemaVersion": "DbSchemaVersion.json" - }, - "CheatCommandAlwaysAllow": true, - - - "AuthRule": { - "ClientStandaloneAllow": true, - "ClientBySsoAccountAuthWithLauncherAllow": true, - "PlatformTypeAllows": "WindowsPc" - }, - - "ServerApiUrlCatalog": [ - { "BillingApiServerUrl": { "Ko": "https://stage.caliverse.io/shop" } }, - { "ChatAiApiServerUrl": { "Ko": "https://ai-stage-api.caliverse.io" } }, - { "S3ResourceImageUrl": { "Ko": "https://d3s9natejb9ydz.cloudfront.net/Stage" } } - ], - - - "NftRule": { - "NftDBAccess": true, - "CPNftForOwnerAllGetUrl": "https://stage-api.caliverse.io/v1/nft/game" - }, - - "AIChat": { - "BaseAddress": "https://ai-stage-api.caliverse.io", - "PrivateKey": "PPt+3OLhgLnXCGXrezRL4ZmtA3FmCZtjOgDPvx1MMAY/0VcXFDQQfBRnfR4c2FmzGILuPjue/xtNyLUFOe3EYg==" - }, - - "EchoSystem": { - "BaseAddress": "https://eco-system-stage-rollup-admin-api.caliverse.io", - "SlackAddress": "https://hooks.slack.com/services/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" - }, - - "Billing": { - "BaseAddress": "https://stage-api.caliverse.io" - }, - - "Ugq": { - "ApiServerAddress": "http://internal-Metaverse-Stage-UGQAPI-Game-88775700.us-west-2.elb.amazonaws.com:11201", - "UrlInGamePrefix": "/api/v1/InGame" - }, - - "Rabbitmq": { - "HostName": "b-d0e44de3-7fb3-4120-b851-4566002eaf8e.mq.us-west-2.amazonaws.com", - "Port": 5671, - "UserName": "serveruser", - "Password": "Zkfflqjtm!33&*(", - "SSL": true - }, - - "GameConfig": { - "ReservationWaitTimeMSec": 120000, - "LoginCacheExpiryTimeMSec": 3600000, - "ServerSwitchCacheExpiryTimeMSec": 300000 - }, - - "ControlAgentEnable": true, - - "PerformanceCheckEnable": false, - - "BattleSystemEnable": true, - - "MetaverseBroker": { - "JwtSecretKey": "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78", - "ExpireMinutes": 1440, - "SsoAccountDb": "Server=stage-caliverse-db.cluster-ro-czac0we0qoyx.us-west-2.rds.amazonaws.com;Port=3306;User ID=caliverse;Password=XjEDXb8fi9ZXP5PaxDCxPWeXK03mzk;Database=caliverse", - "MetaverseBrokerDb": "Server=metaverse-broker-stage.cdn6gxjy33pu.us-west-2.rds.amazonaws.com;Port=3306;User ID=broker_user;Password=WkH44p4KiUDVzk5aPfANGtpCu6;Database=metaverse-broker", - "MetaverseBrokerDbLocal": "Server=localhost;Port=3307;User ID=root;Password=root;Database=metaverse-broker" - } -} - diff --git a/BrokerApiTest/Config/ServerConfig.json b/BrokerApiTest/Config/ServerConfig.json deleted file mode 100644 index 860b760..0000000 --- a/BrokerApiTest/Config/ServerConfig.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "LogDir": "./Logs", - "DumpDir": "./", - "LocalServer": true, - "SingleThreaded": false, - "ServiceType": "Dev", - "StandaloneMode": true, - "OfflineMode": false, - "DefaultMaxUser": 500, - "ClientListen": { - "Ip": "", - "Port": 0 - }, - - "SessionKeepAliveTimeSec": 3600, - "MinWorkerThreadCount": 100, - "MinIoThreadCount": 0, - - "AccountLoginBlockEnable": false, - - "SsoAccountDb": "Server=localhost;Port=13306;User ID=external_ro;Password=bQNEXbRWQTtV6bwlqktGyBiuf2KqYF;Database=caliverse", - "SsoAccountAuthJwtSecretKey": "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78", - - "AccountNftDb": "Server=localhost;Port=13306;User ID=external_ro;Password=bQNEXbRWQTtV6bwlqktGyBiuf2KqYF;Database=caliverse", - - "Redis": "127.0.0.1:6379,password=KT-i5#i%-%LxKfZ5YJj6,AsyncTimeout=30000,SyncTimeout=30000,ssl=false,abortConnect=false", - - "Dynamodb": "http://localhost:8000", - - "AWS": { - "Enable": false, - "LocalDynamoDB": false, - "AccessKey": "AKIA4G3CB4Z5T6JUPHJN", - "SecretKey": "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", - "Region": "us-west-2", - "MilestoneName": "MS5", - - "CloudWatchLog": { - "Enable": false, - "LogGroup": "MetaverseLog-Dev", - "LogNamePattern": "Developer", - "LogLevel": "Debug", - "Layout": { - "logTime": "${date:universalTime=true:format=yyyy-MM-ddTHH\\:mm\\:ss.fffZ}", - "level": "${level:upperCase=true}", - "message": "${message}", - "player": "${event-properties:player}", - "properties": "{event-properties:properties}", - "threadId": "${threadid}", - "function": "${event-properties:memberName}", - "filePath": "${event-properties:filePath}", - "lineNumber": "${event-properties:lineNumber}", - "server": "${event-properties:server}", - "ipport": "${event-properties:ipport}" - } - }, - - "S3": { - "ServiceFolderName": "Dev", - "MyhomeUgcInfoBucketName": "metaverse-myhomeugc-test", - "BeaconAppProfileBucketName": "metaverse-beacon-appprofile" - } - }, - - - "ClientProgramVersionCheck": false, - "ClientMinimumRequiredLogicVersion": 0, - "ProgramVersionPath": ".\\Version\\", - "ProgramVersion": { - "MetaSchemaVersion": "MetaSchemaVersion.json", - "MetaDataVersion": "MetaDataVersion.json", - "PacketVersion": "PacketVersion.json", - "ResourceVersion": "ServerResourceVersion.json", - "ConfigVersion": "ServerConfigVersion.json", - "LogicVersion": "ServerBinaryVersion.json", - "DbSchemaVersion": "DbSchemaVersion.json" - }, - "CheatCommandAlwaysAllow": true, - - - "AuthRule": { - "ClientStandaloneAllow": true, - "ClientBySsoAccountAuthWithLauncherAllow": false, - "PlatformTypeAllows": "" - }, - - "LoadBalancingRule": { - "AuthToGameRule": { - "Rule": "WeightedRoundRobin", - "MinRate": 0, - "MaxRate": 100, - "UserLanguageBased": true - } - }, - - "ServerApiUrlCatalog": [ - { "BillingApiServerUrl": "https://caliverse.io/shop" }, - { "ChatAiApiServerUrl": "https://ai-dev-api.caliverse.io" }, - { "S3ResourceImageUrl": "https://d3s9natejb9ydz.cloudfront.net/Dev" }, - { - "RentalGuideURL": { - "ko": "https://calidocu.gitbook.io/caliverse-metaverse/land/land-rental", - "en": "https://calidocu.gitbook.io/caliverse-metaverse/en/land/land-rental", - "jp": "https://calidocu.gitbook.io/caliverse-metaverse/jp/land/land-rental" - } - }, - { - "LandAuctionWebGuide": { - "ko": "https://calidocu.gitbook.io/caliverse-metaverse/land/land-rental", - "en": "https://calidocu.gitbook.io/caliverse-metaverse/en/land/land-rental", - "jp": "https://calidocu.gitbook.io/caliverse-metaverse/jp/land/land-rental" - } - }, - { - "LandManageGuideURL": { - "ko": "https://calidocu.gitbook.io/caliverse-metaverse/land/land-management", - "en": "https://calidocu.gitbook.io/caliverse-metaverse/en/land/land-management", - "jp": "https://calidocu.gitbook.io/caliverse-metaverse/jp/land/land-management" - } - }, - { - "Calium_Exchange_Web1": { - "ko": "https://calidocu.gitbook.io/calium-eco-system/calium-inflation-system-and-its-principles", - "en": "https://calidocu.gitbook.io/calium-eco-system/en/calium-inflation-system-and-its-principles", - "jp": "https://calidocu.gitbook.io/calium-eco-system/jp/calium-inflation-system-and-its-principles" - } - }, - { - "Calium_Exchange_Web2": { - "ko": "https://calium.caliverse.io/onchain", - "en": "https://calium.caliverse.io/onchain", - "jp": "https://calium.caliverse.io/onchain" - } - }, - { - "MyhomeEditGuideUrl": { - "ko": "https://calidocu.gitbook.io/caliverse-metaverse/caliverse-feature-guide/interior", - "en": "https://calidocu.gitbook.io/caliverse-metaverse/en/caliverse-feature-guide/interior", - "jp": "https://calidocu.gitbook.io/caliverse-metaverse/jp/caliverse-feature-guide/interior" - } - }, - { - "WebLinkUrlSeasonPass": { - "ko": "https://calidocu.gitbook.io/caliverse-metaverse/pass-and-claim/season-pass", - "en": "https://calidocu.gitbook.io/caliverse-metaverse/en/pass-and-claim/season-pass", - "jp": "https://calidocu.gitbook.io/caliverse-metaverse/jp/pass-and-claim/season-pass" - } - }, - { - "WebLinkURLCurrency": { - "ko": "https://caliverse.io/shop", - "en": "https://caliverse.io/en/shop", - "jp": "https://caliverse.io/en/shop" - } - }, - { - "CaliumConverterWebGuide": { - "ko": "https://calidocu.gitbook.io/caliverse-metaverse/calium/calium-converter", - "en": "https://calidocu.gitbook.io/caliverse-metaverse/en/calium/calium-converter", - "jp": "https://calidocu.gitbook.io/caliverse-metaverse/jp/calium/calium-converter" - } - } - ], - - "NftRule": { - "NftDBAccess": false, - "CPNftForOwnerAllGetUrl": "https://dev-api.caliverse.io/v1/nft/game" - }, - - "EchoSystem": { - "BaseAddress": "https://eco-system-dev-rollup-admin-api.caliverse.io" - }, - - "AIChat": { - "BaseAddress": "https://ai-dev-api.caliverse.io", - "PrivateKey": "PPt+3OLhgLnXCGXrezRL4ZmtA3FmCZtjOgDPvx1MMAY/0VcXFDQQfBRnfR4c2FmzGILuPjue/xtNyLUFOe3EYg==" - }, - - "Billing": { - "BaseAddress": "https://dev-api.caliverse.io" - }, - - "Ugq": { - "ApiServerAddress": "https://dev-ugqapi.caliverse.io:11000", - //"ApiServerAddress": "http://localhost:1000", - "UrlInGamePrefix": "/api/v1/InGame" - }, - - "Rabbitmq": { - "HostName": "localhost", - "Port": 5672, - "UserName": "admin", - "Password": "admin", - "SSL": false - }, - - "GameConfig": { - "ReservationWaitTimeMSec": 1200000, - "LoginCacheExpiryTimeMSec": 3600000, - "ServerSwitchCacheExpiryTimeMSec": 3600000 - }, - - "ControlAgentEnable": false, - - "PerformanceCheckEnable": false, - - "BattleSystemEnable" : true, - - "MetaverseBroker": { - "JwtSecretKey": "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78", - "ExpireMinutes": 1440, - "SsoAccountDb": "Server=dev-caliverse-db.cluster-ro-czac0we0qoyx.us-west-2.rds.amazonaws.com;Port=3306;User ID=external_ro;Password=bQNEXbRWQTtV6bwlqktGyBiuf2KqYF;Database=caliverse", - "MetaverseBrokerDb": "Server=metaverse-broker-dev.cdn6gxjy33pu.us-west-2.rds.amazonaws.com;Port=3306;User ID=caliverse;Password=Apxkqjtmqmfhzj;Database=metaverse-broker", - "MetaverseBrokerDbLocal": "Server=localhost;Port=3307;User ID=broker;Password=broker;Database=metaverse-broker" - } -} diff --git a/BrokerApiTest/Config/nlog.config b/BrokerApiTest/Config/nlog.config deleted file mode 100644 index 2e42d41..0000000 --- a/BrokerApiTest/Config/nlog.config +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - logger name="*" minlevel="Error" writeTo="logfile"/--> - - - diff --git a/BrokerApiTest/ControllerTests/CurrencyControllerTests.cs b/BrokerApiTest/ControllerTests/CurrencyControllerTests.cs deleted file mode 100644 index 91ebeeb..0000000 --- a/BrokerApiTest/ControllerTests/CurrencyControllerTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Net.Http.Json; -using BrokerApiCore; -using BrokerTest.Helper; - -namespace BrokerTest.Controllers; -public class CurrencyControllerTests -{ - private readonly BrokerTestServer m_server = new BrokerTestServer(); - - [Fact] - public async Task initializeAsync() - { - await Task.CompletedTask; - } - - private HttpClient getTestClient() - { - return m_server.getTestClient(); - } - - [Fact] - public async Task Auth_ValidRequest_ReturnsOkWithAccessToken() - { - // Arrange - const string planet_id = "new_earth"; - const string planet_secret_key = "A8h$KmP3sWxZqL5vYnR7uTgBdEjHkMlQoT1wXzCv"; - var planet_token = string.Empty; - var order_id = string.Empty; - { - var client = getTestClient(); - var request = new PlanetAuthRequest { PlanetId = planet_id, PlanetSecretKey = planet_secret_key }; - - // Act - var response = await client.PostAsJsonAsync("/api/v1/planet/auth", request); - - // Assert - response.EnsureSuccessStatusCode(); - var response_body = await response.Content.ReadFromJsonAsync(); - Assert.NotNull(response_body); - planet_token = response_body.AccessToken; - } - { - var client = getTestClient(); - var request = new ExchangeOrderRequest - { - UserGuid = "1052b08b52ef4d69a27ee1f40911a72f", Sapphire = 10, Emerald = 10 - }; - client.DefaultRequestHeaders.Add("Authorization", $"Bearer {planet_token}"); - var response = await client.PostAsJsonAsync("/api/v1/exchange/order/create", request); - response.EnsureSuccessStatusCode(); - var response_body = await response.Content.ReadFromJsonAsync(); - Assert.NotNull(response_body); - Assert.NotNull(response_body.OrderId); - order_id = response_body.OrderId; - // Assert - } - - //UserGuid:1052b08b52ef4d69a27ee1f40911a72f - { - var client = getTestClient(); - var request = new ExchangeOrderListRequest - { - UserGuid = "1052b08b52ef4d69a27ee1f40911a72f", Option = FindOption.All - }; - client.DefaultRequestHeaders.Add("Authorization", $"Bearer {planet_token}"); - var response = await client.PostAsJsonAsync("/api/v1/exchange/order/list", request); - response.EnsureSuccessStatusCode(); - var response_body = await response.Content.ReadFromJsonAsync(); - Assert.NotNull(response_body); - Assert.NotNull(response_body.Orders); - // Assert - } - - { - var client = getTestClient(); - var request = - new ExchangeOrderCompleteRequest { UserGuid = "1052b08b52ef4d69a27ee1f40911a72f", OrderId = order_id }; - client.DefaultRequestHeaders.Add("Authorization", $"Bearer {planet_token}"); - var response = await client.PostAsJsonAsync("/api/v1/exchange/order/complete", request); - response.EnsureSuccessStatusCode(); - var response_body = await response.Content.ReadFromJsonAsync(); - Assert.NotNull(response_body); - Assert.NotNull(response_body.OrderId); - Assert.Equal(order_id, response_body.OrderId); - Assert.Equal(ExchangeOrderStatus.Completed, response_body.Status); - // Assert - } - } - -} diff --git a/BrokerApiTest/ControllerTests/DevCurrencyControllerTests.cs b/BrokerApiTest/ControllerTests/DevCurrencyControllerTests.cs index 3cd1431..40c49ab 100644 --- a/BrokerApiTest/ControllerTests/DevCurrencyControllerTests.cs +++ b/BrokerApiTest/ControllerTests/DevCurrencyControllerTests.cs @@ -2,8 +2,6 @@ using BrokerApiCore; -using BrokerApiServer; - namespace BrokerTest; public class DevCurrencyControllerTests diff --git a/BrokerApiTest/ControllerTests/DevPlanetUserControllerTests.cs b/BrokerApiTest/ControllerTests/DevPlanetUserControllerTests.cs new file mode 100644 index 0000000..6a076d1 --- /dev/null +++ b/BrokerApiTest/ControllerTests/DevPlanetUserControllerTests.cs @@ -0,0 +1,279 @@ +using System.Net; +using System.Net.Http.Json; + +using BrokerApiCore; + +using ServerCommon; + +using Xunit.Abstractions; + +namespace BrokerTest; +public class DevPlanetUserControllerTests : IAsyncLifetime +{ + private readonly ITestOutputHelper m_test_output_helper; + // const string m_planet_id = "new_earth"; + const string m_planet_id = "igm26_iggymob"; + const string m_planet_secret_key = "A8h$KmP3sWxZqL5vYnR7uTgBdEjHkMlQoT1wXzCv"; + const string m_user_guid = "1052b08b52ef4d69a27ee1f40911a72f"; //park chan heon + const string m_account_id = "20462"; + + //40dbf485dc5b41508b7d53aa77819201 + // const string m_account_id = "20437"; + // const string m_user_guid = "40dbf485dc5b41508b7d53aa77819201"; + + private string m_planet_token = string.Empty; + private readonly MetaTableTestHelper m_meta_table_test_helper = new(); + + // private readonly IBrokerTestServer m_server = new BrokerTestServer(); + private readonly IBrokerTestServer m_server = new BrokerTestRemoteServer("https://dev.planethub.caliverse.io:12000"); + + public DevPlanetUserControllerTests(ITestOutputHelper testOutputHelper) + { + m_test_output_helper = testOutputHelper; + } + + private HttpClient getTestClient() + { + var client = m_server.getTestClient(); + if (m_planet_token != string.Empty) + { + client.DefaultRequestHeaders.Add("Authorization", $"Bearer {m_planet_token}"); + } + + return client; + } + + private HttpClient getTestClientWithBearer() + { + var client = m_server.getTestClient(); + if (m_planet_token != string.Empty) + { + client.DefaultRequestHeaders.Add("Authorization", $"Bearer {m_planet_token}"); + } + + return client; + } + + public async Task InitializeAsync() + { + m_meta_table_test_helper.load(); + + // Arrange + var client = getTestClient(); + var request = new PlanetAuthRequest { PlanetId = m_planet_id, PlanetSecretKey = m_planet_secret_key }; + + // Act + var response = await client.PostAsJsonAsync("/api/v1/planet/auth", request); + + // Assert + response.EnsureSuccessStatusCode(); + var response_body = await response.Content.ReadFromJsonAsync(); + Assert.NotNull(response_body); + Assert.NotEmpty(response_body.AccessToken); + m_planet_token = response_body.AccessToken; + } + + public async Task DisposeAsync() + { + await Task.CompletedTask; + } + + private bool checkError(HttpResponseMessage responseMessage) + { + if (responseMessage.StatusCode != HttpStatusCode.OK) + { + var error_body = responseMessage.Content.ReadFromJsonAsync().Result; + Assert.NotNull(error_body); + m_test_output_helper.WriteLine(error_body.ErrorMessage ?? string.Empty); + Assert.True(false); + } + + return true; + } + + [Fact(DisplayName = "아이템 교환 주문 컨트롤러 테스트 모든 Meta")] + public async Task allMetaTests() + { + MetaTableTestHelper meta_table_test_helper = new(); + meta_table_test_helper.load(); + var meta = meta_table_test_helper.getMetaTableRef(); + Assert.NotNull(meta); + var meta_tuples = meta.MetaTable.PlanetItemExchangePolicyMetaTable.PlanetItemExchangePolicyDataList + .Select(x => + string.Equals(x.CaliverseItemType, CaliverseItemType.Currency.ToString(), + StringComparison.CurrentCultureIgnoreCase) + ? (x.ID, Random.Shared.Next(10, 201)) + : (x.ID, 5)); + // 테스트 이력용 + foreach (var i in Enumerable.Range(0, 1)) + { + foreach (var meta_tuple in meta_tuples) + { + await orderTest(meta_tuple.Item1, meta_tuple.Item2); + } + } + } + + private async Task dailyAmountExceedError(string metaId, int metaAmount, int totalDailyAmount) + { + const string TEST_SEASON_ID = "TEST_SEASON_ID"; + string order_id; + { + // Arrange + var request_uri = "/api/v1/planet/user/exchange/order/create"; + var client = getTestClientWithBearer(); + var exchange_request = new PlanetItemExchangeRequest + { + SeasonId = TEST_SEASON_ID, + UserGuid = m_user_guid, ExchangeMetaId = metaId, ExchangeMetaAmount = metaAmount, + }; + + // Act + var exchange_response = await client.PostAsJsonAsync(request_uri, exchange_request); + if (exchange_response.StatusCode == HttpStatusCode.BadRequest) + { + + } + // Assert + checkError(exchange_response); + var exchange_response_body = + await exchange_response.Content.ReadFromJsonAsync(); + Assert.NotNull(exchange_response_body); + Assert.NotNull(exchange_response_body.ExchangeOrder.OrderId); + order_id = exchange_response_body.ExchangeOrder.OrderId; + } + } + + + private async Task orderTest(string metaId, int metaAmount) + { + const string TEST_SEASON_ID = "TEST_SEASON_ID"; + string order_id; + { + // Arrange + var request_uri = "/api/v1/planet/user/exchange/order/create"; + var client = getTestClientWithBearer(); + var exchange_request = new PlanetItemExchangeRequest + { + SeasonId = TEST_SEASON_ID, + UserGuid = m_user_guid, ExchangeMetaId = metaId, ExchangeMetaAmount = metaAmount, + }; + + // Act + var exchange_response = await client.PostAsJsonAsync(request_uri, exchange_request); + + // Assert + checkError(exchange_response); + var exchange_response_body = + await exchange_response.Content.ReadFromJsonAsync(); + Assert.NotNull(exchange_response_body); + Assert.NotNull(exchange_response_body.ExchangeOrder.OrderId); + order_id = exchange_response_body.ExchangeOrder.OrderId; + } + { + // Arrange + var request_uri = "/api/v1/planet/user/exchange/order/list"; + var client = getTestClientWithBearer(); + var list_request = new PlanetItemExchangeOrderListRequest + { + PlanetId = m_planet_id, + SeasonId = TEST_SEASON_ID, + UserGuid = m_user_guid, Option = FindOption.All, ExchangeMetaId = metaId, + }; + + // Act + var list_response = await client.PostAsJsonAsync(request_uri, list_request); + checkError(list_response); + + // Assert + list_response.EnsureSuccessStatusCode(); + var list_response_body = + await list_response.Content.ReadFromJsonAsync(); + Assert.NotNull(list_response_body); + Assert.NotNull(list_response_body.Orders); + + var order = list_response_body.Orders.FirstOrDefault(x => x.OrderId == order_id); + Assert.NotNull(order); + Assert.Equal(ExchangeOrderStatus.Pending, order.OrderStatus); + } + { + // Arrange + var request_uri = "/api/v1/planet/user/exchange/order/list"; + var client = getTestClientWithBearer(); + var list_request = new PlanetItemExchangeOrderListRequest + { + PlanetId = m_planet_id, + ExchangeMetaId = metaId, + SeasonId = TEST_SEASON_ID, + SsoAccountId = m_account_id, Option = FindOption.All, + }; + + // Act + var list_response = await client.PostAsJsonAsync(request_uri, list_request); + checkError(list_response); + + // Assert + list_response.EnsureSuccessStatusCode(); + var list_response_body = + await list_response.Content.ReadFromJsonAsync(); + Assert.NotNull(list_response_body); + Assert.NotNull(list_response_body.Orders); + + var order = list_response_body.Orders.FirstOrDefault(x => x.OrderId == order_id); + Assert.NotNull(order); + Assert.Equal(ExchangeOrderStatus.Pending, order.OrderStatus); + } + { + // Arrange + var request_uri = "/api/v1/planet/user/exchange/order/list"; + var client = getTestClientWithBearer(); + var list_request = new PlanetItemExchangeOrderListRequest + { + PlanetId = m_planet_id, + ExchangeMetaId = "", + SeasonId = TEST_SEASON_ID, + SsoAccountId = m_account_id, Option = FindOption.All, + }; + + // Act + var list_response = await client.PostAsJsonAsync(request_uri, list_request); + checkError(list_response); + + // Assert + list_response.EnsureSuccessStatusCode(); + var list_response_body = + await list_response.Content.ReadFromJsonAsync(); + Assert.NotNull(list_response_body); + Assert.NotNull(list_response_body.Orders); + + var order = list_response_body.Orders.FirstOrDefault(x => x.OrderId == order_id); + Assert.NotNull(order); + Assert.Equal(ExchangeOrderStatus.Pending, order.OrderStatus); + + Assert.True(list_response_body.TotalCount > 0); + } + { + // Arrange + var request_uri = "/api/v1/planet/user/exchange/order/complete"; + var client = getTestClientWithBearer(); + + // Act + var complete_request = new PlanetItemExchangeCompleteRequest + { + UserGuid = m_user_guid, ExchangeOrderId = order_id + }; + var complete_response = + await client.PostAsJsonAsync(request_uri, complete_request); + checkError(complete_response); + + // Assert + complete_response.EnsureSuccessStatusCode(); + var complete_response_body = + await complete_response.Content.ReadFromJsonAsync(); + Assert.NotNull(complete_response_body); + Assert.NotNull(complete_response_body.ExchangeOrder); + Assert.Equal(order_id, complete_response_body.ExchangeOrder.OrderId); + Assert.Equal(ExchangeOrderStatus.Completed, complete_response_body.ExchangeOrder.OrderStatus); + } + } +} diff --git a/BrokerApiTest/ControllerTests/PlanetControllerTests.cs b/BrokerApiTest/ControllerTests/PlanetControllerTests.cs index df3f1e4..1e3c5fc 100644 --- a/BrokerApiTest/ControllerTests/PlanetControllerTests.cs +++ b/BrokerApiTest/ControllerTests/PlanetControllerTests.cs @@ -5,19 +5,29 @@ using System.Text.Json; using BrokerApiCore; -using BrokerApiServer; -using BrokerTest.Helper; - namespace BrokerTest; public class PlanetControllerTests { - private readonly BrokerTestServer m_server = new(); + private readonly BrokerTestServer m_test_server; private readonly JsonSerializerOptions m_json_options = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNameCaseInsensitive = true }; + public PlanetControllerTests() + { + try + { + m_test_server = new BrokerTestServer(); + } + catch (Exception e) + { + Assert.Fail(e.Message); + throw; + } + } + [Fact] public async Task Auth_ValidRequest_ReturnsOkWithAccessToken() { @@ -25,7 +35,7 @@ public class PlanetControllerTests const string planet_id = "new_earth"; const string planet_secret_key = "A8h$KmP3sWxZqL5vYnR7uTgBdEjHkMlQoT1wXzCv"; - var client = m_server.getTestClient(); + var client = m_test_server.getTestClient(); var request = new PlanetAuthRequest { PlanetId = planet_id, PlanetSecretKey = planet_secret_key }; // Act @@ -36,7 +46,7 @@ public class PlanetControllerTests var auth_response = await response.Content.ReadFromJsonAsync(m_json_options); Assert.NotNull(auth_response); - var jwt_option = m_server.getRequiredService(); + var jwt_option = m_test_server.getRequiredService(); Assert.NotNull(jwt_option); var jwt_service = new JwtParser(jwt_option); var claims = jwt_service.parseToken(auth_response.AccessToken); diff --git a/BrokerApiTest/ControllerTests/PlanetUserControllerTests.cs b/BrokerApiTest/ControllerTests/PlanetUserControllerTests.cs index fa9c23e..171c10b 100644 --- a/BrokerApiTest/ControllerTests/PlanetUserControllerTests.cs +++ b/BrokerApiTest/ControllerTests/PlanetUserControllerTests.cs @@ -3,10 +3,6 @@ using System.Net.Http.Json; using BrokerApiCore; -using BrokerApiServer; - -using BrokerTest.Helper; - using ServerCommon; using Xunit.Abstractions; @@ -18,8 +14,8 @@ public class PlanetUserControllerTests : IAsyncLifetime // const string m_planet_id = "new_earth"; const string m_planet_id = "igm26_iggymob"; const string m_planet_secret_key = "A8h$KmP3sWxZqL5vYnR7uTgBdEjHkMlQoT1wXzCv"; - const string m_user_guid = "1052b08b52ef4d69a27ee1f40911a72f"; //park chan heon - const string m_account_id = "20462"; + const string m_user_guid = "e1e353296146435888eced246da0b42a"; //park chan heon + const string m_account_id = "heon"; //40dbf485dc5b41508b7d53aa77819201 // const string m_account_id = "20437"; @@ -28,8 +24,8 @@ public class PlanetUserControllerTests : IAsyncLifetime private string m_planet_token = string.Empty; private readonly MetaTableTestHelper m_meta_table_test_helper = new(); - // private readonly IBrokerTestServer m_server = new BrokerTestServer(); - private readonly IBrokerTestServer m_server = new BrokerTestRemoteServer("https://dev.planethub.caliverse.io:12000"); + private readonly IBrokerTestServer m_server = new BrokerTestServer(); + // private readonly IBrokerTestServer m_server = new BrokerTestRemoteServer("https://dev.planethub.caliverse.io:12000"); public PlanetUserControllerTests(ITestOutputHelper testOutputHelper) { @@ -89,7 +85,7 @@ public class PlanetUserControllerTests : IAsyncLifetime var error_body = responseMessage.Content.ReadFromJsonAsync().Result; Assert.NotNull(error_body); m_test_output_helper.WriteLine(error_body.ErrorMessage ?? string.Empty); - Assert.True(false); + Assert.Fail(); } return true; @@ -118,25 +114,6 @@ public class PlanetUserControllerTests : IAsyncLifetime } } - // [Fact(DisplayName = "아이템 교환 주문 예외테스트")] - // public async Task exceptionTests() - // { - // MetaTableTestHelper meta_table_test_helper = new(); - // meta_table_test_helper.load(); - // var meta = meta_table_test_helper.getMetaTableRef(); - // Assert.NotNull(meta); - // var meta_tuples = meta.MetaTable.PlanetItemExchangePolicyMetaTable.PlanetItemExchangePolicyDataList - // .Select(x => - // string.Equals(x.CaliverseItemType, CaliverseItemType.Currency.ToString(), - // StringComparison.CurrentCultureIgnoreCase) - // ? (x.ID, Random.Shared.Next(10, 201)) - // : (x.ID, 1)); - // foreach (var meta_tuple in meta_tuples) - // { - // await orderTest(meta_tuple.Item1, meta_tuple.Item2); - // } - // } - private async Task dailyAmountExceedError(string metaId, int metaAmount, int totalDailyAmount) { const string TEST_SEASON_ID = "TEST_SEASON_ID"; diff --git a/BrokerApiTest/ControllerTests/QAPlanetUserControllerTests.cs b/BrokerApiTest/ControllerTests/QAPlanetUserControllerTests.cs index 0fdafe5..b9dd3e3 100644 --- a/BrokerApiTest/ControllerTests/QAPlanetUserControllerTests.cs +++ b/BrokerApiTest/ControllerTests/QAPlanetUserControllerTests.cs @@ -8,8 +8,6 @@ using Xunit.Abstractions; using ServerCommon; using ServerCore; -using BrokerApiServer; -using BrokerTest.Helper; namespace BrokerTest; public class QaPlanetUserControllerTests : IAsyncLifetime { diff --git a/BrokerApiTest/ControllerTests/StagePlanetUserControllerTests.cs b/BrokerApiTest/ControllerTests/StagePlanetUserControllerTests.cs index 6cb9783..27cc9c8 100644 --- a/BrokerApiTest/ControllerTests/StagePlanetUserControllerTests.cs +++ b/BrokerApiTest/ControllerTests/StagePlanetUserControllerTests.cs @@ -8,9 +8,6 @@ using Xunit.Abstractions; using ServerCommon; using ServerCore; -using BrokerApiServer; -using BrokerTest.Helper; - namespace BrokerTest; public class StagePlanetUserControllerTests : IAsyncLifetime { diff --git a/BrokerApiTest/Directory.Build.props b/BrokerApiTest/Directory.Build.props new file mode 100644 index 0000000..181094c --- /dev/null +++ b/BrokerApiTest/Directory.Build.props @@ -0,0 +1,8 @@ + + + false + false + ..\..\obj\AnyCPU\$(MSBuildProjectName)\ + ..\..\bin\ + + \ No newline at end of file diff --git a/BrokerApiTest/DocQuery/DynamoDbClientTests.cs b/BrokerApiTest/DocQuery/DynamoDbClientTests.cs index db20e2f..68a3983 100644 --- a/BrokerApiTest/DocQuery/DynamoDbClientTests.cs +++ b/BrokerApiTest/DocQuery/DynamoDbClientTests.cs @@ -1,15 +1,5 @@ - -using System.Threading.Tasks; - - -using Xunit; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.Model; - - using ServerCore; using ServerBase; -using ServerCommon; namespace BrokerTest; diff --git a/BrokerApiTest/DocQuery/UserTestAttrib.cs b/BrokerApiTest/DocQuery/UserTestAttrib.cs index 5e62470..1b82ef9 100644 --- a/BrokerApiTest/DocQuery/UserTestAttrib.cs +++ b/BrokerApiTest/DocQuery/UserTestAttrib.cs @@ -1,7 +1,6 @@ using Newtonsoft.Json; using ServerBase; -using ServerCommon; namespace BrokerTest; public class UserTestAttrib : AttribBase diff --git a/BrokerApiTest/DocQuery/UserTestDeleteOneAction.cs b/BrokerApiTest/DocQuery/UserTestDeleteOneAction.cs index 2844063..4deda98 100644 --- a/BrokerApiTest/DocQuery/UserTestDeleteOneAction.cs +++ b/BrokerApiTest/DocQuery/UserTestDeleteOneAction.cs @@ -1,9 +1,4 @@ - -using Microsoft.AspNetCore.Identity; - -using ServerCommon; - -using ServerCore; using ServerBase; +using ServerBase; namespace BrokerTest; public class UserTestDeleteOneAction : EntityActionBase { diff --git a/BrokerApiTest/DocQuery/UserTestDoc.cs b/BrokerApiTest/DocQuery/UserTestDoc.cs index fcfad34..35f6833 100644 --- a/BrokerApiTest/DocQuery/UserTestDoc.cs +++ b/BrokerApiTest/DocQuery/UserTestDoc.cs @@ -1,6 +1,4 @@ -using ServerCommon; - -using ServerCore; using ServerBase; +using ServerCore; using ServerBase; namespace BrokerTest; public sealed class UserTestDoc : DynamoDbDocBase { diff --git a/BrokerApiTest/DocQuery/UserTestEntityAttribute.cs b/BrokerApiTest/DocQuery/UserTestEntityAttribute.cs index 84e633f..9a529d2 100644 --- a/BrokerApiTest/DocQuery/UserTestEntityAttribute.cs +++ b/BrokerApiTest/DocQuery/UserTestEntityAttribute.cs @@ -1,6 +1,4 @@ -using ServerCommon; - -using ServerCore; using ServerBase; +using ServerCore; using ServerBase; using ThirdParty.Json.LitJson; diff --git a/BrokerApiTest/DocQuery/UserTestFindOneAction.cs b/BrokerApiTest/DocQuery/UserTestFindOneAction.cs index cc3782c..2021fe4 100644 --- a/BrokerApiTest/DocQuery/UserTestFindOneAction.cs +++ b/BrokerApiTest/DocQuery/UserTestFindOneAction.cs @@ -1,8 +1,4 @@ -using BrokerTest; - -using ServerCommon; - -using ServerCore; using ServerBase; +using ServerCore; using ServerBase; namespace BrokerTest; public class UserTestFindOneAction : EntityActionBase diff --git a/BrokerApiTest/DocQuery/UserTestInsertAction.cs b/BrokerApiTest/DocQuery/UserTestInsertAction.cs index 01c7364..ded3e6d 100644 --- a/BrokerApiTest/DocQuery/UserTestInsertAction.cs +++ b/BrokerApiTest/DocQuery/UserTestInsertAction.cs @@ -1,6 +1,4 @@ -using ServerCommon; - -using ServerCore; using ServerBase; +using ServerCore; using ServerBase; namespace BrokerTest; public class UserTestInsertAction : EntityActionBase diff --git a/BrokerApiTest/DocQuery/UserTestUpdateOneAction.cs b/BrokerApiTest/DocQuery/UserTestUpdateOneAction.cs index 4cc19e4..e50fcce 100644 --- a/BrokerApiTest/DocQuery/UserTestUpdateOneAction.cs +++ b/BrokerApiTest/DocQuery/UserTestUpdateOneAction.cs @@ -1,8 +1,6 @@ using BrokerTest; -using ServerCommon; - -using ServerCore; using ServerBase; +using ServerBase; public class UserTestUpdateOneAction : EntityActionBase { diff --git a/BrokerApiTest/EntityTests/EntityMailTests.cs b/BrokerApiTest/EntityTests/EntityMailTests.cs index 50a3d85..0f8d459 100644 --- a/BrokerApiTest/EntityTests/EntityMailTests.cs +++ b/BrokerApiTest/EntityTests/EntityMailTests.cs @@ -3,12 +3,9 @@ using BrokerApiCore; using Microsoft.Extensions.DependencyInjection; -using ServerCore; using ServerBase; using ServerCommon; -using BrokerApiServer; -using BrokerTest.Helper; namespace BrokerTest; public class EntityMailTests : IAsyncLifetime { @@ -16,12 +13,13 @@ public class EntityMailTests : IAsyncLifetime public EntityMailTests() { - AppBuilderExtensions.initGlobalNlog(); - var config = AppBuilderExtensions.initBrokerServerConfig(); + + // AppBuilderExtensions.initGlobalNlog(); + // var config = AppBuilderExtensions.initBrokerServerConfig(); m_services = new ServiceCollection(); - m_services.AddSingleton(config); - m_services.addBrokerServerLogic(config); - m_services.AddScoped(); + // m_services.AddSingleton(config); + // // m_services.addBrokerServerLogic(config); + // m_services.AddScoped(); } public Task InitializeAsync() @@ -36,6 +34,63 @@ public class EntityMailTests : IAsyncLifetime return Task.CompletedTask; } + [Fact] + async Task mailItemsTest() + { + await using var scope = m_services.BuildServiceProvider(); + var planet_user = scope.GetRequiredService(); + await planet_user.onInit(); + planet_user.setPlanetId("new_earth"); + + var auth_action = planet_user.getEntityAction(); + Assert.NotNull(auth_action); + var result = await auth_action.findAndSetAllAttributeByAccountId("20462"); + Assert.NotNull(result); + Assert.True(result.isSuccess()); + Assert.Equal("20462", planet_user.getEntityAttributeNotNull().AccountId); + + var server_logic = scope.GetRequiredService(); + + + MetaTableTestHelper meta_table_test_helper = new(); + meta_table_test_helper.load(); + var meta_table_ref = meta_table_test_helper.getMetaTableRef(); + var product_policy_mata = + meta_table_ref.MetaTable.PlanetItemExchangePolicyMetaTable.PlanetItemExchangePolicyDataList.FirstOrDefault(x => + x.CaliverseItemType == CaliverseItemType.CaliverseProduct.ToString()); + Assert.NotNull(product_policy_mata); + + var mail_send_by_product_id = async (int productId) => + { + var broker_mail = new BrokerMailEntity(planet_user, server_logic); + var result = await broker_mail.onInit(); + Assert.NotNull(result); + Assert.True(result.isSuccess()); + + meta_table_ref.MetaTable.ProductMetaTable.ProductMetaDataListbyId.TryGetValue( + productId, out var product_meta); + Assert.NotNull(product_meta); + + meta_table_ref.MetaTable.SystemMailMetaTable.SystemMailMetaDataListbyKey.TryGetValue( + product_meta.SystemMail_First, out var mail_meta); + Assert.NotNull(mail_meta); + + var mail_send_action = broker_mail.getEntityActionNotNull(); + // var mail_option = mail_send_action.createSystemSendMailOptionByMeta(product_meta, mail_meta, planet_user.UserGuid, + // planet_user.Nickname, 100); + var mail_items = MailHelper.getMailItems(meta_table_ref, product_meta, 100); + var mail_count = Convert.ToInt32(mail_items.Sum(x => x.Count)); + Assert.Equal(100, mail_count); + }; + + IEnumerable product_ids = [3001, 3002, 3003, 3004]; + foreach (var product_id in product_ids) + { + await mail_send_by_product_id(product_id); + await Task.Delay(10); + } + } + [Fact] public async Task testMethod1() { @@ -80,11 +135,15 @@ public class EntityMailTests : IAsyncLifetime Assert.NotNull(mail_meta); var mail_send_action = broker_mail.getEntityActionNotNull(); - var mail_option = mail_send_action.createSystemSendMailOptionByMeta(product_meta, mail_meta, planet_user.UserGuid, - planet_user.Nickname, 100); + // var mail_option = mail_send_action.createSystemSendMailOptionByMeta(product_meta, mail_meta, planet_user.UserGuid, + // planet_user.Nickname, 100); + var mail_option = mail_send_action.createSystemSendMailOptionByMeta(product_meta, mail_meta, + MailHelper.getMailItems(meta_table_ref, product_meta, 100), + planet_user.UserGuid, + planet_user.Nickname); // var mail_option2 = // Helpers.createMailOptionByProductMeta(product_meta, mail_meta, planet_user.UserGuid, - // planet_user.Nickname); + // planet_user.Nickname);+ result = await mail_send_action.sendMail(mail_option, () => Task.CompletedTask, () => Task.CompletedTask); Assert.NotNull(result); Assert.True(result.isSuccess()); diff --git a/BrokerApiTest/EntityTests/EntityTests.cs b/BrokerApiTest/EntityTests/EntityTests.cs index d917a78..8e3b25a 100644 --- a/BrokerApiTest/EntityTests/EntityTests.cs +++ b/BrokerApiTest/EntityTests/EntityTests.cs @@ -3,8 +3,6 @@ using ServerBase; using ServerCommon; -using BrokerApiServer; - namespace BrokerTest; public class EntityTests { diff --git a/BrokerApiTest/Etc/EnumHelperTest.cs b/BrokerApiTest/Etc/EnumHelperTest.cs index c5a8527..64a3de8 100644 --- a/BrokerApiTest/Etc/EnumHelperTest.cs +++ b/BrokerApiTest/Etc/EnumHelperTest.cs @@ -2,7 +2,7 @@ using MetaAssets; -using ServerCore; using ServerBase; +using ServerCore; public class EnumHelperTest { diff --git a/BrokerApiTest/Helper/BrokerTestServer.cs b/BrokerApiTest/Helper/BrokerTestServer.cs index cd1d2d5..eb50a3a 100644 --- a/BrokerApiTest/Helper/BrokerTestServer.cs +++ b/BrokerApiTest/Helper/BrokerTestServer.cs @@ -1,15 +1,10 @@ using Microsoft.Extensions.Logging; - -using NLog.Extensions.Logging; - using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - using System.Reflection; - using BrokerApiServer; -using BrokerApiServer.Controllers; + +using NLog.Extensions.Logging; namespace BrokerTest; public interface IBrokerTestServer @@ -70,41 +65,21 @@ public class BrokerTestServer: IBrokerTestServerLocal { var port = 12000; var cmd_options = new string[] { "--urls", $"http://localhost:{port}" }; - // Arrange var builder = WebApplication.CreateBuilder(cmd_options); + builder.Services.AddBrokerServerService(cmd_options); + + // 테스트 전용 옵션 - 로깅 builder.Logging.AddNLog(new NLogProviderOptions{ ReplaceLoggerFactory = true }); builder.Logging.AddConsole(); - builder.addAppServices(); - builder.Services.AddHttpContextAccessor(); - // builder.Services.AddControllers(options => { options.Filters.Add(new ResultExceptionFilter()); }); + + // 테스트 전용 옵션 var assembly = Assembly.GetAssembly(typeof(PlanetUserController)); Assert.NotNull(assembly); builder.Services.AddControllers(options => { options.Filters.Add(new ResultExceptionFilter()); }) .AddApplicationPart(assembly); // 이게 없으면 테스트에서 404 에러 발생 - 컨트롤러가 없음 - builder.Services.AddHealthChecks(); - builder.Services.AddEndpointsApiExplorer(); var app = builder.Build(); - app.UseHttpsRedirection(); - app.UseRouting(); - app.UseCors("Everything"); - app.UseMiddleware(); - app.UseAuthorization(); - app.MapControllers(); - app.MapHealthChecks("/healthcheck"); - - app.validateRepoConnections(); - app.brokerServerLogicInit(); - app.metadataMangerInit(TestDefines.MetaDataPath); - - app.Services.GetRequiredService().ApplicationStarted.Register(() => - { - // Log.getLogger().info($"Env : {app.Environment.EnvironmentName}"); - // Log.getLogger().info($"BrokerApiServer started {port}"); - // todo 왜 enpoints가 1개 뿐이지? - Console.WriteLine($"Env : {app.Environment.EnvironmentName}"); - }); - // app.Services.GetRequiredService().ApplicationStopped.Register(Log.shutdown); + app.UseBrokerServerService(); app.RunAsync().ConfigureAwait(false); return app; } diff --git a/BrokerApiTest/Helper/Helpers.cs b/BrokerApiTest/Helper/Helpers.cs index 4c0683a..70995cb 100644 --- a/BrokerApiTest/Helper/Helpers.cs +++ b/BrokerApiTest/Helper/Helpers.cs @@ -1,4 +1,4 @@ public static class TestDefines { - public static string MetaDataPath => Path.GetFullPath("../../../../../bin/Debug/resource/meta", Directory.GetCurrentDirectory()); + public static string MetaDataPath => Path.GetFullPath("./resource/meta", Directory.GetCurrentDirectory()); } diff --git a/BrokerApiTest/Helper/TestUserHelper.cs b/BrokerApiTest/Helper/TestUserHelper.cs index 7c3f528..64f83cb 100644 --- a/BrokerApiTest/Helper/TestUserHelper.cs +++ b/BrokerApiTest/Helper/TestUserHelper.cs @@ -4,7 +4,6 @@ using BrokerApiServer; using ServerCore; using ServerBase; -using ServerCommon; namespace BrokerTest; public class TestUserHelper diff --git a/BrokerApiTest/Jwt/JwtBasicTests.cs b/BrokerApiTest/Jwt/JwtBasicTests.cs index 440dd6c..459046d 100644 --- a/BrokerApiTest/Jwt/JwtBasicTests.cs +++ b/BrokerApiTest/Jwt/JwtBasicTests.cs @@ -1,45 +1,64 @@ - -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; - -using BrokerApiCore; - -using Xunit.Abstractions; - -using BrokerApiServer; - -namespace BrokerTest.Jwt; -//============================================================================================= -// Jwt 토큰 생성 및 파싱 기본 라이브러리 사용 테스트 -//============================================================================================= -public class JwtBasicTests -{ - private readonly ITestOutputHelper m_test_output_helper; - - public JwtBasicTests(ITestOutputHelper testOutputHelper) - { - m_test_output_helper = testOutputHelper; - } - - [Fact] - public void jwtServiceTest() - { - var jwt_option = new JwtOption - { - Secret = "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78", - TokenValidityInMinutes = 1, - }; - var jwt_service = new JwtGenerator(jwt_option); - var token = jwt_service.generateAccessToken("new_earth", "caliverse"); - m_test_output_helper.WriteLine(token); - Assert.NotNull(token); - - var jwt_parser = new JwtParser(jwt_option); - var token_parsed = jwt_parser.parseToken(token); - Assert.NotNull(token_parsed); - var sid = token_parsed.FindFirstValue(JwtRegisteredClaimNames.Sid); - Assert.NotNull(sid); - var typ = token_parsed.FindFirstValue(JwtRegisteredClaimNames.Typ); - Assert.NotNull(typ); - } -} +// using System.IdentityModel.Tokens.Jwt; +// using System.Security.Claims; +// +// using BrokerApiCore; +// +// using Xunit.Abstractions; +// +// using BrokerApiServer; +// +// namespace BrokerTest.Jwt; +// +// //============================================================================================= +// // Jwt 토큰 생성 및 파싱 기본 라이브러리 사용 테스트 +// //============================================================================================= +// public class JwtBasicTests +// { +// private readonly ITestOutputHelper m_test_output_helper; +// +// public JwtBasicTests(ITestOutputHelper testOutputHelper) +// { +// m_test_output_helper = testOutputHelper; +// } +// +// [Fact] +// public void jwtServiceTest() +// { +// var jwt_option = new JwtOption +// { +// Secret = "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78", TokenValidityInMinutes = 1, +// }; +// { +// var jwt_service = new JwtGenerator(jwt_option); +// var token = jwt_service.generateAccessToken("new_earth", "caliverse", +// new DateTime(2025, 3, 1, 0, 0, 0, DateTimeKind.Utc)); +// m_test_output_helper.WriteLine(token); +// Assert.NotNull(token); +// var jwt_parser = new JwtParser(jwt_option); +// var token_parsed = jwt_parser.parseToken(token); +// Assert.NotNull(token_parsed); +// var sid = token_parsed.FindFirstValue(JwtRegisteredClaimNames.Sid); +// Assert.NotNull(sid); +// var typ = token_parsed.FindFirstValue(JwtRegisteredClaimNames.Typ); +// Assert.NotNull(typ); +// var validated = new PlanetTokenValidator().validate(token_parsed); +// Assert.True(true); +// } +// { +// var jwt_service = new JwtGenerator(jwt_option); +// var token = jwt_service.generateAccessToken("new_earth", "caliverse", +// new DateTime(2025, 2, 1, 0, 0, 0, DateTimeKind.Utc)); +// m_test_output_helper.WriteLine(token); +// Assert.NotNull(token); +// var jwt_parser = new JwtParser(jwt_option); +// var token_parsed = jwt_parser.parseToken(token); +// Assert.NotNull(token_parsed); +// var sid = token_parsed.FindFirstValue(JwtRegisteredClaimNames.Sid); +// Assert.NotNull(sid); +// var typ = token_parsed.FindFirstValue(JwtRegisteredClaimNames.Typ); +// Assert.NotNull(typ); +// var validated = new PlanetTokenValidator().validate(token_parsed); +// Assert.True(true); +// } +// } +// } diff --git a/BrokerApiTest/obj/BrokerApiTest.csproj.nuget.dgspec.json b/BrokerApiTest/obj/BrokerApiTest.csproj.nuget.dgspec.json index 7ab429c..1fee3b7 100644 --- a/BrokerApiTest/obj/BrokerApiTest.csproj.nuget.dgspec.json +++ b/BrokerApiTest/obj/BrokerApiTest.csproj.nuget.dgspec.json @@ -88,6 +88,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -114,6 +115,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -121,6 +123,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -159,18 +162,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -183,8 +202,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -297,6 +319,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -323,6 +346,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -330,6 +354,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -368,18 +393,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -392,8 +433,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -497,6 +541,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -523,6 +568,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -530,6 +576,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -568,18 +615,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -592,8 +655,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -679,6 +745,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -705,6 +772,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -712,6 +780,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -750,18 +819,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -774,8 +859,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -854,6 +942,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -880,6 +969,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -887,6 +977,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -925,18 +1016,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -949,8 +1056,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -1032,9 +1142,17 @@ "frameworks": { "net8.0": { "targetAlias": "net8.0", + "dependencies": { + "Ulid": { + "target": "Package", + "version": "[1.3.4, )", + "versionCentrallyManaged": true + } + }, "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -1061,6 +1179,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -1068,6 +1187,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -1106,18 +1226,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -1130,8 +1266,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -1422,6 +1561,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -1448,6 +1588,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -1455,6 +1596,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -1493,18 +1635,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -1517,8 +1675,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -1597,6 +1758,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -1623,6 +1785,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -1630,6 +1793,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -1668,18 +1832,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -1692,8 +1872,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -1786,6 +1969,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -1812,6 +1996,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -1819,6 +2004,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -1857,18 +2043,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -1881,8 +2083,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -1968,6 +2173,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -1994,6 +2200,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -2001,6 +2208,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -2039,18 +2247,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -2063,8 +2287,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -2147,6 +2374,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -2173,6 +2401,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -2180,6 +2409,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -2218,18 +2448,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -2242,8 +2488,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -2348,6 +2597,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -2374,6 +2624,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -2381,6 +2632,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -2419,18 +2671,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -2443,8 +2711,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, @@ -2540,6 +2811,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -2566,6 +2838,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -2573,6 +2846,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -2611,18 +2885,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -2635,8 +2925,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, diff --git a/BrokerApiTest/obj/Debug/net8.0/BrokerApiTest.assets.cache b/BrokerApiTest/obj/Debug/net8.0/BrokerApiTest.assets.cache index 4d3a90e252b701adbc10c8287d1ac7ea19fa131b..d0d7619c04b386837433953929f37e33523cc927 100644 GIT binary patch delta 11506 zcmaJ{30PD|*5x)T2sDd=tU@;^&M?bnV1>zzxCc@`RS_R``%Z)SLd9%b?>d^Rlj{z z_KlnDt3!2u)yKj*IqBZ8VaAz8mZ5h!Uf^_n3hOmCz-M7`;f&nq$k@oZ+~UHgbLY-n zFd#B2H=CZa9kUJW`>X(FxzeTPN<7;++mX2zy0gqHqy1(S7u(vXe>=g(Mo0hdqSBAb z-ON~6CvNj`(?&b!{VIWxYG0#r3ze@^d62F{bRDMa z2wg{6c4crGM;@b#Blk&=Z&00MddWOBg$v9v zk@1nSExZ#Nj~v!RNPLsIR)r{?cuRxEOH6y4S z!yCC|PBD)g|JzFK@c)rDWCg169@=%A_Ha8(zmn6nHOv&=e5SwZ*tIpp6xwC__F-)a zXDDmD>gc>r(|Lu;6dT;sAHtdIcd;z#YdzMVhfrp2@M3k>3|;F8aF(w3SZPB6Rp-F_ z?Cdp9w{!ID3|(#YSvL!q`Mxh}`X&a>E0_;7%!gERjgJ%!^L;%_`ymZJRxqDfVYp7^ z!!|SEQw8{$2E3q&eXfXAJ`7>WH=^O9g0Wa(xL8GfI$TofztHNx)HE(D8Wr_=R&qTJ zzEUt(tT0^T!Xvd~R~5`P4fD08^AAPm!XrI9@`sON&o>J0TPqybvNY&f?$60kuVAig znC~>L8>ICPG$?g9t?IIF9)ep+-S=AE4;t@B#fG)Fa^Rmz{ZCf))HWvTKyCcC0=lDt ze%3^O;UXNQdi7TYbk_*@b3tbAB5);WL?4VS=&MKyRv0O3wj-QVc?J6-S6 zb&4*X#7%JFd}mhI7-w>k zP+hI4(GNK_1e0AL+~g*qb+e+adN5U)gAC0BP2D9dca7C=!WH+f1}URJDC500C>C1@WF-EMj#tF(Dg^`5VU>Sslbet6W>Q-b!lLUX58)cdtX zI6y)*Xw-JoC)rq0@2dpYC}9O!vAFltN!LdWaFB%B%Zkcv<#RC)aBm4KSYx%DogW=E zg-Fn$R%mXXI+qVOnIx<*D;BqmX7(*RVU($lgcYu_+RgRkK)xO%-cL0}NLZ0pEN-Et zK^q-r2`$Qs#!Xy%OV3WvCG;4L-fn#%TRHu1bE+v;!iuwEaa-5k(roQ3q4l$( zk*&&NrTDYIgf>8M$QHp(l5wGwvf=SSOlNB$QMkrDd6IGubpmA}~}FXt(;3gB5|i z23paZe)BR7lW>Pyak-a?JKi)xLK$gA;VG-G(OS0(Sg(iaEc0g{Wg^Xz(8gKO{GH&< zu<;W3gpP0>o7WKEVx6|5;PYsty6)3Gbr2qv_A z8s_&U%M08bnVm}pW_Fk0p2qU-P$ra^j-?YJh)dw%?%;sw?ocW;eTI&0a@~xxJit#_ zKU2rRi+3#!?-EAX*fSW=1C|Ksvvkb(iIEv3H2! z(o&%kQ>^m>M>dv4Vv!dF2u&(#1?C9tdRAAnc{*{N=tsyTM7c5?`N_&|VA zW)9DvDfky3Nxh$J!WK0-kn&PcR$=8AnEsF9V;&>#_&Plh?xd(7iP{9wbTB%!vOh0la}h(w9r6a z-;Fu=X5kj2BvOASso`|A#V%~?jru?caI=~V@LmuEvq!#(xF=BJoaJZK7I5G<)*t;38L!N?kSPQ=d$BuuR5wuZoBVFaF6EzQ{Nf~P_tRfu^($0B!6 z<0d$=5Tm;gv!+GIfnD4kip`-CbJyzF(fL!b(j=*0M>A63AcQbU2-mBVuQ^E1_6Mfp zVHxg4btsvG#$b9MiPi>O*$1Wz9ae!ira>Uq_5nK#2#0JTXd@O3f(X{Q*O7f*n}gmF z67N@H-5eU$mIMcm#h=cC8(S1?#4*2s3!aaV46!O58+^{Fv63)9QsS{{9ZPw6F6zvZ z`c3K_U%Jnk{SrJHkD4XksKEv^EEjf^;qF4U1Gqa1N`wipnGfX`@IbVrJ6pJ!eON}9 z?re}L7Sm!RxUD)?8Xv(jySU@&7?>q&3u!?QTZiw*O1knAUY|sPY3ky{4)?LcxHy<4 z?AJCt8VB2jRoi)e&BhPHslVRrHFGvDUk3KfE=oKp*OJ_7MJ_o3rV9JM zmm4#jHAiLOh=GzYz05-pfdz@slhqy^h|L2f>9>{% z3AiRn;-bH(qv*zA1KShZ56y!lxWDpIc|_>S{aWPWFn2I46ZZW8o*Ad4jw8o7v+BO1 za9OfsReDwPWgH9Zmw~Y<5?{ThW5;Z1;eDeA4oZbC%+fCgFB4d3!0SrdV08*DiBI(( z!*|hQ;6Xf_Dp~0dsSR2Z{Mf1iF<3u_HsY6uNH*e!)jX{`(H)b9N(S5!J}4`2_%KNi zkE-#jJCTLI4U>$AV|+yQ$GQ=eoWCX{;VZ)>9eV?7`7X$k&=cK9NHz|~mGon!13mG= z2q+MG@&uO8S9CmZ=}4F&964{cc*+9}qaa(DHE&UrchZQ7W0s^sWpH8aXo)l5Zf%oU z-buuZqa~x_9qzE@xGGKJtdpt_4DWj3-87gX?BCziwg|&Ak`sB9#qS7zR~7(_N{0o) z{=KV=it=G5W=ZzM$LUZaZ0;16kA-Ss)oH$TgtOYzI9!ta~e-?}uI^cbRJV$Rh^I79J z&bVQmBqrzh)bP5{0q5170R0w+wG+UBRSh>`w+WJ2;{$4Ly>fy?*mPl9eyGE!=c#we zO$l7k8#6xvCvKS|R%J_e+viAjb&D+h4A|&`i4!GV`G~)N9uPKtUKz?*Isryt?IbV> z`};AUGxiIe`3ae^p~Vc<6JeA8hbLyRd3STzt~4inpI}0hK5b12G&}{lKA%w&R@;9% zVz|(XW2I+OP&Y-A5}y-)3`BIwQ`LtKRpH=b0ryQQD0*7av?|PiA&T> zA~j>c##|U6?AaGeGnh9O#tQxXC4bQs@3$`V4flB5mM01ES9l>0o)@-yg>S`ZJsQ^; zqYA*j=DlS6Dqk`jukv(g#V! zB2Jqn>Gt=^sR^E#1?j>t{=nT5$PP~m#=O}QjUTaUHWUe6_fN$g7*z<9h4Me~mxvw0 zs@uwO7}ga+09F^lGs5J#L&>umkF5g(nxB=NtNpCL9Y%dT2@{JY?)-(XV|08sWgLEA zED6i6e92sd6>}sScX>!Q3*FGjhxB+nHCN)nCQc1vu6g$8T>^zdf867%zX|I~V4nAV z>b7XId%&mQAapH(uDGZamI;0K8g~~a5iIp?miXie)-HldVTf&PS>_k(@xo%spa)y}IBX7kfxU|*r#wTTvY4V^)B5DtM&Z(YMDpWK-PcuhBIc+Jw zZKj5kS5wB_UIl&$UIgJD^`?>s#D_{Q=}Tq%A8-8Vo=f(ml1uti#-B8D$!8J*=)S#% zf$l{bMtaUQ0;%L0L9H5kj9d+q<9pG=_CmesUL+Ju&$&sCWmm`Q_o{NX7~ zgZI(k;kK+ik#Z~F9;4=gJ2tPT8THI_Fv1ieQHazO%$kBbcjzutlms5F!DCwCV_&4% z-TwtBGQ~k`k4Qn7TYyjPgG9#Xz|G&(z#XbC> z&-F_e)_lnwbJjvuM1q7o(27i-Uh(zW%nr>ka&9P=od$m_UI$*LM2SjLM-@D@4i1D3 zlE4RR@V4M#-8ed^wXB0^G_T`lOzUB!DOo~K(dccFqfjtYssuhngAb*WyLA}PEEAtK zy>ZM2NH-0a5JzalwjuZ42IvbT@%IhzlztS|^2E;M7e;R+$=V89kE$ym1V-aK6;KRm z*n1-s;L1uGq?#uu|j zG44i_7%!6O=1_^6!xNIM1~p(3b+hY*iMez!`>MMc*_nFtef`x;_xs;_^Ylz+ z<-DW9xiYf-@9UXE7YHq9Vf!)Ge28wtQj-+Z8vtP=HasEeQPS#IWpx96af_7iS5hS?!xv> zJl?|NZ9I14@eUrrJZ??rve@`OG4Tm8aqUr3kbCeDMf9Ao~xqQa${@Uccmk_aE) z<%cVx3{}?j@Ip5WKeEfUrl7Jht!~R*!v#$BM zzlRpz$KxO#hc;a9!pj<*`NnJcD3z-mZF>>F{{xQ?@TkS(BRoFF<1ijac`-B_sl}bA zH>M~T3F?wgUB*@z{8lyC)YAP?OdAQb$qp3;Emt*zR|x7mo%$YIVekjlpyg_=a+M&i z*%4?5f(=Xy<#hu4myZ32tuXX&G;~P$4kEvKkL{%Y=zlhL?8v$CUw6O z>~D6gNS*P9JM_QGJk$~TJAwY8qxZ2DW*?~eGv3IJ`Y%DW*bc#693j2bQR~5wN>0 z%DISEhr=4{DgM3`5-T9rLJfQA8)}M05{1h*O_ttsqLa({1DHyDDB(P4l7&@8H z5B@w>@gWFbJ3=^Gd2fQ!nIO98gtazku2tR()B@;7aQ?QO>YG+mhAIIB6=+8Z{{}W1 z)l3@+DoCfS(G~P9)%7I&7_z!rk3UZ$i9}t+?<|*mB?G$c8F?2`tGLgN2Ul{U{3|-cJ{|)g6?lia}f_wjn%O@fWQVyF;R%Mgr^YPAf2;TVm0Bd6Y4?zT-iz`oj@{l#9F|i(t}OY_YHbfM-yzO9b50l-iyj2#ItS14ZNx$Nw}wA z{dePax3oGwhTzBQymj{=T-~pXBgDtsiKBPZ&{Bk$<}nipEXNkZ-;bUY;H_wX*qg(g z`QP1qe8r9^7q1_}w)I|ZB4qosaGvGv0Y+~&qO(jU&%on+!%!v(&U-VXRL>-c8NBz=Obxbz!`0*xQ=-rO@^9Y4+Ix?eFF5dtlWlRXV@M;eeOz1=iPBHM(FNB;cck;{x zM;}72z`#p9rvl)0spEyf=3)SZo-Lj(n|xV`)UP7UzSl+w=uDWIYT(RIxRVImDVb?n z9P7N3Af^l9&UC2FU}35|nH@8rfs&g61%q_AGdsHRJ1AS_N9dd>a%|@Ncnk#jvk++* zW(k?)(o3`9ia&cs8kIQ)ZptxLWjga)J_?))Ac9jYIz9<<1BvT%F?bhHjT1lP+Z`Gx zv3UmW(Rm&$Gcu#UY@E)=v5ilwy79Vp9{lJgU$|#vAyU5=72g4k@SsuGd-@lX>?@TCS`d%(nJy^;o_LI`&rN10bf!b$4-A_EWIoC`Uj z1i#q8_dA++v;Sy#5K8#5MC~$+Hn9R}Kb9JJQDm67-jTZmX2b9>LeVnJ!mC5!Xc#e) zo)9A`jKA<|5@d!G4nJw&nZ`VbUBR5;dN`9WP0OLY0GE`LU0D(j3UY^KU769_F7MDB z#v+7JuC!Y#41D{dF#c^&BCPL5%#WwxNH?}himx>Ab6=VGku8%!QJ5<Oea z*Ooz9ccRDTqQ^o}d)^X~2p*9{bXN&~d+^(jM*@!|bp1u7vPd{2AKlLg9b>_NB0Bt$ zLPU2p)Xu>)Rcy1s$b1w`k0N@u1}8w+0C+cw@c3D6A&w1q=R3nPpr{8S_nbPBcx-t3 zrf4D&&qI7Pn$a8=TEV-(SGxzUu}!XC_R%2-_E;o;r&QgdSr? z7#_pYr3rlrgLvH3D(DJVVpz6RaGha;(nBb7g{Na#j@0}0VpWfZqj9VqzZo$Sro|CK zs=%4NV=~;0BPLfRWEX4h^LyRL!r$TvJyl{huYpMk#Ptp6bSXTfE_1AQ^^dd&sCIk1{xf~jM5jh2IgdN?P126U_ z0`m$S>CI}S!QZU^=N$hE< zwJqXt&q3*>*F??k%d_G#A+aBkn5~aId5CiNDY-y@}woP8Xew> z4kx16Ty-Z1mHmkoVLM#O!P}|kgmI8GfXM0Vt<%SspV>12ng$R7-GLQrZw18m!o2Sp zNErN^$XQ?TOkqz+a))$2?$nB5oy8ZL2Qw#L*E<288%*?PmpV3} zI+eLW=3w?1&+C(}g=thOFy8YU*+*R4~p*e;HZjU z`5+0_Wf1GoA+geLl6K@DVz`@8MHkpNnk|ⅆoWs=;f&}lTDXK_Crx4dhqJOiI9{< z^!y`j6NY6D?@{xLQ3_Vy2*%R^Bx@nlWf%k6o^uj>}_HrGw#fbuesNg?~~tUts@bkw4ig7tSlwd%&Sd#GpB)-rDhk3}^l@ zJpz&@6CFJbck;#kY(_jBrew}&gL=x6B(Y_c?|si2plAg%L3KW}NCR+A7?R)P*WnH| z$HY5i#=~b*i2l@zy|oDf3Rt#O{g)!XtEHj+3in(l2%US zE6iQwX7Kp45#X_!`M__bEH&UKLf6mY&Ie0s!DOg^oQU&1ao-%t7Zj#J$s(d}zlhnq zQrfp)#l{6qoXduti;3|5rtcQcId7PXn~EibC%yf+;pYFZ(B+CkLz<1+hrH(oBqOWJd{N+S<4X}MVTP$_KQJ@Mhn3Vz>pCa67CuAbw z_ERih8nX6Kzk>)5N@Ra^wYLOzNwG;%%~2^A_CdV92%i05#uopjHEWgEZ1!Fne9OXtN z${o*(w>vzr^~A%=5lrPQ9=ukuX=ZN%>Zn7VtPpPaaElrOM^>?7iw~X_!Scmcbf7b~ zq61y9ZTmlpAO04${jn9c103PI9NdSX5;v&)3(Kp}?#wh_NcbXRTt@>;$8qWmBSJ2`LvCV3TZFs9(!=MOg^(e!BRh#6oonZpE6vQsDLrY*JJ*0ruCyNBPv# z;RLbEnLoVyBFk0=5b!`9PQg}$b&wc>70O^jJXIIBh7+#8#CowIU|h%YSQ;!_$ELHP zaCse@$A-a(^{fyA*WrwNZ#~OqBfzPG#lj_VVsHhEgyHM)+tLbr68on3wg=C#47gCi kdU=mVLt+YLsT0bnAlB(+H>5i4;*L)NsfgdrK88MMyF|JtBbhZvy3rM)o zleY@`<<#%(z0L1!cce_rGT#EA^_#z$%Z!pi?YhhEY|k2x_|ni&{D#AKEZZIFx-h72PkBX4kJaY<^fo^NVyeo^J*g{F>^ j*Xo2!?$V5&%&hJ|xn0Ysd6V4sO>&IP65DMp7~Q!5YKbwq diff --git a/BrokerApiTest/obj/project.assets.json b/BrokerApiTest/obj/project.assets.json index 4e53e0c..6e0ce24 100644 --- a/BrokerApiTest/obj/project.assets.json +++ b/BrokerApiTest/obj/project.assets.json @@ -3132,6 +3132,19 @@ } } }, + "Ulid/1.3.4": { + "type": "package", + "compile": { + "lib/net8.0/Ulid.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/net8.0/Ulid.dll": { + "related": ".xml" + } + } + }, "xunit/2.9.3": { "type": "package", "dependencies": { @@ -3409,7 +3422,8 @@ "Protocol": "1.0.0", "ServerBase": "1.0.0", "ServerCore": "1.0.0", - "UGQDatabase": "1.0.0" + "UGQDatabase": "1.0.0", + "Ulid": "1.3.4" }, "compile": { "bin/placeholder/ServerCommon.dll": {} @@ -8046,6 +8060,28 @@ "useSharedDesignerContext.txt" ] }, + "Ulid/1.3.4": { + "sha512": "6IaGquwjjfW+BoHSV844y12Uy2kxbboYNmsibxr2lotcSPAA3LKy1CKcAQ8JOdAdL4xMoDNXA1oxG41w7fbr6Q==", + "type": "package", + "path": "ulid/1.3.4", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/net6.0/Ulid.dll", + "lib/net6.0/Ulid.xml", + "lib/net7.0/Ulid.dll", + "lib/net7.0/Ulid.xml", + "lib/net8.0/Ulid.dll", + "lib/net8.0/Ulid.xml", + "lib/netstandard2.0/Ulid.dll", + "lib/netstandard2.0/Ulid.xml", + "lib/netstandard2.1/Ulid.dll", + "lib/netstandard2.1/Ulid.xml", + "ulid.1.3.4.nupkg.sha512", + "ulid.nuspec" + ] + }, "xunit/2.9.3": { "sha512": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", "type": "package", @@ -8378,6 +8414,7 @@ "centralPackageVersions": { "Asp.Versioning.Mvc": "8.1.0", "Asp.Versioning.Mvc.ApiExplorer": "8.1.0", + "Aspire.Hosting.AppHost": "9.1.0", "AspNetCore.Swagger.Fluent.Annotations": "1.0.4", "AsyncStateMachine": "1.3.2", "AutoMapper": "14.0.0", @@ -8404,6 +8441,7 @@ "CommunityToolkit.Diagnostics": "8.2.2", "CommunityToolkit.Mvvm": "8.2.2", "Costura.Fody": "5.7.0", + "coverlet.collector": "6.0.4", "Csv": "2.0.93", "DocumentFormat.OpenXml": "2.20.0", "DotNet.MultiMap": "2.2.1", @@ -8411,6 +8449,7 @@ "ExcelDataReader": "3.6.0", "ExcelDataReader.DataSet": "3.6.0", "ExcelNumberFormat": "1.1.0", + "FluentAssertions": "8.3.0", "Google.Protobuf": "3.27.1", "Grpc.AspNetCore": "2.63.0", "Grpc.AspNetCore.Server.Reflection": "2.63.0", @@ -8449,18 +8488,34 @@ "MongoDB.Driver": "3.3.0", "MongoDB.Driver.Core": "2.30.0", "Moq": "4.20.72", + "MSTest.TestAdapter": "3.8.3", + "MSTest.TestFramework": "3.8.3", "MySqlConnector": "2.4.0", "NeoSmart.AsyncLock": "3.2.1", "Newtonsoft.Json": "13.0.3", "Nito.AsyncEx": "5.1.2", "NJsonSchema.CodeGeneration.CSharp": "10.9.0", "NLog": "5.4.0", + "NLog.Extensions.Logging": "5.3.11", "NLog.Web.AspNetCore": "5.3.11", + "NSubstituteAutoSubstitute": "1.1.0", + "OneOf": "3.0.271", + "OpenTelemetry.AutoInstrumentation": "1.11.0", + "OpenTelemetry.Exporter.Console": "1.12.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.12.0", + "OpenTelemetry.Extensions.Hosting": "1.12.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.12.0", + "OpenTelemetry.Instrumentation.Http": "1.12.0", + "OpenTelemetry.PersistentStorage.FileSystem": "1.0.1", "Otp.NET": "1.4.0", "p4api.net": "2023.1.248.4623", "Polly": "8.5.1", "Pomelo.EntityFrameworkCore.MySql": "8.0.2", "RabbitMQ.Client": "6.8.1", + "RedLock.net": "2.3.2", + "Sentry.AspNetCore": "5.10.0", + "Sentry.Extensions.Logging": "5.10.0", + "Sentry.NLog": "5.10.0", "SharpSvn": "1.14003.272", "sqlite-net-pcl": "1.9.172", "SSH.NET": "2025.0.0", @@ -8473,8 +8528,11 @@ "Swashbuckle.AspNetCore.Filters": "8.0.2", "Swashbuckle.AspNetCore.SwaggerUI": "7.2.0", "System.IdentityModel.Tokens.Jwt": "8.9.0", + "System.IO.Hashing": "9.0.1", + "Ulid": "1.3.4", "xunit": "2.9.3", - "xunit.assert": "2.9.2", + "xunit.assert": "2.9.3", + "xunit.extensibility.core": "2.9.3", "xunit.runner.visualstudio": "3.0.1", "YamlDotNet": "16.3.0" }, diff --git a/BrokerApiTest/obj/project.nuget.cache b/BrokerApiTest/obj/project.nuget.cache index 222f2f7..483a3b7 100644 --- a/BrokerApiTest/obj/project.nuget.cache +++ b/BrokerApiTest/obj/project.nuget.cache @@ -1,6 +1,6 @@ { "version": 2, - "dgSpecHash": "Q+hbkpSpm30=", + "dgSpecHash": "1TAk64zf+QU=", "success": true, "projectFilePath": "D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiTest\\BrokerApiTest.csproj", "expectedPackageFiles": [ @@ -178,6 +178,7 @@ "C:\\Users\\user\\.nuget\\packages\\system.text.json\\8.0.0\\system.text.json.8.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.threading.channels\\7.0.0\\system.threading.channels.7.0.0.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\system.windows.extensions\\6.0.0\\system.windows.extensions.6.0.0.nupkg.sha512", + "C:\\Users\\user\\.nuget\\packages\\ulid\\1.3.4\\ulid.1.3.4.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\xunit\\2.9.3\\xunit.2.9.3.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\xunit.abstractions\\2.0.3\\xunit.abstractions.2.0.3.nupkg.sha512", "C:\\Users\\user\\.nuget\\packages\\xunit.analyzers\\1.18.0\\xunit.analyzers.1.18.0.nupkg.sha512", diff --git a/BrokerApiTest/obj/project.packagespec.json b/BrokerApiTest/obj/project.packagespec.json index ce77971..de31d54 100644 --- a/BrokerApiTest/obj/project.packagespec.json +++ b/BrokerApiTest/obj/project.packagespec.json @@ -1 +1 @@ -"restore":{"projectUniqueName":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiTest\\BrokerApiTest.csproj","projectName":"BrokerApiTest","projectPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiTest\\BrokerApiTest.csproj","outputPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiTest\\obj\\","projectStyle":"PackageReference","centralPackageVersionsManagementEnabled":true,"originalTargetFrameworks":["net8.0"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"net8.0":{"targetAlias":"net8.0","projectReferences":{"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiServer\\BrokerApiServer.csproj":{"projectPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiServer\\BrokerApiServer.csproj"}}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"}}"frameworks":{"net8.0":{"targetAlias":"net8.0","dependencies":{"Microsoft.AspNetCore.Mvc.Testing":{"target":"Package","version":"[8.0.2, )","versionCentrallyManaged":true},"Microsoft.NET.Test.Sdk":{"target":"Package","version":"[17.12.0, )","versionCentrallyManaged":true},"Moq":{"target":"Package","version":"[4.20.72, )","versionCentrallyManaged":true},"xunit":{"target":"Package","version":"[2.9.3, )","versionCentrallyManaged":true},"xunit.runner.visualstudio":{"include":"Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive","suppressParent":"All","target":"Package","version":"[3.0.1, )","versionCentrallyManaged":true}},"centralPackageVersions":{"Asp.Versioning.Mvc":"8.1.0","Asp.Versioning.Mvc.ApiExplorer":"8.1.0","AspNetCore.Swagger.Fluent.Annotations":"1.0.4","AsyncStateMachine":"1.3.2","AutoMapper":"14.0.0","AutoMapper.AspNetCore.OData.EFCore":"7.0.1","AutoMapper.Collection":"11.0.0","AutoMapper.Collection.EntityFrameworkCore":"11.0.0","AutoMapper.Contrib.Autofac.DependencyInjection":"9.0.0","AutoMapper.Data":"9.0.0","AutoMapper.EF6":"3.0.1","AutoMapper.Extensions.EnumMapping":"4.1.0","AutoMapper.Extensions.ExpressionMapping":"8.0.0","AWS.Logger.NLog":"3.3.4","AWSSDK.Core":"3.7.402.46","AWSSDK.DynamoDBv2":"3.7.407","AWSSDK.EC2":"3.7.330.4","AWSSDK.ElasticLoadBalancingV2":"3.7.303.15","AWSSDK.OpenSearchService":"3.7.404.81","AWSSDK.S3":"3.7.416.15","AWSSDK.SecurityToken":"3.7.401.89","Axion.ConcurrentHashSet":"1.0.0","BCrypt.Net-Next":"4.0.3","BouncyCastle.Cryptography":"2.5.1","CommandLineParser":"2.9.1","CommunityToolkit.Diagnostics":"8.2.2","CommunityToolkit.Mvvm":"8.2.2","Costura.Fody":"5.7.0","Csv":"2.0.93","DocumentFormat.OpenXml":"2.20.0","DotNet.MultiMap":"2.2.1","DumpExtensions":"2.0.0","ExcelDataReader":"3.6.0","ExcelDataReader.DataSet":"3.6.0","ExcelNumberFormat":"1.1.0","Google.Protobuf":"3.27.1","Grpc.AspNetCore":"2.63.0","Grpc.AspNetCore.Server.Reflection":"2.63.0","Grpc.Tools":"2.64.0","JWT":"11.0.0","MaterialDesignColors":"3.1.1-ci850","MaterialDesignThemes":"5.1.1-ci850","MaxMind.Db":"4.1.0","MaxMind.GeoIP2":"5.2.0","MediatR":"12.3.0","Microsoft.AspNetCore.Authentication.JwtBearer":"8.0.2","Microsoft.AspNetCore.Mvc.Testing":"8.0.2","Microsoft.AspNetCore.OpenApi":"8.0.6","Microsoft.Data.Sqlite":"8.0.6","Microsoft.Diagnostics.NETCore.Client":"0.2.621003","Microsoft.EntityFrameworkCore.Design":"8.0.2","Microsoft.EntityFrameworkCore.Tools":"8.0.2","Microsoft.Extensions.Caching.StackExchangeRedis":"8.0.6","Microsoft.Extensions.Configuration":"9.0.4","Microsoft.Extensions.Configuration.Abstractions":"8.0.0","Microsoft.Extensions.Configuration.UserSecrets":"8.0.0","Microsoft.Extensions.DependencyInjection":"8.0.0","Microsoft.Extensions.DependencyInjection.Abstractions":"8.0.1","Microsoft.Extensions.Hosting":"8.0.0","Microsoft.Extensions.Hosting.Abstractions":"8.0.0","Microsoft.Extensions.Http":"8.0.0","Microsoft.Extensions.Logging":"8.0.0","Microsoft.Extensions.Logging.Abstractions":"9.0.4","Microsoft.Extensions.Options":"9.0.4","Microsoft.Extensions.Options.ConfigurationExtensions":"8.0.0","Microsoft.NET.Test.Sdk":"17.12.0","Microsoft.OpenApi":"1.6.22","Microsoft.Xaml.Behaviors.Wpf":"1.1.122","MongoDB.Analyzer":"1.5.0","MongoDB.Bson":"3.3.0","MongoDB.Driver":"3.3.0","MongoDB.Driver.Core":"2.30.0","Moq":"4.20.72","MySqlConnector":"2.4.0","NeoSmart.AsyncLock":"3.2.1","Newtonsoft.Json":"13.0.3","Nito.AsyncEx":"5.1.2","NJsonSchema.CodeGeneration.CSharp":"10.9.0","NLog":"5.4.0","NLog.Web.AspNetCore":"5.3.11","Otp.NET":"1.4.0","p4api.net":"2023.1.248.4623","Polly":"8.5.1","Pomelo.EntityFrameworkCore.MySql":"8.0.2","RabbitMQ.Client":"6.8.1","SharpSvn":"1.14003.272","sqlite-net-pcl":"1.9.172","SSH.NET":"2025.0.0","StackExchange.Redis":"2.8.31","StackExchange.Redis.Extensions.Core":"11.0.0","StackExchange.Redis.Extensions.Newtonsoft":"11.0.0","StackExchange.Redis.MultiplexerPool":"2.2.0","Swashbuckle.AspNetCore":"7.2.0","Swashbuckle.AspNetCore.Annotations":"7.2.0","Swashbuckle.AspNetCore.Filters":"8.0.2","Swashbuckle.AspNetCore.SwaggerUI":"7.2.0","System.IdentityModel.Tokens.Jwt":"8.9.0","xunit":"2.9.3","xunit.assert":"2.9.2","xunit.runner.visualstudio":"3.0.1","YamlDotNet":"16.3.0"},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"Microsoft.NETCore.App":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json"}} \ No newline at end of file +"restore":{"projectUniqueName":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiTest\\BrokerApiTest.csproj","projectName":"BrokerApiTest","projectPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiTest\\BrokerApiTest.csproj","outputPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiTest\\obj\\","projectStyle":"PackageReference","centralPackageVersionsManagementEnabled":true,"originalTargetFrameworks":["net8.0"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"net8.0":{"targetAlias":"net8.0","projectReferences":{"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiServer\\BrokerApiServer.csproj":{"projectPath":"D:\\03.SVN\\03.caliverse\\Server\\CaliServer\\BrokerApiServer\\BrokerApiServer.csproj"}}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"}}"frameworks":{"net8.0":{"targetAlias":"net8.0","dependencies":{"Microsoft.AspNetCore.Mvc.Testing":{"target":"Package","version":"[8.0.2, )","versionCentrallyManaged":true},"Microsoft.NET.Test.Sdk":{"target":"Package","version":"[17.12.0, )","versionCentrallyManaged":true},"Moq":{"target":"Package","version":"[4.20.72, )","versionCentrallyManaged":true},"xunit":{"target":"Package","version":"[2.9.3, )","versionCentrallyManaged":true},"xunit.runner.visualstudio":{"include":"Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive","suppressParent":"All","target":"Package","version":"[3.0.1, )","versionCentrallyManaged":true}},"centralPackageVersions":{"Asp.Versioning.Mvc":"8.1.0","Asp.Versioning.Mvc.ApiExplorer":"8.1.0","Aspire.Hosting.AppHost":"9.1.0","AspNetCore.Swagger.Fluent.Annotations":"1.0.4","AsyncStateMachine":"1.3.2","AutoMapper":"14.0.0","AutoMapper.AspNetCore.OData.EFCore":"7.0.1","AutoMapper.Collection":"11.0.0","AutoMapper.Collection.EntityFrameworkCore":"11.0.0","AutoMapper.Contrib.Autofac.DependencyInjection":"9.0.0","AutoMapper.Data":"9.0.0","AutoMapper.EF6":"3.0.1","AutoMapper.Extensions.EnumMapping":"4.1.0","AutoMapper.Extensions.ExpressionMapping":"8.0.0","AWS.Logger.NLog":"3.3.4","AWSSDK.Core":"3.7.402.46","AWSSDK.DynamoDBv2":"3.7.407","AWSSDK.EC2":"3.7.330.4","AWSSDK.ElasticLoadBalancingV2":"3.7.303.15","AWSSDK.OpenSearchService":"3.7.404.81","AWSSDK.S3":"3.7.416.15","AWSSDK.SecurityToken":"3.7.401.89","Axion.ConcurrentHashSet":"1.0.0","BCrypt.Net-Next":"4.0.3","BouncyCastle.Cryptography":"2.5.1","CommandLineParser":"2.9.1","CommunityToolkit.Diagnostics":"8.2.2","CommunityToolkit.Mvvm":"8.2.2","Costura.Fody":"5.7.0","coverlet.collector":"6.0.4","Csv":"2.0.93","DocumentFormat.OpenXml":"2.20.0","DotNet.MultiMap":"2.2.1","DumpExtensions":"2.0.0","ExcelDataReader":"3.6.0","ExcelDataReader.DataSet":"3.6.0","ExcelNumberFormat":"1.1.0","FluentAssertions":"8.3.0","Google.Protobuf":"3.27.1","Grpc.AspNetCore":"2.63.0","Grpc.AspNetCore.Server.Reflection":"2.63.0","Grpc.Tools":"2.64.0","JWT":"11.0.0","MaterialDesignColors":"3.1.1-ci850","MaterialDesignThemes":"5.1.1-ci850","MaxMind.Db":"4.1.0","MaxMind.GeoIP2":"5.2.0","MediatR":"12.3.0","Microsoft.AspNetCore.Authentication.JwtBearer":"8.0.2","Microsoft.AspNetCore.Mvc.Testing":"8.0.2","Microsoft.AspNetCore.OpenApi":"8.0.6","Microsoft.Data.Sqlite":"8.0.6","Microsoft.Diagnostics.NETCore.Client":"0.2.621003","Microsoft.EntityFrameworkCore.Design":"8.0.2","Microsoft.EntityFrameworkCore.Tools":"8.0.2","Microsoft.Extensions.Caching.StackExchangeRedis":"8.0.6","Microsoft.Extensions.Configuration":"9.0.4","Microsoft.Extensions.Configuration.Abstractions":"8.0.0","Microsoft.Extensions.Configuration.UserSecrets":"8.0.0","Microsoft.Extensions.DependencyInjection":"8.0.0","Microsoft.Extensions.DependencyInjection.Abstractions":"8.0.1","Microsoft.Extensions.Hosting":"8.0.0","Microsoft.Extensions.Hosting.Abstractions":"8.0.0","Microsoft.Extensions.Http":"8.0.0","Microsoft.Extensions.Logging":"8.0.0","Microsoft.Extensions.Logging.Abstractions":"9.0.4","Microsoft.Extensions.Options":"9.0.4","Microsoft.Extensions.Options.ConfigurationExtensions":"8.0.0","Microsoft.NET.Test.Sdk":"17.12.0","Microsoft.OpenApi":"1.6.22","Microsoft.Xaml.Behaviors.Wpf":"1.1.122","MongoDB.Analyzer":"1.5.0","MongoDB.Bson":"3.3.0","MongoDB.Driver":"3.3.0","MongoDB.Driver.Core":"2.30.0","Moq":"4.20.72","MSTest.TestAdapter":"3.8.3","MSTest.TestFramework":"3.8.3","MySqlConnector":"2.4.0","NeoSmart.AsyncLock":"3.2.1","Newtonsoft.Json":"13.0.3","Nito.AsyncEx":"5.1.2","NJsonSchema.CodeGeneration.CSharp":"10.9.0","NLog":"5.4.0","NLog.Extensions.Logging":"5.3.11","NLog.Web.AspNetCore":"5.3.11","NSubstituteAutoSubstitute":"1.1.0","OneOf":"3.0.271","OpenTelemetry.AutoInstrumentation":"1.11.0","OpenTelemetry.Exporter.Console":"1.12.0","OpenTelemetry.Exporter.OpenTelemetryProtocol":"1.12.0","OpenTelemetry.Extensions.Hosting":"1.12.0","OpenTelemetry.Instrumentation.AspNetCore":"1.12.0","OpenTelemetry.Instrumentation.Http":"1.12.0","OpenTelemetry.PersistentStorage.FileSystem":"1.0.1","Otp.NET":"1.4.0","p4api.net":"2023.1.248.4623","Polly":"8.5.1","Pomelo.EntityFrameworkCore.MySql":"8.0.2","RabbitMQ.Client":"6.8.1","RedLock.net":"2.3.2","Sentry.AspNetCore":"5.10.0","Sentry.Extensions.Logging":"5.10.0","Sentry.NLog":"5.10.0","SharpSvn":"1.14003.272","sqlite-net-pcl":"1.9.172","SSH.NET":"2025.0.0","StackExchange.Redis":"2.8.31","StackExchange.Redis.Extensions.Core":"11.0.0","StackExchange.Redis.Extensions.Newtonsoft":"11.0.0","StackExchange.Redis.MultiplexerPool":"2.2.0","Swashbuckle.AspNetCore":"7.2.0","Swashbuckle.AspNetCore.Annotations":"7.2.0","Swashbuckle.AspNetCore.Filters":"8.0.2","Swashbuckle.AspNetCore.SwaggerUI":"7.2.0","System.IdentityModel.Tokens.Jwt":"8.9.0","System.IO.Hashing":"9.0.1","Ulid":"1.3.4","xunit":"2.9.3","xunit.assert":"2.9.3","xunit.extensibility.core":"2.9.3","xunit.runner.visualstudio":"3.0.1","YamlDotNet":"16.3.0"},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"Microsoft.NETCore.App":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json"}} \ No newline at end of file diff --git a/BrokerApiTest/obj/rider.project.model.nuget.info b/BrokerApiTest/obj/rider.project.model.nuget.info index cc82d41..4f89c45 100644 --- a/BrokerApiTest/obj/rider.project.model.nuget.info +++ b/BrokerApiTest/obj/rider.project.model.nuget.info @@ -1 +1 @@ -17447875961287532 \ No newline at end of file +17507459769450548 \ No newline at end of file diff --git a/BrokerApiTest/obj/rider.project.restore.info b/BrokerApiTest/obj/rider.project.restore.info index 43b5b0b..1d2e414 100644 --- a/BrokerApiTest/obj/rider.project.restore.info +++ b/BrokerApiTest/obj/rider.project.restore.info @@ -1 +1 @@ -17460517968346217 \ No newline at end of file +17527175926882760 \ No newline at end of file diff --git a/GameServer/0. Base/BaseGetSet.cs b/GameServer/0. Base/BaseGetSet.cs index 91e0599..893aa21 100644 --- a/GameServer/0. Base/BaseGetSet.cs +++ b/GameServer/0. Base/BaseGetSet.cs @@ -1,4 +1,5 @@ -using Google.Protobuf; +using GameServer.Contents.WorldEvent; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -24,6 +25,9 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri public SeasonPassManager getSeasonPassManager() => m_season_pass_manager; public LandManager getLandManager() => m_land_manager; public BuildingManager getBuildingManager() => m_building_manager; + public BannerManager getBannerManager() => m_banner_manager; + public RankingScheduleManager getRankingScheduleManager() => m_ranking_schedule_manager; + public RankingManager getRankingManager() => m_ranking_manager; public ReservationManager getReservationManager() { @@ -41,6 +45,8 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri { return ServerBase.ServerMetricsHelper.getServerMetricsCacheRequest(this); } + + public WorldEventScheduleManager getWorldEventScheduleManager() => _worldEventScheduleManager; } @@ -52,4 +58,4 @@ public partial class GameLoginListener : ProudNetListener NullReferenceCheckHelper.throwIfNull(game_server_logic, () => $"game_server_logic is null !!!"); return game_server_logic; } -} \ No newline at end of file +} diff --git a/GameServer/Contents/AppearanceCustomize/AppearanceCustomizeCheat.cs b/GameServer/Contents/AppearanceCustomize/AppearanceCustomizeCheat.cs index dc20652..f678924 100644 --- a/GameServer/Contents/AppearanceCustomize/AppearanceCustomizeCheat.cs +++ b/GameServer/Contents/AppearanceCustomize/AppearanceCustomizeCheat.cs @@ -186,4 +186,4 @@ public class BeaconCharacterCustomize : ChatCommandBase return; } } -} \ No newline at end of file +} diff --git a/GameServer/Contents/BeaconShop/Action/BeaconShopAction.cs b/GameServer/Contents/BeaconShop/Action/BeaconShopAction.cs index 548fac6..1933849 100644 --- a/GameServer/Contents/BeaconShop/Action/BeaconShopAction.cs +++ b/GameServer/Contents/BeaconShop/Action/BeaconShopAction.cs @@ -264,6 +264,7 @@ public class BeaconShopAction : EntityActionBase OwnerNickName = player.getUserNickname(), BeaconMyHomeGuid = beacon_attribute.LocatedInstanceGuid, SellingFinishTime = selling_finish_time, + CreatedAt = DateTime.UtcNow, }; // 6. BeaconShopSoldPriceDoc이 존재하는 지 체크하고, 없으면 생성 한다 !!! @@ -648,7 +649,7 @@ public class BeaconShopAction : EntityActionBase // 3. 비용 차감 var money_action = player.getEntityAction(); - result = await money_action.changeMoney(CurrencyType.Calium, -1 * beacon_shop_item_attribute.PriceForUnit * itemAmount); + result = await money_action.changeMoney(CurrencyType.Calium, -1 * beacon_shop_item_attribute.PriceForUnit * itemAmount, useCaliumEvent:false); if (result.isFail()) { err_msg = $"Not enough calium !!! : itemGuid:{itemGuid}, need Calium fee : {beacon_shop_item_attribute.PriceForUnit * itemAmount} - {player.toBasicString()}"; @@ -791,18 +792,27 @@ public class BeaconShopAction : EntityActionBase private bool isRentalMyhome(UgcNpc ugcNpc) { - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - var beacon_attribute = ugcNpc.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(beacon_attribute, () => $"beacon_attribute is null !!!"); - var rental_agent_action = player.getEntityAction(); - - if (beacon_attribute.State != EntityStateType.UsingByMyHome || - rental_agent_action.isRentalMyhome(beacon_attribute.LocatedInstanceGuid) == false) - { + if (beacon_attribute.State != EntityStateType.UsingByMyHome) return false; + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var myhome_agent_action = player.getEntityAction(); + if (myhome_agent_action.tryGetMyHome(beacon_attribute.LocatedInstanceGuid, out _)) + { + var rental_agent_action = player.getEntityAction(); + + if (rental_agent_action.isRentalMyhome(beacon_attribute.LocatedInstanceGuid) == false) + return false; + } + else + { + if (!MapManager.Instance.getRoomMapTree(beacon_attribute.LocatedInstanceGuid, out _)) + return false; } return true; @@ -811,24 +821,54 @@ public class BeaconShopAction : EntityActionBase // 랜탈 기간이 안전한 시간 내 인지 확인 private bool isRentalSafeTime(UgcNpc ugcNpc) { - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - var beacon_attribute = ugcNpc.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(beacon_attribute, () => $"beacon_attribute is null !!!"); - var rental_agent_action = player.getEntityAction(); + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - var rental_finish_time = rental_agent_action.getRentalMyhomeFinishTime(beacon_attribute.LocatedInstanceGuid); - if (rental_finish_time.HasValue == false) + var myhome_agent_action = player.getEntityAction(); + if (myhome_agent_action.tryGetMyHome(beacon_attribute.LocatedInstanceGuid, out _)) { - return false; + var rental_agent_action = player.getEntityAction(); + + var rental_finish_time = rental_agent_action.getRentalMyhomeFinishTime(beacon_attribute.LocatedInstanceGuid); + if (rental_finish_time.HasValue == false) + { + return false; + } + + var now = DateTime.UtcNow; + if (rental_finish_time.Value.AddSeconds(-5) < now) + { + return false; + } } - - var now = DateTime.UtcNow; - if (rental_finish_time.Value.AddSeconds(-5) < now) + else { - return false; + if (!MapManager.Instance.getRoomMapTree(beacon_attribute.LocatedInstanceGuid, out var room_map_tree)) + return false; + + var building_map_tree = room_map_tree.ParentBuildingMapTree; + if (!building_map_tree.tryGetFloorByMyhomeGuid(beacon_attribute.LocatedInstanceGuid, out var floor)) + return false; + + var server_logic = GameServerApp.getServerLogic(); + var building_manager = server_logic.getBuildingManager(); + if (!building_manager.tryGetBuilding(building_map_tree.BuildingMetaId, out var building)) + return false; + + var building_floor_agent_action = building.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(building_floor_agent_action, () => $"building_floor_agent_action is null !!!"); + + if (!building_floor_agent_action.tryGetBuildingFloor(floor, out var building_floor)) + return false; + + var building_floor_action = building_floor.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(building_floor_action, () => $"building_floor_action is null !!!"); + + if (building_floor_action.isRentalFinish(-5)) + return false; } return true; @@ -1076,7 +1116,7 @@ public class BeaconShopAction : EntityActionBase // 1. 돈 지급 var money_action = player.getEntityAction(); - result = await money_action.changeMoney(CurrencyType.Calium, beacon_shop_sold_price_attribute.GivenPrice); + result = await money_action.changeMoney(CurrencyType.Calium, beacon_shop_sold_price_attribute.GivenPrice, useCaliumEvent:false); if (result.isFail()) { err_msg = $"Failed to changeMoney !!! : change price delta :{beacon_shop_sold_price_attribute.GivenPrice} - {player.toBasicString()}"; @@ -1152,7 +1192,7 @@ public class BeaconShopAction : EntityActionBase return result; } - public async Task BeaconShopSearchItem(META_ID item_meta_id) + public async Task BeaconShopSearchItem(List item_meta_ids, int page, int itemPerPage, List sortInfos) { var result = new Result(); var err_msg = string.Empty; @@ -1162,15 +1202,15 @@ public class BeaconShopAction : EntityActionBase var server_logic = GameServerApp.getServerLogic(); NullReferenceCheckHelper.throwIfNull(server_logic, () => $"server_logic is null !!! - {player.toBasicString()}"); - (result, var beacon_shop_mongo_doc_list) = await m_beacon_shop_repository.get((int)item_meta_id); + (result, var beacon_shop_mongo_doc_list, var total_page) = await m_beacon_shop_repository.findIn(item_meta_ids, page, itemPerPage, sortInfos); if(result.isFail() || beacon_shop_mongo_doc_list == null) { if (result.isSuccess()) result.setFail(ServerErrorCode.BeaconShopFailedGetBoardItem, err_msg); - PacketHandler.BeaconShopSearchItemPacketHandler.send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(player, result, null); + PacketHandler.BeaconShopSearchItemPacketHandler.send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(player, result, null, 0, 0); return result; } - PacketHandler.BeaconShopSearchItemPacketHandler.send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(player, result, beacon_shop_mongo_doc_list); + PacketHandler.BeaconShopSearchItemPacketHandler.send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(player, result, beacon_shop_mongo_doc_list, page, total_page); return result; } @@ -1463,4 +1503,29 @@ public class BeaconShopAction : EntityActionBase return result; } + + public async Task<(Result, List?)> getBeaconShopRecentItems(int limitCount) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + (result, var beacon_shop_mongo_docs) = await m_beacon_shop_repository.recent(limitCount); + if (result.isFail()) + { + err_msg = $"Failed to BeaconShopRepository.recent() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, null); + } + NullReferenceCheckHelper.throwIfNull(beacon_shop_mongo_docs, () => $"beacon_shop_mongo_docs is null !!!"); + + var beacon_shop_recent_items = beacon_shop_mongo_docs.Select(x => x.toBeaconShopItemMongoDataClient()).ToList(); + + return (result, beacon_shop_recent_items); + } } diff --git a/GameServer/Contents/BeaconShop/MongoDb/BeaconShopMongoDoc.cs b/GameServer/Contents/BeaconShop/MongoDb/BeaconShopMongoDoc.cs index 740584d..d7c5f96 100644 --- a/GameServer/Contents/BeaconShop/MongoDb/BeaconShopMongoDoc.cs +++ b/GameServer/Contents/BeaconShop/MongoDb/BeaconShopMongoDoc.cs @@ -22,5 +22,7 @@ public class BeaconShopMongoDoc public string BeaconMyHomeGuid { get; set; } = null!; [BsonRequired] public DateTime SellingFinishTime { get; set; } = new(); + [BsonRequired] + public DateTime CreatedAt { get; set; } = new(); } diff --git a/GameServer/Contents/BeaconShop/MongoDb/BeaconShopRepository.cs b/GameServer/Contents/BeaconShop/MongoDb/BeaconShopRepository.cs index 6846901..86aa8f9 100644 --- a/GameServer/Contents/BeaconShop/MongoDb/BeaconShopRepository.cs +++ b/GameServer/Contents/BeaconShop/MongoDb/BeaconShopRepository.cs @@ -10,6 +10,7 @@ using BEACON_GUID = System.String; using META_ID = System.UInt32; using USER_GUID = System.String; using ITEM_GUID = System.String; +using ServerCommon; namespace GameServer; @@ -44,6 +45,72 @@ public class BeaconShopRepository : MongoDbRepository } } + public async Task<(Result, List?, int)> findIn(List tagIds, int page, int itemPerPage, List sortInfos) + { + var result = new Result(); + var err_msg = string.Empty; + + var filter_builder = Builders.Filter; + var filter = filter_builder.In(x => x.TagId, tagIds); + + var sort_builder = Builders.Sort; + + var sorts = sortInfos.Where(x => + { + switch (x.SortColumnType) + { + case SortColumnType.Price: + case SortColumnType.ItemId: + return true; + default: + return false; + } + }).Select(x => + { + SortDefinition sort; + + var sort_column = string.Empty; + switch (x.SortColumnType) + { + case SortColumnType.Price: + sort_column = "PriceForUnit"; + break; + case SortColumnType.ItemId: + sort_column = "TagId"; + break; + } + + if (x.SortType == SortType.Descending) + { + sort = sort_builder.Descending(sort_column); + } + else + { + sort = sort_builder.Ascending(sort_column); + } + + return sort; + }); + + var sort = sort_builder.Combine(sorts); + var skip_count = (page - 1) * itemPerPage; + + try + { + var total_count = (int)await m_collection.CountDocumentsAsync(filter); + var total_page = total_count / itemPerPage + (total_count % itemPerPage > 0 ? 1 : 0) + (total_count == 0 ? 1 : 0); + + var findData = await m_collection.Find(filter).Sort(sort).Skip(skip_count).Limit(itemPerPage).ToListAsync(); + return (result, findData.ToList(), total_page); + } + catch (Exception ex) + { + err_msg = $"MongoDB Write Exception Error: {ex.Message}"; + result.setFail(ServerErrorCode.BeaconShopFailedGetBoardItem, err_msg); + return (result, null, 0); + } + } + public async Task insert(BeaconShopMongoDoc beaconShopEntity) { var result = new Result(); @@ -131,4 +198,25 @@ public class BeaconShopRepository : MongoDbRepository return result; } + + public async Task<(Result, List?)> recent(int limitCount) + { + var result = new Result(); + var err_msg = string.Empty; + + var builder = Builders.Filter; + var filter = builder.Where(x => true); + + try + { + var recentData = await m_collection.Find(filter).Sort(Builders.Sort.Descending("CreatedAt")).Limit(limitCount).ToListAsync(); + return (result, recentData); + } + catch (Exception ex) + { + err_msg = $"MongoDB Write Exception Error: {ex.Message}"; + result.setFail(ServerErrorCode.BeaconShopFailedRecentBoardItem, err_msg); + return (result, null); + } + } } diff --git a/GameServer/Contents/BeaconShop/PacketHandler/BeaconShopRecentRegisterItemsPacketHandler.cs b/GameServer/Contents/BeaconShop/PacketHandler/BeaconShopRecentRegisterItemsPacketHandler.cs new file mode 100644 index 0000000..330ef7a --- /dev/null +++ b/GameServer/Contents/BeaconShop/PacketHandler/BeaconShopRecentRegisterItemsPacketHandler.cs @@ -0,0 +1,71 @@ +using GameServer.PacketHandler; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ClientToGameRes.Types; +using static ClientToLoginMessage.Types; + +namespace GameServer.PacketHandler +{ + [PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_BEACON_SHOP_RECENT_REGISTER_ITEMS), typeof(BeaconShopRecentRegisterItemsPacketHandler), typeof(GameLoginListener))] + internal class BeaconShopRecentRegisterItemsPacketHandler : PacketRecvHandler + { + public static bool send_S2C_ACK_BEACON_SHOP_RECENT_REGISTER_ITEMS(Player owner, Result result, GS2C_ACK_BEACON_SHOP_RECENT_REGISTER_ITEMS res) + { + var ack_packet = new ClientToGame(); + ack_packet.Response = new ClientToGameRes(); + + ack_packet.Response.ErrorCode = result.ErrorCode; + ack_packet.Response.AckBeaconShopRecentRegisterItems = res; + + if (false == GameServerApp.getServerLogic().onSendPacket(owner, ack_packet)) + { + return false; + } + + return true; + } + + public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + NullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + var request = req_msg.Request.ReqBeaconShopRecentRegisterItems; + var res = new GS2C_ACK_BEACON_SHOP_RECENT_REGISTER_ITEMS(); + + var limit_count = MetaHelper.GameConfigMeta.BeaconShopRecentItemCount; + + var beacon_shop_action = player.getEntityAction(); + (result, var beacon_shop_recent_items) = await beacon_shop_action.getBeaconShopRecentItems(limit_count); + if (result.isFail()) + { + err_msg = $"Fail to getBeaconShopRecentItems() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + send_S2C_ACK_BEACON_SHOP_RECENT_REGISTER_ITEMS(player, result, res); + return result; + } + NullReferenceCheckHelper.throwIfNull(beacon_shop_recent_items, () => $"beacon_shop_recent_items is null !!!"); + + res.RecentRegisterBeaconShopItemBoardInfos.AddRange(beacon_shop_recent_items); + + send_S2C_ACK_BEACON_SHOP_RECENT_REGISTER_ITEMS(player, result, res); + + return result; + } + } +} diff --git a/GameServer/Contents/BeaconShop/PacketHandler/BeaconShopSearchItemPacketHandler.cs b/GameServer/Contents/BeaconShop/PacketHandler/BeaconShopSearchItemPacketHandler.cs index ba596fa..d31969d 100644 --- a/GameServer/Contents/BeaconShop/PacketHandler/BeaconShopSearchItemPacketHandler.cs +++ b/GameServer/Contents/BeaconShop/PacketHandler/BeaconShopSearchItemPacketHandler.cs @@ -19,7 +19,7 @@ namespace GameServer.PacketHandler; [PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_BEACON_SHOP_SEARCH_ITEM), typeof(BeaconShopSearchItemPacketHandler), typeof(GameLoginListener))] public class BeaconShopSearchItemPacketHandler : PacketRecvHandler { - public static bool send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(Player player, Result result, List? beaconShopMongoDocs) + public static bool send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(Player player, Result result, List? beaconShopMongoDocs, int currentPage, int totalPage) { var ack_packet = new ClientToGame(); ack_packet.Response = new ClientToGameRes(); @@ -30,6 +30,8 @@ public class BeaconShopSearchItemPacketHandler : PacketRecvHandler if (result.isSuccess() && beaconShopMongoDocs != null) { ack_packet.Response.AckBeaconShopSearchItem.BeaconShopItemBoardInfos.AddRange(beaconShopMongoDocs.Select(x => x.toBeaconShopItemMongoDataClient()).ToList()); + ack_packet.Response.AckBeaconShopSearchItem.CurrentPage = currentPage; + ack_packet.Response.AckBeaconShopSearchItem.TotalPage = totalPage; } if (false == GameServerApp.getServerLogic().onSendPacket(player, ack_packet)) @@ -55,7 +57,7 @@ public class BeaconShopSearchItemPacketHandler : PacketRecvHandler result.setFail(ServerErrorCode.EntityActionNotFound, err_msg); Log.getLogger().error(err_msg); - send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(entity_player, result, null); + send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(entity_player, result, null, 0, 0); return result; } @@ -66,26 +68,29 @@ public class BeaconShopSearchItemPacketHandler : PacketRecvHandler result.setFail(ServerErrorCode.ClassTypeCastIsNull, err_msg); Log.getLogger().error(result.toBasicString()); - send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(entity_player, result, null); + send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(entity_player, result, null, 0, 0); return result; } var request = game_msg.Request.ReqBeaconShopSearchItem; - if (request.ItemMetaid == 0) + if (request.ItemMetaids.Count == 0) { - err_msg = $"Invalid Request Argument !!! : ItemMetaid : {request.ItemMetaid}"; + err_msg = $"Invalid Request Argument !!! : ItemMetaids empty"; result.setFail(ServerErrorCode.BeaconShopInvalidArgument, err_msg); Log.getLogger().error(result.toBasicString()); - send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(entity_player, result, null); + send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(entity_player, result, null, 0, 0); return result; } - result = await beacon_shop_action.BeaconShopSearchItem((META_ID)request.ItemMetaid); + var page = request.Page < 1 ? 1 : request.Page; + var limit_count = MetaHelper.GameConfigMeta.BeaconShopSearchCount; + + result = await beacon_shop_action.BeaconShopSearchItem(request.ItemMetaids.ToList(), page, limit_count, request.SortInfo.toList()); if (result.isFail()) { - send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(entity_player, result, null); + send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(entity_player, result, null, 0, 0); return result; } @@ -100,6 +105,6 @@ public class BeaconShopSearchItemPacketHandler : PacketRecvHandler var player = entityWithSession as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! - {entityWithSession.toBasicString()}"); - send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(player, errorResult, null); + send_S2C_ACK_BEACON_SHOP_SEARCH_ITEM(player, errorResult, null, 0, 0); } } diff --git a/GameServer/Contents/Calium/Action/CaliumConverterAction.cs b/GameServer/Contents/Calium/Action/CaliumConverterAction.cs index 0e98bca..4afabf4 100644 --- a/GameServer/Contents/Calium/Action/CaliumConverterAction.cs +++ b/GameServer/Contents/Calium/Action/CaliumConverterAction.cs @@ -68,7 +68,7 @@ public class CaliumConverterAction : EntityActionBase var total_calium = calium_storage_action.getTotalCalium(CaliumStorageType.Converter); if (total_calium < 0) { - err_msg = $"fail to convert calium!!! : loading calium - {checkConvertConditions}"; + err_msg = $"fail to convert calium!!! : loading calium - {nameof(checkConvertConditions)}"; result.setFail(ServerErrorCode.FailToLoadCalium, err_msg); Log.getLogger().error(err_msg); return result; diff --git a/GameServer/Contents/Claim/PacketHandler/ClaimRewardPacketHandler.cs b/GameServer/Contents/Claim/PacketHandler/ClaimRewardPacketHandler.cs index 597f536..9605b92 100644 --- a/GameServer/Contents/Claim/PacketHandler/ClaimRewardPacketHandler.cs +++ b/GameServer/Contents/Claim/PacketHandler/ClaimRewardPacketHandler.cs @@ -1,4 +1,5 @@ -using Google.Protobuf; +using GameServer.EventInvokers; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -50,6 +51,8 @@ public class ClaimRewardPacketHandler : PacketRecvHandler { await QuestManager.It.QuestCheck(owner, new QuestReward(EQuestEventTargetType.REWARD, EQuestEventNameType.RECEIVED, reward.RewardGroupId)); } + + await owner.getGameEventCollector().collectEvent(new GameEventInvokerClaimRewarded(owner)); return result; } diff --git a/GameServer/Contents/Craft/Action/CraftAction.cs b/GameServer/Contents/Craft/Action/CraftAction.cs index 70f51e4..59941cd 100644 --- a/GameServer/Contents/Craft/Action/CraftAction.cs +++ b/GameServer/Contents/Craft/Action/CraftAction.cs @@ -1,6 +1,7 @@ using MetaAssets; using ServerCommon; -using ServerCore; using ServerBase; +using ServerCore; +using ServerBase; using System.Collections.Concurrent; using NeoSmart.AsyncLock; using GameServer.PacketHandler; @@ -10,912 +11,998 @@ using ANCHOR_GUID = System.String; using LOCATION_UNIQUE_ID = System.String; using System.Diagnostics.CodeAnalysis; using System.Linq; +using GameServer.EventInvokers; namespace GameServer { - public class CraftAction : EntityActionBase +public class CraftAction : EntityActionBase +{ + private ConcurrentDictionary m_Crafts = new(); + private bool m_isUpdateCraft = false; + + public CraftAction(Player owner) + : base(owner) { - private ConcurrentDictionary m_Crafts = new(); - private bool m_isUpdateCraft = false; + } - public CraftAction(Player owner) - : base(owner) + public override Task onInit() + { + var result = new Result(); + + return Task.FromResult(result); + } + + public override void onClear() + { + m_Crafts.Clear(); + } + + public bool isCraftingHome() + { + if (m_Crafts.Count == 0) { + return false; } - public override Task onInit() - { - var result = new Result(); + return true; + } - return Task.FromResult(result); - } + public void setUpdateCraft(bool isUpdateCraft) + { + m_isUpdateCraft = isUpdateCraft; + } - public override void onClear() - { - m_Crafts.Clear(); - } + public bool isAnchorCrafting(string anchorGuid) + { + return m_Crafts.ContainsKey(anchorGuid); + } - public bool isCraftingHome() + public List getCraftInfoList() + { + return m_Crafts.Values.Select(x => x.toCraftData4Client()).ToList(); + } + + public bool getCraftInfo(string anchorGuid, [MaybeNullWhen(false)] out CraftInfo craftInfo) + { + craftInfo = default; + + if (!m_Crafts.TryGetValue(anchorGuid, out var craft)) + return false; + + craftInfo = craft.toCraftData4Client(); + + return true; + } + + public async Task AddCraftFromDocs(List craftDocs) + { + var result = new Result(); + var err_msg = string.Empty; + + m_Crafts.Clear(); + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + + foreach (var craft_doc in craftDocs) { - if(m_Crafts.Count == 0) + (result, var craft) = await Craft.createCraftFromDoc(player, craft_doc); + if (craft is null) { - return false; - } - return true; - } - - public void setUpdateCraft(bool isUpdateCraft) - { - m_isUpdateCraft = isUpdateCraft; - } - - public bool isAnchorCrafting(string anchorGuid) - { - return m_Crafts.ContainsKey(anchorGuid); - } - - public List getCraftInfoList() - { - return m_Crafts.Values.Select(x => x.toCraftData4Client()).ToList(); - } - - public bool getCraftInfo(string anchorGuid, [MaybeNullWhen(false)] out CraftInfo craftInfo ) - { - craftInfo = default; - - if (!m_Crafts.TryGetValue(anchorGuid, out var craft)) - return false; - - craftInfo = craft.toCraftData4Client(); - - return true; - } - - public async Task AddCraftFromDocs(List craftDocs) - { - var result = new Result(); - var err_msg = string.Empty; - - m_Crafts.Clear(); - - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - - - foreach (var craft_doc in craftDocs) - { - (result, var craft) = await Craft.createCraftFromDoc(player, craft_doc); - if(craft is null) - { - err_msg = $"Failed to create craft from doc"; - Log.getLogger().error(err_msg); - continue; - } - - var craft_attribute = craft.getEntityAttribute(); - if (craft_attribute is null) - { - err_msg = $"craft_attribute is null"; - Log.getLogger().error(err_msg); - continue; - } - if (m_Crafts.TryAdd(craft_attribute.AnchorGuid, craft) == false) - { - err_msg = $"Failed to get craft attribute : {nameof(CraftAttribute)}"; - result.setFail(ServerErrorCode.EntityAttributeIsNull, err_msg); - Log.getLogger().error(result.toBasicString()); - return result; - } + err_msg = $"Failed to create craft from doc"; + Log.getLogger().error(err_msg); + continue; } + var craft_attribute = craft.getEntityAttribute(); + if (craft_attribute is null) + { + err_msg = $"craft_attribute is null"; + Log.getLogger().error(err_msg); + continue; + } + + if (m_Crafts.TryAdd(craft_attribute.AnchorGuid, craft) == false) + { + err_msg = $"Failed to get craft attribute : {nameof(CraftAttribute)}"; + result.setFail(ServerErrorCode.EntityAttributeIsNull, err_msg); + Log.getLogger().error(result.toBasicString()); + return result; + } + } + + return result; + } + + public async Task ReloadCraft() + { + var result = new Result(); + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var server_logic = GameServerApp.getServerLogic(); + + var craft_doc = new CraftDoc(); + craft_doc.setCombinationKeyForPK(player.getUserGuid()); + craft_doc.onApplyPKSK(); + var query_config = server_logic.getDynamoDbClient().makeQueryConfigForReadByPKOnly(craft_doc.getPK()); + (result, var craft_docs) = await server_logic.getDynamoDbClient() + .simpleQueryDocTypesWithQueryOperationConfig(query_config); + if (result.isFail()) + { return result; } - public async Task ReloadCraft() + result = await AddCraftFromDocs(craft_docs); + if (result.isFail()) { - var result = new Result(); - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - - var server_logic = GameServerApp.getServerLogic(); - - var craft_doc = new CraftDoc(); - craft_doc.setCombinationKeyForPK(player.getUserGuid()); - craft_doc.onApplyPKSK(); - var query_config = server_logic.getDynamoDbClient().makeQueryConfigForReadByPKOnly(craft_doc.getPK()); - (result, var craft_docs) = await server_logic.getDynamoDbClient().simpleQueryDocTypesWithQueryOperationConfig(query_config); - if (result.isFail()) - { - return result; - } - - result = await AddCraftFromDocs(craft_docs); - if (result.isFail()) - { - return result; - } - return result; } - public async Task StartCraftProcess(ANCHOR_GUID anchor_guid, META_ID craft_meta_id, UGCNPC_GUID beacon_guid, Pos beacon_pos, int craft_count) + return result; + } + + public async Task StartCraftProcess(ANCHOR_GUID anchor_guid, META_ID craft_meta_id, UGCNPC_GUID beacon_guid, + Pos beacon_pos, int craft_count) + { + var result = new Result(); + var err_msg = string.Empty; + + if (m_isUpdateCraft == true) { - var result = new Result(); - var err_msg = string.Empty; + await ReloadCraft(); + } - if(m_isUpdateCraft == true) + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var server_logic = GameServerApp.getServerLogic(); + + var game_zone_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!!"); + + var curr_map = game_zone_action.getLinkedToMap(); + NullReferenceCheckHelper.throwIfNull(curr_map, () => $"curr_map is null !!!"); + + if (MetaData.Instance._CraftingMetaTable.TryGetValue((int)craft_meta_id, out var crafting_meta_data) == false) + { + err_msg = + $"Not found meta of crafting !!! : craft_meta_id : {craft_meta_id} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingMetaDataNotFound, err_msg); + Log.getLogger().error(err_msg); + StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); + return result; + } + + if (!crafting_meta_data.Enable) + { + err_msg = + $"This craft is currently disabled. craft_meta_id: {craft_meta_id} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingMetaDataDisabled, err_msg); + Log.getLogger().error(err_msg); + StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); + return result; + } + + result = checkStartCrafting(anchor_guid, crafting_meta_data, beacon_guid, craft_count); + if (result.isFail()) + { + StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); + return result; + } + + var inventory_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!!"); + + var player_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(player_action, () => $"player_action is null !!!"); + + var myhome_agent_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(myhome_agent_action, () => $"myhome_agent_action is null !!!"); + + DateTime now = DateTimeHelper.Current; + DateTime craft_finish_date = now.AddSeconds(crafting_meta_data.CraftingTime * craft_count); + + string myhome_guid = string.Empty; + + UgcNpc? ugc_npc = null; + if (beacon_guid != string.Empty) + { + ugc_npc = player_action.findUgcNpc(beacon_guid); + if (ugc_npc == null) { - await ReloadCraft(); - } - - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - - var server_logic = GameServerApp.getServerLogic(); - - var game_zone_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!!"); - - if (MetaData.Instance._CraftingMetaTable.TryGetValue((int)craft_meta_id, out var crafting_meta_data) == false) - { - err_msg = $"Not found meta of crafting !!! : craft_meta_id : {craft_meta_id} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftingMetaDataNotFound, err_msg); + err_msg = $"UgcNpc Not Found UgcNpc Guid : {beacon_guid} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg); Log.getLogger().error(err_msg); StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); return result; } - result = checkStartCrafting(anchor_guid, crafting_meta_data, beacon_guid, craft_count); - if (result.isFail()) + craft_finish_date = craft_finish_date.AddSeconds(-1 * crafting_meta_data.Beacon_ReduceTime * craft_count); + myhome_guid = myhome_agent_action.getMyHomeGuidByAnchorGuid(anchor_guid); + if (myhome_guid.isNullOrWhiteSpace()) { + err_msg = $"Failed to get Myhome Guid. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.AnchorIsNotInMyhome, err_msg); + Log.getLogger().error(err_msg); StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); return result; } + } - var inventory_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!!"); + var fn_start_craft_profile = async delegate() + { + var result = new Result(); + var err_msg = string.Empty; - var player_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(player_action, () => $"player_action is null !!!"); + var invokers = new List(); - var myhome_agent_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(myhome_agent_action, () => $"myhome_agent_action is null !!!"); + DynamoDbDocBase? dynamoDbDocBase = null; - DateTime now = DateTimeHelper.Current; - DateTime craft_finish_date = now.AddSeconds(crafting_meta_data.CraftingTime * craft_count); - - string myhome_guid = string.Empty; - - UgcNpc? ugc_npc = null; - if (beacon_guid != string.Empty) + if (ugc_npc != null) { - ugc_npc = player_action.findUgcNpc(beacon_guid); - if (ugc_npc == null) - { - err_msg = $"UgcNpc Not Found UgcNpc Guid : {beacon_guid} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg); - Log.getLogger().error(err_msg); - StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); - return result; - } - - craft_finish_date = craft_finish_date.AddSeconds(-1 * crafting_meta_data.Beacon_ReduceTime * craft_count); - myhome_guid = myhome_agent_action.getMyHomeGuidByAnchorGuid(anchor_guid); - if(myhome_guid.isNullOrWhiteSpace()) - { - err_msg = $"Failed to get Myhome Guid. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.AnchorIsNotInMyhome, err_msg); - Log.getLogger().error(err_msg); - StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); - return result; - } - } - - var fn_start_craft_profile = async delegate () - { - var result = new Result(); - var err_msg = string.Empty; - - var invokers = new List(); - - DynamoDbDocBase? dynamoDbDocBase = null; - - if (ugc_npc != null) - { - EntityPos entityPos = beacon_pos == null ? new EntityPos() : new EntityPos() { X = beacon_pos.X, Y = beacon_pos.Y, Z = beacon_pos.Z, FacingAngle = beacon_pos.Angle }; - modifyNpcData(player, ugc_npc, anchor_guid, entityPos); - dynamoDbDocBase = MakeNewNpcLocationDoc(ugc_npc, entityPos, anchor_guid, myhome_guid); - } - - List decrease_changed_items = new List(); - foreach (var material in crafting_meta_data.Material) - { - (result, var found_delete_item) = await inventory_action.tryDeleteItemByMetaId((META_ID)material.ItemId, (ushort)(material.ItemValue * craft_count)); - if (result.isFail()) + var entityPos = beacon_pos == null + ? new EntityPos() + : new EntityPos() { - StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); - return result; - } + X = beacon_pos.X, Y = beacon_pos.Y, Z = beacon_pos.Z, FacingAngle = beacon_pos.Angle + }; + var anchor_meta_id = getAnchorMetaId(player, anchor_guid); - decrease_changed_items.AddRange(found_delete_item); - } + var ugc_npc_action = ugc_npc.getEntityAction(); - (result, var craft) = await Craft.createCraft(player, anchor_guid, craft_meta_id, beacon_guid, now, craft_finish_date, craft_count); + ugc_npc_action.modifyStateInfo(EntityStateType.UsingByCrafting, entityPos, anchor_guid, + (META_ID)anchor_meta_id); + ugc_npc_action.setCountingAsConnectedUser(false); + result = await ugc_npc_action.tryLocateInGameZone(curr_map, myhome_guid); if (result.isFail()) { - StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); + err_msg = $"Failed to tryLocateInGameZone() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + return result; } - NullReferenceCheckHelper.throwIfNull(craft, () => $"craft is null !!!"); - var craft_attribute = craft.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(craft_attribute, () => $"craft_attribute is null !!!"); + dynamoDbDocBase = MakeNewNpcLocationDoc(ugc_npc, entityPos, anchor_guid, myhome_guid); + } - var task_log_data = CraftBusinessLogHelper.toCraftLogInfo(craft_attribute); - invokers.Add(new CraftBusinessLog(task_log_data)); - - var batch = new QueryBatchEx( player, LogActionType.CraftStart - , server_logic.getDynamoDbClient() ); - { - if(dynamoDbDocBase != null) batch.addQuery(new DBQEntityWrite(new List() { dynamoDbDocBase })); - batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); - } - - batch.appendBusinessLogs(invokers); - - result = await QueryHelper.sendQueryAndBusinessLog(batch); + List decrease_changed_items = new List(); + foreach (var material in crafting_meta_data.Material) + { + (result, var found_delete_item) = await inventory_action.tryDeleteItemByMetaId((META_ID)material.ItemId, + (ushort)(material.ItemValue * craft_count)); if (result.isFail()) { StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); return result; } - m_Crafts.TryAdd(anchor_guid, craft); - - if (ugc_npc != null) - { - var ugc_npc_action = ugc_npc.getEntityAction(); - ugc_npc_action.setCountingAsConnectedUser(false); - var game_zone_action = player.getEntityAction(); - - var map = game_zone_action.getLinkedToMap(); - NullReferenceCheckHelper.throwIfNull(map, () => $"map is null !!!"); - - result = await ugc_npc_action.tryLocateInGameZone(map, myhome_guid); - if(result.isSuccess()) - { - UgcNpcNotifyHelper.send_GS2C_NTF_BEACON_COMPACT_UPDATE(player, ugc_npc, ugc_npc_action.toUgcNpcCompact()); - } - } - - StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result, craft, decrease_changed_items, beacon_pos); - game_zone_action.broadcast(player, CraftNotifyHelper.makeNotiCraftInfoPacket(m_Crafts.Select(x => x.Value.toCraftData4Client()).ToList(), player.getUserGuid())); - return result; - }; - - result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "StartCraft", fn_start_craft_profile); - if (result.isFail()) - { - err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; - Log.getLogger().error(err_msg); + decrease_changed_items.AddRange(found_delete_item); } + (result, var craft) = await Craft.createCraft(player, anchor_guid, craft_meta_id, beacon_guid, now, + craft_finish_date, craft_count); + if (result.isFail()) + { + StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); + return result; + } + + NullReferenceCheckHelper.throwIfNull(craft, () => $"craft is null !!!"); + + var craft_attribute = craft.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(craft_attribute, () => $"craft_attribute is null !!!"); + + var task_log_data = CraftBusinessLogHelper.toCraftLogInfo(craft_attribute); + invokers.Add(new CraftBusinessLog(task_log_data)); + + var batch = new QueryBatchEx(player, LogActionType.CraftStart + , server_logic.getDynamoDbClient()); + { + if (dynamoDbDocBase != null) + batch.addQuery(new DBQEntityWrite(new List() { dynamoDbDocBase })); + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + } + + batch.appendBusinessLogs(invokers); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) + { + StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result); + return result; + } + + m_Crafts.TryAdd(anchor_guid, craft); + + if (ugc_npc != null) + { + var ugc_npc_action = ugc_npc.getEntityAction(); + UgcNpcNotifyHelper.send_GS2C_NTF_BEACON_COMPACT_UPDATE(player, ugc_npc, + ugc_npc_action.toUgcNpcCompact()); + } + + StartCraftPacketHandler.send_S2C_ACK_START_CRAFT(player, result, craft, decrease_changed_items, beacon_pos); + game_zone_action.broadcast(player, + CraftNotifyHelper.makeNotiCraftInfoPacket(m_Crafts.Select(x => x.Value.toCraftData4Client()).ToList(), + player.getUserGuid())); + return result; + }; + + result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "StartCraft", + fn_start_craft_profile); + if (result.isFail()) + { + err_msg = + $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + } + + return result; + } + + private int getAnchorMetaId(Player player, string anchor_guid) + { + var game_zone_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!!"); + + var crafting_anchor = game_zone_action.GetAnchor(anchor_guid); + if (crafting_anchor == null) + { + return 0; + } + + return crafting_anchor.AnchorProp.TableId; + } + + private Result checkStartCrafting(ANCHOR_GUID anchor_guid, CraftingMetaData crafting_meta_data, + UGCNPC_GUID beacon_guid, int craft_count) + { + var result = new Result(); + var err_msg = string.Empty; + + if (m_Crafts.TryGetValue(anchor_guid, out var craft) == true) + { + err_msg = + $"this Anchor is already Crafting now. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingAlreadyCrafting, err_msg); + Log.getLogger().error(err_msg); return result; } - private void modifyNpcData(Player player, UgcNpc ugc_npc, string anchor_guid, EntityPos currPos) + if (beacon_guid != string.Empty) { - var anchor_meta_id = getAnchorMetaId(player, anchor_guid); - var ugc_npc_action = ugc_npc.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {player.toBasicString()}"); - - ugc_npc_action.modifyStateInfo( EntityStateType.UsingByCrafting - , currPos - , anchor_guid, (META_ID)anchor_meta_id ); - } - - private int getAnchorMetaId(Player player, string anchor_guid) - { - var game_zone_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!!"); - - var crafting_anchor = game_zone_action.GetAnchor(anchor_guid); - if (crafting_anchor == null) - { - return 0; - } - - return crafting_anchor.AnchorProp.TableId; - } - - private Result checkStartCrafting(ANCHOR_GUID anchor_guid, CraftingMetaData crafting_meta_data, UGCNPC_GUID beacon_guid, int craft_count) - { - var result = new Result(); - var err_msg = string.Empty; - - if (m_Crafts.TryGetValue(anchor_guid, out var craft) == true) - { - err_msg = $"this Anchor is already Crafting now. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftingAlreadyCrafting, err_msg); - Log.getLogger().error(err_msg); - return result; - } - - if (beacon_guid != string.Empty) - { - result = checkNpcState(beacon_guid); - if (result.isFail()) { return result; } - } - - result = checkAbility(crafting_meta_data,beacon_guid); + result = checkNpcState(beacon_guid); if (result.isFail()) { return result; } + } - result = checkAnchor(crafting_meta_data, anchor_guid); - if(result.isFail()) { return result; } + result = checkAbility(crafting_meta_data, beacon_guid); + if (result.isFail()) { return result; } - //레시피 확인 - if (crafting_meta_data.RecipeType == ERecipeType.Add) - { - result = checkRecipe((META_ID)crafting_meta_data.Id); - if (result.isFail()) { return result; } - } + result = checkAnchor(crafting_meta_data, anchor_guid); + if (result.isFail()) { return result; } - if(crafting_meta_data.max_craft_limit < craft_count || craft_count < 1) - { - err_msg = $"craft limit is over. craft meta ID : {crafting_meta_data.Id} - max_craft_limit : {crafting_meta_data.max_craft_limit} - request craft_count : {craft_count} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftInvalidRequestCount, err_msg); - Log.getLogger().error(err_msg); - return result; - } + //레시피 확인 + if (crafting_meta_data.RecipeType == ERecipeType.Add) + { + result = checkRecipe((META_ID)crafting_meta_data.Id); + if (result.isFail()) { return result; } + } + if (crafting_meta_data.max_craft_limit < craft_count || craft_count < 1) + { + err_msg = + $"craft limit is over. craft meta ID : {crafting_meta_data.Id} - max_craft_limit : {crafting_meta_data.max_craft_limit} - request craft_count : {craft_count} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftInvalidRequestCount, err_msg); + Log.getLogger().error(err_msg); return result; } - private Result checkRecipe(META_ID craft_meta_id) + return result; + } + + private Result checkRecipe(META_ID craft_meta_id) + { + var result = new Result(); + var err_msg = string.Empty; + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var craft_recipe_action = player.getEntityAction(); + if (craft_recipe_action.HasRecipe((META_ID)craft_meta_id) == false) { - var result = new Result(); - var err_msg = string.Empty; - - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - - var craft_recipe_action = player.getEntityAction(); - if (craft_recipe_action.HasRecipe((META_ID)craft_meta_id) == false) - { - err_msg = $"Recipe is not register. Craft_meta_id : {craft_meta_id} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftingRecipeIsNotRegister, err_msg); - Log.getLogger().error(err_msg); - return result; - } - + err_msg = $"Recipe is not register. Craft_meta_id : {craft_meta_id} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingRecipeIsNotRegister, err_msg); + Log.getLogger().error(err_msg); return result; } - private Result checkNpcState(UGCNPC_GUID ugcNpc_guid) + return result; + } + + private Result checkNpcState(UGCNPC_GUID ugcNpc_guid) + { + var result = new Result(); + var err_msg = string.Empty; + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var player_action = player.getEntityAction(); + var ugc_npc = player_action.findUgcNpc(ugcNpc_guid); + if (ugc_npc == null) { - var result = new Result(); - var err_msg = string.Empty; + err_msg = $"UgcNpc Not Found UgcNpc Guid : {ugcNpc_guid} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg); + Log.getLogger().error(err_msg); + return result; + } - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + var ugc_ncp_attribute = ugc_npc.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ugc_ncp_attribute, () => $"ugc_ncp_attribute is null !!!"); + if (ugc_ncp_attribute.State != EntityStateType.None) + { + result.setFail(ServerErrorCode.NpcIsBusy, err_msg); + return result; + } + return result; + } + + private Result checkAbility(CraftingMetaData crafting_meta_data, UGCNPC_GUID ugcNpc_guid) + { + var result = new Result(); + var err_msg = string.Empty; + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var ability_action = player.getEntityAction(); + if (ugcNpc_guid != string.Empty) + { var player_action = player.getEntityAction(); var ugc_npc = player_action.findUgcNpc(ugcNpc_guid); if (ugc_npc == null) { - err_msg = $"UgcNpc Not Found UgcNpc Guid : {ugcNpc_guid} - {getOwner().toBasicString()}"; + err_msg = $"UgcNpc Not Found UgcNpc Guid : {ugcNpc_guid} - {player.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg); Log.getLogger().error(err_msg); return result; } - var ugc_ncp_attribute = ugc_npc.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(ugc_ncp_attribute, () => $"ugc_ncp_attribute is null !!!"); - if (ugc_ncp_attribute.State != EntityStateType.None) + ability_action = ugc_npc.getEntityAction(); + } + + foreach (var attribute in crafting_meta_data.Attribute) + { + if (false == EnumHelper.tryParse(attribute.AttributeName, out var attribute_type)) { - result.setFail(ServerErrorCode.NpcIsBusy, err_msg); + err_msg = $"Invalid Ability Enum Type : {attribute_type} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.ServerTypeInvalid, err_msg); + Log.getLogger().error(err_msg); return result; } + var attribute_value = ability_action.getAbility(attribute_type); + if (attribute_value == null) + { + err_msg = $"Invalid Ability Enum Type : {attribute_type} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.ServerTypeInvalid, err_msg); + Log.getLogger().error(err_msg); + return result; + } + + if (attribute_value < attribute.AttributeValue) + { + err_msg = + $"Not Enough Attribute for Craft. attribute name : {attribute_type}, need attribute : {attribute_value}, current attribute : {attribute.AttributeValue} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.NotEnoughAttribute, err_msg); + Log.getLogger().info(err_msg); + return result; + } + } + + return result; + } + + private Result checkAnchor(CraftingMetaData crafting_meta_data, ANCHOR_GUID anchor_guid) + { + var result = new Result(); + var err_msg = string.Empty; + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var game_zone_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!!"); + + var crafting_anchor = game_zone_action.GetAnchor(anchor_guid); + if (crafting_anchor == null) + { + err_msg = $"anchor is not placed !!! - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingAnchorIsNotPlaced, err_msg); + Log.getLogger().error(err_msg); + HelpCraftPacketHandler.send_S2C_ACK_HELP_CRAFT(player, result); return result; } - private Result checkAbility(CraftingMetaData crafting_meta_data, UGCNPC_GUID ugcNpc_guid) + if (MetaData.Instance._ItemTable.TryGetValue(crafting_anchor.AnchorProp.TableId, out var item_meta_data) == + false) + { + err_msg = + $"Not found meta of Item !!! : item_meta_id : {crafting_anchor.AnchorProp.TableId} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.ItemMetaDataNotFound, err_msg); + Log.getLogger().error(err_msg); + return result; + } + + if (item_meta_data.PropSmallType != crafting_meta_data.PropSmallType) + { + err_msg = + $"Group ID Not match with recipe. item PropSmallType : {item_meta_data.PropSmallType}, crafting PropSmallType : {crafting_meta_data.PropSmallType} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingAnchorIsNotMatchWithRecipe, err_msg); + Log.getLogger().error(err_msg); + return result; + } + + return result; + } + + + private NpcLocationInTargetDoc MakeNewNpcLocationDoc(UgcNpc ugcNpc, EntityPos currPos, string anchorMetaGuid, + string myhomeMetaGuid) + { + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + ArgumentNullReferenceCheckHelper.throwIfNull(ugcNpc, () => $"ugcNpc is null !!! - {player.toBasicString()}"); + + var game_zone_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_action, + () => $"game_zone_action is null !!! - {player.toBasicString()}"); + + var curr_map = game_zone_action.getLinkedToMap(); + NullReferenceCheckHelper.throwIfNull(curr_map, () => $"curr_map is null !!! - {player.toBasicString()}"); + + var ugc_npc_action = ugcNpc.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ugc_npc_action, + () => $"ugc_npc_action is null !!! - {player.toBasicString()}"); + + var ugc_npc_attribute = ugcNpc.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, + () => $"ugc_npc_attribute is null !!! - {player.toBasicString()}"); + + var location_unique_id = curr_map.makeLOCATION_UNIQUE_IDByMetaId(myhomeMetaGuid); + + var npcLocation = new NpcLocation() { AnchorMetaGuid = anchorMetaGuid, CurrentPos = currPos }; + + var doc = new NpcLocationInTargetDoc(location_unique_id, anchorMetaGuid + , EntityType.UgcNpc, ugc_npc_attribute.UgcNpcMetaGuid, npcLocation + , ugc_npc_attribute.OwnerEntityType, ugc_npc_attribute.OwnerGuid); + + doc.setQueryType(QueryType.Insert); + return doc; + } + + private NpcLocationInTargetDoc MakeDeleteNpcLocationDoc(string anchorMetaGuid, string myhomeMetaGuid) + { + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var game_zone_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_action, + () => $"game_zone_action is null !!! - {player.toBasicString()}"); + + var curr_map = game_zone_action.getLinkedToMap(); + NullReferenceCheckHelper.throwIfNull(curr_map, () => $"curr_map is null !!! - {player.toBasicString()}"); + + + var location_unique_id = curr_map.makeLOCATION_UNIQUE_IDByMetaId(myhomeMetaGuid); + + return new NpcLocationInTargetDoc(location_unique_id, anchorMetaGuid); + } + + public async Task StopCraftProcess(ANCHOR_GUID anchor_guid) + { + var result = new Result(); + var err_msg = string.Empty; + + if (m_isUpdateCraft == true) + { + await ReloadCraft(); + } + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var server_logic = GameServerApp.getServerLogic(); + NullReferenceCheckHelper.throwIfNull(server_logic, + () => $"server_logic is null !!! - {player.toBasicString()}"); + + var game_zone_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!!"); + + if (m_Crafts.TryGetValue(anchor_guid, out var craft) == false) + { + err_msg = $"this Anchor is not Crafting now. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingNotCraftingAnchor, err_msg); + Log.getLogger().error(err_msg); + StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result); + return result; + } + + Int32 craft_meta_id = 0; + + var fn_stop_craft_profile = async delegate() { var result = new Result(); var err_msg = string.Empty; - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + var invokers = new List(); - var ability_action = player.getEntityAction(); - if (ugcNpc_guid != string.Empty) + DynamoDbDocBase? dynamoDbDocBase = null; + + var craft_attribute = craft.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(craft_attribute, () => $"craft_attribute is null !!!"); + craft_attribute.deleteEntityAttribute(); + + UgcNpc? ugc_npc = null; + if (craft_attribute.BeaconGuid != string.Empty) { var player_action = player.getEntityAction(); - var ugc_npc = player_action.findUgcNpc(ugcNpc_guid); + ugc_npc = player_action.findUgcNpc(craft_attribute.BeaconGuid); if (ugc_npc == null) { - err_msg = $"UgcNpc Not Found UgcNpc Guid : {ugcNpc_guid} - {player.toBasicString()}"; + err_msg = + $"UgcNpc Not Found UgcNpc Guid : {craft_attribute.BeaconGuid} - {getOwner().toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg); Log.getLogger().error(err_msg); + StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result); return result; } - ability_action = ugc_npc.getEntityAction(); - } + var ugc_npc_action = ugc_npc.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ugc_npc_action, + () => $"ugc_npc_action is null - {player.toBasicString()}"); - foreach (var attribute in crafting_meta_data.Attribute) - { - if (false == EnumHelper.tryParse(attribute.AttributeName, out var attribute_type)) + var myhome_agent_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(myhome_agent_action, () => $"myhome_agent_action is null !!!"); + + var myhome_guid = myhome_agent_action.getMyHomeGuidByAnchorGuid(anchor_guid); + if (myhome_guid.isNullOrWhiteSpace()) { - err_msg = $"Invalid Ability Enum Type : {attribute_type} - {player.toBasicString()}"; - result.setFail(ServerErrorCode.ServerTypeInvalid, err_msg); + err_msg = $"Failed to get Myhome Guid. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.AnchorIsNotInMyhome, err_msg); Log.getLogger().error(err_msg); + StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result); return result; } - var attribute_value = ability_action.getAbility(attribute_type); - if (attribute_value == null) + ugc_npc_action.modifyStateInfo(EntityStateType.None, new EntityPos()); + result = await ugc_npc_action.tryRemoveInGameZone(); + if (result.isFail()) { - err_msg = $"Invalid Ability Enum Type : {attribute_type} - {player.toBasicString()}"; - result.setFail(ServerErrorCode.ServerTypeInvalid, err_msg); + err_msg = $"Failed to tryRemoveInGameZone() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); + return result; } - if (attribute_value < attribute.AttributeValue) - { - err_msg = $"Not Enough Attribute for Craft. attribute name : {attribute_type}, need attribute : {attribute_value}, current attribute : {attribute.AttributeValue} - {player.toBasicString()}"; - result.setFail(ServerErrorCode.NotEnoughAttribute, err_msg); - Log.getLogger().info(err_msg); - return result; - } + dynamoDbDocBase = MakeDeleteNpcLocationDoc(anchor_guid, myhome_guid); } - return result; - } + var inventory_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!!"); - private Result checkAnchor(CraftingMetaData crafting_meta_data, ANCHOR_GUID anchor_guid) - { - var result = new Result(); - var err_msg = string.Empty; - - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - - var game_zone_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!!"); - - var crafting_anchor = game_zone_action.GetAnchor(anchor_guid); - if (crafting_anchor == null) + if (MetaData.Instance._CraftingMetaTable.TryGetValue((int)craft_attribute.CraftMetaId, + out var crafting_meta_data) == false) { - err_msg = $"anchor is not placed !!! - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftingAnchorIsNotPlaced, err_msg); - Log.getLogger().error(err_msg); - HelpCraftPacketHandler.send_S2C_ACK_HELP_CRAFT(player, result); - return result; - } - - if (MetaData.Instance._ItemTable.TryGetValue(crafting_anchor.AnchorProp.TableId, out var item_meta_data) == false) - { - err_msg = $"Not found meta of Item !!! : item_meta_id : {crafting_anchor.AnchorProp.TableId} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.ItemMetaDataNotFound, err_msg); - Log.getLogger().error(err_msg); - return result; - } - - if (item_meta_data.PropSmallType != crafting_meta_data.PropSmallType) - { - err_msg = $"Group ID Not match with recipe. item PropSmallType : {item_meta_data.PropSmallType}, crafting PropSmallType : {crafting_meta_data.PropSmallType} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftingAnchorIsNotMatchWithRecipe, err_msg); - Log.getLogger().error(err_msg); - return result; - } - - return result; - } - - - private NpcLocationInTargetDoc MakeNewNpcLocationDoc(UgcNpc ugcNpc, EntityPos currPos, string anchorMetaGuid, string myhomeMetaGuid) - { - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - ArgumentNullReferenceCheckHelper.throwIfNull(ugcNpc, () => $"ugcNpc is null !!! - {player.toBasicString()}"); - - var game_zone_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!! - {player.toBasicString()}"); - - var curr_map = game_zone_action.getLinkedToMap(); - NullReferenceCheckHelper.throwIfNull(curr_map, () => $"curr_map is null !!! - {player.toBasicString()}"); - - var ugc_npc_action = ugcNpc.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {player.toBasicString()}"); - - var ugc_npc_attribute = ugcNpc.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {player.toBasicString()}"); - - var location_unique_id = curr_map.makeLOCATION_UNIQUE_IDByMetaId(myhomeMetaGuid); - - var npcLocation = new NpcLocation() { AnchorMetaGuid = anchorMetaGuid, CurrentPos = currPos }; - - var doc = new NpcLocationInTargetDoc(location_unique_id, anchorMetaGuid - , EntityType.UgcNpc, ugc_npc_attribute.UgcNpcMetaGuid, npcLocation - , ugc_npc_attribute.OwnerEntityType, ugc_npc_attribute.OwnerGuid); - - doc.setQueryType(QueryType.Insert); - return doc; - } - - private NpcLocationInTargetDoc MakeDeleteNpcLocationDoc(string anchorMetaGuid, string myhomeMetaGuid) - { - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - - var game_zone_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!! - {player.toBasicString()}"); - - var curr_map = game_zone_action.getLinkedToMap(); - NullReferenceCheckHelper.throwIfNull(curr_map, () => $"curr_map is null !!! - {player.toBasicString()}"); - - - var location_unique_id = curr_map.makeLOCATION_UNIQUE_IDByMetaId(myhomeMetaGuid); - - return new NpcLocationInTargetDoc(location_unique_id, anchorMetaGuid); - } - public async Task StopCraftProcess(ANCHOR_GUID anchor_guid) - { - var result = new Result(); - var err_msg = string.Empty; - - if (m_isUpdateCraft == true) - { - await ReloadCraft(); - } - - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - - var server_logic = GameServerApp.getServerLogic(); - NullReferenceCheckHelper.throwIfNull(server_logic, () => $"server_logic is null !!! - {player.toBasicString()}"); - - var game_zone_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!!"); - - if (m_Crafts.TryGetValue(anchor_guid, out var craft) == false) - { - err_msg = $"this Anchor is not Crafting now. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftingNotCraftingAnchor, err_msg); + err_msg = + $"Not found meta of Item !!! : CraftingMetaId:{craft_attribute.CraftMetaId} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingMetaDataNotFound, err_msg); Log.getLogger().error(err_msg); StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result); return result; } - Int32 craft_meta_id = 0; - - var fn_stop_craft_profile = async delegate () + List Increase_changed_items = new List(); + foreach (var material in crafting_meta_data.Material) { - var result = new Result(); - var err_msg = string.Empty; - - var invokers = new List(); - - DynamoDbDocBase? dynamoDbDocBase = null; - - var craft_attribute = craft.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(craft_attribute, () => $"craft_attribute is null !!!"); - craft_attribute.deleteEntityAttribute(); - - UgcNpc? ugc_npc = null; - if (craft_attribute.BeaconGuid != string.Empty) - { - var player_action = player.getEntityAction(); - ugc_npc = player_action.findUgcNpc(craft_attribute.BeaconGuid); - if (ugc_npc == null) - { - err_msg = $"UgcNpc Not Found UgcNpc Guid : {craft_attribute.BeaconGuid} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg); - Log.getLogger().error(err_msg); - StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result); - return result; - } - - var ugc_npc_action = ugc_npc.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null - {player.toBasicString()}"); - - var myhome_agent_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(myhome_agent_action, () => $"myhome_agent_action is null !!!"); - - var myhome_guid = myhome_agent_action.getMyHomeGuidByAnchorGuid(anchor_guid); - if (myhome_guid.isNullOrWhiteSpace()) - { - err_msg = $"Failed to get Myhome Guid. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.AnchorIsNotInMyhome, err_msg); - Log.getLogger().error(err_msg); - StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result); - return result; - } - - ugc_npc_action.modifyStateInfo(EntityStateType.None, new EntityPos(), ""); - dynamoDbDocBase = MakeDeleteNpcLocationDoc(anchor_guid, myhome_guid); - } - - var inventory_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!!"); - - if (MetaData.Instance._CraftingMetaTable.TryGetValue((int)craft_attribute.CraftMetaId, out var crafting_meta_data) == false) - { - err_msg = $"Not found meta of Item !!! : CraftingMetaId:{craft_attribute.CraftMetaId} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftingMetaDataNotFound, err_msg); - Log.getLogger().error(err_msg); - StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result); - return result; - } - - List Increase_changed_items = new List(); - foreach(var material in crafting_meta_data.Material) - { - (result, var crafting_items) = await inventory_action.tryTakalbleToBag((META_ID)material.ItemId, (ushort)(material.ItemValue * craft_attribute.CraftCount)); - if (result.isFail()) - { - StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result); - return result; - } - - Increase_changed_items.AddRange(crafting_items); - } - - var task_log_data = CraftBusinessLogHelper.toCraftLogInfo(craft_attribute); - invokers.Add(new CraftBusinessLog(task_log_data)); - - var batch = new QueryBatchEx( player, LogActionType.CraftStop - , server_logic.getDynamoDbClient() ); - { - if(dynamoDbDocBase != null) batch.addQuery(new DBQEntityDelete(dynamoDbDocBase)); - batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); - } - - batch.appendBusinessLogs(invokers); - - result = await QueryHelper.sendQueryAndBusinessLog(batch); + (result, var crafting_items) = await inventory_action.tryTakalbleToBag((META_ID)material.ItemId, + (ushort)(material.ItemValue * craft_attribute.CraftCount)); if (result.isFail()) { StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result); return result; } - m_Crafts.TryRemove(anchor_guid, out craft); - - if (ugc_npc != null) - { - var ugc_npc_action = ugc_npc.getEntityAction(); - var game_zone_action = player.getEntityAction(); - result = await ugc_npc_action.tryRemoveInGameZone(); - if(result.isSuccess()) - { - UgcNpcNotifyHelper.send_GS2C_NTF_BEACON_COMPACT_UPDATE(player, ugc_npc, ugc_npc_action.toUgcNpcCompact()); - } - } - - StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result, anchor_guid, craft_attribute.BeaconGuid, Increase_changed_items); - game_zone_action.broadcast(player, CraftNotifyHelper.makeNotiCraftInfoPacket(m_Crafts.Select(x => x.Value.toCraftData4Client()).ToList(), player.getUserGuid())); - - craft_meta_id = (Int32)craft_attribute.CraftMetaId; - - return result; - }; - - result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "StopCraft", fn_stop_craft_profile); - if (result.isFail()) - { - err_msg = $"Failed to runTransaction() !!! : {result.toBasicString()} - {player.toBasicString()}"; - Log.getLogger().error(err_msg); - } - - if (craft_meta_id > 0) - { - await QuestManager.It.QuestCheck(player, new QuestCrafting(EQuestEventTargetType.CRAFTING, EQuestEventNameType.STOPPED, craft_meta_id)); - } - - return result; - } - - public async Task FinishCraftProcess(ANCHOR_GUID anchor_guid) - { - var result = new Result(); - var err_msg = string.Empty; - - if (m_isUpdateCraft == true) - { - await ReloadCraft(); - } - - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - var server_logic = GameServerApp.getServerLogic(); - - var game_zone_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!!"); - - if (m_Crafts.TryGetValue(anchor_guid, out var craft) == false) - { - err_msg = $"this Anchor is not Crafting now. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftingNotCraftingAnchor, err_msg); - Log.getLogger().error(err_msg); - return result; - } - - var craft_attribute = craft.getOriginEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(craft_attribute, () => $"craft_attribute is null !!!"); - - DateTime now = DateTimeHelper.Current; - if (craft_attribute.CraftFinishTime > now) - { - err_msg = $"Crafting Not Finish Time : now : {now} - craftFinishTime : {craft_attribute.CraftFinishTime} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftingNotFinish, err_msg); - Log.getLogger().error(err_msg); - return result; - } - - if (MetaData.Instance._CraftingMetaTable.TryGetValue((int)craft_attribute.CraftMetaId, out var crafting_meta_data) == false) - { - err_msg = $"Not found meta of Item !!! : CraftingMetaId:{craft_attribute.CraftMetaId} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.CraftingMetaDataNotFound, err_msg); - Log.getLogger().error(err_msg); - return result; - } - - var fn_finish_craft_profile = async delegate () - { - var result = new Result(); - var err_msg = string.Empty; - - var invokers = new List(); - DynamoDbDocBase? dynamoDbDocBase = null; - - var inventory_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!!"); - - var craft_attribute = craft.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(craft_attribute, () => $"craft_attribute is null !!!"); - craft_attribute.deleteEntityAttribute(); - - List Increase_changed_items = new List(); - (result, var crafting_items) = await inventory_action.tryTakalbleToBag((META_ID)crafting_meta_data.Crafting_ItemId, (ushort)(crafting_meta_data.Crafting_ItemValue * craft_attribute.CraftCount)); - if (result.isFail()) - { - FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result); - return result; - } - Increase_changed_items.AddRange(crafting_items); + } - UgcNpc? ugc_npc = null; - if (craft_attribute.BeaconGuid != string.Empty) + var task_log_data = CraftBusinessLogHelper.toCraftLogInfo(craft_attribute); + invokers.Add(new CraftBusinessLog(task_log_data)); + + var batch = new QueryBatchEx(player, LogActionType.CraftStop + , server_logic.getDynamoDbClient()); + { + if (dynamoDbDocBase != null) batch.addQuery(new DBQEntityDelete(dynamoDbDocBase)); + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + } + + batch.appendBusinessLogs(invokers); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) + { + StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result); + return result; + } + + m_Crafts.TryRemove(anchor_guid, out craft); + + if (ugc_npc != null) + { + var ugc_npc_action = ugc_npc.getEntityAction(); + UgcNpcNotifyHelper.send_GS2C_NTF_BEACON_COMPACT_UPDATE(player, ugc_npc, + ugc_npc_action.toUgcNpcCompact()); + } + + StopCraftPacketHandler.send_S2C_ACK_STOP_CRAFT(player, result, anchor_guid, craft_attribute.BeaconGuid, + Increase_changed_items); + game_zone_action.broadcast(player, + CraftNotifyHelper.makeNotiCraftInfoPacket(m_Crafts.Select(x => x.Value.toCraftData4Client()).ToList(), + player.getUserGuid())); + + craft_meta_id = (Int32)craft_attribute.CraftMetaId; + + return result; + }; + + result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "StopCraft", + fn_stop_craft_profile); + if (result.isFail()) + { + err_msg = $"Failed to runTransaction() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + } + + if (craft_meta_id > 0) + { + await QuestManager.It.QuestCheck(player, + new QuestCrafting(EQuestEventTargetType.CRAFTING, EQuestEventNameType.STOPPED, craft_meta_id)); + } + + return result; + } + + public async Task FinishCraftProcess(ANCHOR_GUID anchor_guid) + { + var result = new Result(); + var err_msg = string.Empty; + + if (m_isUpdateCraft == true) + { + await ReloadCraft(); + } + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + var server_logic = GameServerApp.getServerLogic(); + + var game_zone_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!!"); + + if (m_Crafts.TryGetValue(anchor_guid, out var craft) == false) + { + err_msg = $"this Anchor is not Crafting now. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingNotCraftingAnchor, err_msg); + Log.getLogger().error(err_msg); + return result; + } + + var craft_attribute = craft.getOriginEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(craft_attribute, () => $"craft_attribute is null !!!"); + + DateTime now = DateTimeHelper.Current; + if (craft_attribute.CraftFinishTime > now) + { + err_msg = + $"Crafting Not Finish Time : now : {now} - craftFinishTime : {craft_attribute.CraftFinishTime} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingNotFinish, err_msg); + Log.getLogger().error(err_msg); + return result; + } + + if (MetaData.Instance._CraftingMetaTable.TryGetValue((int)craft_attribute.CraftMetaId, + out var crafting_meta_data) == false) + { + err_msg = + $"Not found meta of Item !!! : CraftingMetaId:{craft_attribute.CraftMetaId} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.CraftingMetaDataNotFound, err_msg); + Log.getLogger().error(err_msg); + return result; + } + + ushort to_add_count = 0; + var fn_finish_craft_profile = async delegate() + { + var result = new Result(); + var err_msg = string.Empty; + + var invokers = new List(); + DynamoDbDocBase? dynamoDbDocBase = null; + + var inventory_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!!"); + + var craft_attribute = craft.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(craft_attribute, () => $"craft_attribute is null !!!"); + craft_attribute.deleteEntityAttribute(); + + List Increase_changed_items = new List(); + to_add_count = (ushort)(crafting_meta_data.Crafting_ItemValue * craft_attribute.CraftCount); + (result, var crafting_items) = + await inventory_action.tryTakalbleToBag((META_ID)crafting_meta_data.Crafting_ItemId, to_add_count); + if (result.isFail()) + { + FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result); + return result; + } + + Increase_changed_items.AddRange(crafting_items); + + UgcNpc? ugc_npc = null; + if (craft_attribute.BeaconGuid != string.Empty) + { + var player_action = player.getEntityAction(); + ugc_npc = player_action.findUgcNpc(craft_attribute.BeaconGuid); + if (ugc_npc == null) { - var player_action = player.getEntityAction(); - ugc_npc = player_action.findUgcNpc(craft_attribute.BeaconGuid); - if (ugc_npc == null) - { - err_msg = $"UgcNpc Not Found UgcNpc Guid : {craft_attribute.BeaconGuid} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg); - Log.getLogger().error(err_msg); - FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result); - return result; - } - - var myhome_agent_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(myhome_agent_action, () => $"myhome_agent_action is null !!!"); - - var myhome_guid = myhome_agent_action.getMyHomeGuidByAnchorGuid(anchor_guid); - if (myhome_guid.isNullOrWhiteSpace()) - { - err_msg = $"Failed to get Myhome Guid. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; - result.setFail(ServerErrorCode.AnchorIsNotInMyhome, err_msg); - Log.getLogger().error(err_msg); - FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result); - return result; - } - var ugc_npc_action = ugc_npc.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null - {player.toBasicString()}"); - - ugc_npc_action.modifyStateInfo(EntityStateType.None, new EntityPos(), ""); - dynamoDbDocBase = MakeDeleteNpcLocationDoc(anchor_guid, myhome_guid); - - (result, var bonus_items) = await inventory_action.tryTakalbleToBag((META_ID)crafting_meta_data.Beacon_BonusItemId, (ushort)(1 * craft_attribute.CraftCount)); - if (result.isFail()) - { - FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result); - return result; - } - - Increase_changed_items.AddRange(bonus_items); + err_msg = + $"UgcNpc Not Found UgcNpc Guid : {craft_attribute.BeaconGuid} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg); + Log.getLogger().error(err_msg); + FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result); + return result; } - var task_log_data = CraftBusinessLogHelper.toCraftLogInfo(craft_attribute); - invokers.Add(new CraftBusinessLog(task_log_data)); + var myhome_agent_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(myhome_agent_action, () => $"myhome_agent_action is null !!!"); - var batch = new QueryBatchEx(player, LogActionType.CraftFinish - , server_logic.getDynamoDbClient()); + var myhome_guid = myhome_agent_action.getMyHomeGuidByAnchorGuid(anchor_guid); + if (myhome_guid.isNullOrWhiteSpace()) { - if (dynamoDbDocBase != null) batch.addQuery(new DBQEntityDelete(dynamoDbDocBase)); - batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + err_msg = $"Failed to get Myhome Guid. anchor_guid : {anchor_guid} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.AnchorIsNotInMyhome, err_msg); + Log.getLogger().error(err_msg); + FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result); + return result; } - batch.appendBusinessLogs(invokers); + var ugc_npc_action = ugc_npc.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ugc_npc_action, + () => $"ugc_npc_action is null - {player.toBasicString()}"); - result = await QueryHelper.sendQueryAndBusinessLog(batch); + ugc_npc_action.modifyStateInfo(EntityStateType.None, new EntityPos()); + result = await ugc_npc_action.tryRemoveInGameZone(); + if (result.isFail()) + { + string room_id = string.Empty; + LOCATION_UNIQUE_ID unique_id = string.Empty; + int map_id = 0; + var map = game_zone_action.getLinkedToMap(); + if (map is not null) + { + room_id = map.m_room_id; + unique_id = map.getLocationUniqueId(); + map_id = map.MapMId; + } + + err_msg = + $"Failed to tryRemoveInGameZone Player room_id : {room_id}, Player Map Location id : {unique_id}, Player MapMid : {map_id}, BeaconGuid : {craft_attribute.BeaconGuid} - {getOwner().toBasicString()}"; + Log.getLogger().error(err_msg); + } + + dynamoDbDocBase = MakeDeleteNpcLocationDoc(anchor_guid, myhome_guid); + + // 보너스 아이템 id + var beaconBonusItemId = crafting_meta_data.Beacon_BonusItemId; + // 보너스 아이템 수 적용 + var beaconBonusItemValue = crafting_meta_data.Beacon_BonusItemValue; + (result, var bonusItems) = await inventory_action.tryTakalbleToBag((META_ID)beaconBonusItemId, + (ushort)(beaconBonusItemValue * craft_attribute.CraftCount)); if (result.isFail()) { FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result); return result; } - m_Crafts.TryRemove(anchor_guid, out craft); + Increase_changed_items.AddRange(bonusItems); + } - if (ugc_npc != null) - { - var ugc_npc_action = ugc_npc.getEntityAction(); - var game_zone_action = player.getEntityAction(); - result = await ugc_npc_action.tryRemoveInGameZone(); - if (result.isFail()) - { - string room_id = string.Empty; - LOCATION_UNIQUE_ID unique_id = string.Empty; - int map_id = 0; - var map = game_zone_action.getLinkedToMap(); - if (map is not null) - { - room_id = map.m_room_id; - unique_id = map.getLocationUniqueId(); - map_id = map.MapMId; - } - err_msg = $"Failed to tryRemoveInGameZone Player room_id : {room_id}, Player Map Location id : {unique_id}, Player MapMid : {map_id}, BeaconGuid : {craft_attribute.BeaconGuid} - {getOwner().toBasicString()}"; - Log.getLogger().error(err_msg); - } - } + var task_log_data = CraftBusinessLogHelper.toCraftLogInfo(craft_attribute); + invokers.Add(new CraftBusinessLog(task_log_data)); - FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result, anchor_guid, craft_attribute.BeaconGuid, Increase_changed_items); - game_zone_action.broadcast(player, CraftNotifyHelper.makeNotiCraftInfoPacket(m_Crafts.Select(x => x.Value.toCraftData4Client()).ToList(), player.getUserGuid())); + var batch = new QueryBatchEx(player, LogActionType.CraftFinish + , server_logic.getDynamoDbClient()); + { + if (dynamoDbDocBase != null) batch.addQuery(new DBQEntityDelete(dynamoDbDocBase)); + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + } - return result; - }; + batch.appendBusinessLogs(invokers); - result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "FinishCraft", fn_finish_craft_profile); + result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) { - err_msg = $"Failed to runTransaction() !!! : {result.toBasicString()} - {player.toBasicString()}"; - Log.getLogger().error(err_msg); + FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result); + return result; } - if (crafting_meta_data.Crafting_ItemId > 0) - { - await QuestManager.It.QuestCheck(player, new QuestCrafting(EQuestEventTargetType.CRAFTING, EQuestEventNameType.COMPLETED, crafting_meta_data.Crafting_ItemId)); - } - + m_Crafts.TryRemove(anchor_guid, out craft); + + FinishCraftPacketHandler.send_S2C_ACK_FINISH_CRAFT(player, result, anchor_guid, craft_attribute.BeaconGuid, + Increase_changed_items); + game_zone_action.broadcast(player, + CraftNotifyHelper.makeNotiCraftInfoPacket(m_Crafts.Select(x => x.Value.toCraftData4Client()).ToList(), + player.getUserGuid())); return result; - } + }; - public Result cheatAllCraftFinish() + result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "FinishCraft", + fn_finish_craft_profile); + if (result.isFail()) { - var result = new Result(); - var err_msg = string.Empty; + err_msg = $"Failed to runTransaction() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + } - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + if (crafting_meta_data.Crafting_ItemId > 0) + { + await QuestManager.It.QuestCheck(player, + new QuestCrafting(EQuestEventTargetType.CRAFTING, EQuestEventNameType.COMPLETED, + crafting_meta_data.Crafting_ItemId)); - var craftList = m_Crafts.Select(x => x.Value).ToList(); - - foreach (var craft in craftList) + if (to_add_count == 0) { - var craft_attribute = craft.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(craft_attribute, () => $"craft_attribute is null !!!"); - - craft_attribute.CraftFinishTime = DateTimeHelper.Current; - craft_attribute.modifiedEntityAttribute(); + err_msg = + $"to_add_count is zero !!! : Crafting_ItemValue : {crafting_meta_data.Crafting_ItemValue} and crafte_count : {craft_attribute.CraftCount}"; + Log.getLogger().warn(err_msg); } - return result; + for (int i = 0; i < to_add_count; i++) + { + await player.getGameEventCollector() + .collectEvent(new GameEventInvokerCraftingCompleted(player, crafting_meta_data.Crafting_ItemId)); + } } + + + return result; + } + + public Result cheatAllCraftFinish() + { + var result = new Result(); + var err_msg = string.Empty; + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var craftList = m_Crafts.Select(x => x.Value).ToList(); + + foreach (var craft in craftList) + { + var craft_attribute = craft.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(craft_attribute, () => $"craft_attribute is null !!!"); + + craft_attribute.CraftFinishTime = DateTimeHelper.Current; + craft_attribute.modifiedEntityAttribute(); + } + + return result; } } +} diff --git a/GameServer/Contents/Craft/CraftCheat.cs b/GameServer/Contents/Craft/CraftCheat.cs index fbf7849..4f2fdea 100644 --- a/GameServer/Contents/Craft/CraftCheat.cs +++ b/GameServer/Contents/Craft/CraftCheat.cs @@ -1,9 +1,12 @@ -using ServerCommon; -using ServerCore; using ServerBase; +using MetaAssets; +using ServerCommon; +using ServerCore; +using ServerBase; namespace GameServer; -[ChatCommandAttribute("crafthelpinit", typeof(CheatCraftHelpInit), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +[ChatCommandAttribute("crafthelpinit", typeof(CheatCraftHelpInit), AuthAdminLevelType.Developer, + AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] internal class CheatCraftHelpInit : ChatCommandBase { public override async Task invoke(Player player, string token, string[] args) @@ -11,16 +14,17 @@ internal class CheatCraftHelpInit : ChatCommandBase Log.getLogger().info($"CraftHelpInit"); var craft_help_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(craft_help_action, () => $"craft_help_action is null !!! - {player.toBasicString()}"); + NullReferenceCheckHelper.throwIfNull(craft_help_action, + () => $"craft_help_action is null !!! - {player.toBasicString()}"); var server_logic = GameServerApp.getServerLogic(); - var fn_start = async delegate () + var fn_start = async delegate() { craft_help_action.cheatChangeCraftHelpInit(); var batch = new QueryBatchEx(player, LogActionType.CheatCommandCraftHelpInit - , server_logic.getDynamoDbClient()); + , server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } @@ -36,17 +40,97 @@ internal class CheatCraftHelpInit : ChatCommandBase var transaction_name = "CheatCommandCraftHelpInit"; var result = await player.runTransactionRunnerSafelyWithTransGuid(player.getUserGuid() - , TransactionIdType.PrivateContents, transaction_name - , fn_start); + , TransactionIdType.PrivateContents, transaction_name + , fn_start); if (result.isFail()) { - var err_msg = $"Failed to runTransactionRunnerSafelyWithTransGuid() !!! : {result.toBasicString()} - transactionName:{transaction_name}, {player.toBasicString()}"; + var err_msg = + $"Failed to runTransactionRunnerSafelyWithTransGuid() !!! : {result.toBasicString()} - transactionName:{transaction_name}, {player.toBasicString()}"; Log.getLogger().error(err_msg); } } } -[ChatCommandAttribute("registercraftrecipe", typeof(CheatRegisterCraftRecipe), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +[ChatCommandAttribute("addcraftmaterials", typeof(CheatAddCraftMaterials), AuthAdminLevelType.Developer, + AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +internal class CheatAddCraftMaterials : ChatCommandBase +{ + public override async Task invoke(Player player, string token, string[] args) + { + if (args.Length < 1) + { + Log.getLogger() + .error($"[Cheat] Invalid Argument. Usage: /addcraftmaterials [recipe_meta_id] [craft_count(optional)]"); + return; + } + + if (!uint.TryParse(args[0], out uint recipe_meta_id)) + { + Log.getLogger().error($"[Cheat] Invalid recipe_meta_id: {args[0]}"); + return; + } + + int craft_count = 1; + if (args.Length > 1 && !int.TryParse(args[1], out craft_count)) + { + Log.getLogger().error($"[Cheat] Invalid craft_count: {args[1]}"); + return; + } + + if (MetaData.Instance._CraftingMetaTable.TryGetValue((int)recipe_meta_id, out var crafting_meta_data) == false) + { + Log.getLogger().error($"[Cheat] CraftingMetaData not found for id: {recipe_meta_id}"); + return; + } + + Log.getLogger().info($"[Cheat] AddCraftMaterials for recipe {recipe_meta_id}, count {craft_count}"); + + var server_logic = GameServerApp.getServerLogic(); + + var fn_add_materials = async delegate() + { + var result = new Result(); + var inventory_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(inventory_action, + () => $"inventory_action is null for player {player.toBasicString()}"); + + foreach (var material in crafting_meta_data.Material) + { + (result, var new_items) = await inventory_action.tryTakalbleToBag((uint)material.ItemId, + (ushort)(material.ItemValue * craft_count)); + if (result.isFail()) + { + // 인벤토리가 가득 찰 경우를 대비해 실패 시에도 계속 진행하도록 할 수 있습니다. + Log.getLogger().warn($"[Cheat] Failed to add material {material.ItemId}: {result.toBasicString()}"); + result.setSuccess(); // 에러를 무시하고 다음 재료 지급을 시도 + } + } + + var batch = new QueryBatchEx(player, LogActionType.CheatCommandAddCraftItems, + server_logic.getDynamoDbClient()); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + } + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + return result; + }; + + var transaction_name = "Cheat.AddCraftMaterials"; + var result = await player.runTransactionRunnerSafelyWithTransGuid(player.getUserGuid(), + TransactionIdType.PrivateContents, transaction_name, fn_add_materials); + + if (result.isFail()) + { + var err_msg = + $"[Cheat] Failed to runTransactionRunnerSafelyWithTransGuid() for AddCraftMaterials: {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + } + } +} + +[ChatCommandAttribute("registercraftrecipe", typeof(CheatRegisterCraftRecipe), AuthAdminLevelType.Developer, + AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] internal class CheatRegisterCraftRecipe : ChatCommandBase { public override async Task invoke(Player player, string token, string[] args) @@ -54,7 +138,8 @@ internal class CheatRegisterCraftRecipe : ChatCommandBase Log.getLogger().info($"CheatRegisterCraftRecipe"); var craft_recipe_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(craft_recipe_action, () => $"craft_recipe_action is null !!! - {player.toBasicString()}"); + NullReferenceCheckHelper.throwIfNull(craft_recipe_action, + () => $"craft_recipe_action is null !!! - {player.toBasicString()}"); var server_logic = GameServerApp.getServerLogic(); @@ -67,13 +152,13 @@ internal class CheatRegisterCraftRecipe : ChatCommandBase if (uint.TryParse(args[0], out uint recipe_meta_id) == false) return; - var fn_start = async delegate () + var fn_start = async delegate() { var result = await craft_recipe_action.RegisterRecipe(recipe_meta_id); if (result.isFail()) return result; var batch = new QueryBatchEx(player, LogActionType.CheatCommandRegisterCraftRecipe - , server_logic.getDynamoDbClient()); + , server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } @@ -89,17 +174,19 @@ internal class CheatRegisterCraftRecipe : ChatCommandBase var transaction_name = "Cheat.CommandRegisterCraftRecipe"; var result = await player.runTransactionRunnerSafelyWithTransGuid(player.getUserGuid() - , TransactionIdType.PrivateContents, transaction_name - , fn_start); + , TransactionIdType.PrivateContents, transaction_name + , fn_start); if (result.isFail()) { - var err_msg = $"Failed to runTransactionRunnerSafelyWithTransGuid() !!! : {result.toBasicString()} - transactionName:{transaction_name}, {player.toBasicString()}"; + var err_msg = + $"Failed to runTransactionRunnerSafelyWithTransGuid() !!! : {result.toBasicString()} - transactionName:{transaction_name}, {player.toBasicString()}"; Log.getLogger().error(err_msg); } } } -[ChatCommandAttribute("craftfinish", typeof(CheatAllCraftFinish), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +[ChatCommandAttribute("craftfinish", typeof(CheatAllCraftFinish), AuthAdminLevelType.Developer, + AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] internal class CheatAllCraftFinish : ChatCommandBase { public override async Task invoke(Player player, string token, string[] args) @@ -107,17 +194,18 @@ internal class CheatAllCraftFinish : ChatCommandBase Log.getLogger().info($"CheatAllCraftFinish"); var craft_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(craft_action, () => $"craft_action is null !!! - {player.toBasicString()}"); + NullReferenceCheckHelper.throwIfNull(craft_action, + () => $"craft_action is null !!! - {player.toBasicString()}"); var server_logic = GameServerApp.getServerLogic(); - var fn_start = async delegate () + var fn_start = async delegate() { var result = craft_action.cheatAllCraftFinish(); if (result.isFail()) return result; var batch = new QueryBatchEx(player, LogActionType.CheatAllCraftFinish - , server_logic.getDynamoDbClient()); + , server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } @@ -133,12 +221,360 @@ internal class CheatAllCraftFinish : ChatCommandBase var transaction_name = "Cheat.CommandAllCraftFinish"; var result = await player.runTransactionRunnerSafelyWithTransGuid(player.getUserGuid() - , TransactionIdType.PrivateContents, transaction_name - , fn_start); + , TransactionIdType.PrivateContents, transaction_name + , fn_start); if (result.isFail()) { - var err_msg = $"Failed to runTransactionRunnerSafelyWithTransGuid() !!! : {result.toBasicString()} - transactionName:{transaction_name}, {player.toBasicString()}"; + var err_msg = + $"Failed to runTransactionRunnerSafelyWithTransGuid() !!! : {result.toBasicString()} - transactionName:{transaction_name}, {player.toBasicString()}"; Log.getLogger().error(err_msg); } } -} \ No newline at end of file +} + +[ChatCommandAttribute("testallcraftrecipes", typeof(CheatTestAllCraftRecipes), AuthAdminLevelType.Developer, + AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +internal class CheatTestAllCraftRecipes : ChatCommandBase +{ + public override async Task invoke(Player player, string token, string[] args) + { + Log.getLogger().info($"[Cheat] TestAllCraftRecipes started for player {player.toBasicString()}"); + + var fn_test_all_recipes = async delegate() + { + var result = new Result(); + var server_logic = GameServerApp.getServerLogic(); + + // 액션 가져오기 + var craft_recipe_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(craft_recipe_action, + () => $"craft_recipe_action is null for player {player.toBasicString()}"); + + var inventory_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(inventory_action, + () => $"inventory_action is null for player {player.toBasicString()}"); + + var craft_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(craft_action, + () => $"craft_action is null for player {player.toBasicString()}"); + + var game_zone_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_action, + () => $"game_zone_action is null for player {player.toBasicString()}"); + + var player_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(player_action, + () => $"player_action is null for player {player.toBasicString()}"); + + // 1. 모든 레시피 등록 및 재료 지급 + Log.getLogger() + .info($"[Cheat][{player.toBasicString()}] Step 1: Registering all recipes and adding materials."); + foreach (var crafting_meta_data in MetaData.Instance._CraftingMetaTable.Values) + { + if (crafting_meta_data.Enable == false) + continue; + + // 레시피 등록 + if (!craft_recipe_action.HasRecipe((uint)crafting_meta_data.Id)) + { + result = await craft_recipe_action.RegisterRecipe((uint)crafting_meta_data.Id); + if (result.isFail()) + { + Log.getLogger() + .warn( + $"[Cheat][{player.toBasicString()}] Failed to register recipe {crafting_meta_data.Id}: {result.toBasicString()}"); + result.setSuccess(); // 실패해도 계속 진행 + } + } + + // 재료 아이템 지급 + foreach (var material in crafting_meta_data.Material) + { + (result, var new_items) = + await inventory_action.tryTakalbleToBag((uint)material.ItemId, (ushort)material.ItemValue); + if (result.isFail()) + { + Log.getLogger() + .warn( + $"[Cheat][{player.toBasicString()}] Failed to add material {material.ItemId} for recipe {crafting_meta_data.Id}: {result.toBasicString()}"); + result.setSuccess(); // 실패해도 계속 진행 + } + } + } + + // 2. 비컨 없이 제작 시작 -> 즉시 완료 -> 완료 처리 테스트 + Log.getLogger() + .info($"[Cheat][{player.toBasicString()}] Step 2: Testing Start -> Finish Craft (without Beacon)."); + (result, var testAnchorGuid1, var testRecipeId1) = + await TestStartAndFinishCraft(player, craft_action, game_zone_action, player_action, false); + if (result.isFail()) + { + Log.getLogger() + .error($"[Cheat][{player.toBasicString()}] Failed Start->Finish test: {result.toBasicString()}"); + return result; // 중대한 오류 시 중단 + } + + Log.getLogger().info($"[Cheat][{player.toBasicString()}] Step 2: Success (without Beacon)."); + + + // 3. 비컨 없이 제작 시작 -> 중단 테스트 + Log.getLogger() + .info($"[Cheat][{player.toBasicString()}] Step 3: Testing Start -> Stop Craft (without Beacon)."); + (result, var testAnchorGuid2, var testRecipeId2) = + await TestStartAndStopCraft(player, craft_action, game_zone_action, player_action, false); + if (result.isFail()) + { + Log.getLogger() + .error( + $"[Cheat][{player.toBasicString()}] Failed Start->Stop test (without Beacon): {result.toBasicString()}"); + return result; // 중대한 오류 시 중단 + } + + Log.getLogger().info($"[Cheat][{player.toBasicString()}] Step 3: Success (without Beacon)."); + + // 4. 비컨 사용 제작 시작 -> 즉시 완료 -> 완료 처리 테스트 + Log.getLogger() + .info($"[Cheat][{player.toBasicString()}] Step 4: Testing Start -> Finish Craft (with Beacon)."); + (result, var testAnchorGuid3, var testRecipeId3) = + await TestStartAndFinishCraft(player, craft_action, game_zone_action, player_action, true); + if (result.isFail()) + { + // 비컨이 없는 경우 테스트를 건너뛸 수 있도록 경고만 기록하고 계속 진행 + if (result.ErrorCode != ServerErrorCode.UgcNpcNotFound) + { + Log.getLogger() + .error( + $"[Cheat][{player.toBasicString()}] Failed Start->Finish test (with Beacon): {result.toBasicString()}"); + return result; + } + else + { + Log.getLogger() + .warn( + $"[Cheat][{player.toBasicString()}] Skipped Start->Finish test (with Beacon): No available beacon found."); + } + } + else + { + Log.getLogger().info($"[Cheat][{player.toBasicString()}] Step 4: Success (with Beacon)."); + } + + // 5. 비컨 사용 제작 시작 -> 중단 테스트 + Log.getLogger() + .info($"[Cheat][{player.toBasicString()}] Step 5: Testing Start -> Stop Craft (with Beacon)."); + (result, var testAnchorGuid4, var testRecipeId4) = + await TestStartAndStopCraft(player, craft_action, game_zone_action, player_action, true); + if (result.isFail()) + { + if (result.ErrorCode != ServerErrorCode.UgcNpcNotFound) + { + Log.getLogger() + .error( + $"[Cheat][{player.toBasicString()}] Failed Start->Stop test (with Beacon): {result.toBasicString()}"); + return result; + } + else + { + Log.getLogger() + .warn( + $"[Cheat][{player.toBasicString()}] Skipped Start->Stop test (with Beacon): No available beacon found."); + } + } + else + { + Log.getLogger().info($"[Cheat][{player.toBasicString()}] Step 5: Success (with Beacon)."); + } + + Log.getLogger().info($"[Cheat][{player.toBasicString()}] TestAllCraftRecipes finished successfully."); + return result; + }; + + var transaction_name = "Cheat.TestAllCraftRecipes"; + var result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, transaction_name, + fn_test_all_recipes); + if (result.isFail() && result.ErrorCode != ServerErrorCode.UgcNpcNotFound) // 비컨 없음 오류는 최종 실패로 간주하지 않음 + { + var err_msg = + $"[Cheat] Failed to runTransactionRunnerSafelyWithTransGuid() for TestAllCraftRecipes: {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + } + } + + private async Task<(Result, string, uint)> TestStartAndFinishCraft(Player player, CraftAction craftAction, + GameZoneAction gameZoneAction, PlayerAction playerAction, bool useBeacon) + { + // 테스트에 사용할 레시피와 앵커 찾기 + var (result, anchorGuid, recipeId) = await FindWorkableRecipeAndAnchor(gameZoneAction); + if (result.isFail()) return (result, string.Empty, 0); + + var (beaconResult, beaconGuid, beaconPos) = GetBeaconInfo(playerAction, gameZoneAction, useBeacon, anchorGuid); + if (beaconResult.isFail()) return (beaconResult, string.Empty, 0); + + // 2-1. 제작 시작 + result = await craftAction.StartCraftProcess(anchorGuid, recipeId, beaconGuid, beaconPos, 1); + if (result.isFail()) return (result, anchorGuid, recipeId); + + // 2-2. 제작 즉시 완료 치트 적용 + result = craftAction.cheatAllCraftFinish(); + if (result.isFail()) return (result, anchorGuid, recipeId); + + // 2-3. 제작 완료 처리 + result = await craftAction.FinishCraftProcess(anchorGuid); + return (result, anchorGuid, recipeId); + } + + private async Task<(Result, string, uint)> TestStartAndStopCraft(Player player, CraftAction craftAction, + GameZoneAction gameZoneAction, PlayerAction playerAction, bool useBeacon) + { + // 테스트에 사용할 레시피와 앵커 찾기 + var (result, anchorGuid, recipeId) = await FindWorkableRecipeAndAnchor(gameZoneAction); + if (result.isFail()) return (result, string.Empty, 0); + + var (beaconResult, beaconGuid, beaconPos) = GetBeaconInfo(playerAction, gameZoneAction, useBeacon, anchorGuid); + if (beaconResult.isFail()) return (beaconResult, string.Empty, 0); + + // 3-1. 제작 시작 + result = await craftAction.StartCraftProcess(anchorGuid, recipeId, beaconGuid, beaconPos, 1); + if (result.isFail()) return (result, anchorGuid, recipeId); + + // 3-2. 제작 중단 + result = await craftAction.StopCraftProcess(anchorGuid); + return (result, anchorGuid, recipeId); + } + + private (Result, string, Pos) GetBeaconInfo(PlayerAction playerAction, GameZoneAction gameZoneAction, + bool useBeacon, string anchorGuid) + { + if (!useBeacon) + { + return (new Result(), string.Empty, new Pos()); + } + + var ugcNpc = playerAction.getHadUgcNpcs().Values.FirstOrDefault(npc => + npc.getEntityAttribute()?.State == EntityStateType.None + ); + if (ugcNpc == null) + { + return (new Result().Fail(ServerErrorCode.UgcNpcNotFound, "No available beacon found for test"), + string.Empty, new Pos()); + } + + // 실제 앵커 위치를 기반으로 비컨 위치를 설정해야 클라이언트에서 정상적으로 표시됨 + var anchor = gameZoneAction.GetAnchor(anchorGuid); + var beaconPos = new Pos { X = 1, Y = 1, Z = 1, Angle = 0 }; + if (anchor != null) + { + beaconPos.X = anchor.AnchorPos.X + 1; + beaconPos.Y = anchor.AnchorPos.Y; + beaconPos.Z = anchor.AnchorPos.Z; + } + + return (new Result(), ugcNpc.getUgcNpcMetaGuid(), beaconPos); + } + + private async Task<(Result, string, uint)> FindWorkableRecipeAndAnchor(GameZoneAction gameZoneAction, + bool createAnchorsIfNotFound = true) + { + var player = gameZoneAction.getOwner() as Player; + var anchors = gameZoneAction.getLinkedToMap()?.getAnchors(); + foreach (var craftingMetaData in MetaData.Instance._CraftingMetaTable.Values) + { + if (!craftingMetaData.Enable) continue; + + if (anchors != null) + { + foreach (var anchor in anchors.Values) + { + if (MetaData.Instance._ItemTable.TryGetValue(anchor.AnchorProp.TableId, out var itemMetaData)) + { + if (itemMetaData.PropSmallType == craftingMetaData.PropSmallType) + { + // 사용 가능한 앵커와 레시피를 찾음 + return (new Result(), anchor.AnchorGuid, (uint)craftingMetaData.Id); + } + } + } + } + } + + if (createAnchorsIfNotFound) + { + // 사용 가능한 앵커가 없다면, 테스트용 앵커를 생성하고 다시 시도합니다. + Log.getLogger() + .warn( + $"[Cheat][{player?.toBasicString()}] No workable anchor found. Attempting to create test anchors."); + var createResult = await CheatCreateTestAnchors(gameZoneAction); + if (createResult.isSuccess()) + { + // 앵커 생성 후 다시 한번 탐색 (이때는 다시 생성하지 않음) + return await FindWorkableRecipeAndAnchor(gameZoneAction, false); + } + } + + var err_msg = $"[Cheat][{player?.toBasicString()}] No workable anchor and recipe found for testing."; + Log.getLogger().warn(err_msg); + return (new Result().Fail(ServerErrorCode.CraftingAnchorIsNotPlaced, err_msg), string.Empty, 0); + } + + ///

+ /// 테스트에 필요한 모든 종류의 제작 앵커를 현재 맵에 강제로 생성하고 배치합니다. + /// + private async Task CheatCreateTestAnchors(GameZoneAction gameZoneAction) + { + var player = gameZoneAction.getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => "Player is null in CheatCreateTestAnchors"); + + var map = gameZoneAction.getLinkedToMap(); + if (map == null) + { + return new Result().Fail(ServerErrorCode.EntityNotLinkedToMap, "Player is not in a map."); + } + + var anchorItemMetas = MetaData.Instance._ItemTable.Values + .Where(item => item is + { + TypeLarge: EItemLargeType.PROP, + PropSmallType: not EPropSmallType.None and not EPropSmallType.Default + }) + .ToList(); + + if (!anchorItemMetas.Any()) + { + Log.getLogger().warn("[Cheat] No craft anchor item metadata found to create."); + return new Result().Fail(ServerErrorCode.ItemMetaDataNotFound, "No craft anchor item metadata found."); + } + + Log.getLogger().info($"[Cheat] Creating {anchorItemMetas.Count} test anchors..."); + + var posX = 0; + foreach (var itemMeta in anchorItemMetas) + { + var anchorGuid = Guid.NewGuid().ToString("N"); + var prop = new AnchorProp { TableId = itemMeta.ItemId, }; + + var pos = new Pos + { + X = posX, Y = 0, Z = 0, Angle = 0, + }; + + // Map에 AnchorInfo를 직접 추가합니다. + var anchorInfo = new AnchorInfo(anchorGuid, pos, prop); + if (map.getAnchors().TryAdd(anchorGuid, anchorInfo)) + { + Log.getLogger() + .info( + $"[Cheat] Successfully created test anchor '{itemMeta.Name}' ({itemMeta.ItemId}) with GUID {anchorGuid} at X={posX}"); + posX += 2; // 다음 앵커 위치 + } + else + { + Log.getLogger().warn($"[Cheat] Failed to add test anchor for item {itemMeta.ItemId} to map."); + } + } + + // 이 치트는 DB에 저장하지 않고, 현재 세션의 Map 객체에만 앵커를 추가하여 임시로 사용합니다. + // 따라서 별도의 트랜잭션 처리는 필요 없습니다. + await Task.CompletedTask; + return new Result(); + } +} diff --git a/GameServer/Contents/Farming/Helper/FarmingHelper.cs b/GameServer/Contents/Farming/Helper/FarmingHelper.cs index 807971c..2ca05b7 100644 --- a/GameServer/Contents/Farming/Helper/FarmingHelper.cs +++ b/GameServer/Contents/Farming/Helper/FarmingHelper.cs @@ -122,7 +122,7 @@ public static class FarmingHelper found_anchor_info.PropState = farming_effect_attrib.FarmingState.toPropState(); found_anchor_info.respawnTime = farming_effect_attrib.FarmingRespawnTime.ToTimestamp(); - currMap.PropModifyNoti(found_anchor_info.AnchorGuid); + currMap.broadcast_PropModify(found_anchor_info.AnchorGuid); var player_manager = server_logic.getPlayerManager(); if (false == player_manager.tryGetUserByPrimaryKey(user_guid, out var found_user)) diff --git a/GameServer/Contents/Farming/PacketHandler/FarmingCancelPacketHandler.cs b/GameServer/Contents/Farming/PacketHandler/FarmingCancelPacketHandler.cs index 4058768..0c260cf 100644 --- a/GameServer/Contents/Farming/PacketHandler/FarmingCancelPacketHandler.cs +++ b/GameServer/Contents/Farming/PacketHandler/FarmingCancelPacketHandler.cs @@ -215,12 +215,27 @@ public class FarmingCancelPacketHandler : PacketRecvHandler return result; }; - result = await player.runTransactionRunnerSafelyWithTransGuid( anchorMetaGuid - , TransactionIdType.PrivateContents, "FarmingCancel", fn_farming_cancel); - if (result.isFail()) + var tranRunner = player.findTransactionRunner(TransactionIdType.PrivateContents); + if (null != tranRunner) { - err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; - Log.getLogger().error(err_msg); + result = await fn_farming_cancel.Invoke(); + if (result.isFail()) + { + err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return result; + } + } + else + { + result = await player.runTransactionRunnerSafelyWithTransGuid( anchorMetaGuid + , TransactionIdType.PrivateContents, "FarmingCancel", fn_farming_cancel); + if (result.isFail()) + { + err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return result; + } } await farmingCancleQuestCheck(player, anchorMetaGuid, farming_effect_for_quest_check_nullable); diff --git a/GameServer/Contents/GameMode/Action/GameGameObjectAction.cs b/GameServer/Contents/GameMode/Action/GameGameObjectAction.cs deleted file mode 100644 index f701288..0000000 --- a/GameServer/Contents/GameMode/Action/GameGameObjectAction.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Collections.Concurrent; -using GameServer.Contents.GameMode.InteractionObject; -using GameServer.Contents.GameMode.Manage.PlayManage; -using MetaAssets; -using ServerBase; -using ServerCommon; -using ServerCore; - -namespace GameServer.Contents.GameMode.Action; - -public class GameGameObjectAction : EntityActionBase, IGameObject -{ - //todo : 장르별로 구분할 필요 있으면 depth 하나 더 둔다. - - protected EGameObjectType m_object_type = EGameObjectType.None; - public GameGameObjectAction (EntityBase owner, EGameObjectType objectType) - : base(owner) - { - m_object_type = objectType; - } - - public virtual Task interact(string anchorGuid, DateTime interactionTime) - { - Log.getLogger().error($"interact Not implemant, {toBasicString()}"); - throw new NotImplementedException(); - } - - public override Task onInit() - { - Log.getLogger().error($"onInit Not implemant, {toBasicString()}"); - throw new NotImplementedException(); - } - - public override void onClear() - { - Log.getLogger().error($"onClear Not implemant, {toBasicString()}"); - throw new NotImplementedException(); - } - - public override string toBasicString() - { - return $"GameObjectType : {m_object_type}, " + base.toBasicString(); - } -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Action/GameModeBestRecordAction.cs b/GameServer/Contents/GameMode/Action/GameModeBestRecordAction.cs new file mode 100644 index 0000000..46b6203 --- /dev/null +++ b/GameServer/Contents/GameMode/Action/GameModeBestRecordAction.cs @@ -0,0 +1,182 @@ +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace GameServer; + +/// +/// 이 클래스는 플레이어의 게임 모드 최고 기록을 DynamoDB에서 읽고, 업데이트하는 역할을 담당 +/// +public class GameModeBestRecordAction : EntityActionBase +{ + + private Player? _player; + + /// + /// GameModeBestRecordAction을 생성합니다. + /// + /// 이 Action을 소유하는 Player 엔티티 + public GameModeBestRecordAction(Player owner) : base(owner) { } + + // 메모리에 캐싱된 플레이어의 최고 기록 데이터 + private GameModeBestRecordAttrib? _bestRecordAttrib; + + // 메모리에 캐싱된 플레이어의 최고 기록 DynamoDB 문서 객체 + private GameModeBestRecordDoc? _bestRecordDoc; + + private Player GetPlayer() + { + if (_player == null) + { + _player = getOwner() as Player; + } + NullReferenceCheckHelper.throwIfNull(_player, () => "Player is null"); + return _player; + } + + /// + /// Action 초기화 시 호출됩니다. + /// 플레이어의 최고 기록 문서를 DB에서 읽어와 메모리에 캐싱합니다. + /// + public override async Task onInit() + { + return await Task.FromResult(new Result()); + } + + /// + /// Action 정리 시 호출됩니다. + /// + public override void onClear() { } + + /// + /// 플레이어의 최고 기록 문서를 DynamoDB에서 읽어옵니다. + /// 문서가 존재하지 않으면 새로 생성하고 DB에 삽입한 후, 메모리에 캐싱합니다. + /// 이 메서드는 주로 플레이어 로그인 시점에 호출되어야 합니다. + /// + private async Task TryReadBestRecordDoc() + { + var userGuid = GetPlayer().getUserGuid(); + + var serverLogic = GameServerApp.getServerLogic(); + var dbClient = serverLogic.getDynamoDbClient(); + NullReferenceCheckHelper.throwIfNull(dbClient, () => "DynamoDBClient is null"); + + // PK 형식: "game_mode_best_record#" + var pk = $"{GameModeBestRecordDoc.PK}{userGuid}"; + + var queryConfig = dbClient.makeQueryConfigForReadByPKOnly(pk); + var (queryResult, foundDoc) = await dbClient.simpleQueryDocTypeWithQueryOperationConfig(queryConfig); + + GameModeBestRecordDoc? docToCache; + + // DB에 문서가 없는 경우 새로 생성 + if (queryResult.isFail() || foundDoc == null) + { + var newDoc = new GameModeBestRecordDoc(userGuid); + var newAttrib = newDoc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(newAttrib, () => "newAttrib is null"); + + newAttrib.UserGuid = userGuid; + newAttrib.Records = []; + + var insertResult = await dbClient.simpleInsertDocumentWithDocType(newDoc); + if (insertResult.isFail()) + { + var errMsg = $"Failed to simpleInsertDocumentWithDocType !!! - {GetPlayer().toBasicString()}"; + Log.getLogger().error(errMsg); + return insertResult; + } + + // 새로 생성한 문서를 캐싱 대상으로 설정 + docToCache = newDoc; + queryResult.setSuccess(); // 결과 상태를 성공으로 변경 + } + else + { + // DB에서 찾은 문서를 캐싱 대상으로 설정 + docToCache = foundDoc; + } + + var bestRecordAttrib = docToCache.getAttrib(); + NullReferenceCheckHelper.throwIfNull(bestRecordAttrib, () => "bestRecordAttrib is null"); + + // 조회 또는 생성된 문서를 메모리에 캐싱 + _bestRecordDoc = docToCache; + _bestRecordAttrib = bestRecordAttrib; + return queryResult; + } + + /// + /// 새로운 점수로 최고 기록 갱신을 시도합니다. + /// 기존 기록이 없거나 새로운 점수가 더 좋을 경우(값이 더 작을 경우)에만 기록을 갱신하고 DB에 저장합니다. + /// + /// 게임 모드 ID + /// 맵(인스턴스) 메타 ID + /// 새로운 점수 (달리기 모드에서는 완료 시간 ms) + /// 작업 결과 + public async Task<(Result result, bool isNewBest, long prevBestScore, long newBestScore)> TryUpdateBestRecord(int gameModeId, int instanceMetaId, long newScore) + { + // onInit에서 _bestRecordAttrib와 _bestRecordDoc이 캐싱되지 않았다면 로직을 수행할 수 없음 + if (_bestRecordAttrib == null || _bestRecordDoc == null) + { + var result = await TryReadBestRecordDoc(); + if (result.isFail()) + { + return (result, false, 0, 0); + } + NullReferenceCheckHelper.throwIfNull(_bestRecordDoc, () => "BestRecord has not been loaded. It must be called after login."); + NullReferenceCheckHelper.throwIfNull(_bestRecordAttrib, () => "BestRecord has not been loaded. It must be called after login"); + } + + var key = $"{gameModeId}#{instanceMetaId}"; + + // 기존 기록이 있는지 확인하고, 없거나 새 기록이 더 좋으면 갱신 + if (!_bestRecordAttrib.Records.TryGetValue(key, out var oldRecord)) + { + if (oldRecord == null) + { + oldRecord = new GameModeBestRecord(gameModeId, instanceMetaId, newScore); + _bestRecordAttrib.Records[key] = oldRecord; + } + } + + var prevBestScore = oldRecord.Score; + + if (newScore < prevBestScore) + { + // 최고 기록 갱신이 아니므로 아무것도 하지 않고 성공 반환 + return (new Result(), false, prevBestScore, newScore); + } + + // // 변경된 내용을 DB에 저장하기 위해 트랜잭션 러너를 사용 + // var transactionRunner = GetPlayer().findTransactionRunner(TransactionIdType.PrivateContents); + // if (transactionRunner is null) + // { + // var errMsg = $"TransactionRunner not found. Cannot save best record. Player: {GetPlayer().toBasicString()}"; + // Log.getLogger().error(errMsg); + // return new Result().Fail(ServerErrorCode.TransactionRunnerNotFound, errMsg); + // } + + // 트랜잭션을 안전하게 실행하여 변경된 내용을 DB에 반영 + var dbClient = GameServerApp.getServerLogic().getDynamoDbClient(); + var transactionResult = await GetPlayer().runTransactionRunnerSafely( + TransactionIdType.PrivateContents, + "tryUpdateBestRecord", async () => + { + var result = await dbClient.simpleUpdateDocumentWithDocType(_bestRecordDoc); + if (result.isFail()) + { + var errMsg = $"Failed to simpleUpdateDocumentWithDocType !!! - {GetPlayer().toBasicString()}"; + Log.getLogger().error(errMsg); + } + return result; + // var batch = new QueryBatchEx(GetPlayer(), LogActionType.GameModePersonalBestRecord, dbClient); + // // Player에 연결된 모든 'dirty' 상태의 Attribute를 DB에 쓰도록 쿼리를 추가 + // batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + // // TODO: 필요 시 비즈니스 로그 추가 + // // batch.appendBusinessLog(...); + // return QueryHelper.sendQueryAndBusinessLog(batch); + }); + return (transactionResult, true, prevBestScore, newScore); + } +} diff --git a/GameServer/Contents/GameMode/Action/GameModeObjectInteractionHandlerActionBase.cs b/GameServer/Contents/GameMode/Action/GameModeObjectInteractionHandlerActionBase.cs new file mode 100644 index 0000000..c2396ea --- /dev/null +++ b/GameServer/Contents/GameMode/Action/GameModeObjectInteractionHandlerActionBase.cs @@ -0,0 +1,189 @@ +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace GameServer.Contents.GameMode.Action; + +public abstract class GameModeObjectInteractionHandlerActionBase : EntityActionBase, IGameObjectInteractionHandler +{ + protected EGameModeObjectType m_object_type = EGameModeObjectType.None; + protected bool m_need_transaction = false; + protected bool m_need_lock = false; + + public GameModeObjectInteractionHandlerActionBase (EntityBase owner, EGameModeObjectType objectType, bool needTransaction, bool needLock) + : base(owner) + { + m_object_type = objectType; + m_need_transaction = needTransaction; + m_need_lock = needLock; + } + + public async Task interact(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + var result = new Result(); + await Task.CompletedTask; + + if (m_need_lock) + { + result = await interactWithLock(player, interactionTime, handler); + } + else + { + result = await interactWithoutLock(player, interactionTime, handler); + } + + + return result; + } + + //나중에 공통으로 올릴 로직이 있으면 올린다.. + public abstract Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler); + + private async Task interactWithLock(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + var result = new Result(); + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + using (var releaser = await game_mode_base.getAsyncLock()) + { + result = await proceedInteract(player, interactionTime, handler); + if(result.isFail()) return result; + } + + return result; + } + private async Task interactWithoutLock(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + var result = new Result(); + + result = await proceedInteract(player, interactionTime, handler); + if(result.isFail()) return result; + + + return result; + } + + private async Task proceedInteract(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + var result = await validattionCheck(player, interactionTime, handler); + if (result.isFail()) return result; + + if (m_need_transaction) + { + result = await player.runTransactionRunnerSafelyWithTransGuid( + player.getUserGuid(), + TransactionIdType.PrivateContents, + "InteractGameObject", + delegateInterationWithTransaction); + } + else + { + result = await delegateInterationWithoutTransaction(); + } + + if (result.isFail()) + { + var err_msg = $"Failed to runTransactionRunnerSafelyWithTransGuid() !!! : {result.toBasicString()}, {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return result; + } + + return result; + async Task delegateInterationWithTransaction() => await delegateInteractWithTransaction(player, handler); + async Task delegateInterationWithoutTransaction() => await delegateInteractWithoutTransaction(player, handler); + } + + public async Task delegateInteractWithTransaction(Player player, IGameObjectInteractionDataHandler handler) + { + var result = await interactDetail(player, handler); + + var server_logic = GameServerApp.getServerLogic(); + var batch = new QueryBatchEx(player, LogActionType.GameModeObjectInteraction, server_logic.getDynamoDbClient()); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + } + ILogInvokerEx business_log = getBusinessLog(handler); + batch.appendBusinessLog(business_log); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + + //보상 내용 가져올것 + var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); + NullReferenceCheckHelper.throwIfNull(found_transaction_runner, () => $"found_transaction_runner is null !!! - {player.toBasicString()}"); + var data_handler_base = handler as GameObjectInteractionDataCommonHandlerBase; + NullReferenceCheckHelper.throwIfNull(data_handler_base, () => $"data_handler_base is null !!! - {player.toBasicString()}"); + + var common_result = found_transaction_runner.getCommonResult(); + data_handler_base.m_common_result = common_result; + + return result; + } + + public async Task delegateInteractWithoutTransaction(Player player, IGameObjectInteractionDataHandler handler) + { + var result = await interactDetail(player, handler); + if (result.isFail()) return result; + + ILogInvokerEx business_log = getBusinessLog(handler); + BusinessLogger.collectLog(player, business_log); + return result; + } + + public void notifyAfterInteract(Player player, IGameObjectInteractionDataHandler handler) + { + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + var common_handler_base = (GameObjectInteractionDataCommonHandlerBase)handler; + + var anchor_guid = common_handler_base.m_interaction_anchor_guid; + if (game_mode_base.getGameModeType() == GameModeType.TPS_FFA) + { + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_INTERACTION(game_mode_base, player.getUserGuid(), anchor_guid);//todo : 코드 이관 처리 후 삭제 예정 + } + //GameNotifyHelper.broadcast_GS2C_NTF_GAME_MODE_OBJECT_INTERACTION(game_mode_base, player.getUserGuid(), anchor_guid);//todo : 코드 이관 처리후 활성화 예정 + + if (common_handler_base.m_need_noti_objects.Count > 0) + { + var game_mode_infos = new List(); + System.Collections.Generic.List infos = new(); + foreach (var game_object in common_handler_base.m_need_noti_objects) + { + BattleObjectInfo info = new(); + info.AnchorGuid = game_object.m_anchor_guid; + info.IsActive = game_object.m_is_active ? BoolType.True : BoolType.False; + infos.Add(info);//todo : 코드 이관 처리 후 삭제 예정 + + var game_mode_info = new GameModeObjectInfo(); + game_mode_info.AnchorGuid = game_object.m_anchor_guid; + game_mode_info.IsActive = game_object.m_is_active ? BoolType.True : BoolType.False; + game_mode_infos.Add(game_mode_info); + } + + if (game_mode_base.getGameModeType() == GameModeType.TPS_FFA) + { + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(game_mode_base, infos);//todo : 코드 이관 처리 후 삭제 예정 + } + GameNotifyHelper.broadcast_GS2C_NTF_GAME_MODE_OBJECT_INFO(game_mode_base, game_mode_infos); + } + + notifyDetail(player, handler); + } + + public abstract void notifyDetail(Player player, IGameObjectInteractionDataHandler handler); + + public abstract Task interactDetail(Player player, IGameObjectInteractionDataHandler handler); + public abstract ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler); + + + public override string toBasicString() + { + return $"GameObjectType : {m_object_type}, " + base.toBasicString(); + } +} diff --git a/GameServer/Contents/GameMode/Action/GameModePlayRegulationAction.cs b/GameServer/Contents/GameMode/Action/GameModePlayRegulationAction.cs new file mode 100644 index 0000000..642d846 --- /dev/null +++ b/GameServer/Contents/GameMode/Action/GameModePlayRegulationAction.cs @@ -0,0 +1,583 @@ +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using Google.Protobuf.WellKnownTypes; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer.Contents.GameMode; + +public class GameModePlayRegulationAction : EntityActionBase +{ + private bool m_is_checked = false; + + public GameModePlayRegulationAction(EntityBase owner) : base(owner) + { + } + + public override Task onInit() + { + var result = new Result(); + return Task.FromResult(result); + } + + public override void onClear() + { + } + + public async Task getGameModeMatchRestriction(GameModeType gameModeType) + { + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, + () => $"GameModePlayRegulationAttribute is null !!! - {player.toBasicString()}"); + + if (false == attribute.m_match_restriction.TryGetValue(gameModeType, out var restriction)) + { + restriction = new ServerCommon.MatchRestriction(); + attribute.m_match_restriction.TryAdd(gameModeType, restriction); + } + + await Task.CompletedTask; + return restriction; + } + + public async Task getGameModePlayPenalty(GameModeType gameModeType) + { + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, + () => $"GameModePlayRegulationAttribute is null !!! - {player.toBasicString()}"); + + if (false == attribute.m_play_penalty.TryGetValue(gameModeType, out var penalty)) + { + penalty = new ServerCommon.GameModePlayPenalty(); + attribute.m_play_penalty.TryAdd(gameModeType, penalty); + } + + await Task.CompletedTask; + return penalty; + } + + + public async Task refresh() + { + var result = new Result(); + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, + () => $"GameModePlayRegulationAttribute is null !!! - {player.toBasicString()}"); + + var need_update = false; + var now = DateTimeHelper.Current; + foreach (var restriction in attribute.m_match_restriction) + { + if (restriction.Value.m_next_refresh_time < now) + { + need_update = true; + break; + } + } + + foreach (var penalty in attribute.m_play_penalty) + { + if (penalty.Value.m_penalty_end_time < now && penalty.Value.m_is_max) + { + need_update = true; + break; + } + } + + if (need_update) + { + result = await updateGameModePlayRegulation(); + if (result.isFail()) + { + string err_msg = + $"Failed to updateGameModePlayRegulation()!!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + } + } + } + + public async Task updateGameModePlayRegulation() + { + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, + () => $"GameModePlayRegulationAttribute is null !!! - {player.toBasicString()}"); + + var result = new Result(); + var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); + if (found_transaction_runner is null) + { + result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, + "udpateGameModePlayerRegulation", delegateUpdateRegulationWithTransaction); + } + else + { + result = await delegateUpdateRegulation(); + } + + if (result.isFail()) + { + string err_msg = + $"Failed to runTransactionRunnerSafely()!!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return result; + } + + + //GameNotifyHelper.send_S2C_NTF_GAME_MODE_PLAY_REGULATION(player); + + return result; + + async Task delegateUpdateRegulationWithTransaction() => await updateRegulationWithTransaction(); + async Task delegateUpdateRegulation() => await delegateUdpateRegulation(); + } + + + private async Task updateRegulationWithTransaction() + { + var result = new Result(); + await Task.CompletedTask; + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + + result = await updateRegulationAttribute(); + if (result.isFail()) return result; + + var server_logic = GameServerApp.getServerLogic(); + var batch = new QueryBatchEx(player, LogActionType.UpdateGameModePlayerRegulation, + server_logic.getDynamoDbClient()); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + } + + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"player is null !!! -"); + var log_info = makeLog(attribute); + + var log_invoker = new GameModePlayerRegulationBusinessLog(log_info); + batch.appendBusinessLog(log_invoker); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) return result; + + return result; + } + + private GameModePlayerRegulationLogInfo makeLog(GameModePlayRegulationAttribute attribute) + { + var log_info = new GameModePlayerRegulationLogInfo(); + + log_info.m_match_restriction = attribute.m_match_restriction.ToDictionary( + x => x.Key, + x => x.Value.clone() + ); + ; + log_info.m_play_penalty = attribute.m_play_penalty.ToDictionary( + x => x.Key, + x => x.Value.clone() + ); + + return log_info; + } + + private async Task delegateUdpateRegulation() + { + var result = new Result(); + + result = await updateRegulationAttribute(); + if (result.isFail()) return result; + return result; + } + + private Task updateRegulationAttribute() + { + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, + () => $"GameModePlayRegulationAttribute is null !!! - {player.toBasicString()}"); + + var result = new Result(); + + var now = DateTimeHelper.Current; + + foreach (var restriction in attribute.m_match_restriction) + { + if (restriction.Value.m_next_refresh_time < now) + { + restriction.Value.m_current_matched_count = 0; + restriction.Value.m_next_refresh_time = now.Date.AddDays(1); + } + } + + foreach (var penalty in attribute.m_play_penalty) + { + if (penalty.Value.m_penalty_end_time < now && penalty.Value.m_is_max) + { + penalty.Value.m_penalty_count = 0; + penalty.Value.m_is_max = false; + } + } + + attribute.modifiedEntityAttribute(true); + return Task.FromResult(result); + } + + public Result setFromDoc(GameModePlayRegulationDoc doc) + { + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, + () => $"GameModePlayRegulationAttribute is null !!! - {player.toBasicString()}"); + + var result = new Result(); + + attribute.addDefaultGameModePlayRegulation(); + if (false == doc.getAttribWrappers() + .TryGetValue(typeof(GameModePlayRegulationAttrib), out var to_copy_doc_attrib)) + { + var err_msg = $"Fail to get UgqDailyRewardCountAttrib"; + result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg); + return result; + } + + var attrib_base = to_copy_doc_attrib.getAttribBase(); + var doc_attrib = attrib_base as GameModePlayRegulationAttrib; + if (doc_attrib is null) + { + var err_msg = $"Fail to get UgqDailyRewardCountAttrib"; + result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg); + return result; + } + + foreach (var match in doc_attrib.m_match_restriction) + { + attribute.m_match_restriction[match.Key] = match.Value; + } + + foreach (var penalty in doc_attrib.m_play_penalty) + { + attribute.m_play_penalty[penalty.Key] = penalty.Value; + } + + return result; + } + + public async Task addMatchCount(EntityType entityType) + { + var game_mode_type = GameModeHelper.convertEntityTypeToGameModeType(entityType); + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + + var server_logic = GameServerApp.getServerLogic(); + + var fn_add_match_count = async delegate() + { + var result = new Result(); + + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, + () => $"GameModePlayRegulationAttribute is null !!! - {player.toBasicString()}"); + + if (false == attribute.m_match_restriction.TryGetValue(game_mode_type, out var restriction)) + { + restriction = new ServerCommon.MatchRestriction() + { + m_current_matched_count = 0, m_next_refresh_time = DateTimeHelper.Current.Date.AddDays(1) + }; + attribute.m_match_restriction.TryAdd(game_mode_type, restriction); + } + + restriction.m_current_matched_count++; + attribute.m_match_restriction.TryAdd(game_mode_type, restriction); + attribute.modifiedEntityAttribute(true); + + var batch = new QueryBatchEx(player, LogActionType.GameModeAddMatchCount + , server_logic.getDynamoDbClient(), true); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + } + batch.appendBusinessLogs(makeLogInfo(attribute)); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) return result; + + return result; + }; + + var tran_result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, + "udpateGameModePlayerRegulation", fn_add_match_count); + if (tran_result.isFail()) + { + return tran_result; + } + + return tran_result; + } + + private List makeLogInfo(GameModePlayRegulationAttribute attribute) + { + var invokers = new List(); + + var log_info = new GameModeMatchRegulationLogInfo(); + log_info.m_match_restriction = attribute.m_match_restriction.ToDictionary( + x => x.Key, + x => x.Value.clone() + ); + ; + log_info.m_play_penalty = attribute.m_play_penalty.ToDictionary( + x => x.Key, + x => x.Value.clone() + ); + + invokers.Add(new GameModeMatchRegulationBusinessLog(log_info)); + + return invokers; + } + + public async Task playPenaltyCheck(GameModeBase gameModeBase, bool needDBWrite = false) + { + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + + var result = new Result(); + switch (gameModeBase.getGameModeType()) + { + case GameModeType.RUN_RACE: + case GameModeType.RUN_ADV: + case GameModeType.RUN_PRACTICE: + case GameModeType.RUN_HONOR: + result = await runRacePenaltyCheck(gameModeBase, needDBWrite); + break; + default: + //패널티 체크는 현재 race밖에 없음 추가 발생하는 모드 있으면 여기에 넣을것 + break; + } + + return result; + } + + private async Task runRacePenaltyCheck(GameModeBase gameModeBase, bool neewWrite) + { + var result = new Result(); + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + + var state = gameModeBase.getState(); + + var run_race = gameModeBase as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_race is null !!! -"); + // 페널티 횟수가 설정되어 있지 않으면 페널치를 부여하지 않는다. + if (run_race.m_run_race_meta.m_penalty_quit_count == 0) { return result; } + + switch (state.getStateType()) + { + case GameModeState.Wait: + case GameModeState.Ready: + case GameModeState.Play: + result = await increasePenalty(gameModeBase, run_race.m_run_race_meta.m_penalty_quit_count, + run_race.m_run_race_meta.m_penalty_time, neewWrite); + return result; + default: + return result; + } + } + + private async Task increasePenalty(GameModeBase gameModeBase, int penaltyQuitCount, int penaltyTime, + bool neewWrite) + { + var result = new Result(); + + if (m_is_checked) return result; + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, + () => $"GameModePlayRegulationAttribute is null !!! - {player.toBasicString()}"); + + var game_mode_type = gameModeBase.getGameModeType(); + if (false == attribute.m_play_penalty.TryGetValue(game_mode_type, out var penalty)) + { + penalty = new ServerCommon.GameModePlayPenalty(); + } + + penalty.m_penalty_count++; + if (penaltyQuitCount <= penalty.m_penalty_count) + { + penalty.m_penalty_start_time = DateTimeHelper.Current; + penalty.m_penalty_end_time = DateTimeHelper.Current.AddSeconds(penaltyTime); + penalty.m_is_max = true; + } + + attribute.m_play_penalty[game_mode_type] = penalty; + m_is_checked = true; + attribute.modifiedEntityAttribute(); + + var write_result = new Result(); + if (neewWrite) + { + (var doc_result, var user_play_regulation_doc) = await attribute.toDocBase(); + if (doc_result.isSuccess()) + { + var server_logic = GameServerApp.getServerLogic(); + var db_client = server_logic.getDynamoDbClient(); + NullReferenceCheckHelper.throwIfNull(user_play_regulation_doc, () => $"user_base_doc is null !!! -"); + write_result = await user_play_regulation_doc.updateDoc4Query(); + if (write_result.isFail()) + { + var err_msg = + $"Failed to increasePenalty updateDoc4Query() !!! {write_result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return write_result; + } + + write_result = await db_client.simpleUpdateDocumentWithDocType(user_play_regulation_doc); + if (write_result.isFail()) + { + var err_msg = + $"Failed to increasePenalty simpleUpdateDocumentWithDocType() !!! {write_result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return write_result; + } + } + } + + var invokers = new List(); + var log_info = new GameModePenaltyLogInfo(gameModeBase.getGameModeId(), game_mode_type, + gameModeBase.getRoomId(), attribute.m_match_restriction, attribute.m_play_penalty); + var business_log = new GameModePenaltyBusinessLog(log_info); + invokers.Add(business_log); + var log_action = new LogActionEx(LogActionType.GameModePenalty); + BusinessLogger.collectLogs(log_action, player, invokers); + + return result; + } + + /// + /// 페널티 카운드와 적용 시간을 리셋한다. + /// + /// + /// + public async Task resetPlayPenalty(GameModeType gameModeType) + { + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + + var server_logic = GameServerApp.getServerLogic(); + + var fn_reset_play_penalty = async delegate() + { + var result = new Result(); + + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, + () => $"GameModePlayRegulationAttribute is null !!! - {player.toBasicString()}"); + + if (false == attribute.m_play_penalty.TryGetValue(gameModeType, out var penalty)) + { + penalty = new ServerCommon.GameModePlayPenalty(); + } + + penalty.m_penalty_count = 0; + penalty.m_is_max = false; + penalty.m_penalty_end_time = penalty.m_penalty_end_time <= DateTime.UtcNow + ? penalty.m_penalty_end_time + : DateTimeHelper.Current.Subtract(TimeSpan.FromSeconds(1)); + attribute.m_play_penalty[gameModeType] = penalty; + attribute.modifiedEntityAttribute(); + + var batch = new QueryBatchEx(player, LogActionType.GameModeAddMatchCount + , server_logic.getDynamoDbClient(), true); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + } + batch.appendBusinessLogs(makeLogInfo(attribute)); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) return result; + + return result; + }; + + var tran_result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, + "resetGameModePlayerRegulation.PlayPenalty", fn_reset_play_penalty); + if (tran_result.isFail()) + { + return tran_result; + } + + return tran_result; + } + + /// + /// 매치 카운트를 리셋한다. + /// + /// + /// + public async Task resetMatchCount(GameModeType gameModeType) + { + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! -"); + + var server_logic = GameServerApp.getServerLogic(); + + var fn_reset_match_count = async delegate() + { + var result = new Result(); + + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, + () => $"GameModePlayRegulationAttribute is null !!! - {player.toBasicString()}"); + + if (false == attribute.m_match_restriction.TryGetValue(gameModeType, out var restriction)) + { + restriction = new ServerCommon.MatchRestriction() + { + m_current_matched_count = 0, m_next_refresh_time = DateTimeHelper.Current.Date.AddDays(1) + }; + attribute.m_match_restriction.TryAdd(gameModeType, restriction); + } + + restriction.m_current_matched_count = 0; + attribute.m_match_restriction[gameModeType] = restriction; + attribute.modifiedEntityAttribute(true); + + var batch = new QueryBatchEx(player, LogActionType.GameModeAddMatchCount + , server_logic.getDynamoDbClient(), true); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + } + batch.appendBusinessLogs(makeLogInfo(attribute)); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) return result; + + return result; + }; + + var tran_result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, + "resetGameModePlayerRegulation.MatchCount", fn_reset_match_count); + if (tran_result.isFail()) + { + return tran_result; + } + + return tran_result; + } +} diff --git a/GameServer/Contents/GameMode/Action/GameModePositionSetAction.cs b/GameServer/Contents/GameMode/Action/GameModePositionSetAction.cs new file mode 100644 index 0000000..9f2d851 --- /dev/null +++ b/GameServer/Contents/GameMode/Action/GameModePositionSetAction.cs @@ -0,0 +1,53 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Action; + +public class GameModePositionSetAction : EntityActionBase +{ + public GameModePositionSetAction(EntityBase owner) + : base(owner) + { + } + public override void onClear() + { + return; + } + + public override async Task onInit() + { + await Task.CompletedTask; + + var result = new Result(); + + return result; + } + + public (Result, Pos) changeUserAnchorPos(string anchorGuid, Player player) + { + var tps_ffa = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + + var err_msg = string.Empty; + + + var location_action = player.getEntityAction(); + var currenct_pos = location_action.getCurrentPos(); + + var result = new Result(); + if (false == tps_ffa.getInstanceRoom().getMap().getAnchors().TryGetValue(anchorGuid, out var anchorInfo)) + { + err_msg = $"anchorInfo not exist idx : {anchorGuid}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_msg);; + return (result, currenct_pos); + } + + currenct_pos = anchorInfo.AnchorPos.Clone(); + currenct_pos.Z += 100; + location_action.tryUpdateCurrentPos(currenct_pos); + + return (result, currenct_pos); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Action/GameModePostUpdateAction.cs b/GameServer/Contents/GameMode/Action/GameModePostUpdateAction.cs new file mode 100644 index 0000000..a1d6a50 --- /dev/null +++ b/GameServer/Contents/GameMode/Action/GameModePostUpdateAction.cs @@ -0,0 +1,42 @@ +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Action; + +public class GameModePostUpdateAction : EntityActionBase +{ + //private DateTime m_next_logging_time = DateTimeHelper.Current; + public GameModePostUpdateAction(EntityBase owner) : base(owner) + { + } + + public override Task onInit() + { + var result = new Result(); + return Task.FromResult(result); + } + public override void onClear(){} + + public async Task update() + { + var result = new Result(); + await Task.CompletedTask; + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + (result, var is_migrated) = game_mode_base.m_host_migrator.migrateCheck(); + if (result.isFail()) return result; + + if (is_migrated) + { + string host_user_guid = game_mode_base.m_host_migrator.getHostUserGuid(); + Log.getLogger().info($"GameMode Host set complete!!! gameMode : {game_mode_base.getEntityType()}, " + + $"room_id : {game_mode_base.getInstanceRoom().getMap().m_room_id} host_user_guid : {host_user_guid}"); + GameNotifyHelper.broadcast_GS2C_NTF_P2P_HOST_UPDATE(game_mode_base.getInstanceRoom(), host_user_guid); + } + return result; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Action/GameModePreUpdateAction.cs b/GameServer/Contents/GameMode/Action/GameModePreUpdateAction.cs new file mode 100644 index 0000000..c8ef6f3 --- /dev/null +++ b/GameServer/Contents/GameMode/Action/GameModePreUpdateAction.cs @@ -0,0 +1,27 @@ +using ServerBase; + +namespace GameServer.Contents.GameMode.Action; + +public class GameModePreUpdateAction : EntityActionBase +{ + public GameModePreUpdateAction(EntityBase owner) : base(owner) + { + } + + public override Task onInit() + { + var result = new Result(); + return Task.FromResult(result); + } + public override void onClear(){} + + public async Task update() + { + var result = new Result(); + await Task.CompletedTask; + return result; + } + + + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Action/GameModeStateCheckAction.cs b/GameServer/Contents/GameMode/Action/GameModeStateCheckAction.cs new file mode 100644 index 0000000..6a0725a --- /dev/null +++ b/GameServer/Contents/GameMode/Action/GameModeStateCheckAction.cs @@ -0,0 +1,44 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Action; + +public class GameModeStateCheckAction : EntityActionBase +{ + public GameModeStateCheckAction(EntityBase owner) + : base(owner) + { + } + public override void onClear() + { + return; + } + + public override async Task onInit() + { + await Task.CompletedTask; + + var result = new Result(); + + return result; + } + + public async Task stateUpdate() + { + var run_race = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"GameModeRunRace is null !!"); + + var game_mode = getOwner() as IGameMode; + NullReferenceCheckHelper.throwIfNull(game_mode, () => $"game_mode is null !!"); + var current_state = run_race.getState(); + + await current_state.update(game_mode); + + var result = new Result(); + + await Task.CompletedTask; + return result; + } + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Cheat/CheatGameModeBestRecordTest.cs b/GameServer/Contents/GameMode/Cheat/CheatGameModeBestRecordTest.cs new file mode 100644 index 0000000..990ac45 --- /dev/null +++ b/GameServer/Contents/GameMode/Cheat/CheatGameModeBestRecordTest.cs @@ -0,0 +1,96 @@ +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using System; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Cheat; + +/// +/// 개인 최고 기록 저장을 테스트하기 위한 치트 명령어입니다. +/// +/// 사용법: ReqChat best_record_test [게임모드ID] [맵ID] [기록(초)] +/// +/// +/// - 게임모드ID: 필수 항목입니다. 기록을 저장할 게임 모드의 ID입니다. +/// - 맵ID: 선택 항목입니다. 생략 시 기본값(2001)이 사용됩니다. +/// - 기록(초): 선택 항목입니다. 생략 시 60~300초 사이의 랜덤 값이 사용됩니다. +/// +/// +/// 1. 모든 인자 사용: /best_record_test 1001 2001 120 +///
- 게임모드 1001, 맵 2001의 기록을 120초로 저장합니다. +///
+/// +/// 2. 맵ID, 기록 생략: /best_record_test 1001 +///
- 게임모드 1001, 맵 2001(기본값)의 기록을 랜덤 값으로 저장합니다. +///
+/// +/// 3. 기록만 생략: /best_record_test 1001 2002 +///
- 게임모드 1001, 맵 2002의 기록을 랜덤 값으로 저장합니다. +///
+///
+[ChatCommandAttribute("best_record_test", typeof(CheatGameModeBestRecordTest), + AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +public class CheatGameModeBestRecordTest : ChatCommandBase +{ + public override async Task invoke(Player player, string token, string[] args) + { + try + { + if (args.Length == 0) + { + var usageMsg = $"[사용법] /{token} [게임모드ID] [맵ID] [기록(초)]"; + Log.getLogger().info($"[Cheat] {player.toBasicString()}: {usageMsg}"); + + var exampleMsg = $"[예시] /{token} 1001 2001 120"; + Log.getLogger().info($"[Cheat] {player.toBasicString()}: {exampleMsg}"); + + var noteMsg = $"[참고] 맵ID와 기록(초)는 생략 가능합니다. (기록은 랜덤값으로 설정됩니다)"; + Log.getLogger().info($"[Cheat] {player.toBasicString()}: {noteMsg}"); + return; + } + + if (!int.TryParse(args[0], out var gameModeId)) + { + var errorMsg = $"게임모드 ID가 올바르지 않습니다: {args[0]}"; + Log.getLogger().warn($"[Cheat] {player.toBasicString()}: {errorMsg}"); + return; + } + + // 맵 ID가 없으면 기본값(2001) 사용 + var instanceMetaId = Random.Shared.Next(100001, 100099); + if (args.Length > 1 && !int.TryParse(args[1], out instanceMetaId)) + { + var errorMsg = $"맵 ID가 올바르지 않습니다: {args[1]}"; + Log.getLogger().warn($"[Cheat] {player.toBasicString()}: {errorMsg}"); + return; + } + + // 기록(초)이 없으면 60~300초 사이의 랜덤값 사용 + var recordTimeSec = new Random().Next(60, 301); + if (args.Length > 2 && !int.TryParse(args[2], out recordTimeSec)) + { + var errorMsg = $"기록(초)이 올바르지 않습니다: {args[2]}"; + Log.getLogger().warn($"[Cheat] {player.toBasicString()}: {errorMsg}"); + return; + } + + var recordTimeMs = recordTimeSec * 1000L; + + var action = player.getEntityAction(); + var result = await action.TryUpdateBestRecord(gameModeId, instanceMetaId, recordTimeMs); + + var resultMsg = result.result.isSuccess() + ? $"최고 기록 저장 성공: GameModeId={gameModeId}, MapId={instanceMetaId}, Record={recordTimeSec}s ({recordTimeMs}ms)" + : $"최고 기록 저장 실패: {result.result.toBasicString()}"; + + Log.getLogger().info($"[Cheat] {player.toBasicString()}: {resultMsg}"); + } + catch (Exception ex) + { + Log.getLogger() + .error( + $"[Cheat] CheatGameModeBestRecordSave failed. player:{player.toBasicString()}, args:{string.Join(" ", args)}, ex:{ex}"); + } + } +} diff --git a/GameServer/Contents/GameMode/Cheat/CheatGameModeHostNoti.cs b/GameServer/Contents/GameMode/Cheat/CheatGameModeHostNoti.cs new file mode 100644 index 0000000..87cae6b --- /dev/null +++ b/GameServer/Contents/GameMode/Cheat/CheatGameModeHostNoti.cs @@ -0,0 +1,34 @@ +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Cheat; + +[ChatCommandAttribute("gamemodehostnoti", typeof(CheatGameModeHostNoti), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +public class CheatGameModeHostNoti : ChatCommandBase +{ + public override async Task invoke(Player player, string token, string[] args) + { + var room_id = player.getCurrentInstanceRoomId(); + if (room_id.Equals(string.Empty)) + { + Log.getLogger().error($"cheat:gamemodehostnoti.... this cheat only use in instance"); + return; + } + + if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + { + Log.getLogger().error($"cheat:gamemodehostnoti.... room is null room_id : {room_id}"); + return; + } + + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var host_user_guid = game_mode_base.m_host_migrator.getHostUserGuid(); + GameNotifyHelper.broadcast_GS2C_NTF_P2P_HOST_UPDATE(game_mode_base.getInstanceRoom(), host_user_guid); + + await Task.CompletedTask; + + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Cheat/CheatGameModeUserKick.cs b/GameServer/Contents/GameMode/Cheat/CheatGameModeUserKick.cs new file mode 100644 index 0000000..f9f5d58 --- /dev/null +++ b/GameServer/Contents/GameMode/Cheat/CheatGameModeUserKick.cs @@ -0,0 +1,24 @@ +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Cheat; + +[ChatCommandAttribute("gameuserkick", typeof(CheatGameModeUserKick), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +public class CheatGameModeUserKick: ChatCommandBase +{ + public override async Task invoke(Player player, string token, string[] args) + { + var room_id = player.getCurrentInstanceRoomId(); + if (room_id.Equals(string.Empty)) + { + Log.getLogger().error($"cheat:gameuserkick.... this cheat only use in instance"); + return; + } + + GameNotifyHelper.send_GS2C_NTF_GAME_USER_KICK(player, GameModeKickReason.LoadingTimeExpired); + + await Task.CompletedTask; + + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/DbQuery/DBQGameModePlayRegulationReadAll.cs b/GameServer/Contents/GameMode/DbQuery/DBQGameModePlayRegulationReadAll.cs new file mode 100644 index 0000000..8cf4008 --- /dev/null +++ b/GameServer/Contents/GameMode/DbQuery/DBQGameModePlayRegulationReadAll.cs @@ -0,0 +1,109 @@ +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace GameServer.Contents.GameMode.DbQuery; + +public class DBQGameModePlayRegulationReadAll : QueryExecutorBase +{ + private string m_combination_key_for_pk = string.Empty; + private string m_pk = string.Empty; + + + private readonly List m_play_regulation_docs = new(); + + public DBQGameModePlayRegulationReadAll(string combinationKeyForPK) : base(typeof(DBQGameModePlayRegulationReadAll).Name) + { + m_combination_key_for_pk = combinationKeyForPK; + } + + + //===================================================================================== + // DB 쿼리 직전에 준비해야 할 로직들을 작성한다. + //===================================================================================== + public override Task onPrepareQuery() + { + var result = new Result(); + var err_msg = string.Empty; + + var owner = getOwner(); + NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); + + var regulation_doc = new GameModePlayRegulationDoc(); + regulation_doc.setCombinationKeyForPK(m_combination_key_for_pk); + + var error_code = regulation_doc.onApplyPKSK(); + if (error_code.isFail()) + { + err_msg = $"Failed to onApplyPKSK() !!! : {error_code.toBasicString()} - {toBasicString()}, {owner.toBasicString()}"; + result.setFail(error_code, err_msg); + Log.getLogger().error(result.toBasicString()); + return Task.FromResult(result); + } + + m_pk = regulation_doc.getPK(); + + return Task.FromResult(result); + } + + //===================================================================================== + // onPrepareQuery()를 성공할 경우 호출된다. + //===================================================================================== + public override async Task onQuery() + { + var result = new Result(); + var err_msg = string.Empty; + + var query_batch = getQueryBatch(); + NullReferenceCheckHelper.throwIfNull(query_batch, () => "query_batch is null !!!"); + + var db_connector = query_batch.getDynamoDbConnector(); + + var owner = getOwner(); + NullReferenceCheckHelper.throwIfNull(owner, () => "owner is null !!!"); + + var regulation_attribute = owner.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(regulation_attribute, () => "regulation_attribute is null !!!"); + + var query_config = db_connector.makeQueryConfigForReadByPKSK(m_pk); + (result, var read_docs) = await db_connector.simpleQueryDocTypesWithQueryOperationConfig(query_config, eventTid: query_batch.getTransId()); + if (result.isFail()) + { + //데이터 없으면 여기서 그냥 메모리에만 넣어준다. + Log.getLogger().warn($"GameModePlayRegulationDoc not exitst, so make new Data {result.toBasicString()}"); + + return new(); + } + + m_play_regulation_docs.AddRange(read_docs); + + var regulation_action = owner.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(regulation_action, () => "regulation_action is null !!!"); + + foreach (var read_doc in read_docs) + { + result = regulation_action.setFromDoc(read_doc); + if (result.isFail()) + { + Log.getLogger().error(result.toBasicString()); + continue; + } + } + + return result; + } + + public override Task onQueryResponseCommit() + { + return Task.CompletedTask; + } + + public override Task onQueryResponseRollback(Result errorResult) + { + return Task.CompletedTask; + + } + + private new Player? getOwner() => getQueryBatch()?.getLogActor() as Player; +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Helper/GameModeHelper.cs b/GameServer/Contents/GameMode/Helper/GameModeHelper.cs index 7d90a86..af9daa9 100644 --- a/GameServer/Contents/GameMode/Helper/GameModeHelper.cs +++ b/GameServer/Contents/GameMode/Helper/GameModeHelper.cs @@ -1,22 +1,19 @@ -using GameServer.Contents.GameMode.Manage; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage; using GameServer.Contents.GameMode.Manage.LeaveManage; using GameServer.Contents.GameMode.Manage.PlayManage; using GameServer.Contents.GameMode.Mode_Battle.Manage; using GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.Manage; using GameServer.Contents.GameMode.Mode_Running.Manage; +using GameServer.Contents.GameMode.Mode_Running.ModePractice.Helper; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Helper; using GameServer.Contents.GameMode.Mode_Running.ModeRace.Manage; -using Google.Protobuf; -using Google.Protobuf.WellKnownTypes; - - +using GameServer.Matching; using ServerCore; using ServerBase; using ServerCommon; -using ServerCommon.BusinessLogDomain; using MetaAssets; - - namespace GameServer; @@ -24,7 +21,7 @@ public class GameModeHelper { public static (Result, IGameModeJoinHandler) getGameModeJoinHandler(InstanceRoom instanceRoom, int gameModeId, string roomId) { - (var genre_type, var mode_type) = getGameModeMeta(gameModeId, roomId); + var mode_type = getGameModeType(gameModeId); var result = new Result(); switch (mode_type) { @@ -35,7 +32,10 @@ public class GameModeHelper case GameModeType.RUN_ADV: return (result, new RunAdventureJoinHandler(instanceRoom)); case GameModeType.RUN_RACE: + case GameModeType.RUN_HONOR: return (result, new RunRaceJoinHandler(instanceRoom)); + case GameModeType.RUN_PRACTICE: + return (result, new RunPracticeJoinHandler(instanceRoom)); //case GameModeType.DANCE_: // return (result, new (instanceRoom)); default: @@ -44,10 +44,10 @@ public class GameModeHelper return (result, new TPSFreeForAllJoinHandler(instanceRoom)); } } - + public static (Result, IGameModeInitHandler) getGameModeInitHandler(InstanceRoom instanceRoom, int gameModeId, string roomId) { - (var genre_type, var mode_type) = getGameModeMeta(gameModeId, roomId); + var mode_type = getGameModeType(gameModeId); var result = new Result(); switch (mode_type) { @@ -58,9 +58,12 @@ public class GameModeHelper case GameModeType.RUN_ADV: return (result, new RunAdventureInitHandler(instanceRoom)); case GameModeType.RUN_RACE: - return (result, new RunRaceInitHandler(instanceRoom)); + case GameModeType.RUN_HONOR: + return (result, new RunRaceInitHandler(instanceRoom, mode_type)); + case GameModeType.RUN_PRACTICE: + return (result, new RunPracticeInitHandler(instanceRoom)); default: - var err_msg = $"getGameModeInitHandler not exist!!!! mode_type : {mode_type}"; + var err_msg = $"getGameModeInitHandler not exist!!!! mode_type : {mode_type}, roomId : {roomId}"; result.setFail(ServerErrorCode.GameModeInitHandlerNotExist, err_msg); return (result, new TPSFreeForAllInitHandler(instanceRoom)); } @@ -69,7 +72,7 @@ public class GameModeHelper public static (Result, IGameModeJoinSuccessHandler?) getGameModeJoinSuccessHandler(Player player, InstanceRoom instanceRoom, int gameModeId, string roomId) { var result = new Result(); - (var genre_type, var mode_type) = getGameModeMeta(gameModeId, roomId); + var mode_type = getGameModeType(gameModeId); switch (mode_type) { case GameModeType.TPS_FFA: @@ -79,27 +82,35 @@ public class GameModeHelper case GameModeType.RUN_ADV: return (result, new RunAdventureJoinSuccessHandler(player, instanceRoom)); case GameModeType.RUN_RACE: + case GameModeType.RUN_HONOR: return (result, new RunRaceJoinSuccessHandler(player, instanceRoom)); + case GameModeType.RUN_PRACTICE: + return (result, new RunPracticeJoinSuccessHandler(player, instanceRoom)); default: - var err_msg = $"getGameModeInitHandler not exist !!!! mode_type : {mode_type}"; + var err_msg = $"getGameModeInitHandler not exist !!!! mode_type : {mode_type}, roomId : {roomId}"; result.setFail(ServerErrorCode.GameModeJoinSuccessHandlerNotExist, err_msg); return (result, null); } } - public static (Result, IGameMode?) createGameMode(InstanceRoom instanceRoom, GameModeType modeType) + public static (Result, IGameMode?) createGameMode(InstanceRoom instanceRoom, GameModeType modeType, int gameModeId) { var result = new Result(); switch (modeType) { case GameModeType.TPS_FFA: - return (result, new GameModeTPSFreeForAll(new GameModeTPSFreeForAllData(), instanceRoom)); - case GameModeType.TPS_TDM: - return (result, new GameModeTPSTeamDeathMatch(new GameModeTPSTeamDeathMatchData(), instanceRoom)); + return (result, new GameModeTPSFreeForAll(instanceRoom, gameModeId)); + + //return (result, new GameModeTPSTeamDeathMatch(instanceRoom, gameModeId)); case GameModeType.RUN_ADV: - return (result, new GameModeRunAdventure(new GameModeRunAdventureData(), instanceRoom)); + return (result, new GameModeRunAdventure(instanceRoom, gameModeId)); case GameModeType.RUN_RACE: - return (result, new GameModeRunRace(new GameModeRunRaceData(), instanceRoom)); + return (result, new GameModeRunRace(instanceRoom, gameModeId, EntityType.GameModeRunRace)); + case GameModeType.RUN_HONOR: + return (result, new GameModeRunRace(instanceRoom, gameModeId, EntityType.GameModeRunHonor)); + case GameModeType.RUN_PRACTICE: + return (result, new GameModeRunPractice(instanceRoom, gameModeId)); + case GameModeType.TPS_TDM: default: var err_msg = $"createGameMode not exist !!!! mode_type : {modeType}"; result.setFail(ServerErrorCode.GameModeCreateFail, err_msg); @@ -107,41 +118,50 @@ public class GameModeHelper } } - public static (Result, IGameModeLeaveHandler?) getGameModeLeaveHandler(Player player, string roomId) + public static (Result, IGameModeLeaveHandler?) getGameModeLeaveHandler(IGameMode gameMode, Player player, ServerInfo serverInfo, List invokers, string roomId) { var result = new Result(); - (var genre_type, var mode_type) = getGameModeMeta(1, roomId); + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var mode_type = getGameModeType(game_mode_base.getGameModeId()); switch (mode_type) { case GameModeType.TPS_FFA: - return (result, new TPSFreeForAllLeaveHandler(player, roomId)); + return (result, new TPSFreeForAllLeaveHandler(gameMode, player, serverInfo, invokers, roomId)); case GameModeType.TPS_TDM: - return (result, new TPSTeamDeathMatchLeaveHandler(player, roomId)); + return (result, new TPSTeamDeathMatchLeaveHandler(gameMode, player, serverInfo, invokers, roomId)); case GameModeType.RUN_ADV: - return (result, new RunAdventureLeaveHandler(player, roomId)); + return (result, new RunAdventureLeaveHandler(gameMode, player, serverInfo, invokers, roomId)); case GameModeType.RUN_RACE: - return (result, new RunRaceLeaveHandler(player, roomId)); + case GameModeType.RUN_HONOR: + return (result, new RunRaceLeaveHandler(gameMode, player, serverInfo, invokers, roomId, mode_type)); + case GameModeType.RUN_PRACTICE: + return (result, new RunPracticeLeaveHandler(gameMode, player, serverInfo, invokers, roomId)); default: - var err_msg = $"getGameModeInitHandler not exist !!!! mode_type : {mode_type}"; + var err_msg = $"getGameModeInitHandler not exist !!!! mode_type : {mode_type}, roomId : {roomId}"; result.setFail(ServerErrorCode.GameModeJoinSuccessHandlerNotExist, err_msg); return (result, null); } } - - public static (Result, IGameModeDestroyHandler?) getGameModeDestroyHandler(string roomId) + + public static (Result, IGameModeDestroyHandler?) getGameModeDestroyHandler(GameModeBase gameModeBase) { var result = new Result(); - (var genre_type, var mode_type) = getGameModeMeta(1, roomId); + var mode_type = getGameModeType(gameModeBase.getGameModeId()); switch (mode_type) { case GameModeType.TPS_FFA: - return (result, new TPSFreeForAllDestroyHandler(roomId)); + return (result, new TPSFreeForAllDestroyHandler(gameModeBase.getRoomId())); case GameModeType.TPS_TDM: - return (result, new TPSTeamDeathMatchDestroyHandler(roomId)); + return (result, new TPSTeamDeathMatchDestroyHandler(gameModeBase.getRoomId())); case GameModeType.RUN_ADV: - return (result, new RunAdventureDestroyHandler(roomId)); + return (result, new RunAdventureDestroyHandler(gameModeBase.getRoomId())); case GameModeType.RUN_RACE: - return (result, new RunRaceDestroyHandler(roomId)); + case GameModeType.RUN_HONOR: + return (result, new RunRaceDestroyHandler(gameModeBase.getRoomId(), mode_type)); + case GameModeType.RUN_PRACTICE: + return (result, new RunPracticeDestroyHandler(gameModeBase.getRoomId())); default: var err_msg = $"getGameModeInitHandler not exist !!!! mode_type : {mode_type}"; result.setFail(ServerErrorCode.GameModeJoinSuccessHandlerNotExist, err_msg); @@ -149,48 +169,49 @@ public class GameModeHelper } } - - public static IHostMigrator createHostMigrator(int gameModeId) + public static (Result, IPreparationForLeavingGameHandler?) getPreparationForLeavingGameHandler(Player player, IGameMode gameMode, string roomId) { var result = new Result(); - (var genre_type, var mode_type) = getGameModeMeta(gameModeId, "battle"); //kihoon 임시 마이그레이터 나중에 수정 필요 + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + var mode_type = getGameModeType(game_mode_base.getGameModeId()); switch (mode_type) { case GameModeType.TPS_FFA: - //return HostMigrationFactory.createCommonHostMigrator((int) BattlePlayMode.PodCombat, GameServerApp.getServerLogic()); - return new BattleFFAModeHostMigrator(); + return (result, new TpsFreeForAllPreparationForLeavingHandler(player, gameMode)); + case GameModeType.TPS_TDM: + return (result, new TpsTeamDeathMatchPreparationForLeavingHandler(player, gameMode)); + case GameModeType.RUN_ADV: + return (result, new RunAdventurePreparationForLeavingHandler(player, gameMode)); + case GameModeType.RUN_RACE: + case GameModeType.RUN_HONOR: + return (result, new RunRacePreparationForLeavingHandler(player, gameMode)); + case GameModeType.RUN_PRACTICE: + return (result, new RunPracticePreparationForLeavingHandler(player, gameMode)); default: - Log.getLogger().error($"createHostMigrator Not implements mode : {mode_type}"); - return new BattleFFAModeHostMigrator(); + var err_msg = $"getGameModeInitHandler not exist !!!! mode_type : {mode_type}"; + result.setFail(ServerErrorCode.GameModePreparationForLeavingHandlerNotExist, err_msg); + return (result, null); } } - - - public static (GameGenreType genreType, GameModeType modeType) getGameModeMeta(int gameModeId, string roomid) + public static GameModeType getGameModeType(int gameModeId) { - // kihoon todo : Meta에서 데이터 가져와야 한다. - // kihoon todo : Meta가 없을경우의 result 처리 필요 - - //kihoon todo : roomid 는 임시 나중에 mode id로 넣어서 처리 해줘야 된다. - if (roomid.Contains("battle")) + if (false == MetaData.Instance.m_game_mode_all_metas.TryGetValue(gameModeId, out var gameModeMetaData)) { - return (GameGenreType.TPS, GameModeType.TPS_FFA); - } - else if (roomid.Contains("1017101")) - { - return (GameGenreType.Running, GameModeType.RUN_RACE); + Log.getLogger().error($"getGameModeMeta not exist!!!! gameModeId : {gameModeId}"); + return GameModeType.None; } - return (GameGenreType.TPS, GameModeType.TPS_FFA); + return gameModeMetaData.m_game_mode_type; } - + public static GameModeBase toGameModeBase(IGameMode gameMode) { var game_mode_base = gameMode as GameModeBase; NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); return game_mode_base; } - + public static async Task leaveGameInstanceRoom(string userGuid, string roomId) { var result = new Result(); @@ -202,17 +223,17 @@ public class GameModeHelper instance_room_storage.Init(server_logic.getRedisDb(), ""); await instance_room_storage.leaveInstanceRoom(userGuid, roomId); - + var instance_room_id_base = getInstanceRoomIdBaseFromInstanceRoomId(roomId); await instance_room_storage.decreaseInstanceRoomScore(instance_room_id_base, roomId); return result; } - + public static string getInstanceRoomIdBaseFromInstanceRoomId(string instanceRoomId) { var instance_room_Id_element = instanceRoomId.Split(":"); - + if (instance_room_Id_element.Length >= 2) { return $"{instance_room_Id_element[0]}:{instance_room_Id_element[1]}"; @@ -221,44 +242,345 @@ public class GameModeHelper return string.Empty; } - public static IGameObject? getObjectInteractAction(GameModeBase gameModeBase, Anchor anchor) + public static IMatchingReservationHandler getMatchingStateEventHandler(EntityType entityType) + { + switch (entityType) + { + case EntityType.GameModeTpsfreeForAll: + return new TpsFfaMatchingReservationHandler(); + //case EntityType.GameModeTpsteamDeathMatch: + // return getTpsTeamDeathMatchInteractAction(gameModeBase, anchor); + //case EntityType.GameModeRunAdventure: + // return getRunAcventureInteractAction(gameModeBase, anchor); + case EntityType.GameModeRunRace: + case EntityType.GameModeRunHonor: + return new RunRaceMatchingReservationHandler(); + //kihoon todo : 연습 모드에 이게 필요한진 물어볼것 + default: + var err_msg = $"getMatchingStateEventHandler not exist!!!! entity_type : {entityType}"; + Log.getLogger().error(err_msg); + return new RunRaceMatchingReservationHandler(); + } + } + + public static IGameObjectInteractionHandler? getObjectInteractAction(GameModeBase gameModeBase, Anchor anchor, EGameModeObjectType objectType) + { + var entity_type = gameModeBase.getEntityType(); + switch (entity_type) + { + case EntityType.GameModeTpsfreeForAll: + return TpsFfaHelper.getTpsFfaInteractAction(gameModeBase, objectType, anchor.GUID); + case EntityType.GameModeTpsteamDeathMatch: + return getTpsTeamDeathMatchInteractAction(gameModeBase, objectType);//2506030 기준 미구현 + case EntityType.GameModeRunAdventure: + return getRunAcventureInteractAction(gameModeBase, objectType);//2506030 기준 미구현 + case EntityType.GameModeRunRace: + case EntityType.GameModeRunHonor: + return RunRaceHelper.getRunRaceInteractAction(gameModeBase, objectType); + case EntityType.GameModeRunPractice: + return RunPracticeHelper.getRunPracticeInteractAction(gameModeBase, objectType); + default: + return null; + } + } + + public static GameModeType convertEntityTypeToGameModeType(EntityType entityType) + { + switch (entityType) + { + case EntityType.GameModeTpsfreeForAll: + return GameModeType.TPS_FFA; + case EntityType.GameModeTpsteamDeathMatch: + return GameModeType.TPS_TDM; + case EntityType.GameModeRunAdventure: + return GameModeType.RUN_ADV; + case EntityType.GameModeRunRace: + return GameModeType.RUN_RACE; + case EntityType.GameModeRunPractice: + return GameModeType.RUN_PRACTICE; + case EntityType.GameModeRunHonor: + return GameModeType.RUN_HONOR; + default: + Log.getLogger().error($"convertEntityTypeToGameModeType objectType error !!!, entityType : {entityType}"); + return GameModeType.None; + } + } + + public static IGameObjectInteractionDataHandler? getGameObjectInteractionDataHandler(Player player, GameModeBase gameModeBase, Anchor anchor, EGameModeObjectType objectType, DateTime interaction_time) + { + var entity_type = gameModeBase.getEntityType(); + switch (entity_type) + { + case EntityType.GameModeTpsfreeForAll: + return TpsFfaHelper.getTpsFfaInteractDataHandler(player, gameModeBase, objectType, anchor.GUID, gameModeBase.getRoomId(), interaction_time); + case EntityType.GameModeTpsteamDeathMatch: + case EntityType.GameModeRunAdventure: + case EntityType.GameModeRunRace: + case EntityType.GameModeRunHonor: + return RunRaceHelper.getRunRaceInteractDataHandler(player, objectType, anchor.GUID, anchor.TableID, gameModeBase.getRoomId(), interaction_time); + case EntityType.GameModeRunPractice: + return RunPracticeHelper.getRunPracticeInteractDataHandler(player, objectType, anchor.GUID, anchor.TableID, gameModeBase.getRoomId(), interaction_time); + default: + Log.getLogger().error($"getGameObjectInteractionDataHandler objectType error !!!, anchorGuid : {anchor.GUID}, objectType : {objectType}, roomId : {gameModeBase.getRoomId()}, player : {player.toBasicString()}"); + return TpsFfaHelper.getTpsFfaInteractDataHandler(player, gameModeBase, objectType, anchor.GUID, gameModeBase.getRoomId(), interaction_time); + } + } + + public static IDeadHandler? getPlayerDeadAction(GameModeBase gameModeBase) { var entity_type = gameModeBase.getEntityType(); switch (entity_type) { case EntityType.GameModeTpsfreeForAll: - //kihoon todo : table id 넘겨서 pickup, combat, weapon, buff, 인지 전달 받아야 된다. - return getTpsFreeForAllInteractAction(gameModeBase, anchor); + return null; case EntityType.GameModeTpsteamDeathMatch: - return getTpsTeamDeathMatchInteractAction(gameModeBase, anchor); + return null; case EntityType.GameModeRunAdventure: - return getRunAcventureInteractAction(gameModeBase, anchor); + return null; case EntityType.GameModeRunRace: - return getRunRaceInteractAction(gameModeBase, anchor); + case EntityType.GameModeRunHonor: + return RunRaceHelper.getDeadAction(gameModeBase); + case EntityType.GameModeRunPractice: + return RunPracticeHelper.getDeadAction(gameModeBase); default: - return null; + return null; } } - public static IGameObject? getRunRaceInteractAction(GameModeBase gameModeBase, Anchor anchor) + public static IRespawnHandler? getPlayerRespawnAction(GameModeBase gameModeBase) { - //kihoon todo :현재 savePoint 밖에 없어서 그거 넘긴다. - return gameModeBase.getEntityAction(); + var entity_type = gameModeBase.getEntityType(); + + switch (entity_type) + { + case EntityType.GameModeTpsfreeForAll: + return null; + case EntityType.GameModeTpsteamDeathMatch: + return null; + case EntityType.GameModeRunAdventure: + return null; + case EntityType.GameModeRunRace: + case EntityType.GameModeRunHonor: + return RunRaceHelper.getRespawnAction(gameModeBase); + case EntityType.GameModeRunPractice: + return RunPracticeHelper.getRespawnAction(gameModeBase); + default: + return null; + } + } + public static IGameModeLoadCompleteHandler getPlayerLoadCompleteAction(GameModeBase gameModeBase) + { + var entity_type = gameModeBase.getEntityType(); + + switch (entity_type) + { + case EntityType.GameModeTpsteamDeathMatch: + case EntityType.GameModeRunAdventure: + //위모드는 아직 없긴 하다... + Log.getLogger().error($"getPlayerLoadCompleteAction not implemented !!!, entity_type : {entity_type}"); + throw new NotImplementedException(); + case EntityType.GameModeTpsfreeForAll: + return TpsFfaHelper.getLoadCompleteAction(gameModeBase); + case EntityType.GameModeRunRace: + case EntityType.GameModeRunHonor: + return RunRaceHelper.getLoadCompleteAction(gameModeBase); + case EntityType.GameModeRunPractice: + return RunPracticeHelper.getLoadCompleteAction(gameModeBase); + default: + Log.getLogger().error($"getPlayerLoadCompleteAction not implemented !!!, entity_type : {entity_type}"); + throw new NotImplementedException(); + + } } - public static IGameObject? getTpsFreeForAllInteractAction(GameModeBase gameModeBase, Anchor anchor) + public static IGameModeDisconnectHandler getGameModeDisconnectHandler(GameModeBase gameModeBase, Player player) + { + var game_mode_type = gameModeBase.getGameModeType(); + + switch (game_mode_type) + { + case GameModeType.RUN_RACE: + case GameModeType.RUN_HONOR: + return new RunRaceDisconnectHandler(gameModeBase, player, game_mode_type); + case GameModeType.RUN_PRACTICE: + return new RunPracticeDisconnectHandler(gameModeBase, player); + case GameModeType.TPS_FFA: + return new TpsFreeForAllDisconnectHandler(gameModeBase, player); + case GameModeType.None: + case GameModeType.TPS_TDM: + case GameModeType.RUN_ADV: + default: + Log.getLogger().error($"getPlayerLoadCompleteAction not implemented !!!, game_mode_type : {game_mode_type}"); + throw new NotImplementedException(); + } + } + + + + public static IGameObjectInteractionHandler? getTpsTeamDeathMatchInteractAction(GameModeBase gameModeBase, EGameModeObjectType objectType) { return null; } - - public static IGameObject? getTpsTeamDeathMatchInteractAction(GameModeBase gameModeBase, Anchor anchor) + + public static IGameObjectInteractionHandler? getRunAcventureInteractAction(GameModeBase gameModeBase, EGameModeObjectType objectType) { return null; } - - public static IGameObject? getRunAcventureInteractAction(GameModeBase gameModeBase, Anchor anchor) + + public static MatchGameStateType convertGameStateToMatchState(EntityType entityType, GameModeState gameModeState) { - return null; + switch (entityType) + { + case EntityType.GameModeTpsfreeForAll: + return convertTpsFfaGameStateToMatchState(gameModeState); + case EntityType.GameModeRunRace: + case EntityType.GameModeRunHonor: + return convertRunRaceGameStateToMatchState(gameModeState); + case EntityType.GameModeRunPractice: + return convertRunPracticeGameStateToMatchState(gameModeState); + case EntityType.GameModeRunAdventure: + return MatchGameStateType.None; + case EntityType.GameModeTpsteamDeathMatch: + return MatchGameStateType.None; + default: + return MatchGameStateType.None; + } } -} \ No newline at end of file + + public static MatchGameStateType convertTpsFfaGameStateToMatchState(GameModeState gaemModeState) + { + switch (gaemModeState) + { + case GameModeState.Loading: + case GameModeState.Created: + case GameModeState.LoadingWait: + case GameModeState.Wait: + case GameModeState.Rounding: + case GameModeState.RoundWait: + return MatchGameStateType.Start; + case GameModeState.RoundEndAll: + return MatchGameStateType.Finish; + case GameModeState.Destroyed: + return MatchGameStateType.Destroy; + default: + return MatchGameStateType.None; + } + } + + public static MatchGameStateType convertRunRaceGameStateToMatchState(GameModeState gaemModeState) + { + switch (gaemModeState) + { + case GameModeState.Loading: + case GameModeState.Created: + case GameModeState.LoadingWait: + case GameModeState.Wait: + return MatchGameStateType.Idle; + case GameModeState.Ready: + return MatchGameStateType.Ready; + case GameModeState.Play: + return MatchGameStateType.Start; + case GameModeState.RewardSummary: + case GameModeState.Result: + return MatchGameStateType.Finish; + case GameModeState.Destroyed: + return MatchGameStateType.Destroy; + default: + return MatchGameStateType.None; + } + } + + public static MatchGameStateType convertRunPracticeGameStateToMatchState(GameModeState gaemModeState) + { + switch (gaemModeState) + { + case GameModeState.Loading: + case GameModeState.Created: + case GameModeState.LoadingWait: + case GameModeState.Wait: + return MatchGameStateType.Idle; + case GameModeState.Ready: + return MatchGameStateType.Ready; + case GameModeState.Play: + return MatchGameStateType.Start; + case GameModeState.RewardSummary: + case GameModeState.Result: + return MatchGameStateType.Finish; + case GameModeState.Destroyed: + return MatchGameStateType.Destroy; + default: + return MatchGameStateType.None; + } + } + + public static MatchGameStateType convertRunSingleGameStateToMatchState(GameModeState gaemModeState) + { + switch (gaemModeState) + { + case GameModeState.Loading: + case GameModeState.Created: + case GameModeState.LoadingWait: + case GameModeState.Wait: + return MatchGameStateType.Idle; + case GameModeState.Ready: + return MatchGameStateType.Ready; + case GameModeState.Play: + return MatchGameStateType.Start; + case GameModeState.RewardSummary: + case GameModeState.Result: + return MatchGameStateType.Finish; + case GameModeState.Destroyed: + return MatchGameStateType.Destroy; + default: + return MatchGameStateType.None; + } + } + + + + public static (Result, BattleObjectMetaData) getGameModeObjectMeta(string anchorGuid, GameModeBase gameModeBase) + { + //Explain : 게임모드 개선중 이전에 사용중이던 BattleObject 정보를 그대로 사용하기로 협의됨.(2025-06) 해서 Battle이라는 이름이 그대로 사용 + var map = gameModeBase.getInstanceRoom().getMap(); + string err_msg = string.Empty; + var result = new Result(); + if(false == map.getAnchors().TryGetValue(anchorGuid, out var anchorInfo)) + { + err_msg = $"Anchor Info Not Exist.... room_id : {map.m_room_id} anchor guid : {anchorGuid}"; + result.setFail(ServerErrorCode.BattleInstanceNotExistAnchors, err_msg); + return (result, new(new BattleObjectMetaDataMutable())); + } + + if (false == MapDataTable.Instance.getAnchor(anchorGuid, out var anchor)) + { + err_msg = $"Not found Anchor in MapFileType !!! : anchourGuid:{anchorGuid}"; + //Log.getLogger(err_msg); + result.setFail(ServerErrorCode.BattleInstanceNotExistAnchors, err_msg); + return (result, new(new BattleObjectMetaDataMutable())); + } + + var table_id = anchorInfo.AnchorProp.TableId; + + if (anchor.Type.Equals(BattleConstant.BATTLE_OBJECT_GROUP_ANCHOR_NAME)) + { + if (false == MetaData.Instance._BattleObjectSpawnGroupMetaTable.TryGetValue(table_id, out var battle_object_spawn_group_meta)) + { + err_msg = $"_BattleObjectSpawnGroupMetaTable data Not Exist.... room_id : {map.m_room_id} anchor guid : {anchorGuid}, tableId : {table_id}"; + result.setFail(ServerErrorCode.BattleInstanceObjectMetaNotExist, err_msg); + return (result, new(new BattleObjectMetaDataMutable())); + } + + table_id = battle_object_spawn_group_meta.BattleObjectID; + } + + if (false == MetaData.Instance._BattleObjectMetaTable.TryGetValue(table_id, out var game_object_meta)) + { + err_msg = $"_BattleObjectMetaTable data Not Exist.... room_id : {map.m_room_id} anchor guid : {anchorGuid}, tableId : {table_id}"; + result.setFail(ServerErrorCode.BattleInstanceObjectMetaNotExist, err_msg); + return (result, new(new BattleObjectMetaDataMutable())); + } + + return (result, game_object_meta); + } +} diff --git a/GameServer/Contents/GameMode/Helper/GameNotifyHelper.cs b/GameServer/Contents/GameMode/Helper/GameNotifyHelper.cs index d5794af..b160b4c 100644 --- a/GameServer/Contents/GameMode/Helper/GameNotifyHelper.cs +++ b/GameServer/Contents/GameMode/Helper/GameNotifyHelper.cs @@ -1,35 +1,194 @@ -using Google.Protobuf.WellKnownTypes; +using GameServer.Contents.GameMode.Manage.PlayManage; +using Google.Protobuf.WellKnownTypes; +using MetaAssets; using Newtonsoft.Json; +using ServerCommon; using ServerCore; +using ANCHOR_GUID = System.String; +using USER_GUID = System.String; +using USER_POS = Pos; + namespace GameServer.Contents.GameMode.Helper; public class GameNotifyHelper { - public static void broadcast_GS2C_NTF_GAME_STATE_UPDATE(InstanceRoom instanceRoom, GameModeState state, DateTime nextUpdatableTime) + public static void broadcast_GS2C_NTF_GAME_STATE_UPDATE(IGameMode gameMode, GameModeState state, DateTime nextUpdatableTime, StateUpdateReasonType updateReason) { - var ntf = makeNtfGameStateUpdate(instanceRoom, state, nextUpdatableTime); - Log.getLogger().debug($"broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE ntf instanceRoom Id : {instanceRoom.getMap().m_room_id} data : {JsonConvert.SerializeObject(ntf.Message.NtfGameStateUpdate)}"); - instanceRoom.Broadcast(ntf); + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_bse is null !!!"); + var instance_room = game_mode_base.getInstanceRoom(); + var ntf = makeNtfGameStateUpdate(state, nextUpdatableTime, updateReason); + Log.getLogger().debug($"broadcast_GS2C_NTF_GAME_STATE_UPDATE ntf instanceRoom Id : {instance_room.getMap().m_room_id} data : {JsonConvert.SerializeObject(ntf.Message.NtfGameModeStateUpdate)}"); + instance_room.Broadcast(ntf); } - public static void send_GS2C_NTF_GAME_STATE_UPDATE(Player player, InstanceRoom instanceRoom, GameModeState state, DateTime nextUpdatableTime) + public static void send_GS2C_NTF_GAME_STATE_UPDATE(Player player, InstanceRoom instanceRoom, GameModeState state, DateTime nextUpdatableTime, StateUpdateReasonType updateReason) { - var ntf = makeNtfGameStateUpdate(instanceRoom, state, nextUpdatableTime); - Log.getLogger().debug($"send_GS2C_NTF_GAME_STATE_UPDATE ntf instanceRoom Id : {instanceRoom.getMap().m_room_id} data : {JsonConvert.SerializeObject(ntf.Message.NtfGameStateUpdate)}"); + var ntf = makeNtfGameStateUpdate(state, nextUpdatableTime, updateReason); + Log.getLogger().debug($"send_GS2C_NTF_GAME_STATE_UPDATE ntf instanceRoom Id : {instanceRoom.getMap().m_room_id} data : {JsonConvert.SerializeObject(ntf.Message.NtfGameModeStateUpdate)}"); GameServerApp.getServerLogic().onSendPacket(player, ntf); } - public static ClientToGame makeNtfGameStateUpdate(InstanceRoom instanceRoom, GameModeState state, DateTime nextUpdatableTime) + public static ClientToGame makeNtfGameStateUpdate(GameModeState state, DateTime nextUpdatableTime, StateUpdateReasonType updateReason) { ClientToGame ntf = new ClientToGame(); ntf.Message = new ClientToGameMessage(); - ntf.Message.NtfGameStateUpdate = new ClientToGameMessage.Types.GS2C_NTF_GAME_STATE_UPDATE_NOTI(); - ntf.Message.NtfGameStateUpdate.CurrentState = state; - ntf.Message.NtfGameStateUpdate.NextUpdatableTime = Timestamp.FromDateTime(nextUpdatableTime); + ntf.Message.NtfGameModeStateUpdate = new ClientToGameMessage.Types.GS2C_NTF_GAME_MODE_STATE_UPDATE(); + ntf.Message.NtfGameModeStateUpdate.CurrentState = state; + ntf.Message.NtfGameModeStateUpdate.NextUpdatableTime = Timestamp.FromDateTime(nextUpdatableTime); + ntf.Message.NtfGameModeStateUpdate.UpdateReason = updateReason; return ntf; } + public static void send_GS2C_NTF_GAME_MODE_OVERALL_INFO(Player player, GameModeBase gameModeBase) + { + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfGameModeOverallInfo = new ClientToGameMessage.Types.GS2C_NTF_GAME_MODE_OVERALL_INFO(); + ntf.Message.NtfGameModeOverallInfo.GameCreateTime = Timestamp.FromDateTime(gameModeBase.getGameCreateTime()); + ntf.Message.NtfGameModeOverallInfo.ServerTime = Timestamp.FromDateTime(DateTimeHelper.Current); + GameServerApp.getServerLogic().onSendPacket(player, ntf); + } + + public static void broadcast_GS2C_NTF_GAME_MODE_OBJECT_INFO(GameModeBase gameModeBase, List infos) + { + if (infos.Count == 0) return; + + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfGameModeObjectInfo = new ClientToGameMessage.Types.GS2C_NTF_GAME_MODE_OBJECT_INFO(); + ntf.Message.NtfGameModeObjectInfo.GameModeObjectInfos.AddRange(infos); + + var instance_room = gameModeBase.getInstanceRoom(); + Log.getLogger().debug($"broadcast_GS2C_NTF_GAME_MODE_OBJECT_INFO ntf room Id : {instance_room.getMap().m_room_id} data : {JsonConvert.SerializeObject(ntf.Message.NtfGameModeObjectInfo)}"); + instance_room.Broadcast(ntf); + } + + public static void broadcast_GS2C_NTF_P2P_HOST_UPDATE(InstanceRoom instanceRoom, string hostUserGuid) + { + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfP2PHostUpdate = new ClientToGameMessage.Types.GS2C_NTF_P2P_HOST_UPDATE(); + ntf.Message.NtfP2PHostUpdate.HostUserGuid = hostUserGuid; + + Log.getLogger().debug($"broadcast_GS2C_NTF_P2P_HOST_UPDATE ntf room Id : {instanceRoom.getMap().m_room_id} data : {JsonConvert.SerializeObject(ntf.Message.NtfP2PHostUpdate)}"); + instanceRoom.Broadcast(ntf); + } + + public static void send_GS2C_NTF_P2P_HOST_UPDATE(Player player, string hostUserGuid) + { + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfP2PHostUpdate = new ClientToGameMessage.Types.GS2C_NTF_P2P_HOST_UPDATE(); + ntf.Message.NtfP2PHostUpdate.HostUserGuid = hostUserGuid; + GameServerApp.getServerLogic().onSendPacket(player, ntf); + } + + public static void broadcast_GS2C_NTF_GAME_PLAYER_RESPAWN(GameModeBase gameModeBase, string respawnUserGuid, Pos pos) + { + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfGamePlayerRespawn = new ClientToGameMessage.Types.GS2C_NTF_GAME_PLAYER_RESPAWN(); + ntf.Message.NtfGamePlayerRespawn.RespawnUserGuid = respawnUserGuid; + ntf.Message.NtfGamePlayerRespawn.Pos = pos; + + var instance_room = gameModeBase.getInstanceRoom(); + Log.getLogger().debug($"broadcast_GS2C_NTF_GAME_PLAYER_RESPAWN ntf room Id : {instance_room.getMap().m_room_id} data : {JsonConvert.SerializeObject(ntf.Message.NtfGamePlayerRespawn)}"); + instance_room.Broadcast(ntf); + } + + + public static void send_GS2C_NTF_GAME_USER_KICK(Player player, GameModeKickReason reason) + { + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfGameUserKick = new ClientToGameMessage.Types.GS2C_NTF_GAME_USER_KICK(); + ntf.Message.NtfGameUserKick.Reason = reason; + + Log.getLogger().debug($"send_GS2C_NTF_GAME_USER_KICK ntf player : {player.toBasicString()} data : {JsonConvert.SerializeObject(ntf.Message.NtfGameUserKick)}"); + GameServerApp.getServerLogic().onSendPacket(player, ntf); + } + + public static void send_GS2C_NTF_READY_POS(Player player, string anchorGuid, Pos pos) + { + var ntf = makeNtfReadyPos(player.getUserGuid(), anchorGuid, pos); + + GameServerApp.getServerLogic().onSendPacket(player, ntf); + } + + public static void broadcast_GS2C_NTF_READY_POS(InstanceRoom instanceRoom, Dictionary userPosInfos) + { + var ntf = makeNtfReadyPos(userPosInfos); + instanceRoom.Broadcast(ntf); + } + private static ClientToGame makeNtfReadyPos(Dictionary userPosInfos) + { + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfReadyPos = new ClientToGameMessage.Types.GS2C_NTF_READY_POS(); + + + foreach (var pair in userPosInfos) + { + GameModePlayerPositionInfo pos_info = new GameModePlayerPositionInfo(); + pos_info.RespawnUserGuid = pair.Value.Item1; + pos_info.Pos = pair.Value.Item2; + pos_info.AnchorGuid = pair.Key; + ntf.Message.NtfReadyPos.GameModePlayerPositionInfos.Add(pos_info); + } + + return ntf; + } + + private static ClientToGame makeNtfReadyPos(string userGuid, string anchorGuid, Pos pos) + { + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfReadyPos = new ClientToGameMessage.Types.GS2C_NTF_READY_POS(); + + + GameModePlayerPositionInfo pos_info = new GameModePlayerPositionInfo(); + pos_info.RespawnUserGuid = userGuid; + pos_info.Pos = pos; + pos_info.AnchorGuid = anchorGuid; + ntf.Message.NtfReadyPos.GameModePlayerPositionInfos.Add(pos_info); + return ntf; + } + + public static bool send_S2C_NTF_GAME_MODE_PLAY_REGULATION(Player player) + { + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!! - {player.toBasicString()}"); + + var ntf_packet = new ClientToGame(); + ntf_packet.Message = new ClientToGameMessage(); + ntf_packet.Message.NtfGameModePlayRegulation = new ClientToGameMessage.Types.GS2C_NTF_GAME_MODE_PLAY_REGULATION(); + + ntf_packet.Message.NtfGameModePlayRegulation.MatchRestriction.AddRange( + attribute.m_match_restriction.Select(kv => new MatchRestriction + { + GameModeType = kv.Key.ToString(), // GameModeType이 enum이면 string으로 변환 + MatchCount = kv.Value.m_current_matched_count, + NextRefreshTime = Timestamp.FromDateTime(kv.Value.m_next_refresh_time.ToUniversalTime()) + }) + ); + ntf_packet.Message.NtfGameModePlayRegulation.GamePlayPenalty.AddRange( + attribute.m_play_penalty.Select(kv => new GameModePlayPenalty + { + GameModeType = kv.Key.ToString(), + PenaltyCount = kv.Value.m_penalty_count, + PenaltyStartTime = Timestamp.FromDateTime(kv.Value.m_penalty_start_time), + PenaltyEndTime = Timestamp.FromDateTime(kv.Value.m_penalty_end_time) + }) + ); + if (false == GameServerApp.getServerLogic().onSendPacket(player, ntf_packet)) + { + return false; + } + + return true; + } } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/InteractionObject/GameObject.cs b/GameServer/Contents/GameMode/InteractionObject/GameObject.cs index 93f5192..c3b53df 100644 --- a/GameServer/Contents/GameMode/InteractionObject/GameObject.cs +++ b/GameServer/Contents/GameMode/InteractionObject/GameObject.cs @@ -42,9 +42,17 @@ public class GameObjectWeapon : GameObject } } -public class GameObjectBuff : GameObject +public class RunRaceBuff : GameModeObject { - public GameObjectBuff(string anchorGuid) : base(EntityType.GameObjectBuff, anchorGuid) + public RunRaceBuff(string anchorGuid) : base(EGameModeObjectType.Buff, anchorGuid) + { + + } +} + +public class RunRaceRunningBuff : GameModeObject +{ + public RunRaceRunningBuff(string anchorGuid) : base(EGameModeObjectType.RunningBuff, anchorGuid) { } diff --git a/GameServer/Contents/GameMode/InteractionObject/GameObjectInteractionDataCommonHandlerBase.cs b/GameServer/Contents/GameMode/InteractionObject/GameObjectInteractionDataCommonHandlerBase.cs new file mode 100644 index 0000000..1ee817c --- /dev/null +++ b/GameServer/Contents/GameMode/InteractionObject/GameObjectInteractionDataCommonHandlerBase.cs @@ -0,0 +1,40 @@ +using GameServer.Contents.GameMode.InteractionObject; +using MetaAssets; +using ServerCommon; +using ServerCommon._1._Define.BusinessLog.GameMode; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer; + +public abstract class GameObjectInteractionDataCommonHandlerBase : IGameObjectInteractionDataHandler +{ + public string m_user_guid { get; } = string.Empty; + public int m_talbe_id { get; set; } = 0; + public string m_interaction_anchor_guid { get; } = string.Empty; + public DateTime m_client_interaction_time { get; } = DateTimeHelper.Current;//이값을 쓸지 말지 고민 필요..이거 쓰면 클라시간, 서버시간 예외처리 넣고 사용해야된다...(해킹..) + + public GameModeObject m_interaction_object_info { get; set; } = new GameObjectEmpty(); + public List m_need_noti_objects { get; set; } = new ();//변경이 되는 OBJECT들은 여기에 우선 넣고 NOTI 보낸는데 사용 + + public DateTime m_next_active_time { get; set; } = DateTimeHelper.Current; + public IGameObjectInteractionLogInfo m_interaction_log_info { get; protected set; } = new GameObjectInteractionLogInfoEmpty(); + public BattleObjectMetaData m_game_object_meta { get; set; } = new(new BattleObjectMetaDataMutable()); + + public CommonResult m_common_result { get; set; } = new CommonResult(); + + protected GameObjectInteractionDataCommonHandlerBase(string userGuid, string interactionAnchorGuid, int tableId, DateTime interactionTime) + { + m_user_guid = userGuid; + m_interaction_anchor_guid = interactionAnchorGuid; + m_client_interaction_time = interactionTime; + m_talbe_id = tableId; + } + + protected GameObjectInteractionDataCommonHandlerBase(string userGuid, string interactionAnchorGuid, DateTime interactionTime) + { + m_user_guid = userGuid; + m_interaction_anchor_guid = interactionAnchorGuid; + m_client_interaction_time = interactionTime; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/InteractionObject/IGameObjectInteractionDataHandler.cs b/GameServer/Contents/GameMode/InteractionObject/IGameObjectInteractionDataHandler.cs new file mode 100644 index 0000000..9a7f9ed --- /dev/null +++ b/GameServer/Contents/GameMode/InteractionObject/IGameObjectInteractionDataHandler.cs @@ -0,0 +1,8 @@ +using ServerCommon; +using ServerCommon.BusinessLogDomain; + +namespace GameServer.Contents.GameMode.InteractionObject; + +public interface IGameObjectInteractionDataHandler +{ +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Log/GameModePenaltyBusinessLog.cs b/GameServer/Contents/GameMode/Log/GameModePenaltyBusinessLog.cs new file mode 100644 index 0000000..bb29bce --- /dev/null +++ b/GameServer/Contents/GameMode/Log/GameModePenaltyBusinessLog.cs @@ -0,0 +1,28 @@ +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; + +namespace GameServer; + +public class GameModePenaltyBusinessLog : ILogInvokerEx +{ + private GameModePenaltyLogInfo m_info; + + + public GameModePenaltyBusinessLog(GameModePenaltyLogInfo logInfo) : base(LogDomainType.GameModePenalty) + { + m_info = logInfo; + } + + + + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(new GameModePenaltyLogInfo(this, m_info)); + } + + public override bool hasLog() + { + return true; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRoom.cs b/GameServer/Contents/GameMode/Manage/GameModeInstanceRoom.cs similarity index 53% rename from GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRoom.cs rename to GameServer/Contents/GameMode/Manage/GameModeInstanceRoom.cs index b986bf1..3cb613a 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRoom.cs +++ b/GameServer/Contents/GameMode/Manage/GameModeInstanceRoom.cs @@ -1,39 +1,10 @@ using System.Diagnostics.CodeAnalysis; - -using Google.Protobuf; -using Google.Protobuf.WellKnownTypes; - - -using ServerCore; -using ServerBase; -using ServerCommon; using ServerCommon.BusinessLogDomain; -using MetaAssets; - namespace GameServer; public partial class InstanceRoom { - public async Task battleInstanceInit() - { - // var result = await BattleInstanceManager.It.battleInstanceInit(this, _roomId); - // if (result.isFail()) - // { - // Log.getLogger().error(result.toBasicString()); - // return false; - // } - await Task.CompletedTask; - return true; - } - - public async Task sendGameModeInstanceJoinSuccess(Player player) - { - await Task.CompletedTask; - //var game_mod_handler = GameModeManager.It.getGameModeJoinHandler(_placeType); - return new(); - } - public bool tryGetInstanceMember(string userGuid, [MaybeNullWhen(false)] out Player out_player) { @@ -60,5 +31,16 @@ public partial class InstanceRoom return users; } - + + + public List getInstanceMemberGuids() + { + List user_guids = new(); + + foreach (var player in m_players.Values) + { + user_guids.Add(player.getUserGuid()); + } + return user_guids; + } } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/JoinManage/GameModeInitHandlerBase.cs b/GameServer/Contents/GameMode/Manage/JoinManage/GameModeInitHandlerBase.cs index 8179c52..54b9640 100644 --- a/GameServer/Contents/GameMode/Manage/JoinManage/GameModeInitHandlerBase.cs +++ b/GameServer/Contents/GameMode/Manage/JoinManage/GameModeInitHandlerBase.cs @@ -1,6 +1,7 @@ using GameServer.Contents.GameMode.Helper; using GameServer.Contents.GameMode.Manage; using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; using ServerBase; using ServerCommon; using ServerCore; @@ -21,9 +22,9 @@ public abstract class GameModeInitHandlerBase : IGameModeInitHandler public abstract Result gamedModeInstanceInitValidate(); - public virtual async Task gamedModeInstanceInit() + public virtual async Task gamedModeInstanceInit(int gameModeId) { - var result = await GameModeManager.It.createGameMode(m_instance_room, m_game_mode_type); //kihoon todo : 이렇게 짜는게 맞는지...고민좀 해보자 + var result = await GameModeManager.It.createGameMode(m_instance_room, m_game_mode_type, gameModeId); if (result.isFail()) return result; var room_id = m_instance_room.getMap().m_room_id; @@ -38,13 +39,13 @@ public abstract class GameModeInitHandlerBase : IGameModeInitHandler var initializer = gameMode as IInitializer; NullReferenceCheckHelper.throwIfNull(initializer, () => $"initializer is null !!! casting error"); - var gamd_mod_base = gameMode as GameModeBase; - NullReferenceCheckHelper.throwIfNull(gamd_mod_base, () => $"gamd_mod_base is null !!! casting error"); + var gamd_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(gamd_mode_base, () => $"gamd_mode_base is null !!! casting error"); //모드별로 필요한 내용 초기화 - using (var releaser = await gamd_mod_base.getAsyncLock()) + using (var releaser = await gamd_mode_base.getAsyncLock()) { - if (gamd_mod_base.isLoadCompleted()) return result; + if (gamd_mode_base.isLoadCompleted()) return result; result = await initializer.onInit(); if (result.isFail()) return result; @@ -52,4 +53,9 @@ public abstract class GameModeInitHandlerBase : IGameModeInitHandler return result; } + + public GameModeType getGameModeType() + { + return m_game_mode_type; + } } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/JoinManage/GameModeJoinHandlerBase.cs b/GameServer/Contents/GameMode/Manage/JoinManage/GameModeJoinHandlerBase.cs index eb9f3bf..b53e8e3 100644 --- a/GameServer/Contents/GameMode/Manage/JoinManage/GameModeJoinHandlerBase.cs +++ b/GameServer/Contents/GameMode/Manage/JoinManage/GameModeJoinHandlerBase.cs @@ -1,4 +1,5 @@ -using ServerCommon; +using MetaAssets; +using ServerCommon; namespace GameServer.Contents.GameMode.Manage; diff --git a/GameServer/Contents/GameMode/Manage/JoinManage/GameModeJoinSuccessHandlerBase.cs b/GameServer/Contents/GameMode/Manage/JoinManage/GameModeJoinSuccessHandlerBase.cs index d565f93..66cb2d7 100644 --- a/GameServer/Contents/GameMode/Manage/JoinManage/GameModeJoinSuccessHandlerBase.cs +++ b/GameServer/Contents/GameMode/Manage/JoinManage/GameModeJoinSuccessHandlerBase.cs @@ -1,4 +1,6 @@ -using ServerBase; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; using ServerCommon; namespace GameServer.Contents.GameMode.Manage; @@ -18,7 +20,7 @@ public abstract class GameModeJoinSuccessHandlerBase : IGameModeJoinSuccessHandl m_instance_room = instanceRoom; } - public async Task joinSuccess() + public async Task joinSuccess(GameModeBase gameModeBase) { var result = joinSuccessValidate(); if (result.isFail()) return result; @@ -29,15 +31,24 @@ public abstract class GameModeJoinSuccessHandlerBase : IGameModeJoinSuccessHandl result = joinSuccessNotify().Result; if (result.isFail()) return result; + registUserState(gameModeBase); + MatchManager.It.RoomEventHandler.GameMemberJoin(gameModeBase.getRoomId(), m_player.getUserGuid()); + joinSuccessWriteLog(); return result; } public abstract Result joinSuccessValidate(); + public abstract Task joinSuccessConfirmation(); public abstract Task joinSuccessNotify(); public abstract void joinSuccessWriteLog(); + + private void registUserState(GameModeBase gameModeBase) + { + gameModeBase.registUserState(m_player.getUserGuid()); + } } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/JoinManage/IGameModeInitHandler.cs b/GameServer/Contents/GameMode/Manage/JoinManage/IGameModeInitHandler.cs index 05e91bc..8103ecf 100644 --- a/GameServer/Contents/GameMode/Manage/JoinManage/IGameModeInitHandler.cs +++ b/GameServer/Contents/GameMode/Manage/JoinManage/IGameModeInitHandler.cs @@ -1,7 +1,11 @@ -namespace GameServer; +using MetaAssets; + +namespace GameServer; public interface IGameModeInitHandler { public Result gamedModeInstanceInitValidate(); - public Task gamedModeInstanceInit(); + public Task gamedModeInstanceInit(int gameModeId); + + public GameModeType getGameModeType(); } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/JoinManage/IGameModeJoinSuccessHandler.cs b/GameServer/Contents/GameMode/Manage/JoinManage/IGameModeJoinSuccessHandler.cs index 20b321c..9562848 100644 --- a/GameServer/Contents/GameMode/Manage/JoinManage/IGameModeJoinSuccessHandler.cs +++ b/GameServer/Contents/GameMode/Manage/JoinManage/IGameModeJoinSuccessHandler.cs @@ -1,6 +1,8 @@ -namespace GameServer.Contents.GameMode.Manage; +using GameServer.Contents.GameMode.Manage.PlayManage; + +namespace GameServer.Contents.GameMode.Manage; public interface IGameModeJoinSuccessHandler { - public Task joinSuccess(); + public Task joinSuccess(GameModeBase gameModeBase); } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeDestroyHandlerBase.cs b/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeDestroyHandlerBase.cs index 5e17294..1748466 100644 --- a/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeDestroyHandlerBase.cs +++ b/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeDestroyHandlerBase.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; using ServerBase; using ServerCommon; using ServerCore; @@ -9,7 +10,7 @@ public abstract class GameModeDestroyHandlerBase : IGameModeDestroyHandler { protected readonly GameModeType m_game_mode_type; protected string m_room_id; - protected IGameMode? m_game_mode; + //protected IGameMode? m_game_mode; public GameModeDestroyHandlerBase(string roomId, GameModeType gameModeType) { @@ -33,8 +34,11 @@ public abstract class GameModeDestroyHandlerBase : IGameModeDestroyHandler var game_mode_base = gameMode as GameModeBase; NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!! casting error"); + using (var releaser = await game_mode_base.getAsyncLock()) { + game_mode_base.deleteGameMemberJoinReservation(); + var ticker = game_mode_base.getEntityTicker(); if (ticker is not null) { @@ -49,17 +53,16 @@ public abstract class GameModeDestroyHandlerBase : IGameModeDestroyHandler Log.getLogger().debug(err_msg); } } - + + //모드별 destroy 처리 await postDestroy(gameMode); - - if (false == GameModeManager.It.tryRemoveGameMode(m_room_id, out var _)) + + if (false == GameModeManager.It.tryRemoveGameMode(m_room_id, "GameModeDestroyHandler", out var _)) { var err_msg = $"game_mode is null !!!! gameModeType : {m_game_mode_type}, instanceRoomId : {m_room_id}"; Log.getLogger().warn(err_msg); } - - } diff --git a/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeDisconnectHandlerBase.cs b/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeDisconnectHandlerBase.cs new file mode 100644 index 0000000..79e5282 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeDisconnectHandlerBase.cs @@ -0,0 +1,46 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Manage.LeaveManage; + +public abstract class GameModeDisconnectHandlerBase : IGameModeDisconnectHandler +{ + protected readonly GameModeType m_game_mode_type; + protected Player m_player; + protected IGameMode m_game_mode; + private List m_invokers = new(); + + public GameModeDisconnectHandlerBase(GameModeType gameModeType, IGameMode gameMode, Player player) + { + m_game_mode_type = gameModeType; + m_game_mode = gameMode; + m_player = player; + } + + public async Task gameModeDisconnect() + { + + var game_mode_base = m_game_mode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var result = new Result(); + result = await disconnect(); + if(result.isFail()) return result; + + result = await notifyAfterDisconnect(); + if(result.isFail()) return result; + + MatchManager.It.RoomEventHandler.GameMemberQuit(game_mode_base.getRoomId(), m_player.getUserGuid()); + + result = await logAfterDisconnect(); + if(result.isFail()) return result; + + return result; + } + + public abstract Task disconnect(); + public abstract Task notifyAfterDisconnect(); + public abstract Task logAfterDisconnect(); +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeLeaveHandlerBase.cs b/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeLeaveHandlerBase.cs index 6324849..d9a7794 100644 --- a/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeLeaveHandlerBase.cs +++ b/GameServer/Contents/GameMode/Manage/LeaveManage/GameModeLeaveHandlerBase.cs @@ -1,5 +1,6 @@ using GameServer.Contents.GameMode.Helper; using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; using ServerBase; using ServerCommon; using ServerCore; @@ -10,44 +11,41 @@ public abstract class GameModeLeaveHandlerBase : IGameModeLeaveHandler { protected readonly GameModeType m_game_mode_type; protected Player m_player; + protected IGameMode m_game_mode; + protected ServerInfo m_server_info; protected string m_room_id; + private List m_invokers; - public GameModeLeaveHandlerBase(Player player, string roomId, GameModeType gameModeType) + public GameModeLeaveHandlerBase(IGameMode gameMode, Player player, ServerInfo serverInfo, List invokers, string roomId, GameModeType gameModeType) { m_game_mode_type = gameModeType; m_player = player; + m_game_mode = gameMode; + m_server_info = serverInfo; m_room_id = roomId; + m_invokers = invokers; } public virtual async Task gameModeLeave() { var result = new Result(); - if (false == GameModeManager.It.tryGetGameMode(m_room_id, out var gameMode)) - { - var err_msg = $"battle_instance_room is null : room_id - {m_room_id}"; - Log.getLogger().error(err_msg); - result.setFail(ServerErrorCode.BattleInstanceInfoNotExist, err_msg); - return result; - } - - await removeUserFromInstanceRoom(m_player, gameMode); - await leaveGameZone(gameMode); - await removeHost(gameMode); + await removeUserFromInstanceRoom(m_player, m_game_mode); + await leaveGameMode(m_game_mode); + await removeHost(m_game_mode); - result = await postLeave(gameMode); + result = await postLeave(m_game_mode); if(result.isFail()) return result; - result = await notifyAfterLeave(gameMode); + result = await notifyAfterLeave(m_game_mode); if(result.isFail()) return result; - result = await logAfterLeave(gameMode); + result = await logAfterLeave(m_game_mode); if(result.isFail()) return result; - - await destroyCheck(gameMode); + await destroyCheck(m_game_mode); return result; } @@ -79,7 +77,7 @@ public abstract class GameModeLeaveHandlerBase : IGameModeLeaveHandler } } - public async Task leaveGameZone(IGameMode gameMode) + public async Task leaveGameMode(IGameMode gameMode) { var game_mode_base = GameModeHelper.toGameModeBase(gameMode); await Task.CompletedTask; @@ -108,7 +106,7 @@ public abstract class GameModeLeaveHandlerBase : IGameModeLeaveHandler var game_mode_base = GameModeHelper.toGameModeBase(gameMode); if (game_mode_base.getInstanceRoom().isDestroy) { - (var result, var destroy_handler) = GameModeHelper.getGameModeDestroyHandler(game_mode_base.getRoomId()); + (var result, var destroy_handler) = GameModeHelper.getGameModeDestroyHandler(game_mode_base); if (result.isSuccess() && destroy_handler is not null) { await destroy_handler.gameModeDestroy(); diff --git a/GameServer/Contents/GameMode/Manage/LeaveManage/IGameModeDisconnectHandler.cs b/GameServer/Contents/GameMode/Manage/LeaveManage/IGameModeDisconnectHandler.cs new file mode 100644 index 0000000..175fc34 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/LeaveManage/IGameModeDisconnectHandler.cs @@ -0,0 +1,6 @@ +namespace GameServer.Contents.GameMode.Manage.LeaveManage; + +public interface IGameModeDisconnectHandler +{ + Task gameModeDisconnect(); +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/LeaveManage/IPreparationForLeavingGameHandler.cs b/GameServer/Contents/GameMode/Manage/LeaveManage/IPreparationForLeavingGameHandler.cs new file mode 100644 index 0000000..4090b37 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/LeaveManage/IPreparationForLeavingGameHandler.cs @@ -0,0 +1,6 @@ +namespace GameServer.Contents.GameMode.Manage.LeaveManage; + +public interface IPreparationForLeavingGameHandler +{ + Task preparationForLeaving(); +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/LeaveManage/PreparationForLeavingGameBase.cs b/GameServer/Contents/GameMode/Manage/LeaveManage/PreparationForLeavingGameBase.cs new file mode 100644 index 0000000..11b0566 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/LeaveManage/PreparationForLeavingGameBase.cs @@ -0,0 +1,67 @@ +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using Nettention.Proud; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Manage.LeaveManage; + +public abstract class PreparationForLeavingGameBase : IPreparationForLeavingGameHandler +{ + protected IGameMode m_game_mode; + protected Player m_player; + + public PreparationForLeavingGameBase(Player player, IGameMode gameMode) + { + m_game_mode = gameMode; + m_player = player; + } + + public async Task preparationForLeaving() + { + //var result = await preparation();//나중에 필요할때 추가 + //if (result.isFail()) return result; + + //result = await postPreparationForLeaving(); //나중에 필요할때 추가 + //if (result.isFail()) return result; + + var result = await leavingHostCheck(); + if (result.isFail()) return result; + + await Task.CompletedTask; + return result; + } + + private async Task leavingHostCheck() + { + var result = new Result(); + var game_mod_base = m_game_mode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mod_base, () => $"game_mod_base is null !!!"); + var host_guid = game_mod_base.m_host_migrator.getHostUserGuid(); + var user_guid = m_player.getUserGuid(); + if (user_guid.Equals(host_guid)) + { + var hosts = new List(); + hosts.Add(m_player.getHostId()); + var p2p_group_id = game_mod_base.getInstanceRoom().getMap().getP2PGroupId(); + var define_result = game_mod_base.m_host_migrator.defineHost(p2p_group_id, new SuperPeerSelectionPolicy(), hosts.ToArray()); + if (define_result.isFail()) + { + Log.getLogger().warn(define_result.toBasicString()); + //이걸 define_result로 넘겨야 되나 말아야 되나....리팩토링 후에 고민좀 해보자... 만약 두명이 동시에 나간다고 하면?? + return define_result; + } + + var new_host_user_guid = game_mod_base.m_host_migrator.getHostUserGuid(); + GameNotifyHelper.broadcast_GS2C_NTF_P2P_HOST_UPDATE(game_mod_base.getInstanceRoom(), new_host_user_guid); + } + + await Task.CompletedTask; + return result; + } + + public abstract Task preparation(); + public abstract Task postPreparationForLeaving(); + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/Matching/IMatchingReservationHandler.cs b/GameServer/Contents/GameMode/Manage/Matching/IMatchingReservationHandler.cs new file mode 100644 index 0000000..5a48cc7 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/Matching/IMatchingReservationHandler.cs @@ -0,0 +1,8 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; + +namespace GameServer.Matching; + +public interface IMatchingReservationHandler +{ + Task matchingReservation(IGameMode gameMode, string userGuid, string roomGuid, string teamId); +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/Matching/MatchingReservationHandlerBase.cs b/GameServer/Contents/GameMode/Manage/Matching/MatchingReservationHandlerBase.cs new file mode 100644 index 0000000..8080520 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/Matching/MatchingReservationHandlerBase.cs @@ -0,0 +1,64 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using Newtonsoft.Json; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace GameServer.Matching; + +public abstract class MatchingReservationHandlerBase : IMatchingReservationHandler +{ + + public async Task matchingReservation(IGameMode gameMode, string userGuid, string roomId, string teamId) + { + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var result = new Result(); + var err_msg = string.Empty; + + if (false == int.TryParse(teamId, out var converted_teamId)) + { + err_msg = $"teamId is not int : {teamId}, room_id : {roomId}, user_guid : {userGuid}"; + result.setFail(ServerErrorCode.GameModeTeamIdParseError, err_msg); + Log.getLogger().error($"{result.toBasicString()}"); + return result; + } + + using (var releaser = await game_mode_base.getAsyncLock()) + { + var teams = game_mode_base.getGameRoomInfo().Teams; + var team_idx = converted_teamId - 1; + var team_info = game_mode_base.getGameRoomInfo().Teams[team_idx]; + if (team_info is null) + { + err_msg = $"Teams[{team_idx}] is not exist : {teamId}, room_id : {roomId}, user_guid : {userGuid}, teams : {JsonConvert.SerializeObject(teams)}"; + result.setFail(ServerErrorCode.GameModeTeamIdParseError, err_msg); + Log.getLogger().error($"{result.toBasicString()}"); + return result; + } + + var add_team_info = new GameRoomInfo.TeamInfo() + { + Idx = team_idx, + Id = teamId, + UserGuid = userGuid + }; + + GameRoomInfo.TeamInfo[] new_teams = new GameRoomInfo.TeamInfo[teams.Length + 1]; + for (var i = 0; i < teams.Length; i++) + { + new_teams[i] = teams[i];//일단 이렇게 하자. + } + new_teams[teams.Length] = add_team_info; + + result = await detailMatchingReservation(gameMode, userGuid, roomId, teamId); + if (result.isFail()) return result; + } + + return result; + } + + public abstract Task detailMatchingReservation(IGameMode gameMode, string userGuid, string roomGuid, string teamId); + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/GameModeBase.cs b/GameServer/Contents/GameMode/Manage/PlayManage/GameModeBase.cs index 340db4a..be23326 100644 --- a/GameServer/Contents/GameMode/Manage/PlayManage/GameModeBase.cs +++ b/GameServer/Contents/GameMode/Manage/PlayManage/GameModeBase.cs @@ -1,66 +1,136 @@ -using Amazon.DynamoDBv2.Model; +using System.Collections.Concurrent; +using GameServer.Contents.GameMode.Action; using GameServer.Contents.GameMode.Helper; using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Matching; +using MetaAssets; using NeoSmart.AsyncLock; +using Newtonsoft.Json; using ServerBase; using ServerCommon; +using ServerCommon.GameMode; using ServerCore; +using USER_GUID = System.String; +using ANCHOR_GUID = System.String; + namespace GameServer.Contents.GameMode.Manage.PlayManage; public abstract class GameModeBase : EntityBase, IGameMode, IWithLogActor { - protected bool m_is_init_completed = false; - protected InstanceRoom m_instance_room; - public readonly IHostMigrator m_host_migrator; - protected EntityTicker? m_ticker; - protected Int32 m_ticker_interval_msecs = GameModeConstants.GAME_MODE_DEFAULT_INTERVAL_MSECS; - protected IGameModeState? m_current_game_mode_state; //kihoon todo: 이거 수정해야된다. IGameMode를 받으면 안될듯...아니면 받더래도 + //todo : 리팩토링. LoadMetaHandler 만들것.. + //todo : 리팩토링. 생성부분 builders패턴으로 하면 좋을듯.. - public GameModeBase(EntityType type, InstanceRoom instanceRoom) : base(type) + //Game Management + private int m_game_mode_id = 0; + protected InstanceRoom m_instance_room; + protected bool m_is_init_completed = false; + protected DateTime m_game_create_time = DateTimeHelper.Current; + public readonly IHostMigrator m_host_migrator; + protected GameRoomInfo? m_game_room_info = null; + + //todo: meta, event meta를 관리할 핸들러 만들어서 관리... + protected readonly GameModeAllMeta m_game_mode_all_meta; + + //Matching Management + private readonly IMatchingReservationHandler m_matching_state_event_handler; + private MatchGameMemberJoinDelegate m_matching_game_member_join_delegate; + + //State Management + protected IGameModeState m_current_game_mode_state; + private readonly IGameModeTransitionStrategy m_state_transition_strategy; + + //Task Ticker Management + protected Int32 m_ticker_interval_msecs = GameModeConstants.GAME_MODE_DEFAULT_INTERVAL_MSECS; + protected EntityTicker? m_ticker; + + //User Management + protected ConcurrentDictionary m_users_state = new();//todo : user의 팀이나 전체적인 유저 상태 관리를 위한 객체 필요 + + //Ready Pos + //todo meta_handler 에서 관리 해주면 좋을듯.. + protected ConcurrentDictionary m_ready_pos = new(); + + //Start Pos + protected ConcurrentDictionary m_start_pos = new(); + + public GameModeBase(EntityType type, InstanceRoom instanceRoom, int gameModeId, IGameModeTransitionStrategy strategy) : base(type) { m_is_init_completed = false; m_instance_room = instanceRoom; - ArgumentNullReferenceCheckHelper.throwIfNull(instanceRoom, () => $"GameModeBase constructor instanceRoom is null !!!"); - m_host_migrator = HostMigrationFactory.createCommonHostMigrator(1, GameServerApp.getServerLogic()); //kihoon todo : gamemodeID를 넘겨야 한다; + m_game_mode_id = gameModeId; + m_state_transition_strategy = strategy; + MetaData.Instance.m_game_mode_all_metas.TryGetValue(gameModeId, out var metaData); + NullReferenceCheckHelper.throwIfNull(metaData, () => $"metaData is null !!!"); + m_game_mode_all_meta = metaData; + + m_host_migrator = HostMigrationFactory.createCommonHostMigrator(gameModeId, GameServerApp.getServerLogic()); + m_current_game_mode_state = new GameModeStateNone(this); + changeState(m_current_game_mode_state).Wait(); + + m_matching_state_event_handler = GameModeHelper.getMatchingStateEventHandler(type); + m_matching_game_member_join_delegate += GameMemberJoinReserve; + MatchManager.It.RoomEventHandler.AddGameMemberJoinReserveAction(instanceRoom.getMap().m_room_id, m_matching_game_member_join_delegate); } - + public override async Task onInit() { var result = new Result(); - - //action 추가 + + //GameMode에 필요한 action 추가 addEntityActions(); - //GameModeBase 쪽 로직 더 넣을것 있으면 여기에 - + //base초기화 - 추가된 attributes 와 actions에 대한 init 처리, 호출 안하면 수동으로 처리 필요 result = await base.onInit(); if (result.isFail()) return result; + //ticker 생성 전 초기화 + result = await initBeforeTaskCreate(); + if (result.isFail()) return result; - //init이 다 되면 ticker 만든다. + + + //ticker 생성 result = await createTask(); if (result.isFail()) return result; - //타이머 생성후 초기해야될 것이 있으면 한다. - await initAfterTimerCreate(); + //ticker 생성 후 초기화 + result = await initAfterTaskCreate(); + if (result.isFail()) return result; - m_is_init_completed = true; + //초기화 완료 + m_is_init_completed = true; + + //호스트 처리 + m_host_migrator.setGroupHostId(m_instance_room.getMap().getP2PGroupId()); + + //초기화 후 필요한 셋팅 처리 + result = await setGameInfoAfterLoadComplete(); + if (result.isFail()) return result; return result; } - - public void addEntityActions() + #region EntityActions 추가 + private void addEntityActions() { - - } - - public string getRoomId() - { - return m_instance_room.getMap().m_room_id; + addCommonEntityActions(); + addDetailEntityActions(); } + private void addCommonEntityActions() + { + //addEntityAction(new GameModePreUpdateAction(this));//나중에 필요하면 넣자 + addEntityAction(new GameModePostUpdateAction(this));//나중에 필요하면 넣자 + addEntityAction(new GameModeStateCheckAction(this)); + addEntityAction(new GameModePositionSetAction(this)); + + } + protected abstract void addDetailEntityActions(); + #endregion + + + #region GameLogic Task 생성 private async Task createTask() { var result = new Result(); @@ -69,8 +139,6 @@ public abstract class GameModeBase : EntityBase, IGameMode, IWithLogActor NullReferenceCheckHelper.throwIfNull(m_ticker, () => $"m_ticker is null !!! - id"); await m_ticker.onInit(); - await Task.CompletedTask; - return result; } @@ -83,7 +151,6 @@ public abstract class GameModeBase : EntityBase, IGameMode, IWithLogActor private async Task gameModeTaskTick() { if (!m_is_init_completed) return; - try { await taskUpdate(); @@ -93,36 +160,137 @@ public abstract class GameModeBase : EntityBase, IGameMode, IWithLogActor Log.getLogger().error($"Exception !!!, new gameModeTaskTick() : exception:{e}, basicString : {toBasicString()}"); } } + #endregion + + #region TaskCreate 이후 필요한 초기화 처리 - public abstract Task taskUpdate(); - public abstract Task initAfterTimerCreate(); - - public override string toBasicString() + private async Task initBeforeTaskCreate() { - var basic_string = base.toBasicString() + $"GameModeBase.... room_id : {getRoomId()}"; - return basic_string; + var result = await loadGameRoomInfo(); + if (result.isFail()) return result; + + result = await detailInitBeforeTaskCreate(); + if (result.isFail()) return result; + + setAnchorInfos(); + + return result; } - public bool isLoadCompleted() + protected abstract Task detailInitBeforeTaskCreate(); + + private void setAnchorInfos() { - return m_is_init_completed; + foreach (var anchors in m_instance_room.getMap().getAnchors()) + { + + if (false == MapDataTable.Instance.getAnchor(anchors.Key, out var found_anchor)) + { + var err_msg = $"Not found Anchor in MapDataTable !!! : anchorMetaGuid:{anchors.Key}"; + Log.getLogger().error(err_msg); + continue; + } + + if (found_anchor.Type == "AT_ReadyPos") + { + setReadyPos(anchors.Key, found_anchor); + } + + if (found_anchor.Type == "AT_StartPos") + { + setStartPos(anchors.Key, found_anchor); + } + setDetailAnchorInfos(anchors.Key, found_anchor); + } } + private void setReadyPos(string anchorGuid, Anchor anchor) + { + m_ready_pos.TryAdd(anchorGuid, anchor); + } + private void setStartPos(string anchorGuid, Anchor anchor) + { + m_start_pos.TryAdd(anchorGuid, anchor); + } + + protected abstract void setDetailAnchorInfos(string anchorGuid, Anchor anchor); + #endregion - - public IGameModeState getGameModeState() + + + #region TaskCreate 이후 필요한 초기화 처리 + private async Task initAfterTaskCreate() { - NullReferenceCheckHelper.throwIfNull(m_current_game_mode_state, () => $"m_current_game_mode_state is null !!"); - return m_current_game_mode_state; + var result = await baseInitAfterTaskCreate(); + if (result.isFail()) return result; + + result = await detailInitAfterTaskCreate(); + if (result.isFail()) return result; + return result; + } + private async Task baseInitAfterTaskCreate() + { + var result = new Result(); + await Task.CompletedTask; + + return result; + } + protected abstract Task detailInitAfterTaskCreate(); + + #endregion + + #region TaskUpdate 관련 Method + private async Task taskUpdate() + { + //base에서 필요한 로직 처리.. 나중에 필요할때 넣을것 + //var result = await preBaseTaskUpdate(); + //if (result.isFail()) return; + + //2. child에 대한 로직 처리 + var result = await deatilTaskUpdate(); //state하고 상관 없는 공통로직 처리 + if (result.isFail()) return; + + //4. state update 로직 처리 + var state_check_action = getEntityAction(); + NullReferenceCheckHelper.throwIfNull(state_check_action, () => $"state_check_action is null !!"); + result = await state_check_action.stateUpdate(); + if (result.isFail()) return; + + + //base에서 필요한 로직 처리.. 여기 host migration이있다 + result = await postBaseTaskUpdate(); + if (result.isFail()) return; + + } + protected abstract Task deatilTaskUpdate(); + + + #endregion + + #region LoadComplete 후 처리 + private async Task setGameInfoAfterLoadComplete() + { + var result = await setBaseGameInfoAfterLoadComplete(); + if (result.isFail()) return result; + + result = await setDetailGameInfoAfterLoadComplete(); + if (result.isFail()) return result; + + return result; } - public void setGameModeState(IGameModeState state) + private async Task setBaseGameInfoAfterLoadComplete() { - m_current_game_mode_state = state; + var result = new Result(); + m_game_create_time = DateTimeHelper.Current; + + await Task.CompletedTask; + return result; } - public InstanceRoom getInstanceRoom() => m_instance_room; - public EntityTicker? getEntityTicker() => m_ticker; + protected abstract Task setDetailGameInfoAfterLoadComplete(); + #endregion + public ILogActor toLogActor() { var server_logic = GameServerApp.getServerLogic(); @@ -132,4 +300,251 @@ public abstract class GameModeBase : EntityBase, IGameMode, IWithLogActor var log_info = new BattleInstanceActorLog(region_id, server_name, getRoomId(), m_instance_room.getInstanceId()); return log_info; } + + public IGameModeState? loadNextState(IGameModeState currentState) + { + return m_state_transition_strategy.tryGetNextState(currentState, this); + } + + public async Task changeState(IGameModeState newState) + { + Log.getLogger().debug($"changeState logic Start RoomId : {getRoomId()}, gameModeId : {getGameModeId()}, newState : {newState.getStateType()}" ); + // 1. 기존 State 정리 및 이벤트 해제 + if (m_current_game_mode_state is null) + { + Log.getLogger().error($"game mode state is null !!! gameModeId : {getGameModeId()}, newState : {newState.getStateType()}" ); + return; + } + + var histories = m_current_game_mode_state.getStateChangeHistories().ToList(); + await m_current_game_mode_state.exit(); + Log.getLogger().debug($"changeState logic state Exit Done RoomId : {getRoomId()}, gameModeId : {getGameModeId()}, newState : {newState.getStateType()}" ); + // GameModeStateBase의 이벤트 해제 + if (m_current_game_mode_state is not GameModeStateBase currentStateBase) + { + Log.getLogger().error($"currentStateBase is null !!! gameModeId : {getGameModeId()}, newState : {newState.getStateType()}" ); + return; + } + currentStateBase.m_state_change_requested -= stateChangeRequested; + + // 2. 새 State 설정 + m_current_game_mode_state = newState; + + // 3. 새 State 이벤트 등록 + if (newState is not GameModeStateBase newStateBase) + { + Log.getLogger().error($"newStateBase is null !!! gameModeId : {getGameModeId()}, newState : {newState.getStateType()}" ); + return; + } + newStateBase.m_state_change_requested += stateChangeRequested; + + // 4. 새 State 진입 + await m_current_game_mode_state.enter(this); + + Log.getLogger().debug($"changeState logic state enter Done RoomId : {getRoomId()}, gameModeId : {getGameModeId()}, newState : {newState.getStateType()}" ); + m_current_game_mode_state.getStateChangeHistories().AddRange(histories); + } + + public void GameMemberJoinReserve(string userGuid, string roomId, string teamId) + { + m_matching_state_event_handler.matchingReservation(this, userGuid, roomId, teamId).Wait(); + } + + public void deleteGameMemberJoinReservation() + { + MatchManager.It.RoomEventHandler.DeleteGameMemberJoinReserveAction(getRoomId()); + m_matching_game_member_join_delegate = (string userGuid, string roomId, string teamId) => { }; + } + + private void stateChangeRequested(IGameModeState nextState) + { + changeState(nextState).Wait(); + } + + public void registUserState(USER_GUID userGuid) + { + m_users_state.AddOrUpdate(userGuid, GameModePlayerState.Join, (key, oldValue) => GameModePlayerState.Join); + } + + public void updateUserState(USER_GUID userGuid, GameModePlayerState state) + { + m_users_state.AddOrUpdate(userGuid, state, (key, oldValue) => state); + } + + public List getLoadCompletedUserGuids() + { + return m_users_state + .Where(pair => pair.Value == GameModePlayerState.LoadComplete) + .Select(pair => pair.Key) + .ToList(); + } + + public List getNotLoadedUserGuids() + { + return m_users_state + .Where(pair => pair.Value < GameModePlayerState.LoadComplete) + .Select(pair => pair.Key) + .ToList(); + } + + public void removePlayUserState(string userGuid) + { + m_users_state.TryRemove(userGuid, out _); + } + + public string getRoomId() => m_instance_room.getMap().m_room_id; + public bool isLoadCompleted() => m_is_init_completed; + public InstanceRoom getInstanceRoom() => m_instance_room; + public EntityTicker? getEntityTicker() => m_ticker; + public DateTime getGameCreateTime() => m_game_create_time; + public override string toBasicString() => base.toBasicString() + $"GameModeBase.... room_id : {getRoomId()}"; + + public IGameModeState getState() + { + NullReferenceCheckHelper.throwIfNull(m_current_game_mode_state, () => $"m_current_game_mode_state is null !!!"); + return m_current_game_mode_state; + } + + public int getGameModeId() => m_game_mode_id; + public Task<(Result, StateUpdateReasonType)> isGameSustainable() + { + var result = new Result(); + if (false == playableUserCountCheck()) + { + result.setFail(ServerErrorCode.GameModeNotEnoughPlayableUserCount); + return Task.FromResult((result, StateUpdateReasonType.NotEnoughPlayableUser)); + } + + //나중에 게임 타입별로 체크할것 있으면 여기에 추가 + + return Task.FromResult((result, StateUpdateReasonType.None)); + } + + + private bool playableUserCountCheck() + { + var current_users = getInstanceRoom().getInstanceMemberGuids(); + + var team_infos = getGameRoomInfo().Teams.ToList(); + + var check_users_team_info = new Dictionary>(); + + foreach (var team_info in team_infos) + { + if (false == current_users.Contains(team_info.UserGuid)) + { + Log.getLogger().warn($"{team_info.UserGuid} user exist in team info.. but not exist current users"); + continue; + } + + if (false == check_users_team_info.TryGetValue(team_info.Idx, out var teamList)) + { + teamList = new List(); + } + teamList.Add(team_info.UserGuid); + check_users_team_info[team_info.Idx] = teamList; + } + + var current_team_count = check_users_team_info.Count; + var playable_min_team_count = getGameRoomInfo().MinTeamCount; + + // 최소 팀 수보다 팀이 적으면 false + if (current_team_count < playable_min_team_count) + { + Log.getLogger().error($"Not enough team count !!! : roomId : {getRoomId()}, minTeamCount : {playable_min_team_count}, current_team_count : {current_team_count}"); + return false; + } + + //todo : 추후 ms에대해서 draw 상태나 팀전 있을때 활성화 한다. + // var team_policies = getGameRoomInfo().TeamPolicies; + // // 팀 인덱스 기준으로 정책 확인 + // var arr_team_policies = getGameRoomInfo().TeamPolicies; + // for (int i = 0; i < arr_team_policies.Length; i++) + // { + // if (false == check_users_team_info.ContainsKey(i)) + // { + // Log.getLogger().warn($"Not found team info !!! : roomId : {getRoomId()}, teamIdx : {i}, totatlTeam info : {JsonConvert.SerializeObject(check_users_team_info)}"); + // //이게 여기서 continue를 하는게 맞나? + // continue; + // } + // + // if (check_users_team_info[i].Count < arr_team_policies[i].MinMemberCount) + // { + // Log.getLogger().error($"Not enough team member count !!! : roomId : {getRoomId()}, minTeamMemberCount : {arr_team_policies[i].MinMemberCount}, " + + // $"current_team_count : {check_users_team_info[i].Count}"); + // //해당 팀이 들어왔는데 minteamcount 보다 적기 때문에 false + // return false; + // } + // } + return true; + + } + + public bool isUserLoadingCompleteAll() + { + return m_users_state.All(user => user.Value == GameModePlayerState.LoadComplete); + } + + public ConcurrentDictionary getUserState() => m_users_state; + + + + public ConcurrentDictionary getReadyPos() => m_ready_pos; + + + private async Task loadGameRoomInfo() + { + var storage = new GameInstanceRoomStorage(); + storage.Init(GameServerApp.getServerLogic().getRedisDb(), ""); + (var result, var game_room_info) = await storage.GetInstanceRoomGameInfoAsync(m_instance_room.getMap().m_room_id); + if (result.isFail()) return result; + NullReferenceCheckHelper.throwIfNull(game_room_info, () => $"game_room_info is null !!!"); + + m_game_room_info = game_room_info; + return result; + } + + public GameRoomInfo getGameRoomInfo() + { + NullReferenceCheckHelper.throwIfNull(m_game_room_info, () => $"m_game_room_info is null !!!"); + return m_game_room_info; + } + + public Int32 getMinPlayableUserCount() + { + var game_room_info = getGameRoomInfo(); + NullReferenceCheckHelper.throwIfNull(game_room_info.TeamPolicies, () => $"game_room_info.TeamPolicies is null !!!"); + var total_min_cnt = game_room_info.TeamPolicies.Sum(policy => policy.MinMemberCount); + return total_min_cnt; + } + + public GameModeType getGameModeType() + { + return GameModeHelper.convertEntityTypeToGameModeType(getEntityType()); + } + + //나중에 필요할때 넣을것 + // private async Task preBaseTaskUpdate() + // { + // var pre_update_action = getEntityAction(); + // NullReferenceCheckHelper.throwIfNull(pre_update_action, () => $"pre_update_action is null !!! - id"); + // + // var result = await pre_update_action.update(); + // if (result.isFail()) return result; + // + // return result; + // } + + private async Task postBaseTaskUpdate() + { + var post_update_action = getEntityAction(); + NullReferenceCheckHelper.throwIfNull(post_update_action, () => $"post_update_action is null !!! - id"); + var result = await post_update_action.update(); + if (result.isFail()) return result; + + return result; + } + + public GameModeAllMeta getMetas() => m_game_mode_all_meta; + public ConcurrentDictionary getStartPos() => m_start_pos; } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/GameModeLifeCycleTicker.cs b/GameServer/Contents/GameMode/Manage/PlayManage/GameModeLifeCycleTicker.cs index ed3f45f..a14b6be 100644 --- a/GameServer/Contents/GameMode/Manage/PlayManage/GameModeLifeCycleTicker.cs +++ b/GameServer/Contents/GameMode/Manage/PlayManage/GameModeLifeCycleTicker.cs @@ -16,15 +16,11 @@ public class GameModeLifeCycleTicker : EntityTicker } public override async Task onTaskTick() { - 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(); - var seasonPassManager = server_logic.getSeasonPassManager(); if (true == server_config.PerformanceCheckEnable) { diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/GameModeManager.cs b/GameServer/Contents/GameMode/Manage/PlayManage/GameModeManager.cs index 01f150f..ac5e9c7 100644 --- a/GameServer/Contents/GameMode/Manage/PlayManage/GameModeManager.cs +++ b/GameServer/Contents/GameMode/Manage/PlayManage/GameModeManager.cs @@ -1,7 +1,9 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; +using GameServer.Contents.GameMode; using GameServer.Contents.GameMode.Helper; using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; using ServerCore; using NeoSmart.AsyncLock; using ServerBase; @@ -13,32 +15,44 @@ public class GameModeManager : Singleton { private AsyncLock m_lock = new(); private ConcurrentDictionary m_game_modes = new(); /**/ - + public async Task onInit() { - //1. 이벤트 관리하는 Task 추가 - //var result = await createBattleEventCheckerTask(); //kihoon todo : 이부분은 GameModeEventCheckerTask라는 걸로 변경 처리 해준다. 여기서 처리 안해줄수 도 있다. await Task.CompletedTask; } - - public async Task createGameMode(InstanceRoom instanceRoom, GameModeType gameModeType) + public async Task gameModePlayRegulationCheck(Player player) { - (var result, var game_mode) = GameModeHelper.createGameMode(instanceRoom, gameModeType); + var regulation_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(regulation_action, + () => $"GameModePlayRegulationAction is null !!! - {player.toBasicString()}"); + await regulation_action.refresh(); + + GameNotifyHelper.send_S2C_NTF_GAME_MODE_PLAY_REGULATION(player); + } + + + public async Task createGameMode(InstanceRoom instanceRoom, GameModeType gameModeType, int gameModeId) + { + (var result, var game_mode) = GameModeHelper.createGameMode(instanceRoom, gameModeType, gameModeId); if (result.isFail()) return result; if (game_mode is null) { - var err_msg = $"game_mode is null !!!! gameModeType : {gameModeType}, instanceRoomId : {instanceRoom.getMap().m_room_id}"; + var err_msg = + $"game_mode is null !!!! gameModeType : {gameModeType}, instanceRoomId : {instanceRoom.getMap().m_room_id}"; Log.getLogger().error(err_msg); result.setFail(ServerErrorCode.GameModeClassIsNull, err_msg); return result; } + var room_id = instanceRoom.getMap().m_room_id; - + if (false == tryAdd(room_id, game_mode)) { //이미 들어가져 있으므로 로그만 남겨놓고 리턴 - Log.getLogger().warn($"game_mode already exist...... gameModeType : {gameModeType}, instanceRoomId : {instanceRoom.getMap().m_room_id}"); + Log.getLogger() + .warn( + $"game_mode already exist...... gameModeType : {gameModeType}, instanceRoomId : {instanceRoom.getMap().m_room_id}"); } await Task.CompletedTask; @@ -51,21 +65,72 @@ public class GameModeManager : Singleton if (ret == false) { - var err_msg = $"gameMode is null : room_id - {roomId}"; + var err_msg = $"tryGetGameMode gameMode is null : room_id - {roomId}"; Log.getLogger().error(err_msg); } return ret; } - public bool tryRemoveGameMode(string roomId, [MaybeNullWhen(false)] out IGameMode gameMode) + public bool tryRemoveGameMode(string roomId, string callHistory, [MaybeNullWhen(false)] out IGameMode gameMode) { + Log.getLogger().info($"tryRemoveGameMode : roomId : {roomId}, callHistory : {callHistory}"); return m_game_modes.TryRemove(roomId, out gameMode); } public bool tryAdd(string roomId, IGameMode gameMode) { + Log.getLogger().info($"tryAddGameMode : roomId : {roomId}"); return m_game_modes.TryAdd(roomId, gameMode); } - -} \ No newline at end of file + + public bool isGameModeRoom(string roomId) + { + return m_game_modes.ContainsKey(roomId); + } + + public async Task LeaveGameModeRoom(Player player, string roomId) + { + if (false == tryGetGameMode(roomId, out var gameMode)) + { + Log.getLogger().warn($"LeaveGameModeRoom gameMode is null : room_id - {roomId}"); + return; + } + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var game_mode_disconnect_handler = GameModeHelper.getGameModeDisconnectHandler(game_mode_base, player); + + var result = await game_mode_disconnect_handler.gameModeDisconnect(); + if (result.isFail()) return; + + + } + + public async Task resetMatchCount(Player player, GameModeType gameModeType) + { + var regulation_action = player.getEntityAction(); + var result = await regulation_action.resetMatchCount(gameModeType); + if (result.isFail()) + { + Log.getLogger().error($"resetMatchCount failed !!! - {player.toBasicString()}"); + return result; + } + GameNotifyHelper.send_S2C_NTF_GAME_MODE_PLAY_REGULATION(player); + return new Result(); + } + + public async Task resetPlayPenalty(Player player, GameModeType gameModeType) + { + var regulation_action = player.getEntityAction(); + var result = await regulation_action.resetPlayPenalty(gameModeType); + if (result.isFail()) + { + Log.getLogger().error($"resetMatchCount failed !!! - {player.toBasicString()}"); + return result; + } + GameNotifyHelper.send_S2C_NTF_GAME_MODE_PLAY_REGULATION(player); + return new Result(); + } +} + diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/GameObjectnteractionDataBase.cs b/GameServer/Contents/GameMode/Manage/PlayManage/GameObjectnteractionDataBase.cs new file mode 100644 index 0000000..57b961e --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/PlayManage/GameObjectnteractionDataBase.cs @@ -0,0 +1,14 @@ +using MetaAssets; + +namespace GameServer.Contents.GameMode.Manage.PlayManage; + +public class GameObjectnteractionDataBase : IGameObjectnteractionDataHandler +{ + public EGameModeObjectType m_game_object_type { get; private set; } + + public GameObjectnteractionDataBase(EGameModeObjectType gameObjectType) + { + m_game_object_type = gameObjectType; + } + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/IDeadHandler.cs b/GameServer/Contents/GameMode/Manage/PlayManage/IDeadHandler.cs index 88e6d75..1b8849d 100644 --- a/GameServer/Contents/GameMode/Manage/PlayManage/IDeadHandler.cs +++ b/GameServer/Contents/GameMode/Manage/PlayManage/IDeadHandler.cs @@ -2,5 +2,6 @@ public interface IDeadHandler { - + Task dead(Player player, string deadUserGuid, string killerUserGuid, GameModeDeadType deadType, DateTime deadTime); + void notifyAfterDead(Player player); } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/IGameMode.cs b/GameServer/Contents/GameMode/Manage/PlayManage/IGameMode.cs index 7d7787f..5e9eecb 100644 --- a/GameServer/Contents/GameMode/Manage/PlayManage/IGameMode.cs +++ b/GameServer/Contents/GameMode/Manage/PlayManage/IGameMode.cs @@ -1,6 +1,12 @@ -namespace GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; + +namespace GameServer.Contents.GameMode.Manage.PlayManage; public interface IGameMode { - -} \ No newline at end of file + int getGameModeId(); + Task<(Result, StateUpdateReasonType)> isGameSustainable(); + GameModeType getGameModeType(); +} + + diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/IGameModeLoadCompleteHandler.cs b/GameServer/Contents/GameMode/Manage/PlayManage/IGameModeLoadCompleteHandler.cs new file mode 100644 index 0000000..ceebec4 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/PlayManage/IGameModeLoadCompleteHandler.cs @@ -0,0 +1,6 @@ +namespace GameServer.Contents.GameMode.Manage.PlayManage; + +public interface IGameModeLoadCompleteHandler +{ + Task loadComplete(Player player); +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/IGameObject.cs b/GameServer/Contents/GameMode/Manage/PlayManage/IGameObject.cs deleted file mode 100644 index 49dba80..0000000 --- a/GameServer/Contents/GameMode/Manage/PlayManage/IGameObject.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace GameServer.Contents.GameMode.Manage.PlayManage; - -public interface IGameObject -{ - Task interact(string anchorGuid, DateTime interactionTime); -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/IGameObjectInteractionHandler.cs b/GameServer/Contents/GameMode/Manage/PlayManage/IGameObjectInteractionHandler.cs new file mode 100644 index 0000000..c28b3df --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/PlayManage/IGameObjectInteractionHandler.cs @@ -0,0 +1,10 @@ +using GameServer.Contents.GameMode.InteractionObject; + +namespace GameServer.Contents.GameMode.Manage.PlayManage; + +public interface IGameObjectInteractionHandler +{ + Task interact(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler dataHandler); + + void notifyAfterInteract(Player player, IGameObjectInteractionDataHandler dataHandler); +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/IGameObjectnteractionDataHandler.cs b/GameServer/Contents/GameMode/Manage/PlayManage/IGameObjectnteractionDataHandler.cs new file mode 100644 index 0000000..b2bc3f9 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/PlayManage/IGameObjectnteractionDataHandler.cs @@ -0,0 +1,6 @@ +namespace GameServer.Contents.GameMode.Manage.PlayManage; + +public interface IGameObjectnteractionDataHandler +{ + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/PlayManage/IRespawnHandler.cs b/GameServer/Contents/GameMode/Manage/PlayManage/IRespawnHandler.cs new file mode 100644 index 0000000..f4dd4ba --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/PlayManage/IRespawnHandler.cs @@ -0,0 +1,7 @@ +namespace GameServer.Contents.GameMode.Manage.PlayManage; + +public interface IRespawnHandler +{ + Task respawn(Player player, DateTime deadTime); + void notifyAfterRespawn(Player player); +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/StateManage/GameModeStateBase.cs b/GameServer/Contents/GameMode/Manage/StateManage/GameModeStateBase.cs index 83245af..89c5842 100644 --- a/GameServer/Contents/GameMode/Manage/StateManage/GameModeStateBase.cs +++ b/GameServer/Contents/GameMode/Manage/StateManage/GameModeStateBase.cs @@ -1,42 +1,163 @@ -using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MongoDB.Bson.Serialization.Conventions; +using Newtonsoft.Json; using ServerCore; namespace GameServer.Contents.GameMode.Manage.StateManage; -public abstract class GameModeStateBase : IGameModeState +public abstract class GameModeStateBase : IGameModeState, IGameModeStateManager { - protected IGameMode m_game_mode; - protected GameModeBase m_game_mode_base; - private GameModeState m_state = GameModeState.None; - protected DateTime m_next_state_change_time; - - - public GameModeStateBase(IGameMode gameMode, GameModeState state) + private GameModeState m_state_type = GameModeState.None; + protected DateTime m_old_time = DateTimeHelper.Current; + protected DateTime m_next_state_change_time = DateTimeHelper.Current; + protected IGameMode m_current_game_mode; + private StateUpdateReasonType m_state_update_reason = StateUpdateReasonType.None; + private readonly List m_state_change_histories = new(); + public event Action? m_state_change_requested; + + public GameModeStateBase(GameModeState stateType, IGameMode gameMode, StateUpdateReasonType updateReason = StateUpdateReasonType.NormalFlow) { - m_game_mode = gameMode; - var game_mode_base = m_game_mode as GameModeBase; - NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!"); - m_game_mode_base = game_mode_base; + m_state_type = stateType; + m_current_game_mode = gameMode; + m_state_update_reason = updateReason; + } + + + protected void requestStateChange(IGameModeState nextState) + { + if (m_state_change_requested is null) + { + Log.getLogger().error("m_state_change_requested is null!!!!!!"); + return; + } + m_state_change_requested?.Invoke(nextState); + } + + public async Task enter(IGameMode gameMode) + { + await enterDetail(); + + GameNotifyHelper.broadcast_GS2C_NTF_GAME_STATE_UPDATE(gameMode, getStateType(), m_next_state_change_time, getUpdateReason()); + + //게임별로 보내야 되는 Notify 보낸다. + notifyDetail(); + + sendStateToMatchServer(); + + writeBusinessLog(); + + await postProcess(); + } + + public abstract Task enterDetail(); + public abstract void notifyDetail(); + public abstract void writeBusinessLog(); + public abstract Task postProcess(); + + private void sendStateToMatchServer() + { + var game_mode_base = m_current_game_mode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + var entityType = game_mode_base.getEntityType(); + var match_state_type = GameModeHelper.convertGameStateToMatchState(entityType, getStateType()); + MatchManager.It.RoomEventHandler.ChangeGameState(game_mode_base.getRoomId(), match_state_type); + } + + public async Task exit() + { + await exitDetail(); + notifyDetailAfterExit(); + } + public abstract Task exitDetail(); + public abstract void notifyDetailAfterExit(); + + public async Task update(IGameMode gameMode) + { + //해당 상태에서 업데이트 해줘야 될것 확인 + await updateDetail(); + + //상태 변경 체크 + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + var next = game_mode_base.loadNextState(this); + if (next is not null) + { + requestStateChange(next); + } + } + + public abstract Task updateDetail(); + + public void extendStateTime(TimeSpan extension, string reason, bool notify = false) + { + var newTime = m_next_state_change_time.Add(extension); + setNextStateChangeTime(newTime, "ExtendTime", reason, notify); + + } + + public void setStateTime(DateTime newTime, string reason, bool notify = false) + { + setNextStateChangeTime(newTime, "SetTime", reason, notify); + } + + private void setNextStateChangeTime(DateTime newTime, string action, string reason, bool notify = false) + { + var oldTime = m_next_state_change_time; + m_old_time = oldTime; + m_next_state_change_time = newTime; + + // 모든 후처리 한 번에 + addStateChangeHistory(action, reason, oldTime, newTime); + + if (notify) + { + notifyStateTimeChanged(newTime); + } + - m_next_state_change_time = DateTimeHelper.Current.AddSeconds(10); //kihoon todo: 대기시간은 나중에 meta로 반영해야 된다. - m_state = state; } - - public abstract void enter(); - public abstract void update(); - public abstract void exit(); - public abstract GameModeState checkState(); - - public GameModeState getStateType() + + private void notifyStateTimeChanged(DateTime newTime) { - return m_state; + if (m_current_game_mode != null) + { + GameNotifyHelper.broadcast_GS2C_NTF_GAME_STATE_UPDATE( + m_current_game_mode, + getStateType(), + newTime, + getUpdateReason() + ); + } + } + + private void addStateChangeHistory(string action, string reason, DateTime oldTime, DateTime newTime) + { + m_state_change_histories.Add(new StateChangeHistory() + { + m_time = DateTimeHelper.Current, + m_action = action, + m_reason = reason, + m_old_time = oldTime, + m_new_time = newTime + }); + var game_mode_base = m_current_game_mode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + Log.getLogger().info($"state change history added... gameModeId : {m_current_game_mode.getGameModeId()}, " + + $"roomId : {game_mode_base.getGameRoomInfo().Id}, total Histories : {JsonConvert.SerializeObject(m_state_change_histories)}, " + + $"current users : {JsonConvert.SerializeObject(game_mode_base.getInstanceRoom().getInstanceMemberGuids())}"); } - public DateTime getNextStateChangeTime() - { - return m_next_state_change_time; - } + public GameModeState getStateType() => m_state_type; + public DateTime getNextStateChangeTime() => m_next_state_change_time; + public IReadOnlyList getStateChangeHistrory() => m_state_change_histories; + protected IGameMode getGameMode() => m_current_game_mode!; + + public StateUpdateReasonType getUpdateReason() => m_state_update_reason; + + public List getStateChangeHistories() => m_state_change_histories; } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/StateManage/GameModeStateNone.cs b/GameServer/Contents/GameMode/Manage/StateManage/GameModeStateNone.cs new file mode 100644 index 0000000..fcb414a --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/StateManage/GameModeStateNone.cs @@ -0,0 +1,46 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Manage.StateManage; + +public class GameModeStateNone : GameModeStateBase +{ + public GameModeStateNone(IGameMode gameMode) : base(GameModeState.None, gameMode) + { + + } + + public override Task enterDetail() + { + Log.getLogger().info("GameModeStateNone enter called"); + + return Task.CompletedTask; + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + public override void notifyDetailAfterExit() + { + + } + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/StateManage/GameModeTransitionStrategyBase.cs b/GameServer/Contents/GameMode/Manage/StateManage/GameModeTransitionStrategyBase.cs new file mode 100644 index 0000000..e41f66c --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/StateManage/GameModeTransitionStrategyBase.cs @@ -0,0 +1,66 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; + +namespace GameServer.Contents.GameMode.Manage.StateManage; + +public abstract class GameModeTransitionStrategyBase : IGameModeTransitionStrategy +{ + public IGameModeState? tryGetNextState(IGameModeState currentState, IGameMode gameMode) + { + switch (currentState.getStateType()) + { + case GameModeState.None: + return getNextStateByNone(currentState, gameMode); + case GameModeState.Created: + return getNextStateByCreate(currentState, gameMode); + case GameModeState.Destroyed: + return getNextStateByDestroyed(currentState, gameMode); + case GameModeState.LoadingWait: + return getNextStateByLoadingWait(currentState, gameMode); + case GameModeState.Wait: + return getNextStateByWait(currentState, gameMode); + case GameModeState.Ready: + return getNextStateByReady(currentState, gameMode); + case GameModeState.Play: + return getNextStateByPlay(currentState, gameMode); + case GameModeState.Result: + return getNextStateByResult(currentState, gameMode); + case GameModeState.RewardSummary: + return getNextStateByRewardSummary(currentState, gameMode); + case GameModeState.End: + return getNextStateByEnd(currentState, gameMode); + case GameModeState.Rounding: + return getNextStateByRounding(currentState, gameMode); + case GameModeState.RoundWait: + return getNextStateByRoundWait(currentState, gameMode); + case GameModeState.RoundEndAll: + return getNextStateByRoundEndAll(currentState, gameMode); + case GameModeState.EventEnd: + return getNextStateEventEnd(currentState, gameMode); + default: + return null; + } + } + + + public abstract IGameModeState? getNextStateByNone(IGameModeState currentState, IGameMode gameMode); + public abstract IGameModeState? getNextStateByCreate(IGameModeState currentState, IGameMode gameMode); + + public IGameModeState? getNextStateByDestroyed(IGameModeState currentState, IGameMode gameMode) + { + return null; //nothing to do + } + + public abstract IGameModeState? getNextStateByLoadingWait(IGameModeState currentState, IGameMode gameMode); + public abstract IGameModeState? getNextStateByWait(IGameModeState currentState, IGameMode gameMode); + public abstract IGameModeState? getNextStateByReady(IGameModeState currentState, IGameMode gameMode); + public abstract IGameModeState? getNextStateByPlay(IGameModeState currentState, IGameMode gameMode); + public abstract IGameModeState? getNextStateByResult(IGameModeState currentState, IGameMode gameMode); + public abstract IGameModeState? getNextStateByRewardSummary(IGameModeState currentState, IGameMode gameMode); + public abstract IGameModeState? getNextStateByEnd(IGameModeState currentState, IGameMode gameMode); + + public abstract IGameModeState? getNextStateByRounding(IGameModeState currentState, IGameMode gameMode); + public abstract IGameModeState? getNextStateByRoundWait(IGameModeState currentState, IGameMode gameMode); + public abstract IGameModeState? getNextStateByRoundEndAll(IGameModeState currentState, IGameMode gameMode); + + public abstract IGameModeState? getNextStateEventEnd(IGameModeState currentState, IGameMode gameMode); +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/StateManage/IGameModeState.cs b/GameServer/Contents/GameMode/Manage/StateManage/IGameModeState.cs index b14a4e8..2599782 100644 --- a/GameServer/Contents/GameMode/Manage/StateManage/IGameModeState.cs +++ b/GameServer/Contents/GameMode/Manage/StateManage/IGameModeState.cs @@ -1,14 +1,20 @@ using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; namespace GameServer; public interface IGameModeState { - public void enter(); - public void exit(); + public Task enter(IGameMode gameMode); + public Task exit(); + public Task update(IGameMode gameMode); - public void update(); - public GameModeState checkState(); public GameModeState getStateType(); + + public StateUpdateReasonType getUpdateReason(); + + public List getStateChangeHistories(); + + public DateTime getNextStateChangeTime(); } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/StateManage/IGameModeStateManager.cs b/GameServer/Contents/GameMode/Manage/StateManage/IGameModeStateManager.cs new file mode 100644 index 0000000..8e40251 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/StateManage/IGameModeStateManager.cs @@ -0,0 +1,10 @@ +namespace GameServer.Contents.GameMode.Manage.StateManage; + +public interface IGameModeStateManager +{ + void extendStateTime(TimeSpan extension, string reason, bool notify = false); + void setStateTime(DateTime newTime, string reason, bool notify = false); + DateTime getNextStateChangeTime(); + IReadOnlyList getStateChangeHistrory(); + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/StateManage/IGameModeTransitionStrategy.cs b/GameServer/Contents/GameMode/Manage/StateManage/IGameModeTransitionStrategy.cs new file mode 100644 index 0000000..fb58334 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/StateManage/IGameModeTransitionStrategy.cs @@ -0,0 +1,8 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; + +namespace GameServer; + +public interface IGameModeTransitionStrategy +{ + IGameModeState? tryGetNextState(IGameModeState currentState, IGameMode gameMode); +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Manage/StateManage/StateChangeHistory.cs b/GameServer/Contents/GameMode/Manage/StateManage/StateChangeHistory.cs new file mode 100644 index 0000000..ce88807 --- /dev/null +++ b/GameServer/Contents/GameMode/Manage/StateManage/StateChangeHistory.cs @@ -0,0 +1,11 @@ +namespace GameServer.Contents.GameMode.Manage.StateManage; + +public class StateChangeHistory +{ + public DateTime m_time { get; set; } + public string m_action { get; set; } = string.Empty; + public string m_reason { get; set; } = string.Empty; + public DateTime m_old_time { get; set; } + public DateTime m_new_time { get; set; } + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceManager.cs b/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceManager.cs index 9819849..c3c4b23 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceManager.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceManager.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using GameServer.Contents.Battle.Tickers; -using GameServer.Contents.GameMode.Mode_Battle.Manage; +using GameServer.Contents.GameMode.Helper; using Google.Protobuf.WellKnownTypes; using NeoSmart.AsyncLock; @@ -12,8 +12,8 @@ using Newtonsoft.Json; using ServerCore; using ServerBase; using ServerCommon; -using Constant = System.Reflection.Metadata.Constant; +using EVENT_ID = System.Int32; namespace GameServer; @@ -28,114 +28,39 @@ public class BattleInstanceManager : Singleton { //1. 이벤트 관리하는 Task 추가 var result = await createBattleEventCheckerTask(); - + } - public async Task tryLeaveBattelInstance(Player player, string instanceRoomId) - { - Log.getLogger().debug($"tryLeaveBattelInstance start instanceRoomId : {instanceRoomId}"); - - var location_attribute = player.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(location_attribute, () => $"location attribute is null !!! - {player.toBasicString()}"); - - //var instanceRoomId = location_attribute.CurrentIndunLocation.InstanceRoomId; - if (instanceRoomId == string.Empty) - { - Log.getLogger().warn($"tryLeaveBattelInstance IntanceRoomId is Empty"); - return; - } - - if (false == GameModeManager.It.tryGetGameMode(instanceRoomId, out var gameMode)) - { - return; - } - - var ffa = gameMode as GameModeTPSFreeForAll; - NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!! - {player.toBasicString()}"); - - await ffa.removePodCombat(player); - - var host_user_guid = ffa.m_host_migrator.getHostUserGuid(); - if (player.getUserGuid().Equals(host_user_guid)) - { - using (var releaser = await ffa.getAsyncLock()) - { - ffa.m_host_migrator.removeHost(); - } - } - - - //여기서 instanceroom 이 is Destroy 면 DestroyBattleRoom 호출 해줄것 - if (ffa.getInstanceRoom().isDestroy) - { - //kihoon todo : 이부분 수정 필요 - //m_battle_instances.TryRemove(instanceRoomId, out var _); - //await battleInstanceRoom.destroyBattleRoom(); - Log.getLogger().debug($"tryLeaveBattelInstance destroy battle room instanceRoomId : {instanceRoomId}"); - } - } - - // public async Task tryDestroyBattleRoom(string instanceRoomId) - // { - // await Task.CompletedTask; - // if (false == instanceRoomId.Contains(BattleConstant.PREFIX_BATTLE_INSTANCE_ROOM_ID)) return; - // - // // if (false == m_battle_instances.TryGetValue(instanceRoomId, out var battleInstanceRoom)) - // // { - // // Log.getLogger().error($"instanceRoomId : {instanceRoomId} is not exist instnaces"); - // // return; - // // } - // - // // var is_destroy = battleInstanceRoom.m_instance_room.isDestroy; - // // if (is_destroy) - // // { - // // await battleInstanceRoom.destroyBattleRoom(); - // // //m_battle_instances.TryRemove(instanceRoomId, out _); - // // } - // } - - - - - - public async Task LeaveBattleRoom(Player player, string instanceRoomId, bool disconnected = false) - { - (var result, var leeave_handler) = GameModeHelper.getGameModeLeaveHandler(player, instanceRoomId); - if (result.isFail() || leeave_handler is null) return false; - - await leeave_handler.gameModeLeave(); - Log.getLogger().debug($"leave battle room player : {player.toBasicString()}, instanceRoomId : {instanceRoomId}"); - - return true; - } - - public async Task sendNtfAboutBattleInstance(GameModeTPSFreeForAll battieInstanceRoom, Player player) + public async Task sendNtfAboutBattleInstance(GameModeTPSFreeForAll battieInstanceRoom, Player player) { var result = new Result(); BattleRoomNotifyHelper.send_GS2C_NTF_BATTLE_INSTANCE_STATE(battieInstanceRoom, player); BattleRoomNotifyHelper.send_GS2C_NTF_POD_COMBAT_STATE(battieInstanceRoom, player); + sendNtfBattleObjectsState(battieInstanceRoom, player); + var state = battieInstanceRoom.getState(); + GameNotifyHelper.send_GS2C_NTF_GAME_STATE_UPDATE(player, battieInstanceRoom.getInstanceRoom(), state.getStateType(), state.getNextStateChangeTime(), state.getUpdateReason()); var host_user_guid = battieInstanceRoom.m_host_migrator.getHostUserGuid(); if (!host_user_guid.Equals(string.Empty)) { - BattleRoomNotifyHelper.send_GS2C_NTF_P2P_HOST_UPDATE(player, host_user_guid); + GameNotifyHelper.send_GS2C_NTF_P2P_HOST_UPDATE(player, host_user_guid); } await Task.CompletedTask; return result; } - - private void sendNtfBattleObjectsState(GameModeTPSFreeForAll battleInstanceRoom, Player player) + + private void sendNtfBattleObjectsState(GameModeTPSFreeForAll battleInstanceRoom, Player player) { List infos = new(); var attribute = battleInstanceRoom.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); - + var now = DateTimeHelper.Current; - + foreach (var weapon in attribute.m_combat_pod_mode.m_weapons.Values) { BattleObjectInfo info = new(); @@ -143,7 +68,7 @@ public class BattleInstanceManager : Singleton info.IsActive = weapon.m_active_time <= now ? BoolType.True : BoolType.False; infos.Add(info); } - + foreach (var buff in attribute.m_combat_pod_mode.m_buffs.Values) { BattleObjectInfo info = new(); @@ -151,7 +76,7 @@ public class BattleInstanceManager : Singleton info.IsActive = buff.m_active_time <= now ? BoolType.True : BoolType.False; infos.Add(info); } - + foreach (BattleObjectPodStorage storage in attribute.m_combat_pod_mode.m_pod_storages.Values) { //storage.m_is_active = true; @@ -160,7 +85,7 @@ public class BattleInstanceManager : Singleton info.IsActive = storage.m_is_active ? BoolType.True : BoolType.False; infos.Add(info); } - + foreach (BattleObjectPickupPod pickup in attribute.m_combat_pod_mode.m_pickup_pods.Values) { BattleObjectInfo info = new(); @@ -169,7 +94,7 @@ public class BattleInstanceManager : Singleton infos.Add(info); Log.getLogger().info($"pickup pod info anchor_guid : {info.AnchorGuid}, is Active : {info.IsActive}"); } - + BattleRoomNotifyHelper.send_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(infos, player); } @@ -177,7 +102,7 @@ public class BattleInstanceManager : Singleton { var location_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); - + var current_indun_location = location_action.getCurrentLocation() as IndunLocation; NullReferenceCheckHelper.throwIfNull(current_indun_location, () => $"current_indun_location is null !!! - {player.toBasicString()}"); @@ -189,79 +114,68 @@ public class BattleInstanceManager : Singleton { return true; } - return false; - //result.setFail(ServerErrorCode.BattleInstnaceNotUsedPacket, ""); + return false; } - + public List getAllProtoBattleEvents() { List infos = new(); - foreach(var system_battle_event in m_battle_events.Values.ToList()) + foreach (var system_battle_event in m_battle_events.Values.ToList()) { - BattleEventInfo battle_event_info = new(); - - battle_event_info.EventId = system_battle_event.m_event_id; - battle_event_info.InstanceId = system_battle_event.m_instance_id; - battle_event_info.StartTime = Timestamp.FromDateTime(system_battle_event.m_start_time); - battle_event_info.ConfigDataId = system_battle_event.m_ffa_config_data_id; - battle_event_info.RewardGroupId = system_battle_event.m_ffa_reward_group_id; - battle_event_info.HotTime = system_battle_event.m_ffa_hot_time; - battle_event_info.RoundCount = system_battle_event.m_round_count; + var battle_event_info = createBattleEventInfo(system_battle_event.m_event_id, + system_battle_event.m_instance_id, + system_battle_event.m_start_time, + system_battle_event.m_end_time, + system_battle_event.m_ffa_config_data_id, + system_battle_event.m_ffa_reward_group_id, + system_battle_event.m_ffa_hot_time, + system_battle_event.m_game_mode_id, + system_battle_event.m_round_count); infos.Add(battle_event_info); } return infos; } - + public BattleEventInfo makeProtoBattleEvent(SystemBattleEvent battleEvent) + { + return createBattleEventInfo(battleEvent.m_event_id, + battleEvent.m_instance_id, + battleEvent.m_start_time, + battleEvent.m_end_time, + battleEvent.m_ffa_config_data_id, + battleEvent.m_ffa_reward_group_id, + battleEvent.m_ffa_hot_time, + battleEvent.m_game_mode_id, + battleEvent.m_round_count); + } + + public BattleEventInfo createBattleEventInfo(int eventId, int instanceId, DateTime startTime, DateTime endTime, int ffaConfigDataId, int ffaRewardGroupId, int ffaHotTime, int gameModeId, int roundCount) { BattleEventInfo battle_event_info = new(); - battle_event_info.EventId = battleEvent.m_event_id; - battle_event_info.InstanceId = battleEvent.m_instance_id; - battle_event_info.StartTime = Timestamp.FromDateTime(battleEvent.m_start_time); - battle_event_info.ConfigDataId = battleEvent.m_ffa_config_data_id; - battle_event_info.RewardGroupId = battleEvent.m_ffa_reward_group_id; - battle_event_info.HotTime = battleEvent.m_ffa_hot_time; - battle_event_info.RoundCount = battleEvent.m_round_count; + battle_event_info.EventId = eventId; + battle_event_info.InstanceId = instanceId; + battle_event_info.StartTime = Timestamp.FromDateTime(startTime); + battle_event_info.EndTime = Timestamp.FromDateTime(endTime); + battle_event_info.ConfigDataId = ffaConfigDataId; + battle_event_info.RewardGroupId = ffaRewardGroupId; + battle_event_info.HotTime = ffaHotTime; + battle_event_info.GameModeId = gameModeId; + battle_event_info.RoundCount = roundCount; return battle_event_info; } - // public async Task setBattleEventAttribs(List battleEventAttribs) - // { - // using (await m_lock_data_load.LockAsync()) - // { - // m_battle_event_attribs.Clear(); - // m_battle_event_attribs = battleEventAttribs; - // } - // - // } - - // public async Task> copyBattleEventAttribs() - // { - // List attribs = new(); - // using (await m_lock_data_load.LockAsync()) - // { - // foreach (var attrib in m_battle_event_attribs) - // { - // attribs.Add(attrib.clone()); - // } - // } - // - // return attribs; - // } - - - public ConcurrentDictionary getSystemBattleEvents() + public ConcurrentDictionary getSystemBattleEvents() { return m_battle_events; } - public bool getSystemBattleEvent(Int32 eventId, [MaybeNullWhen(false)]out SystemBattleEvent battleEvent) + public bool getSystemBattleEvent(EVENT_ID eventId, [MaybeNullWhen(false)]out SystemBattleEvent battleEvent) { if (false == m_battle_events.TryGetValue(eventId, out battleEvent)) { @@ -283,32 +197,45 @@ public class BattleInstanceManager : Singleton } } - public bool existSystemBattleEvent(Int32 eventId) + public void clearSystemBattleEvents() + { + m_battle_events.Clear(); + } + + public bool existSystemBattleEvent(EVENT_ID eventId) { return m_battle_events.ContainsKey(eventId); } + public void updateSystemBattleEvent(SystemBattleEvent ev) + { + m_battle_events.AddOrUpdate(ev.m_event_id, ev, (key, tuple) => ev); + } + public void logSystemBattleEvents() { Log.getLogger().info($"updated system battle events : {JsonConvert.SerializeObject(m_battle_events)}"); } - public void setSystemBattleEvents(ConcurrentDictionary newEvents) + public void setSystemBattleEvents(ConcurrentDictionary newEvents) { m_battle_events = newEvents; } - public bool addNewSystemBattleEvent(SystemBattleEvent ev, bool needErrroLog) + public bool addNewSystemBattleEvent(List evs, bool needErrroLog) { - if (false == m_battle_events.TryAdd(ev.m_event_id, ev)) + foreach (var ev in evs) { - if (needErrroLog) + if (false == m_battle_events.TryAdd(ev.m_event_id, ev)) { - Log.getLogger().error($"System battle Event try Add Fail, eventId : {ev.m_event_id}, events : {JsonConvert.SerializeObject(m_battle_events)}"); - } - return false; - } + if (needErrroLog) + { + Log.getLogger().error($"System battle Event try Add Fail, eventId : {ev.m_event_id}, events : {JsonConvert.SerializeObject(m_battle_events)}"); + } + continue; + } + } return true; } diff --git a/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRespawn.cs b/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRespawn.cs deleted file mode 100644 index e83bdb7..0000000 --- a/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRespawn.cs +++ /dev/null @@ -1,10 +0,0 @@ -using ServerCommon; -using ServerCore; using ServerBase; - -namespace GameServer; - -public class BattleInstanceRespawn -{ - public int m_spawn_idx { get; set; } = 0; - public DateTime m_next_usable_time = DateTimeHelper.Current; -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRoomHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRoomHandler.cs deleted file mode 100644 index 45764b2..0000000 --- a/GameServer/Contents/GameMode/Mode-Battle/Manage/BattleInstanceRoomHandler.cs +++ /dev/null @@ -1,379 +0,0 @@ -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 BattleInstanceRoomHandler -{ - private string m_user_guid = string.Empty; - private InstanceMetaData m_instance_meta_data = new(new InstanceMetaDataMutable()); - public BattleInstanceRoomHandler(string userGuid, InstanceMetaData instanceMetaData) - { - m_user_guid = userGuid; - m_instance_meta_data = instanceMetaData; - } - - public async Task<(Result, string)> joinBattleInstance(string userGuid, Int32 eventId, Timestamp eventStartTS, Int32 instanceMetaId) //BattleInstanceType battleInstanceType - { - var result = new Result(); - var err_msg = string.Empty; - - var server_logic = GameServerApp.getServerLogic(); - - var instance_room_storage = new InstanceRoomStorage(); - instance_room_storage.Init(server_logic.getRedisDb(), ""); - - var instance_room_id = string.Empty; - - (result, instance_room_id) = await joinBattleInstanceRoom(userGuid, eventId, eventStartTS); - if (result.isFail()) - { - err_msg = $"Failed to joinInstanceRoom() !!! : {result.toBasicString()}"; - Log.getLogger().error(err_msg); - - return (result, string.Empty); - } - - return (result, instance_room_id); - } - - public async Task<(Result, string)> joinBattleInstanceRoom(string userGuid, Int32 eventId, Timestamp eventStartTS) - { - var result = new Result(); - string err_msg; - - var server_logic = GameServerApp.getServerLogic(); - - var instance_room_storage = new InstanceRoomStorage(); - instance_room_storage.Init(server_logic.getRedisDb(), ""); - - long timestampInSeconds = eventStartTS.Seconds; - - //long start_seqs_num = timestampInSeconds / 100; //방의 시퀀스번호를 정하는 시작 번호 - int start_seqs_num = 1; //방의 시퀀스번호를 정하는 시작 번호 - - //인스턴스 이름 base로 들어가는 문자열 생성 - var instance_room_id_base = makeBattleInstanceRoomIdBase(eventId, timestampInSeconds, m_instance_meta_data.Id); - var instance_room_id = string.Empty; - - //1.전체 roomList,와 스코어를 먼저 가져온다. - var battle_instance_room_storage = new BattleInstanceRoomStorage(); - battle_instance_room_storage.Init(server_logic.getRedisDb(), ""); - (result, var total_room_list) = await battle_instance_room_storage.getBattleInstanceTotalRoomListWithScore(instance_room_id_base, m_instance_meta_data.LimitCount); - if (result.isFail() || total_room_list == null) - { - return (result, string.Empty); - } - - var ordered_available_room = await battle_instance_room_storage.getBattleInstanceRoomList(instance_room_id_base, m_instance_meta_data.LimitCount); - var total_room_length = total_room_list.Length; - - Log.getLogger().info($"join battle instance.. room capacity state.. " + - $"totalroomlist : {JsonConvert.SerializeObject(total_room_list)}, available room : {JsonConvert.SerializeObject(ordered_available_room)}"); - - //total_room_list가 없고 비어있는 방도 없으면, 신규 생성 - if(total_room_length == 0 && ordered_available_room.Count == 0) - { - (result, instance_room_id) = await createAndJoinBattleInstance(instance_room_id_base, start_seqs_num, userGuid); - if (result.isFail()) return (result, instance_room_id); - Log.getLogger().info($"join battle instance..make new first room instance_room_id : {instance_room_id}"); - } - //기존 방은 있으나 지금 입장 가능한 방이 없는경우 모든 방이 풀방이므로 비어있는 시퀀스 찾아서 방생성 - else if (total_room_length > 0 && ordered_available_room.Count == 0) - { - //total_room_list 에서 생성되어 있는 roomSeq를 확인한다. - HashSet using_room_seqs = new(); - - foreach (var room_entry in total_room_list) - { - var room_id = room_entry.Element.ToString(); - - var room_element = room_id.Split(":"); - if (false == int.TryParse(room_element[^1], out var seq)) - { - err_msg = $"Failed to try parse room_element !!! room_id : {room_id}"; - Log.getLogger().error(err_msg); - result.setFail(ServerErrorCode.BattleInstanceSeqParseError, err_msg); - return (result, string.Empty); - } - using_room_seqs.Add(seq); - } - - var not_using_seq = findNotUsingSeqNum(using_room_seqs, start_seqs_num); - - (result, instance_room_id) = await createAndJoinBattleInstance(instance_room_id_base, not_using_seq, userGuid); - if (result.isFail()) return (result, instance_room_id); - - Log.getLogger().info($"join battle instance..make new another room instance_room_id : {instance_room_id}"); - } - //비어 있는 방이 있는 경우 - else if(ordered_available_room.Count > 0) - { - //빈방이 있는 경우 로직 처리 - foreach (var room_id in ordered_available_room) - { - var instance_room_Info = await instance_room_storage.GetInstanceRoomInfo(room_id); - if (instance_room_Info == null) - { - await InstanceRoomHandler.deleteInstanceRoom(room_id); - continue; - } - - result = await joinBattleInstanceRoomFromRedis(userGuid, room_id, instance_room_id_base); - if (result.isSuccess()) - { - instance_room_id = room_id; - break; - } - } - - Log.getLogger().info($"join battle instance..join exists room instance_room_id : {instance_room_id}"); - } - - if (instance_room_id == string.Empty) - { - var new_seq = total_room_length + 1; - //위 로직을 전부 태웠는데도 방이 안만들어졌으면 전체 방수 + 1해서 만든다... - Log.getLogger().warn($"instance room id is still empty... so make new instance instance_room_id_base : {instance_room_id_base}, new_seq : {new_seq}"); - (result, instance_room_id) = await createAndJoinBattleInstance(instance_room_id_base, new_seq, userGuid); - } - - return (result, instance_room_id); - } - - private async Task<(Result, string)> createAndJoinBattleInstance(string instanceRoomIdBase, int seq, string userGuid) - { - string err_msg = string.Empty; - (var result, var instance_room_id) = await createBattleInstanceRoomFromRedis(instanceRoomIdBase, m_instance_meta_data.Id, seq); - if (result.isFail()) - { - err_msg = $"Failed to createInstanceRoomFromRedis() !!! : {result.toBasicString()} - userGuid:{userGuid}"; - Log.getLogger().error(err_msg); - - return (result, string.Empty); - } - - result = await joinBattleInstanceRoomFromRedis(userGuid, instance_room_id, instanceRoomIdBase); - if (result.isFail()) - { - err_msg = $"Failed to joinInstanceRoomFromRedis() !!! : {result.toBasicString()}"; - Log.getLogger().error(err_msg); - - return (result, string.Empty); - } - - Log.getLogger().info($"create and join battle instance instanceRoomIdBase : {instanceRoomIdBase}, seq : {seq}, userGuid : {userGuid}"); - - return (result, instance_room_id); - - } - - private int findNotUsingSeqNum(HashSet usingRoomSeqs, int startSeqNum) - { - var loop_count = usingRoomSeqs.Count + 1; - - var start_seq = startSeqNum; - - for (int i = 1; i <= loop_count; i++) - { - if (usingRoomSeqs.Contains(start_seq)) - { - start_seq++; - continue; - } - - return start_seq; - } - - return start_seq; - } - - public async Task<(Result, string)> createBattleInstanceRoomFromRedis(string instanceRoomIdBase, int instanceMetaId, int startSeqsNum) - { - var result = new Result(); - var err_msg = string.Empty; - - var server_logic = GameServerApp.getServerLogic(); - - var instance_room_storage = new InstanceRoomStorage(); - instance_room_storage.Init(server_logic.getRedisDb(), ""); - - (result, var instance_server_info) = await InstanceRoomHandler.getBestInstanceServerForCreateInstance(m_instance_meta_data.LimitCount); - if (result.isFail() || instance_server_info == null) - { - err_msg = $"Failed to getBestInstanceServerForCreateInstance() !!! : {result.toBasicString()}"; - Log.getLogger().error(err_msg); - - return (result, string.Empty); - } - - if (instance_server_info.Address == string.Empty || instance_server_info.Port == 0) - { - err_msg = $"ServerInfo is invalid !!! Address:{instance_server_info.Address}, Port:{instance_server_info.Port}"; - result.setFail(ServerErrorCode.ValidServerNotFound, err_msg); - Log.getLogger().error(result.toBasicString()); - - return (result, string.Empty); - } - - var instance_room_id = makeBattleInstanceRoomId(instanceRoomIdBase, startSeqsNum); - - (result, var is_created) = await instance_room_storage.createInstanceRoom(instance_room_id, instance_server_info.Address, instance_server_info.Port, instanceMetaId); - if (result.isFail()) - { - err_msg = $"Failed to createInstanceRoom() !!! : {result.toBasicString()}"; - Log.getLogger().error(err_msg); - - return (result, string.Empty); - } - - if (is_created) - { - - var battle_instance_room_storage = new BattleInstanceRoomStorage(); - battle_instance_room_storage.Init(server_logic.getRedisDb(), ""); - result = await battle_instance_room_storage.addBattleInstanceRoomList(instanceRoomIdBase, instance_room_id); - if (result.isFail()) - { - err_msg = $"Failed to addInstanceRoomList() !!! : {result.toBasicString()}"; - Log.getLogger().error(err_msg); - - return (result, string.Empty); - } - } - - return (result, instance_room_id); - } - - public static string makeBattleInstanceRoomIdBase(Int32 eventId, long timestampInSeconds, Int32 instanceId) - { - if (eventId == 0) - { - return $"{BattleConstant.PREFIX_BATTLE_INSTANCE_ROOM_ID}:{eventId}_1234567890_{instanceId}"; - } - - return $"{BattleConstant.PREFIX_BATTLE_INSTANCE_ROOM_ID}:{eventId}_{timestampInSeconds}_{instanceId}"; - } - - static string makeBattleInstanceRoomId(string instanceRoomIdBase, int startSeqsNum) - { - //var server_logic = GameServerApp.getServerLogic(); - //var instance_room_storage = new InstanceRoomStorage(); - // instance_room_storage.Init(server_logic.getRedisDb(), ""); - // var room_id_seq = await instance_room_storage.getRoomIdSeq(); - // if (room_id_seq == 0) - // return string.Empty; - - return $"{instanceRoomIdBase}:{startSeqsNum}"; - } - - - // async Task<(Result, string)> joinBattleInstanceRoom(string userGuid, BattleInstanceType instanceType, int instanceMetaId) - // { - // var result = new Result(); - // string err_msg; - // - // var server_logic = GameServerApp.getServerLogic(); - // - // var battle_instance_room_storage = new BattleInstanceRoomStorage(); - // battle_instance_room_storage.Init(server_logic.getRedisDb(), ""); - // - // if (!MetaData.Instance._IndunTable.TryGetValue(instanceMetaId, out var instance_meta_data)) - // { - // err_msg = $"Failed to MetaData.TryGetValue() !!! : instanceMetaId:{instanceMetaId} - userGuid:{userGuid}"; - // result.setFail(ServerErrorCode.InstanceMetaDataNotFound, err_msg); - // Log.getLogger().error(result.toBasicString()); - // - // return (result, string.Empty); - // } - // var instance_room_id_base = makeBattleInstanceRoomIdBase(instanceType, instanceMetaId); - // var instance_room_id = string.Empty; - // - // //인스턴스 정보가 redis에 없으면 roomlist에서 제거 처리 - // var room_list = await instance_room_storage.GetInstanceRoomList(instance_room_id_base, instance_meta_data.LimitCount); - // foreach (var room_id in room_list) - // { - // var instance_room_Info = await instance_room_storage.GetInstanceRoomInfo(room_id); - // if (instance_room_Info == null) - // { - // await InstanceRoomHandler.deleteInstanceRoom(room_id); - // continue; - // } - // - // result = await joinBattleInstanceRoomFromRedis(userGuid, room_id, instanceMetaId, instance_room_id_base, instance_room_Info.UgcNpcCount); - // if (result.isSuccess()) - // { - // instance_room_id = room_id; - // break; - // } - // } - // - // if (instance_room_id == string.Empty) - // { - // (result, instance_room_id) = await createBattleInstanceRoomFromRedis(instance_room_id_base, instanceMetaId); - // if (result.isFail()) - // { - // err_msg = $"Failed to createInstanceRoomFromRedis() !!! : {result.toBasicString()} - userGuid:{userGuid}"; - // Log.getLogger().error(err_msg); - // - // return (result, string.Empty); - // } - // - // result = await joinBattleInstanceRoomFromRedis(userGuid, instance_room_id, instanceMetaId, instance_room_id_base, 0); - // if (result.isFail()) - // { - // err_msg = $"Failed to joinInstanceRoomFromRedis() !!! : {result.toBasicString()}"; - // Log.getLogger().error(err_msg); - // - // return (result, string.Empty); - // } - // } - // - // return (result, instance_room_id); - // } - public async Task joinBattleInstanceRoomFromRedis(string userGuid, string instanceRoomId, string instanceRoomIdBase) - { - var result = new Result(); - var err_msg = string.Empty; - - var server_logic = GameServerApp.getServerLogic(); - - var instance_room_storage = new InstanceRoomStorage(); - instance_room_storage.Init(server_logic.getRedisDb(), ""); - - result = await instance_room_storage.joinInstanceRoom(userGuid, instanceRoomId, m_instance_meta_data.LimitCount); - if (result.isFail()) - { - err_msg = $"Failed to TryJoinInstanceRoom() !!! : {result.toBasicString()}"; - Log.getLogger().debug(err_msg); - - return result; - } - - result = await instance_room_storage.increaseInstanceRoomScore(instanceRoomIdBase, instanceRoomId); - if (result.isFail()) - { - await instance_room_storage.leaveInstanceRoom(userGuid, instanceRoomId); - - err_msg = $"Failed to increaseInstanceRoomScore() !!! : {result.toBasicString()} - userGuid:{userGuid}"; - Log.getLogger().debug(err_msg); - - return result; - } - return result; - } - - - - - -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/Manage/GameModeTPS.cs b/GameServer/Contents/GameMode/Mode-Battle/Manage/GameModeTPS.cs deleted file mode 100644 index f9a33da..0000000 --- a/GameServer/Contents/GameMode/Mode-Battle/Manage/GameModeTPS.cs +++ /dev/null @@ -1,27 +0,0 @@ -using GameServer.Contents.GameMode.Manage.PlayManage; - -namespace GameServer.Contents.GameMode.Mode_Battle.Manage; - -public class GameModeTPS: GameModeBase, IGameModeGenre -{ - public GameModeTPS(EntityType type, InstanceRoom instanceRoom) : base(type, instanceRoom) - { - } - - - public override Task taskUpdate() - { - return Task.CompletedTask; - } - - public override string toBasicString() - { - var basic_string = base.toBasicString() + $"GameModeTPS...."; - return basic_string; - } - - public override Task initAfterTimerCreate() - { - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceAction.cs index f5133e9..0f95c34 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceAction.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceAction.cs @@ -42,15 +42,14 @@ public class BattleInstanceAction : EntityActionBase return result; } - var event_id = BattleRoomHelper.getBattleEventIdFromRoomId(room_id); + var room_info = game_mode_base.getGameRoomInfo(); + var event_id = room_info.GameEventId; BattleInstanceSnapshotDoc doc = new(room_id); - //string snapshot_load_type = "LOAD"; if (read_docs.Count == 0 || event_id == 0) { //데이터가 없으므로 생성 처리 result = await createBattleInstanceSnapshot(room_id); if (result.isFail()) return result; - // snapshot_load_type = "CREATE"; } else if (read_docs.Count > 0) { @@ -64,19 +63,9 @@ public class BattleInstanceAction : EntityActionBase if (result.isFail()) return result; Log.getLogger().debug($"loadOrCreateSnapshot room_id : {game_mode_base.toBasicString()}"); - - var attribute = game_mode_base.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); - - //kihoon todo : 이거 Base쪽으로 올려야 된다. - //var invokers = new List(); - //var log_action = new LogAction(LogActionType.BattleInstanceSnapshotCreate); - //BattleSnapShotBusinessLog business_log = new(battle_instance_room, snapshot_load_type); - //invokers.Add(business_log); - //BusinessLogger.collectLogs(log_action, battle_instance_room, invokers); - return result; } @@ -91,7 +80,7 @@ public class BattleInstanceAction : EntityActionBase var game_mode_base = getOwner() as GameModeBase; NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"battle_instance_room is null !!!"); - var game_mode_ffa = getOwner() as GameModeTPSFreeForAll; + var game_mode_ffa = getOwner() as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(game_mode_ffa, () => $"battle_instance_room is null !!!"); var server_logic = GameServerApp.getServerLogic(); @@ -102,9 +91,8 @@ public class BattleInstanceAction : EntityActionBase NullReferenceCheckHelper.throwIfNull(battle_instance_snaptshot_attribute, () => $"battle_instance_snaptshot_attribute is null !!!"); battle_instance_snaptshot_attribute.m_room_id = roomId; - battle_instance_snaptshot_attribute.m_combat_pod_mode.m_current_state_start_time = game_mode_ffa.m_battle_instance_event_start_time; + battle_instance_snaptshot_attribute.m_combat_pod_mode.m_current_state_start_time = game_mode_ffa.m_game_start_time; - //kihoon 이거다 수정해야된다. addRespawnPosInfoFromMeta(game_mode_ffa, battle_instance_snaptshot_attribute); addStorageInfoFromMeta(game_mode_ffa, battle_instance_snaptshot_attribute); addPickupPodInfoFromMeta(game_mode_ffa, battle_instance_snaptshot_attribute); @@ -125,7 +113,6 @@ public class BattleInstanceAction : EntityActionBase battle_instance_snaptshot_attribute.modifiedEntityAttribute(true); - //kihoon todo : 이거 수정해야된다. var batch = new QueryBatchEx(game_mode_base, LogActionType.BattleInstanceSnapshotCreate, server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); @@ -166,140 +153,7 @@ public class BattleInstanceAction : EntityActionBase return result; } - // private void deleteChangedGuidAnchor(BattleInstanceRoom battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, BattleInstanceSnapshotAttrib attrib) - // { - // deleteChangedBattleObjectPodStorageAnchor(battleInstanceRoom, attribute, attrib); - // deleteChangedBattleObjectPickupPodAnchor(battleInstanceRoom, attribute, attrib); - // deleteChangedRespawnAnchor(battleInstanceRoom, attribute, attrib); - // deleteChangedBattleObjectWeaponAnchor(battleInstanceRoom, attribute, attrib); - // deleteChangedBattleObjectBuffAnchor(battleInstanceRoom, attribute, attrib); - // - // //pod combat은 timer에서 처리해준다. - // } - - private void setNewGuidAnchor(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) - { - addRespawnPosInfoFromMeta(battleInstanceRoom, attribute); - addStorageInfoFromMeta(battleInstanceRoom, attribute); - addPickupPodInfoFromMeta(battleInstanceRoom, attribute); - - - var anchors = battleInstanceRoom.getInstanceRoom().getMap().getAnchors(); - foreach (var anchor in anchors) - { - addBattleObjectWeapon(attribute, anchor.Value); - addBattleObjectBuff(attribute, anchor.Value); - } - - - - } - - private void deleteChangedBattleObjectPodStorageAnchor(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, BattleInstanceSnapshotAttrib attrib) - { - List delete_keys = new(); - foreach (var stand in attrib.m_combat_pod_attrib.m_pod_storages) - { - //기존에 들고 있던 guid 가 map에서 없어지거나 바뀌었을 경우가 있을수 있으므로 예외 처리 - if (false == battleInstanceRoom.getInstanceRoom().getMap().getAnchors().ContainsKey(stand.Key)) - { - Log.getLogger().warn($"stand : {stand.Key} is not exist.. so don't add attribute.... roomId : {battleInstanceRoom.getRoomId()}"); - delete_keys.Add(stand.Key); - continue; - } - attribute.m_combat_pod_mode.m_pod_storages.TryAdd(stand.Key, stand.Value); - } - - foreach (var delete_key in delete_keys) - { - attribute.m_combat_pod_mode.m_pod_storages.TryRemove(delete_key, out _); - } - } - - private void deleteChangedBattleObjectPickupPodAnchor(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, BattleInstanceSnapshotAttrib attrib) - { - List delete_keys = new(); - foreach (var box in attrib.m_combat_pod_attrib.m_pickup_pods) - { - //기존에 들고 있던 guid 가 map에서 없어지거나 바뀌었을 경우가 있을수 있으므로 예외 처리 - if (false == battleInstanceRoom.getInstanceRoom().getMap().getAnchors().ContainsKey(box.Key)) - { - Log.getLogger().info($"box : {box.Key} is not exist.. so don't add attribute.... roomId : {battleInstanceRoom.getRoomId()}"); - delete_keys.Add(box.Key); - continue; - } - attribute.m_combat_pod_mode.m_pickup_pods.TryAdd(box.Key, box.Value); - } - - foreach (var delete_key in delete_keys) - { - attribute.m_combat_pod_mode.m_pickup_pods.TryRemove(delete_key, out _); - } - } - - private void deleteChangedRespawnAnchor(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, BattleInstanceSnapshotAttrib attrib) - { - List delete_keys = new(); - foreach (var each in attrib.m_combat_pod_attrib.m_respawns) - { - //map에 respawn pos가 없어지거나 바뀌었을 경우가 있을수 있으므로 예외 처리 - if (false == battleInstanceRoom.m_respawn_pos_anchors_meta.Contains(each.Key)) - { - Log.getLogger().warn($"spawn idx : {each.Key} is not exist.. so don't add attribute.... roomId : {battleInstanceRoom.getRoomId()}"); - delete_keys.Add(each.Key); - continue; - } - attribute.m_combat_pod_mode.m_respawns.TryAdd(each.Key, each.Value); - } - foreach (var delete_key in delete_keys) - { - attribute.m_combat_pod_mode.m_respawns.TryRemove(delete_key, out _); - } - } - - private void deleteChangedBattleObjectWeaponAnchor(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, BattleInstanceSnapshotAttrib attrib) - { - List delete_keys = new(); - foreach (var weapon in attrib.m_combat_pod_attrib.m_weapons) - { - //기존에 들고 있던 guid 가 map에서 없어지거나 바뀌었을 경우가 있을수 있으므로 예외 처리 - if (false == battleInstanceRoom.getInstanceRoom().getMap().getAnchors().ContainsKey(weapon.Key)) - { - Log.getLogger().warn($"weapon : {weapon.Key} is not exist.. so don't add attribute.... roomId : {battleInstanceRoom.getRoomId()}"); - delete_keys.Add(weapon.Key); - continue; - } - attribute.m_combat_pod_mode.m_weapons.TryAdd(weapon.Key, weapon.Value); - } - - foreach (var delete_key in delete_keys) - { - attribute.m_combat_pod_mode.m_weapons.TryRemove(delete_key, out _); - } - } - - private void deleteChangedBattleObjectBuffAnchor(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, BattleInstanceSnapshotAttrib attrib) - { - List delete_keys = new(); - foreach (var buff in attrib.m_combat_pod_attrib.m_buffs) - { - //map에 respawn pos가 없어지거나 바뀌었을 경우가 있을수 있으므로 예외 처리 - if (false == battleInstanceRoom.getInstanceRoom().getMap().getAnchors().ContainsKey(buff.Key)) - { - Log.getLogger().warn($"buff : {buff.Key} is not exist.. so don't add attribute.... roomId : {battleInstanceRoom.getInstanceRoom().getMap().m_room_id}"); - delete_keys.Add(buff.Key); - continue; - } - attribute.m_combat_pod_mode.m_buffs.TryAdd(buff.Key, buff.Value); - } - - foreach (var delete_key in delete_keys) - { - attribute.m_combat_pod_mode.m_buffs.TryRemove(delete_key, out _); - } - } - - private void addRespawnPosInfoFromMeta(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + private void addRespawnPosInfoFromMeta(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) { var now = DateTimeHelper.Current; @@ -310,7 +164,7 @@ public class BattleInstanceAction : EntityActionBase } } - private void addStorageInfoFromMeta(GameModeTPSFreeForAll ffa, BattleInstanceSnapshotAttribute attribute) + private void addStorageInfoFromMeta(GameModeTPSFreeForAll ffa, BattleInstanceSnapshotAttribute attribute) { foreach (var storage_group in ffa.m_battle_pod_storage_guid_group.Values) { @@ -321,7 +175,7 @@ public class BattleInstanceAction : EntityActionBase } } - private void addPickupPodInfoFromMeta(GameModeTPSFreeForAll ffa, BattleInstanceSnapshotAttribute attribute) + private void addPickupPodInfoFromMeta(GameModeTPSFreeForAll ffa, BattleInstanceSnapshotAttribute attribute) { foreach (var pickup_pod_group in ffa.m_battle_pickup_pod_guid_group.Values) { @@ -341,7 +195,7 @@ public class BattleInstanceAction : EntityActionBase var talbe_id = anchorInfo.AnchorProp.TableId; if (false == MetaData.Instance._BattleObjectMetaTable.TryGetValue(talbe_id, out var battle_object)) return; - if (false == battle_object.ObjectType.Equals(EBattleObjectType.Weapon)) return; + if (false == battle_object.ObjectType.Equals(EGameModeObjectType.Weapon)) return; BattleObjectWeapon weapon = new(anchorInfo.AnchorGuid); attribute.m_combat_pod_mode.m_weapons.TryAdd(anchorInfo.AnchorGuid, weapon); @@ -354,7 +208,7 @@ public class BattleInstanceAction : EntityActionBase var talbe_id = anchorInfo.AnchorProp.TableId; if (false == MetaData.Instance._BattleObjectMetaTable.TryGetValue(talbe_id, out var battle_object)) return; - if (false == battle_object.ObjectType.Equals(EBattleObjectType.Buff)) return; + if (false == battle_object.ObjectType.Equals(EGameModeObjectType.Buff)) return; BattleObjectBuff buff = new(anchorInfo.AnchorGuid); attribute.m_combat_pod_mode.m_buffs.TryAdd(anchorInfo.AnchorGuid, buff); @@ -379,7 +233,7 @@ public class BattleInstanceAction : EntityActionBase } - private async Task createBattleObjectPickupPodReward(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + private async Task createBattleObjectPickupPodReward(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) { var result = new Result(); @@ -391,7 +245,7 @@ public class BattleInstanceAction : EntityActionBase return result; } - private async Task createBattleObjectCombatPod(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + private async Task createBattleObjectCombatPod(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) { var battle_update_action = battleInstanceRoom.getEntityAction(); NullReferenceCheckHelper.throwIfNull(battle_update_action, () => $"battle_update_action is null !!!"); @@ -400,20 +254,9 @@ public class BattleInstanceAction : EntityActionBase if (result.isFail()) return result; - //여기서 PodCombatState 패킷전송 - //BattleRoomNotifyHelper.send_GS2C_NTF_POD_COMBAT_STATE(); - return result; } - // private void setBattleObjectPodCombat(BattleInstanceRoom battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, BattleInstanceSnapshotAttrib attrib) - // { - // - // foreach (var pod_combat in attrib.m_combat_pod_attrib.m_combat_pods) - // { - // //var pod_combat_guid = pod_combat.Key; - // //attribute.m_pod_combat_mode.m_pod_combats.AddOrUpdate(pod_combat.Key, pod_combat.Value, (key, old) => pod_combat.Value); - // } - // } + } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceStateUpdateAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceStateUpdateAction.cs index 10c91d7..87fa29c 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceStateUpdateAction.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceStateUpdateAction.cs @@ -1,5 +1,7 @@ -using GameServer.Contents.GameMode.Mode_Battle.Manage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Battle.Manage; using MetaAssets; +using Microsoft.AspNetCore.Mvc.Diagnostics; using Newtonsoft.Json; using ServerBase; using ServerCommon; @@ -10,115 +12,104 @@ namespace GameServer; public partial class BattleInstanceUpdateAction { - - private async Task battleInstanceStateUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + + // private async Task battleInstanceStateUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + // { + // var result = new Result(); + // var round_state = attribute.m_combat_pod_mode.m_game_state; + // switch (round_state) + // { + // case GameModeState.Rounding: + // //라운드가 끝났을때 Wait 혹은 End 처리 + // //result = await battleInstanceRoundingStateUpdate(battleInstanceRoom, attribute); + // break; + // case GameModeState.RoundWait: + // //시간 됐을때 Rounding 처리 + // //result = await battleInstanceWaitStateUpdate(battleInstanceRoom, attribute); + // break; + // case GameModeState.RoundEndAll: + // //시간 지났을때 kick 처리 + // result = await battleInstanceEndStateUpdate(battleInstanceRoom, attribute); + // break; + // case GameModeState.Destroyed: + // break; + // default: + // Log.getLogger().error($"GameModeState None error room_id : {battleInstanceRoom.getRoomId()}"); + // return result; + // } + // return result; + // } + + public async Task updateRoundStart(DateTime now) { - var result = new Result(); - var round_state = attribute.m_combat_pod_mode.m_round_state_type; - switch (round_state) + var tps_ffa = getOwner() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + var attribute = tps_ffa.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"BattleInstanceSnapshotAttribute is null !!!"); + + + using (var releaser = await tps_ffa.getAsyncLock()) { - case BattleRoundStateType.Rounding: - //라운드가 끝났을때 Wait 혹은 End 처리 - result = await battleInstanceRoundingStateUpdate(battleInstanceRoom, attribute); - break; - case BattleRoundStateType.RoundWait: - //시간 됐을때 Rounding 처리 - result = await battleInstanceWaitStateUpdate(battleInstanceRoom, attribute); - break; - case BattleRoundStateType.RoundEndAll: - //시간 지났을때 kick 처리 - result = await battleInstanceEndStateUpdate(battleInstanceRoom, attribute); - break; - case BattleRoundStateType.Destroyed: - break; - default: - Log.getLogger().error($"BattleRoundStateType None error room_id : {battleInstanceRoom.getRoomId()}"); - return result; - } - return result; - } - - private async Task battleInstanceWaitStateUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) - { - var result = new Result(); - await Task.CompletedTask; - - var now = DateTimeHelper.Current; - - var state_start_time = attribute.m_combat_pod_mode.m_current_state_start_time; - var wait_end_time = state_start_time.AddSeconds(battleInstanceRoom.m_ffa_config_meta.NextRoundWaitTime); - - //시간이 지났을 경우 state 바꿔주는 처리 필요 - if (now < wait_end_time) return result; - - - using (var releaser = await battleInstanceRoom.getAsyncLock()) - { - attribute.m_combat_pod_mode.m_round_state_type = BattleRoundStateType.Rounding; - attribute.m_combat_pod_mode.m_current_state_start_time = wait_end_time; + attribute.m_combat_pod_mode.m_game_state = GameModeState.Rounding; + attribute.m_combat_pod_mode.m_current_state_start_time = now; attribute.m_combat_pod_mode.m_current_round += 1; attribute.m_combat_pod_mode.m_charged_step = 0; attribute.m_combat_pod_mode.m_rewarded_step = 0; attribute.m_combat_pod_mode.m_pod_combat.setActive(now); - - - (result, var anchor_guid) = BattleRoomHelper.getRandomCombatPodAnchorGuid(attribute, battleInstanceRoom); + + + (var result, var anchor_guid) = BattleRoomHelper.getRandomCombatPodAnchorGuid(attribute, tps_ffa); if (result.isFail()) { - Log.getLogger().Error("get Random pod combat anchor guid faile !!! "); - return result; + Log.getLogger().error("get Random pod combat anchor guid faile !!!"); + return; } - attribute.m_combat_pod_mode.m_pod_combat.m_source_storage_anchor_guid = anchor_guid; - - } - - BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(battleInstanceRoom); - BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(battleInstanceRoom); - battleObjectStateInitAndNotify(battleInstanceRoom, attribute); - - - //로그 추가 - var invokers = new List(); - var log_action = new LogAction(LogActionType.BattleRoundStateUpdate.ToString()); - - var room_id = battleInstanceRoom.getRoomId(); - var ended_round = attribute.m_combat_pod_mode.m_current_round; - var round_state = attribute.m_combat_pod_mode.m_round_state_type; - var users = battleInstanceRoom.getInstanceRoom().tryGetInstanceExistUserForLog(); - BattleRoundUpdateBusinessLog business_log = new(room_id, ended_round, round_state, users); - invokers.Add(business_log); - //BusinessLogger.collectLogs(log_action, battleInstanceRoom, invokers); //kihoon todo : 이거 수정해야된다. - - return result; - } - - private async Task battleInstanceRoundingStateUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + + public void objectStateInitAndNotify() { + var tps_ffa = getOwner() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + var attribute = tps_ffa.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"BattleInstanceSnapshotAttribute is null !!!"); + + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(tps_ffa); + BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(tps_ffa); + + var infos = new List(); var result = new Result(); - var now = DateTimeHelper.Current; - var round_start_time = attribute.m_combat_pod_mode.m_current_state_start_time; - var round_end_time = round_start_time.AddSeconds(battleInstanceRoom.m_ffa_config_meta.RoundTime); - - //라운드에 대한 시간이 지났을 경우 state 바꿔주는 처리 필요 - if (round_end_time <= now) + foreach (BattleObjectPodStorage storage in attribute.m_combat_pod_mode.m_pod_storages.Values) { - await roundingUpdate(battleInstanceRoom, round_end_time); - } - else - { - await podCombatRewardUpdate(battleInstanceRoom, attribute, now); - await stepUpdate(battleInstanceRoom, attribute, now); - } + storage.m_reward_cnt = 0; - return result; + storage.m_is_active = true; + BattleObjectInfo info = new(); + info.AnchorGuid = storage.m_anchor_guid; + info.IsActive = BoolType.True; + infos.Add(info); + } + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(tps_ffa, infos); } - - private async Task roundingUpdate(GameModeTPSFreeForAll battleInstanceRoom, DateTime roundEndTime) + + public async Task updateRoundState() + { + var tps_ffa = getOwner() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + var attribute = tps_ffa.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"BattleInstanceSnapshotAttribute is null !!!"); + + var now = DateTimeHelper.Current; + await podCombatRewardUpdate(tps_ffa, attribute, now); + await stepUpdate(tps_ffa, attribute, now); + + return new Result(); + } + + public async Task roundingUpdate(GameModeTPSFreeForAll battleInstanceRoom, DateTime roundEndTime) { var result = new Result(); string occupier_guid = string.Empty; @@ -156,7 +147,7 @@ public partial class BattleInstanceUpdateAction } } - private async Task roundingUpdateWithReward(GameModeTPSFreeForAll battleInstanceRoom, DateTime roundEndTime, Player player) + private async Task roundingUpdateWithReward(GameModeTPSFreeForAll battleInstanceRoom, DateTime roundEndTime, Player player) { var result = new Result(); @@ -165,22 +156,22 @@ public partial class BattleInstanceUpdateAction List infos = new(); CommonResult common_result = new(); - + var fn_battle_round_update = async delegate() { var attribute = battleInstanceRoom.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!! - {battleInstanceRoom.toBasicString()}"); - if (battleInstanceRoom.m_round_count <= attribute.m_combat_pod_mode.m_current_round) + if (battleInstanceRoom.m_ffa_meta.m_round_count <= attribute.m_combat_pod_mode.m_current_round) { - attribute.m_combat_pod_mode.m_round_state_type = BattleRoundStateType.RoundEndAll; + attribute.m_combat_pod_mode.m_game_state = GameModeState.RoundEndAll; //배틍 종료에 대한 정보를 알려줄지 말지에 대한 noti //need_battle_end_result_noti = true; } else { - attribute.m_combat_pod_mode.m_round_state_type = BattleRoundStateType.RoundWait; + attribute.m_combat_pod_mode.m_game_state = GameModeState.RoundWait; } attribute.m_combat_pod_mode.m_current_state_start_time = roundEndTime; @@ -195,24 +186,24 @@ public partial class BattleInstanceUpdateAction attribute.modifiedEntityAttribute(true); Log.getLogger().debug($"roundingUpdateWithReward attribute info : {JsonConvert.SerializeObject(attribute)}"); - + (result, var doc) = await attribute.toDocBase(); if (result.isFail()) return result; - + //batch 처리 var batch = new QueryBatchEx(player, LogActionType.BattleRoundStateUpdate, server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); if (doc is not null) { - batch.addQuery(new DBQEntityWrite(doc)); + batch.addQuery(new DBQEntityWrite(doc)); } } - + //로그 추가 var room_id = battleInstanceRoom.getRoomId(); var ended_round = attribute.m_combat_pod_mode.m_current_round; - var round_state = attribute.m_combat_pod_mode.m_round_state_type; + var round_state = attribute.m_combat_pod_mode.m_game_state; var users = battleInstanceRoom.getInstanceRoom().tryGetInstanceExistUserForLog(); BattleRoundUpdateBusinessLog business_log = new(room_id, ended_round, round_state, users); batch.appendBusinessLog(business_log); @@ -221,18 +212,18 @@ public partial class BattleInstanceUpdateAction , player.getUserGuid(), player.getUserNickname() , reward_start_step, reward_end_step, rewards); batch.appendBusinessLog(combat_business_log); - + result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) return result; - + //보상 내용 가져올것 var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); NullReferenceCheckHelper.throwIfNull(found_transaction_runner, () => $"found_transaction_runner is null !!! - {battleInstanceRoom.toBasicString()}"); common_result = found_transaction_runner.getCommonResult(); return result; }; - + result = await player.runTransactionRunnerSafelyWithTransGuid(player.getUserGuid(), TransactionIdType.PrivateContents, BattleConstant.BATTLE_INSTANCE_TRANSACTION_NAME, fn_battle_round_update); if (result.isFail()) { @@ -241,38 +232,40 @@ public partial class BattleInstanceUpdateAction return result; } - BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(battleInstanceRoom); + //BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(battleInstanceRoom); BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(battleInstanceRoom, infos); BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(battleInstanceRoom); BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_REWARD(battleInstanceRoom, player.getUserGuid(), common_result); - + return result; } - private async Task roundingUpdateWithoutReward(GameModeTPSFreeForAll battleInstanceRoom, DateTime roundEndTime) + private async Task roundingUpdateWithoutReward(GameModeTPSFreeForAll battleInstanceRoom, DateTime roundEndTime) { var result = new Result(); - + bool need_battle_end_result_noti = false; var server_logic = GameServerApp.getServerLogic(); List infos = new(); - + + var log_actor = battleInstanceRoom as IWithLogActor; + NullReferenceCheckHelper.throwIfNull(log_actor, () => $"log_actor is null !!! - {battleInstanceRoom.toBasicString()}"); var fn_battle_round_update = async delegate() { var attribute = battleInstanceRoom.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!! - {battleInstanceRoom.toBasicString()}"); - if (battleInstanceRoom.m_round_count <= attribute.m_combat_pod_mode.m_current_round) + if (battleInstanceRoom.m_ffa_meta.m_round_count <= attribute.m_combat_pod_mode.m_current_round) { - attribute.m_combat_pod_mode.m_round_state_type = BattleRoundStateType.RoundEndAll; + attribute.m_combat_pod_mode.m_game_state = GameModeState.RoundEndAll; //배틍 종료에 대한 정보를 알려줄지 말지에 대한 noti need_battle_end_result_noti = true; } else { - attribute.m_combat_pod_mode.m_round_state_type = BattleRoundStateType.RoundWait; + attribute.m_combat_pod_mode.m_game_state = GameModeState.RoundWait; } attribute.m_combat_pod_mode.m_current_state_start_time = roundEndTime; @@ -285,29 +278,30 @@ public partial class BattleInstanceUpdateAction attribute.modifiedEntityAttribute(true); //batch 처리 - // var batch = new QueryBatch(battleInstanceRoom, LogActionType.BattleRoundStateUpdate, server_logic.getDynamoDbClient()); - // { - // batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); - // } - + var batch = new QueryBatch(battleInstanceRoom, LogActionType.BattleRoundStateUpdate.ToString(), server_logic.getDynamoDbClient()); + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + + //로그 추가 var room_id = battleInstanceRoom.getRoomId(); var ended_round = attribute.m_combat_pod_mode.m_current_round; - var round_state = attribute.m_combat_pod_mode.m_round_state_type; + var round_state = attribute.m_combat_pod_mode.m_game_state; var users = battleInstanceRoom.getInstanceRoom().tryGetInstanceExistUserForLog(); BattleRoundUpdateBusinessLog business_log = new(room_id, ended_round, round_state, users); - //batch.appendBusinessLog(business_log); //kihoon todo : 로그랑 배치 수정해서 올려야 된다. - - //result = await QueryHelper.sendQueryAndBusinessLog(batch); + batch.appendBusinessLog(business_log); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + + await Task.CompletedTask; if (result.isFail()) return result; - - + + return result; }; - - result = await battleInstanceRoom.runTransactionRunnerSafelyWithTransGuid(battleInstanceRoom.getRoomId(), + + result = await battleInstanceRoom.runTransactionRunnerSafelyWithTransGuid(battleInstanceRoom.getRoomId(), TransactionIdType.PrivateContents, BattleConstant.BATTLE_INSTANCE_TRANSACTION_NAME, fn_battle_round_update); if (result.isFail()) { @@ -315,40 +309,40 @@ public partial class BattleInstanceUpdateAction Log.getLogger().error(err_msg); return result; } - - BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(battleInstanceRoom); + + //BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(battleInstanceRoom); BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(battleInstanceRoom, infos); BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(battleInstanceRoom); if (need_battle_end_result_noti) { - + } return result; } - private async Task stepUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, DateTime now) + private async Task stepUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, DateTime now) { - var max_charge_level = battleInstanceRoom.m_ffa_reward_meta - .Where(kvp => kvp.Key == battleInstanceRoom.m_ffa_reward_meta.Keys.Max()) + var max_charge_level = battleInstanceRoom.m_ffa_meta.m_charge_reward_meta + .Where(kvp => kvp.Key == battleInstanceRoom.m_ffa_meta.m_charge_reward_meta.Keys.Max()) .Select(kvp => kvp.Key) .FirstOrDefault(); - - //최대 스텝이면 알려줄것 없다. + + //최대 스텝이면 알려줄것 없다. if (max_charge_level <= attribute.m_combat_pod_mode.m_charged_step) return; - + //시간이 아직 안지났을 경우는 m_charged_step 시간을 체크 해줘야 된다... var round_start_time = attribute.m_combat_pod_mode.m_current_state_start_time; var current_charge_level = attribute.m_combat_pod_mode.m_charged_step; - var next_charge_level = current_charge_level + 1; - + var next_charge_level = current_charge_level + 1; + //다음 충전까지 필요한 시간의 합 - var next_charge_time_total = battleInstanceRoom.m_ffa_reward_meta - .Where(kvp => kvp.Key <= next_charge_level) - .Sum(kvp => kvp.Value.ChargeTime); - + var next_charge_time_total = battleInstanceRoom.m_ffa_meta.m_charge_reward_meta + .Where(kvp => kvp.Key <= next_charge_level) + .Sum(kvp => kvp.Value.m_charge_time); + //여기도 using만 사용 로그만 남길것... using (var releaser = await battleInstanceRoom.getAsyncLock()) { @@ -361,7 +355,7 @@ public partial class BattleInstanceUpdateAction } } - private async Task podCombatRewardUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, DateTime now) + private async Task podCombatRewardUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, DateTime now) { var occupier_guid = string.Empty; CommonResult common_result = new(); @@ -380,20 +374,25 @@ public partial class BattleInstanceUpdateAction var charged_step = attribute.m_combat_pod_mode.m_charged_step; var rewarded_step = attribute.m_combat_pod_mode.m_rewarded_step; if (charged_step <= rewarded_step) return; - + var accupied_time = attribute.m_combat_pod_mode.m_pod_combat.m_occupied_time; - - - var rewadable_time = accupied_time.AddSeconds(battleInstanceRoom.m_ffa_config_meta.GetRewardTime); + + + var rewadable_time = accupied_time.AddSeconds(battleInstanceRoom.m_ffa_meta.m_get_reward_time); if (rewadable_time > now) return; var result = new Result(); var fn_pod_combat_reward_update = async delegate() { - //여기서 보상처리 가능하다. - var rewards = makePodCombatOccupyReward(rewarded_step, charged_step, battleInstanceRoom); - result = await addPodCommbatOccupyReward(player, battleInstanceRoom, rewards); + //여기서 보상처리 가능하다. + var a_rewards = makePodCombatOccupyReward(rewarded_step, charged_step, battleInstanceRoom); + List reward_list = new(); + foreach (var reward in a_rewards) + { + reward_list.Add(BattleRoomHelper.modifyRewardHotTime(reward, battleInstanceRoom.m_ffa_meta.m_hot_time)); + } + result = await addPodCommbatOccupyReward(player, reward_list); if (result.isFail()) { string err_msg = $"addPodCommbatOccupyReward return fail.. error : {result.toBasicString()}"; @@ -403,9 +402,9 @@ public partial class BattleInstanceUpdateAction attribute.m_combat_pod_mode.m_rewarded_step = charged_step; attribute.m_combat_pod_mode.m_pod_combat.m_occupied_time = rewadable_time; - - var max_charge_level = battleInstanceRoom.m_ffa_reward_meta - .Where(kvp => kvp.Key == battleInstanceRoom.m_ffa_reward_meta.Keys.Max()) + + var max_charge_level = battleInstanceRoom.m_ffa_meta.m_charge_reward_meta + .Where(kvp => kvp.Key == battleInstanceRoom.m_ffa_meta.m_charge_reward_meta.Keys.Max()) .Select(kvp => kvp.Key) .FirstOrDefault(); if (max_charge_level <= charged_step) @@ -421,7 +420,7 @@ public partial class BattleInstanceUpdateAction { Log.getLogger().error(result.toBasicString()); } - + var server_logic = GameServerApp.getServerLogic(); //batch 처리 var batch = new QueryBatchEx(player, LogActionType.BattlePodCombatOccupyReward, server_logic.getDynamoDbClient()); @@ -429,34 +428,34 @@ public partial class BattleInstanceUpdateAction batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); if (doc is not null) { - batch.addQuery(new DBQEntityWrite(doc)); + batch.addQuery(new DBQEntityWrite(doc)); } } BattleObjectLogInfo info = new(); - info.m_battle_object_type = EBattleObjectType.Pod_Combat; - + info.m_battle_object_type = EGameModeObjectType.Pod_Combat; + List log_infos = new(); var room_id = battleInstanceRoom.getRoomId(); var current_round = attribute.m_combat_pod_mode.m_current_round; - var round_state_type = attribute.m_combat_pod_mode.m_round_state_type; + var round_state_type = attribute.m_combat_pod_mode.m_game_state; var business_log = new BattlePodCombatRewardBusinessLog(room_id, current_round, round_state_type - , player.getUserGuid(), player.getUserNickname(), charged_step, rewarded_step, rewards); - + , player.getUserGuid(), player.getUserNickname(), charged_step, rewarded_step, reward_list); + batch.appendBusinessLog(business_log); - + result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) return result; - + //보상 내용 가져올것 var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); NullReferenceCheckHelper.throwIfNull(found_transaction_runner, () => $"found_transaction_runner is null !!! - {player.toBasicString()}"); common_result = found_transaction_runner.getCommonResult(); return result; }; - - - result = await player.runTransactionRunnerSafelyWithTransGuid(player.getUserGuid(), + + + result = await player.runTransactionRunnerSafelyWithTransGuid(player.getUserGuid(), TransactionIdType.PrivateContents, BattleConstant.BATTLE_INSTANCE_TRANSACTION_NAME, fn_pod_combat_reward_update); if (result.isFail()) { @@ -465,7 +464,7 @@ public partial class BattleInstanceUpdateAction return; } } - + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(battleInstanceRoom); BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_REWARD(battleInstanceRoom, occupier_guid, common_result); if (need_pod_combat_noti) @@ -473,40 +472,40 @@ public partial class BattleInstanceUpdateAction BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(battleInstanceRoom); } } - - private async Task battleInstanceEndStateUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) - { - var result = new Result(); - await Task.CompletedTask; - var all_round_end_time = attribute.m_combat_pod_mode.m_current_state_start_time; - var end_time = all_round_end_time.AddSeconds(battleInstanceRoom.m_ffa_config_meta.ResultUIWaitTime); - - var now = DateTimeHelper.Current; - if (now < end_time) return result; - - using (var releaser = await battleInstanceRoom.getAsyncLock()) - { - attribute.m_combat_pod_mode.m_round_state_type = BattleRoundStateType.Destroyed; - attribute.m_combat_pod_mode.m_current_state_start_time = end_time; - } - - var users = battleInstanceRoom.getInstanceRoom().tryGetInstanceExistUserForLog(); - - //여기서 배틀 인스턴스가 완전히 shutdown 됐다는 Noti 전달 - BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(battleInstanceRoom); - - //log 추가 - var invokers = new List(); - var log_action = new LogAction(LogActionType.BattleRoundStateUpdate.ToString()); - - var room_id = battleInstanceRoom.getRoomId(); - var ended_round = attribute.m_combat_pod_mode.m_current_round; - var round_state = attribute.m_combat_pod_mode.m_round_state_type; - BattleRoundUpdateBusinessLog business_log = new(room_id, ended_round, round_state, users); - invokers.Add(business_log); - //BusinessLogger.collectLogs(log_action, battleInstanceRoom, invokers);// kihoon todo : 이거 수정해야된다. - - return result; - } -} \ No newline at end of file + // private async Task battleInstanceEndStateUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + // { + // var result = new Result(); + // await Task.CompletedTask; + // + // var all_round_end_time = attribute.m_combat_pod_mode.m_current_state_start_time; + // var end_time = all_round_end_time.AddSeconds(battleInstanceRoom.m_ffa_meta.m_result_ui_wait_time); + // + // var now = DateTimeHelper.Current; + // if (now < end_time) return result; + // + // using (var releaser = await battleInstanceRoom.getAsyncLock()) + // { + // attribute.m_combat_pod_mode.m_game_state = GameModeState.Destroyed; + // attribute.m_combat_pod_mode.m_current_state_start_time = end_time; + // } + // + // var users = battleInstanceRoom.getInstanceRoom().tryGetInstanceExistUserForLog(); + // + // //여기서 배틀 인스턴스가 완전히 shutdown 됐다는 Noti 전달 + // BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(battleInstanceRoom); + // + // //log 추가 + // var invokers = new List(); + // var log_action = new LogAction(LogActionType.BattleRoundStateUpdate.ToString()); + // + // var room_id = battleInstanceRoom.getRoomId(); + // var ended_round = attribute.m_combat_pod_mode.m_current_round; + // var round_state = attribute.m_combat_pod_mode.m_game_state; + // BattleRoundUpdateBusinessLog business_log = new(room_id, ended_round, round_state, users); + // invokers.Add(business_log); + // BusinessLogger.collectLogs(log_action, battleInstanceRoom, invokers); + // + // return result; + // } +} diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceUpdateAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceUpdateAction.cs index 00ab6ae..963c502 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceUpdateAction.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleInstanceUpdateAction.cs @@ -1,5 +1,7 @@ using GameServer.Contents.Battle.Reward; +using GameServer.Contents.GameMode.Helper; using GameServer.Contents.GameMode.Mode_Battle.Manage; +using GameServer.Contents.Reward; using MetaAssets; using Nettention.Proud; using Newtonsoft.Json; @@ -28,252 +30,8 @@ public partial class BattleInstanceUpdateAction : EntityActionBase { } - - public async Task update() - { - var battle_instance_room = getOwner() as GameModeTPSFreeForAll; - NullReferenceCheckHelper.throwIfNull(battle_instance_room, () => $"battle_instance_room is null !!!"); - var attribute = battle_instance_room.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); - var result = await battleObjectStateUpdate(battle_instance_room, attribute); - if (result.isFail()) - { - Log.getLogger().error($"battleObjectStateUpdate result error !!! result : {result.toBasicString()}"); - return result; - } - - result = await battleInstanceStateUpdate(battle_instance_room, attribute); - if (result.isFail()) - { - Log.getLogger().error($"battleInstanceStateUpdate result error !!! result : {result.toBasicString()}"); - return result; - } - - result = await battleInstancHostUpdate(battle_instance_room); - if (result.isFail()) - { - Log.getLogger().error($"battleInstancHostUpdate result error !!! result : {result.toBasicString()}"); - return result; - } - - - return result; - } - - private async Task battleInstancHostUpdate(GameModeTPSFreeForAll battleInstanceRoom) - { - var result = new Result(); - - string host_user_guid = string.Empty; - using (var releaser = await battleInstanceRoom.getAsyncLock()) - { - //이미 host 지정이 끝났으면 리턴 - //kihoon todo : 일단 계속 체크해서 호스트가 얼마나 바뀌는지 확인 해보고 적절한 주기 찾아볼것 - if (!battleInstanceRoom.m_host_migrator.getHostUserGuid().Equals(string.Empty)) return result; - - var p2p_group_id = battleInstanceRoom.getInstanceRoom().getMap().getP2PGroupId(); - result = battleInstanceRoom.m_host_migrator.defineHost(p2p_group_id, new SuperPeerSelectionPolicy(), new HostID[1]); - if (result.isFail()) return result; - - host_user_guid = battleInstanceRoom.m_host_migrator.getHostUserGuid(); - - Log.getLogger().info($"battleInstancHost set complete host_user_guid : {host_user_guid}"); - } - - BattleRoomNotifyHelper.broadcast_GS2C_NTF_P2P_HOST_UPDATE(battleInstanceRoom.getInstanceRoom(), host_user_guid); - - return result; - } - - private async Task battleObjectStateUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) - { - var result = new Result(); - await Task.CompletedTask; - - var now = DateTimeHelper.Current; - List infos = new(); - List log_infos = new(); - using (var releaser = await battleInstanceRoom.getAsyncLock()) - { - (var weapon_info, var weapon_log_info) = battleObjectWeaponStateUpdate(attribute, now); - infos.AddRange(weapon_info); - log_infos.AddRange(weapon_log_info); - - (var buff_info, var buff_log_info) = battleObjectBuffStateUpdate(attribute, now); - infos.AddRange(buff_info); - log_infos.AddRange(buff_log_info); - - - (var pickup_pod_info, var pickup_pod_log_info) = battleObjectPickupPodUpdate(attribute, now); - infos.AddRange(pickup_pod_info); - log_infos.AddRange(pickup_pod_log_info); - } - - if (infos.Count > 0) - { - BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(battleInstanceRoom, infos); - - var invokers = new List(); - var log_action = new LogAction(LogActionType.BattleObjectStateUpdate.ToString()); - var business_log = new BattleObjectStateUpdateBusinessLog(battleInstanceRoom.getInstanceRoom().getMap().m_room_id, log_infos); - invokers.Add(business_log); - BusinessLogger.collectLogs(log_action, battleInstanceRoom, invokers); //kihoon todo : 이거 수정해야된다. - } - return result; - } - - private (List, List) battleObjectPickupPodUpdate(BattleInstanceSnapshotAttribute attribute, DateTime now) - { - // if (attribute.m_combat_pod_mode.m_round_state_type.Equals(BattleRoundStateType.RoundEndAll) || - // attribute.m_combat_pod_mode.m_round_state_type.Equals(BattleRoundStateType.Destroyed)) return new(); - - List infos = new(); - List log_infos = new(); - var battle_instance_room = getOwner() as GameModeTPSFreeForAll; - NullReferenceCheckHelper.throwIfNull(battle_instance_room, () => $"battle_instance_room is null !!!"); - - foreach (var generated_info in attribute.m_combat_pod_mode.m_pickup_pod_generated_info) - { - var group_id = generated_info.Key.Item1; - var idx = generated_info.Key.Item2; - - if (false == attribute.m_combat_pod_mode.m_pickup_pod_generated_info.TryGetValue(generated_info.Key, out var generatedInfo)) - { - Log.getLogger().error($"m_pickup_pod_generated_info i : {idx} not exist!!!"); - continue; - } - - var anchor_guid = generatedInfo.m_anchor_guid; - var next_generated_time = generatedInfo.m_next_generate_time; - - //아직 시간이 안됐거나, 할당된게 있으면 continue; - if (now < next_generated_time || anchor_guid.Equals(string.Empty) == false) continue; - - //비어 있으면 할당 해준다. 시간체크는 보상 받았을때 해준다. - (var result, var allocated_anchor_guid) = BattleRoomHelper.getRandomPickupPod(group_id, idx, battle_instance_room, attribute); - if (result.isFail()) - { - Log.getLogger(result.toBasicString()); - continue; - } - - if(false == battle_instance_room.getInstanceRoom().getMap().getAnchors().TryGetValue(allocated_anchor_guid, out var anchorInfo)) - { - Log.getLogger().error($"not exist anchor anchor_guid : {allocated_anchor_guid}"); - continue; - } - - var table_id = anchorInfo.AnchorProp.TableId; - if (false == MetaData.Instance._BattleObjectSpawnGroupMetaTable.TryGetValue(table_id, out var battle_object_spawn_group)) - { - Log.getLogger().error($"not exist _BattleObjectSpawnGroupMetaTable anchor_guid : {allocated_anchor_guid}, tableid : {table_id}"); - continue; - } - - PickupPodGeneratedInfo new_generated_info = new(generated_info.Key.Item1, generated_info.Key.Item2, allocated_anchor_guid, generatedInfo.m_anchor_guid, generatedInfo.m_next_generate_time); - - attribute.m_combat_pod_mode.m_pickup_pod_generated_info.AddOrUpdate(generated_info.Key, new_generated_info, (i, tuple) => new_generated_info); - if (false == attribute.m_combat_pod_mode.m_pickup_pods.TryGetValue(allocated_anchor_guid, out var pickupPod)) - { - var err_msg = $"pickup pod date not exist !! allocated_anchor_guid : {allocated_anchor_guid}, roomId : {battle_instance_room.getRoomId()}"; - Log.getLogger().error(err_msg); - continue; - } - - pickupPod.m_is_active = true; - - BattleObjectInfo info = new(); - info.AnchorGuid = allocated_anchor_guid; - info.IsActive = pickupPod.m_is_active ? BoolType.True : BoolType.False; - infos.Add(info); - - BattleObjectLogInfo log_info = new(); - log_info.m_anchor_guid = pickupPod.m_anchor_guid; - log_info.m_battle_object_type = pickupPod.m_type; - log_info.m_active_time = pickupPod.m_active_time; - log_infos.Add(log_info); - } - - - return (infos, log_infos); - } - - private (List, List) battleObjectBuffStateUpdate(BattleInstanceSnapshotAttribute attribute, DateTime now) - { - List infos = new(); - List log_infos = new(); - foreach (BattleObjectBuff buff in attribute.m_combat_pod_mode.m_buffs.Values) - { - if (buff.m_active_time <= now && buff.m_is_active == false) - { - buff.m_is_active = true; - - BattleObjectInfo info = new(); - info.AnchorGuid = buff.m_anchor_guid; - info.IsActive = BoolType.True; - infos.Add(info); - - BattleObjectLogInfo log_info = new(); - log_info.m_anchor_guid = buff.m_anchor_guid; - log_info.m_battle_object_type = buff.m_type; - log_info.m_active_time = buff.m_active_time; - log_infos.Add(log_info); - } - } - - return (infos, log_infos); - } - - private (List, List) battleObjectWeaponStateUpdate(BattleInstanceSnapshotAttribute attribute, DateTime now) - { - List infos = new(); - List log_infos = new(); - foreach (BattleObjectWeapon weapon in attribute.m_combat_pod_mode.m_weapons.Values) - { - if (weapon.m_active_time <= now && weapon.m_is_active == false) - { - weapon.m_is_active = true; - - BattleObjectInfo info = new(); - info.AnchorGuid = weapon.m_anchor_guid; - info.IsActive = BoolType.True; - infos.Add(info); - - BattleObjectLogInfo log_info = new(); - log_info.m_anchor_guid = weapon.m_anchor_guid; - log_info.m_battle_object_type = weapon.m_type; - log_info.m_active_time = weapon.m_active_time; - log_infos.Add(log_info); - } - } - - return (infos, log_infos); - } - - public Result battleObjectStateInitAndNotify(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) - { - var infos = new List(); - var result = new Result(); - - foreach (BattleObjectPodStorage storage in attribute.m_combat_pod_mode.m_pod_storages.Values) - { - storage.m_reward_cnt = 0; - - storage.m_is_active = true; - BattleObjectInfo info = new(); - info.AnchorGuid = storage.m_anchor_guid; - info.IsActive = BoolType.True; - infos.Add(info); - } - BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(battleInstanceRoom, infos); - return result; - } - - - - - public async Task createCombatPod(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + public async Task createCombatPod(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) { await Task.CompletedTask; var result = new Result(); @@ -283,12 +41,12 @@ public partial class BattleInstanceUpdateAction : EntityActionBase if (result.isFail()) return result; attribute.m_combat_pod_mode.m_pod_combat = new BattleObjectPodCombat(combat_pod_guid, anchor_guid, DateTimeHelper.Current); - + attribute.m_combat_pod_mode.m_pod_combat.m_state = PodCombatStateType.None;//처음 wait 상태일때는 None 상태로 전달 return result; } - public async Task createPickupPodReward(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + public async Task createPickupPodReward(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) { await Task.CompletedTask; var result = new Result(); @@ -333,7 +91,7 @@ public partial class BattleInstanceUpdateAction : EntityActionBase } - public async Task createPickupPodRewardByGroup(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, Int32 groupId, Int32 maxRewardCount) + public async Task createPickupPodRewardByGroup(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, Int32 groupId, Int32 maxRewardCount) { await Task.CompletedTask; @@ -371,14 +129,14 @@ public partial class BattleInstanceUpdateAction : EntityActionBase } - public async Task<(Result, List, int, int)> podCombatUpdateIfHasOccupier(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, Player player) + public async Task<(Result, List, int, int)> podCombatUpdateIfHasOccupier(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute, Player player) { var result = new Result(); var occupier_guid = attribute.m_combat_pod_mode.m_pod_combat.m_current_occupier_guid; if (occupier_guid.Equals(string.Empty)) return (result, new(), 0, 0); - var max_charge_level = battleInstanceRoom.m_ffa_reward_meta - .Where(kvp => kvp.Key == battleInstanceRoom.m_ffa_reward_meta.Keys.Max()) + var max_charge_level = battleInstanceRoom.m_ffa_meta.m_charge_reward_meta + .Where(kvp => kvp.Key == battleInstanceRoom.m_ffa_meta.m_charge_reward_meta.Keys.Max()) .Select(kvp => kvp.Key) .FirstOrDefault(); @@ -386,17 +144,23 @@ public partial class BattleInstanceUpdateAction : EntityActionBase var rewardable_step_end = max_charge_level; var rewards = makePodCombatOccupyReward(rewardable_step_start, rewardable_step_end, battleInstanceRoom); - result = await addPodCommbatOccupyReward(player, battleInstanceRoom, rewards); + List reward_list = new(); + foreach (var reward in rewards) + { + reward_list.Add(BattleRoomHelper.modifyRewardHotTime(reward, battleInstanceRoom.m_ffa_meta.m_hot_time)); + } + + result = await addPodCommbatOccupyReward(player, reward_list); if (result.isFail()) return (result, new(), 0, 0); attribute.m_combat_pod_mode.m_rewarded_step = max_charge_level; attribute.m_combat_pod_mode.m_charged_step = max_charge_level; - return (result, rewards, rewardable_step_start, rewardable_step_end); + return (result, reward_list, rewardable_step_start, rewardable_step_end); } - private async Task addPodCommbatOccupyReward(Player player, GameModeTPSFreeForAll battleInstanceRoom, List rewards) + private async Task addPodCommbatOccupyReward(Player player,List rewards) { IReward reward_proc = new RewardPodCombat(player, player.getUserGuid(), rewards); var result = await RewardManager.It.proceedRewardProcess(reward_proc); @@ -408,36 +172,34 @@ public partial class BattleInstanceUpdateAction : EntityActionBase return result; } - private List makePodCombatOccupyReward(int start, int end, GameModeTPSFreeForAll battleInstanceRoom) + private List makePodCombatOccupyReward(int start, int end, GameModeTPSFreeForAll battleInstanceRoom) { - List rewards = new(); + List ret_rewards = new(); for (int i = start + 1; i <= end; i++) { if (i == 0) continue; - if (false == battleInstanceRoom.m_ffa_reward_meta.TryGetValue(i, out var ffaRewardData)) + if (false == battleInstanceRoom.m_ffa_meta.m_charge_reward_meta.TryGetValue(i, out var ffaRewardData)) { - Log.getLogger().error($"m_ffa_reward_meta not exist battleInstanceRoom i : {i}, meta : {JsonConvert.SerializeObject(battleInstanceRoom.m_ffa_reward_meta)}"); + Log.getLogger().error($"m_ffa_reward_meta not exist battleInstanceRoom i : {i}, meta : {JsonConvert.SerializeObject(battleInstanceRoom.m_ffa_meta.m_charge_reward_meta)}"); continue; } - var item_id = ffaRewardData.RewardItemID; - var item_count = ffaRewardData.RewardCount * battleInstanceRoom.m_hot_time_reward; - - RewardMutable reward_metable = new(); - reward_metable.Item = new(); - reward_metable.Item.Id = item_id; - reward_metable.Item.Count = item_count; - - MetaAssets.Reward reward = new MetaAssets.Reward(reward_metable); + var reward_group_id = ffaRewardData.m_reward_group_id; + if(MetaData.Instance._RewardMetaTable.TryGetValue(reward_group_id, out var reward_meta_data_list) == false) + { + Log.getLogger().error($"reward_meta_data_list not exist battleInstanceRoom i : {i}, meta : {JsonConvert.SerializeObject(ffaRewardData)}"); + continue; + } - rewards.Add(reward); + var rewards = RewardHelper.convertRewardMetaToMetaAssetrewards(reward_meta_data_list); + ret_rewards.AddRange(rewards); - Log.getLogger().Debug($"pod Combat reward Added step : {i}, ItemId : {item_id}, itemCount : {item_count}"); + Log.getLogger().Debug($"pod Combat reward Added step : {i}, reward_group_id : {reward_group_id}"); } - return rewards; + return ret_rewards; } private List battleObjectPickupPodsDeactive(BattleInstanceSnapshotAttribute attribute) diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleLocationAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleLocationAction.cs index 9785895..2ada7f3 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleLocationAction.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleLocationAction.cs @@ -7,7 +7,7 @@ namespace GameServer; internal partial class LocationAction { - public async Task tryMoveToBattleIndun(InstanceRoomInfo instanceRoomInfo, Pos? enterPos = null, bool isReturn = false) + public async Task tryMoveToBattleIndun(InstanceRoomInfo instanceRoomInfo, string gameTeamId = "", Pos? enterPos = null, bool isReturn = false) { var result = new Result(); var err_msg = string.Empty; @@ -16,7 +16,7 @@ internal partial class LocationAction NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); var server_logic = GameServerApp.getServerLogic(); - + var location_attribute = player.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(location_attribute, () => $"location_attribute is null !!! - {player.toBasicString()}"); @@ -31,14 +31,15 @@ internal partial class LocationAction return result; } - + + // 현재 서버가 채널인 경우 if (server_logic.getServerType().toServerType() == ServerType.Channel) { location_attribute.LastestChannelServerLocation.ServerName = server_logic.getServerName(); location_attribute.LastestChannelServerLocation.WorldMetaId = (int)server_logic.getWorldId(); location_attribute.LastestChannelServerLocation.SpawnPos = m_current_loaction.SpawnPos; location_attribute.LastestChannelServerLocation.ForwardAngle = m_current_loaction.ForwardAngle; - + server_logic.getReturnManager().addReturnUser(player.getUserGuid()); } else if (isReturn == false) @@ -51,10 +52,10 @@ internal partial class LocationAction indun_location.ForwardAngle = m_current_loaction.ForwardAngle; location_attribute.ReturnIndunLocations.Add(indun_location); - + server_logic.getReturnManager().addReturnUser(player.getUserGuid()); } - + if (enterPos != null) { enterPos.Z += 100; @@ -62,7 +63,8 @@ internal partial class LocationAction location_attribute.EnterIndunLocation.InstanceRoomId = instanceRoomInfo.roomId; location_attribute.EnterIndunLocation.InstanceMetaId = instanceRoomInfo.InstanceId; - location_attribute.EnterIndunLocation.fromPos(enterPos == null ? MapManager.Instance.GetStartPos(indun_data.RoomFile) : enterPos); + location_attribute.EnterIndunLocation.GameTeamId = gameTeamId; + location_attribute.EnterIndunLocation.fromPos(enterPos == null ? MapManager.Instance.GetStartPos(indun_data.RoomFile) : enterPos); location_attribute.modifiedEntityAttribute(); var location_cache = m_location_cache_request_nullable.getLocationCache(); @@ -88,4 +90,4 @@ internal partial class LocationAction return m_location_cache_request_nullable; } -} \ No newline at end of file +} diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleObjectInteractAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleObjectInteractAction.cs index e421bdd..71ad2db 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleObjectInteractAction.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleObjectInteractAction.cs @@ -72,7 +72,7 @@ public class BattleObjectInteractAction : EntityActionBase handler.m_interaction_log_info.m_has_pod_combat = has_pod_combat; handler.m_interaction_log_info.m_pod_type = ECombatPodType.Storage; - + handler.m_battle_object.m_is_active = false; var now = DateTimeHelper.Current; if (has_pod_combat) @@ -113,8 +113,6 @@ public class BattleObjectInteractAction : EntityActionBase var next_active_time = now.AddSeconds(handler.m_battle_object_meta.RespawnTime); handler.m_object_active_time = handler.m_interaction_log_info.m_object_next_active_time = handler.m_battle_object.m_active_time = next_active_time; handler.m_battle_object.m_is_active = handler.m_interaction_log_info.m_is_active = false; - - } public async Task interactPodCombat(BattleObjectInteractionLogicHandler handler, Player player) @@ -151,10 +149,11 @@ public class BattleObjectInteractAction : EntityActionBase combat_pod.m_occupied_time = now; - handler.m_interaction_log_info.m_battle_object_type = EBattleObjectType.Pod_Combat; + handler.m_interaction_log_info.m_battle_object_type = EGameModeObjectType.Pod_Combat; handler.m_interaction_log_info.m_pod_type = ECombatPodType.Pod; handler.m_interaction_log_info.m_pod_combat_pos = combat_pod.m_currenct_Pos; handler.m_interaction_log_info.m_pod_combat_state = combat_pod.m_state; + attribute.modifiedEntityAttribute(); return result; } diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleObjectRewardAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleObjectRewardAction.cs index a178ba8..37c8948 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleObjectRewardAction.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleObjectRewardAction.cs @@ -5,6 +5,9 @@ using MetaAssets; using Newtonsoft.Json; using ServerBase; using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCommon.Contents.GameMode; +using ServerCommon.GameMode; using ServerCore; namespace GameServer; @@ -44,13 +47,13 @@ public class BattleObjectRewardAction : EntityActionBase var reward_id = pod_storage_meta.TypeID; var reward_count = pod_storage_meta.ObjectValue; - var battle_instance_room = getOwner() as GameModeTPSFreeForAll; + var battle_instance_room = getOwner() as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(battle_instance_room, () => $"battle_instance_room is null !!!"); RewardMutable reward_metable = new(); reward_metable.Item = new(); reward_metable.Item.Id = reward_id; - reward_metable.Item.Count = reward_count * battle_instance_room.m_hot_time_reward; + reward_metable.Item.Count = reward_count * battle_instance_room.m_ffa_meta.m_hot_time; MetaAssets.Reward reward = new MetaAssets.Reward(reward_metable); List rewards = new(){reward}; @@ -65,7 +68,7 @@ public class BattleObjectRewardAction : EntityActionBase return result; } - public async Task pickupPodReward(Player player, BattleInstanceSnapshotAttribute attribute, BattleObjectInteractionLogicHandler handler) + public async Task pickupPodReward(Player player, BattleInstanceSnapshotAttribute attribute, BattleObjectInteractionLogicHandler handler)//todo : 코드이관후 삭제 처리 예정 { var result = new Result(); @@ -79,7 +82,7 @@ public class BattleObjectRewardAction : EntityActionBase return result; } - var battle_instance_room = getOwner() as GameModeTPSFreeForAll; + var battle_instance_room = getOwner() as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(battle_instance_room, () => $"battle_instance_room is null !!!"); Int32 idx = Int32.MinValue; @@ -125,15 +128,37 @@ public class BattleObjectRewardAction : EntityActionBase PickupPodGeneratedInfo assigned_generated_info = new(group_id, idx, string.Empty, rewarded_anchor_guid, next_gen_time); attribute.m_combat_pod_mode.m_pickup_pod_generated_info.AddOrUpdate((group_id, idx), assigned_generated_info, (key, existingValue) => assigned_generated_info); - var pickup_pod_meta = MetaData.Instance._BattlePickupPodMeta; - RewardMutable reward_metable = new(); - reward_metable.Item = new(); - reward_metable.Item.Id = pickup_pod_meta.TypeID; - reward_metable.Item.Count = pickup_pod_meta.ObjectValue * battle_instance_room.m_hot_time_reward; - MetaAssets.Reward reward = new MetaAssets.Reward(reward_metable); - List rewards = new(){reward}; + + if (false == MetaData.Instance.m_game_mode_all_metas.TryGetValue(battle_instance_room.getGameModeId(), out var gameModeMeta)) + { + err_msg = $"not exist m_game_mode_all_metas anchor_guid : {handler.m_interaction_anchor_guid}, tableid : {table_id}, gameMode : {battle_instance_room.getGameModeId()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.BattleInstanceObjectMetaNotExist, err_msg); + return result; + } + + var detail_meta = gameModeMeta.m_detail_meta as TPSFfaMeta; + NullReferenceCheckHelper.throwIfNull(detail_meta, () => $"detail_meta is null !!!"); + + var pickup_pod_reward_condition = detail_meta.getPickupPodRewardCondition() as TpsFfaPickupPodRewardCondition; + NullReferenceCheckHelper.throwIfNull(pickup_pod_reward_condition, () => $"pickup_pod_reward_condition is null !!!"); - Log.getLogger().info($"pickup pods reward : {JsonConvert.SerializeObject(rewards)}"); + if (false == MetaData.Instance._RewardMetaTable.TryGetValue(pickup_pod_reward_condition.m_reward_group_id, out var rewardMetas)) + { + err_msg = $"not exist pickup_pod_reward_condition reward meta rewardGroupId : {pickup_pod_reward_condition.m_reward_group_id}, tableid : {table_id}, gameMode : {battle_instance_room.getGameModeId()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.RewardInfoNotExist, err_msg); + return result; + } + + var rewards = new List(); + + + foreach (var reward in rewardMetas) + { + var modified_reward = BattleRoomHelper.modifyRewardHotTime(reward.Reward, battle_instance_room.m_ffa_meta.m_hot_time); + rewards.Add(modified_reward); + } IReward reward_proc = new RewardPickupPod(player, player.getUserGuid(), rewards); result = await RewardManager.It.proceedRewardProcess(reward_proc); if (result.isFail()) @@ -145,6 +170,108 @@ public class BattleObjectRewardAction : EntityActionBase return result; } + + + + public async Task pickupPodReward(Player player, BattleInstanceSnapshotAttribute attribute, TpsFfaPickupPodInteractionDataHandler handler) + { + var result = new Result(); + string err_msg = string.Empty; + + if (false == attribute.m_combat_pod_mode.m_pickup_pods.TryGetValue(handler.m_interaction_anchor_guid, out var pickupPod)) + { + err_msg = $"Not exist pickup pod anchor : {handler.m_interaction_anchor_guid}, player : {player.toBasicString()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.BattleInstanceObjectMetaNotExist, err_msg); + return result; + } + var battle_instance_room = getOwner() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(battle_instance_room, () => $"battle_instance_room is null !!!"); + + Int32 idx = Int32.MinValue; + Int32 group_id = 0; + string rewarded_anchor_guid = String.Empty; + foreach (var generated_info in attribute.m_combat_pod_mode.m_pickup_pod_generated_info) + { + var generated_info_tuple = generated_info.Value; + + + if (false == handler.m_interaction_anchor_guid.Equals(generated_info_tuple.m_anchor_guid)) + { + Log.getLogger().info($"m_pickup_pod_generated_info data interaction anchor_guid : {handler.m_interaction_anchor_guid}, reward setted anchor_guid : {generated_info_tuple.m_anchor_guid} "); + continue; + } + + (group_id, idx) = generated_info.Key; + rewarded_anchor_guid = generated_info_tuple.m_anchor_guid; + Log.getLogger().info($"rewarded_anchor_guid rewarded_anchor_guid : {rewarded_anchor_guid}, groupId : {group_id}, idx : {idx}"); + break; + } + + if (idx == Int32.MinValue) return result; //보상줄게 없다는 얘기이므로 그량 리턴 + if(false == battle_instance_room.getInstanceRoom().getMap().getAnchors().TryGetValue(handler.m_interaction_anchor_guid, out var anchorInfo)) + { + err_msg = $"not exist anchor anchor_guid : {handler.m_interaction_anchor_guid}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.BattleInstanceNotExistAnchors, err_msg); + return result; + } + + var table_id = anchorInfo.AnchorProp.TableId; + if (false == MetaData.Instance._BattleObjectSpawnGroupMetaTable.TryGetValue(table_id, out var battle_object_spawn_group)) + { + err_msg = $"not exist _BattleObjectSpawnGroupMetaTable anchor_guid : {handler.m_interaction_anchor_guid}, tableid : {table_id}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.BattleInstanceObjectMetaNotExist, err_msg); + return result; + } + + var now = DateTimeHelper.Current; + var next_gen_time = now.AddSeconds(battle_object_spawn_group.RespawnTime); + PickupPodGeneratedInfo assigned_generated_info = new(group_id, idx, string.Empty, rewarded_anchor_guid, next_gen_time); + attribute.m_combat_pod_mode.m_pickup_pod_generated_info.AddOrUpdate((group_id, idx), assigned_generated_info, (key, existingValue) => assigned_generated_info); + + + if (false == MetaData.Instance.m_game_mode_all_metas.TryGetValue(battle_instance_room.getGameModeId(), out var gameModeMeta)) + { + err_msg = $"not exist m_game_mode_all_metas anchor_guid : {handler.m_interaction_anchor_guid}, tableid : {table_id}, gameMode : {battle_instance_room.getGameModeId()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.BattleInstanceObjectMetaNotExist, err_msg); + return result; + } + + var detail_meta = gameModeMeta.m_detail_meta as TPSFfaMeta; + NullReferenceCheckHelper.throwIfNull(detail_meta, () => $"detail_meta is null !!!"); + + var pickup_pod_reward_condition = detail_meta.getPickupPodRewardCondition() as TpsFfaPickupPodRewardCondition; + NullReferenceCheckHelper.throwIfNull(pickup_pod_reward_condition, () => $"pickup_pod_reward_condition is null !!!"); + + if (false == MetaData.Instance._RewardMetaTable.TryGetValue(pickup_pod_reward_condition.m_reward_group_id, out var rewardMetas)) + { + err_msg = $"not exist pickup_pod_reward_condition reward meta rewardGroupId : {pickup_pod_reward_condition.m_reward_group_id}, tableid : {table_id}, gameMode : {battle_instance_room.getGameModeId()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.RewardInfoNotExist, err_msg); + return result; + } + + var rewards = new List(); + foreach (var reward in rewardMetas) + { + rewards.Add(reward.Reward); + } + IReward reward_proc = new RewardPickupPod(player, player.getUserGuid(), rewards); + result = await RewardManager.It.proceedRewardProcess(reward_proc); + if (result.isFail()) + { + return result; + } + + var log_info = handler.m_interaction_log_info as TpsFfaPickupPodInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaBuffInteractionLogInfo is null !!!"); + log_info.m_rewards.AddRange(rewards); + + return result; + } } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleZoneMoveAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleZoneMoveAction.cs deleted file mode 100644 index 1e819d0..0000000 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/BattleZoneMoveAction.cs +++ /dev/null @@ -1,182 +0,0 @@ -using Google.Protobuf.WellKnownTypes; -using MetaAssets; -using Newtonsoft.Json; -using ServerBase; -using ServerCommon; -using ServerCommon.BusinessLogDomain; -using ServerCore; -using USER_GUID = System.String; - -namespace GameServer; - -public partial class GameZoneMoveAction -{ - public async Task<(Result, ServerConnectInfo?, List)> tryJoinBattleInstance(USER_GUID userGuid, Int32 eventId, Timestamp eventStartTS, InstanceMetaData instanceMetaData) - { - var result = new Result(); - var err_msg = string.Empty; - - var server_connect_info = new ServerConnectInfo(); - var business_logs = new List(); - - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); - var server_logic = GameServerApp.getServerLogic(); - - //아이템 혹은 재화 사용해서 입장하는건지, 퍼블릭인지 그에 맞게 조건을 체크하는 함수. 아직 기획적인 내용은 없어서 주석처리.. - // result = player.checkInstanceAccess(instanceMetaId); - // if (result.isFail()) - // { - // err_msg = $"Failed to checkInstanceAccess() !!! : {result.toBasicString()}"; - // Log.getLogger().error(err_msg); - // - // return (result, null, business_logs); - // } - - //현재 포지션은 departure pos 가 된다. 관련한 로그 처리 - var departure_position_info = player.getCurrentPositionInfo(); - var departure_position_log_info = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Departure, departure_position_info); - var departure_position_business_log = new PositionBusinessLog(departure_position_log_info); - business_logs.Add(departure_position_business_log); - - BattleInstanceRoomHandler battle_instance_room_handler = new(player.getUserGuid(), instanceMetaData); - //적절한 인스턴스 가져오고, 없으면 생성해서 Join처리 - (result, var instance_room_id) = await battle_instance_room_handler.joinBattleInstance(player.getUserGuid(), eventId, eventStartTS, instanceMetaData.Id); - if (result.isFail()) - { - err_msg = $"Failed to joinInstance() !!! : {result.toBasicString()} - {player.toBasicString()}"; - Log.getLogger().error(err_msg); - - return (result, null, business_logs); - } - - var instance_room_storage = new InstanceRoomStorage(); - instance_room_storage.Init(server_logic.getRedisDb(), ""); - - var instance_room_info = await instance_room_storage.GetInstanceRoomInfo(instance_room_id); - if (instance_room_info == null) - { - err_msg = $"Failed to GetInstanceRoomInfo() !!! : instanceRoomId:{instance_room_id} - {player.toBasicString()}"; - result.setFail(ServerErrorCode.NotExistRoomInfoForEnter, err_msg); - Log.getLogger().error(result.toBasicString()); - - return (result, null, business_logs); - } - - var server_name = ServerType.Indun.toServerName(instance_room_info.InstanceAddress, (ushort)instance_room_info.InstancePort); - - // 이동 예약 걸기 - var message = new ServerMessage.Types.GS2GS_REQ_RESERVATION_ENTER_TO_SERVER(); - message.MoveType = ServerMoveType.Force; - message.RequestUserGuid = userGuid; - message.RequestServerName = server_logic.getServerName(); - - var reserved = await server_logic.getReservationManager().registerReservationEnterToServer(message, server_name); - - // 예약 실패 체크 - if (null == reserved) - { - err_msg = $"Failed to Reservation Enter to server !!! : {server_name} - {player.toBasicString()}"; - result.setFail(ServerErrorCode.FailedToReservationEnter, err_msg); - Log.getLogger().error(result.toBasicString()); - - return (result, null, business_logs); - } - - var location_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); - - result = await location_action.tryMoveToBattleIndun(instance_room_info); - if (result.isFail()) - { - err_msg = $"Failed to tryMoveToIndun() !!! : {result.toBasicString()}"; - Log.getLogger().error(err_msg); - - return (result, null, business_logs); - } - - var buff_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(buff_action, () => $"buff_action is null !!! - {player.toBasicString()}"); - - result = await buff_action.MoveServer(instanceMetaData.placeType()); - if (result.isFail()) - { - err_msg = $"Failed to MoveServer() !!! : {result.toBasicString()}"; - Log.getLogger().error(err_msg); - - return (result, null, business_logs); - } - - (result, var reserved_to_switch_server) = await ServerConnectionSwitchHelper.startServerSwitch(player, server_logic.getRedisConnector(), server_name); - if (result.isFail() || null == reserved_to_switch_server) - { - err_msg = $"Failed to startServerSwitch() !!! : {result.toBasicString()}"; - Log.getLogger().error(err_msg); - - return (result, null, business_logs); - } - - var game_login_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_login_action, () => $"game_login_action is null !!! - {player.toBasicString()}"); - - var login_cache = game_login_action.getLoginCacheRequest()?.getLoginCache(); - NullReferenceCheckHelper.throwIfNull(login_cache, () => $"login_cache is null !! player: {player.toBasicString()}"); - login_cache.ReservedToSwitchServer = reserved_to_switch_server; - - server_connect_info.ServerAddr = instance_room_info.InstanceAddress; - server_connect_info.ServerPort = instance_room_info.InstancePort; - server_connect_info.Otp = reserved_to_switch_server.OneTimeKey; - server_connect_info.RoomId = instance_room_info.roomId; - - //배틀 인스턴스 같은 경우는 실제 인스턴스 진입시에 Pos를 정해야 하기 때문에 여기서는 빈값으로 처리 - //var start_pos = MapManager.Instance.GetBattleInstnaceStartPos(indun_meta_data.RoomFile, instance_room_id); - var arrival_position_log_info = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Arrival, server_name, instance_room_info.roomId, MapFileType.Instance, instance_room_info.InstanceId, new()); - var arrival_position_business_log = new PositionBusinessLog(arrival_position_log_info); - business_logs.Add(arrival_position_business_log); - - Log.getLogger().debug($"tryJoinBattleInstance server connect info : {JsonConvert.SerializeObject(server_connect_info)}"); - - return (result, server_connect_info, business_logs); - } - - public async Task<(Result, ClientToGameRes.Types.GS2C_ACK_LEAVE_BATTLE_INSTANCE)> tryLeaveBattleInstanceRoom() - { - var result = new Result(); - var err_msg = string.Empty; - - var res = new ClientToGameRes.Types.GS2C_ACK_LEAVE_BATTLE_INSTANCE(); - - var player = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); - - var user_attribute = player.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user_attribute is null !!! - {player.toBasicString()}"); - - var farming_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user_attribute is null !!! - {player.toBasicString()}"); - - var location_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); - - var current_indun_location = location_action.getCurrentLocation() as IndunLocation; - NullReferenceCheckHelper.throwIfNull(current_indun_location, () => $"current_indun_location is null !!! - {player.toBasicString()}"); - - var instance_room_Id = current_indun_location.InstanceRoomId; - if (instance_room_Id == string.Empty) - return (result, res); - - if (!await BattleInstanceManager.It.LeaveBattleRoom(player, instance_room_Id)) - { - err_msg = $"Fail to LeaveBattleRoom() !!! : instanceRoomId:{instance_room_Id}"; - result.setFail(ServerErrorCode.NotExistInstanceRoom, err_msg); - Log.getLogger().error(result.toBasicString()); - - return (result, res); - } - - Log.getLogger().debug($"try leave battle instancr room done instance_room_Id : {instance_room_Id}"); - - return (result, res); - } - -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/FfaGameObjectPodStorageInteractAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/FfaGameObjectPodStorageInteractAction.cs deleted file mode 100644 index ce5655b..0000000 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/FfaGameObjectPodStorageInteractAction.cs +++ /dev/null @@ -1,57 +0,0 @@ -using GameServer.Contents.GameMode.Action; -using GameServer.Contents.GameMode.Manage.PlayManage; -using MetaAssets; -using ServerBase; -using ServerCommon; -using ServerCore; - -namespace GameServer; - -public class FfaGameObjectPodStorageInteractAction : GameGameObjectAction -{ - public FfaGameObjectPodStorageInteractAction(EntityBase owner) - : base(owner, EGameObjectType.Pod_Combat) - { - } - - public override async Task onInit() - { - await Task.CompletedTask; - var result = new Result(); - return result; - } - - public override void onClear() - { - return; - } - - public override async Task interact(string anchorGuid, DateTime interactionTime) - { - await Task.CompletedTask; - var result = new Result(); - - var game_mode_base = getOwner() as GameModeBase; - NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); - var attribute = game_mode_base.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); - - bool has_pod_combat = hasPodCombat(anchorGuid, attribute); - - - return result; - } - - public override string toBasicString() - { - return $"class name : {nameof(FfaGameObjectPodStorageInteractAction)}...." + base.toBasicString(); - } - - - private bool hasPodCombat(string anchorGuid, BattleInstanceSnapshotAttribute attribute) - { - var pod_combat = attribute.m_combat_pod_mode.m_pod_combat; - if (pod_combat.m_source_storage_anchor_guid.Equals(anchorGuid) && pod_combat.m_state.Equals(PodCombatStateType.Active)) return true; - return false; - } -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaBuffInteractAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaBuffInteractAction.cs new file mode 100644 index 0000000..f770834 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaBuffInteractAction.cs @@ -0,0 +1,101 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer; + +public class TpsFfaBuffInteractAction : GameModeObjectInteractionHandlerActionBase +{ + public TpsFfaBuffInteractAction(EntityBase owner) : base(owner, EGameModeObjectType.Buff, false, true) + { + } + + public override Task onInit() + { + return Task.FromResult(new Result()); + } + + public override void onClear() + { + return; + } + + public override async Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var attribute = game_mode_base.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); + + var buff_data_handler = handler as TpsFfaBuffInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(buff_data_handler, () => $"weapon_data_handler is null !!!"); + + var anchor_guid = buff_data_handler.m_interaction_anchor_guid; + (var result, var game_object_meta) = GameModeHelper.getGameModeObjectMeta(anchor_guid, game_mode_base); + if (result.isFail()) return result; + buff_data_handler.m_game_object_meta = game_object_meta; + + if (false == attribute.m_combat_pod_mode.m_buffs.TryGetValue(anchor_guid, out var buff)) + { + buff = new BattleObjectBuff(anchor_guid); + attribute.m_combat_pod_mode.m_buffs.TryAdd(anchor_guid, buff); + } + + //활성화 체크 + buff_data_handler.m_interaction_object_info = buff; + if (buff_data_handler.m_interaction_object_info.m_is_active == false) + { + var err_msg = $"battle_object weapon is false " + + $"anchorGuid : {buff_data_handler.m_interaction_anchor_guid}, " + + $" nextActivateTime : {buff.m_active_time}"; + result.setFail(ServerErrorCode.BattleInstanceObjectInteractionNotActive, err_msg); + return result; + } + + await Task.CompletedTask; + return result; + + } + + public override Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var buff_data_handler = handler as TpsFfaBuffInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(buff_data_handler, () => $"buff_data_handler is null !!!"); + var now = DateTimeHelper.Current; + + var next_active_time = now.AddSeconds(buff_data_handler.m_game_object_meta.RespawnTime); + + + var log_info = buff_data_handler.m_interaction_log_info as TpsFfaBuffInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaBuffInteractionLogInfo is null !!!"); + + buff_data_handler.m_next_active_time = log_info.m_object_next_active_time = buff_data_handler.m_interaction_object_info.m_active_time = next_active_time; + buff_data_handler.m_interaction_object_info.m_is_active = log_info.m_is_active = false; + buff_data_handler.m_need_noti_objects.Add(buff_data_handler.m_interaction_object_info); + return Task.FromResult(new Result()); + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + return; + } + + + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + var buff_data_handler = handler as TpsFfaBuffInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(buff_data_handler, () => $"buff_data_handler is null !!!"); + + var log_info = buff_data_handler.m_interaction_log_info as TpsFfaBuffInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaBuffInteractionLogInfo is null !!!"); + + return new TpsFfaBuffInteractionBuisinessLog(log_info); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaBuffInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaBuffInteractionDataHandler.cs new file mode 100644 index 0000000..c981f2e --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaBuffInteractionDataHandler.cs @@ -0,0 +1,12 @@ +using ServerCommon.BusinessLogDomain; + +namespace GameServer; + +public class TpsFfaBuffInteractionDataHandler : GameObjectInteractionDataCommonHandlerBase +{ + public TpsFfaBuffInteractionDataHandler(string userGuid, string userNickname, string interactionAnchorGuid, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, interactionTime) + { + m_interaction_log_info = new TpsFfaBuffInteractionLogInfo(roomId, userGuid, userNickname, interactionAnchorGuid); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaCombatPodInteractAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaCombatPodInteractAction.cs new file mode 100644 index 0000000..467c577 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaCombatPodInteractAction.cs @@ -0,0 +1,102 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer; + +public class TpsFfaCombatPodInteractAction : GameModeObjectInteractionHandlerActionBase +{ + public TpsFfaCombatPodInteractAction(EntityBase owner) : base(owner, EGameModeObjectType.Pod_Combat, false, true) + { + } + + + public override Task onInit() + { + return Task.FromResult(new Result()); + } + + public override void onClear() + { + } + + public override Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + var result = new Result(); + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + string err_msg = string.Empty; + var attribute = game_mode_base.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"battle_instance_attribute is null !!!"); + + var combat_pod_data_handler = handler as TpsFfaCombatPodInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(combat_pod_data_handler, () => $"combat_pod_data_handler is null !!!"); + + var combat_pod = combat_pod_data_handler.m_combat_pod = attribute.m_combat_pod_mode.m_pod_combat; + + if (false == combat_pod.m_current_occupier_guid.Equals(string.Empty) && false == combat_pod.m_state.Equals(PodCombatStateType.Dropped)) + { + //다른 유저가 소유중일때는 상호 작용 할수 없다. + err_msg = $"Pod Combat Already accupied Player : {player.toBasicString()}, possessioned user : {combat_pod.m_current_occupier_guid}"; + result.setFail(ServerErrorCode.BattleInstancePodCombatAlreadyOccupy, err_msg); + Log.getLogger().error(err_msg); + return Task.FromResult(result); + } + return Task.FromResult(result); + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var combat_pod_data_handler = handler as TpsFfaCombatPodInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(combat_pod_data_handler, () => $"combat_pod_data_handler is null !!!"); + if (combat_pod_data_handler.m_is_need_combat_pod_noti) + { + var game_mode_base = getOwner() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(game_mode_base); + } + } + + public override Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var combat_pod_data_handler = handler as TpsFfaCombatPodInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(combat_pod_data_handler, () => $"combat_pod_data_handler is null !!!"); + + combat_pod_data_handler.m_is_need_combat_pod_noti = true; + var combat_pod = combat_pod_data_handler.m_combat_pod; + + var now = DateTimeHelper.Current; + combat_pod.m_state = PodCombatStateType.Possesion; + combat_pod.m_currenct_Pos = player.getCurrentPositionInfo().Pos; + combat_pod.m_state_change_time = now; + combat_pod.m_current_occupier_guid = player.getUserGuid(); + combat_pod.m_occupied_time = now; + + var log_info = combat_pod_data_handler.m_interaction_log_info as TpsFfaCombatPodInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"log_info is null !!!"); + + log_info.m_pod_type = ECombatPodType.Pod; + log_info.m_pod_combat_pos = combat_pod.m_currenct_Pos; + log_info.m_pod_combat_state = combat_pod.m_state; + + return Task.FromResult(new Result()); + } + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + var combat_pod_data_handler = handler as TpsFfaCombatPodInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(combat_pod_data_handler, () => $"combat_pod_data_handler is null !!!"); + + var log_info = combat_pod_data_handler.m_interaction_log_info as TpsFfaCombatPodInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaCombatPodInteractionLogInfo is null !!!"); + + return new TpsFfaCombatPodInteractionBuisinessLog(log_info); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaCombatPodInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaCombatPodInteractionDataHandler.cs new file mode 100644 index 0000000..7dfe72f --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaCombatPodInteractionDataHandler.cs @@ -0,0 +1,15 @@ +using ServerCommon; +using ServerCommon.BusinessLogDomain; + +namespace GameServer; + +public class TpsFfaCombatPodInteractionDataHandler : GameObjectInteractionDataCommonHandlerBase +{ + public BattleObjectPodCombat m_combat_pod = new(); + public bool m_is_need_combat_pod_noti = false; + public TpsFfaCombatPodInteractionDataHandler(string userGuid, string userNickname, string interactionAnchorGuid, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, interactionTime) + { + m_interaction_log_info = new TpsFfaCombatPodInteractionLogInfo(roomId, userGuid, userNickname, interactionAnchorGuid); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaLoadCompleteAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaLoadCompleteAction.cs new file mode 100644 index 0000000..226316b --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaLoadCompleteAction.cs @@ -0,0 +1,38 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace GameServer; + +public class TpsFfaLoadCompleteAction : EntityActionBase, IGameModeLoadCompleteHandler +{ + public TpsFfaLoadCompleteAction(EntityBase owner) : base(owner) + { + + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public async Task loadComplete(Player player) + { + await Task.CompletedTask; + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_moce_base is null !!!"); + game_mode_base.updateUserState(player.getUserGuid(), GameModePlayerState.LoadComplete); + + Log.getLogger().debug($"user_guid : {player.getUserGuid()} is load complete., total Users state: {JsonConvert.SerializeObject(game_mode_base.getUserState())}"); + return new Result(); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaObjectUpdateAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaObjectUpdateAction.cs new file mode 100644 index 0000000..1a66c71 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaObjectUpdateAction.cs @@ -0,0 +1,211 @@ +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer; + +public class TpsFfaObjectUpdateAction : EntityActionBase +{ + public TpsFfaObjectUpdateAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + + var result = new Result(); + + + return result; + } + + public override void onClear() + { + + } + + public async Task updateFfaObject() + { + var tps_ffa = getOwner() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + var attribute = tps_ffa.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"BattleInstanceSnapshotAttribute is null !!!"); + var result = await battleObjectStateUpdate(tps_ffa, attribute); + if (result.isFail()) + { + Log.getLogger().error($"updateFfaObject result error !!! result : {result.toBasicString()}"); + return result; + } + + return result; + } + + + private async Task battleObjectStateUpdate(GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + { + var result = new Result(); + await Task.CompletedTask; + + var now = DateTimeHelper.Current; + List infos = new(); + List log_infos = new(); + using (var releaser = await battleInstanceRoom.getAsyncLock()) + { + (var weapon_info, var weapon_log_info) = battleObjectWeaponStateUpdate(attribute, now); + infos.AddRange(weapon_info); + log_infos.AddRange(weapon_log_info); + + (var buff_info, var buff_log_info) = battleObjectBuffStateUpdate(attribute, now); + infos.AddRange(buff_info); + log_infos.AddRange(buff_log_info); + + + (var pickup_pod_info, var pickup_pod_log_info) = battleObjectPickupPodUpdate(attribute, now); + infos.AddRange(pickup_pod_info); + log_infos.AddRange(pickup_pod_log_info); + } + + if (infos.Count > 0) + { + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(battleInstanceRoom, infos); + + var invokers = new List(); + var log_action = new LogAction(LogActionType.BattleObjectStateUpdate.ToString()); + var business_log = new BattleObjectStateUpdateBusinessLog(battleInstanceRoom.getInstanceRoom().getMap().m_room_id, log_infos); + invokers.Add(business_log); + BusinessLogger.collectLogs(log_action, battleInstanceRoom, invokers); + } + return result; + } + + private (List, List) battleObjectPickupPodUpdate(BattleInstanceSnapshotAttribute attribute, DateTime now) + { + // if (attribute.m_combat_pod_mode.m_round_state_type.Equals(BattleRoundStateType.RoundEndAll) || + // attribute.m_combat_pod_mode.m_round_state_type.Equals(BattleRoundStateType.Destroyed)) return new(); + + List infos = new(); + List log_infos = new(); + var battle_instance_room = getOwner() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(battle_instance_room, () => $"battle_instance_room is null !!!"); + + foreach (var generated_info in attribute.m_combat_pod_mode.m_pickup_pod_generated_info) + { + var group_id = generated_info.Key.Item1; + var idx = generated_info.Key.Item2; + + if (false == attribute.m_combat_pod_mode.m_pickup_pod_generated_info.TryGetValue(generated_info.Key, out var generatedInfo)) + { + Log.getLogger().error($"m_pickup_pod_generated_info i : {idx} not exist!!!"); + continue; + } + + var anchor_guid = generatedInfo.m_anchor_guid; + var next_generated_time = generatedInfo.m_next_generate_time; + + //아직 시간이 안됐거나, 할당된게 있으면 continue; + if (now < next_generated_time || anchor_guid.Equals(string.Empty) == false) continue; + + //비어 있으면 할당 해준다. 시간체크는 보상 받았을때 해준다. + (var result, var allocated_anchor_guid) = BattleRoomHelper.getRandomPickupPod(group_id, idx, battle_instance_room, attribute); + if (result.isFail()) + { + Log.getLogger(result.toBasicString()); + continue; + } + + if(false == battle_instance_room.getInstanceRoom().getMap().getAnchors().TryGetValue(allocated_anchor_guid, out var anchorInfo)) + { + Log.getLogger().error($"not exist anchor anchor_guid : {allocated_anchor_guid}"); + continue; + } + + //var table_id = anchorInfo.AnchorProp.TableId; + // if (false == MetaData.Instance._BattleObjectSpawnGroupMetaTable.TryGetValue(table_id, out var battle_object_spawn_group)) + // { + // Log.getLogger().error($"not exist _BattleObjectSpawnGroupMetaTable anchor_guid : {allocated_anchor_guid}, tableid : {table_id}"); + // continue; + // } + + + PickupPodGeneratedInfo new_generated_info = new(generated_info.Key.Item1, generated_info.Key.Item2, allocated_anchor_guid, generatedInfo.m_anchor_guid, generatedInfo.m_next_generate_time); + attribute.m_combat_pod_mode.m_pickup_pod_generated_info.AddOrUpdate(generated_info.Key, new_generated_info, (i, tuple) => new_generated_info); + if (false == attribute.m_combat_pod_mode.m_pickup_pods.TryGetValue(allocated_anchor_guid, out var pickupPod)) + { + var err_msg = $"pickup pod date not exist !! allocated_anchor_guid : {allocated_anchor_guid}, roomId : {battle_instance_room.getRoomId()}"; + Log.getLogger().error(err_msg); + continue; + } + + pickupPod.m_is_active = true; + + BattleObjectInfo info = new(); + info.AnchorGuid = allocated_anchor_guid; + info.IsActive = pickupPod.m_is_active ? BoolType.True : BoolType.False; + infos.Add(info); + + BattleObjectLogInfo log_info = new(); + log_info.m_anchor_guid = pickupPod.m_anchor_guid; + log_info.m_battle_object_type = pickupPod.m_type; + log_info.m_active_time = pickupPod.m_active_time; + log_infos.Add(log_info); + } + + + return (infos, log_infos); + } + + private (List, List) battleObjectBuffStateUpdate(BattleInstanceSnapshotAttribute attribute, DateTime now) + { + List infos = new(); + List log_infos = new(); + foreach (BattleObjectBuff buff in attribute.m_combat_pod_mode.m_buffs.Values) + { + if (buff.m_active_time <= now && buff.m_is_active == false) + { + buff.m_is_active = true; + + BattleObjectInfo info = new(); + info.AnchorGuid = buff.m_anchor_guid; + info.IsActive = BoolType.True; + infos.Add(info); + + BattleObjectLogInfo log_info = new(); + log_info.m_anchor_guid = buff.m_anchor_guid; + log_info.m_battle_object_type = buff.m_type; + log_info.m_active_time = buff.m_active_time; + log_infos.Add(log_info); + } + } + + return (infos, log_infos); + } + + private (List, List) battleObjectWeaponStateUpdate(BattleInstanceSnapshotAttribute attribute, DateTime now) + { + List infos = new(); + List log_infos = new(); + foreach (BattleObjectWeapon weapon in attribute.m_combat_pod_mode.m_weapons.Values) + { + if (weapon.m_active_time <= now && weapon.m_is_active == false) + { + weapon.m_is_active = true; + + BattleObjectInfo info = new(); + info.AnchorGuid = weapon.m_anchor_guid; + info.IsActive = BoolType.True; + infos.Add(info); + + BattleObjectLogInfo log_info = new(); + log_info.m_anchor_guid = weapon.m_anchor_guid; + log_info.m_battle_object_type = weapon.m_type; + log_info.m_active_time = weapon.m_active_time; + log_infos.Add(log_info); + } + } + + return (infos, log_infos); + } + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPickupPodInteractAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPickupPodInteractAction.cs new file mode 100644 index 0000000..dabef79 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPickupPodInteractAction.cs @@ -0,0 +1,100 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer; + +public class TpsFfaPickupPodInteractAction : GameModeObjectInteractionHandlerActionBase +{ + public TpsFfaPickupPodInteractAction(EntityBase owner) + : base(owner, EGameModeObjectType.Pod_Combat, true, true) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public override Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + return Task.FromResult(new Result()); + } + + public override async Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var result = new Result(); + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + var attribute = game_mode_base.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); + + + var pickup_pod_data_handler = handler as TpsFfaPickupPodInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(pickup_pod_data_handler, () => $"pickup_pod_data_handler is null !!!"); + var log_info = pickup_pod_data_handler.m_interaction_log_info as TpsFfaPickupPodInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaBuffInteractionLogInfo is null !!!"); + + var now = DateTimeHelper.Current; + var next_active_time = now.AddSeconds(pickup_pod_data_handler.m_game_object_meta.RespawnTime); + + pickup_pod_data_handler.m_next_active_time = log_info.m_object_next_active_time = pickup_pod_data_handler.m_interaction_object_info.m_active_time = next_active_time; + pickup_pod_data_handler.m_interaction_object_info.m_is_active = log_info.m_is_active = false; + pickup_pod_data_handler.m_need_noti_objects.Add(pickup_pod_data_handler.m_interaction_object_info); + + //보상처리 + var reward_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(reward_action, () => $"handler.m_pod_combat is null !!!"); + result = await reward_action.pickupPodReward(player, attribute, pickup_pod_data_handler); + + return result; + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var data_handler_base = handler as GameObjectInteractionDataCommonHandlerBase; + NullReferenceCheckHelper.throwIfNull(data_handler_base, () => $"data_handler_base is null !!! - {player.toBasicString()}"); + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"data_handler_base is null !!! - {player.toBasicString()}"); + + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_REWARD(game_mode_base, player.getUserGuid(), data_handler_base.m_common_result); + } + + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + var pickup_pod_data_handler = handler as TpsFfaPickupPodInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(pickup_pod_data_handler, () => $"pickup_pod_data_handler is null !!!"); + + var log_info = pickup_pod_data_handler.m_interaction_log_info as TpsFfaPickupPodInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaPickupPodInteractionLogInfo is null !!!"); + + return new TpsFfaPickupPodInteractionBuisinessLog(log_info); + } + + public override string toBasicString() + { + return $"class name : {nameof(TpsFfaPickupPodInteractAction)}...." + base.toBasicString(); + } + + private bool hasPodCombat(string anchorGuid, BattleInstanceSnapshotAttribute attribute) + { + var pod_combat = attribute.m_combat_pod_mode.m_pod_combat; + if (pod_combat.m_source_storage_anchor_guid.Equals(anchorGuid) && pod_combat.m_state.Equals(PodCombatStateType.Active)) return true; + return false; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPickupPodInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPickupPodInteractionDataHandler.cs new file mode 100644 index 0000000..f20f96f --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPickupPodInteractionDataHandler.cs @@ -0,0 +1,14 @@ +using ServerCommon.BusinessLogDomain; + +namespace GameServer; + +public class TpsFfaPickupPodInteractionDataHandler : GameObjectInteractionDataCommonHandlerBase +{ + //public BattleObjectPodCombat m_combat_pod = new(); + //public bool m_is_need_combat_pod_noti = false; + public TpsFfaPickupPodInteractionDataHandler(string userGuid, string userNickname, string interactionAnchorGuid, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, interactionTime) + { + m_interaction_log_info = new TpsFfaPickupPodInteractionLogInfo(roomId, userGuid, userNickname, interactionAnchorGuid); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPodStorageInteractAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPodStorageInteractAction.cs new file mode 100644 index 0000000..43d171b --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPodStorageInteractAction.cs @@ -0,0 +1,177 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer; + +public class TpsFfaPodStorageInteractAction : GameModeObjectInteractionHandlerActionBase +{ + public TpsFfaPodStorageInteractAction(EntityBase owner) : base(owner, EGameModeObjectType.Pod_Combat, false, true) + { + } + + public override Task onInit() + { + return Task.FromResult(new Result()); + } + + public override void onClear() + { + return; + } + + public override async Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var attribute = game_mode_base.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); + + var pod_storage_data_handler = handler as TpsFfaPodStorageInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(pod_storage_data_handler, () => $"pod_storage_data_handler is null !!!"); + + + var anchor_guid = pod_storage_data_handler.m_interaction_anchor_guid; + //meta 체크.. 이것도 위로 올려야 되는에 어떻게 해야될까... + (var result, var game_object_meta) = GameModeHelper.getGameModeObjectMeta(anchor_guid, game_mode_base); + if (result.isFail()) return result; + pod_storage_data_handler.m_game_object_meta = game_object_meta; + + if (false == attribute.m_combat_pod_mode.m_pod_storages.TryGetValue(anchor_guid, out var podStorage)) + { + podStorage = new BattleObjectPodStorage(anchor_guid); + attribute.m_combat_pod_mode.m_pod_storages.TryAdd(anchor_guid, podStorage); + } + + //활성화 체크 + pod_storage_data_handler.m_interaction_object_info = podStorage; + if (pod_storage_data_handler.m_interaction_object_info.m_is_active == false) + { + var err_msg = $"battle_object pod storage is false " + + $"anchorGuid : {pod_storage_data_handler.m_interaction_anchor_guid}, " + + $" nextActivateTime : {podStorage.m_active_time}"; + result.setFail(ServerErrorCode.BattleInstanceObjectInteractionNotActive, err_msg); + return result; + } + + + await Task.CompletedTask; + return result; + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var pod_storage_data_handler = handler as TpsFfaPodStorageInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(pod_storage_data_handler, () => $"pod_storage_data_handler is null !!!"); + + var game_mode_base = getOwner() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + if (pod_storage_data_handler.m_is_need_combat_pod_noti) + { + + BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(game_mode_base); + } + + if (pod_storage_data_handler.m_need_noti_objects.Count > 0) + { + var object_infos = new List(); + foreach (var objs in pod_storage_data_handler.m_need_noti_objects) + { + BattleObjectInfo info = new(); + info.AnchorGuid = objs.m_anchor_guid; + info.IsActive = objs.m_is_active ? BoolType.True : BoolType.False; + object_infos.Add(info); + } + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(game_mode_base, object_infos); + } + } + + public override async Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var result = new Result(); + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + var attribute = game_mode_base.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); + + var pod_storage_data_handler = handler as TpsFfaPodStorageInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(pod_storage_data_handler, () => $"pod_storage_data_handler is null !!!"); + + bool has_pod_combat = hasPodCombat(pod_storage_data_handler.m_interaction_anchor_guid, attribute, pod_storage_data_handler); + + + var log_info = pod_storage_data_handler.m_interaction_log_info as TpsFfaPodStorageInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"log_info is null !!!"); + + var log_info_base = (GameObjectInteractionLogInfoBase)pod_storage_data_handler.m_interaction_log_info; + + log_info.m_has_pod_combat = has_pod_combat; + log_info.m_pod_type = ECombatPodType.Storage; + log_info_base.m_is_active = false; + + var now = DateTimeHelper.Current; + if (has_pod_combat) + { + var pod_combat = pod_storage_data_handler.m_combat_pod; + pod_combat.m_state = PodCombatStateType.Possesion; + pod_combat.m_currenct_Pos = player.getCurrentPositionInfo().Pos; + pod_combat.m_state_change_time = now; + pod_combat.m_active_time = now; + pod_combat.m_current_occupier_guid = player.getUserGuid(); + pod_combat.m_occupied_time = now; + pod_storage_data_handler.m_need_noti_objects.Add(pod_combat); + + foreach (var stand in attribute.m_combat_pod_mode.m_pod_storages.Values) + { + stand.m_is_active = false; + pod_storage_data_handler.m_need_noti_objects.Add(stand); + } + + log_info.m_pod_combat_pos = pod_combat.m_currenct_Pos; + log_info.m_pod_combat_state = pod_combat.m_state; + + pod_storage_data_handler.m_is_need_combat_pod_noti = true; + } + else + { + //보상 줘야되면 보상 처리 로직 넣어야 한다. + //need transaction도 true로 활성화 해라 + //log_info.m_rewards 애 데이터 기입 + } + + + + await Task.CompletedTask; + return result; + } + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + var pod_storage_data_handler = handler as TpsFfaPodStorageInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(pod_storage_data_handler, () => $"pod_storage_data_handler is null !!!"); + + var log_info = pod_storage_data_handler.m_interaction_log_info as TpsFfaPodStorageInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaBuffInteractionLogInfo is null !!!"); + + return new TpsFfaPodStorageInteractionBuisinessLog(log_info); + } + + + private bool hasPodCombat(string anchorGuid, BattleInstanceSnapshotAttribute attribute, TpsFfaPodStorageInteractionDataHandler handler) + { + var pod_combat = attribute.m_combat_pod_mode.m_pod_combat; + if (pod_combat.m_source_storage_anchor_guid.Equals(anchorGuid) && pod_combat.m_state.Equals(PodCombatStateType.Active)) + { + handler.m_combat_pod = pod_combat; + return true; + } + return false; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPodStorageInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPodStorageInteractionDataHandler.cs new file mode 100644 index 0000000..bd5f6ab --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaPodStorageInteractionDataHandler.cs @@ -0,0 +1,15 @@ +using ServerCommon; +using ServerCommon.BusinessLogDomain; + +namespace GameServer; + +public class TpsFfaPodStorageInteractionDataHandler : GameObjectInteractionDataCommonHandlerBase +{ + public BattleObjectPodCombat m_combat_pod = new(); + public bool m_is_need_combat_pod_noti = false; + public TpsFfaPodStorageInteractionDataHandler(string userGuid, string userNickname, string interactionAnchorGuid, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, interactionTime) + { + m_interaction_log_info = new TpsFfaPodStorageInteractionLogInfo(roomId, userGuid, userNickname, interactionAnchorGuid); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaSetUserReadyPosAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaSetUserReadyPosAction.cs new file mode 100644 index 0000000..0be09a3 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaSetUserReadyPosAction.cs @@ -0,0 +1,129 @@ +using System.Numerics; +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; +using ServerCore; + +namespace GameServer; + +using ANCHOR_GUID = System.String; +using USER_GUID = System.String; +using USER_POS = Pos; + +public class TpsFfaSetUserReadyPosAction : EntityActionBase +{ + public TpsFfaSetUserReadyPosAction(EntityBase owner) : base(owner) + { + } + + public override Task onInit() + { + return Task.FromResult(new Result()); + } + + public override void onClear() + { + return; + } + + public async Task setReadyPos() + { + var tps_ffa = getOwner() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + + + var anchor_list = tps_ffa.m_respawn_pos_anchors_meta.ToList(); + var shuffled_anchor_list = getShuffledAnchorList(anchor_list); + + + var result = await readyPosAllocateAndNotify(shuffled_anchor_list); + if (result.isFail()) return result; + + + return result; + + + + } + + private string[] getShuffledAnchorList(List originList) + { + string[] shuffles = originList.ToArray(); + if (originList.Any()) + { + Random random = new Random(); + for (int i = originList.Count - 1; i > 0; i--) + { + int j = random.Next(i + 1); + var temp = shuffles[i]; + shuffles[i] = shuffles[j]; + shuffles[j] = temp; + } + } + return shuffles; + } + + private async Task readyPosAllocateAndNotify(string[] shuffledAnchorList) + { + var result = new Result(); + var tps_ffa = getOwner() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + + var guids = tps_ffa.getInstanceRoom().getInstanceMemberGuids().ToArray(); + + var attribute = tps_ffa.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); + + var now = DateTimeHelper.Current; + + var pos_set_action = tps_ffa.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(pos_set_action, () => $"pos_set_action is null !!!"); + + Dictionary ready_pos_dict = new Dictionary(); + using (var releaser = await tps_ffa.getAsyncLock()) + { + for (int i = 0; i < guids.Length; i++) + { + result = await changeRespawnObjectActiveTime(shuffledAnchorList[i], tps_ffa, attribute, now); + if (result.isFail()) continue; + + if (false == tps_ffa.getInstanceRoom().tryGetInstanceMember(guids[i], out var player)) + { + var err_msg = $"userGuid : {guids[i]} is not exist in instance room !!!"; + Log.getLogger().error(err_msg); + continue; + } + + (result , var pos) = pos_set_action.changeUserAnchorPos(shuffledAnchorList[i], player); + if (result.isFail()) continue; + + + ready_pos_dict.Add(shuffledAnchorList[i], (guids[i], pos)); + } + } + + GameNotifyHelper.broadcast_GS2C_NTF_READY_POS(tps_ffa.getInstanceRoom(), ready_pos_dict); + + + return result; + } + + + + private Task changeRespawnObjectActiveTime(string anchorGuid, GameModeTPSFreeForAll tpsFfa, BattleInstanceSnapshotAttribute attribute, DateTime now) + { + + var result = new Result(); + var next_respawn_time = now.AddSeconds(tpsFfa.m_ffa_meta.m_player_respawn_time); + attribute.m_combat_pod_mode.m_respawns.AddOrUpdate(anchorGuid, next_respawn_time, (key, old) => next_respawn_time); + return Task.FromResult(result); + } + + private void setUserLocationPos(string userGuid) + { + + + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaWeaponInteractAction.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaWeaponInteractAction.cs new file mode 100644 index 0000000..ed8cfe8 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaWeaponInteractAction.cs @@ -0,0 +1,100 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer; + +public class TpsFfaWeaponInteractAction : GameModeObjectInteractionHandlerActionBase +{ + public TpsFfaWeaponInteractAction(EntityBase owner) : base(owner, EGameModeObjectType.Weapon, false, true) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + return; + } + + public override async Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var attribute = game_mode_base.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); + + var weapon_data_handler = handler as TpsFfaWeaponInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(weapon_data_handler, () => $"weapon_data_handler is null !!!"); + + var anchor_guid = weapon_data_handler.m_interaction_anchor_guid; + //meta 체크.. 이것도 위로 올려야 되는에 어떻게 해야될까... + (var result, var game_object_meta) = GameModeHelper.getGameModeObjectMeta(anchor_guid, game_mode_base); + if (result.isFail()) return result; + weapon_data_handler.m_game_object_meta = game_object_meta; + + if (false == attribute.m_combat_pod_mode.m_weapons.TryGetValue(anchor_guid, out var weapon)) + { + weapon = new BattleObjectWeapon(anchor_guid); + attribute.m_combat_pod_mode.m_weapons.TryAdd(anchor_guid, weapon); + } + + //활성화 체크 + weapon_data_handler.m_interaction_object_info = weapon; + if (weapon_data_handler.m_interaction_object_info.m_is_active == false) + { + var err_msg = $"battle_object weapon is false " + + $"anchorGuid : {weapon_data_handler.m_interaction_anchor_guid}, " + + $" nextActivateTime : {weapon.m_active_time}"; + result.setFail(ServerErrorCode.BattleInstanceObjectInteractionNotActive, err_msg); + return result; + } + + await Task.CompletedTask; + return result; + } + + public override Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var weapon_data_handler = handler as TpsFfaWeaponInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(weapon_data_handler, () => $"weapon_data_handler is null !!!"); + var now = DateTimeHelper.Current; + var next_active_time = now.AddSeconds(weapon_data_handler.m_game_object_meta.RespawnTime); + + + var log_info = weapon_data_handler.m_interaction_log_info as TpsFfaWeaponInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaWeaponInteractionLogInfo is null !!!"); + + weapon_data_handler.m_next_active_time = log_info.m_object_next_active_time = weapon_data_handler.m_interaction_object_info.m_active_time = next_active_time; + weapon_data_handler.m_interaction_object_info.m_is_active = log_info.m_is_active = false; + weapon_data_handler.m_need_noti_objects.Add(weapon_data_handler.m_interaction_object_info); + return Task.FromResult(new Result()); + } + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + var weapon_data_handler = handler as TpsFfaWeaponInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(weapon_data_handler, () => $"weapon_data_handler is null !!!"); + + var log_info = weapon_data_handler.m_interaction_log_info as TpsFfaWeaponInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaWeaponInteractionLogInfo is null !!!"); + + return new TpsFfaWeaponInteractionBuisinessLog(log_info); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaWeaponInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaWeaponInteractionDataHandler.cs new file mode 100644 index 0000000..d1057eb --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Action/TpsFfaWeaponInteractionDataHandler.cs @@ -0,0 +1,13 @@ + +using ServerCommon.BusinessLogDomain; + +namespace GameServer; + +public class TpsFfaWeaponInteractionDataHandler : GameObjectInteractionDataCommonHandlerBase +{ + public TpsFfaWeaponInteractionDataHandler(string userGuid, string userNickname, string interactionAnchorGuid, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, interactionTime) + { + m_interaction_log_info = new TpsFfaWeaponInteractionLogInfo(roomId, userGuid, userNickname, interactionAnchorGuid); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Cheat/BattleCheat.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Cheat/BattleCheat.cs index 202ef35..4ecea15 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Cheat/BattleCheat.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Cheat/BattleCheat.cs @@ -1,4 +1,5 @@ -using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; using GameServer.Contents.GameMode.Mode_Battle.Manage; using ServerBase; using ServerCommon; @@ -10,8 +11,10 @@ namespace GameServer; [ChatCommandAttribute("battleinstanceinit", typeof(CheatBattleInstanceInit), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] public class CheatBattleInstanceInit : ChatCommandBase { - public override async Task invoke(Player player, string token, string[] args) + public override Task invoke(Player player, string token, string[] args) { + return Task.CompletedTask; + /* Log.getLogger().info($"battleinstanceinit"); var server_logic = GameServerApp.getServerLogic(); @@ -43,7 +46,7 @@ public class CheatBattleInstanceInit : ChatCommandBase var attribute = game_mode_base.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); - if (false == attribute.m_combat_pod_mode.m_round_state_type.Equals(BattleRoundStateType.RoundEndAll) && false == attribute.m_combat_pod_mode.m_round_state_type.Equals(BattleRoundStateType.Destroyed)) + if (false == attribute.m_combat_pod_mode.m_game_state.Equals(GameModeState.RoundEndAll) && false == attribute.m_combat_pod_mode.m_game_state.Equals(GameModeState.Destroyed)) { Log.getLogger().error($"cheat:battleinstanceinit.... this cheat only RoundEndAll room_id : {room_id}"); return; @@ -51,7 +54,7 @@ public class CheatBattleInstanceInit : ChatCommandBase var now = DateTimeHelper.Current; - var ffa = gameMode as GameModeTPSFreeForAll; + var ffa = gameMode as GameModeTPSFreeForAll; if (ffa is null) { Log.getLogger().error($"cheat:battleinstanceinit.... ffa is null room_id : {room_id}"); @@ -60,7 +63,7 @@ public class CheatBattleInstanceInit : ChatCommandBase ffa.setEventStartTime(now); - attribute.m_combat_pod_mode.m_round_state_type = BattleRoundStateType.Rounding; + attribute.m_combat_pod_mode.m_game_state = GameModeState.Rounding; attribute.m_combat_pod_mode.m_current_state_start_time = now; attribute.m_combat_pod_mode.m_current_round = 1; attribute.m_combat_pod_mode.m_charged_step = 0; @@ -81,11 +84,11 @@ public class CheatBattleInstanceInit : ChatCommandBase var update_action = game_mode_base.getEntityAction(); NullReferenceCheckHelper.throwIfNull(update_action, () => $"attribute is null !!!"); - update_action.battleObjectStateInitAndNotify(ffa, attribute); + update_action.objectStateInitAndNotify(); BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(ffa); - } + }*/ } } @@ -96,10 +99,6 @@ public class BattleHostOccupy : ChatCommandBase { public override async Task invoke(Player player, string token, string[] args) { - //Log.getLogger().info($"battleinstanceinit"); - - var server_logic = GameServerApp.getServerLogic(); - var room_id = player.getCurrentInstanceRoomId(); if (room_id.Equals(string.Empty)) { @@ -107,37 +106,23 @@ public class BattleHostOccupy : ChatCommandBase return; } - - //kihoon todo : 임시 분기 처리 - //나중에 게임모드로 분리되면 그때 재작업 - if (room_id.Contains("battle")) + if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) { - //var battle_instance_room = BattleInstanceManager.It.getBattleInstanceRoom(room_id); - if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) - { - Log.getLogger().error($"cheat:gamemodeoccupyhost.... battle_instance_room is null room_id : {room_id}"); - return; - } - - var game_mode_base = gameMode as GameModeBase; - NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); - - using (var releaser = await game_mode_base.getAsyncLock()) - { - game_mode_base.m_host_migrator.modifyHost(player.getUserGuid()); - } - BattleRoomNotifyHelper.broadcast_GS2C_NTF_P2P_HOST_UPDATE(game_mode_base.getInstanceRoom(), player.getUserGuid()); + Log.getLogger().error($"cheat:gamemodeoccupyhost.... battle_instance_room is null room_id : {room_id}"); + return; } - else if (room_id.Contains("1017101")) + + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + using (var releaser = await game_mode_base.getAsyncLock()) { - var instance_room = InstanceRoomManager.Instance.getInstanceRoomByRoomId(room_id); - if (instance_room is null) - { - Log.getLogger().error($"cheat:gamemodeoccupyhost.... instance_room is null room_id : {room_id}"); - return; - } - BattleRoomNotifyHelper.broadcast_GS2C_NTF_P2P_HOST_UPDATE(instance_room, player.getUserGuid()); + game_mode_base.m_host_migrator.modifyHost(player.getUserGuid()); } + GameNotifyHelper.broadcast_GS2C_NTF_P2P_HOST_UPDATE(game_mode_base.getInstanceRoom(), player.getUserGuid()); + + + diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Doc/BattleEventDoc.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Doc/BattleEventDoc.cs index 64969f4..b38f4f7 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Doc/BattleEventDoc.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Doc/BattleEventDoc.cs @@ -18,39 +18,41 @@ public class BattleEventAttrib : AttribBase [JsonProperty("start_hour")] public Int32 m_start_hour = 0; //00 ~ 23; [JsonProperty("start_min")] public Int32 m_start_min = 0; //00 ~ 59; - [JsonProperty("end_date")] public DateTime m_end_date = DateTimeHelper.Current.Date.AddHours(12); //이시간이 지나면 더이상 이벤트 열지 않는다. + [JsonProperty("open_duration_minutes")] public Int32 m_open_duration_minutes = 240; - //[JsonProperty("sequence_id")] public Int32 m_sequence_id { get; set; } = 0; - [JsonProperty("instance_id")] public Int32 m_instance_id { get; set; } = 0; + [JsonProperty("end_date")] public DateTime m_end_date = DateTimeHelper.Current.Date.AddHours(12); //이시간이 지나면 더이상 이벤트 열지 않는다. + + [JsonProperty("game_mode_id")] public Int32 m_game_mode_id { get; set; } = 1; + [JsonProperty("instance_id")] public Int32 m_instance_id { get; set; } = 0;//미사용 예정 [JsonProperty("once_period_type")] public OncePeriodRangeType m_period { get; set; } = OncePeriodRangeType.NONE; [JsonProperty("day_of_week_type")] public HashSet m_day_of_week_type { get; set; } = new(); - //[JsonProperty("battle_play_mode")] public BattlePlayMode m_battle_playe_mode { get; set; } = BattlePlayMode.None; - [JsonProperty("ffa_config_data_id")] public Int32 m_ffa_config_data_id { get; set; } = 4; - [JsonProperty("ffa_reward_group_id")] public Int32 m_ffa_reward_group_id { get; set; } = 7; + [JsonProperty("ffa_config_data_id")] public Int32 m_ffa_config_data_id { get; set; } = 4;//미사용 예정 + [JsonProperty("ffa_reward_group_id")] public Int32 m_ffa_reward_group_id { get; set; } = 7;//미사용 예정 [JsonProperty("ffa_hot_time")] public Int32 m_ffa_hot_time { get; set; } = 1; - [JsonProperty("round_count")] public Int32 m_round_count { get; set; } = 1; + [JsonProperty("round_count")] public Int32 m_round_count { get; set; } = 1;//미사용 예정 - public BattleEventAttrib clone() - { - BattleEventAttrib new_attrib = new(); - new_attrib.m_event_id = this.m_event_id; - new_attrib.m_is_active = this.m_is_active; - new_attrib.m_start_day = this.m_start_day; - new_attrib.m_start_hour = this.m_start_hour; - new_attrib.m_start_min = this.m_start_min; - new_attrib.m_instance_id = this.m_instance_id; - new_attrib.m_period = this.m_period; - new_attrib.m_day_of_week_type = this.m_day_of_week_type; - new_attrib.m_ffa_config_data_id = this.m_ffa_config_data_id; - new_attrib.m_ffa_reward_group_id = this.m_ffa_reward_group_id; - new_attrib.m_ffa_hot_time = this.m_ffa_hot_time; - - new_attrib.m_end_date = this.m_end_date; - new_attrib.m_round_count = this.m_round_count; - - return new_attrib; - } + // public BattleEventAttrib clone() + // { + // BattleEventAttrib new_attrib = new(); + // new_attrib.m_event_id = this.m_event_id; + // new_attrib.m_game_mode_id = this.m_game_mode_id; + // new_attrib.m_is_active = this.m_is_active; + // new_attrib.m_start_day = this.m_start_day; + // new_attrib.m_start_hour = this.m_start_hour; + // new_attrib.m_start_min = this.m_start_min; + // new_attrib.m_instance_id = this.m_instance_id; + // new_attrib.m_period = this.m_period; + // new_attrib.m_day_of_week_type = this.m_day_of_week_type; + // new_attrib.m_ffa_config_data_id = this.m_ffa_config_data_id; + // new_attrib.m_ffa_reward_group_id = this.m_ffa_reward_group_id; + // new_attrib.m_ffa_hot_time = this.m_ffa_hot_time; + // + // new_attrib.m_end_date = this.m_end_date; + // new_attrib.m_round_count = this.m_round_count; + // + // return new_attrib; + // } } diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceRoom.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceRoom.cs deleted file mode 100644 index 6d88dd5..0000000 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceRoom.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Collections.Concurrent; -using Axion.Collections.Concurrent; -using GameServer.Contents.Battle.Log; -using GameServer.Contents.GameMode.Helper; -using GameServer.Contents.GameMode.Manage.PlayManage; -using GameServer.Contents.GameMode.Mode_Battle.Manage; -using MetaAssets; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Org.BouncyCastle.Tls.Crypto.Impl; -using ServerBase; -using ServerCommon; -using ServerCore; - -namespace GameServer; - - - - - -public class BattleInstanceRoom : EntityBase, IWithLogActor, IBattleMode -{ - - public BattleInstanceRoom() - : base(EntityType.BattleInstance) - { - - } - - - public override async Task onInit() - { - await Task.CompletedTask; - var result = new Result(); - return result; - } - - public ILogActor toLogActor() - { - var server_logic = GameServerApp.getServerLogic(); - var region_id = server_logic.getServerConfig().getRegionId(); - var server_name = server_logic.getServerName(); - - var log_info = new BattleInstanceActorLog(region_id, server_name, "", 0); - return log_info; - } - - - public async Task LeaveBattleRoom(Player player, string roomId, bool disconnected = false) - { - if (false == GameModeManager.It.tryGetGameMode(roomId, out var gameMode)) return false; - - var game_mode_base = GameModeHelper.toGameModeBase(gameMode); - Log.getLogger().info($"LeaveBattleRoom, player : {player.toBasicString()}, roomId : {roomId}, disconnected : {disconnected}"); - - //await game_mode_base.getInstanceRoom().LeaveGameInstanceRoom(player, disconnected); - await Task.CompletedTask; - if (game_mode_base.getInstanceRoom().isDestroy) - { - Log.getLogger().info($"m_instance_room.isDestroy, so destroyBattleRoom : {player.toBasicString()}, roomId : {roomId}, disconnected : {disconnected}"); - InstanceRoomManager.Instance.DestroyRoom(roomId); - Log.getLogger().info($"destroy InstanceRoomManager room Player : {player.toBasicString()}, roomId : {roomId}, disconnected : {disconnected}"); - } - - return true; - } - - public override string toBasicString() - { - return $"BattleInstanceRoom room_id"; - } - - - public Task init() - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceSnapshotAttribute.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceSnapshotAttribute.cs index 563f051..9bc9d5d 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceSnapshotAttribute.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceSnapshotAttribute.cs @@ -30,8 +30,8 @@ public class BattleInstanceSnapshotPlayModePodCombat [JsonProperty] public ConcurrentDictionary m_buffs {get; set;} = new (); - [JsonProperty] public BattleRoundStateType m_round_state_type { get; set; } = BattleRoundStateType.Rounding; - [JsonProperty] public int m_current_round { get; set; } = 1; + [JsonProperty] public GameModeState m_game_state { get; set; } = GameModeState.None; + [JsonProperty] public int m_current_round { get; set; } = 0; [JsonProperty] public DateTime m_current_state_start_time { get; set; } = DateTimeHelper.Current; [JsonProperty] public int m_charged_step { get; set; } = 0; @@ -69,7 +69,7 @@ public class BattleInstanceSnapshotPlayModePodCombat m_pickup_pod_generated_info.Clear(); m_weapons.Clear(); m_buffs.Clear(); - m_round_state_type = BattleRoundStateType.None; + m_game_state = GameModeState.None; m_current_round = 1; m_current_state_start_time = DateTimeHelper.Current; m_charged_step = 0; @@ -142,7 +142,7 @@ public class BattleInstanceSnapshotAttribute : EntityAttributeBase, ICopyEntityA to_copy_battle_instance_snapshot_attrib.m_combat_pod_attrib.m_pickup_pod_generated_info.AddRange(m_combat_pod_mode.m_pickup_pod_generated_info.Values); to_copy_battle_instance_snapshot_attrib.m_combat_pod_attrib.m_weapons = m_combat_pod_mode.m_weapons; to_copy_battle_instance_snapshot_attrib.m_combat_pod_attrib.m_buffs = m_combat_pod_mode.m_buffs; - to_copy_battle_instance_snapshot_attrib.m_combat_pod_attrib.m_round_state_type = m_combat_pod_mode.m_round_state_type; + to_copy_battle_instance_snapshot_attrib.m_combat_pod_attrib.m_game_state = m_combat_pod_mode.m_game_state; to_copy_battle_instance_snapshot_attrib.m_combat_pod_attrib.m_current_round = m_combat_pod_mode.m_current_round; to_copy_battle_instance_snapshot_attrib.m_combat_pod_attrib.m_current_state_start_time = m_combat_pod_mode.m_current_state_start_time; to_copy_battle_instance_snapshot_attrib.m_combat_pod_attrib.m_charged_step = m_combat_pod_mode.m_charged_step; @@ -241,7 +241,7 @@ public class BattleInstanceSnapshotAttribute : EntityAttributeBase, ICopyEntityA } //기타 진행 상황 - cloned.m_combat_pod_mode.m_round_state_type = m_combat_pod_mode.m_round_state_type; + cloned.m_combat_pod_mode.m_game_state = m_combat_pod_mode.m_game_state; cloned.m_combat_pod_mode.m_current_round = m_combat_pod_mode.m_current_round; cloned.m_combat_pod_mode.m_current_state_start_time = m_combat_pod_mode.m_current_state_start_time; cloned.m_combat_pod_mode.m_charged_step = m_combat_pod_mode.m_charged_step; @@ -298,7 +298,7 @@ public class BattleInstanceSnapshotAttribute : EntityAttributeBase, ICopyEntityA m_combat_pod_mode.m_weapons = other_attribute.m_combat_pod_mode.m_weapons; m_combat_pod_mode.m_buffs = other_attribute.m_combat_pod_mode.m_buffs; - m_combat_pod_mode.m_round_state_type = other_attribute.m_combat_pod_mode.m_round_state_type; + m_combat_pod_mode.m_game_state = other_attribute.m_combat_pod_mode.m_game_state; m_combat_pod_mode.m_current_round = other_attribute.m_combat_pod_mode.m_current_round; m_combat_pod_mode.m_current_state_start_time = other_attribute.m_combat_pod_mode.m_current_state_start_time; m_combat_pod_mode.m_charged_step = other_attribute.m_combat_pod_mode.m_charged_step; @@ -336,7 +336,7 @@ public class BattleInstanceSnapshotAttribute : EntityAttributeBase, ICopyEntityA battle_instance_snapshot_attrib.m_combat_pod_attrib.m_pickup_pod_generated_info.AddRange(m_combat_pod_mode.m_pickup_pod_generated_info.Values); battle_instance_snapshot_attrib.m_combat_pod_attrib.m_weapons = m_combat_pod_mode.m_weapons; battle_instance_snapshot_attrib.m_combat_pod_attrib.m_buffs = m_combat_pod_mode.m_buffs; - battle_instance_snapshot_attrib.m_combat_pod_attrib.m_round_state_type = m_combat_pod_mode.m_round_state_type; + battle_instance_snapshot_attrib.m_combat_pod_attrib.m_game_state = m_combat_pod_mode.m_game_state; battle_instance_snapshot_attrib.m_combat_pod_attrib.m_current_round = m_combat_pod_mode.m_current_round; battle_instance_snapshot_attrib.m_combat_pod_attrib.m_current_state_start_time = m_combat_pod_mode.m_current_state_start_time; battle_instance_snapshot_attrib.m_combat_pod_attrib.m_charged_step = m_combat_pod_mode.m_charged_step; @@ -382,7 +382,7 @@ public class BattleInstanceSnapshotAttribute : EntityAttributeBase, ICopyEntityA m_combat_pod_mode.m_weapons = doc_attrib.m_combat_pod_attrib.m_weapons; m_combat_pod_mode.m_buffs = doc_attrib.m_combat_pod_attrib.m_buffs; - m_combat_pod_mode.m_round_state_type = doc_attrib.m_combat_pod_attrib.m_round_state_type; + m_combat_pod_mode.m_game_state = doc_attrib.m_combat_pod_attrib.m_game_state; m_combat_pod_mode.m_current_round = doc_attrib.m_combat_pod_attrib.m_current_round; m_combat_pod_mode.m_current_state_start_time = doc_attrib.m_combat_pod_attrib.m_current_state_start_time; m_combat_pod_mode.m_charged_step = doc_attrib.m_combat_pod_attrib.m_charged_step; @@ -439,7 +439,7 @@ public class BattleInstanceSnapshotAttribute : EntityAttributeBase, ICopyEntityA copy_to_attribute.m_combat_pod_mode.m_pickup_pod_generated_info = copy_from_attribute.m_combat_pod_mode.m_pickup_pod_generated_info; copy_to_attribute.m_combat_pod_mode.m_weapons = copy_from_attribute.m_combat_pod_mode.m_weapons; copy_to_attribute.m_combat_pod_mode.m_buffs = copy_from_attribute.m_combat_pod_mode.m_buffs; - copy_to_attribute.m_combat_pod_mode.m_round_state_type = copy_from_attribute.m_combat_pod_mode.m_round_state_type; + copy_to_attribute.m_combat_pod_mode.m_game_state = copy_from_attribute.m_combat_pod_mode.m_game_state; copy_to_attribute.m_combat_pod_mode.m_current_round = copy_from_attribute.m_combat_pod_mode.m_current_round; copy_to_attribute.m_combat_pod_mode.m_current_state_start_time = copy_from_attribute.m_combat_pod_mode.m_current_state_start_time; copy_to_attribute.m_combat_pod_mode.m_charged_step = copy_from_attribute.m_combat_pod_mode.m_charged_step; diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceSnapshotDoc.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceSnapshotDoc.cs index 2ed237b..5363e36 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceSnapshotDoc.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/BattleInstanceSnapshotDoc.cs @@ -19,7 +19,6 @@ public class BattleInstanceSnapshotPlayModePodCombatAttrib [JsonProperty("pickup_pods")] public ConcurrentDictionary m_pickup_pods {get; set;} = new (); - [JsonProperty("pickup_pods_generation_info")] public List m_pickup_pod_generated_info { get; set; } = new(); [JsonProperty("weapons")] @@ -28,7 +27,7 @@ public class BattleInstanceSnapshotPlayModePodCombatAttrib [JsonProperty("buffs")] public ConcurrentDictionary m_buffs {get; set;} = new (); - [JsonProperty("state")] public BattleRoundStateType m_round_state_type { get; set; } = BattleRoundStateType.Rounding; + [JsonProperty("state")] public GameModeState m_game_state { get; set; } = GameModeState.None; [JsonProperty("round")] public int m_current_round { get; set; } = 1; [JsonProperty("state_start_time")] public DateTime m_current_state_start_time { get; set; } = DateTimeHelper.Current; diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/SystemBattleEvent.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/SystemBattleEvent.cs index 7261acc..6187cce 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/SystemBattleEvent.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/SystemBattleEvent.cs @@ -6,20 +6,19 @@ namespace GameServer; public class SystemBattleEvent { public Int32 m_event_id { get; set; } = 0; - public Int32 m_instance_id { get; set; } = 0; - + public Int32 m_instance_id { get; set; } = 0;//미사용 예정 public DateTime m_ready_time { get; set; } = DateTimeHelper.Current; public bool m_is_send_noti { get; set; } = false; - + public Int32 m_game_mode_id { get; set; } = 1; public DateTime m_start_time { get; set; } = DateTimeHelper.Current; - public Int32 m_ffa_config_data_id { get; set; } = 4; - public Int32 m_ffa_reward_group_id { get; set; } = 7; + public DateTime m_end_time { get; set; } = DateTimeHelper.Current; + public Int32 m_ffa_config_data_id { get; set; } = 4;//미사용 예정 + public Int32 m_ffa_reward_group_id { get; set; } = 7;//미사용 예정 public Int32 m_ffa_hot_time { get; set; } = 1; - public Int32 m_round_count { get; set; } = 4; + public Int32 m_round_count { get; set; } = 1; public SystemBattleEvent() { } - } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/TpsFfaChargeRewardData.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/TpsFfaChargeRewardData.cs new file mode 100644 index 0000000..de25e2f --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Entity/TpsFfaChargeRewardData.cs @@ -0,0 +1,14 @@ +namespace GameServer; + +public class TpsFfaChargeRewardData +{ + public readonly int m_charge_level; + public readonly int m_charge_time; + public readonly int m_reward_group_id; + public TpsFfaChargeRewardData(int chargeLevel, int chargeTime, int rewardGroupId) + { + m_charge_level = chargeLevel; + m_charge_time = chargeTime; + m_reward_group_id = rewardGroupId; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleConstant.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleConstant.cs index 5ff3efb..6ae5ee2 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleConstant.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleConstant.cs @@ -4,15 +4,11 @@ public static class BattleConstant { public static readonly string PREFIX_BATTLE_INSTANCE_ROOM_ID = "battle_instance"; public static readonly string RESPAWN_POS_ANCHOR_NAME = "AT_RespawnPos"; - public static readonly string BATTLE_OBJECT_ANCHOR_NAME = "AT_BattleObject"; + public static readonly string START_POS_ANCHOR_NAME = "AT_StartPos"; public static readonly string BATTLE_OBJECT_GROUP_ANCHOR_NAME = "AT_BattleObjectGroup"; - public static readonly string BATTLE_ROOM_TRANSACTION_ID_NAME = "BattleRoom"; public static readonly string BATTLE_POD_STORAGE_NAME = "Pod_CombatStand"; - public static readonly string PREFIX_BATTLE_TRANSACTION_GUID = "battle_tran:"; public static readonly string BATTLE_INSTANCE_TRANSACTION_NAME = "BattleInstanceUpdate"; - public static readonly int BATTLE_EVENT_CHECK_INTERVAL = 240_000;//4분 - public static readonly int BATTLE_ROOM_CHECK_INTERVAL = 200; } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleInstancePlayModeHelper.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleInstancePlayModeHelper.cs index c3f35d6..b9b668b 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleInstancePlayModeHelper.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleInstancePlayModeHelper.cs @@ -9,22 +9,22 @@ namespace GameServer; public static class BattleInstancePlayModeHelper { - public static BattleInstancesObject? getBattleObject(string anchorGuid, BattleInstanceSnapshotAttribute attribute, EBattleObjectType type) + public static GameModeObject? getBattleObject(string anchorGuid, BattleInstanceSnapshotAttribute attribute, EGameModeObjectType type) { - BattleInstancesObject? battle_instance_object = null; - if (type.Equals(EBattleObjectType.Weapon)) + GameModeObject? battle_instance_object = null; + if (type.Equals(EGameModeObjectType.Weapon)) { battle_instance_object = getBattleObjectWeapon(anchorGuid, attribute); } - else if (type.Equals(EBattleObjectType.Buff)) + else if (type.Equals(EGameModeObjectType.Buff)) { battle_instance_object = getBattleObjectBuff(anchorGuid, attribute); } - else if (type.Equals(EBattleObjectType.Pod_Combat)) + else if (type.Equals(EGameModeObjectType.Pod_Combat)) { battle_instance_object = getBattleObjectPodCombat(anchorGuid, attribute); } - else if (type.Equals(EBattleObjectType.Pod_Box)) + else if (type.Equals(EGameModeObjectType.Pod_Box)) { battle_instance_object = getBattleObjectPickupPod(anchorGuid, attribute); } @@ -32,7 +32,7 @@ public static class BattleInstancePlayModeHelper return battle_instance_object; } - private static BattleInstancesObject getBattleObjectWeapon(string anchorGuid, BattleInstanceSnapshotAttribute attribute) + private static GameModeObject getBattleObjectWeapon(string anchorGuid, BattleInstanceSnapshotAttribute attribute) { if (false == attribute.m_combat_pod_mode.m_weapons.TryGetValue(anchorGuid, out var battle_instance_object_weapon)) { @@ -42,7 +42,7 @@ public static class BattleInstancePlayModeHelper return battle_instance_object_weapon; } - private static BattleInstancesObject getBattleObjectBuff(string anchorGuid, BattleInstanceSnapshotAttribute attribute) + private static GameModeObject getBattleObjectBuff(string anchorGuid, BattleInstanceSnapshotAttribute attribute) { if (false == attribute.m_combat_pod_mode.m_buffs.TryGetValue(anchorGuid, out var battle_instance_object_buff)) { @@ -52,7 +52,7 @@ public static class BattleInstancePlayModeHelper return battle_instance_object_buff; } - private static BattleInstancesObject getBattleObjectPodCombat(string anchorGuid, BattleInstanceSnapshotAttribute attribute) + private static GameModeObject getBattleObjectPodCombat(string anchorGuid, BattleInstanceSnapshotAttribute attribute) { if (false == attribute.m_combat_pod_mode.m_pod_storages.TryGetValue(anchorGuid, out var storages)) { @@ -63,7 +63,7 @@ public static class BattleInstancePlayModeHelper return storages; } - private static BattleInstancesObject getBattleObjectPickupPod(string anchorGuid, BattleInstanceSnapshotAttribute attribute) + private static GameModeObject getBattleObjectPickupPod(string anchorGuid, BattleInstanceSnapshotAttribute attribute) { if (false == attribute.m_combat_pod_mode.m_pickup_pods.TryGetValue(anchorGuid, out var pickupPod)) { @@ -76,18 +76,12 @@ public static class BattleInstancePlayModeHelper return pickupPod; } - public static bool isPodCombat(BattleInstanceSnapshotAttribute attribute, string anchorGuid) - { - return attribute.m_combat_pod_mode.m_pod_combat.m_anchor_guid.Equals(anchorGuid); - } - - public static (Result, BattleObjectMetaData) getBattleObjectMeta(string anchorGuid, GameModeBase battleInstanceRoom) { var map = battleInstanceRoom.getInstanceRoom().getMap(); string err_msg = string.Empty; var result = new Result(); - if(false == map.m_anchors.TryGetValue(anchorGuid, out var anchorInfo)) + if(false == map.getAnchors().TryGetValue(anchorGuid, out var anchorInfo)) { err_msg = $"Anchor Info Not Exist.... room_id : {map.m_room_id} anchor guid : {anchorGuid}"; result.setFail(ServerErrorCode.BattleInstanceNotExistAnchors, err_msg); diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleObjectInteractionLogicHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleObjectInteractionLogicHandler.cs index fb95f29..40b8219 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleObjectInteractionLogicHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleObjectInteractionLogicHandler.cs @@ -21,7 +21,7 @@ public class BattleObjectInteractionLogicHandler public string m_user_guid { get; } = string.Empty; public string m_interaction_anchor_guid { get; } = string.Empty; public BattleObjectMetaData m_battle_object_meta { get; set; } = new(new BattleObjectMetaDataMutable()); - public BattleInstancesObject m_battle_object { get; set; } = new BattleObjectEmpty(); + public GameModeObject m_battle_object { get; set; } = new GameObjectEmpty(); public bool m_is_combat_pod { get; set; } = false; //true : 전달 받은 anchor_guid 가 combat pod을 의미한다. @@ -29,7 +29,7 @@ public class BattleObjectInteractionLogicHandler public bool m_is_need_combat_pod_noti { get; set; } = false; - public List m_need_noti_objects { get; set; } = new(); //인터렉션시 종속적으로상태가 같이 바뀌는 object들은 여기에 넣어서 나중에 noti 보낼때 사용 + public List m_need_noti_objects { get; set; } = new(); //인터렉션시 종속적으로상태가 같이 바뀌는 object들은 여기에 넣어서 나중에 noti 보낼때 사용 public DateTime m_object_active_time { get; set; } = DateTimeHelper.Current; diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleRoomHelper.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleRoomHelper.cs index a7f6290..8b2fb60 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleRoomHelper.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleRoomHelper.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Mode_Battle.Manage; +using MetaAssets; using Newtonsoft.Json; using ServerBase; using ServerCommon; @@ -38,7 +39,7 @@ public class BattleRoomHelper } - public static (Result, string) getRandomCombatPodAnchorGuid(BattleInstanceSnapshotAttribute attribute, GameModeTPSFreeForAll battleInstanceRoom) + public static (Result, string) getRandomCombatPodAnchorGuid(BattleInstanceSnapshotAttribute attribute, GameModeTPSFreeForAll battleInstanceRoom) { var result = new Result(); @@ -82,7 +83,7 @@ public class BattleRoomHelper return (result, string.Empty); } - public static (Result, string) getRandomRespawnPos(BattleInstanceSnapshotAttribute attribute, GameModeTPSFreeForAll battleInstanceRoom) + public static (Result, string) getRandomRespawnPos(BattleInstanceSnapshotAttribute attribute, GameModeTPSFreeForAll battleInstanceRoom) { List active_respawns = new(); var now = DateTimeHelper.Current; @@ -112,7 +113,7 @@ public class BattleRoomHelper } - public static (Result, string) getRandomPickupPod(int groupId, int idx, GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) + public static (Result, string) getRandomPickupPod(int groupId, int idx, GameModeTPSFreeForAll battleInstanceRoom, BattleInstanceSnapshotAttribute attribute) { var result = new Result(); @@ -171,9 +172,6 @@ public class BattleRoomHelper public static SystemBattleEvent? getBattleRoomStartTimeByEventId(string roomId) { - - //instanceroom:battle_instance:0:1017006:timestamp:5978:info//룸ID의 형태 - var event_id = getBattleEventIdFromRoomId(roomId); if (event_id == 0) @@ -204,65 +202,54 @@ public class BattleRoomHelper public static DateTime calculateDestroyedTime(DateTime startTime, Int32 configId, Int32 roundCount) { - if (false == MetaData.Instance._BattleFFAConfigMetaTable.TryGetValue(configId, out var meta)) + // if (false == MetaData.Instance._BattleFFAConfigMetaTable.TryGetValue(configId, out var meta)) + // { + // Log.getLogger().error($"Not Exist _BattleFFAConfigMetaTable configId : {configId}"); + // return DateTimeHelper.Current.AddSeconds(-10); + // } + + if(false == MetaData.Instance.Meta.GameModeTpsFfaMetaTable.GameModeTpsFfaMetaDataListbyId.TryGetValue(configId, out var metaTable)) { Log.getLogger().error($"Not Exist _BattleFFAConfigMetaTable configId : {configId}"); return DateTimeHelper.Current.AddSeconds(-10); } - + if (roundCount < 1) { Log.getLogger().error($"roundCount less than 1 roundCount : {roundCount}"); return DateTimeHelper.Current.AddSeconds(-10); } - var round_time = meta.RoundTime; - var wait_time = meta.NextRoundWaitTime; - var result_time = meta.ResultUIWaitTime; + var round_time = metaTable.RoundTime; + var wait_time = metaTable.NextRoundWaitTime; + var result_time = metaTable.ResultUIWaitTime; var process_time_sec = (round_time * roundCount) + (wait_time * (roundCount - 1)) + result_time; return startTime.AddSeconds(process_time_sec); } - - public static DateTime calculateRoomJoinableTime(DateTime startTime, Int32 configId, Int32 roundCount) + public static MetaAssets.Reward modifyRewardHotTime(MetaAssets.Reward reward, int hotTime) { - if (false == MetaData.Instance._BattleFFAConfigMetaTable.TryGetValue(configId, out var meta)) + RewardMutable reward_mutable = new(); + if (reward.Currency != null) { - Log.getLogger().error($"Not Exist _BattleFFAConfigMetaTable configId : {configId}"); - return DateTimeHelper.Current.AddSeconds(-10); + reward_mutable.Currency = new(); + reward_mutable.Currency.Id = reward.Currency.Id; + reward_mutable.Currency.Value = reward.Currency.Value * hotTime; + MetaAssets.Reward modified_reward = new MetaAssets.Reward(reward_mutable); + return modified_reward; } - if (roundCount < 1) + if (reward.Item != null) { - Log.getLogger().error($"roundCount less than 1 roundCount : {roundCount}"); - return DateTimeHelper.Current.AddSeconds(-10); + reward_mutable.Item = new(); + reward_mutable.Item.Id = reward.Item.Id; + reward_mutable.Item.Count = reward.Item.Count * hotTime; + MetaAssets.Reward modified_reward = new MetaAssets.Reward(reward_mutable); + return modified_reward; } - - - var round_time = meta.RoundTime; - var wait_time = meta.NextRoundWaitTime; - - var process_time_sec = (round_time * roundCount) + (wait_time * (roundCount - 1)) - meta.EntranceClosingTime; - - return startTime.AddSeconds(process_time_sec); + + return reward; } - - public static IHostMigrator createHostMigrator(BattlePlayMode mode) - { - switch (mode) - { - case BattlePlayMode.PodCombat: - - //return HostMigrationFactory.createCommonHostMigrator((int) BattlePlayMode.PodCombat, GameServerApp.getServerLogic()); - return new BattleFFAModeHostMigrator(); - default: - Log.getLogger($"createHostMigrator Not implements mode : {mode}"); - throw new NotImplementedException(); - - } - } - - } diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleRoomNotifyHelper.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleRoomNotifyHelper.cs index 126cbe9..4b12471 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleRoomNotifyHelper.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/BattleRoomNotifyHelper.cs @@ -1,4 +1,5 @@ -using GameServer.Contents.GameMode.Mode_Battle.Manage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Battle.Manage; using Google.Protobuf.WellKnownTypes; using Newtonsoft.Json; using ServerCommon; @@ -9,13 +10,13 @@ namespace GameServer; public static class BattleRoomNotifyHelper { - public static void send_GS2C_NTF_POD_COMBAT_STATE(GameModeTPSFreeForAll battleInstanceRoom, Player player) + public static void send_GS2C_NTF_POD_COMBAT_STATE(GameModeTPSFreeForAll battleInstanceRoom, Player player) { var ntf = makePodCombatStateNotify(battleInstanceRoom); GameServerApp.getServerLogic().onSendPacket(player, ntf); } - public static void broadcast_GS2C_NTF_POD_COMBAT_STATE(GameModeTPSFreeForAll battleInstanceRoom) + public static void broadcast_GS2C_NTF_POD_COMBAT_STATE(GameModeTPSFreeForAll battleInstanceRoom) { var ntf = makePodCombatStateNotify(battleInstanceRoom); @@ -24,7 +25,7 @@ public static class BattleRoomNotifyHelper battleInstanceRoom.getInstanceRoom().Broadcast(ntf); } - private static ClientToGame makePodCombatStateNotify(GameModeTPSFreeForAll battleInstanceRoom) + private static ClientToGame makePodCombatStateNotify(GameModeTPSFreeForAll battleInstanceRoom) { ClientToGame ntf = new ClientToGame(); ntf.Message = new ClientToGameMessage(); @@ -51,7 +52,7 @@ public static class BattleRoomNotifyHelper } - public static void send_GS2C_NTF_BATTLE_INSTANCE_STATE(GameModeTPSFreeForAll battleInstanceRoom, Player player) + public static void send_GS2C_NTF_BATTLE_INSTANCE_STATE(GameModeTPSFreeForAll battleInstanceRoom, Player player) { var ntf = makeBattleInstanceStateNotify(battleInstanceRoom); Log.getLogger().debug($"send_GS2C_NTF_BATTLE_INSTANCE_STATE ntf battleInstanceRoom Id : {battleInstanceRoom.getRoomId()} data : {JsonConvert.SerializeObject(ntf.Message.NtfBattleInstanceState)}"); @@ -59,33 +60,35 @@ public static class BattleRoomNotifyHelper GameServerApp.getServerLogic().onSendPacket(player, ntf); } - public static void broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(GameModeTPSFreeForAll battleInstanceRoom) + public static void broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(GameModeTPSFreeForAll battleInstanceRoom) { var ntf = makeBattleInstanceStateNotify(battleInstanceRoom); Log.getLogger().debug($"broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE ntf battleInstanceRoom Id : {battleInstanceRoom.getRoomId()} data : {JsonConvert.SerializeObject(ntf.Message.NtfBattleInstanceState)}"); battleInstanceRoom.getInstanceRoom().Broadcast(ntf); } - private static ClientToGame makeBattleInstanceStateNotify(GameModeTPSFreeForAll battleInstanceRoom) + private static ClientToGame makeBattleInstanceStateNotify(GameModeTPSFreeForAll battleInstanceRoom) { ClientToGame ntf = new ClientToGame(); ntf.Message = new ClientToGameMessage(); ntf.Message.NtfBattleInstanceState = new ClientToGameMessage.Types.GS2C_NTF_BATTLE_INSTANCE_STATE(); - ntf.Message.NtfBattleInstanceState.CreateTime = Timestamp.FromDateTime(battleInstanceRoom.m_battle_instance_event_start_time); + ntf.Message.NtfBattleInstanceState.CreateTime = Timestamp.FromDateTime(battleInstanceRoom.m_game_start_time); + var attribute = battleInstanceRoom.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); ntf.Message.NtfBattleInstanceState.CurrentRound = attribute.m_combat_pod_mode.m_current_round; ntf.Message.NtfBattleInstanceState.RoundStateStartTime = Timestamp.FromDateTime(attribute.m_combat_pod_mode.m_current_state_start_time); - ntf.Message.NtfBattleInstanceState.RoundStateType = attribute.m_combat_pod_mode.m_round_state_type; + ntf.Message.NtfBattleInstanceState.RoundStateType = attribute.m_combat_pod_mode.m_game_state = battleInstanceRoom.getState().getStateType(); ntf.Message.NtfBattleInstanceState.RewardedStep = attribute.m_combat_pod_mode.m_rewarded_step; ntf.Message.NtfBattleInstanceState.ChargedStep = attribute.m_combat_pod_mode.m_charged_step; + ntf.Message.NtfBattleInstanceState.NextUpdatableTime = Timestamp.FromDateTime(battleInstanceRoom.getState().getNextStateChangeTime()); return ntf; } - public static void send_GS2C_NTF_PLAYER_DEATH(GameModeTPSFreeForAll battleInstanceRoom, string killerGuid, string deadUserGuid, Player player) + public static void send_GS2C_NTF_PLAYER_DEATH(GameModeTPSFreeForAll battleInstanceRoom, string killerGuid, string deadUserGuid, Player player) { ClientToGame ntf = new ClientToGame(); ntf.Message = new ClientToGameMessage(); @@ -99,7 +102,7 @@ public static class BattleRoomNotifyHelper } - public static void send_GS2C_NTF_PLAYER_RESPAWN(GameModeTPSFreeForAll battleInstanceRoom, Pos pos, Player player) + public static void broadcast_GS2C_NTF_PLAYER_RESPAWN(GameModeTPSFreeForAll battleInstanceRoom, Pos pos, Player player) { ClientToGame ntf = new ClientToGame(); ntf.Message = new ClientToGameMessage(); @@ -112,30 +115,28 @@ public static class BattleRoomNotifyHelper battleInstanceRoom.getInstanceRoom().Broadcast(ntf); } - public static void broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(GameModeTPSFreeForAll battleInstanceRoom, BattleObjectInteractionLogicHandler handler) + public static void broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(GameModeBase gameModeBase, string anchorGuid, BattleObjectInteractionLogicHandler handler) { BattleObjectInfo info = new(); - info.AnchorGuid = handler.m_battle_object.m_anchor_guid; + info.AnchorGuid = anchorGuid; var now = DateTimeHelper.Current; info.IsActive = handler.m_battle_object.m_is_active ? BoolType.True : BoolType.False; List infos = new(); infos.Add(info); - - - broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(battleInstanceRoom, infos); + broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(gameModeBase, infos); } - public static void broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(GameModeTPSFreeForAll battleInstanceRoom, List infos) + public static void broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(GameModeBase gameModeBase, List infos) { ClientToGame ntf = new ClientToGame(); ntf.Message = new ClientToGameMessage(); ntf.Message.NtfBattleObjectStateInfo = new ClientToGameMessage.Types.GS2C_NTF_BATTLE_OBJECT_STATE_INFO(); ntf.Message.NtfBattleObjectStateInfo.BattleObjectInfos.AddRange(infos); - Log.getLogger().debug($"broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO ntf battleInstanceRoom Id : {battleInstanceRoom.getRoomId()} data :{JsonConvert.SerializeObject(infos)}"); + Log.getLogger().debug($"broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO ntf battleInstanceRoom Id : {gameModeBase.getRoomId()} data :{JsonConvert.SerializeObject(infos)}"); - battleInstanceRoom.getInstanceRoom().Broadcast(ntf); + gameModeBase.getInstanceRoom().Broadcast(ntf); } public static void send_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(List infos, Player player) @@ -152,7 +153,7 @@ public static class BattleRoomNotifyHelper } - public static void broadcast_GS2C_NTF_BATTLE_OBJECT_INTERACTION(GameModeTPSFreeForAll battleInstanceRoom, string userGuid, string anchorGuid) + public static void broadcast_GS2C_NTF_BATTLE_OBJECT_INTERACTION(GameModeBase gameModeBase, string userGuid, string anchorGuid) { ClientToGame ntf = new ClientToGame(); ntf.Message = new ClientToGameMessage(); @@ -160,12 +161,13 @@ public static class BattleRoomNotifyHelper ntf.Message.NtfBattleObjectInteraction.AnchorGuid = anchorGuid; ntf.Message.NtfBattleObjectInteraction.UserGuid = userGuid; - Log.getLogger().debug($"broadcast_GS2C_NTF_BATTLE_OBJECT_INTERACTION ntf battleInstanceRoom Id : {battleInstanceRoom.getRoomId()} data : {JsonConvert.SerializeObject(ntf.Message.NtfBattleObjectInteraction)}"); - - battleInstanceRoom.getInstanceRoom().Broadcast(ntf); + Log.getLogger().debug($"broadcast_GS2C_NTF_BATTLE_OBJECT_INTERACTION ntf battleInstanceRoom Id : {gameModeBase.getRoomId()} data : {JsonConvert.SerializeObject(ntf.Message.NtfBattleObjectInteraction)}"); + gameModeBase.getInstanceRoom().Broadcast(ntf); } - public static void broadcast_GS2C_NTF_BATTLE_REWARD(GameModeTPSFreeForAll battleInstanceRoom, string acquireUserGuid, CommonResult commonResult) + + + public static void broadcast_GS2C_NTF_BATTLE_REWARD(GameModeBase battleInstanceRoom, string acquireUserGuid, CommonResult commonResult) { if (commonResult.EntityCommonResults.Count == 0) return; if (acquireUserGuid.Equals(string.Empty)) return; @@ -190,13 +192,33 @@ public static class BattleRoomNotifyHelper GameServerApp.getServerLogic().onSendPacket(player, ntf); } + public static void broadcastBattleEventNotify() + { + var users = GameServerApp.getServerLogic().getPlayerManager().getUsers(); + var players = users.Values.ToArray(); + + if (players.Length == 0) return; + + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfBattleEvent = new ClientToGameMessage.Types.GS2C_NTF_BATTLE_EVENT(); + ntf.Message.NtfBattleEvent.BattleEvent.AddRange(BattleInstanceManager.It.getAllProtoBattleEvents()); + + List userGuids = players.Select(p => p.getUserGuid()).ToList(); + Log.getLogger().debug($"broadcastBattleEventNotify send ntf user_guids : {userGuids}, ntf : {JsonConvert.SerializeObject(ntf.Message.NtfBattleEvent)}"); + foreach (var each in players) + { + GameServerApp.getServerLogic().onSendPacket(each, ntf); + } + } + public static void broadcast_GS2C_NTF_BATTLE_EVENT(List infos) { ClientToGame ntf = new ClientToGame(); ntf.Message = new ClientToGameMessage(); ntf.Message.NtfBattleEvent = new ClientToGameMessage.Types.GS2C_NTF_BATTLE_EVENT(); ntf.Message.NtfBattleEvent.BattleEvent.AddRange(infos); - + Log.getLogger().debug($"broadcast battle event ntf ntf : {JsonConvert.SerializeObject(ntf.Message.NtfBattleEvent)}"); foreach (var each in GameServerApp.getServerLogic().getPlayerManager().getUsers()) { var player = each.Value; @@ -204,7 +226,7 @@ public static class BattleRoomNotifyHelper } } - public static void broadcast_GS2C_NTF_PREPARATION_FOR_LEAVING_BATTLE_INSTANCE(GameModeTPSFreeForAll battleInstanceRoom, string leavingUserGuid) + public static void broadcast_GS2C_NTF_PREPARATION_FOR_LEAVING_BATTLE_INSTANCE(GameModeTPSFreeForAll battleInstanceRoom, string leavingUserGuid) { ClientToGame ntf = new ClientToGame(); ntf.Message = new ClientToGameMessage(); @@ -215,24 +237,6 @@ public static class BattleRoomNotifyHelper battleInstanceRoom.getInstanceRoom().Broadcast(ntf); } - - public static void broadcast_GS2C_NTF_P2P_HOST_UPDATE(InstanceRoom instanceRoom, string hostUserGuid) - { - ClientToGame ntf = new ClientToGame(); - ntf.Message = new ClientToGameMessage(); - ntf.Message.NtfP2PHostUpdate = new ClientToGameMessage.Types.GS2C_NTF_P2P_HOST_UPDATE(); - ntf.Message.NtfP2PHostUpdate.HostUserGuid = hostUserGuid; - - Log.getLogger().debug($"broadcast_GS2C_NTF_P2P_HOST_UPDATE ntf battleInstanceRoom Id : {instanceRoom.getMap().m_room_id} data : {JsonConvert.SerializeObject(ntf.Message.NtfP2PHostUpdate)}"); - instanceRoom.Broadcast(ntf); - } - public static void send_GS2C_NTF_P2P_HOST_UPDATE(Player player, string hostUserGuid) - { - ClientToGame ntf = new ClientToGame(); - ntf.Message = new ClientToGameMessage(); - ntf.Message.NtfP2PHostUpdate = new ClientToGameMessage.Types.GS2C_NTF_P2P_HOST_UPDATE(); - ntf.Message.NtfP2PHostUpdate.HostUserGuid = hostUserGuid; - GameServerApp.getServerLogic().onSendPacket(player, ntf); - } + } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/TpsFfaHelper.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/TpsFfaHelper.cs new file mode 100644 index 0000000..48dc634 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Helper/TpsFfaHelper.cs @@ -0,0 +1,98 @@ +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerCommon; +using ServerCore; + +namespace GameServer; + +public static class TpsFfaHelper +{ + public static IGameObjectInteractionHandler? getTpsFfaInteractAction(GameModeBase gameModeBase, EGameModeObjectType objectType, string anchorGuid) + { + switch (objectType) + { + case EGameModeObjectType.Weapon: + return gameModeBase.getEntityAction(); + case EGameModeObjectType.Buff: + return gameModeBase.getEntityAction(); + case EGameModeObjectType.Pod_Box: + return gameModeBase.getEntityAction(); + case EGameModeObjectType.Pod_Combat: + return getTpsFfaPodCombatInteractAction(gameModeBase, anchorGuid); + default: + Log.getLogger().error($"getTpsFfaInteractAction objectType error !!!, gameModeId : {gameModeBase.getGameModeId()}, objectType : {objectType}"); + return null; + } + } + + public static IGameObjectInteractionDataHandler? getTpsFfaInteractDataHandler(Player player, GameModeBase gameModeBase, EGameModeObjectType objectType, string anchorGuid, string roomId, DateTime interactionTime) + { + switch (objectType) + { + case EGameModeObjectType.Weapon: + return new TpsFfaWeaponInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, roomId, interactionTime); + case EGameModeObjectType.Buff: + return new TpsFfaBuffInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, roomId, interactionTime); + case EGameModeObjectType.Pod_Box: + return new TpsFfaPickupPodInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, roomId, interactionTime); + case EGameModeObjectType.Pod_Combat: + return getTpsFfaPodCombatInteractDataHandler(player, gameModeBase, anchorGuid, interactionTime); + default: + Log.getLogger().error($"getTpsFfaInteractAction objectType error !!!, anchorGuid : {anchorGuid}, objectType : {objectType}, roomId : {roomId}, player : {player.toBasicString()}"); + return new TpsFfaWeaponInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, roomId, interactionTime); + } + } + + private static IGameObjectInteractionDataHandler? getTpsFfaPodCombatInteractDataHandler(Player player, GameModeBase gameModeBase, string anchorGuid, DateTime interactionTime) + { + var attribute = gameModeBase.getEntityAttribute(); + if (attribute is null) + { + Log.getLogger().error($"BattleInstanceSnapshotAttribute is null !!!, gameModeId : {gameModeBase.getGameModeId()}, anchorguid : {anchorGuid}"); + return null; + } + + var is_combat_pod = isPodCombat(attribute, anchorGuid); + if (is_combat_pod) + { + return new TpsFfaCombatPodInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, gameModeBase.getRoomId(), interactionTime); + } + else + { + return new TpsFfaPodStorageInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, gameModeBase.getRoomId(), interactionTime); + } + } + + private static IGameObjectInteractionHandler? getTpsFfaPodCombatInteractAction(GameModeBase gameModeBase, string anchorGuid) + { + var attribute = gameModeBase.getEntityAttribute(); + if (attribute is null) + { + Log.getLogger().error($"BattleInstanceSnapshotAttribute is null !!!, gameModeId : {gameModeBase.getGameModeId()}, anchorguid : {anchorGuid}"); + return null; + } + + var is_combat_pod = isPodCombat(attribute, anchorGuid); + if (is_combat_pod) + { + return gameModeBase.getEntityAction(); + } + else + { + return gameModeBase.getEntityAction(); + } + } + + public static bool isPodCombat(BattleInstanceSnapshotAttribute attribute, string anchorGuid) + { + return attribute.m_combat_pod_mode.m_pod_combat.m_anchor_guid.Equals(anchorGuid); + } + + public static IGameModeLoadCompleteHandler getLoadCompleteAction(GameModeBase gameModeBase) + { + var handler = gameModeBase.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(handler, () => $"TpsFfaLoadCompleteAction handler is null !!!"); + return handler; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattlePodCombatRewardBusinessLog.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattlePodCombatRewardBusinessLog.cs index 3b1592e..dc05255 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattlePodCombatRewardBusinessLog.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattlePodCombatRewardBusinessLog.cs @@ -8,7 +8,7 @@ public class BattlePodCombatRewardBusinessLog : ILogInvokerEx { private BattlePodCombatRewardLogInfo m_info; - public BattlePodCombatRewardBusinessLog(string roomId, int round, BattleRoundStateType roundSate + public BattlePodCombatRewardBusinessLog(string roomId, int round, GameModeState roundSate , string rewardUserGuid, string rewardUserNickname , int chargedStep, int rewardedStep, List rewards) : base(LogDomainType.BattleReward) { diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleRespawnBusinessLog.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleRespawnBusinessLog.cs index 3ee362b..6fb0611 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleRespawnBusinessLog.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleRespawnBusinessLog.cs @@ -8,7 +8,7 @@ public class BattleRespawnBusinessLog : ILogInvokerEx private BattleRespawnLogInfo m_info; - public BattleRespawnBusinessLog(string roomId, int round, BattleRoundStateType roundState, string userGuid, string userNickname, string respawnAnchorGuid, DateTime nextAvailableRespawnTimeAtThisAnchor) + public BattleRespawnBusinessLog(string roomId, int round, GameModeState roundState, string userGuid, string userNickname, string respawnAnchorGuid, DateTime nextAvailableRespawnTimeAtThisAnchor) : base(LogDomainType.BattleRespawn) { m_info = new(this, roomId, round, roundState, userGuid, userNickname, respawnAnchorGuid, nextAvailableRespawnTimeAtThisAnchor); diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleRoundUpdateBusinessLog.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleRoundUpdateBusinessLog.cs index 51a7254..8a709c4 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleRoundUpdateBusinessLog.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleRoundUpdateBusinessLog.cs @@ -7,7 +7,7 @@ public class BattleRoundUpdateBusinessLog : ILogInvokerEx { private BattleRoundingUpdateLogInfo m_info; - public BattleRoundUpdateBusinessLog(string roomId, int endedRound, BattleRoundStateType roundSate, List users): base(LogDomainType.BattleRound) + public BattleRoundUpdateBusinessLog(string roomId, int endedRound, GameModeState roundSate, List users): base(LogDomainType.BattleRound) { m_info = new BattleRoundingUpdateLogInfo(this, roomId, endedRound, roundSate, users); } diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleSnapShotBusinessLog.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleSnapShotBusinessLog.cs index 55a95d4..733c13a 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleSnapShotBusinessLog.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Log/BattleSnapShotBusinessLog.cs @@ -10,9 +10,8 @@ public class BattleSnapShotBusinessLog : ILogInvokerEx { private BattleSnapshotLogInfo m_info; - public BattleSnapShotBusinessLog(GameModeTPSFreeForAll battleInstanceRoom, string loadType): base(LogDomainType.BattleSnapshot) + public BattleSnapShotBusinessLog(GameModeTPSFreeForAll battleInstanceRoom, string loadType): base(LogDomainType.BattleSnapshot) { - //kihoon todo : GameModeTPSFreeForAll 이걸로 받을지 아니면 고칠지 고민 필요 m_info = createBattleSnapshotLogInfo(this, battleInstanceRoom, loadType); } @@ -27,7 +26,7 @@ public class BattleSnapShotBusinessLog : ILogInvokerEx - public BattleSnapshotLogInfo createBattleSnapshotLogInfo(ILogInvoker parent, GameModeTPSFreeForAll battleInstanceRoom, string loadType) + public BattleSnapshotLogInfo createBattleSnapshotLogInfo(ILogInvoker parent, GameModeTPSFreeForAll battleInstanceRoom, string loadType) { var room_id = battleInstanceRoom.getRoomId(); BattleSnapshotLogInfo info = new(parent, room_id, loadType); @@ -38,13 +37,9 @@ public class BattleSnapShotBusinessLog : ILogInvokerEx ServerCore.Log.getLogger().warn($"attribute is null so return empty battleInstanceRoom.roomId() : {room_id}"); return info; } - - //info.m_play_mode = battleInstanceRoom.m_play_mode; - info.m_battle_instance_event_start_time = battleInstanceRoom.m_battle_instance_event_start_time; - info.m_pod_combat_reward_group_id = battleInstanceRoom.m_pod_combat_reward_group_id; - info.m_pod_combat_ffa_id = battleInstanceRoom.m_pod_combat_ffa_id; - info.m_hot_time_reward = battleInstanceRoom.m_hot_time_reward; - info.m_round_count = battleInstanceRoom.m_round_count; + info.m_battle_instance_event_start_time = battleInstanceRoom.m_game_start_time; + info.m_hot_time_reward = battleInstanceRoom.m_ffa_meta.m_hot_time; + info.m_round_count = battleInstanceRoom.m_ffa_meta.m_round_count; info.m_respawns = attribute.m_combat_pod_mode.m_respawns; @@ -54,7 +49,7 @@ public class BattleSnapShotBusinessLog : ILogInvokerEx info.m_pickup_pod_generated_info.AddRange(attribute.m_combat_pod_mode.m_pickup_pod_generated_info.Values); info.m_weapons = attribute.m_combat_pod_mode.m_weapons; info.m_buffs = attribute.m_combat_pod_mode.m_buffs; - info.m_round_state_type = attribute.m_combat_pod_mode.m_round_state_type; + info.m_game_mode_state = attribute.m_combat_pod_mode.m_game_state; info.m_current_round = attribute.m_combat_pod_mode.m_current_round; info.m_current_state_start_time = attribute.m_combat_pod_mode.m_current_state_start_time; info.m_charged_step = attribute.m_combat_pod_mode.m_charged_step; diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/GameModeTPSFreeForAll.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/GameModeTPSFreeForAll.cs index f4e0db4..2f5b711 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/GameModeTPSFreeForAll.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/GameModeTPSFreeForAll.cs @@ -1,9 +1,12 @@ using System.Collections.Concurrent; using Axion.Collections.Concurrent; using GameServer.Contents.Battle.Log; +using GameServer.Contents.GameMode.Action; using GameServer.Contents.GameMode.Helper; using GameServer.Contents.GameMode.Manage.PlayManage; using GameServer.Contents.GameMode.Mode_Battle.Manage; +using GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.Manage; +using GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.Meta; using MetaAssets; using Newtonsoft.Json; using ServerBase; @@ -12,149 +15,91 @@ using ServerCore; namespace GameServer; -public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode +public class GameModeTPSFreeForAll : GameModeBase //where T : ITPSMode 나중에 필요하면 추가 할것 { - T m_tps_mode_data; + public DateTime m_game_start_time { get; private set; } = DateTimeHelper.Current; //실제 이벤트 시작시간 + public DateTime m_game_event_end_time { get; private set; } = DateTimeHelper.MaxTime; //실제 이벤트 종료시간 + public TpsFfaMetaData m_ffa_meta = new(); //여기서 필요한거 한번 참조 해서 가지고 있는게 편하다. - public DateTime m_battle_instance_event_start_time { get; private set; } = DateTimeHelper.Current; //실제 이벤트 시작시간 - //public DateTime m_battle_instance_load_time { get; } = DateTimeHelper.Current; //이벤트 시작시간과 관계 없이 메모리에 로드 될때의 시간 이 변수가 필요할지 모르겠다. - - - //kihoon todo : 이거 위치 수정해야됨 - //todo : 제네릭으로 받아도 되려나?.. 고민 필요 우선 임시데이터 - public Int32 m_pod_combat_reward_group_id = 1; - public Int32 m_pod_combat_ffa_id = 1; - public BattleFFAConfigData m_ffa_config_meta; - public Dictionary m_ffa_reward_meta = new(); - public Int32 m_hot_time_reward = 1; - public Int32 m_round_count = 8; - - public ConcurrentHashSet m_respawn_pos_anchors_meta { get; private set; } = new(); //이건 랜덤 돌릴 일이 잦아서 그냥 들고 있는다... + public ConcurrentHashSet m_respawn_pos_anchors_meta { get; private set; } = new();//이건 랜덤 돌릴 일이 잦아서 그냥 들고 있는다... + public List<(string /*anchor_guid*/, string/*userGuid*/)> m_start_pos_anchors_meta { get; private set; } = new(); public ConcurrentDictionary> m_battle_pod_storage_guid_group { get; private set; } = new(); //pod combat을 생성해줄때 필요 public ConcurrentDictionary> m_battle_pickup_pod_guid_group { get; private set; } = new(); - - public GameModeTPSFreeForAll(T tpsModeData, InstanceRoom instanceRoom) : base(EntityType.GameModeRunRace, instanceRoom) + + public GameModeTPSFreeForAll(InstanceRoom instanceRoom, int gameModeId) + : base(EntityType.GameModeTpsfreeForAll, instanceRoom, gameModeId, new TPSFreeForAllTransitionStrategy()) { - m_tps_mode_data = tpsModeData; - - - // kihoon todo : 이것도 위치 이동이 필요하다. - if (false == MetaData.Instance._BattleFFAConfigMetaTable.TryGetValue(m_pod_combat_ffa_id, out var configData)) - { - var err_msg = $"Not exist Battle Conig Data id : {m_pod_combat_ffa_id}"; - Log.getLogger().error(err_msg); - NullReferenceCheckHelper.throwIfNull(configData, () => $"configData is null !!!"); - } - m_ffa_config_meta = new BattleFFAConfigData(new BattleFFAConfigDataMutable() - { - Id = configData.Id, - Description = configData.Description, - PlayerRespawnTime = configData.PlayerRespawnTime, - DefaultRoundCount = configData.DefaultRoundCount, - RoundTime = configData.RoundTime, - NextRoundWaitTime = configData.NextRoundWaitTime, - ResultUIWaitTime = configData.ResultUIWaitTime, - GetRewardTime = configData.GetRewardTime, - EntranceClosingTime = configData.EntranceClosingTime, - CurrencyType = configData.CurrencyType, - CurrencyCount = configData.CurrencyCount, - TPSGuideURL = configData.TPSGuideURL - }); } - - - public override async Task onInit() - { - Log.getLogger().debug("run race onInit called"); - - //제너릭 init - await m_tps_mode_data.initTPSMode(); - - - //레이스 모드에 필요한 액션 추가 - addFFAEntityAction(); - - //게임 모드에 필요한 상수값 입력 - setDefaultMetaConstants(); - - await initBattleRoom(); - - - - //다 마무리 되면 부모 init 호출 - var result = await base.onInit(); - - Log.getLogger().debug("run race onInit done"); - - return result; - - - - - } - - private void addFFAEntityAction() + //===================================================================================== + // Add DetailEntityActions + //===================================================================================== + #region Add DetailEntityActions + protected override void addDetailEntityActions() { Log.getLogger().debug("FFA addEntityAction called"); - + //Action 추가 - //kihoon todo : 이거 아래 걸로 일부 수정 필요 addEntityAction(new BattleInstanceAction(this)); addEntityAction(new BattleInstanceUpdateAction(this)); addEntityAction(new BattleObjectInteractAction(this)); addEntityAction(new BattleObjectRewardAction(this)); - - - addEntityAction(new FfaGameObjectPodStorageInteractAction(this)); - - + + addEntityAction(new TpsFfaPickupPodInteractAction(this)); + addEntityAction(new TpsFfaPodStorageInteractAction(this)); + addEntityAction(new TpsFfaWeaponInteractAction(this)); + addEntityAction(new TpsFfaCombatPodInteractAction(this)); + addEntityAction(new TpsFfaBuffInteractAction(this)); + + addEntityAction(new TpsFfaLoadCompleteAction(this)); + addEntityAction(new TpsFfaObjectUpdateAction(this)); + + addEntityAction(new TpsFfaSetUserReadyPosAction(this)); + + //Attribute 추가 - addEntityAttribute(new BattleInstanceSnapshotAttribute(this)); //ㅏkihoon todo : 이거 여기에 넣는게 맞나? - + addEntityAttribute(new BattleInstanceSnapshotAttribute(this)); + Log.getLogger().debug("FFA addEntityAction done"); } + #endregion - public async Task initBattleRoom() + + + //===================================================================================== + // DetailInitBeforeTaskCreate + //===================================================================================== + #region DetailInitBeforeTaskCreate + protected override async Task detailInitBeforeTaskCreate() + { + //게임 모드에 필요한 상수값 입력 + setDefaultMetaConstants(); + var result = await initFfaBeforeTaskCreate(); + if (result.isFail()) return result; + + return result; + } + #endregion + + protected override Task detailInitAfterTaskCreate() { var result = new Result(); - - // instance 정보 추가 등록 - var server_logic = GameServerApp.getServerLogic(); - var instance_room_storage = new InstanceRoomStorage(); - instance_room_storage.Init(server_logic.getRedisDb(), ""); - - - - DateTime start_time = DateTimeHelper.Current; - Int32 config_id = 1; - Int32 reward_id = 1; - Int32 hot_time = 1; - Int32 round_count = 8; - var system_battle_event = BattleRoomHelper.getBattleRoomStartTimeByEventId(getRoomId()); - if (system_battle_event is null) - { - Log.getLogger().error($"system_battle_event is null!!! : {result.toBasicString()} - instanceRoomId:{getRoomId()}"); - } - else - { - start_time = system_battle_event.m_start_time; - config_id = system_battle_event.m_ffa_config_data_id; - reward_id = system_battle_event.m_ffa_reward_group_id; - hot_time = system_battle_event.m_ffa_hot_time; - round_count = system_battle_event.m_round_count; - } - result = await instance_room_storage.setInstanceRoomExtraInfo(getRoomId(), EPlaceType.BattleRoom, start_time); //kihoon todo : 이거 나중에 GameRoom으로 바뀔때 문제 되니까 코드 바꿔야 된다. - if (result.isFail()) - { - Log.getLogger().error($"Failed to BattleRoom setInstanceRoomExtraInfo() !!! : {result.toBasicString()} - instanceRoomId:{getRoomId()}"); - return result; - } - + return Task.FromResult(result); + } + + public async Task initFfaBeforeTaskCreate() + { + var result = new Result(); + + //DateTime start_time = DateTimeHelper.Current; + + loadMetaDatas(); + loadOverridedMetaDatas(); + //Anchor 정보 로드 loadAnchorPos(); - + //스냅샷 데이터 로드, 없으면 신규 생성 var battle_instance_action = getEntityAction(); NullReferenceCheckHelper.throwIfNull(battle_instance_action, () => $"battle_instance_action is null !!!"); @@ -169,11 +114,84 @@ public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode } Log.getLogger().info($"load Snapshot done instanceId : {m_instance_room.getInstanceId()}, roomId : {m_instance_room.getMap().m_room_id}"); - + await Task.CompletedTask; return result; } + public void loadMetaDatas() + { + var game_mode_id = getGameModeId(); + // park.chanheon 매칭 메타 데이터 체크 부분 오류 주석 처리 + // if(false == MetaData.Instance.Meta.GameModeMatchMetaTable.GameModeMatchDataListbyGameModeMatchID.TryGetValue(game_mode_id, out var matchMeta)) + // { + // var err_msg = $"Not exist ffa matchMetaData Data gameModeId : {game_mode_id}"; + // Log.getLogger().error(err_msg); + // NullReferenceCheckHelper.throwIfNull(matchMeta, () => $"matchMetaData is null !!!"); + // } + + if (false == MetaData.Instance.Meta.GameModeTpsFfaMetaTable.GameModeTpsFfaMetaDataListbyId.TryGetValue(m_game_mode_all_meta.m_config_id, out var ffaMetaData)) + { + var err_msg = $"Not exist Battle ffaMetaData Data gameModeId : {game_mode_id}"; + Log.getLogger().error(err_msg); + NullReferenceCheckHelper.throwIfNull(ffaMetaData, () => $"metaData is null !!!"); + } + + if(false == MetaData.Instance.m_game_mode_all_metas.TryGetValue(game_mode_id, out var metaData)) + { + var err_msg = $"Not exist ffa metaData Data gameModeId : {game_mode_id}"; + Log.getLogger().error(err_msg); + NullReferenceCheckHelper.throwIfNull(metaData, () => $"metaData is null !!!"); + } + + m_ffa_meta = new TpsFfaMetaData( + ffaMetaData.GameModeTpsFfaID, + ffaMetaData.PlayerRespawnTime, + ffaMetaData.RoundTime, + ffaMetaData.NextRoundWaitTime, + ffaMetaData.ResultUIWaitTime, + ffaMetaData.GetRewardTime, + ffaMetaData.DefaultRoundCount, + metaData.m_common_data.MaxStartWaitTime + ); + + foreach (var reward_meta in m_game_mode_all_meta.m_condition_reward_metas) + { + var condition_meta = reward_meta.Value; + + int charge_level = 0; + int charge_time = 0; + foreach (var cond in condition_meta.m_conditions) + { + if(!cond.ConditionType.Equals(GameModeConditionType.ObtainedByOwningForTime)) continue; + + if (cond.VariableName.Equals(GameModeConditionVariableName.ChargeLevel)) + { + charge_level = (int)cond.VariableValue; + } + if (cond.VariableName.Equals(GameModeConditionVariableName.ChargeTime)) + { + charge_time = (int)cond.VariableValue; + } + + if (charge_level > 0 && charge_time > 0) break; + } + + var reward_group_id = condition_meta.m_reward_group_id; + var reward_data = new TpsFfaChargeRewardData(charge_level, charge_time, reward_group_id); + + m_ffa_meta.m_charge_reward_meta.TryAdd(charge_level, reward_data); + + } + } + + public void loadOverridedMetaDatas() + { + var game_room_info = getGameRoomInfo(); + m_ffa_meta.setHotTime(game_room_info.HotTimeEvent); + + } + private void loadAnchorPos() { foreach (var anchor_info_dict in m_instance_room.getMap().getAnchors()) @@ -191,14 +209,45 @@ public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode var table_id = anchor_info.AnchorProp.TableId; loadRespawnPos(type, anchor_guid); loadBattleObjectGroups(type, anchor_guid, table_id); - + loadStartPos(type, anchor_guid, anchor); } + + shuffleStartPos(); } - + + private void loadStartPos(string type, string anchorGuid, Anchor anchor) + { + if (!type.Equals(BattleConstant.START_POS_ANCHOR_NAME)) return; + + if (!anchor.EditorName.Contains("BP_Anchor_StartPos_")) + { + return; + } + + m_start_pos_anchors_meta.Add((anchorGuid, string.Empty)); + Log.getLogger().info($"m_start_pos_anchors_meta add success type : {type}, anchorGuid : {anchorGuid}"); + } + + private void shuffleStartPos() + { + var rnd = new Random(); + + var array = m_start_pos_anchors_meta.ToArray(); + + for (int i = array.Length - 1; i > 0; i--) + { + int j = rnd.Next(i + 1); + (array[i], array[j]) = (array[j], array[i]); + } + m_start_pos_anchors_meta = new List>(array); + + Log.getLogger().info($"shuffled m_start_pos_anchors_meta : {JsonConvert.SerializeObject(m_start_pos_anchors_meta)}"); + } + private void loadRespawnPos(string type, string anchorGuid) { if (!type.Equals(BattleConstant.RESPAWN_POS_ANCHOR_NAME)) return; - + if (false == m_respawn_pos_anchors_meta.Add(anchorGuid)) { Log.getLogger().warn($"respawnPos add fail type : {type}, anchorGuid : {anchorGuid}"); @@ -223,14 +272,14 @@ public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode return false; } - EBattleObjectType object_type = battle_object_meta.ObjectType; + EGameModeObjectType object_type = battle_object_meta.ObjectType; var group_id = battle_object_spawn_meta.GroupID; - if (object_type.Equals(EBattleObjectType.Pod_Combat) && battle_object_meta.Name.Equals(BattleConstant.BATTLE_POD_STORAGE_NAME)) + if (object_type.Equals(EGameModeObjectType.Pod_Combat) && battle_object_meta.Name.Equals(BattleConstant.BATTLE_POD_STORAGE_NAME)) { loadBattleObjectPodStorageGroup(anchorGuid, group_id); } - else if (object_type.Equals(EBattleObjectType.Pod_Box)) + else if (object_type.Equals(EGameModeObjectType.Pod_Box)) { loadBattleObjectPickupPodGroup(anchorGuid, group_id); } @@ -250,7 +299,7 @@ public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode return; } poses.Add(anchorGuid); - + Log.getLogger().info($"m_battle_pod_stand_group_pos_meta Add anchorGuid : {anchorGuid}, group_id : {groupId}"); } @@ -266,13 +315,13 @@ public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode return; } poses.Add(anchorGuid); - + Log.getLogger().info($"m_battle_pod_box_group_pos_meta Add anchorGuid : {anchorGuid}, group_id : {groupId}"); } - + public void setEventStartTime(DateTime t) { - m_battle_instance_event_start_time = t; + m_game_start_time = t; } private void setDefaultMetaConstants() @@ -280,27 +329,34 @@ public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode m_ticker_interval_msecs = GameModeConstants.GAME_MODE_TPS_FFA_CHECK_INTERVAL_MSECS; //이것도 위로 올릴수 있을것 같은데?? } - public override async Task taskUpdate() + protected override async Task deatilTaskUpdate() { - //여기에 타이머 처리 해야된다. - var battle_update_action = getEntityAction(); - NullReferenceCheckHelper.throwIfNull(battle_update_action, () => $"battle_update_action is null !!!"); - - var result = await battle_update_action.update(); - if (result.isFail()) - { - var err_msg = $"playr mode hander update error : {result.toBasicString()}"; - Log.getLogger().error(err_msg); - } - await Task.CompletedTask; + //여기에 타이머 처리 해야된다. + //var battle_update_action = getEntityAction(); + //NullReferenceCheckHelper.throwIfNull(battle_update_action, () => $"battle_update_action is null !!!"); + + //var result = await battle_update_action.update(); + //if (result.isFail()) + //{ + // var err_msg = $"playr mode hander update error : {result.toBasicString()}"; + // Log.getLogger().error(err_msg); + // return result; + //} + //return result; + var object_update_action = getEntityAction(); + NullReferenceCheckHelper.throwIfNull(object_update_action, () => $"object_update_action is null !!"); + var result = await object_update_action.updateFfaObject(); + if (result.isFail()) return result; + return result; + } - + public override string toBasicString() { var basic_string = base.toBasicString() + $"GameModeTPSFreeForAll...."; return basic_string; } - + public async Task removePodCombat(Player player) { var attribute = getEntityAttribute(); @@ -313,22 +369,19 @@ public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode attribute.m_combat_pod_mode.m_pod_combat.changeDropState(player.getCurrentPositionInfo().Pos); } - var ffa = this as GameModeTPSFreeForAll; - NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!!"); - - BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(ffa); //kihoon todo : 이거 코드 이상하다... 수정해야된다. + BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(this); } } - + public async Task destroyBattleRoom() { await Task.CompletedTask; - + //여기서 timer 종료 처리 var entity_ticker = getEntityTicker(); if(entity_ticker is not null) entity_ticker.getCancelToken().Cancel(); - + Log.getLogger().debug($"battle instance room token canceled m_room_id :{m_instance_room.getMap().m_room_id}"); var fn_save_battle_instance = async delegate() @@ -337,7 +390,7 @@ public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode var fn_result = new Result(); using (var releaser = await getAsyncLock()) { - //여기서 battleRoomSnapshot 저장 + //여기서 battleRoomSnapshot 저장 var battle_instance_snapshot_attribute = getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(battle_instance_snapshot_attribute, () => $"battle_instance_snapshot_attribute is null !!!"); if (!battle_instance_snapshot_attribute.m_combat_pod_mode.m_pod_combat.m_current_occupier_guid.Equals(string.Empty)) @@ -346,18 +399,15 @@ public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode } battle_instance_snapshot_attribute.modifiedEntityAttribute(true); - + var batch = new QueryBatchEx(this, LogActionType.BattleInstanceSnapshotSave, server_logic.getDynamoDbClient(), true); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } - - var ffa = this as GameModeTPSFreeForAll; - NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!!"); - - BattleSnapShotBusinessLog business_log = new(ffa, ""); + + BattleSnapShotBusinessLog business_log = new(this, ""); batch.appendBusinessLog(business_log); - + fn_result = await QueryHelper.sendQueryAndBusinessLog(batch); if (fn_result.isSuccess()) @@ -377,8 +427,23 @@ public class GameModeTPSFreeForAll : GameModeTPS where T : ITPSMode var err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().error(err_msg); } - - - + + + } -} \ No newline at end of file + + protected override async Task setDetailGameInfoAfterLoadComplete() + { + var result = new Result(); + m_game_create_time = DateTimeHelper.Current; + + await Task.CompletedTask; + return result; + } + + protected override void setDetailAnchorInfos(string anchorGuid, Anchor anchor) + { + } + + +} diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllDestroyHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllDestroyHandler.cs index fc6a67f..249f285 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllDestroyHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllDestroyHandler.cs @@ -3,6 +3,7 @@ using GameServer.Contents.GameMode.Helper; using GameServer.Contents.GameMode.Manage.LeaveManage; using GameServer.Contents.GameMode.Manage.PlayManage; using GameServer.Contents.GameMode.Mode_Battle.Manage; +using MetaAssets; using Newtonsoft.Json; using ServerBase; using ServerCommon; @@ -46,7 +47,7 @@ public class TPSFreeForAllDestroyHandler : GameModeDestroyHandlerBase batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } - var ffa = game_mode_base as GameModeTPSFreeForAll; + var ffa = game_mode_base as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!! - {game_mode_base.toBasicString()}"); diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllInitHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllInitHandler.cs index 7b269f9..38157bc 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllInitHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllInitHandler.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Manage; +using MetaAssets; using ServerCommon; using ServerCore; @@ -15,32 +16,6 @@ public class TPSFreeForAllInitHandler : GameModeInitHandlerBase var result = new Result(); return result; } - - // public override async Task gamedModeInstanceInit() - // { - // Log.getLogger().debug("tps ffa gamedModeInstanceInit called"); - // - // //await base.gamedModeInstanceInit(); //kihoon todo 이거 바꿔줘야 한다. .. - // - // - // - // var room_id = m_instance_room.getMap().m_room_id; - // var result = BattleInstanceManager.It.battleInstanceInit(m_instance_room, room_id).Result; //kihoon todo :이거 리팩토링 대상 - // if (result.isFail()) - // { - // Log.getLogger().error(result.toBasicString()); - // } - // - // //kihoon todo : 임시 코드 나중에 gameMode로 이동해야됨 - // //var battle_room = BattleInstanceManager.It.getBattleInstanceRoom(room_id); - // //NullReferenceCheckHelper.throwIfNull(battle_room, () => $"battle_room is null !!"); - // - // //GameModeManager.It.tryAdd(room_id, battle_room); - // - // Log.getLogger().debug("tps ffa gamedModeInstanceInit done"); - // - // return result; - // } } diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllJoinHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllJoinHandler.cs index 42b2047..d50cdbb 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllJoinHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllJoinHandler.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Manage; +using MetaAssets; using ServerCommon; using ServerCore; @@ -27,19 +28,10 @@ public class TPSFreeForAllJoinHandler : GameModeJoinHandlerBase //instanceroom 정보는 남아있는데 battleinstance만 없어지는 케이스가 있어서 예외 처리를 위해 넣어놓음 var room_id = m_instance_room.getMap().m_room_id; - //var battle_instance_room = BattleInstanceManager.It.getBattleInstanceRoom(room_id); //kihoon todo : 이거 리팩토링 대상.... IGameMode에서 가져오는게 맞나?? 다른 모드 넣으면서 고민좀 해보자... + //var battle_instance_room = BattleInstanceManager.It.getBattleInstanceRoom(room_id); //todo : 리팩토링 대상.... IGameMode에서 가져오는게 맞나?? 다른 모드 넣으면서 고민좀 해보자... if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) { Log.getLogger().error($"Battle Room Instance is null.. so init start roomId : {room_id}"); - - //kihoon todo : 이거 코드 바꿔야 된다. - // result = Task.Run(() => BattleInstanceManager.It.battleInstanceInit(m_instance_room, room_id)).GetAwaiter().GetResult(); - // if (result.isFail()) - // { - // err_msg = $"BattleIntanceJoin init error, _roomId : {room_id}"; - // Log.getLogger().error(err_msg); - // return result; - // } } Log.getLogger().debug("tps ffa gamedModeInstanceJoin done"); diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllJoinSuccessHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllJoinSuccessHandler.cs index 9e360e5..478d002 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllJoinSuccessHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllJoinSuccessHandler.cs @@ -1,5 +1,8 @@ -using GameServer.Contents.GameMode.Manage; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage; using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using Newtonsoft.Json; using ServerBase; using ServerCommon; using ServerCore; @@ -8,26 +11,18 @@ namespace GameServer.Contents.GameMode.Mode_Battle.Manage; public class TPSFreeForAllJoinSuccessHandler : GameModeJoinSuccessHandlerBase { - //private readonly BattleInstanceRoom m_battle_instance_room;//kihoon todo : 이거 GameMode 로 변경해야 한다... .. private IGameMode m_game_mode; public TPSFreeForAllJoinSuccessHandler(Player player, InstanceRoom instanceRoom) : base(player, GameModeType.TPS_FFA, instanceRoom) { var room_id = instanceRoom.getMap().m_room_id; - //m_battle_instance_room = BattleInstanceManager.It.getBattleInstanceRoom(room_id)!; - //NullReferenceCheckHelper.throwIfNull(m_battle_instance_room, () => $"m_battle_instance_room is null !!!"); - GameModeManager.It.tryGetGameMode(room_id, out var gameMode); NullReferenceCheckHelper.throwIfNull(gameMode, () => $"gameMode is null !!!"); m_game_mode = gameMode; - - - //m_battle_instance_room = battle_instance_room; } public override Result joinSuccessValidate() { var result = new Result(); - return result; @@ -38,59 +33,131 @@ public class TPSFreeForAllJoinSuccessHandler : GameModeJoinSuccessHandlerBase var result = new Result(); string err_msg = string.Empty; - var ffa = m_game_mode as GameModeTPSFreeForAll; + var ffa = m_game_mode as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!!"); - - var room_id = ffa.getRoomId(); var battle_instance_attribute = ffa.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(battle_instance_attribute, () => $"battle_instance_attribute is null !!!"); - - (result, var pos_meta_guid) = BattleRoomHelper.getRandomRespawnPos(battle_instance_attribute, ffa); - if (result.isFail()) + + var state_type = ffa.getState().getStateType(); + if (state_type == GameModeState.Rounding || state_type == GameModeState.RoundWait || state_type == GameModeState.RoundEndAll) { - return result; + await setFirstRespawnPos(ffa, battle_instance_attribute); + if(result.isFail()) return result; } - - if (false == ffa.m_respawn_pos_anchors_meta.Contains(pos_meta_guid)) + else { - err_msg = $"respawn pos meta not exist idx : {pos_meta_guid}"; - result.setFail(ServerErrorCode.BattleInstanceUsableSpawnPointNotExist, err_msg); - return result; - } - - using (var releaser = await ffa.getAsyncLock()) - { - var now = DateTimeHelper.Current; - var next_respawn_time = now.AddSeconds(ffa.m_ffa_config_meta.PlayerRespawnTime); - - battle_instance_attribute.m_combat_pod_mode.m_respawns.AddOrUpdate(pos_meta_guid, next_respawn_time, (key, old) => next_respawn_time); - - var location_action = m_player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {m_player.toBasicString()}"); - - - var currenct_pos = location_action.getCurrentPos(); - if (false == ffa.getInstanceRoom().getMap().getAnchors().TryGetValue(pos_meta_guid, out var anchorInfo)) - { - err_msg = $"anchorInfo not exist idx : {pos_meta_guid}"; - result.setFail(ServerErrorCode.BattleInstanceNotExistAnchors, err_msg); - return result; - } - - currenct_pos = anchorInfo.AnchorPos.Clone(); - currenct_pos.Z += 100; - location_action.tryUpdateCurrentPos(currenct_pos); + //kihoon todo : start_pos를 분리 해야된다. + //kihoon todo : 처음 start pos는 broad cast 하는게 없는데? 이건 뭘 써야 되지? + result = await setFirstStartPos(ffa); + if(result.isFail()) return result; } return result; } + private Task setFirstRespawnPos(GameModeTPSFreeForAll ffa, BattleInstanceSnapshotAttribute attribute) + { + var result = new Result(); + (result, var pos_meta_guid) = BattleRoomHelper.getRandomRespawnPos(attribute, ffa); + if (result.isFail()) return Task.FromResult(result); + + if (false == ffa.m_respawn_pos_anchors_meta.Contains(pos_meta_guid)) + { + var err_msg = $"respawn pos meta not exist idx : {pos_meta_guid}"; + result.setFail(ServerErrorCode.BattleInstanceUsableSpawnPointNotExist, err_msg); + return Task.FromResult(result); + } + + var now = DateTimeHelper.Current; + var next_respawn_time = now.AddSeconds(ffa.m_ffa_meta.m_player_respawn_time); + + attribute.m_combat_pod_mode.m_respawns.AddOrUpdate(pos_meta_guid, next_respawn_time, (key, old) => next_respawn_time); + + var location_action = m_player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {m_player.toBasicString()}"); + + + var currenct_pos = location_action.getCurrentPos(); + if (false == ffa.getInstanceRoom().getMap().getAnchors().TryGetValue(pos_meta_guid, out var anchorInfo)) + { + var err_msg = $"anchorInfo not exist idx : {pos_meta_guid}"; + result.setFail(ServerErrorCode.BattleInstanceNotExistAnchors, err_msg); + return Task.FromResult(result); + } + currenct_pos = anchorInfo.AnchorPos.Clone(); + currenct_pos.Z += 100; + location_action.tryUpdateCurrentPos(currenct_pos); + BattleRoomNotifyHelper.broadcast_GS2C_NTF_PLAYER_RESPAWN(ffa, currenct_pos, m_player); + return Task.FromResult(result); + } + + + private Task setFirstStartPos(GameModeTPSFreeForAll ffa) + { + var result = new Result(); + + bool is_set = false; + string start_anchor_guid = string.Empty; + + for(int k = 0; k < 2 ; k ++) + { + for(int j=0; j< ffa.m_start_pos_anchors_meta.Count ; j++) + { + start_anchor_guid = ffa.m_start_pos_anchors_meta[j].Item1; + var user_guid = ffa.m_start_pos_anchors_meta[j].Item2; + if (!user_guid.Equals(string.Empty)) continue; + + ffa.m_start_pos_anchors_meta[j] = (start_anchor_guid, m_player.getUserGuid()); + is_set = true; + break; + } + + if (is_set == false) + { + //예외 처리를 위한 임시 코드... start_pos가 유저 수보다 적을 경우 + for (int j = 0; j < ffa.m_start_pos_anchors_meta.Count; j++) + { + start_anchor_guid = ffa.m_start_pos_anchors_meta[j].Item1; + ffa.m_start_pos_anchors_meta[j] = (start_anchor_guid, string.Empty); + } + } + else break; + } + + if (is_set == false) + { + //이러면 진짜 에러 + var err_msg = $"m_start_pos_anchors_meta can not allocate !!!"; + result.setFail(ServerErrorCode.BattleInstanceNotExistAnchors, err_msg); + return Task.FromResult(result); + } + + var location_action = m_player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {m_player.toBasicString()}"); + + + var currenct_pos = location_action.getCurrentPos(); + if (false == ffa.getInstanceRoom().getMap().getAnchors().TryGetValue(start_anchor_guid, out var anchorInfo)) + { + var err_msg = $"start_anchor_guid not exist idx : {start_anchor_guid}"; + result.setFail(ServerErrorCode.BattleInstanceNotExistAnchors, err_msg); + return Task.FromResult(result); + } + currenct_pos = anchorInfo.AnchorPos.Clone(); + currenct_pos.Z += 100; + location_action.tryUpdateCurrentPos(currenct_pos); + + LocationNotifyHelper.send_S2C_NTF_SET_LOCATION(m_player); + return Task.FromResult(result); + } + + public override async Task joinSuccessNotify() { var result = new Result(); m_player.send_S2C_NTF_SET_LOCATION(); - var ffa = m_game_mode as GameModeTPSFreeForAll; + var ffa = m_game_mode as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!!"); result = await BattleInstanceManager.It.sendNtfAboutBattleInstance(ffa, m_player); if (result.isFail()) return result; @@ -100,15 +167,13 @@ public class TPSFreeForAllJoinSuccessHandler : GameModeJoinSuccessHandlerBase public override void joinSuccessWriteLog() { - var ffa = m_game_mode as GameModeTPSFreeForAll; + var ffa = m_game_mode as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!!"); var room_id = ffa.getRoomId(); var user_guid = m_player.getUserGuid(); var user_nickname = m_player.getUserNickname(); - - var battle_instance_attribute = ffa.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(battle_instance_attribute, () => $"battle_instance_attribute is null !!!"); diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllLeaveHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllLeaveHandler.cs index 734068b..6e4fb12 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllLeaveHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllLeaveHandler.cs @@ -2,6 +2,8 @@ using GameServer.Contents.GameMode.Manage.LeaveManage; using GameServer.Contents.GameMode.Manage.PlayManage; using GameServer.Contents.GameMode.Mode_Battle.Manage; +using MetaAssets; +using ServerBase; using ServerCommon; using ServerCore; @@ -9,7 +11,8 @@ namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.Manage; public class TPSFreeForAllLeaveHandler : GameModeLeaveHandlerBase { - public TPSFreeForAllLeaveHandler(Player player, string roomId) : base(player, roomId, GameModeType.TPS_FFA) + public TPSFreeForAllLeaveHandler(IGameMode gameMode, Player player, ServerInfo serverInfo, List invokers, string roomId) + : base(gameMode, player, serverInfo, invokers, roomId, GameModeType.TPS_FFA) { } @@ -18,11 +21,12 @@ public class TPSFreeForAllLeaveHandler : GameModeLeaveHandlerBase public override async Task postLeave(IGameMode gameMode) { Log.getLogger().debug($"ffa postLeave start instanceRoomId : {m_room_id}"); - var ffa = gameMode as GameModeTPSFreeForAll; + var ffa = gameMode as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(ffa, () => $"location ffa is null !!! - {m_player.toBasicString()}"); await ffa.removePodCombat(m_player); - + ffa.removePlayUserState(m_player.getUserGuid()); + return new Result(); } diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllTransitionStrategy.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllTransitionStrategy.cs new file mode 100644 index 0000000..cdbb7a6 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TPSFreeForAllTransitionStrategy.cs @@ -0,0 +1,171 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.State; + +using GameServer.Contents.GameMode.State; +using Newtonsoft.Json; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.Manage; + +public class TPSFreeForAllTransitionStrategy : GameModeTransitionStrategyBase +{ + + public override IGameModeState? getNextStateByNone(IGameModeState currentState, IGameMode gameMode) + { + return new TpsFfaStateCreated(gameMode); + } + + public override IGameModeState? getNextStateByCreate(IGameModeState currentState, IGameMode gameMode) + { + return new GameModeStateLoadingWait(gameMode); + } + + public override IGameModeState? getNextStateByLoadingWait(IGameModeState currentState, IGameMode gameMode) + { + var next_state_change_time = currentState.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + if (next_state_change_time < now) + { + Log.getLogger().info($"loading wait time exceeded, so change state to Wait, userState : {JsonConvert.SerializeObject(game_mode_base.getUserState())}"); + return new TpsFfaStateWait(gameMode, StateUpdateReasonType.LoadingWaitTimeExceeded); + } + if(game_mode_base.isUserLoadingCompleteAll()) + { + Log.getLogger().info($"All User loading complete, so change state to Wait, userState : {JsonConvert.SerializeObject(game_mode_base.getUserState())}"); + return new TpsFfaStateWait(gameMode, StateUpdateReasonType.UserLoadComplete); + } + + return null; + } + + public override IGameModeState? getNextStateByWait(IGameModeState currentState, IGameMode gameMode) + { + var wait_state = currentState as TpsFfaStateWait; + NullReferenceCheckHelper.throwIfNull(wait_state, () => $"ffa wait_state is null !!!"); + + var next_state_change_time = wait_state.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + + if (next_state_change_time <= now) + { + Log.getLogger().info($"Wait Time Done, Change to ready state roomId : {game_mode_base.getRoomId()}"); + return new TpsFfaStateReady(gameMode); + } + return null; + } + + + public override IGameModeState? getNextStateByReady(IGameModeState currentState, IGameMode gameMode) + { + var ready_state = currentState as TpsFfaStateReady; + NullReferenceCheckHelper.throwIfNull(ready_state, () => $"Ready state is null !!!"); + + var next_state_change_time = ready_state.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + Log.getLogger().info($"Ready Time Done, Change to ready state roomId : {game_mode_base.getRoomId()}"); + return new TpsFfaStateRounding(gameMode); + } + + + + public override IGameModeState? getNextStateByEnd(IGameModeState currentState, IGameMode gameMode) + { + return null; + } + + public override IGameModeState? getNextStateByRounding(IGameModeState currentState, IGameMode gameMode) + { + var play_state = currentState as TpsFfaStateRounding; + NullReferenceCheckHelper.throwIfNull(play_state, () => $"Play state is null !!!"); + + var next_state_change_time = play_state.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + + + var tps_ffa = gameMode as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + + var round_count = tps_ffa.m_ffa_meta.m_round_count; + + var attribute = tps_ffa.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); + + if (attribute.m_combat_pod_mode.m_current_round >= round_count || tps_ffa.getGameRoomInfo().EventEndTime < now) + { + return new TpsFfaStateRoundEndAll(gameMode); + } + + return new TpsFfaStateRoundWait(gameMode); + + } + + + + public override IGameModeState? getNextStateByRoundWait(IGameModeState currentState, IGameMode gameMode) + { + var play_state = currentState as TpsFfaStateRoundWait; + NullReferenceCheckHelper.throwIfNull(play_state, () => $"Play state is null !!!"); + + var next_state_change_time = play_state.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + + var tps_ffa = gameMode as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + + if (tps_ffa.getGameRoomInfo().EventEndTime < now) + { + Log.getLogger().info($"event end time : {tps_ffa.getGameRoomInfo().EventEndTime}, so change state to RoundEndAll"); + return new TpsFfaStateRoundEndAll(gameMode); + } + + return new TpsFfaStateRounding(gameMode); + } + + public override IGameModeState? getNextStateByRoundEndAll(IGameModeState currentState, IGameMode gameMode) + { + var play_state = currentState as TpsFfaStateRoundEndAll; + NullReferenceCheckHelper.throwIfNull(play_state, () => $"Play state is null !!!"); + + var next_state_change_time = play_state.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + return null; + } + + public override IGameModeState? getNextStateEventEnd(IGameModeState currentState, IGameMode gameMode) + { + return null; + } + + + #region These states are not used in TpsFfa Mode + + public override IGameModeState? getNextStateByPlay(IGameModeState currentState, IGameMode gameMode) + { + return null;//not use this mode + } + public override IGameModeState? getNextStateByResult(IGameModeState currentState, IGameMode gameMode) + { + return null; //not use this mode + } + + public override IGameModeState? getNextStateByRewardSummary(IGameModeState currentState, IGameMode gameMode) + { + return null; //not use this mode + } + #endregion +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFfaMatchingReservationHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFfaMatchingReservationHandler.cs new file mode 100644 index 0000000..3c98a7f --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFfaMatchingReservationHandler.cs @@ -0,0 +1,15 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.State; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.State; +using GameServer.Matching; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.Manage; + +public class TpsFfaMatchingReservationHandler : MatchingReservationHandlerBase +{ + public override Task detailMatchingReservation(IGameMode gameMode, string userGuid, string roomGuid, string teamId) + { + return Task.FromResult(new Result()); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFreeForAllDisconnectHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFreeForAllDisconnectHandler.cs new file mode 100644 index 0000000..35b595f --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFreeForAllDisconnectHandler.cs @@ -0,0 +1,38 @@ +using GameServer.Contents.GameMode.Manage.LeaveManage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.Manage; + +public class TpsFreeForAllDisconnectHandler : GameModeDisconnectHandlerBase +{ + public TpsFreeForAllDisconnectHandler(IGameMode gameMode, Player player) : base(GameModeType.TPS_FFA, gameMode, player) + { + } + + public override async Task disconnect() + { + var result = new Result(); + + await Task.CompletedTask; + + + var ffa = m_game_mode as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(ffa, () => $"location ffa is null !!! - {m_player.toBasicString()}"); + await ffa.removePodCombat(m_player); + + return result; + } + + public override Task notifyAfterDisconnect() + { + return Task.FromResult(new Result()); + } + + public override Task logAfterDisconnect() + { + var result = new Result(); + return Task.FromResult(result); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFreeForAllPreparationForLeavingHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFreeForAllPreparationForLeavingHandler.cs new file mode 100644 index 0000000..2b6b486 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Manage/TpsFreeForAllPreparationForLeavingHandler.cs @@ -0,0 +1,30 @@ +using GameServer.Contents.GameMode.Manage.LeaveManage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; + +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.Manage; + +public class TpsFreeForAllPreparationForLeavingHandler : PreparationForLeavingGameBase +{ + + public TpsFreeForAllPreparationForLeavingHandler(Player player, IGameMode gameMode) : base(player, gameMode) + { + + } + public override async Task preparation() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + + public override async Task postPreparationForLeaving() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Meta/TpsFfaMetaData.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Meta/TpsFfaMetaData.cs new file mode 100644 index 0000000..e999cf2 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Meta/TpsFfaMetaData.cs @@ -0,0 +1,46 @@ +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.Meta; + +public class TpsFfaMetaData +{ + public readonly Int32 m_id; + public readonly Int32 m_player_respawn_time; + public readonly Int32 m_round_time; + public readonly Int32 m_next_round_wait_time; + public readonly Int32 m_result_ui_wait_time; + public readonly Int32 m_get_reward_time; + public readonly Int32 m_max_start_wait_time; + + public Int32 m_round_count { get; set; } = 1; + public Int32 m_hot_time { get; set; }= 1; + public Dictionary m_charge_reward_meta = new(); + + + public TpsFfaMetaData() + { + } + + public TpsFfaMetaData(Int32 id, Int32 respawnTime, Int32 roundTime, Int32 nextRoundWaitTime, + Int32 resultUiWaitTime, Int32 getRewardTime, Int32 defaultRoundCount, Int32 maxStartWaitTime) + { + m_id = id; + m_player_respawn_time = respawnTime; + m_round_count = defaultRoundCount; + m_round_time = roundTime; + m_next_round_wait_time = nextRoundWaitTime; + m_result_ui_wait_time = resultUiWaitTime; + m_get_reward_time = getRewardTime; + m_max_start_wait_time = maxStartWaitTime; + } + + public void setRoundCount(int roundCount) + { + m_round_count = roundCount; + } + + public void setHotTime(int hotTime) + { + m_hot_time = hotTime; + } + + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattleObjectInteractionPacketHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattleObjectInteractionPacketHandler.cs index 2a1fe44..b0f4268 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattleObjectInteractionPacketHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattleObjectInteractionPacketHandler.cs @@ -1,5 +1,4 @@ using GameServer.Contents.GameMode.Manage.PlayManage; -using GameServer.Contents.GameMode.Mode_Battle.Manage; using Google.Protobuf; using MetaAssets; using ServerBase; @@ -23,18 +22,6 @@ public class BattleObjectInteractionPacketHandler : PacketRecvHandler var player = session as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - - //var server_config = server_logic.getServerConfig(); - // bool is_battle_system_active = server_config.BattleSystemEnable; - // - // if (false == is_battle_system_active) - // { - // err_msg = $"is_battle_system_active is false!!!"; - // result.setFail(ServerErrorCode.BattleInstanceInActive, err_msg); - // Log.getLogger().error(err_msg); - // send_S2C_ACK_BATTLE_OBJECT_INTERACTION(player, result, ""); - // return result; - // } var req_msg = recvMessage as ClientToGame; ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); @@ -62,17 +49,7 @@ public class BattleObjectInteractionPacketHandler : PacketRecvHandler send_S2C_ACK_BATTLE_OBJECT_INTERACTION(player, result, anchor_guid); return result; } - - - // var battle_instance_room = BattleInstanceManager.It.getBattleInstanceRoom(room_id); - // if (battle_instance_room == null) - // { - // err_msg = $"battle_instance_room is null : room_id - {room_id}"; - // Log.getLogger().error(err_msg); - // result.setFail(ServerErrorCode.BattleInstanceInfoNotExist, err_msg); - // send_S2C_ACK_BATTLE_OBJECT_INTERACTION(player, result, anchor_guid); - // return result; - // } + var game_mode_base = gameMode as GameModeBase; NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); @@ -111,7 +88,6 @@ public class BattleObjectInteractionPacketHandler : PacketRecvHandler var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); NullReferenceCheckHelper.throwIfNull(found_transaction_runner, () => $"found_transaction_runner is null !!! - {player.toBasicString()}"); common_result = found_transaction_runner.getCommonResult(); - } return result; }; @@ -127,12 +103,14 @@ public class BattleObjectInteractionPacketHandler : PacketRecvHandler //순서 바꾸지 말것(클라 요청사항) send_S2C_ACK_BATTLE_OBJECT_INTERACTION(player, result, anchor_guid); + + - var ffa = game_mode_base as GameModeTPSFreeForAll; + var ffa = game_mode_base as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!! - {player.toBasicString()}"); BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_INTERACTION(ffa, player.getUserGuid(), anchor_guid); - BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(ffa, handler); + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_OBJECT_STATE_INFO(ffa, anchor_guid, handler); sendOtherNotifies(handler, ffa, common_result); @@ -140,7 +118,7 @@ public class BattleObjectInteractionPacketHandler : PacketRecvHandler return result; } - private void sendOtherNotifies(BattleObjectInteractionLogicHandler handler, GameModeTPSFreeForAll battleInstanceRoom, CommonResult commonResult) + private void sendOtherNotifies(BattleObjectInteractionLogicHandler handler, GameModeTPSFreeForAll battleInstanceRoom, CommonResult commonResult) { //pod combat이 활성화 되서 stand는 전부 비활성화 if (handler.m_need_noti_objects.Count > 0) @@ -161,11 +139,7 @@ public class BattleObjectInteractionPacketHandler : PacketRecvHandler { BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(battleInstanceRoom); } - - if (handler.m_is_need_combat_pod_noti) - { - BattleRoomNotifyHelper.broadcast_GS2C_NTF_POD_COMBAT_STATE(battleInstanceRoom); - } + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_REWARD(battleInstanceRoom, handler.m_user_guid, commonResult); } @@ -178,7 +152,7 @@ public class BattleObjectInteractionPacketHandler : PacketRecvHandler string err_msg = string.Empty; - handler.m_is_combat_pod = BattleInstancePlayModeHelper.isPodCombat(attribute, anchorGuid); + handler.m_is_combat_pod = TpsFfaHelper.isPodCombat(attribute, anchorGuid); if (handler.m_is_combat_pod) { Log.getLogger().debug($"m is pod combat is true anchor_guid : {anchorGuid}, player : {player.toBasicString()}"); @@ -234,14 +208,14 @@ public class BattleObjectInteractionPacketHandler : PacketRecvHandler handler.m_interaction_log_info.m_battle_object_type = handler.m_battle_object_meta.ObjectType; switch (handler.m_battle_object_meta.ObjectType) { - case EBattleObjectType.Buff: - case EBattleObjectType.Weapon: + case EGameModeObjectType.Buff: + case EGameModeObjectType.Weapon: result = await interact_action.interactCommonBattleObject(handler, player); break; - case EBattleObjectType.Pod_Box: + case EGameModeObjectType.Pod_Box: result = await interact_action.interactPickupPod(handler, player); break; - case EBattleObjectType.Pod_Combat: + case EGameModeObjectType.Pod_Combat: result = await interact_action.interactPodStorage(handler, player); break; } diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattlePlayerDeadPacketHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattlePlayerDeadPacketHandler.cs index 8807f59..afa52d9 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattlePlayerDeadPacketHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattlePlayerDeadPacketHandler.cs @@ -64,7 +64,7 @@ public class BattlePlayerDeadPacketHandler : PacketRecvHandler Log.getLogger().info($"Player Deat packet received room_id : {room_id}, anchor_guid : {killer_guid}, packetCreateTime : {packet_create_time}, player : {player.toBasicString()}"); } - var ffa = gameMode as GameModeTPSFreeForAll; + var ffa = gameMode as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!!"); result = await playerDeadProcess(player, killer_guid, ffa); if (result.isFail()) return result; @@ -74,7 +74,7 @@ public class BattlePlayerDeadPacketHandler : PacketRecvHandler return result; } - public async Task killUserQuestCheck(string killerGuid, GameModeTPSFreeForAll battleInstanceRoom) + public async Task killUserQuestCheck(string killerGuid, GameModeTPSFreeForAll battleInstanceRoom) { battleInstanceRoom.getInstanceRoom().tryGetInstanceMember(killerGuid, out var killer); @@ -96,7 +96,7 @@ public class BattlePlayerDeadPacketHandler : PacketRecvHandler await QuestManager.It.QuestCheck(killer, new QuestTpsPlayerKill(EQuestEventTargetType.TPS, EQuestEventNameType.PLAYERKILL, kill_count)); } - public async Task playerDeadProcess(Player player, string kilerUserGuid, GameModeTPSFreeForAll battleInstanceRoom) + public async Task playerDeadProcess(Player player, string kilerUserGuid, GameModeTPSFreeForAll battleInstanceRoom) { var dead_user_guid = player.getUserGuid(); var dead_user_nickname = player.getUserNickname(); diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattlePlayerRespawnPacketHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattlePlayerRespawnPacketHandler.cs index 6e34612..77a7ff6 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattlePlayerRespawnPacketHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/BattlePlayerRespawnPacketHandler.cs @@ -35,7 +35,6 @@ public class BattlePlayerRespawnPacketHandler : PacketRecvHandler } var room_id = player.getCurrentInstanceRoomId(); - //var battle_instance_room = BattleInstanceManager.It.getBattleInstanceRoom(room_id); if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) { @@ -71,13 +70,11 @@ public class BattlePlayerRespawnPacketHandler : PacketRecvHandler var attribute = game_mode_base.getEntityAttribute(); var next_respawn_time = DateTimeHelper.Current; NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); - var ffa = gameMode as GameModeTPSFreeForAll; + var ffa = gameMode as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!!"); using (var releaser = await game_mode_base.getAsyncLock()) { - - (result, var pos_meta_guid) = BattleRoomHelper.getRandomRespawnPos(attribute, ffa); if (result.isFail()) { @@ -94,12 +91,12 @@ public class BattlePlayerRespawnPacketHandler : PacketRecvHandler } var now = DateTimeHelper.Current; - next_respawn_time = now.AddSeconds(ffa.m_ffa_config_meta.PlayerRespawnTime); + next_respawn_time = now.AddSeconds(ffa.m_ffa_meta.m_player_respawn_time); attribute.m_combat_pod_mode.m_respawns.AddOrUpdate(pos_meta_guid, next_respawn_time, (key, old) => next_respawn_time); attribute.modifiedEntityAttribute(true); - if (false == game_mode_base.getInstanceRoom().getMap().m_anchors.TryGetValue(pos_meta_guid, out var anchorInfo)) + if (false == game_mode_base.getInstanceRoom().getMap().getAnchors().TryGetValue(pos_meta_guid, out var anchorInfo)) { err_msg = $"anchorInfo not exist pos_meta_guid : {pos_meta_guid}"; result.setFail(ServerErrorCode.BattleInstanceNotExistAnchors, err_msg); @@ -108,22 +105,21 @@ public class BattlePlayerRespawnPacketHandler : PacketRecvHandler } anchor_info = anchorInfo; - } var cloned_anchor_pos = anchor_info.AnchorPos.Clone(); cloned_anchor_pos.Z += 100; send_S2C_ACK_BATTLE_PLAYER_RESPAWN(player, cloned_anchor_pos, result); - BattleRoomNotifyHelper.send_GS2C_NTF_PLAYER_RESPAWN(ffa, cloned_anchor_pos, player); + BattleRoomNotifyHelper.broadcast_GS2C_NTF_PLAYER_RESPAWN(ffa, cloned_anchor_pos, player); //로그 남긴다. var invokers = new List(); var log_action = new LogAction(LogActionType.BattleUserRespawn.ToString()); var round = attribute.m_combat_pod_mode.m_current_round; - var round_state = attribute.m_combat_pod_mode.m_round_state_type; + var game_state = attribute.m_combat_pod_mode.m_game_state; var users = ffa.getInstanceRoom().tryGetInstanceExistUserForLog(); - BattleRespawnBusinessLog business_log = new(room_id, round, round_state, player.getUserGuid(), player.getUserNickname(), anchor_info.AnchorGuid, next_respawn_time); + BattleRespawnBusinessLog business_log = new(room_id, round, game_state, player.getUserGuid(), player.getUserNickname(), anchor_info.AnchorGuid, next_respawn_time); invokers.Add(business_log); BusinessLogger.collectLogs(log_action, player, invokers); diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/JoinBattleInstancePacketHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/JoinBattleInstancePacketHandler.cs index daba38e..dab7266 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/JoinBattleInstancePacketHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/JoinBattleInstancePacketHandler.cs @@ -9,233 +9,10 @@ namespace GameServer.PacketHandler; [PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_JOIN_BATTLE_INSTANCE), typeof(JoinBattleInstancePacketHandler), typeof(GameLoginListener))] public class JoinBattleInstancePacketHandler : PacketRecvHandler { - public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) - { - var err_msg = string.Empty; - - var player = entityWithSession as Player; - NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - - var req_msg = recvMessage as ClientToGame; - ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); - - var request = req_msg.Request.ReqJoinBattleInstance; - ArgumentNullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {player.toBasicString()}"); - - var result = parameterValidationCheck(player, request); - if (result.isFail()) - { - send_S2C_ACK_JOIN_BATTLE_INSTANCE(player, result, null); - return result; - } - - Int32 event_id = request.EventId; - var packet_create_time = request.PacketCreateTime; - if (packet_create_time is not null) - { - Log.getLogger().info($"Join Battle Instance packet received event_id : {event_id}, packetCreateTime : {packet_create_time}, player : {player.toBasicString()}"); - } - - //Battle Instance는 결과적으로 eventId의해 처리될 확률이 높다. 이거 코드 수정 필요 - if (event_id == 0) - { - var server_config = ServerConfigHelper.getServerConfig(); - NullReferenceCheckHelper.throwIfNull(server_config, () => $"server_config is null !!!"); - - // ServiceType.Dev 타입일 경우 무조건 치트 사용이 가능 하다. !!! - if (server_config.ServiceType.Equals(ServiceType.Dev.ToString())) - { - return await processPacketByLandId(player, request); - } - else - { - err_msg = $"event_id must have over zero !!! : event_id:{event_id} - {player.toBasicString()}"; - result.setFail(ServerErrorCode.InstanceMetaDataNotFound, err_msg); - Log.getLogger().error(result.toBasicString()); - - send_S2C_ACK_JOIN_BATTLE_INSTANCE(player, result, null); - return result; - } - } - else - { - return await processPacketByEventId(player, event_id); - } - } - - private async Task processJoinBattleInstance(Player player, Int32 eventId, Int32 instanceId, Timestamp eventStartTS) + public override Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) { var result = new Result(); - string err_msg = string.Empty; - - Log.getLogger().info($"player : {player.getUserGuid()} join battle instance eventId : {eventId}, intanceId : {instanceId}, evenstStartTS : {eventStartTS}"); - - if (!MetaData.Instance._IndunTable.TryGetValue(instanceId, out var indun_meta_data)) - { - err_msg = $"Failed to MetaData.TryGetValue() !!! : instanceMetaId:{instanceId} - {player.toBasicString()}"; - result.setFail(ServerErrorCode.InstanceMetaDataNotFound, err_msg); - Log.getLogger().error(result.toBasicString()); - - send_S2C_ACK_JOIN_BATTLE_INSTANCE(player, result, null); - return result; - } - - //3. indun content type이 battleRoom이 아니면 리턴 - var server_connect_info = new ServerConnectInfo(); - var business_logs = new List(); - if (indun_meta_data.ContentsType != ContentsType.BattleRoom) - { - err_msg = $"Failed to MetaData.TryGetValue() !!! : instanceMetaId:{instanceId} - {player.toBasicString()}"; - result.setFail(ServerErrorCode.BattleRoomContentsTypeOnly, err_msg); - send_S2C_ACK_JOIN_BATTLE_INSTANCE(player, result, null); - return result; - } - - - var game_zone_move_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_move_action, () => $"game_zone_move_action is null !!! - {player.toBasicString()}"); - - var server_logic = GameServerApp.getServerLogic(); - - var fn_transaction_runner = async delegate () - { - var result = new Result(); - - (result, server_connect_info, business_logs) = await game_zone_move_action.tryJoinBattleInstance(player.getUserGuid(), eventId, eventStartTS, indun_meta_data); - if (result.isFail()) - { - err_msg = $"Failed to tryJoinInstance() !!! : {result.toBasicString()}"; - Log.getLogger().error(err_msg); - - send_S2C_ACK_JOIN_BATTLE_INSTANCE(player, result, null); - return result; - } - - var batch = new QueryBatch(player, LogActionType.JoinInstance.ToString(), server_logic.getDynamoDbClient(), true); - { - batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); - batch.addQuery(new QueryFinal()); - } - - //if(address_business_log != null) batch.appendBusinessLog(address_business_log); - if(business_logs.Count > 0) batch.appendBusinessLogs(business_logs); - - result = await QueryHelper.sendQueryAndBusinessLog(batch); - - - - if (result.isFail()) - { - err_msg = $"Failed to sendQueryAndBusinessLog() !!! : {result.toBasicString()} - {player.toBasicString()}"; - Log.getLogger().error(err_msg); - - send_S2C_ACK_JOIN_BATTLE_INSTANCE(player, result, null); - return result; - } - - //이거 쓸지 안쓸지 모르겠군...일단 주석 처리 - //await QuestManager.It.QuestCheckWithoutTransaction(player, new QuestInstance(EQuestEventTargetType.INSTANCE, EQuestEventNameType.ENTERED, request.LandId, request.Floor)); - //QuestNotifyHelper.send_GS2C_NTF_QUEST_REWARD(player, common_result); - //var common_result = found_transaction_runner.getCommonResult(); - //var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); - //NullReferenceCheckHelper.throwIfNull(found_transaction_runner, () => $"found_transaction_runner is null !!! - {player.toBasicString()}"); - - send_S2C_ACK_JOIN_BATTLE_INSTANCE(player, result, server_connect_info); - - return result; - }; - - result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "JoinBattleInstance", fn_transaction_runner); - if (result.isFail()) - { - err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; - Log.getLogger().error(err_msg); - } - - return result; - } - - private async Task processPacketByEventId(Player player, Int32 eventId) - { - var result = new Result(); - string err_msg = string.Empty; - - if (false == BattleInstanceManager.It.getSystemBattleEvent(eventId, out var systemBattleEvent)) - { - err_msg = $"not exist system battle evetn : eventId:{eventId} - {player.toBasicString()}"; - result.setFail(ServerErrorCode.BattleInstanceNotExistEventInfo, err_msg); - Log.getLogger().error(result.toBasicString()); - - send_S2C_ACK_JOIN_BATTLE_INSTANCE(player, result, null); - return result; - } - - //이벤트 인스턴스 시작시간, 종료 시간 계산해서 들어가게 - var now = DateTimeHelper.Current; - var joinable_time = BattleRoomHelper.calculateRoomJoinableTime(systemBattleEvent.m_start_time, systemBattleEvent.m_ffa_config_data_id, systemBattleEvent.m_round_count); - if (joinable_time < now) - { - err_msg = $"already joinable_time passed : eventId:{eventId} joinable_time : {joinable_time} - {player.toBasicString()}"; - result.setFail(ServerErrorCode.BattleInstanceClosingTime, err_msg); - Log.getLogger().error(result.toBasicString()); - - send_S2C_ACK_JOIN_BATTLE_INSTANCE(player, result, null); - return result; - } - - var instance_meta_id = systemBattleEvent.m_instance_id; - return await processJoinBattleInstance(player, eventId, instance_meta_id, Timestamp.FromDateTime(systemBattleEvent.m_start_time)); - - - - } - - private async Task processPacketByLandId(Player player, ClientToGameReq.Types.C2GS_REQ_JOIN_BATTLE_INSTANCE request) - { - Int32 land_id = request.LandId; - Int32 floor_id = request.Floor; - Int32 building_id = request.BuildingId; - - var result = new Result(); - string err_msg = string.Empty; - - var game_zone_move_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_move_action, () => $"game_zone_move_action is null !!! - {player.toBasicString()}"); - - //RoomMapTree 정로를 이용해서 Instance 정보 호출 - (result, var room_map_tree, var address_business_log) = MapHelper.tryGetRoomMapTree(land_id, floor_id, building_id); - if (result.isFail() || null == room_map_tree) - { - err_msg = $"Failed to tryGetRoomMapTree() !!! : {result.toBasicString()} - {player.toBasicString()}"; - Log.getLogger().error(err_msg); - - send_S2C_ACK_JOIN_BATTLE_INSTANCE(player, result, null); - return result; - } - - //instance meta id를 이용해 IndunMetadata 정보 호출 - var instance_meta_id = room_map_tree.InstanceMetaId; - return await processJoinBattleInstance(player, 0, instance_meta_id, Timestamp.FromDateTime(DateTimeHelper.MinTime.Date)); - - } - - private Result parameterValidationCheck(Player player, ClientToGameReq.Types.C2GS_REQ_JOIN_BATTLE_INSTANCE request) - { - var server_logic = GameServerApp.getServerLogic(); - var server_config = server_logic.getServerConfig(); - bool is_battle_system_active = server_config.BattleSystemEnable; - - string err_msg = string.Empty; - var result = new Result(); - - if (false == is_battle_system_active) - { - err_msg = $"is_battle_system_active is false!!!"; - result.setFail(ServerErrorCode.BattleInstanceInActive, err_msg); - Log.getLogger().error(err_msg); - return result; - } - return result; + return Task.FromResult(result); } public static bool send_S2C_ACK_JOIN_BATTLE_INSTANCE(Player owner, Result result, ServerConnectInfo? serverConnectInfo) diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/LeaveBattleInstancePacketHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/LeaveBattleInstancePacketHandler.cs index 739c91a..17672e7 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/LeaveBattleInstancePacketHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/LeaveBattleInstancePacketHandler.cs @@ -1,4 +1,5 @@ -using GameServer.Contents.GameMode.Mode_Battle.Manage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Battle.Manage; using MetaAssets; using Newtonsoft.Json; using ServerBase; @@ -17,22 +18,10 @@ public class LeaveBattleInstancePacketHandler : PacketRecvHandler var err_msg = string.Empty; var server_logic = GameServerApp.getServerLogic(); - var server_config = server_logic.getServerConfig(); - bool is_battle_system_active = server_config.BattleSystemEnable; var player = entityWithSession as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"entity_player is null !!!"); - //앞단에 넣으면 좋겠지만... 일단 여기에.. - if (false == is_battle_system_active) - { - err_msg = $"is_battle_system_active is false!!!"; - result.setFail(ServerErrorCode.BattleInstanceInActive, err_msg); - Log.getLogger().error(err_msg); - send_S2C_ACK_LEAVE_BATTLE_INSTANCE(player, result, null); - return result; - } - var req_msg = recvMessage as ClientToGame; ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); @@ -103,6 +92,31 @@ public class LeaveBattleInstancePacketHandler : PacketRecvHandler send_S2C_ACK_LEAVE_BATTLE_INSTANCE(player, result, null); return result; } + + + var location_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); + + var current_indun_location = location_action.getCurrentLocation() as IndunLocation; + NullReferenceCheckHelper.throwIfNull(current_indun_location, () => $"location_action is null !!! - {player.toBasicString()}"); + var room_id = current_indun_location.InstanceRoomId; + if (room_id == string.Empty) + { + err_msg = $"current_indun_location instance_room_Id is empty!!!! - {nameof(LeaveInstancePacketHandler)}"; + result.setFail(ServerErrorCode.ValidServerNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + send_S2C_ACK_LEAVE_BATTLE_INSTANCE(player, result, null); + return result; + } + + if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + { + err_msg = $"LeaveBattleInstancePacketHandler game_instance_room is null : room_id - {room_id}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeClassIsNull, err_msg); + send_S2C_ACK_LEAVE_BATTLE_INSTANCE(player, result, null); + return result; + } // 4. instance room 떠나기 result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "LeaveBattleInstance", leaveBattleInstanceDelegate); @@ -113,35 +127,34 @@ public class LeaveBattleInstancePacketHandler : PacketRecvHandler return result; } - // if (prev_is_in_concert && prev_indun_data is not null) - // { - // await QuestManager.It.QuestCheck(entity_player, new QuestConcert(EQuestEventTargetType.CONCERT, EQuestEventNameType.ENDED, prev_indun_data.MapId)); - // } - - return result; - async Task leaveBattleInstanceDelegate() => await leaveBattleInstanceAsync(player, move_server_info, prev_indun_data, invokers); + async Task leaveBattleInstanceDelegate() => await leaveBattleInstanceAsync(gameMode, player, move_server_info, invokers, room_id); } - public async Task leaveBattleInstanceAsync(Player player, ServerInfo server_info, InstanceMetaData prevIndunData, List invokers) + public async Task leaveBattleInstanceAsync(IGameMode gameMode, Player player, ServerInfo serverInfo, List invokers, string roomId) { var server_logic = GameServerApp.getServerLogic(); var battle_zone_move_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(battle_zone_move_action, () => $"game_zone_action is null !!! - {player.toBasicString()}"); - - var result = new Result(); - string err_msg; - // 1. leave battle room - (result, _) = await battle_zone_move_action.tryLeaveBattleInstanceRoom(); + MatchManager.It.RoomEventHandler.GameMemberQuit(roomId, player.getUserGuid()); + + string err_msg = string.Empty; + (var result, var leave_handler) = GameModeHelper.getGameModeLeaveHandler(gameMode, player, serverInfo, invokers, roomId); + if (result.isFail() || leave_handler is null) + { + err_msg = $"tryLeaveBattelInstance instance_room_Id is Empty player : {player.toBasicString()}"; + result.setFail(ServerErrorCode.GameModeLeaveHandlerNotExist, err_msg); + send_S2C_ACK_LEAVE_BATTLE_INSTANCE(player, result, null); + return result; + } + + result = await leave_handler.gameModeLeave(); if (result.isFail()) { - err_msg = $"Failed to tryLeaveInstance() !!! : {result.toBasicString()} - {player.toBasicString()}"; - Log.getLogger().error(err_msg); - send_S2C_ACK_LEAVE_BATTLE_INSTANCE(player, result, null); return result; } @@ -158,24 +171,7 @@ public class LeaveBattleInstancePacketHandler : PacketRecvHandler send_S2C_ACK_LEAVE_BATTLE_INSTANCE(player, result, null); return result; } - - //var battle_instance_room = BattleInstanceManager.It.getBattleInstanceRoom(instance_room_Id); - - if (false == GameModeManager.It.tryGetGameMode(instance_room_Id, out var gameMode)) - { - err_msg = $"battle_instance_room not esist instance_room_Id : {instance_room_Id}, player : {player.toBasicString()}"; - result.setFail(ServerErrorCode.BattleInstanceInfoNotExist, err_msg); - send_S2C_ACK_LEAVE_BATTLE_INSTANCE(player, result, null); - return result; - } - - var ffa = gameMode as GameModeTPSFreeForAll; - NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!! - {player.toBasicString()}"); - - var battle_instance_room_attribute = ffa.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(battle_instance_room_attribute, () => $"battle_instance_room_attribute is null !!! - {player.toBasicString()}"); - - // 2. move to channel + result = await location_action.tryMoveToChannel(); if (result.isFail()) { @@ -197,7 +193,7 @@ public class LeaveBattleInstancePacketHandler : PacketRecvHandler } // 3. otp 생성 - (result, var reserved_to_switch_server) = await ServerConnectionSwitchHelper.startServerSwitch(player, server_logic.getRedisConnector(), server_info.Name); + (result, var reserved_to_switch_server) = await ServerConnectionSwitchHelper.startServerSwitch(player, server_logic.getRedisConnector(), serverInfo.Name); if (result.isFail() || null == reserved_to_switch_server) { err_msg = $"Fail to startServerSwitch() !!! : {result.toBasicString()}"; @@ -217,8 +213,8 @@ public class LeaveBattleInstancePacketHandler : PacketRecvHandler var gameServer_connection_info = new ServerConnectInfo { - ServerAddr = server_info.Address, - ServerPort = server_info.Port, + ServerAddr = serverInfo.Address, + ServerPort = serverInfo.Port, Otp = reserved_to_switch_server.OneTimeKey, }; diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/PreparationForLeavingBattleInstancePacketHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/PreparationForLeavingBattleInstancePacketHandler.cs index 7107bb2..ade0f36 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/PreparationForLeavingBattleInstancePacketHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/PacketHandler/PreparationForLeavingBattleInstancePacketHandler.cs @@ -1,4 +1,5 @@ -using GameServer.Contents.GameMode.Mode_Battle.Manage; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Mode_Battle.Manage; using Nettention.Proud; using ServerBase; using ServerCore; @@ -15,24 +16,9 @@ public class PreparationForLeavingBattleInstancePacketHandler : PacketRecvHandle var result = new Result(); var err_msg = string.Empty; - - var server_logic = GameServerApp.getServerLogic(); - var server_config = server_logic.getServerConfig(); - bool is_battle_system_active = server_config.BattleSystemEnable; - var player = entityWithSession as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"entity_player is null !!!"); - //앞단에 넣으면 좋겠지만... 일단 여기에.. - if (false == is_battle_system_active) - { - err_msg = $"is_battle_system_active is false!!!"; - result.setFail(ServerErrorCode.BattleInstanceInActive, err_msg); - Log.getLogger().error(err_msg); - send_S2C_ACK_PREPARE_FOR_LEAVING_BATTLE_INSTANCE(player, result); - return result; - } - var req_msg = recvMessage as ClientToGame; ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); var request = req_msg.Request.ReqPreparationForLeavingInstance; @@ -53,7 +39,6 @@ public class PreparationForLeavingBattleInstancePacketHandler : PacketRecvHandle var room_id = player.getCurrentInstanceRoomId(); - //var battle_instance_room = BattleInstanceManager.It.getBattleInstanceRoom(room_id); if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) { err_msg = $"battle_instance_room is null : room_id - {room_id}"; @@ -62,16 +47,16 @@ public class PreparationForLeavingBattleInstancePacketHandler : PacketRecvHandle send_S2C_ACK_PREPARE_FOR_LEAVING_BATTLE_INSTANCE(player, result); return result; } + var user_guid = player.getUserGuid(); - var ffa = gameMode as GameModeTPSFreeForAll; + var ffa = gameMode as GameModeTPSFreeForAll; NullReferenceCheckHelper.throwIfNull(ffa, () => $"ffa is null !!!"); var fn_preparation_for_leaving = async delegate() { bool need_pod_combat_drop_noti = false; using (var releaser = await ffa.getAsyncLock()) { - var attribute = ffa.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(attribute, () => $"attribute is null !!!"); var current_owner_guid = attribute.m_combat_pod_mode.m_pod_combat.m_current_occupier_guid; @@ -107,7 +92,7 @@ public class PreparationForLeavingBattleInstancePacketHandler : PacketRecvHandle } var new_host_user_guid = ffa.m_host_migrator.getHostUserGuid(); - BattleRoomNotifyHelper.broadcast_GS2C_NTF_P2P_HOST_UPDATE(ffa.getInstanceRoom(), new_host_user_guid); + GameNotifyHelper.broadcast_GS2C_NTF_P2P_HOST_UPDATE(ffa.getInstanceRoom(), new_host_user_guid); } return result; }; @@ -121,7 +106,6 @@ public class PreparationForLeavingBattleInstancePacketHandler : PacketRecvHandle send_S2C_ACK_PREPARE_FOR_LEAVING_BATTLE_INSTANCE(player, result); } - return result; } diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Redis/BattleInstanceRoomStorage.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Redis/BattleInstanceRoomStorage.cs index d605d21..6c4beb4 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Redis/BattleInstanceRoomStorage.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Redis/BattleInstanceRoomStorage.cs @@ -11,10 +11,6 @@ namespace ServerCommon; public class BattleInstanceRoomStorage { - - //readonly int KEEP_INSTANCE_ROOM_TIME = (60 * 1000); - - //ConnectionMultiplexer _connection = default!; IDatabase _database = default!; string _roomKeyPrefix = String.Empty; @@ -41,29 +37,6 @@ public class BattleInstanceRoomStorage _instanceKeyPrefix += "instanceroom"; } - - public async Task addBattleInstanceRoomList(string instanceRoomIdBase, string instanceRoomId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); - - await _database.SortedSetAddAsync(instance_room_list_key, instanceRoomId, 0); - await _database.KeyExpireAsync(instance_room_list_key, TimeSpan.FromMilliseconds(300000)); - } - catch (Exception e) - { - err_msg = $"Failed to addInstanceRoomList from Redis !!! : message:{e}"; - result.setFail(ServerErrorCode.TryCatchException, err_msg); - Log.getLogger().fatal(err_msg); - } - - return result; - } - //이코드는 만약에 GetInstanceRoomListKey 에 키값이 변경된다면 battleInstance는 문제가 생길수 있음 public string GetInstanceRoomListKey(string instanceRoomIdBase) { @@ -133,541 +106,3 @@ public class BattleInstanceRoomStorage } -/* - string GetRoomIdSeqKey() - { - return $"{_roomKeyPrefix}:roomidseq"; - } - - public string GetRoomInfoKey(string instanceRoomId) - { - return $"{_roomKeyPrefix}:{instanceRoomId}:info"; - } - - public string GetRoomMemberListKey(string instanceRoomId) - { - return $"{_roomKeyPrefix}:{instanceRoomId}:memberlist"; - } - - - - public async Task getRoomIdSeq() - { - var result = new Result(); - var err_msg = string.Empty; - - long room_id_seq = 0; - - try - { - string room_id_seq_key = GetRoomIdSeqKey(); - room_id_seq = await _database.StringIncrementAsync(room_id_seq_key); - } - catch (Exception e) - { - err_msg = $"Failed to getRoomIdSeq from Redis !!! : message:{e}"; - result.setFail(ServerErrorCode.TryCatchException, err_msg); - Log.getLogger().fatal(result.toBasicString()); - } - - return room_id_seq; - } - - public async Task<(Result, bool)> createInstanceRoom(string instanceRoomId, string instanceAddress, int instancePort, int instanceMetaId) - { - var result = new Result(); - var err_msg = string.Empty; - - var is_created = false; - - try - { - string room_info_key = GetRoomInfoKey(instanceRoomId); - - var instance_room_info = new InstanceRoomInfo(); - instance_room_info.roomId = instanceRoomId; - instance_room_info.InstanceAddress = instanceAddress; - instance_room_info.InstancePort = instancePort; - instance_room_info.InstanceId = instanceMetaId; - instance_room_info.UgcNpcCount = 0; - - // 중복 확인 - var exist_instance_room_state = await isExistInstanceRoom(instanceRoomId, instanceMetaId); - switch (exist_instance_room_state) - { - case ExistInstanceRoomState.None: - { - var hash_entries = TypeConvertHelper.toHashEntries(instance_room_info); - await _database.HashSetAsync(room_info_key, hash_entries); - await _database.KeyExpireAsync(room_info_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); - - is_created = true; - } - break; - case ExistInstanceRoomState.Different: - { - err_msg = $"InstanceRoomId is Duplicated !!! : instanceRoomId:{instanceRoomId}, instanceMetaId:{instanceMetaId}"; - result.setFail(ServerErrorCode.InstanceRoomIdDuplicated, err_msg); - Log.getLogger().error(err_msg); - - return (result, is_created); - } - case ExistInstanceRoomState.Exist: - { - // 존재하는 방이 만드려는 방과 동일하다. - // 해당 방으로 입장한다. - } - break; - case ExistInstanceRoomState.Exception: - { - err_msg = $"Failed to isExistInstanceRoom() !!!"; - result.setFail(ServerErrorCode.TryCatchException, err_msg); - Log.getLogger().error(result.toBasicString()); - - return (result, is_created); - } - default: - { - err_msg = $"ExistInstanceRoomState Invalid !!! : state:{exist_instance_room_state}"; - result.setFail(ServerErrorCode.NotImplemented, err_msg); - Log.getLogger().error(result.toBasicString()); - - return (result, is_created); - } - } - } - catch (Exception e) - { - err_msg = $"Failed to createInstanceRoom from Redis !!! : message:{e}"; - result.setFail(ServerErrorCode.TryCatchException, err_msg); - Log.getLogger().fatal(err_msg); - } - - return (result, is_created); - } - - async Task isExistInstanceRoom(string instanceRoomId, int instanceMetaId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_info_key = GetRoomInfoKey(instanceRoomId); - - // 기존 방 정보 얻기 - var value = await _database.HashGetAllAsync(instance_room_info_key, CommandFlags.PreferReplica); - if (value.Length <= 0) - return ExistInstanceRoomState.None; - - // 내가 원하는 방인지 확인 - var instance_room_info = TypeConvertHelper.toClassFromHashEntries(value); - if (instance_room_info.roomId != instanceRoomId || instance_room_info.InstanceId != instanceMetaId) - return ExistInstanceRoomState.Different; - } - catch (Exception e) - { - err_msg = $"Failed to isExistInstanceRoom from Redis !!! : message:{e}"; - result.setFail(ServerErrorCode.TryCatchException, err_msg); - Log.getLogger().fatal(result.toBasicString()); - - return ExistInstanceRoomState.Exception; - } - - return ExistInstanceRoomState.Exist; - } - - - - public async Task joinInstanceRoom(string userGuid, string instanceRoomId, int limitCount) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_member_list_key = GetRoomMemberListKey(instanceRoomId); - - var push_after_length = await _database.ListRightPushAsync(instance_room_member_list_key, userGuid); - if (push_after_length > limitCount) - { - await _database.ListTrimAsync(instance_room_member_list_key, 0, limitCount - 1); - var trim_after_position = await _database.ListPositionAsync(instance_room_member_list_key, userGuid); - if (trim_after_position == -1) - { - err_msg = $"roomId:{instanceRoomId} is full !!! : pushAfterLength:{push_after_length}, limitCount:{limitCount}, trimAfterPosition:{trim_after_position} - userGuid:{userGuid}"; - result.setFail(ServerErrorCode.InstanceRoomIsFull, err_msg); - Log.getLogger().debug(err_msg); - - return result; - } - } - - await _database.KeyExpireAsync(instance_room_member_list_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); - } - catch (Exception e) - { - err_msg = $"Failed to joinInstanceRoom from Redis !!! : message:{e} - userGuid:{userGuid}"; - result.setFail(ServerErrorCode.TryCatchException, err_msg); - Log.getLogger().fatal(result.toBasicString()); - } - - return result; - } - - public async Task increaseInstanceRoomScore(string instanceRoomIdBase, string instanceRoomId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); - - await _database.SortedSetIncrementAsync(instance_room_list_key, instanceRoomId, 1); - } - catch (Exception e) - { - err_msg = $"Failed to increaseInstanceRoomScore from Redis !!! : message:{e}"; - result.setFail(ServerErrorCode.TryCatchException, err_msg); - Log.getLogger().fatal(result.toBasicString()); - } - - return result; - } - - public async Task increaseInstanceRoomUgcNpcScore(string instanceRoomId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_info_key = GetRoomInfoKey(instanceRoomId); - await _database.HashIncrementAsync(instance_room_info_key, nameof(InstanceRoomInfo.UgcNpcCount), 1); - } - catch (Exception e) - { - var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to decreaseInstanceRoomUgcNpcScore from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; - result.setFail(error_code, err_msg); - Log.getLogger().error(err_msg); - } - - return result; - } - - public async Task> GetInstanceRoomMemberList(string roomId) - { - List returnList = new(); - string roomKey = GetRoomMemberListKey(roomId); - var roomMemberList = await _database.ListRangeAsync(roomKey); - - if (roomMemberList == null) return returnList; - - returnList.AddRange(roomMemberList.Select(x => x.ToString())); - return returnList; - } - - public async Task leaveInstanceRoom(string userGuid, string instanceRoomId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_list_key = GetRoomMemberListKey(instanceRoomId); - - await _database.ListRemoveAsync(instance_room_list_key, userGuid); - } - catch (Exception e) - { - err_msg = $"Failed to leaveInstanceRoom from Redis !!! : message:{e} - userGuid:{userGuid}"; - result.setFail(ServerErrorCode.TryCatchException, err_msg); - Log.getLogger().fatal(err_msg); - } - - return result; - } - - public async Task decreaseInstanceRoomScore(string instanceRoomIdBase, string instanceRoomId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); - - await _database.SortedSetDecrementAsync(instance_room_list_key, instanceRoomId, 1); - } - catch (Exception e) - { - var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to decreaseInstanceRoomScore from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; - result.setFail(error_code, err_msg); - Log.getLogger().error(err_msg); - } - - return result; - } - - public async Task decreaseInstanceRoomUgcNpcScore(string instanceRoomId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_info_key = GetRoomInfoKey(instanceRoomId); - await _database.HashDecrementAsync(instance_room_info_key, nameof(InstanceRoomInfo.UgcNpcCount), 1); - } - catch (Exception e) - { - var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to decreaseInstanceRoomUgcNpcScore from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; - result.setFail(error_code, err_msg); - Log.getLogger().error(err_msg); - } - - return result; - } - - public async Task deleteInstanceRoom(string instanceRoomId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string roomInfoKey = GetRoomInfoKey(instanceRoomId); - string roomMemberListKey = GetRoomMemberListKey(instanceRoomId); - - await _database.KeyDeleteAsync(roomInfoKey); - await _database.KeyDeleteAsync(roomMemberListKey); - } - catch (Exception e) - { - var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to deleteInstanceRoom from Redis !!! : errorCode{error_code}, errMsg:{e.Message}"; - result.setFail(error_code, err_msg); - Log.getLogger().error(err_msg); - } - - return result; - } - - public async Task removeInstanceRoomList(string instanceRoomIdBase, string instanceRoomId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); - - await _database.SortedSetRemoveAsync(instance_room_list_key, instanceRoomId); - } - catch (Exception e) - { - var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to removeInstanceRoomList from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; - result.setFail(error_code, err_msg); - Log.getLogger().error(err_msg); - } - - return result; - } - - public async Task GetInstanceRoomInfo(string instanceRoomId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - var instance_room_info_key = GetRoomInfoKey(instanceRoomId); - var value = await _database.HashGetAllAsync(instance_room_info_key, CommandFlags.PreferReplica); - if (value.Length <= 0) return null; - - return TypeConvertHelper.toClassFromHashEntries(value); - } - catch (Exception e) - { - err_msg = $"Failed to GetInstanceRoomInfo() from Redis !!! : message:{e}"; - result.setFail(ServerErrorCode.TryCatchException, err_msg); - Log.getLogger().error(err_msg); - - return null; - } - } - - public async Task setInstanceRoomExtraInfo(string roomId, EPlaceType placeType, DateTime startTime) - { - var result = new Result(); - - try - { - var instance_info = await GetInstanceRoomInfo(roomId); - if (null == instance_info) - { - result.setFail(ServerErrorCode.NotExistInstanceRoom); - return result; - } - - instance_info.InstancePlaceType = placeType; - instance_info.InstanceStartTime = startTime.ToTimestamp(); - - var room_info_key = GetRoomInfoKey(roomId); - var hash_entries = TypeConvertHelper.toHashEntries(instance_info); - await _database.HashSetAsync(room_info_key, hash_entries); - await _database.KeyExpireAsync(room_info_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); - - return result; - } - catch (Exception ex) - { - result.setFail(ServerErrorCode.InstanceRoomException, $"{ex}"); - Log.getLogger().error($"{ex.ToString()}"); - return result; - } - } - - public async Task setInstanceRoomMyhomeGuid(string roomId, string myhomeGuid) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - var instance_info = await GetInstanceRoomInfo(roomId); - if (null == instance_info) - { - err_msg = $"Failed to GetInstanceRoomInfo() !!! : instanceRoomId:{roomId}"; - result.setFail(ServerErrorCode.InstanceRoomNotExistAtRedis, err_msg); - Log.getLogger().error(result.toBasicString()); - - return result; - } - - instance_info.MyhomeGuid = myhomeGuid; - - var room_info_key = GetRoomInfoKey(roomId); - var hash_entries = TypeConvertHelper.toHashEntries(instance_info); - await _database.HashSetAsync(room_info_key, hash_entries); - await _database.KeyExpireAsync(room_info_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); - - return result; - } - catch (Exception e) - { - err_msg = $"Failed to setInstanceRoomMyhomeGuid from Redis !!! : message:{e}"; - result.setFail(ServerErrorCode.TryCatchException, err_msg); - Log.getLogger().fatal(err_msg); - - return result; - } - } - - - - - public async Task getInstanceRoomListTotalUserCount(string instanceRoomIdBase) - { - var err_msg = string.Empty; - - int total_user_count = 0; - - try - { - string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); - - var sorted_set_entities = await _database.SortedSetRangeByRankWithScoresAsync(instance_room_list_key); - foreach (var entity in sorted_set_entities) - { - if (entity.Score < 0) - continue; - - total_user_count += (int)entity.Score; - } - } - catch (Exception e) - { - var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to get InstanceRoomListTotalUserCount from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; - Log.getLogger().error(err_msg); - } - - return total_user_count; - } - - public async Task getInstanceRoomUserCount(string instanceRoomId) - { - var err_msg = string.Empty; - - int user_count = 0; - - try - { - var room_member_list_key = GetRoomMemberListKey(instanceRoomId); - - user_count = (int)await _database.ListLengthAsync(room_member_list_key, CommandFlags.PreferReplica); - } - catch (Exception e) - { - var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to get InstanceRoomUserCount from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; - Log.getLogger().error(err_msg); - } - - return user_count; - } - - public async Task keepInstanceRoom(string instanceRoomId) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_info_key = GetRoomInfoKey(instanceRoomId); - string intstance_room_member_list_key = GetRoomMemberListKey(instanceRoomId); - - await _database.KeyExpireAsync(instance_room_info_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); - await _database.KeyExpireAsync(intstance_room_member_list_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); - } - catch(Exception e) - { - var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to KeepInstanceRoom from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; - result.setFail(error_code, err_msg); - Log.getLogger().error(err_msg); - } - - return result; - } - - public async Task keepInstanceRoomList(string instanceRoomIdBase) - { - var result = new Result(); - var err_msg = string.Empty; - - try - { - string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); - - await _database.KeyExpireAsync(instance_room_list_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME), ExpireWhen.Always, CommandFlags.FireAndForget); - } - catch (Exception e) - { - var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to KeepInstanceRoomList from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; - result.setFail(error_code, err_msg); - Log.getLogger().error(err_msg); - } - - return result; - } - } -} -*/ \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateCreated.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateCreated.cs new file mode 100644 index 0000000..3b8d3c6 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateCreated.cs @@ -0,0 +1,47 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; + +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.State; + +public class TpsFfaStateCreated : GameModeStateBase +{ + public TpsFfaStateCreated(IGameMode gameMode) : base(GameModeState.Created, gameMode) + { + + } + + public override Task enterDetail() + { + extendStateTime(TimeSpan.FromSeconds(20), "RunRaceStateCreated" ); + return Task.CompletedTask; + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateReady.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateReady.cs new file mode 100644 index 0000000..92ca937 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateReady.cs @@ -0,0 +1,59 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.State; + +public class TpsFfaStateReady : GameModeStateBase +{ + //private DateTime m_next_state_change_time = DateTimeHelper.Current; + public TpsFfaStateReady(IGameMode gameMode) : base(GameModeState.Ready, gameMode)//이 상태는 아직 사용중이 아니다... 이걸 어떻게 해야할지 고민 필요.. + { + + } + + public override Task enterDetail() + { + var update_time = DateTimeHelper.Current.AddSeconds(3); //meta로 + + var tps_ffa = m_current_game_mode as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + + setStateTime(update_time, "TpsFfaStateReady"); + return Task.CompletedTask; + } + + public override Task updateDetail() + { + + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override async Task exitDetail() + { + var tps_ffa = m_current_game_mode as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + var ready_pos_action = tps_ffa.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ready_pos_action, () => $"ready_pos_action is null !!!"); + await ready_pos_action.setReadyPos(); + + } + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRoundEndAll.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRoundEndAll.cs new file mode 100644 index 0000000..c0d4ff8 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRoundEndAll.cs @@ -0,0 +1,62 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.State; + +public class TpsFfaStateRoundEndAll : GameModeStateBase +{ + + + public TpsFfaStateRoundEndAll(IGameMode gameMode) : base(GameModeState.RoundEndAll, gameMode) + { + + } + + public override Task enterDetail() + { + var tpf_ffa = getGameMode() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tpf_ffa, () => $"game_mode_base is null !!!"); + + var next_state_change_time = getNextStateChangeTime(); + + var update_time = next_state_change_time.AddSeconds(tpf_ffa.m_ffa_meta.m_result_ui_wait_time); + setStateTime(update_time, "TpsFfaStateRoundEndAll"); + + return Task.CompletedTask; + + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + var tps_ffa = m_current_game_mode as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"game_mode_base is null !!!"); + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(tps_ffa); + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRoundWait.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRoundWait.cs new file mode 100644 index 0000000..dec1f82 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRoundWait.cs @@ -0,0 +1,59 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.State; + +public class TpsFfaStateRoundWait : GameModeStateBase +{ + + + public TpsFfaStateRoundWait(IGameMode gameMode) : base(GameModeState.RoundWait, gameMode) + { + + } + + public override Task enterDetail() + { + var tps_ffa = getGameMode() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"game_mode_base is null !!!"); + + var next_state_change_time = getNextStateChangeTime(); + var update_time = next_state_change_time.AddSeconds(tps_ffa.m_ffa_meta.m_next_round_wait_time); + setStateTime(update_time, "TpsFfaStateRoundWait"); + + return Task.CompletedTask; + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + var tps_ffa = m_current_game_mode as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"game_mode_base is null !!!"); + BattleRoomNotifyHelper.broadcast_GS2C_NTF_BATTLE_INSTANCE_STATE(tps_ffa); + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRounding.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRounding.cs new file mode 100644 index 0000000..d9ffd1b --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateRounding.cs @@ -0,0 +1,97 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.State; + +public class TpsFfaStateRounding : GameModeStateBase +{ + public TpsFfaStateRounding(IGameMode gameMode) : base(GameModeState.Rounding, gameMode) + { + } + + public override async Task enterDetail() + { + var tps_ffa = getGameMode() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"game_mode_base is null !!!"); + var update_action = tps_ffa.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(update_action, () => $"update_action is null !!!"); + var attribute = tps_ffa.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"BattleInstanceSnapshotAttribute is null !!!"); + + //라운드 셋팅 + var now = DateTimeHelper.Current; + await update_action.updateRoundStart(now); + + //시간 변경 + var update_time = now.AddSeconds(tps_ffa.m_ffa_meta.m_round_time); + setStateTime(update_time, $"TpsFfaStateRound : {attribute.m_combat_pod_mode.m_current_round}"); + } + + + public override void notifyDetail() + { + var tps_ffa = getGameMode() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"game_mode_base is null !!!"); + var update_action = tps_ffa.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(update_action, () => $"update_action is null !!!"); + + update_action.objectStateInitAndNotify(); + } + + public override async Task updateDetail() + { + var tps_ffa = getGameMode() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"game_mode_base is null !!!"); + var update_action = tps_ffa.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(update_action, () => $"update_action is null !!!"); + + await update_action.updateRoundState(); + return new Result(); + } + + public override void writeBusinessLog() + { + var invokers = new List(); + var log_action = new LogAction(LogActionType.BattleRoundStateUpdate.ToString()); + + var tps_ffa = getGameMode() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + var room_id = tps_ffa.getRoomId(); + + var attribute = tps_ffa.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"BattleInstanceSnapshotAttribute is null !!!"); + + var ended_round = attribute.m_combat_pod_mode.m_current_round; + var round_state = attribute.m_combat_pod_mode.m_game_state;; + var users = tps_ffa.getInstanceRoom().tryGetInstanceExistUserForLog(); + BattleRoundUpdateBusinessLog business_log = new(room_id, ended_round, round_state, users); + invokers.Add(business_log); + BusinessLogger.collectLogs(log_action, tps_ffa, invokers); + } + + public override async Task exitDetail() + { + var tps_ffa = getGameMode() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + var update_action = tps_ffa.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(update_action, () => $"BattleInstanceUpdateAction is null !!!"); + var attribute = tps_ffa.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"BattleInstanceSnapshotAttribute is null !!!"); + + var now = DateTimeHelper.Current; + await update_action.roundingUpdate(tps_ffa, now); + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateWait.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateWait.cs new file mode 100644 index 0000000..0a7f9dc --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/State/TpsFfaStateWait.cs @@ -0,0 +1,51 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Battle.ModeFreeForAll.State; + +public class TpsFfaStateWait : GameModeStateBase +{ + public TpsFfaStateWait(IGameMode gameMode, StateUpdateReasonType updateReason = StateUpdateReasonType.None) : base(GameModeState.Wait, gameMode, updateReason) + { + + } + + public override Task enterDetail() + { + var tps_ffa = getGameMode() as GameModeTPSFreeForAll; + NullReferenceCheckHelper.throwIfNull(tps_ffa, () => $"tps_ffa is null !!!"); + + var update_time = DateTimeHelper.Current.AddSeconds(tps_ffa.m_ffa_meta.m_max_start_wait_time); + setStateTime(update_time, "TpsFfaStateWait"); + + return Task.CompletedTask; + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Tickers/BattleEventCheckTicker.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Tickers/BattleEventCheckTicker.cs index 14940e3..2354641 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Tickers/BattleEventCheckTicker.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Tickers/BattleEventCheckTicker.cs @@ -1,11 +1,14 @@ using System.Collections.Concurrent; using System.Net; +using GameServer; using GameServer.Contents.Battle.Doc; using MetaAssets; using Newtonsoft.Json; using ServerBase; using ServerCommon; using ServerCore; +using BattleEventAttrib = GameServer.Contents.Battle.Doc.BattleEventAttrib; +using EVENT_ID = System.Int32; namespace GameServer; @@ -14,11 +17,11 @@ public class BattleEventCheckTicker : EntityTicker public BattleEventCheckTicker(double onTickIntervalMilliseconds, CancellationTokenSource? cts) : base(EntityType.BattleEventCheckTicker, onTickIntervalMilliseconds, cts) { - - + + } - + public override async Task onTaskTick() { var result = await refreshEvent(); @@ -32,7 +35,7 @@ public class BattleEventCheckTicker : EntityTicker public override async Task onInit() { await refreshEvent(); - + var result = new Result(); return result; @@ -40,27 +43,28 @@ public class BattleEventCheckTicker : EntityTicker public async Task refreshEvent() { - var result = new Result(); + var result = new Result(); (result, var docs) = await loadFromDB(); if (result.isFail()) return result; if (docs.Count == 0) { Log.getLogger().info("Not exist Battle Evnet From DB"); + BattleInstanceManager.It.clearSystemBattleEvents(); return result; } //실제로 작동하는 battle event로 만들기 await updateBattleEvent(docs); - + return result; } - + private async Task<(Result, List)> loadFromDB() { var server_logic = GameServerApp.getServerLogic(); var dynamo_db_client = server_logic.getDynamoDbClient(); - + var doc = new BattleEventDoc(); var query_config = dynamo_db_client.makeQueryConfigForReadByPKOnly(doc.getPK()); (var result, var read_doc_list) = await dynamo_db_client.simpleQueryDocTypesWithQueryOperationConfig(query_config); @@ -70,205 +74,180 @@ public class BattleEventCheckTicker : EntityTicker } Log.getLogger().info($"load battle event from db eventdoc count : {read_doc_list.Count}"); - + return (result, read_doc_list); } - - - public List loadTempEvent() - { - List battle_event_attribs = new(); - - for(int i = 1; i < 15; i++) - { - var battle_event_attrib = new BattleEventAttrib(); - battle_event_attrib.m_event_id = i; - battle_event_attrib.m_is_active = i == 11 ? false : true; - - battle_event_attrib.m_start_day = DateTimeHelper.Current.Date; - battle_event_attrib.m_start_hour = i-1; //9시 부터 셋팅 - battle_event_attrib.m_start_min = 00; - - - battle_event_attrib.m_instance_id = 1017006 + ((i % 3) % 2); - battle_event_attrib.m_period = OncePeriodRangeType.Daily; - battle_event_attrib.m_day_of_week_type = new(); - battle_event_attrib.m_ffa_config_data_id = 1; - battle_event_attrib.m_ffa_reward_group_id = 1; - battle_event_attrib.m_ffa_hot_time = 1 + (i % 3) ; - - battle_event_attrib.m_round_count = 1 + (i % 5); - battle_event_attrib.m_end_date = DateTimeHelper.Current.Date.AddDays(7); - - battle_event_attribs.Add(battle_event_attrib); - - } - return battle_event_attribs; - } - public async Task updateBattleEvent(List docs) { - await Task.CompletedTask; DateTime now = DateTimeHelper.Current; DateTime today = now.Date; - - deleteOldEvents(now); - - foreach (var read_doc in docs) - { - addNewEvent(read_doc, today, now); - } + //지난 이벤트 삭제 + bool need_noti = deleteOldEvents(now); + foreach (var read_doc in docs) { - checkModifiedEvent(now, read_doc); + var is_new_event = addOrUpdateEvent(read_doc); + if(is_new_event) need_noti = true; } + + need_noti = deleteDeactiveEventsBeforeStart(docs, now); + + //if (need_noti) //kihoon 일단 사이클마다 돌리는것으로 정리 추후 성능 이슈 발생시 need_noti 주석 풀것 + //{ + BattleRoomNotifyHelper.broadcastBattleEventNotify(); + //} BattleInstanceManager.It.logSystemBattleEvents(); } - private void addNewEvent(BattleEventDoc doc, DateTime today, DateTime now) + private bool addOrUpdateEvent(BattleEventDoc doc) { var attrib = doc.getAttrib(); if (attrib is null) { Log.getLogger().error("BattleEventDoc BattleEventAttrib is null !!!!"); - return; + return false; } + //비활성화 된 이벤트는 추가 하지 않는다. + if (attrib.m_is_active == false) return false; - //메모리에 존재하는 이벤트면 리턴 - if (BattleInstanceManager.It.existSystemBattleEvent(attrib.m_event_id)) return; - - //아직 시작 안한 이벤트면 리턴 - var start_day = DateTime.SpecifyKind(attrib.m_start_day, DateTimeKind.Utc); - - var recently_events = CreateSystemBattleEvents(attrib, start_day, now); - foreach (var new_ev in recently_events) + + //이미 존재하는 이벤트는 추가 하지 않는다. + var event_id = attrib.m_event_id; + var is_exist = BattleInstanceManager.It.existSystemBattleEvent(event_id); + if (is_exist) return false;; + + //end_date가 지난 이벤트는 추가하지 않는다. + var now = DateTimeHelper.Current; + var today = now.Date; + var start_day = DateTime.SpecifyKind(attrib.m_start_day, DateTimeKind.Utc); + if (attrib.m_end_date < now) return false; + + var next_day = today.AddDays(1); + var before_day = today.AddDays(-1); + + + //end_time이 지난 이벤트도 추가하지 않는다. + var before_start_time = makeSystemBattleEventStartTime(attrib, start_day, before_day); + var before_end_time = before_start_time.AddMinutes(attrib.m_open_duration_minutes); + if (now <= before_end_time) { - BattleInstanceManager.It.addNewSystemBattleEvent(new_ev, true); + addEvent(attrib, before_start_time, before_end_time, now, before_day); + return true; } - var new_events = CreateSystemBattleEvents(attrib, today, now); - foreach (var new_ev in new_events) + var today_start_time = makeSystemBattleEventStartTime(attrib, start_day, today); + var today_end_time = today_start_time.AddMinutes(attrib.m_open_duration_minutes); + if (now < today_end_time) { - BattleInstanceManager.It.addNewSystemBattleEvent(new_ev, true); + addEvent(attrib, today_start_time, today_end_time, now, today); + return true; } - - //다음날 events 도 추가능한것은 추가한다. - var tomorrow_new_events = CreateSystemBattleEvents(attrib, today.AddDays(1), now); - foreach (var new_ev in tomorrow_new_events) + + var next_start_time = makeSystemBattleEventStartTime(attrib, start_day, next_day); + var next_end_time = next_start_time.AddMinutes(attrib.m_open_duration_minutes); + if (now < next_end_time) { - BattleInstanceManager.It.addNewSystemBattleEvent(new_ev, false); + addEvent(attrib, next_start_time, next_end_time, now, next_day); + return true; } - - + + return false; } - private void checkModifiedEvent(DateTime now, BattleEventDoc doc) - { - var events = BattleInstanceManager.It.getSystemBattleEvents(); - - var attrib = doc.getAttrib(); - if (attrib is null) - { - Log.getLogger().error("BattleEventDoc BattleEventAttrib is null !!!!"); - return; - } - - if (false == events.TryGetValue(attrib.m_event_id, out var event_data)) return; - if (event_data.m_start_time <= now) return; - - var delete_event_ids = new List(); - if (attrib.m_is_active == false) - { - delete_event_ids.Add(attrib.m_event_id); - } - - if (delete_event_ids.Count > 0) - { - Log.getLogger().info($"deleted event ids : {JsonConvert.SerializeObject(delete_event_ids)}"); - BattleInstanceManager.It.removeSystemBattleEvents(delete_event_ids); - } - - } - - private void deleteOldEvents(DateTime now) - { - var events = BattleInstanceManager.It.getSystemBattleEvents(); - - //오래된 이벤트 id 취합 및 삭제 - List deleted_event_ids = new(); - foreach (var battle_events in events.Values.ToList()) - { - var event_id = battle_events.m_event_id; - var start_time = battle_events.m_start_time; - - var destroyed_time = BattleRoomHelper.calculateDestroyedTime(start_time, battle_events.m_ffa_config_data_id, battle_events.m_round_count); - if (destroyed_time < now) - { - deleted_event_ids.Add(event_id); - } - } - - if (deleted_event_ids.Count > 0) - { - Log.getLogger().info($"deleted event ids : {JsonConvert.SerializeObject(deleted_event_ids)}"); - BattleInstanceManager.It.removeSystemBattleEvents(deleted_event_ids); - } - - - } - - private List CreateSystemBattleEvents(BattleEventAttrib attrib, DateTime startDay, DateTime now) + private void addEvent(BattleEventAttrib attrib, DateTime startTime, DateTime endTime, DateTime now, DateTime today) { List new_events = new(); - if (BattleInstanceManager.It.existSystemBattleEvent(attrib.m_event_id)) return new_events; - - var start_time = makeSystemBattleEventStartTime(attrib, startDay); - - //파괴되는 시간을 계산했는데 이벤트 종료시간보다 크면 안넣는다. - var joinableTime_time = BattleRoomHelper.calculateRoomJoinableTime(start_time, attrib.m_ffa_config_data_id, attrib.m_round_count); - if (joinableTime_time < now) return new_events; - if (attrib.m_end_date < joinableTime_time) return new_events; - - //이벤트 비활성 상태면 안넣는다. - if (attrib.m_is_active == false) return new_events; - - switch (attrib.m_period) + var period = attrib.m_period; + switch (period) { case OncePeriodRangeType.NONE: case OncePeriodRangeType.Daily: - new_events.AddRange(makeDailySystemBattleEvent(start_time, now, attrib)); + new_events.AddRange(makeDailySystemBattleEvent(startTime, endTime, attrib)); break; case OncePeriodRangeType.Weekly: - new_events.AddRange(makeWeekliySystemBattleEvent(startDay, now, attrib)); + new_events.AddRange(makeWeekliySystemBattleEvent(today, now, attrib)); break; case OncePeriodRangeType.Monthly: - new_events.AddRange(makeMonthiySystemBattleEvent(startDay, now, attrib)); + new_events.AddRange(makeMonthiySystemBattleEvent(today, now, attrib)); break; case OncePeriodRangeType.Nolimit: Log.getLogger($"There is not used Type in Battle System Event !!!! eventId : {attrib.m_event_id}"); break; } - if (new_events.Count > 0) - { - string new_events_str = JsonConvert.SerializeObject(new_events); - Log.getLogger().info($"new battele events set {new_events_str}"); - } - - return new_events; + BattleInstanceManager.It.addNewSystemBattleEvent(new_events, true); } - private DateTime makeSystemBattleEventStartTime(BattleEventAttrib attrib, DateTime today) + private bool deleteDeactiveEventsBeforeStart(List docs, DateTime now) + { + var deleted_event_ids = new List(); + foreach (var read_doc in docs) + { + var attrib = read_doc.getAttrib(); + if (attrib is null) + { + Log.getLogger().error("BattleEventDoc BattleEventAttrib is null !!!!"); + continue; + } + if (false == BattleInstanceManager.It.getSystemBattleEvent(attrib.m_event_id, out var battleEevent)) + { + continue; + } + + if (false == attrib.m_is_active && now < battleEevent.m_start_time) + { + deleted_event_ids.Add(attrib.m_event_id); + } + } + + if (deleted_event_ids.Count > 0) + { + Log.getLogger().info($"deleteDeactiveEventsBeforeStart deleted event ids : {JsonConvert.SerializeObject(deleted_event_ids)}"); + BattleInstanceManager.It.removeSystemBattleEvents(deleted_event_ids); + return true; + } + + return false; + } + + private bool deleteOldEvents(DateTime now) + { + var events = BattleInstanceManager.It.getSystemBattleEvents(); + + //오래된 이벤트 id 취합 및 삭제 + List deleted_event_ids = new(); + foreach (var battle_events in events.Values.ToList()) + { + var event_id = battle_events.m_event_id; + var end_time = battle_events.m_end_time; + if (end_time < now) + { + deleted_event_ids.Add(event_id); + continue; + } + } + + if (deleted_event_ids.Count > 0) + { + Log.getLogger().info($"deleted event ids : {JsonConvert.SerializeObject(deleted_event_ids)}"); + BattleInstanceManager.It.removeSystemBattleEvents(deleted_event_ids); + return true; + } + + return false; + } + + + private DateTime makeSystemBattleEventStartTime(BattleEventAttrib attrib, DateTime startDay, DateTime today) { switch (attrib.m_period) { case OncePeriodRangeType.NONE: - var start_day = DateTime.SpecifyKind(attrib.m_start_day, DateTimeKind.Utc); - return start_day.AddHours(attrib.m_start_hour).AddMinutes(attrib.m_start_min); + return startDay.AddHours(attrib.m_start_hour).AddMinutes(attrib.m_start_min); case OncePeriodRangeType.Daily: case OncePeriodRangeType.Weekly: case OncePeriodRangeType.Monthly: @@ -279,51 +258,14 @@ public class BattleEventCheckTicker : EntityTicker } return DateTimeHelper.MinTime; } - // private List makeNoneTypeSystemBattleEvent(DateTime startTime, DateTime now, BattleEventAttrib attrib) - // { - // List events = new(); - // - // if (startTime >= now) // && startTime < now.AddDays(1)) - // { - // if (attrib.m_end_date <= now) return events; - // - // SystemBattleEvent new_event = new SystemBattleEvent - // { - // m_event_id = attrib.m_event_id, - // m_instance_id = attrib.m_instance_id, - // m_start_time = startTime, - // m_ffa_config_data_id = attrib.m_ffa_config_data_id, - // m_ffa_reward_group_id = attrib.m_ffa_reward_group_id, - // m_ready_time = startTime.AddMinutes(-5), - // m_is_send_noti = false, - // m_ffa_hot_time = attrib.m_ffa_hot_time, - // m_round_count = attrib.m_round_count - // }; - // events.Add(new_event); - // } - // return events; - // } - private List makeDailySystemBattleEvent(DateTime startTime, DateTime now, BattleEventAttrib attrib) + private List makeDailySystemBattleEvent(DateTime startTime, DateTime endTime, BattleEventAttrib attrib) { List events = new(); - - if (attrib.m_end_date <= now) return events; - SystemBattleEvent new_event = new SystemBattleEvent - { - m_event_id = attrib.m_event_id, - m_instance_id = attrib.m_instance_id, - m_start_time = startTime, - m_ffa_config_data_id = attrib.m_ffa_config_data_id, - m_ffa_reward_group_id = attrib.m_ffa_reward_group_id, - m_ready_time = startTime.AddMinutes(-5), - m_is_send_noti = false, - m_ffa_hot_time = attrib.m_ffa_hot_time, - m_round_count = attrib.m_round_count - }; - events.Add(new_event); - + var battle_event = makeSystemBattleEvent(attrib, startTime, endTime); + if(BattleInstanceManager.It.existSystemBattleEvent(battle_event.m_event_id)) return events; + events.Add(battle_event); return events; } @@ -336,26 +278,14 @@ public class BattleEventCheckTicker : EntityTicker DateTime eventTime = today.AddDays(daysUntilNext).Date .AddHours(attrib.m_start_hour) .AddMinutes(attrib.m_start_min); - - if (eventTime >= now)// && eventTime < now.AddDays(1)) + if (eventTime >= now) { if (attrib.m_end_date <= now) return events; - - events.Add(new SystemBattleEvent - { - m_event_id = attrib.m_event_id, - m_instance_id = attrib.m_instance_id, - m_start_time = eventTime, - m_ffa_config_data_id = attrib.m_ffa_config_data_id, - m_ffa_reward_group_id = attrib.m_ffa_reward_group_id, - m_ready_time = eventTime.AddMinutes(-5), - m_is_send_noti = false, - m_ffa_hot_time = attrib.m_ffa_hot_time, - m_round_count = attrib.m_round_count - }); + var ev = makeSystemBattleEvent(attrib, eventTime, eventTime.AddMinutes(attrib.m_open_duration_minutes)); + if (BattleInstanceManager.It.existSystemBattleEvent(ev.m_event_id)) continue; + events.Add(ev); } } - return events; } @@ -367,24 +297,34 @@ public class BattleEventCheckTicker : EntityTicker attrib.m_start_hour, attrib.m_start_min, 0 ); - if (monthlyTime >= now)// && monthlyTime < now.AddDays(1)) + if (monthlyTime >= now) { if (attrib.m_end_date <= now) return events; - events.Add(new SystemBattleEvent - { - - m_event_id = attrib.m_event_id, - m_instance_id = attrib.m_instance_id, - m_start_time = monthlyTime, - m_ffa_config_data_id = attrib.m_ffa_config_data_id, - m_ffa_reward_group_id = attrib.m_ffa_reward_group_id, - m_ready_time = monthlyTime.AddMinutes(-5), - m_is_send_noti = false, - m_ffa_hot_time = attrib.m_ffa_hot_time, - m_round_count = attrib.m_round_count - }); + var ev = makeSystemBattleEvent(attrib, monthlyTime, monthlyTime.AddMinutes(attrib.m_open_duration_minutes)); + if (BattleInstanceManager.It.existSystemBattleEvent(ev.m_event_id)) return events; + events.Add(ev); } return events; } + + private SystemBattleEvent makeSystemBattleEvent(BattleEventAttrib attrib, DateTime startTime, DateTime endTime) + { + var system_battle_event = new SystemBattleEvent + { + m_event_id = attrib.m_event_id, + m_game_mode_id = attrib.m_game_mode_id, + m_instance_id = attrib.m_instance_id, + m_start_time = startTime, + m_end_time = endTime, + m_ffa_config_data_id = attrib.m_ffa_config_data_id, + m_ffa_reward_group_id = attrib.m_ffa_reward_group_id, + m_ready_time = startTime.AddMinutes(-5), + m_is_send_noti = false, + m_ffa_hot_time = attrib.m_ffa_hot_time, + m_round_count = attrib.m_round_count + }; + + return system_battle_event; + } } diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Tickers/BattleEventNotifyTicker.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Tickers/BattleEventNotifyTicker.cs index 64bf04f..dabed5e 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Tickers/BattleEventNotifyTicker.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeFreeForAll/Tickers/BattleEventNotifyTicker.cs @@ -14,7 +14,8 @@ public class BattleEventNotifyTicker : EntityTicker public override async Task onTaskTick() { - await notifyBattleEvent(); + await Task.CompletedTask;//kihoon : ms8 1개만 보낼경우 클라에서 정상적으로 표시 안되는 이슈 있다고 해서 아예 안보내는 것으로 처리 + //await notifyBattleEvent(); } private async Task notifyBattleEvent() diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/GameModeTPSTeamDeathMatch.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/GameModeTPSTeamDeathMatch.cs index 48a8d7c..77c4b25 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/GameModeTPSTeamDeathMatch.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/GameModeTPSTeamDeathMatch.cs @@ -1,19 +1,55 @@ using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.Manage; +using MetaAssets; +using ServerCore; namespace GameServer.Contents.GameMode.Mode_Battle.Manage; -public class GameModeTPSTeamDeathMatch : GameModeTPS where T : ITPSMode +public class GameModeTPSTeamDeathMatch// : GameModeBase //where T : ITPSMode { - T m_tps_mode_data; + //T m_tps_mode_data; - public GameModeTPSTeamDeathMatch(T tpsModeData, InstanceRoom instanceRoom) : base(EntityType.GameModeTpsteamDeathMatch, instanceRoom) - { - m_tps_mode_data = tpsModeData; - } - - public override string toBasicString() - { - var basic_string = base.toBasicString() + $"GameModeTPSTeamDeathMatch...."; - return basic_string; - } + // public GameModeTPSTeamDeathMatch(InstanceRoom instanceRoom, int gameModeId) + // : base(EntityType.GameModeTpsteamDeathMatch, instanceRoom, gameModeId, new RunAdventureTransitionStrategy()) + // { + // } + // + // protected override void addDetailEntityActions() + // { + // return; + // } + // + // protected override Task deatilTaskUpdate() + // { + // var result = new Result(); + // return Task.FromResult(result); + // } + // + // protected override Task detailInitBeforeTaskCreate() + // { + // var result = new Result(); + // return Task.FromResult(result); + // } + // + // protected override Task detailInitAfterTaskCreate() + // { + // var result = new Result(); + // return Task.FromResult(result); + // } + // + // protected override async Task setDetailGameInfoAfterLoadComplete() + // { + // var result = new Result(); + // m_game_create_time = DateTimeHelper.Current; + // + // await Task.CompletedTask; + // return result; + // } + // + // + // public override string toBasicString() + // { + // var basic_string = base.toBasicString() + $"GameModeTPSTeamDeathMatch...."; + // return basic_string; + // } } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchDestroyHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchDestroyHandler.cs index c9eb7a5..1a732d3 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchDestroyHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchDestroyHandler.cs @@ -1,5 +1,6 @@ using GameServer.Contents.GameMode.Manage.LeaveManage; using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; using ServerCommon; namespace GameServer.Contents.GameMode.Mode_Battle.Manage; diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchInitHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchInitHandler.cs index 216f22d..69f8942 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchInitHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchInitHandler.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Manage; +using MetaAssets; using ServerCommon; namespace GameServer.Contents.GameMode.Mode_Battle.Manage; diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchJoinHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchJoinHandler.cs index 175137e..5b3c454 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchJoinHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchJoinHandler.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Manage; +using MetaAssets; using ServerCommon; using ServerCore; diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchJoinSuccessHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchJoinSuccessHandler.cs index f982658..82e7447 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchJoinSuccessHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchJoinSuccessHandler.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Manage; +using MetaAssets; using ServerCommon; using ServerCore; diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchLeaveHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchLeaveHandler.cs index 952c124..71f0ff5 100644 --- a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchLeaveHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchLeaveHandler.cs @@ -1,12 +1,15 @@ using GameServer.Contents.GameMode.Manage.LeaveManage; using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; using ServerCommon; namespace GameServer.Contents.GameMode.Mode_Battle.Manage; public class TPSTeamDeathMatchLeaveHandler : GameModeLeaveHandlerBase { - public TPSTeamDeathMatchLeaveHandler(Player player, string roomId) : base(player, roomId, GameModeType.TPS_TDM) + public TPSTeamDeathMatchLeaveHandler(IGameMode gameMode, Player player, ServerInfo serverInfo, List invokers, string roomId) + : base(gameMode, player, serverInfo, invokers, roomId, GameModeType.TPS_TDM) { } diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchTransitionStrategy.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchTransitionStrategy.cs new file mode 100644 index 0000000..646a3f9 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TPSTeamDeathMatchTransitionStrategy.cs @@ -0,0 +1,11 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; + +namespace GameServer.Contents.GameMode.Mode_Battle.Manage; + +public class TPSTeamDeathMatchTransitionStrategy: IGameModeTransitionStrategy +{ + public IGameModeState? tryGetNextState(IGameModeState currentState, IGameMode gameMode) + { + return null; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TpsTeamDeathMatchPreparationForLeavingHandler.cs b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TpsTeamDeathMatchPreparationForLeavingHandler.cs new file mode 100644 index 0000000..c6aa97d --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Battle/ModeTeamDeathMatch/Manage/TpsTeamDeathMatchPreparationForLeavingHandler.cs @@ -0,0 +1,29 @@ +using GameServer.Contents.GameMode.Manage.LeaveManage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; + +namespace GameServer.Contents.GameMode.Mode_Battle.Manage; + +public class TpsTeamDeathMatchPreparationForLeavingHandler : PreparationForLeavingGameBase +{ + public TpsTeamDeathMatchPreparationForLeavingHandler(Player player, IGameMode gameMode) : base(player, gameMode) + { + + } + + public override async Task preparation() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + + public override async Task postPreparationForLeaving() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/Manage/GameModeRun.cs b/GameServer/Contents/GameMode/Mode-Running/Manage/GameModeRun.cs index b626ada..26fe7d5 100644 --- a/GameServer/Contents/GameMode/Mode-Running/Manage/GameModeRun.cs +++ b/GameServer/Contents/GameMode/Mode-Running/Manage/GameModeRun.cs @@ -1,40 +1,17 @@ using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; using ServerCommon; using ServerCore; namespace GameServer.Contents.GameMode.Mode_Running.Manage; -public class GameModeRun : GameModeBase, IGameModeGenre +public abstract class GameModeRun : GameModeBase, IGameModeGenre { - public GameModeRun(EntityType type, InstanceRoom instanceRoom) : base(type, instanceRoom) + public GameModeRun(EntityType type, InstanceRoom instanceRoom, int gameModeId, IGameModeTransitionStrategy strategy) + : base(type, instanceRoom, gameModeId, strategy) { } - public override Task onInit() - { - return base.onInit(); - } - - public override Task taskUpdate() - { - Log.getLogger().debug("run race taskUpdate called"); - - - Log.getLogger().debug("run race taskUpdate done"); - - return Task.CompletedTask; - } - - public override string toBasicString() - { - var basic_string = base.toBasicString() + $"GameModeRun...."; - return basic_string; - } - - public override Task initAfterTimerCreate() - { - m_current_game_mode_state = new RaceStateReady(this); - return Task.CompletedTask; - } + public override string toBasicString() => base.toBasicString() + $"GameModeRun...."; } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/GameModeRunAdventure.cs b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/GameModeRunAdventure.cs index 6cda07e..2934175 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/GameModeRunAdventure.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/GameModeRunAdventure.cs @@ -1,18 +1,58 @@ using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerCommon; +using ServerCore; namespace GameServer.Contents.GameMode.Mode_Running.Manage; -public class GameModeRunAdventure : GameModeRun where T : IRunningMode +public class GameModeRunAdventure : GameModeBase //where T : IRunningMode { - T m_run_mode_data; - public GameModeRunAdventure(T runModeData, InstanceRoom instanceRoom) : base(EntityType.GameModeRunAdventure, instanceRoom) + //T m_run_mode_data; + public GameModeRunAdventure(InstanceRoom instanceRoom, int gameModeId) + : base(EntityType.GameModeRunAdventure, instanceRoom, gameModeId, new RunAdventureTransitionStrategy()) { - m_run_mode_data = runModeData; + //m_run_mode_data = runModeData; } - + + protected override void addDetailEntityActions() + { + return; + } + + protected override Task deatilTaskUpdate() + { + var result = new Result(); + return Task.FromResult(result); + } + + protected override Task detailInitBeforeTaskCreate() + { + var result = new Result(); + return Task.FromResult(result); + } + + protected override Task detailInitAfterTaskCreate() + { + var result = new Result(); + return Task.FromResult(result); + } + + protected override async Task setDetailGameInfoAfterLoadComplete() + { + var result = new Result(); + m_game_create_time = DateTimeHelper.Current; + + await Task.CompletedTask; + return result; + } + public override string toBasicString() { var basic_string = base.toBasicString() + $"GameModeRunAdventure...."; return basic_string; } + + protected override void setDetailAnchorInfos(string anchorGuid, Anchor anchor) + { + } } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureDestroyHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureDestroyHandler.cs index c722392..43a981c 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureDestroyHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureDestroyHandler.cs @@ -1,5 +1,6 @@ using GameServer.Contents.GameMode.Manage.LeaveManage; using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; using ServerCommon; namespace GameServer.Contents.GameMode.Mode_Running.Manage; diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureInitHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureInitHandler.cs index 9a35547..2d3471a 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureInitHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureInitHandler.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Manage; +using MetaAssets; using ServerCommon; namespace GameServer.Contents.GameMode.Mode_Running.Manage; diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureJoinHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureJoinHandler.cs index c252fe3..9a7459c 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureJoinHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureJoinHandler.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Manage; +using MetaAssets; using ServerCommon; using ServerCore; diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureJoinSuccessHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureJoinSuccessHandler.cs index 2b984e8..64f1bd7 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureJoinSuccessHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureJoinSuccessHandler.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Manage; +using MetaAssets; using ServerCommon; namespace GameServer.Contents.GameMode.Mode_Running.Manage; diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureLeaveHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureLeaveHandler.cs index 8a05dd6..65d7fff 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureLeaveHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureLeaveHandler.cs @@ -1,12 +1,15 @@ using GameServer.Contents.GameMode.Manage.LeaveManage; using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; using ServerCommon; namespace GameServer.Contents.GameMode.Mode_Running.Manage; public class RunAdventureLeaveHandler : GameModeLeaveHandlerBase { - public RunAdventureLeaveHandler(Player player, string roomId) : base(player, roomId, GameModeType.RUN_ADV) + public RunAdventureLeaveHandler(IGameMode gameMode, Player player, ServerInfo serverInfo, List invokers, string roomId) + : base(gameMode, player, serverInfo, invokers, roomId, GameModeType.RUN_ADV) { } diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventurePreparationForLeavingHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventurePreparationForLeavingHandler.cs new file mode 100644 index 0000000..a908d3c --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventurePreparationForLeavingHandler.cs @@ -0,0 +1,29 @@ +using GameServer.Contents.GameMode.Manage.LeaveManage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; + +namespace GameServer.Contents.GameMode.Mode_Running.Manage; + +public class RunAdventurePreparationForLeavingHandler : PreparationForLeavingGameBase +{ + public RunAdventurePreparationForLeavingHandler(Player player, IGameMode gameMode) : base(player, gameMode) + { + + } + + public override async Task preparation() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + + public override async Task postPreparationForLeaving() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureTransitionStrategy.cs b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureTransitionStrategy.cs new file mode 100644 index 0000000..8cf13e2 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeAdventure/Manage/RunAdventureTransitionStrategy.cs @@ -0,0 +1,12 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; + +namespace GameServer.Contents.GameMode.Mode_Running.Manage; + +public class RunAdventureTransitionStrategy : IGameModeTransitionStrategy +{ + public IGameModeState? tryGetNextState(IGameModeState currentState, IGameMode gameMode) + { + return null; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeBuffInteractAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeBuffInteractAction.cs new file mode 100644 index 0000000..03fb5f2 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeBuffInteractAction.cs @@ -0,0 +1,108 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeBuffInteractAction : GameModeObjectInteractionHandlerActionBase +{ + public RunPracticeBuffInteractAction(EntityBase owner) + : base(owner, EGameModeObjectType.RunningBuff, false, false) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public override Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + Result result = new Result(); + + return Task.FromResult(result); + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + + return; + } + + public override Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var result = new Result(); + var err_msg = string.Empty; + + var run_practice = getOwner() as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"run_practice is null !!!"); + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var buff_data_handler = handler as RunPracticeBuffInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(buff_data_handler, () => $"buff_data_handler is null !!!"); + var anchor_guid = buff_data_handler.m_interaction_anchor_guid; + + (result, var meta) = GameModeHelper.getGameModeObjectMeta(anchor_guid, game_mode_base); + if(result.isFail()) return Task.FromResult(result); + + var room_id = run_practice.getInstanceRoom().getMap().m_room_id; + if (false == run_practice.m_running_buffs.TryGetValue(anchor_guid, out var gameObjectBuff)) + { + + gameObjectBuff = new GameModeObject(meta.ObjectType, anchor_guid); + run_practice.m_running_buffs.AddOrUpdate(anchor_guid, gameObjectBuff, (key, val) => gameObjectBuff); + } + + if (false == gameObjectBuff.m_is_active) + { + err_msg = $"gameObjectBuff m_is_active false!!! room_id : {room_id}, anchor_guid : {anchor_guid}, player : {player.toBasicString()}, gameObject : {JsonConvert.SerializeObject(gameObjectBuff)}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_msg); + return Task.FromResult(result); + } + + buff_data_handler.m_need_noti_objects.Add((GameModeObject)gameObjectBuff); + + + if (meta.RespawnTime > 0) + { + gameObjectBuff.m_is_active = false; + gameObjectBuff.m_active_time = DateTimeHelper.Current.AddSeconds(meta.RespawnTime); + } + run_practice.m_running_buffs.AddOrUpdate(anchor_guid, gameObjectBuff, (key, val) => gameObjectBuff); + + + return Task.FromResult(new Result()); + } + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + var buff_data_handler = handler as RunPracticeBuffInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(buff_data_handler, () => $"RunPracticeBuffInteractionDataHandler is null !!!"); + + var log_info = buff_data_handler.m_interaction_log_info as RunPracticeBuffInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"RunPracticeBuffInteractionLogInfo is null !!!"); + + return new RunPracticeBuffInteractionBuisinessLog(log_info); + } + + public override string toBasicString() + { + return $"class name : {nameof(RunPracticeCheckPointInteractAction)}...." + base.toBasicString(); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeBuffInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeBuffInteractionDataHandler.cs new file mode 100644 index 0000000..02956f2 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeBuffInteractionDataHandler.cs @@ -0,0 +1,12 @@ +using ServerCommon; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeBuffInteractionDataHandler : GameObjectInteractionDataCommonHandlerBase +{ + public RunPracticeBuffInteractionDataHandler(string userGuid, string userNickname, string interactionAnchorGuid, int tableId, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, tableId, interactionTime) + { + m_interaction_log_info = new RunPracticeBuffInteractionLogInfo(roomId, userGuid, userNickname, interactionAnchorGuid, tableId); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeCheckPointInteractAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeCheckPointInteractAction.cs new file mode 100644 index 0000000..7314195 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeCheckPointInteractAction.cs @@ -0,0 +1,66 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.InteractionObject; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeCheckPointInteractAction : GameModeObjectInteractionHandlerActionBase +{ + public RunPracticeCheckPointInteractAction(EntityBase owner) : + base(owner, EGameModeObjectType.CheckPoint, false, false) + { + + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public override Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + return Task.FromResult(new Result()); + } + + + + public override Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + //check point push 기능 넣지 않느다. + //check abusing 필요한가? + + return Task.FromResult(new Result()); + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + return; + } + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + var checkpoint_data_handler = handler as RunPracticeCheckPointInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(checkpoint_data_handler, () => $"checkpoint_data_handler is null !!!"); + + var log_info = checkpoint_data_handler.m_interaction_log_info as RunPracticeCheckPointInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaCombatPodInteractionLogInfo is null !!!"); + + return new RunPracticeCheckPointInteractionBuisinessLog(log_info); + } + + public override string toBasicString() + { + return $"class name : {nameof(RunPracticeCheckPointInteractAction)}...." + base.toBasicString(); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeCheckPointInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeCheckPointInteractionDataHandler.cs new file mode 100644 index 0000000..ea75b44 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeCheckPointInteractionDataHandler.cs @@ -0,0 +1,12 @@ +using ServerCommon; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeCheckPointInteractionDataHandler : GameObjectInteractionDataCommonHandlerBase +{ + public RunPracticeCheckPointInteractionDataHandler(string userGuid, string nickName, string interactionAnchorGuid, int tableId, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, tableId, interactionTime) + { + m_interaction_log_info = new RunPracticeCheckPointInteractionLogInfo(roomId, userGuid, nickName, interactionAnchorGuid, tableId); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeDeadAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeDeadAction.cs new file mode 100644 index 0000000..7a8ddd9 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeDeadAction.cs @@ -0,0 +1,35 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using ServerBase; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeDeadAction : EntityActionBase, IDeadHandler +{ + public RunPracticeDeadAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public async Task dead(Player player, string deadUserGuid, string killerUserGuid, GameModeDeadType deadType, DateTime deadTime) + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public void notifyAfterDead(Player player) + { + return; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeGoalLineInteractionAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeGoalLineInteractionAction.cs new file mode 100644 index 0000000..3ab95b8 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeGoalLineInteractionAction.cs @@ -0,0 +1,77 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeGoalLineInteractionAction : GameModeObjectInteractionHandlerActionBase +{ + public RunPracticeGoalLineInteractionAction(EntityBase owner) : base(owner, EGameModeObjectType.Goal, false, true) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public override Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode is null !!!"); + var state = game_mode_base.getState(); + var state_type = state.getStateType(); + var result = new Result(); + if (state_type != GameModeState.Play) + { + var err_msg = $"goaline interaction allowed only play state!!!! currentState : {state_type}, gameModeId : {game_mode_base.getGameModeId()}, " + + $"roomId : {game_mode_base.getInstanceRoom().getMap().m_room_id}, player : {player.toBasicString()}"; + result.setFail(ServerErrorCode.GameModeInvalidAction, err_msg);; + Log.getLogger().error(result.toBasicString()); + return Task.FromResult(result); + } + + NullReferenceCheckHelper.throwIfNull(state, () => $"state is null !!!"); + + return Task.FromResult(new Result()); + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + } + + public override Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode is null !!!"); + + var play_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + play_action.updateGoalLine(); + + return Task.FromResult(new Result()); + } + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + var goal_line_data_handler = handler as RunPracticeGoalLineInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(goal_line_data_handler, () => $"RunPracticeGoalLineInteractionDataHandler is null !!!"); + + var log_info = goal_line_data_handler.m_interaction_log_info as RunPracticeGoalLineInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"RunPracticeGoalLineInteractionLogInfo is null !!!"); + + return new RunPracticeGoalLineInteractionBuisinessLog(log_info); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeGoalLineInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeGoalLineInteractionDataHandler.cs new file mode 100644 index 0000000..381755f --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeGoalLineInteractionDataHandler.cs @@ -0,0 +1,12 @@ +using ServerCommon; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeGoalLineInteractionDataHandler : GameObjectInteractionDataCommonHandlerBase +{ + public RunPracticeGoalLineInteractionDataHandler(string userGuid, string userNickname, string interactionAnchorGuid, int tableId, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, tableId, interactionTime) + { + m_interaction_log_info = new RunPracticeGoalLineInteractionLogInfo(roomId, userGuid, userNickname, interactionAnchorGuid, tableId); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeLoadCompleteAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeLoadCompleteAction.cs new file mode 100644 index 0000000..eef0605 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeLoadCompleteAction.cs @@ -0,0 +1,44 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeLoadCompleteAction : EntityActionBase, IGameModeLoadCompleteHandler +{ + public RunPracticeLoadCompleteAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public async Task loadComplete(Player player) + { + await Task.CompletedTask; + + var run_practice = getOwner() as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"game_moce_base is null !!!"); + //todo : updateUserState 호출 안함.. 나중에 필요할경우 추가 할것.. + + //var state_base = run_practice.getState() as GameModeStateBase; + //NullReferenceCheckHelper.throwIfNull(state_base, () => $"state_base is null !!!"); + //state_base.setStateTime(DateTimeHelper.Current, "LoadComplete"); + + var play_action = run_practice.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + play_action.loadingComplete(); + + return new Result(); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeObjectUpdateAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeObjectUpdateAction.cs new file mode 100644 index 0000000..c43c01f --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeObjectUpdateAction.cs @@ -0,0 +1,80 @@ +using GameServer.Contents.GameMode.Helper; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeObjectUpdateAction : EntityActionBase +{ + public RunPracticeObjectUpdateAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public async Task updateObject() + { + await Task.CompletedTask; + + var result = await updateBuff(); + if (result.isFail()) return result; + + return result; + } + + private async Task updateBuff() + { + await Task.CompletedTask; + + var run_practice = getOwner() as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"run_practice is null !!!"); + + var now = DateTimeHelper.Current; + var infos = new List(); + List active_buff_guids = new(); + + foreach (var buff in run_practice.m_running_buffs.Values) + { + if (buff.m_active_time <= now && buff.m_is_active == false) + { + active_buff_guids.Add(buff.m_anchor_guid); + } + } + await updateAndNoti(run_practice, active_buff_guids); + + return new(); + } + + private async Task updateAndNoti(GameModeRunPractice gameMode, List activeBuffs) + { + if (activeBuffs.Count == 0) return; + + List infos = new(); + using (var releaser = await gameMode.getAsyncLock()) + { + foreach (var guid in activeBuffs) + { + if (gameMode.m_running_buffs.TryGetValue(guid, out var buff)) + { + var info = new GameModeObjectInfo(); + buff.m_is_active = true; + info.AnchorGuid = buff.m_anchor_guid; + info.IsActive = buff.m_is_active ? BoolType.True : BoolType.False; + infos.Add(info); + } + } + } + + GameNotifyHelper.broadcast_GS2C_NTF_GAME_MODE_OBJECT_INFO(gameMode, infos); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticePlayAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticePlayAction.cs new file mode 100644 index 0000000..b901b03 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticePlayAction.cs @@ -0,0 +1,64 @@ +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticePlayAction : EntityActionBase +{ + private bool m_is_play_done = false; + public Int32 m_respawn_count { get; private set; } = 0; + private bool m_is_loading_complete = false; + public DateTime m_start_time { get; private set; } = DateTime.MinValue; + public DateTime m_end_time { get; private set;} = DateTime.MinValue; + + public RunPracticePlayAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + } + + public bool isPlayDone() + { + return m_is_play_done; + } + + public void updateGoalLine() + { + if (m_is_play_done) + { + Log.getLogger().warn("m_is_play_done already true..."); + return; + } + m_is_play_done = true; + m_end_time = DateTimeHelper.Current; + } + + public void increaseRespawnCount() + { + m_respawn_count++; + } + + public bool isLoadingComplete() + { + return m_is_loading_complete; + } + + public void loadingComplete() + { + m_is_loading_complete = true; + } + + public void updateStartTime(DateTime now) + { + m_start_time = now; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeReadyPosAssignAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeReadyPosAssignAction.cs new file mode 100644 index 0000000..995cf0e --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeReadyPosAssignAction.cs @@ -0,0 +1,6 @@ +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeReadyPosAssignAction +{ + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeRespawnAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeRespawnAction.cs new file mode 100644 index 0000000..5e9b2ee --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Actions/RunPracticeRespawnAction.cs @@ -0,0 +1,45 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; + +public class RunPracticeRespawnAction : EntityActionBase, IRespawnHandler +{ + public RunPracticeRespawnAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public Task respawn(Player player, DateTime deadTime) + { + var result = new Result(); + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var play_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + + play_action.increaseRespawnCount(); + + return Task.FromResult(result); + + } + + public void notifyAfterRespawn(Player player) + { + return; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Helper/RunPracticeHelper.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Helper/RunPracticeHelper.cs new file mode 100644 index 0000000..0e50ae2 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Helper/RunPracticeHelper.cs @@ -0,0 +1,61 @@ +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using MetaAssets; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Helper; + +public static class RunPracticeHelper +{ + public static IGameObjectInteractionHandler? getRunPracticeInteractAction(GameModeBase gameModeBase, EGameModeObjectType objectType) + { + switch (objectType) + { + case EGameModeObjectType.CheckPoint: + return gameModeBase.getEntityAction(); + case EGameModeObjectType.Goal: + return gameModeBase.getEntityAction(); + case EGameModeObjectType.RunningBuff: + return gameModeBase.getEntityAction(); + default: + return null; + } + } + public static IRespawnHandler? getRespawnAction(GameModeBase gameModeBase) + { + return gameModeBase.getEntityAction(); + } + public static IGameModeLoadCompleteHandler getLoadCompleteAction(GameModeBase gameModeBase) + { + var handler = gameModeBase.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(handler, () => $"RunPracticeLoadCompleteAction handler is null !!!"); + return handler; + } + + public static IDeadHandler? getDeadAction(GameModeBase gameModeBase) + { + return gameModeBase.getEntityAction(); + } + + public static IGameObjectInteractionDataHandler? getRunPracticeInteractDataHandler(Player player, EGameModeObjectType objectType, string anchorGuid, int tableId, string roomId, DateTime interactionTime) + { + switch (objectType) + { + case EGameModeObjectType.CheckPoint: + + return new RunPracticeCheckPointInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, tableId, roomId, interactionTime); + case EGameModeObjectType.RunningBuff: + return new RunPracticeBuffInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, tableId, roomId, interactionTime); + case EGameModeObjectType.Goal: + return new RunPracticeGoalLineInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, tableId, roomId, interactionTime); + default: + Log.getLogger().error($"getRunPracticeInteractDataHandler objectType error !!!, anchorGuid : {anchorGuid}, objectType : {objectType}, roomId : {roomId}, player : {player.toBasicString()}"); + return new RunPracticeCheckPointInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, tableId, roomId, interactionTime); + } + } + + + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/GameModeRunPractice.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/GameModeRunPractice.cs new file mode 100644 index 0000000..a45ebb9 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/GameModeRunPractice.cs @@ -0,0 +1,143 @@ +using System.Collections.Concurrent; +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; +using GameServer.Contents.GameMode.Mode_Running.ModePractice.Meta; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCore; +using ANCHOR_GUID = System.String; +namespace GameServer; + +public class GameModeRunPractice : GameModeBase +{ + public RunPracticeMetaData m_run_practice_meta { get; } + public ConcurrentDictionary m_running_buffs = new(); + + public List> m_check_points { get; private set; } = new(); + + public GameModeRunPractice(InstanceRoom instanceRoom, int gameModeId) + : base(EntityType.GameModeRunPractice, instanceRoom, gameModeId, new RunPracticeTransitionStrategy()) + { + m_run_practice_meta = new RunPracticeMetaData(gameModeId); + } + + public override Task onInit() + { + Log.getLogger().debug("run practice onInit called"); + + //게임 모드에 필요한 상수값 입력 + setDefaultMetaConstants(); + + //다 마무리 되면 부모 init 호출 + var result = base.onInit(); + + + Log.getLogger().debug("run practice onInit done"); + return result; + } + + protected override void addDetailEntityActions() + { + addEntityAction(new RunPracticePlayAction(this)); + addEntityAction(new RunPracticeCheckPointInteractAction(this)); + addEntityAction(new RunPracticeBuffInteractAction(this)); + addEntityAction(new RunPracticeGoalLineInteractionAction(this)); + addEntityAction(new RunPracticeDeadAction(this)); + addEntityAction(new RunPracticeRespawnAction(this)); + + + + addEntityAction(new RunPracticeObjectUpdateAction(this)); + addEntityAction(new RunPracticeLoadCompleteAction(this)); + + } + + protected override async Task deatilTaskUpdate() + { + var object_update_action = getEntityAction(); + NullReferenceCheckHelper.throwIfNull(object_update_action, () => $"object_update_action is null !!"); + var result = await object_update_action.updateObject(); + if (result.isFail()) return result; + return result; + + } + + + + + + protected override void setDetailAnchorInfos(string anchorGuid, Anchor anchor) + { + //todo : 위로 올릴 만한 로직이다... + if (false == MetaData.Instance._BattleObjectMetaTable.TryGetValue(anchor.TableID, out var tableMeta)) + { + return; + } + + addCheckPoint(tableMeta, anchor); + } + private void addCheckPoint(BattleObjectMetaData tableMeta, Anchor anchor) + { + //check Point + if (tableMeta.ObjectType != EGameModeObjectType.CheckPoint || !anchor.HurdleValues.Contains("CheckpointID")) + { + return; + } + + var arr_hurdle_values = anchor.HurdleValues.Split(" "); + if (arr_hurdle_values.Length < 2) return; + + if (false == Int32.TryParse(arr_hurdle_values[1], out var checkpoint_id)) + { + Log.getLogger().error($"anchor.HurdleValues parse error tableMeta.ObjectType error!!!! objectType : {tableMeta.ObjectType}, anchorGuid : {anchor.GUID}"); + return; + } + RunRaceCheckPointValue check_point_value = new RunRaceCheckPointValue(checkpoint_id, anchor.TableID); + addCheckPoint(anchor.GUID, check_point_value); + + + } + + private void addCheckPoint(string anchorGuid, RunRaceCheckPointValue checkPoint) + { + m_check_points.Add(new KeyValuePair(anchorGuid, checkPoint)); + m_check_points = m_check_points.OrderBy(kv => kv.Value.m_check_point_id).ToList(); + } + + + + + + //=========================================================================================================================================================== + // 현재 모드에서 미사용 + //=========================================================================================================================================================== + protected override async Task detailInitAfterTaskCreate() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + + private void setDefaultMetaConstants() + { + m_ticker_interval_msecs = GameModeConstants.GAME_MODE_RUN_RACE_CHECK_INTERVAL_MSECS; + } + + protected override Task detailInitBeforeTaskCreate() + { + var result = new Result(); + return Task.FromResult(result); + } + + protected override async Task setDetailGameInfoAfterLoadComplete() + { + var result = new Result(); + await Task.CompletedTask; + return result; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeDestroyHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeDestroyHandler.cs new file mode 100644 index 0000000..00df873 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeDestroyHandler.cs @@ -0,0 +1,21 @@ +using GameServer.Contents.GameMode.Manage.LeaveManage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; + +namespace GameServer; + +public class RunPracticeDestroyHandler : GameModeDestroyHandlerBase +{ + public RunPracticeDestroyHandler(string roomId) : base(roomId, GameModeType.RUN_PRACTICE) + { + + } + + public override async Task postDestroy(IGameMode gameMode) + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeDisconnectHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeDisconnectHandler.cs new file mode 100644 index 0000000..350939e --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeDisconnectHandler.cs @@ -0,0 +1,32 @@ +using GameServer.Contents.GameMode.Manage.LeaveManage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerCore; + +namespace GameServer; + +public class RunPracticeDisconnectHandler : GameModeDisconnectHandlerBase +{ + public RunPracticeDisconnectHandler(IGameMode gameMode, Player player) : base(GameModeType.RUN_PRACTICE, gameMode, player) + { + } + + public override async Task disconnect() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + + public override Task notifyAfterDisconnect() + { + return Task.FromResult(new Result()); + } + + public override Task logAfterDisconnect() + { + var result = new Result(); + return Task.FromResult(result); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeInitHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeInitHandler.cs new file mode 100644 index 0000000..66be509 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeInitHandler.cs @@ -0,0 +1,17 @@ +using MetaAssets; + +namespace GameServer; + +public class RunPracticeInitHandler : GameModeInitHandlerBase +{ + public RunPracticeInitHandler(InstanceRoom instanceRoom) : base(instanceRoom, GameModeType.RUN_PRACTICE) + { + } + + public override Result gamedModeInstanceInitValidate() + { + var result = new Result(); + + return result; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeJoinHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeJoinHandler.cs new file mode 100644 index 0000000..7530f93 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeJoinHandler.cs @@ -0,0 +1,31 @@ +using GameServer.Contents.GameMode.Manage; +using MetaAssets; +using ServerCore; + +namespace GameServer; + +public class RunPracticeJoinHandler : GameModeJoinHandlerBase +{ + public RunPracticeJoinHandler(InstanceRoom instanceRoom) : base(instanceRoom, GameModeType.RUN_PRACTICE) + { + + } + + public override Result gamedModeInstanceJoinValidate() + { + var result = new Result(); + return result; + } + + public override Result gamedModeInstanceJoin(Player player) + { + Log.getLogger().debug("run practice gamedModeInstanceJoin called"); + + var result = new Result(); + string err_msg = string.Empty; + + + Log.getLogger().debug("run practice gamedModeInstanceJoin done"); + return result; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeJoinSuccessHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeJoinSuccessHandler.cs new file mode 100644 index 0000000..5cbf184 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeJoinSuccessHandler.cs @@ -0,0 +1,103 @@ +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using MetaAssets; +using ServerBase; +using ServerCore; + +namespace GameServer; + +public class RunPracticeJoinSuccessHandler : GameModeJoinSuccessHandlerBase +{ + public RunPracticeJoinSuccessHandler(Player player, InstanceRoom instanceRoom) : base(player, GameModeType.RUN_PRACTICE, instanceRoom) + { + } + public override Result joinSuccessValidate() + { + var result = new Result(); + + return result; + } + + public override Task joinSuccessConfirmation() + { + var result = new Result(); + setStartPos(); + return Task.FromResult(result); + } + + public override async Task joinSuccessNotify() + { + var result = new Result(); + + var room_id = m_instance_room.getMap().m_room_id; + + var err_msg = string.Empty; + if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + { + err_msg = $"not exist GameMode after join success!!!! roomId : {room_id}"; + + result.setFail(ServerErrorCode.GameModeClassIsNull, err_msg); + Log.getLogger().error(err_msg); + return result; + } + + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var state = game_mode_base.getState(); + + var state_base = state as GameModeStateBase; + NullReferenceCheckHelper.throwIfNull(state_base, () => $"state_base is null !!!"); + + m_player.send_S2C_NTF_SET_LOCATION(); + + GameNotifyHelper.send_GS2C_NTF_GAME_MODE_OVERALL_INFO(m_player, game_mode_base); + GameNotifyHelper.send_GS2C_NTF_GAME_STATE_UPDATE(m_player, game_mode_base.getInstanceRoom(), state.getStateType(), state.getNextStateChangeTime(), state_base.getUpdateReason()); + + var host_user_guid = game_mode_base.m_host_migrator.getHostUserGuid(); + GameNotifyHelper.send_GS2C_NTF_P2P_HOST_UPDATE(m_player, host_user_guid); + + await Task.CompletedTask; + return result; + } + + public override void joinSuccessWriteLog() + { + + } + + private void setStartPos() + { + var room_id = m_instance_room.getMap().m_room_id; + + if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + { + var err_msg = $"not exist GameMode after join success setStartPos!!!! roomId : {room_id}"; + Log.getLogger().error(err_msg); + return ; + } + var run_practice = gameMode as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"GameModeRunRace is null !!! - {m_player.toBasicString()}"); + + foreach (var pos in run_practice.getStartPos()) + { + if (false == m_instance_room.getMap().getAnchors().TryGetValue(pos.Key, out var anchorInfo)) + { + var err_msg = $"setStartPos() anchorInfo not exist idx : {pos.Key}"; + Log.getLogger().error(err_msg); + continue; + } + var location_action = m_player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {m_player.toBasicString()}"); + var currenct_pos = location_action.getCurrentPos(); + + currenct_pos = anchorInfo.AnchorPos.Clone(); + currenct_pos.Z += 100; + location_action.tryUpdateCurrentPos(currenct_pos); + Log.getLogger().info($"{m_player.toBasicString()} setStartPos() - {pos.Key}"); + break; + } + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeLeaveHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeLeaveHandler.cs new file mode 100644 index 0000000..586fad3 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeLeaveHandler.cs @@ -0,0 +1,37 @@ +using GameServer.Contents.GameMode.Manage.LeaveManage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; + +namespace GameServer; + +public class RunPracticeLeaveHandler : GameModeLeaveHandlerBase +{ + public RunPracticeLeaveHandler(IGameMode gameMode, Player player, ServerInfo serverInfo, List invokers, string roomId) + : base(gameMode, player, serverInfo, invokers, roomId, GameModeType.RUN_PRACTICE) + { + } + + public override async Task postLeave(IGameMode gameMode) + { + var result = new Result(); + await Task.CompletedTask; + return result; + } + + public override async Task notifyAfterLeave(IGameMode gameMode) + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + + public override async Task logAfterLeave(IGameMode gameMode) + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticePreparationForLeavingHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticePreparationForLeavingHandler.cs new file mode 100644 index 0000000..d560702 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticePreparationForLeavingHandler.cs @@ -0,0 +1,29 @@ +using GameServer.Contents.GameMode.Manage.LeaveManage; +using GameServer.Contents.GameMode.Manage.PlayManage; + +namespace GameServer; + +public class RunPracticePreparationForLeavingHandler : PreparationForLeavingGameBase +{ + public RunPracticePreparationForLeavingHandler(Player player, IGameMode gameMode) : base(player, gameMode) + { + + } + + + public override async Task preparation() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + + public override async Task postPreparationForLeaving() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeTransitionStrategy.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeTransitionStrategy.cs new file mode 100644 index 0000000..94f8464 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Manage/RunPracticeTransitionStrategy.cs @@ -0,0 +1,141 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.Manage; +using GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; +using GameServer.Contents.GameMode.Mode_Running.ModePractice.State; +using GameServer.Contents.GameMode.State; +using Newtonsoft.Json; +using ServerCore; + +namespace GameServer; + +public class RunPracticeTransitionStrategy : GameModeTransitionStrategyBase +{ + public override IGameModeState? getNextStateByNone(IGameModeState currentState, IGameMode gameMode) + { + return new RunPracticeStateCreated(gameMode); + } + + public override IGameModeState? getNextStateByCreate(IGameModeState currentState, IGameMode gameMode) + { + var created_state = currentState as RunPracticeStateCreated; + NullReferenceCheckHelper.throwIfNull(created_state, () => $"create_state is null !!!"); + + var next_state_change_time = created_state.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (next_state_change_time < now) + { + return new GameModeStateLoadingWait(gameMode); + } + return null; + } + + public override IGameModeState? getNextStateByLoadingWait(IGameModeState currentState, IGameMode gameMode) + { + var run_practice = gameMode as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"run_practice is null !!!"); + + var play_action = run_practice.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + + if (play_action.isLoadingComplete()) + { + return new RunPracticeStateWait(gameMode); + } + + return null; + } + + public override IGameModeState? getNextStateByWait(IGameModeState currentState, IGameMode gameMode) + { + var wait_state = currentState as RunPracticeStateWait; + NullReferenceCheckHelper.throwIfNull(wait_state, () => $"run race wait_state is null !!!"); + + var next_state_change_time = wait_state.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + //시간 다 됐으면 ready로 변경 + if (next_state_change_time < now) + { + return new RunPracticeStateReady(gameMode); + } + return null; + } + + public override IGameModeState? getNextStateByReady(IGameModeState currentState, IGameMode gameMode) + { + var next_state_change_time = currentState.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + + return new RunPracticeStatePlay(gameMode); + } + + public override IGameModeState? getNextStateByPlay(IGameModeState currentState, IGameMode gameMode) + { + var run_practice = gameMode as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"run_practice is null !!!"); + + var play_action = run_practice.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + + var next_state_change_time = currentState.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (next_state_change_time <= now || play_action.isPlayDone()) + { + return new RunPracticeStateSummary(gameMode); + } + return null; + + } + + public override IGameModeState? getNextStateByRewardSummary(IGameModeState currentState, IGameMode gameMode) + { + var next_state_change_time = currentState.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + + return new RunPracticeStateResult(gameMode); + } + + + public override IGameModeState? getNextStateByResult(IGameModeState currentState, IGameMode gameMode) + { + var next_state_change_time = currentState.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + + return new RunPracticeStateDestroyed(gameMode);//여기는 뭘 넘겨야 하나? + } + + + //=========================================================================================================================================================== + // 현재 모드에서 미사용 + //=========================================================================================================================================================== + public override IGameModeState? getNextStateByEnd(IGameModeState currentState, IGameMode gameMode) + { + return null; + } + + public override IGameModeState? getNextStateByRounding(IGameModeState currentState, IGameMode gameMode) + { + return null; + } + + public override IGameModeState? getNextStateByRoundWait(IGameModeState currentState, IGameMode gameMode) + { + return null; + } + + public override IGameModeState? getNextStateByRoundEndAll(IGameModeState currentState, IGameMode gameMode) + { + return null; + } + + public override IGameModeState? getNextStateEventEnd(IGameModeState currentState, IGameMode gameMode) + { + return null; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/Meta/RunPracticeMetaData.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Meta/RunPracticeMetaData.cs new file mode 100644 index 0000000..b24f346 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/Meta/RunPracticeMetaData.cs @@ -0,0 +1,43 @@ +using ServerCommon; +using ServerCommon.GameMode; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.Meta; + +public class RunPracticeMetaData +{ + public readonly Int32 m_game_mode_id; + public int m_max_start_wait_time { get; private set; } = 20;//초 + public int m_result_scene_tracsition_wait_time { get; private set; } = 3; + + public int m_max_play_time { get; private set; } = 3600; + + public RunPracticeMetaData(Int32 game_mode_id) + { + m_game_mode_id = game_mode_id; + loadMetaDatas(m_game_mode_id); + } + + protected void loadMetaDatas(int gameModeId) + { + if(false == MetaData.Instance.m_game_mode_all_metas.TryGetValue(gameModeId, out var metaData)) + { + var err_msg = $"Not exist run_race_meta metaData Data gameModeId : {gameModeId}"; + Log.getLogger().error(err_msg); + NullReferenceCheckHelper.throwIfNull(metaData, () => $"metaData is null !!!"); + } + m_max_start_wait_time = metaData.m_common_data.MaxStartWaitTime; + + var run_race_meta = metaData.m_detail_meta as RunRaceMeta; + if (run_race_meta is null) + { + var err_msg = $"Not exist run_race_meta metaData Data gameModeId : {gameModeId}"; + Log.getLogger().error(err_msg); + return; + } + + m_result_scene_tracsition_wait_time = run_race_meta.m_result_scene_tracsition_wait_time; + + m_max_play_time = metaData.m_common_data.MaxPlayTime; + } +} diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateCreated.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateCreated.cs new file mode 100644 index 0000000..cd9cf54 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateCreated.cs @@ -0,0 +1,45 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.State; + +public class RunPracticeStateCreated : GameModeStateBase +{ + public RunPracticeStateCreated(IGameMode gameMode) : base(GameModeState.Created, gameMode) + { + } + + public override Task enterDetail() + { + extendStateTime(TimeSpan.FromSeconds(1), "RunPracticeStateCreated" ); + return Task.CompletedTask; + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + } + + public override Task postProcess() + { + return Task.CompletedTask; + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateDestroyed.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateDestroyed.cs new file mode 100644 index 0000000..26fa662 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateDestroyed.cs @@ -0,0 +1,47 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.State; + +public class RunPracticeStateDestroyed : GameModeStateBase +{ + public RunPracticeStateDestroyed(IGameMode gameMode, StateUpdateReasonType updateReason = StateUpdateReasonType.NormalFlow) + : base(GameModeState.Destroyed, gameMode, updateReason) + { + } + + public override Task enterDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetail() + { + + } + + public override void writeBusinessLog() + { + + } + + public override Task postProcess() + { + return Task.FromResult(new Result()); + } + + public override Task exitDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStatePlay.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStatePlay.cs new file mode 100644 index 0000000..d8ac54e --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStatePlay.cs @@ -0,0 +1,63 @@ + +using GameServer; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; +using ServerCore; + +public class RunPracticeStatePlay : GameModeStateBase +{ + public RunPracticeStatePlay(IGameMode gameMode) : base(GameModeState.Play, gameMode) + { + } + + public override Task enterDetail() + { + var now = DateTimeHelper.Current; + + var run_practice = getGameMode() as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"run_practice is null !!!"); + + var next_update_time = DateTimeHelper.Current.AddSeconds(run_practice.m_run_practice_meta.m_max_play_time); + setStateTime(next_update_time, "InitialPlayTime"); + + + var game_mode_base = m_current_game_mode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var play_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + + play_action.updateStartTime(now); + + return Task.CompletedTask; + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + } + + public override Task postProcess() + { + return Task.CompletedTask; + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + } + + public override Task updateDetail() + { + //kihoon todo : update 할게 있으면 여기서 처리 + return Task.FromResult(new Result()); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateReady.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateReady.cs new file mode 100644 index 0000000..a934abb --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateReady.cs @@ -0,0 +1,83 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.State; + +public class RunPracticeStateReady : GameModeStateBase +{ + public RunPracticeStateReady(IGameMode gameMode) + : base(GameModeState.Ready, gameMode) + { + } + + public override Task enterDetail() + { + var run_practice = getGameMode() as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"run_practice is null !!!"); + + var ready_pos = run_practice.getReadyPos().FirstOrDefault(); + string anchor_guid = ready_pos.Key; + + var pos_set_action = run_practice.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(pos_set_action, () => $"pos_set_action is null !!!"); + + var player_guid = run_practice.getInstanceRoom().getInstanceMemberGuids().FirstOrDefault(); + if (player_guid is null) + { + Log.getLogger().error($"RunPracticeStateReady player_guid is null !!!"); + return Task.CompletedTask; + } + if (false == run_practice.getInstanceRoom().tryGetInstanceMember(player_guid, out var player)) + { + Log.getLogger().error($"player not found !!! user_guid : {player_guid}"); + return Task.CompletedTask; + } + (Result result, var pos) = pos_set_action.changeUserAnchorPos(anchor_guid, player); + if (result.isFail()) return Task.CompletedTask; + var user_pos_infos = new Dictionary(); + user_pos_infos.Add(anchor_guid, (player_guid, pos)); + GameNotifyHelper.broadcast_GS2C_NTF_READY_POS(run_practice.getInstanceRoom(), user_pos_infos); + + var next_state_change_time = getNextStateChangeTime(); + var update_time = next_state_change_time.AddSeconds(run_practice.getMetas().m_common_data.GameReadyCount); + + setStateTime(update_time, "RunPracticeStateReady"); + + return Task.CompletedTask; + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + } + + public override Task postProcess() + { + return Task.CompletedTask; + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateResult.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateResult.cs new file mode 100644 index 0000000..802851d --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateResult.cs @@ -0,0 +1,53 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.State; + +public class RunPracticeStateResult : GameModeStateBase +{ + public RunPracticeStateResult(IGameMode gameMode) + : base(GameModeState.Result, gameMode) + { + } + + public override async Task enterDetail() + { + var run_practice = getGameMode() as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"run_practice is null !!!"); + + var next_update_time = DateTimeHelper.Current.AddSeconds(run_practice.getMetas().m_common_data.ResultUIWaitTime); + setStateTime(next_update_time, "InitialResultTime"); + await Task.CompletedTask; + } + + public override void notifyDetail() + { + + } + + public override void writeBusinessLog() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateSummary.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateSummary.cs new file mode 100644 index 0000000..cbea383 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateSummary.cs @@ -0,0 +1,101 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.ModePractice.Actions; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Helper; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.State; + +public class RunPracticeStateSummary : GameModeStateBase +{ + GameModeRunPracticePlayLogInfo m_play_log_info = new GameModeRunPracticePlayLogInfo(); + public RunPracticeStateSummary(IGameMode gameMode) : base(GameModeState.RewardSummary, gameMode) + { + } + + public override async Task enterDetail() + { + var run_practice = getGameMode() as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"run_practice is null !!!"); + + var next_update_time = DateTimeHelper.Current.AddSeconds(run_practice.m_run_practice_meta.m_result_scene_tracsition_wait_time); + setStateTime(next_update_time, "InitialRewardSummaryTime"); + + + var play_action = run_practice.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + + m_play_log_info.m_room_id = run_practice.getRoomId(); + m_play_log_info.m_start_time = play_action.m_start_time; + m_play_log_info.m_end_time = play_action.m_end_time; + m_play_log_info.m_respawn_count = play_action.m_respawn_count; + + + await Task.CompletedTask; + } + + public override void notifyDetail() + { + var game_mode_base = getGameMode() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + var reward_results = new List(); + var rewardsWithRank = new List(); + + var race_result = new List(); + + var player_guid = game_mode_base.getInstanceRoom().getInstanceMemberGuids().FirstOrDefault(); + if (player_guid is null) + { + Log.getLogger().error($"RunPractice player_guid is null !!!"); + player_guid = string.Empty; + } + + var play_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"game_mode_base is null !!!"); + + RunRaceResult result = new RunRaceResult(); + result.Rank = 1; + result.UserGuid = player_guid; + result.CompletionTime = RunRaceHelper.getCompletionTime(play_action.m_start_time, play_action.m_end_time); + result.RespawnCount = play_action.m_respawn_count; + race_result.Add(result); + + RunRaceNotifyHelper.broadcast_GS2C_NTF_RUN_RACE_RESULT_SUMMARY(game_mode_base, reward_results, rewardsWithRank, race_result, 1); + } + + public override void writeBusinessLog() + { + var invokers = new List(); + var business_log = new RunPracticePlayBusinessLog(m_play_log_info); + invokers.Add(business_log); + var log_action = new LogActionEx(LogActionType.RunPracticePlaySummary); + + var game_mode_base = getGameMode() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + BusinessLogger.collectLogs(log_action, game_mode_base, invokers); + } + + public override Task postProcess() + { + + + return Task.CompletedTask; + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateWait.cs b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateWait.cs new file mode 100644 index 0000000..f3ed7e0 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModePractice/State/RunPracticeStateWait.cs @@ -0,0 +1,57 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModePractice.State; + +public class RunPracticeStateWait : GameModeStateBase +{ + public RunPracticeStateWait(IGameMode gameMode, StateUpdateReasonType updateReason = StateUpdateReasonType.None) + : base(GameModeState.Wait, gameMode, updateReason) + { + } + + public override Task enterDetail() + { + var run_practice = getGameMode() as GameModeRunPractice; + NullReferenceCheckHelper.throwIfNull(run_practice, () => $"run_race is null !!!"); + + var update_time = DateTimeHelper.Current.AddSeconds(run_practice.m_run_practice_meta.m_max_start_wait_time); + setStateTime(update_time, "RunPracticeStateWait"); + + return Task.CompletedTask; + } + + public override Task updateDetail() + { + var result = new Result(); + return Task.FromResult(result); + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + } + + + + + + + public override void notifyDetailAfterExit() + { + } + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RaceGameObjectSavePointInteractAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RaceGameObjectSavePointInteractAction.cs deleted file mode 100644 index d8af1e6..0000000 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RaceGameObjectSavePointInteractAction.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.Concurrent; -using GameServer.Contents.GameMode.Action; -using GameServer.Contents.GameMode.InteractionObject; -using GameServer.Contents.GameMode.Manage.PlayManage; -using MetaAssets; -using ServerBase; -using ServerCommon; - -namespace GameServer; - -public class RaceGameObjectSavePointInteractAction : GameGameObjectAction -{ - protected ConcurrentDictionary m_game_objects = new(); //kihoon todo : 이거 위로 올릴지 말지, 하니면 GameModeBase에서 들고 있을지... 고민해볼것.. - public RaceGameObjectSavePointInteractAction (EntityBase owner) - : base(owner, EGameObjectType.Save_Point) - { - } - - public override async Task onInit() - { - await Task.CompletedTask; - var result = new Result(); - return result; - } - - public override void onClear() - { - return; - } - - public override async Task interact(string anchorGuid, DateTime interactionTime) - { - //여기에 interact 로직 추가해야 된다... - await Task.CompletedTask; - var result = new Result(); - return result; - } - - public override string toBasicString() - { - return $"class name : {nameof(RaceGameObjectSavePointInteractAction)}...." + base.toBasicString(); - } -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RaceStateCheckAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RaceStateCheckAction.cs deleted file mode 100644 index c10ecbc..0000000 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RaceStateCheckAction.cs +++ /dev/null @@ -1,63 +0,0 @@ -using GameServer.Contents.GameMode.Manage.PlayManage; -using GameServer.Contents.GameMode.Mode_Running.Manage; -using GameServer.Contents.GameMode.Mode_Running.ModeRace.Helper; -using ServerBase; -using ServerCommon; -using ServerCore; - -namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; - -public class RaceStateCheckAction : EntityActionBase -{ - public RaceStateCheckAction(EntityBase owner) - : base(owner) - { - } - public override void onClear() - { - return; - } - - public override async Task onInit() - { - await Task.CompletedTask; - - var result = new Result(); - - return result; - } - - public async Task stateUpdate() - { - var run_race = getOwner() as GameModeBase; - NullReferenceCheckHelper.throwIfNull(run_race, () => $"GameModeRunRace is null !!"); - - var game_mode = getOwner() as IGameMode; - NullReferenceCheckHelper.throwIfNull(game_mode, () => $"game_mode is null !!"); - var current_state = run_race.getGameModeState(); - - //kihoon todo : 해당 sate에 따른 업데이트 내용 처리 할게 있으면 여기서 처리 Update를 어떻게 써야 되나.... - current_state.update(); - - var next_state_type = current_state.checkState(); - var current_state_type = current_state.getStateType(); - - //다르면 상태 변경 - if (!current_state_type.Equals(next_state_type)) - { - var next_state = RunRaceHelper.createRaceGameState(game_mode, next_state_type); - - current_state.exit(); - run_race.setGameModeState(next_state); - current_state = run_race.getGameModeState(); - - current_state.enter(); - } - - var result = new Result(); - - await Task.CompletedTask; - return result; - } - -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceBuffInteractAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceBuffInteractAction.cs new file mode 100644 index 0000000..b7fc49a --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceBuffInteractAction.cs @@ -0,0 +1,111 @@ +using System.Collections.Concurrent; +using Amazon.S3.Model.Internal.MarshallTransformations; +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.Manage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Helper; +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using ANCHOR_GUID = System.String; +public class RunRaceBuffInteractAction : GameModeObjectInteractionHandlerActionBase +{ + + public RunRaceBuffInteractAction (EntityBase owner) + : base(owner, EGameModeObjectType.RunningBuff, false, true) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public override Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + Result result = new Result(); + + return Task.FromResult(result); + } + + public override Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var result = new Result(); + var err_msg = string.Empty; + + var game_mode = getOwner() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(game_mode, () => $"game_mode is null !!!"); + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var buff_data_handler = handler as RunRaceBuffInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(buff_data_handler, () => $"buff_data_handler is null !!!"); + var anchor_guid = buff_data_handler.m_interaction_anchor_guid; + + (result, var meta) = GameModeHelper.getGameModeObjectMeta(anchor_guid, game_mode_base); + if(result.isFail()) return Task.FromResult(result); + + var room_id = game_mode.getInstanceRoom().getMap().m_room_id; + if (false == game_mode.m_running_buffs.TryGetValue(anchor_guid, out var gameObjectBuff)) + { + + gameObjectBuff = new GameModeObject(meta.ObjectType, anchor_guid); + game_mode.m_running_buffs.AddOrUpdate(anchor_guid, gameObjectBuff, (key, val) => gameObjectBuff); + } + + if (false == gameObjectBuff.m_is_active) + { + err_msg = $"gameObjectBuff m_is_active false!!! room_id : {room_id}, anchor_guid : {anchor_guid}, player : {player.toBasicString()}, gameObject : {JsonConvert.SerializeObject(gameObjectBuff)}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_msg); + return Task.FromResult(result); + } + + buff_data_handler.m_need_noti_objects.Add((GameModeObject)gameObjectBuff); + + + if (meta.RespawnTime > 0) + { + gameObjectBuff.m_is_active = false; + gameObjectBuff.m_active_time = DateTimeHelper.Current.AddSeconds(meta.RespawnTime); + } + game_mode.m_running_buffs.AddOrUpdate(anchor_guid, gameObjectBuff, (key, val) => gameObjectBuff); + + return Task.FromResult(result); + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + return; + } + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + var buff_data_handler = handler as RunRaceBuffInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(buff_data_handler, () => $"buff_data_handler is null !!!"); + + var log_info = buff_data_handler.m_interaction_log_info as RunRaceBuffInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"TpsFfaCombatPodInteractionLogInfo is null !!!"); + + return new RunRaceBuffInteractionBuisinessLog(log_info); + } + + public override string toBasicString() + { + return $"class name : {nameof(RunRaceCheckPointInteractAction)}...." + base.toBasicString(); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceBuffInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceBuffInteractionDataHandler.cs new file mode 100644 index 0000000..2ac1dfb --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceBuffInteractionDataHandler.cs @@ -0,0 +1,15 @@ +using ServerCommon; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; + +public class RunRaceBuffInteractionDataHandler: GameObjectInteractionDataCommonHandlerBase +{ + //loginfo를 제너릭으로 받는게 좋을듯... + public RunRaceBuffInteractionDataHandler(string userGuid, string userNickname, string interactionAnchorGuid, int tableId, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, tableId, interactionTime) + { + m_interaction_log_info = new RunRaceBuffInteractionLogInfo(roomId, userGuid, userNickname, interactionAnchorGuid, tableId); + } + + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceCheckPointInteractAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceCheckPointInteractAction.cs new file mode 100644 index 0000000..3cd3e55 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceCheckPointInteractAction.cs @@ -0,0 +1,88 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + + +namespace GameServer; + +public class RunRaceCheckPointInteractAction : GameModeObjectInteractionHandlerActionBase +{ + + public RunRaceCheckPointInteractAction (EntityBase owner) + : base(owner, EGameModeObjectType.CheckPoint, false, false) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public override Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + return Task.FromResult(new Result()); + } + + public override Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var run_race_handler = handler as RunRaceCheckPointInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(run_race_handler, () => $"run_race_handler is null !!!"); + + var anchor_guid = run_race_handler.m_interaction_anchor_guid; + + var run_race = getOwner() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_rance is null !!!"); + + var play_action = run_race.getEntityAction(); + + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + play_action.pushCheckPoint(player.getUserGuid(), anchor_guid, run_race_handler.m_talbe_id); + + + play_action.checkAbusing(player, anchor_guid); + + + + + + + return Task.FromResult(new Result()); + + + + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + return; + } + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + //todo : 제너릭 이용해서 일반화 처리 고려해볼것 + var checkpoint_data_handler = handler as RunRaceCheckPointInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(checkpoint_data_handler, () => $"checkpoint_data_handler is null !!!"); + + var log_info = checkpoint_data_handler.m_interaction_log_info as RunRaceCheckPointInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"RunRaceCheckPointInteractionLogInfo is null !!!"); + + return new RunRaceCheckPointInteractionBuisinessLog(log_info); + } + + public override string toBasicString() + { + return $"class name : {nameof(RunRaceCheckPointInteractAction)}...." + base.toBasicString(); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceCheckPointInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceCheckPointInteractionDataHandler.cs new file mode 100644 index 0000000..b2119e3 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceCheckPointInteractionDataHandler.cs @@ -0,0 +1,15 @@ +using ServerCommon; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; + +public class RunRaceCheckPointInteractionDataHandler : GameObjectInteractionDataCommonHandlerBase +{ + //loginfo를 제너릭으로 받는게 좋을듯... + public RunRaceCheckPointInteractionDataHandler(string userGuid, string userNickname, string interactionAnchorGuid, int tableId, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, tableId, interactionTime) + { + m_interaction_log_info = new RunRaceCheckPointInteractionLogInfo(roomId, userGuid, userNickname, interactionAnchorGuid, tableId); + } + + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceDeadAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceDeadAction.cs new file mode 100644 index 0000000..7a52952 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceDeadAction.cs @@ -0,0 +1,67 @@ +using System.Collections.Concurrent; +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Manage.PlayManage; +using ServerBase; +using ServerCommon.GameMode; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; + +using USER_GUID = System.String; + +public class RunRaceDeadAction : EntityActionBase, IDeadHandler +{ + public RunRaceDeadAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public async Task dead(Player player, string deadUserGuid, string killerUserGuid, GameModeDeadType deadType, DateTime interactionTime) + { + var result = new Result(); + + var playAction = getOwner().getEntityAction(); + NullReferenceCheckHelper.throwIfNull(playAction, () => $"play_action is null !!!"); + + playAction.getRespawnInfos().TryGetValue(player.getUserGuid(), out int respawnCnt); + + var runRace = getOwner() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(runRace, () => $"run_race is null !!!"); + + var runRaceMeta = runRace.getMetas().m_detail_meta as RunRaceMeta; + NullReferenceCheckHelper.throwIfNull(runRaceMeta, () => $"run_race_meta is null !!!"); + + // 리스폰 제한 없음 + if (runRaceMeta.m_respawn_cnt_limit == 0) { + return result; + } + + // 리소픈 제한 횟수 남음 + if (respawnCnt < runRaceMeta.m_respawn_cnt_limit) + { + return result; + } + + // 리스폰 횟수 제한 초과 => 게임 종료 + playAction.setEndTime(); + + await Task.CompletedTask; + return result; + } + + public void notifyAfterDead(Player player) + { + return; + } +} diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceGoalLineInteractionAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceGoalLineInteractionAction.cs new file mode 100644 index 0000000..cdde911 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceGoalLineInteractionAction.cs @@ -0,0 +1,131 @@ +using System.Collections.Concurrent; +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.Manage; +using MetaAssets; +using NeoSmart.AsyncLock; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using USER_GUID = System.String; + +public class RunRaceGoalLineInteractionAction : GameModeObjectInteractionHandlerActionBase +{ + public RunRaceGoalLineInteractionAction(EntityBase owner) : base(owner, EGameModeObjectType.Goal, false, true) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public override Task validattionCheck(Player player, DateTime interactionTime, IGameObjectInteractionDataHandler handler) + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode is null !!!"); + var state = game_mode_base.getState(); + var state_type = state.getStateType(); + var result = new Result(); + if (state_type != GameModeState.Play) + { + var err_msg = $"goaline interaction allowed only play state!!!! currentState : {state_type}, gameModeId : {game_mode_base.getGameModeId()}, " + + $"roomId : {game_mode_base.getInstanceRoom().getMap().m_room_id}, player : {player.toBasicString()}"; + result.setFail(ServerErrorCode.GameModeInvalidAction, err_msg);; + Log.getLogger().error(result.toBasicString()); + return Task.FromResult(result); + } + + NullReferenceCheckHelper.throwIfNull(state, () => $"state is null !!!"); + + return Task.FromResult(new Result()); + } + + public override void notifyDetail(Player player, IGameObjectInteractionDataHandler handler) + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode is null !!!"); + var play_state = game_mode_base.getState() as RunRaceStatePlay; + + if (play_state is null) return; + + GameNotifyHelper.broadcast_GS2C_NTF_GAME_STATE_UPDATE(game_mode_base, play_state.getStateType(), play_state.getNextStateChangeTime(), play_state.getUpdateReason()); + } + + public override Task interactDetail(Player player, IGameObjectInteractionDataHandler handler) + { + Log.getLogger().debug($"RunRaceGoalLineInteractionAction player : {player.toBasicString()}"); + var result = new Result(); + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode is null !!!"); + + var user_guid = player.getUserGuid(); + + bool is_first = false; + bool is_all_goalin = false; + + var play_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + + var ranks = play_action.getRanks(); + + if (ranks.Contains(user_guid)) + { + var err_msg = $"run race rank already exists. gameModeId : {game_mode_base.getGameModeId()}, userGuid : {user_guid}"; + result.setFail(ServerErrorCode.GameModeRunRankUserExist, err_msg); + return Task.FromResult(result); + } + ranks.Enqueue(user_guid); + + play_action.registerComplete(user_guid); + + + if(ranks.Count == 1) is_first = true; + //rank 순위가 전부 다 들어와졌으면? + var current_users = game_mode_base.getInstanceRoom().getInstanceMemberGuids(); + + if(ranks.Count >= current_users.Count) is_all_goalin = true; + + var play_state = game_mode_base.getState() as RunRaceStatePlay; + NullReferenceCheckHelper.throwIfNull(play_state, () => $"play_state is null !!!"); + + + if (is_first) + { + Log.getLogger().debug($"is_first player : {player.toBasicString()}"); + play_state.handleFirstPlayerFinish(); + } + + if (is_all_goalin) + { + Log.getLogger().debug($"is_all_goalin player : {player.toBasicString()}"); + play_state.handleAllPlayersFinish(); + } + return Task.FromResult(result); + } + + public override ILogInvokerEx getBusinessLog(IGameObjectInteractionDataHandler handler) + { + var goal_line_data_handler = handler as RunRaceGoalLineInteractionDataHandler; + NullReferenceCheckHelper.throwIfNull(goal_line_data_handler, () => $"goal_line_data_handler is null !!!"); + + var log_info = goal_line_data_handler.m_interaction_log_info as RunRaceGoalLineInteractionLogInfo; + NullReferenceCheckHelper.throwIfNull(log_info, () => $"RunRaceGoalLineInteractionLogInfo is null !!!"); + + return new RunRaceGoalLineInteractionBuisinessLog(log_info); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceGoalLineInteractionDataHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceGoalLineInteractionDataHandler.cs new file mode 100644 index 0000000..b5c430c --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceGoalLineInteractionDataHandler.cs @@ -0,0 +1,15 @@ +using ServerCommon; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; + +public class RunRaceGoalLineInteractionDataHandler: GameObjectInteractionDataCommonHandlerBase +{ + //loginfo를 제너릭으로 받는게 좋을듯... + public RunRaceGoalLineInteractionDataHandler(string userGuid, string userNickname, string interactionAnchorGuid, int tableId, string roomId, DateTime interactionTime) + : base(userGuid, interactionAnchorGuid, tableId, interactionTime) + { + m_interaction_log_info = new RunRaceGoalLineInteractionLogInfo(roomId, userGuid, userNickname, interactionAnchorGuid, tableId); + } + + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceLoadCompleteAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceLoadCompleteAction.cs new file mode 100644 index 0000000..6f4e37e --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceLoadCompleteAction.cs @@ -0,0 +1,38 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; + +public class RunRaceLoadCompleteAction : EntityActionBase, IGameModeLoadCompleteHandler +{ + public RunRaceLoadCompleteAction(EntityBase owner) : base(owner) + { + + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public async Task loadComplete(Player player) + { + await Task.CompletedTask; + + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_moce_base is null !!!"); + game_mode_base.updateUserState(player.getUserGuid(), GameModePlayerState.LoadComplete); + + Log.getLogger().debug($"user_guid : {player.getUserGuid()} is load complete., total Users state: {JsonConvert.SerializeObject(game_mode_base.getUserState())}"); + return new Result(); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceObjectUpdateAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceObjectUpdateAction.cs new file mode 100644 index 0000000..5f816f9 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceObjectUpdateAction.cs @@ -0,0 +1,89 @@ +using System.Collections.Concurrent; +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.Manage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Helper; +using MetaAssets; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; + + +public class RunRaceObjectUpdateAction : EntityActionBase +{ + public RunRaceObjectUpdateAction(EntityBase owner) : base(owner) + { + } + + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public async Task updateObject() + { + await Task.CompletedTask; + + var result = await updateBuff(); + if (result.isFail()) return result; + + return result; + } + + private async Task updateBuff() + { + await Task.CompletedTask; + + var game_mode = getOwner() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(game_mode, () => $"game_mode_base is null !!!"); + + var now = DateTimeHelper.Current; + var infos = new List(); + List active_buff_guids = new(); + + foreach (var buff in game_mode.m_running_buffs.Values) + { + if (buff.m_active_time <= now && buff.m_is_active == false) + { + active_buff_guids.Add(buff.m_anchor_guid); + } + } + await updateAndNoti(game_mode, active_buff_guids); + + return new(); + } + + private async Task updateAndNoti(GameModeRunRace gameMode, List activeBuffs) + { + if (activeBuffs.Count == 0) return; + + List infos = new(); + using (var releaser = await gameMode.getAsyncLock()) + { + foreach (var guid in activeBuffs) + { + if (gameMode.m_running_buffs.TryGetValue(guid, out var buff)) + { + var info = new GameModeObjectInfo(); + buff.m_is_active = true; + info.AnchorGuid = buff.m_anchor_guid; + info.IsActive = buff.m_is_active ? BoolType.True : BoolType.False; + infos.Add(info); + } + } + } + + GameNotifyHelper.broadcast_GS2C_NTF_GAME_MODE_OBJECT_INFO(gameMode, infos); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRacePlayAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRacePlayAction.cs new file mode 100644 index 0000000..0e86eb1 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRacePlayAction.cs @@ -0,0 +1,294 @@ +using System.Collections.Concurrent; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCommon.GameMode; +using ServerCore; + +namespace GameServer.Contents.GameMode.Action; +using USER_GUID = System.String; +using RESPAWN_COUNT = System.Int32; +using COMPLETION_TIME = System.DateTime; + +using ANCHOR_GUID = System.String; +using TABLE_ID = System.Int32; +public class RunRacePlayAction : EntityActionBase +{ + public class RunRaceBestRecord + { + public bool IsNewBest { get; set; } + public int PrevPersonalBestSec { get; set; } + } + public HashSet m_playable_users = new(); + private DateTime m_play_start_time = DateTime.MaxValue; + private DateTime m_play_end_time = DateTime.MaxValue; + private ConcurrentQueue m_ranks = new(); + private ConcurrentDictionary m_respawns = new(); + private ConcurrentDictionary> m_check_points = new(); + private ConcurrentDictionary m_completion_time = new(); + private ConcurrentDictionary _bestRecords = new(); + public ConcurrentDictionary BestRecords => _bestRecords; + public RunRacePlayAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + m_playable_users.Clear(); + m_ranks.Clear(); + m_respawns.Clear(); + return; + } + + public void kickUserIfNotLoaded() + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var not_loaded_users = game_mode_base.getNotLoadedUserGuids(); + foreach (var user_guid in not_loaded_users) + { + if (false == game_mode_base.getInstanceRoom().tryGetInstanceMember(user_guid, out var player)) + { + Log.getLogger().warn($"player : {user_guid} is not found. so can't not send send_GS2C_NTF_GAME_USER_KICK"); + continue; + } + GameNotifyHelper.send_GS2C_NTF_GAME_USER_KICK(player, GameModeKickReason.LoadingTimeExpired); + game_mode_base.removePlayUserState(user_guid); + } + } + + public void registerPlayableUser() + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + + + var guids = game_mode_base.getInstanceRoom().getInstanceMemberGuids(); + foreach (var guid in guids) + { + if (game_mode_base.getUserState().ContainsKey(guid) == false) + { + Log.getLogger().info($"user : {guid} is not loaded. so can't registerPlayableUser"); + continue; + } + m_playable_users.Add(guid); + m_respawns.AddOrUpdate(guid, 0, (key, value) => 0); + } + } + + public void registerComplete(USER_GUID user_guid) + { + + m_completion_time.TryAdd(user_guid, DateTimeHelper.Current); + } + + + public void setStartTime() + { + m_play_start_time = DateTimeHelper.Current; + } + + public void setEndTime() + { + m_play_end_time = DateTimeHelper.Current; + } + + public ConcurrentDictionary getRespawnInfos() + { + return m_respawns; + } + + public void removeNotExistUserFromRespawnInfos(List playUserGuids) + { + var deletable_user_guids = new List(); + foreach (var respawn_user_guid in m_respawns.Keys) + { + if (false == playUserGuids.Contains(respawn_user_guid)) + { + deletable_user_guids.Add(respawn_user_guid); + } + } + + foreach (var user_guid in deletable_user_guids) + { + m_respawns.TryRemove(user_guid, out _); + } + } + + public ConcurrentQueue getRanks() => m_ranks; + public DateTime getPlayStartTime() => m_play_start_time; + public DateTime getPlayEndTime() => m_play_end_time; + + public ConcurrentDictionary getCompletionTime() => m_completion_time; + + public HashSet getPlayableUsers() + { + return m_playable_users; + } + + public ConcurrentDictionary> getCheckPoint() + { + return m_check_points; + } + + public void pushCheckPoint(USER_GUID user_guid, ANCHOR_GUID anchorGuid, TABLE_ID tableId) + { + if (false == m_check_points.TryGetValue(user_guid, out var check_points)) + { + check_points = new Stack(); + } + + var interaction_info = new RunRaceCheckPointInteractionInfo(user_guid, anchorGuid, tableId, DateTimeHelper.Current); + check_points.Push(interaction_info); + m_check_points.AddOrUpdate(user_guid, check_points, (key, value) => check_points); + } + + public void checkAbusing(Player player, string anchorGuid) + { + var run_race = getOwner() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_race is null !!!"); + + + if (false == run_race.getInstanceRoom().getMap().getAnchors().TryGetValue(anchorGuid, out var anchorInfo)) + { + var err_msg = $"checkAbusing Not exist anchorInfo guid : {anchorGuid}"; + Log.getLogger(err_msg); + return; + } + + var table_id = anchorInfo.AnchorProp.TableId; + var run_race_meta = run_race.getMetas().m_detail_meta as RunRaceMeta; + NullReferenceCheckHelper.throwIfNull(run_race_meta, () => $"run_race_meta is null !!!"); + + if (false == run_race_meta.m_check_point_monitor_data.TryGetValue(run_race.getInstanceRoom().getInstanceId(), out var check_point_monitor_data)) + { + var err_msg = $"checkAbusing Not exist m_check_point_monitor_data guid : {anchorGuid}, instanceId : {run_race.getInstanceRoom().getInstanceId()}"; + Log.getLogger(err_msg); + return; + } + + if (false == check_point_monitor_data.TryGetValue(table_id, out var checkPointTime)) + { + var err_msg = $"checkAbusing Not exist check_point_monitor_data guid : {anchorGuid}, instanceId : {run_race.getInstanceRoom().getInstanceId()}, talbeId : {table_id}"; + Log.getLogger(err_msg); + return; + } + + var now = DateTimeHelper.Current; + if (now - m_play_start_time < TimeSpan.FromSeconds(checkPointTime)) + { + //abusing log 수집 + var abusing_invokers = new List(); + var log_action = new LogActionEx(LogActionType.RunRaceCheckPointAbusing); + var log_info = new GameModeRunRaceCheckPointAbusingLogInfo(run_race.getRoomId(), m_play_start_time, now, checkPointTime, table_id); + var business_log = new RunRaceCheckPointAbusingBusinessLog(log_info); + abusing_invokers.Add(business_log); + BusinessLogger.collectLogs(log_action, player, abusing_invokers); + } + } + + public async Task matchCountCheck() + { + await Task.CompletedTask; + var result = new Result(); + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var users = game_mode_base.getInstanceRoom().getInstanceMemberGuids(); + + foreach (var user_guid in users) + { + if (false == game_mode_base.getInstanceRoom().tryGetInstanceMember(user_guid, out var player)) + { + Log.getLogger().warn($"matchCountCheck player : {user_guid} is not found"); + continue; + } + + var action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(action, () => $"regulation_action is null !!!"); + await action.addMatchCount(game_mode_base.getEntityType()); + } + + return result; + } + + public KeyValuePair sortRespawnUser(ConcurrentQueue ranks) + { + var sorted_respawns = new List>(); + + // ranks를 리스트로 변환하여 순서를 유지 + var rank_list = ranks.ToList(); + + // rank에 있는 유저들의 리스폰 정보를 가져옴 + var user_respawn_info = new List<(string user_guid, int respawn_count, int rank_index)>(); + + for (int i = 0; i < rank_list.Count; i++) + { + var user_guid = rank_list[i]; + if (m_respawns.TryGetValue(user_guid, out var respawn_count)) + { + user_respawn_info.Add((user_guid, respawn_count, i)); + } + } + + // 리스폰 횟수 기준 오름차순 정렬, 동일하면 rank 순서 기준 정렬하여 첫 번째 유저 선택 + var top_user = user_respawn_info + .OrderBy(x => x.respawn_count) + .ThenBy(x => x.rank_index) + .FirstOrDefault(); + + // 1등 유저 반환 (없으면 빈 KeyValuePair 반환) + if (top_user != default) + { + return new KeyValuePair(top_user.user_guid, top_user.respawn_count); + } + + return new KeyValuePair(string.Empty, 0); + + } + + public Queue getClonedRank() + { + return new Queue(m_ranks); + + } + + public Dictionary getClonedRespawnInfos() + { + return new Dictionary(m_respawns); + } + + public Dictionary> getClonedCheckPoint() + { + var cloned = new Dictionary>(); + + foreach (var kvp in m_check_points) + { + var clonedQueue = new Queue(); + foreach (var item in kvp.Value) + { + clonedQueue.Enqueue(item); + } + cloned[kvp.Key] = clonedQueue; + } + return cloned; + } + + public Dictionary getClonedCompletionTime() + { + return new Dictionary(m_completion_time); + } +} + + diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceReadyPosAssignAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceReadyPosAssignAction.cs new file mode 100644 index 0000000..bc7ea34 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceReadyPosAssignAction.cs @@ -0,0 +1,94 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Helper; +using Newtonsoft.Json; +using ServerBase; +using ServerCommon; +using ServerCore; + +using ANCHOR_GUID = System.String; +using USER_GUID = System.String; +using USER_POS = Pos; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; + +public class RunRaceReadyPosAssignAction : EntityActionBase +{ + //private readonly Queue m_assignable_ready_anchor_guids = new Queue(); + + public RunRaceReadyPosAssignAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public void procceedReadyPosAssign() + { + var shuffle_users = shuffleReadyUser(); + + sendReadyPosAssign(shuffle_users); + } + + private List shuffleReadyUser() + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode is null !!!"); + + var load_complete_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(load_complete_action, () => $"load_complete_action is null !!!"); + var user_guids = game_mode_base.getLoadCompletedUserGuids() + .OrderBy(x => Random.Shared.Next()) + .ToList(); + + Log.getLogger().debug($"shuffled user_guids : {JsonConvert.SerializeObject(user_guids)}"); + return user_guids; + } + + private void sendReadyPosAssign(List userGuids) + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode is null !!!"); + var ready_pos = game_mode_base.getReadyPos(); + + if (ready_pos.Keys.Count < userGuids.Count) + { + Log.getLogger().error($"m_assignable_ready_anchor_guids less than user count!!! {ready_pos.Keys.Count} : {userGuids.Count}"); + } + + var match_count = Math.Min(ready_pos.Keys.Count, userGuids.Count); + + var anchors = ready_pos.Keys.ToArray(); + + var user_pos_infos = new Dictionary(); + var pos_set_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(pos_set_action, () => $"pos_set_action is null !!!"); + for (int i = 0; i < match_count; i++) + { + var anchorGuid = anchors[i % anchors.Length]; + var user_guid = userGuids[i]; + + if (false == game_mode_base.getInstanceRoom().tryGetInstanceMember(user_guid, out var player)) + { + Log.getLogger().error($"player not found !!! user_guid : {user_guid}"); + continue; + } + (Result result, var pos) = pos_set_action.changeUserAnchorPos(anchorGuid, player); + if (result.isFail()) continue; + user_pos_infos.Add(anchorGuid, (user_guid, pos)); + } + + GameNotifyHelper.broadcast_GS2C_NTF_READY_POS(game_mode_base.getInstanceRoom(), user_pos_infos); + + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceRespawnAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceRespawnAction.cs new file mode 100644 index 0000000..93aed6e --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceRespawnAction.cs @@ -0,0 +1,75 @@ +using System.Collections.Concurrent; +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Helper; +using NeoSmart.AsyncLock; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; + + +using USER_GUID = System.String; +using RESPAWN_COUNT = System.Int32; + +public class RunRaceRespawnAction : EntityActionBase, IRespawnHandler +{ + private AsyncLock m_async_lock = new(); + + + public RunRaceRespawnAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + public async Task respawn(Player player, DateTime respawnTime) + { + var user_guid = player.getUserGuid(); + + var result = new Result(); + using (var releaser = await m_async_lock.LockAsync()) + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var play_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + var respawns = play_action.getRespawnInfos(); + + if (false == respawns.ContainsKey(user_guid)) + { + respawns.TryAdd(user_guid, 0); + } + if(false == respawns.TryGetValue(user_guid, out var count)) + { + var err_str = $"m_respawns add vaule error!!! user_guid : {user_guid}, data : {JsonConvert.SerializeObject(respawns)}"; + result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_str); + return result; + } + respawns.AddOrUpdate(user_guid, count, (key, value) => value + 1); + } + + return result; + } + + public void notifyAfterRespawn(Player player) + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + GameNotifyHelper.broadcast_GS2C_NTF_GAME_PLAYER_RESPAWN(game_mode_base, player.getUserGuid(), new Pos()); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceRewardAction.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceRewardAction.cs new file mode 100644 index 0000000..0adbd57 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Actions/RunRaceRewardAction.cs @@ -0,0 +1,541 @@ +using System.Collections.Concurrent; +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Helper; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Reward; +using GameServer.Contents.Reward; +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCommon.Contents.GameMode; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; + +using USER_GUID = System.String; +using RANK = System.Int32; +using RESPAWN_COUNT = System.Int32; + +public class RunRaceRewardAction : EntityActionBase +{ + public RunRaceRewardAction(EntityBase owner) : base(owner) + { + } + + public override async Task onInit() + { + await Task.CompletedTask; + var result = new Result(); + return result; + } + + public override void onClear() + { + return; + } + + + public async Task<(Result, GameModeRunRaceRewardSummayLogInfo)> raceReward() + { + var game_mode_base = getOwner() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var play_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + + var cloned_rank = play_action.getClonedRank(); + var cloned_respawn = play_action.getClonedRespawnInfos(); + // getCompletionTime() + // var cloned_completion_time = play_action.getClonedCompletionTime(); + var cloned_completion_time = play_action.getCompletionTime().ToArray().ToDictionary(); + var cloned_check_point = play_action.getClonedCheckPoint(); + + var finished_rewards_with_rank = prepareFinishedUserRewards(game_mode_base, play_action); + var respawn_rewards = prepareRespawnRewards(game_mode_base, play_action); + var unfinished_rewards = prepareUnFinishedUserRewards(game_mode_base, play_action); + + var reward_results = new List(); + var result_with_rank = await finishedUserRewards(game_mode_base, finished_rewards_with_rank); + reward_results.AddRange(await respawnRewards(game_mode_base, respawn_rewards)); + reward_results.AddRange(await unFinishedUserRewards(game_mode_base, unfinished_rewards)); + + (var race_results, var play_user) = setRaceResults(game_mode_base, play_action, finished_rewards_with_rank); + + RunRaceNotifyHelper.broadcast_GS2C_NTF_RUN_RACE_RESULT_SUMMARY(game_mode_base, reward_results, result_with_rank, + race_results, play_user.Count); + + var start_play_user = play_action.getPlayableUsers().ToList(); + var end_play_user = play_user; + var log_info = new GameModeRunRaceRewardSummayLogInfo(); + log_info.m_room_id = game_mode_base.getRoomId(); + log_info.m_start_time = play_action.getPlayStartTime(); + log_info.m_end_time = play_action.getPlayEndTime(); + log_info.m_start_play_users = start_play_user; + log_info.m_end_play_users = end_play_user; + log_info.m_finished_rewards_with_rank = finished_rewards_with_rank; + log_info.m_respawn_rewards = respawn_rewards; + log_info.m_unfinished_rewards = unfinished_rewards; + log_info.m_ranks = cloned_rank; + log_info.m_respawns = cloned_respawn; + log_info.m_completion_time = cloned_completion_time; + log_info.m_check_points = cloned_check_point; + + return new(new Result(), log_info); + } + + private async Task> finishedUserRewards(GameModeBase gameModeBase, + Dictionary)> finishedRewards) + { + var room_id = gameModeBase.getRoomId(); + var play_action = gameModeBase.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + var play_start_time = play_action.getPlayStartTime(); + var completion_times = play_action.getCompletionTime(); + + var bestRecords = play_action.BestRecords; + bestRecords.Clear(); + var reward_results = new List(); + foreach (var finished_info in finishedRewards) + { + var guid = finished_info.Key; + var rewards = finished_info.Value.Item2; + var rank = finished_info.Value.Item1; + + if (false == gameModeBase.getInstanceRoom().tryGetInstanceMember(guid, out var player)) + { + Log.getLogger() + .error($"player is null !!! guid : {guid}, gameModeBase : {gameModeBase.toBasicString()}"); + continue; + } + + // 개인 최고 기록 갱신 여부를 트랜잭션에 포함 + var completionTimeSec = RunRaceHelper.getCompletionTime(guid, play_start_time, completion_times); + var gameModeType = gameModeBase.getGameModeType(); + if (completionTimeSec > 0) + { + if (gameModeType is not (GameModeType.RUN_PRACTICE or GameModeType.RUN_HONOR)) + { + var updateResult = await updateBestRecord(player, gameModeBase, completionTimeSec); + bestRecords.TryAdd(guid, + new RunRacePlayAction.RunRaceBestRecord + { + IsNewBest = updateResult.isNewBest, + PrevPersonalBestSec = (int)updateResult.prevBestScore + }); + } + else + { + bestRecords.TryAdd(guid, + new RunRacePlayAction.RunRaceBestRecord { IsNewBest = false, PrevPersonalBestSec = 0 }); + } + } + else + { + bestRecords.TryAdd(guid, + new RunRacePlayAction.RunRaceBestRecord { IsNewBest = false, PrevPersonalBestSec = 0 }); + } + + var business_log = new RunRaceFinishRewardBusinessLog(room_id, guid, rank, rewards); + (var result, var common_result) = + await proceedReward(player, rewards, LogActionType.RunRaceFinishReward, business_log); + if (result.isFail()) + { + Log.getLogger() + .error( + $"failed to finishedUserRewards proceedReward !!! guid : {guid}, gameModeBase : {gameModeBase.toBasicString()}, result : {result.toBasicString()}"); + } + + GameModeRewardResultWithRank reward_result_with_rank = new GameModeRewardResultWithRank(); + reward_result_with_rank.Rank = rank; + reward_result_with_rank.RewardResult = new GameModeRewardResult(); + reward_result_with_rank.RewardResult.CommonResult = common_result; + reward_result_with_rank.RewardResult.RewardType = GameModeRewardType.FinishRankReward; + reward_result_with_rank.RewardResult.UserGuid = guid; + + reward_results.Add(reward_result_with_rank); + } + + return reward_results; + } + + private async Task<(bool isApplied, bool isNewBest, long prevBestScore, long newScore)> updateBestRecord( + Player player, GameModeBase gameModeBase, int completedTimeSec) + { + // 일단 하드 코딩 : RUN_PRACTICE 와 RUN_HONOR 만 적용 + var gameModeType = gameModeBase.getGameModeType(); + if (gameModeType is not (GameModeType.RUN_PRACTICE or GameModeType.RUN_HONOR)) + { + return (false, false, 0, completedTimeSec); + } + + var gameRoomInfo = gameModeBase.getGameRoomInfo(); + var gameModeId = gameRoomInfo.GameModeId; + var instanceMetaId = gameRoomInfo.InstanceMetaId; + + var bestRecordAction = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(bestRecordAction, () => $"bestRecordAction is null !!!"); + + var result = await bestRecordAction.TryUpdateBestRecord(gameModeId, instanceMetaId, completedTimeSec); + if (result.result.isFail()) + { + Log.getLogger() + .error( + $"failed to updateBestRecord !!! gameModeId : {gameModeId}, instanceMetaId : {instanceMetaId}, completedTimeSec : {completedTimeSec}, result : {result.result.toBasicString()}"); + } + + return (true, result.isNewBest, result.prevBestScore, result.newBestScore); + } + + private async Task> respawnRewards(GameModeBase gameModeBase, + Dictionary> respawnRewards) + { + var room_id = gameModeBase.getRoomId(); + var play_action = gameModeBase.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + var respawn_infos = play_action.getRespawnInfos(); + var reward_results = new List(); + + foreach (var respawn_info in respawnRewards) + { + var guid = respawn_info.Key; + var rewards = respawn_info.Value; + + if (false == respawn_infos.TryGetValue(guid, out var respawn_count)) respawn_count = 0; + + if (false == gameModeBase.getInstanceRoom().tryGetInstanceMember(guid, out var player)) + { + Log.getLogger() + .error($"player is null !!! guid : {guid}, gameModeBase : {gameModeBase.toBasicString()}"); + continue; + } + + var business_log = new RunRaceRespawnRewardBusinessLog(room_id, guid, respawn_count, rewards); + (var result, var common_result) = + await proceedReward(player, rewards, LogActionType.RunRaceRespawnReward, business_log); + if (result.isFail()) + { + Log.getLogger() + .error( + $"failed to respawnUserRewards proceedReward !!! guid : {guid}, gameModeBase : {gameModeBase.toBasicString()}, result : {result.toBasicString()}"); + } + + GameModeRewardResult reward_result = new GameModeRewardResult(); + reward_result.UserGuid = guid; + reward_result.CommonResult = common_result; + reward_result.RewardType = GameModeRewardType.MinRespawnBonusReward; + reward_results.Add(reward_result); + } + + return reward_results; + } + + + private async Task> unFinishedUserRewards(GameModeBase gameModeBase, + Dictionary> unfinishRewards) + { + var room_id = gameModeBase.getRoomId(); + var play_action = gameModeBase.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + var respawn_infos = play_action.getRespawnInfos(); + + var reward_results = new List(); + + foreach (var unfinish_info in unfinishRewards) + { + var guid = unfinish_info.Key; + var rewards = unfinish_info.Value; + + if (false == respawn_infos.TryGetValue(guid, out var respawn_count)) respawn_count = 0; + + if (false == gameModeBase.getInstanceRoom().tryGetInstanceMember(guid, out var player)) + { + Log.getLogger() + .error($"player is null !!! guid : {guid}, gameModeBase : {gameModeBase.toBasicString()}"); + continue; + } + + var business_log = new RunRaceUnfinishRewardBusinessLog(room_id, guid, rewards); + (var result, var common_result) = + await proceedReward(player, rewards, LogActionType.RunRaceUnFinishReward, business_log); + if (result.isFail()) + { + Log.getLogger() + .error( + $"failed to respawnUserRewards proceedReward !!! guid : {guid}, gameModeBase : {gameModeBase.toBasicString()}, result : {result.toBasicString()}"); + continue; + } + + GameModeRewardResult reward_result = new GameModeRewardResult(); + reward_result.UserGuid = guid; + reward_result.CommonResult = common_result; + reward_result.RewardType = GameModeRewardType.UnfinishRankReward; + reward_results.Add(reward_result); + } + + return reward_results; + } + + private async Task<(Result, CommonResult)> proceedReward(Player player, List rewards, + LogActionType logActionType, ILogInvokerEx businessLog) + { + await Task.CompletedTask; + + var server_logic = GameServerApp.getServerLogic(); + var common_result = new CommonResult(); + + var fn_reward = async delegate() + { + IReward reward_proc = new RewardRunRace(player, player.getUserGuid(), rewards); + var result = await RewardManager.It.proceedRewardProcess(reward_proc); + + var batch = new QueryBatchEx(player, logActionType, + server_logic.getDynamoDbClient()); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + } + batch.appendBusinessLog(businessLog); + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) return result; + + //보상 내용 가져올것 + var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); + NullReferenceCheckHelper.throwIfNull(found_transaction_runner, + () => $"found_transaction_runner is null !!! - {player.toBasicString()}"); + common_result = found_transaction_runner.getCommonResult(); + return result; + }; + + var result = await player.runTransactionRunnerSafelyWithTransGuid(player.getUserGuid(), + TransactionIdType.PrivateContents, "RunRaceReward", fn_reward); + if (result.isFail()) + { + var err_msg = + $"Failed to runTransactionRunnerSafelyWithTransGuid() !!! : {result.toBasicString()}, {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return (result, common_result); + } + + return (result, common_result); + } + + private (List, List) setRaceResults(GameModeBase gameModeBase, RunRacePlayAction playAction, + Dictionary)> rankInfos) + { + var member_guids = gameModeBase.getInstanceRoom().getInstanceMemberGuids(); + + var race_results = new List(); + var play_start_time = playAction.getPlayStartTime(); + + var respawn_infos = playAction.getRespawnInfos(); + var completion_time = playAction.getCompletionTime(); + + var bestRecords = playAction.BestRecords; + foreach (var guid in member_guids) + { + RunRaceResult race_result = new RunRaceResult(); + race_result.Rank = getRank(guid, rankInfos); + race_result.UserGuid = guid; + race_result.CompletionTime = RunRaceHelper.getCompletionTime(guid, play_start_time, completion_time); + race_result.RespawnCount = getRespawnCount(guid, respawn_infos); + + // 최고 기록 관련 정보 추가 + var isInValue = bestRecords.TryGetValue(guid, out var bestRecord); + if (isInValue) + { + race_result.IsNewBest = bestRecord?.IsNewBest ?? false; + race_result.PrevPersonalBestTime = bestRecord?.PrevPersonalBestSec ?? 0; + } + + race_results.Add(race_result); + } + + return (race_results, member_guids); + } + + + private int getRespawnCount(USER_GUID guid, ConcurrentDictionary respawnInfos) + { + if (false == respawnInfos.TryGetValue(guid, out var respawnCount)) return -1; + + return respawnCount; + } + + private int getRank(USER_GUID guid, Dictionary)> rankInfos) + { + if (false == rankInfos.TryGetValue(guid, out var rankInfo)) return -1; + return rankInfo.Item1; + } + + private Dictionary)> prepareFinishedUserRewards(GameModeBase gameModeBase, + RunRacePlayAction playAction) + { + var ranks = playAction.getRanks(); + + var run_race = gameModeBase as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_race is null !!!"); + + //var play_user_guids = run_race.getInstanceRoom().getInstanceMemberGuids(); + var play_action = run_race.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + + var playable_users = play_action.getPlayableUsers(); + var player_count = playable_users.Count; + int rank = 0; + + var ret = new Dictionary)>(); + foreach (var guid in ranks) + { + rank++; + if (false == run_race.m_run_race_meta.m_finish_rank_reward_conditions.TryGetValue((player_count, rank), + out var condition)) + { + Log.getLogger().error($"not exist finishedReward playerCount : {playAction}, rank : {rank}"); + ret.TryAdd(guid, (rank, new List())); // [INTENT] : 비지니스 로그 남겨 놓기 위해 빈값 처리 + continue; + } + + var rank_reward = condition as RunRaceRankRewardCondition; + if (rank_reward is null) + { + Log.getLogger().error($"rank_reward is null playerCount : {playAction}, rank : {rank}"); + ret.TryAdd(guid, (rank, new List())); + continue; + } + + if (false == MetaData.Instance._RewardMetaTable.TryGetValue(rank_reward.m_reward_group_id, + out var rewardList)) + { + Log.getLogger() + .error( + $"rank_reward is null playerCount : {playAction}, rank : {rank}, m_reward_group_id : {rank_reward.m_reward_group_id}"); + ret.TryAdd(guid, (rank, new List())); + continue; + } + + var rewards = RewardHelper.convertRewardMetaToMetaAssetrewards(rewardList); + ret.TryAdd(guid, (rank, rewards)); + } + + return ret; + } + + private Dictionary> prepareRespawnRewards(GameModeBase gameModeBase, + RunRacePlayAction playAction) + { + var respawn_infos = playAction.getRespawnInfos(); + var ret = new Dictionary>(); + var ranks = playAction.getRanks(); + + var run_race = gameModeBase as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_race is null !!!"); + + var play_action = run_race.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + + //리스폰 랭크 주기 전에 현재 없는 유저는 제외 + var current_play_users = run_race.getInstanceRoom().getInstanceMemberGuids(); + playAction.removeNotExistUserFromRespawnInfos(current_play_users); + + var respawn_user = playAction.sortRespawnUser(ranks); + + if (respawn_user.Key == string.Empty) return ret; + var user_guid = respawn_user.Key; + var player_count = play_action.getPlayableUsers().Count; + + run_race.m_run_race_meta.m_min_respawn_rank_reward_conditions.TryGetValue((player_count, 1), out var condition); + var rank_reward = condition as RunRaceMinimumRespawnRewardCondition; + if (rank_reward is null) + { + Log.getLogger().error($"rank_reward is null playerCount : {player_count}, rank : 1"); + ret.TryAdd(user_guid, new List()); + return ret; + } + + if (false == MetaData.Instance._RewardMetaTable.TryGetValue(rank_reward.m_reward_group_id, out var rewardList)) + { + Log.getLogger() + .error( + $"respawn_reward is null playerCount : {player_count}, m_reward_group_id : {rank_reward.m_reward_group_id}"); + ret.TryAdd(user_guid, new List()); + return ret; + } + + var rewards = RewardHelper.convertRewardMetaToMetaAssetrewards(rewardList); + ret.TryAdd(user_guid, rewards); + + return ret; + } + + private Dictionary> prepareUnFinishedUserRewards(GameModeBase gameModeBase, + RunRacePlayAction playAction) + { + var ret = new Dictionary>(); + var ranks = playAction.getRanks(); + + var check_points = playAction.getCheckPoint(); + + var run_race = gameModeBase as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_race is null !!!"); + var play_user_guids = run_race.getInstanceRoom().getInstanceMemberGuids(); + + //kihoon todo : m_ranks 랑, m_check_points 는 보상 정리 전에 clone떠야 된다. + + var play_action = run_race.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + var player_count = play_action.getPlayableUsers().Count; + + foreach (var (user_guid, user_check_point) in check_points) + { + var last_interaction_info = user_check_point.Pop(); + var check_point_number = run_race.getCheckPointOrder(last_interaction_info.m_anchor_guid); + + if (ranks.Contains(user_guid)) continue; + if (false == run_race.m_run_race_meta.m_unfinish_rank_reward_conditions.ContainsKey((player_count, + check_point_number))) continue; + + run_race.m_run_race_meta.m_unfinish_rank_reward_conditions.TryGetValue((player_count, check_point_number), + out var condition); + var rank_reward = condition as RunRaceUnfinishRankRewardCondition; + if (rank_reward is null) + { + Log.getLogger() + .error( + $"rank_reward is null playerCount : {playAction}, check_point_number : {check_point_number}"); + ret.TryAdd(user_guid, new List()); + continue; + } + + if (false == MetaData.Instance._RewardMetaTable.TryGetValue(rank_reward.m_reward_group_id, + out var rewardList)) + { + Log.getLogger() + .error( + $"unfinished_reward is null playerCount : {playAction}, check_point_number : {check_point_number}, m_reward_group_id : {rank_reward.m_reward_group_id}"); + ret.TryAdd(user_guid, new List()); + continue; + } + + var rewards = RewardHelper.convertRewardMetaToMetaAssetrewards(rewardList); + ret.TryAdd(user_guid, rewards); + } + + return ret; + } + + private async Task processUserRewards( + GameModeBase gameModeBase, + T rewards, + Func rewardProcessor, + string rewardType) + { + try + { + await rewardProcessor(gameModeBase, rewards); + } + catch (Exception ex) + { + Log.getLogger().error($"{rewardType} reward processing failed: {ex.Message}"); + } + } +} diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Helper/RunRaceHelper.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Helper/RunRaceHelper.cs index 26dfcaa..37f9f36 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Helper/RunRaceHelper.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Helper/RunRaceHelper.cs @@ -1,27 +1,78 @@ -using GameServer.Contents.GameMode.Manage.PlayManage; -using GameServer.Contents.GameMode.Manage.StateManage; -using GameServer.Contents.GameMode.Mode_Running.Manage; +using System.Collections.Concurrent; +using GameServer.Contents.GameMode.InteractionObject; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using MetaAssets; +using ServerCommon; using ServerCore; +using USER_GUID = System.String; + +using COMPLETION_TIME = System.DateTime; namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Helper; -public class RunRaceHelper +public static class RunRaceHelper { - public static IGameModeState createRaceGameState(IGameMode gameMode, GameModeState state) + public static IGameObjectInteractionHandler? getRunRaceInteractAction(GameModeBase gameModeBase, EGameModeObjectType objectType) { - switch (state) + switch (objectType) { - case GameModeState.Start: - return new RaceStateStart(gameMode); - case GameModeState.Ready: - return new RaceStateReady(gameMode); - case GameModeState.End: - return new RaceStateReady(gameMode); - case GameModeState.Destroyed: - return new RaceStateReady(gameMode); + case EGameModeObjectType.CheckPoint: + return gameModeBase.getEntityAction(); + case EGameModeObjectType.Goal: + return gameModeBase.getEntityAction(); + case EGameModeObjectType.RunningBuff: + return gameModeBase.getEntityAction(); default: - Log.getLogger().error($"createRaceGameState not defined GameModeState : {state}"); - return new RaceStateEnd(gameMode); + return null; } } + + public static IDeadHandler? getDeadAction(GameModeBase gameModeBase) + { + return gameModeBase.getEntityAction(); + } + + public static IRespawnHandler? getRespawnAction(GameModeBase gameModeBase) + { + return gameModeBase.getEntityAction(); + } + + public static IGameModeLoadCompleteHandler getLoadCompleteAction(GameModeBase gameModeBase) + { + var handler = gameModeBase.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(handler, () => $"RunRaceLoadCompleteAction handler is null !!!"); + return handler; + } + + public static IGameObjectInteractionDataHandler? getRunRaceInteractDataHandler(Player player, EGameModeObjectType objectType, string anchorGuid, int tableId, string roomId, DateTime interactionTime) + { + switch (objectType) + { + case EGameModeObjectType.CheckPoint: + return new RunRaceCheckPointInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, tableId, roomId, interactionTime); + case EGameModeObjectType.RunningBuff: + return new RunRaceBuffInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, tableId, roomId, interactionTime); + case EGameModeObjectType.Goal: + return new RunRaceGoalLineInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, tableId, roomId, interactionTime); + default: + Log.getLogger().error($"getRunRaceInteractDataHandler objectType error !!!, anchorGuid : {anchorGuid}, objectType : {objectType}, roomId : {roomId}, player : {player.toBasicString()}"); + return new RunRaceCheckPointInteractionDataHandler(player.getUserGuid(), player.getUserNickname(), anchorGuid, tableId, roomId, interactionTime); + } + } + + + public static int getCompletionTime(USER_GUID guid, DateTime startTime, ConcurrentDictionary completionTime) + { + if (false == completionTime.TryGetValue(guid, out var completionTimeValue)) + return -1; + + int seconds = (int)Math.Max(-1, (completionTimeValue - startTime).TotalSeconds); + return seconds; + } + public static int getCompletionTime(DateTime startTime, DateTime endTime) + { + int seconds = (int)Math.Max(-1, (endTime - startTime).TotalSeconds); + return seconds; + } } \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Helper/RunRaceNotifyHelper.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Helper/RunRaceNotifyHelper.cs new file mode 100644 index 0000000..51d31da --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Helper/RunRaceNotifyHelper.cs @@ -0,0 +1,30 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.Manage; +using Newtonsoft.Json; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Helper; + +public class RunRaceNotifyHelper +{ + public static void broadcast_GS2C_NTF_RUN_RACE_RESULT_SUMMARY(GameModeBase gameModeBase, + List rewards, + List rewardsWithRank, + List raceResults, + Int32 playUserCount) + { + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfRunRaceResultSummary = new ClientToGameMessage.Types.GS2C_NTF_RUN_RACE_RESULT_SUMMARY(); + ntf.Message.NtfRunRaceResultSummary.RaceRewardResults.AddRange(rewards); + ntf.Message.NtfRunRaceResultSummary.RaceRewardResultsWithRank.AddRange(rewardsWithRank); + ntf.Message.NtfRunRaceResultSummary.RaceResults.AddRange(raceResults); + ntf.Message.NtfRunRaceResultSummary.TotalPlayUserCount = playUserCount; + + var instance_room = gameModeBase.getInstanceRoom(); + Log.getLogger().debug($"broadcast_GS2C_NTF_RUN_RACE_RESULT_SUMMARY ntf room Id : {instance_room.getMap().m_room_id} data : {JsonConvert.SerializeObject(ntf.Message.NtfRunRaceResultSummary)}"); + instance_room.Broadcast(ntf); + } + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceCheckPointAbusingBusinessLog.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceCheckPointAbusingBusinessLog.cs new file mode 100644 index 0000000..26128ae --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceCheckPointAbusingBusinessLog.cs @@ -0,0 +1,25 @@ +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; + +namespace GameServer; + +public class RunRaceCheckPointAbusingBusinessLog : ILogInvokerEx +{ + private GameModeRunRaceCheckPointAbusingLogInfo m_info; + + public RunRaceCheckPointAbusingBusinessLog(GameModeRunRaceCheckPointAbusingLogInfo logInfo) : base(LogDomainType.RunRaceCheckPointAbusing) + { + m_info = logInfo; + } + + public override bool hasLog() + { + return true; + } + + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(new GameModeRunRaceCheckPointAbusingLogInfo(this, m_info)); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceFinishRewardBusinessLog.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceFinishRewardBusinessLog.cs new file mode 100644 index 0000000..c10fdaa --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceFinishRewardBusinessLog.cs @@ -0,0 +1,24 @@ +using ServerBase; +using ServerCommon; + + +namespace GameServer; + +public class RunRaceFinishRewardBusinessLog : ILogInvokerEx +{ + private RunRaceFinishRewardLogInfo m_info; + + public RunRaceFinishRewardBusinessLog(string roomId, string userGuid, int rank, List rewards): base(LogDomainType.RunRaceFinishReward) + { + m_info = new RunRaceFinishRewardLogInfo(this, roomId, userGuid, rank, rewards); + } + + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(m_info); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceRespawnRewardBusinessLog.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceRespawnRewardBusinessLog.cs new file mode 100644 index 0000000..159f6a6 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceRespawnRewardBusinessLog.cs @@ -0,0 +1,22 @@ +using ServerBase; +using ServerCommon; + +namespace GameServer; + +public class RunRaceRespawnRewardBusinessLog : ILogInvokerEx +{ + private RunRaceRespawnRewardLogInfo m_info; + public RunRaceRespawnRewardBusinessLog(string roomId, string userGuid, int respawnCount, List rewards): base(LogDomainType.RunRaceRespawnReward) + { + m_info = new RunRaceRespawnRewardLogInfo(this, roomId, userGuid, respawnCount, rewards); + } + + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(m_info); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceRewardSummaryBusinessLog.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceRewardSummaryBusinessLog.cs new file mode 100644 index 0000000..3d67c4a --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceRewardSummaryBusinessLog.cs @@ -0,0 +1,24 @@ +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; + +namespace GameServer; + +public class RunRaceRewardSummaryBusinessLog : ILogInvokerEx +{ + private GameModeRunRaceRewardSummayLogInfo m_info; + public RunRaceRewardSummaryBusinessLog(GameModeRunRaceRewardSummayLogInfo logInfo) : base(LogDomainType.RunRaceRewardSummary) + { + m_info = logInfo; + } + + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(new GameModeRunRaceRewardSummayLogInfo(this, m_info)); + } + + public override bool hasLog() + { + return true; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceUnfinishRewardBusinessLog.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceUnfinishRewardBusinessLog.cs new file mode 100644 index 0000000..cdea63e --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Log/RunRaceUnfinishRewardBusinessLog.cs @@ -0,0 +1,23 @@ +using ServerBase; +using ServerCommon; + +namespace GameServer; + +public class RunRaceUnfinishRewardBusinessLog : ILogInvokerEx +{ + private RunRaceUnFinishRewardLogInfo m_info; + + public RunRaceUnfinishRewardBusinessLog(string roomId, string userGuid, List rewards): base(LogDomainType.RunRaceUnFinishReward) + { + m_info = new RunRaceUnFinishRewardLogInfo(this, roomId, userGuid, rewards); + } + + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(m_info); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/GameModeRunRace.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/GameModeRunRace.cs index 758b358..c94803c 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/GameModeRunRace.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/GameModeRunRace.cs @@ -1,88 +1,196 @@ -using GameServer.Contents.GameMode.Helper; -using GameServer.Contents.GameMode.Mode_Running.Manage; +using System.Collections.Concurrent; +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Manage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Meta; +using MetaAssets; using ServerBase; using ServerCommon; using ServerCore; +using ANCHOR_GUID = System.String; +using USER_GUID = System.String; +using CHECKPOINT_ID = System.Int32; +using TABLE_ID = System.Int32; namespace GameServer; -public class GameModeRunRace : GameModeRun where T : IRunningMode -{ - T m_run_mode_data; - public GameModeRunRace(T runModeData, InstanceRoom instanceRoom) : base(EntityType.GameModeRunRace, instanceRoom) + +public class GameModeRunRace : GameModeBase +{ + public RunRaceMetaData m_run_race_meta { get; } + public ConcurrentDictionary m_running_buffs = new(); + + public List> m_check_points { get; private set; } = new(); + + public ConcurrentDictionary m_assigned_start_pos { get; set; } = new(); + + public GameModeRunRace(InstanceRoom instanceRoom, int gameModeId, EntityType entityType) : base(entityType, instanceRoom, gameModeId, new RunRaceTransitionStrategy()) { - Log.getLogger().debug("run race constructor called"); - - m_run_mode_data = runModeData; - - Log.getLogger().debug("run race constructor done"); + m_run_race_meta = new RunRaceMetaData(gameModeId); } public override Task onInit() { Log.getLogger().debug("run race onInit called"); - - //제너릭 init - m_run_mode_data.initRunningMode(); - - //레이스 모드에 필요한 액션 추가 - addRaceEntityAction(); - //게임 모드에 필요한 상수값 입력 + //게임 모드에 필요한 상수값 입력 setDefaultMetaConstants(); - - - + //다 마무리 되면 부모 init 호출 var result = base.onInit(); - + Log.getLogger().debug("run race onInit done"); return result; } - - public override Task initAfterTimerCreate() - { - m_current_game_mode_state = new RaceStateReady(this); - - return Task.CompletedTask; - } - - private void addRaceEntityAction() + protected override void addDetailEntityActions() { - Log.getLogger().debug("run race addEntityAction called"); - - addEntityAction(new RaceStateCheckAction(this)); - addEntityAction(new RaceGameObjectSavePointInteractAction(this)); - - Log.getLogger().debug("run race addEntityAction done"); + addEntityAction(new RunRaceObjectUpdateAction(this)); + addEntityAction(new RunRacePlayAction(this)); + addEntityAction(new RunRaceCheckPointInteractAction(this)); + addEntityAction(new RunRaceBuffInteractAction(this)); + addEntityAction(new RunRaceGoalLineInteractionAction(this)); + addEntityAction(new RunRaceDeadAction(this)); + addEntityAction(new RunRaceRespawnAction(this)); + addEntityAction(new RunRaceRewardAction(this)); + addEntityAction(new RunRaceLoadCompleteAction(this)); + addEntityAction(new RunRaceReadyPosAssignAction(this)); } private void setDefaultMetaConstants() { m_ticker_interval_msecs = GameModeConstants.GAME_MODE_RUN_RACE_CHECK_INTERVAL_MSECS; } - - public override async Task taskUpdate() - { - - var state_check_action = getEntityAction(); - NullReferenceCheckHelper.throwIfNull(state_check_action, () => $"location attribute is null !!"); - var result = await state_check_action.stateUpdate(); - if (result.isFail()) return; - + protected override async Task deatilTaskUpdate() + { + //state와 상관 없는 처리 + var object_update_action = getEntityAction(); + NullReferenceCheckHelper.throwIfNull(object_update_action, () => $"object_update_action is null !!"); + var result = await object_update_action.updateObject(); + if (result.isFail()) return result; + + return result; } - + + protected override Task detailInitBeforeTaskCreate() + { + var result = new Result(); + return Task.FromResult(result); + } + + protected override async Task detailInitAfterTaskCreate() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + + protected override async Task setDetailGameInfoAfterLoadComplete() + { + var result = new Result(); + + //NullReferenceCheckHelper.throwIfNull(m_state_factory, () => $"m_state_factory is null !!! - id"); + //var created_state = m_state_factory.createState(); + // m_current_game_mode_state = created_state; + // m_current_game_mode_state.enter(this); + + await Task.CompletedTask; + return result; + } + + public override string toBasicString() { var basic_string = base.toBasicString() + $"GameModeRunRace....roomId : {getRoomId()}"; return basic_string; } + protected override void setDetailAnchorInfos(string anchorGuid, Anchor anchor) + { + if (false == MetaData.Instance._BattleObjectMetaTable.TryGetValue(anchor.TableID, out var tableMeta)) + { + //var err_msg = $"setDetailAnchorInfos _BattleObjectMetaTable table_meta not exist!!!! table_meta : {anchor.TableID}"; + //Log.getLogger().error(err_msg); + return; + } - -} \ No newline at end of file + addCheckPoint(tableMeta, anchor); + } + + private void addCheckPoint(BattleObjectMetaData tableMeta, Anchor anchor) + { + //check Point + if (tableMeta.ObjectType != EGameModeObjectType.CheckPoint || !anchor.HurdleValues.Contains("CheckpointID")) + { + //Log.getLogger().error($"setDetailAnchorInfos tableMeta.ObjectType error!!!! objectType : {tableMeta.ObjectType}, anchorGuid : {anchor.GUID}"); + return; + } + + var arr_hurdle_values = anchor.HurdleValues.Split(" "); + if (arr_hurdle_values.Length < 2) return; + + if (false == Int32.TryParse(arr_hurdle_values[1], out var checkpoint_id)) + { + Log.getLogger().error($"anchor.HurdleValues parse error tableMeta.ObjectType error!!!! objectType : {tableMeta.ObjectType}, anchorGuid : {anchor.GUID}"); + return; + } + RunRaceCheckPointValue check_point_value = new RunRaceCheckPointValue(checkpoint_id, anchor.TableID); + addCheckPoint(anchor.GUID, check_point_value); + + + } + + private void addCheckPoint(string anchorGuid, RunRaceCheckPointValue checkPoint) + { + m_check_points.Add(new KeyValuePair(anchorGuid, checkPoint)); + m_check_points = m_check_points.OrderBy(kv => kv.Value.m_check_point_id).ToList(); + } + + + public Int32 getCheckPointOrder(string anchorGuid) + { + if (string.IsNullOrEmpty(anchorGuid)) + return 0; + + var ordered = m_check_points + .OrderBy(kv => kv.Value.m_check_point_id) + .ToList(); + + var indexed = ordered + .Select((kv, idx) => new { kv.Key, Index = idx }) + .ToList(); + + // 3) anchorGuid와 매칭되는 항목 찾기 + var match = indexed + .FirstOrDefault(x => x.Key.Equals(anchorGuid)); + + // 4) 인덱스 계산 (없으면 -1) + int index = match?.Index ?? -1; + + // 5) 1-based로 반환 (없으면 0) + return index + 1; + + } + + + +} + + +public class RunRaceCheckPointValue +{ + public RunRaceCheckPointValue(CHECKPOINT_ID checkPointId, TABLE_ID tableId) + { + m_check_point_id = checkPointId; + m_table_id = tableId; + } + + public CHECKPOINT_ID m_check_point_id { get; set; } + public TABLE_ID m_table_id { get; set; } +} diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/GameModeRunRaceData.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/GameModeRunRaceData.cs deleted file mode 100644 index ea68369..0000000 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/GameModeRunRaceData.cs +++ /dev/null @@ -1,21 +0,0 @@ -using GameServer.Contents.GameMode.Manage.PlayManage; -using ServerCore; - -namespace GameServer.Contents.GameMode.Mode_Running.Manage; - -public class GameModeRunRaceData : IRunningMode -{ - public async Task initRunningMode() - { - Log.getLogger().debug("run race data initRunningMode called"); - - - - var result = new Result(); - - await Task.CompletedTask; - - Log.getLogger().debug("run race data initRunningMode done"); - return result; - } -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceDestroyHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceDestroyHandler.cs index 5b5629b..a870fae 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceDestroyHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceDestroyHandler.cs @@ -1,6 +1,7 @@ using GameServer.Contents.GameMode.Manage.LeaveManage; using GameServer.Contents.GameMode.Manage.PlayManage; using GameServer.Contents.GameMode.Mode_Running.Manage; +using MetaAssets; using ServerCommon; using ServerCore; @@ -8,11 +9,11 @@ namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Manage; public class RunRaceDestroyHandler : GameModeDestroyHandlerBase { - public RunRaceDestroyHandler(string roomId) : base(roomId, GameModeType.RUN_RACE) + public RunRaceDestroyHandler(string roomId, GameModeType gameModeType) : base(roomId, gameModeType) { - + } - + public override async Task postDestroy(IGameMode gameMode) { var result = new Result(); @@ -21,4 +22,4 @@ public class RunRaceDestroyHandler : GameModeDestroyHandlerBase return result; } -} \ No newline at end of file +} diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceDisconnectHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceDisconnectHandler.cs new file mode 100644 index 0000000..fb18652 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceDisconnectHandler.cs @@ -0,0 +1,40 @@ +using GameServer.Contents.GameMode.Manage.LeaveManage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Manage; + +public class RunRaceDisconnectHandler : GameModeDisconnectHandlerBase +{ + public RunRaceDisconnectHandler(IGameMode gameMode, Player player, GameModeType gameModeType) : base(gameModeType, gameMode, player) + { + } + + public override async Task disconnect() + { + var game_mode_base = m_game_mode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + var regulation_action = m_player.getEntityAction(); + var result = await regulation_action.playPenaltyCheck(game_mode_base, true); + if (result.isFail()) + { + Log.getLogger().warn($"playPenaltyCheck error : room_id - {game_mode_base.getRoomId()}, result : {result.toBasicString()}"); + return result; + } + game_mode_base.removePlayUserState(m_player.getUserGuid()); + return result; + } + + public override Task notifyAfterDisconnect() + { + return Task.FromResult(new Result()); + } + + public override Task logAfterDisconnect() + { + return Task.FromResult(new Result()); + } + +} diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceInitHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceInitHandler.cs index 6c56d38..41c55f8 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceInitHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceInitHandler.cs @@ -1,21 +1,20 @@ using GameServer.Contents.GameMode.Manage; +using MetaAssets; using ServerCommon; namespace GameServer.Contents.GameMode.Mode_Running.Manage; public class RunRaceInitHandler : GameModeInitHandlerBase { - public RunRaceInitHandler(InstanceRoom instanceRoom) : base(instanceRoom, GameModeType.RUN_RACE) + public RunRaceInitHandler(InstanceRoom instanceRoom, GameModeType gameModeType) : base(instanceRoom, gameModeType) { } public override Result gamedModeInstanceInitValidate() { var result = new Result(); - - //kihoon todo : running 추가 return result; } - -} \ No newline at end of file + +} diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceJoinHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceJoinHandler.cs index d2bb446..bfa49a6 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceJoinHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceJoinHandler.cs @@ -1,4 +1,5 @@ using GameServer.Contents.GameMode.Manage; +using MetaAssets; using ServerCommon; using ServerCore; diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceJoinSuccessHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceJoinSuccessHandler.cs index e3475c2..43085e4 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceJoinSuccessHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceJoinSuccessHandler.cs @@ -2,6 +2,7 @@ using GameServer.Contents.GameMode.Manage; using GameServer.Contents.GameMode.Manage.PlayManage; using GameServer.Contents.GameMode.Manage.StateManage; +using MetaAssets; using ServerBase; using ServerCommon; using ServerCore; @@ -25,9 +26,52 @@ public class RunRaceJoinSuccessHandler : GameModeJoinSuccessHandlerBase { var result = new Result(); + setStartPos(); + return Task.FromResult(result); } + private void setStartPos() + { + var room_id = m_instance_room.getMap().m_room_id; + + if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + { + var err_msg = $"not exist GameMode after join success setStartPos!!!! roomId : {room_id}"; + Log.getLogger().error(err_msg); + return ; + } + var run_race = gameMode as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"GameModeRunRace is null !!! - {m_player.toBasicString()}"); + + foreach (var pos in run_race.getStartPos()) + { + if (run_race.m_assigned_start_pos.ContainsKey(pos.Key)) continue; + if(run_race.m_assigned_start_pos.TryAdd(pos.Key, m_player.getUserGuid()) == false) + { + var err_msg = $"m_assigned_start_pos TryAdd fail - {m_player.toBasicString()}"; + Log.getLogger().error(err_msg); + continue; + } + + if (false == m_instance_room.getMap().getAnchors().TryGetValue(pos.Key, out var anchorInfo)) + { + var err_msg = $"setStartPos() anchorInfo not exist idx : {pos.Key}"; + Log.getLogger().error(err_msg); + continue; + } + var location_action = m_player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {m_player.toBasicString()}"); + var currenct_pos = location_action.getCurrentPos(); + + currenct_pos = anchorInfo.AnchorPos.Clone(); + currenct_pos.Z += 100; + location_action.tryUpdateCurrentPos(currenct_pos); + Log.getLogger().info($"{m_player.toBasicString()} setStartPos() - {pos.Key}"); + break; + } + } + public override async Task joinSuccessNotify() { var result = new Result(); @@ -47,13 +91,19 @@ public class RunRaceJoinSuccessHandler : GameModeJoinSuccessHandlerBase var game_mode_base = gameMode as GameModeBase; NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); - var state = game_mode_base.getGameModeState(); + var state = game_mode_base.getState(); var state_base = state as GameModeStateBase; NullReferenceCheckHelper.throwIfNull(state_base, () => $"state_base is null !!!"); - GameNotifyHelper.send_GS2C_NTF_GAME_STATE_UPDATE(m_player, m_instance_room, state.getStateType(), state_base.getNextStateChangeTime()); + m_player.send_S2C_NTF_SET_LOCATION(); + + GameNotifyHelper.send_GS2C_NTF_GAME_MODE_OVERALL_INFO(m_player, game_mode_base); + GameNotifyHelper.send_GS2C_NTF_GAME_STATE_UPDATE(m_player, game_mode_base.getInstanceRoom(), state.getStateType(), state.getNextStateChangeTime(), state_base.getUpdateReason()); + var host_user_guid = game_mode_base.m_host_migrator.getHostUserGuid(); + GameNotifyHelper.send_GS2C_NTF_P2P_HOST_UPDATE(m_player, host_user_guid); + await Task.CompletedTask; return result; } diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceLeaveHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceLeaveHandler.cs index 7387ea4..ac45fa4 100644 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceLeaveHandler.cs +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceLeaveHandler.cs @@ -1,20 +1,33 @@ using GameServer.Contents.GameMode.Manage.LeaveManage; using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; +using ServerBase; using ServerCommon; +using ServerCore; namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Manage; public class RunRaceLeaveHandler : GameModeLeaveHandlerBase { - public RunRaceLeaveHandler(Player player, string roomId) : base(player, roomId, GameModeType.RUN_RACE) + public RunRaceLeaveHandler(IGameMode gameMode, Player player, ServerInfo serverInfo, List invokers, string roomId, GameModeType gameModeType) + : base(gameMode, player, serverInfo, invokers, roomId, gameModeType) { - + } public override async Task postLeave(IGameMode gameMode) { - var result = new Result(); + var game_mode_base = m_game_mode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + var regulation_action = m_player.getEntityAction(); + var result = await regulation_action.playPenaltyCheck(game_mode_base); + if (result.isFail()) + { + Log.getLogger().warn($"playPenaltyCheck error : room_id - {game_mode_base.getRoomId()}, result : {result.toBasicString()}"); + return result; + } + game_mode_base.removePlayUserState(m_player.getUserGuid()); await Task.CompletedTask; return result; } @@ -34,4 +47,4 @@ public class RunRaceLeaveHandler : GameModeLeaveHandlerBase await Task.CompletedTask; return result; } -} \ No newline at end of file +} diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceMatchingReservationHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceMatchingReservationHandler.cs new file mode 100644 index 0000000..a3c8e92 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceMatchingReservationHandler.cs @@ -0,0 +1,24 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.State; +using GameServer.Matching; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Manage; + +public class RunRaceMatchingReservationHandler : MatchingReservationHandlerBase +{ + public override Task detailMatchingReservation(IGameMode gameMode, string userGuid, string roomGuid, string teamId) + { + Log.getLogger().info($"GameMode Match reserved....roodGuid : {roomGuid}, userGuid : {userGuid}, teamId : {teamId}"); + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + game_mode_base.updateUserState(userGuid, GameModePlayerState.MatchReserve); + + if (game_mode_base.getState() is RunRaceStateWait waitState) + { + waitState.deactiveAllUserLoadComplete(); + } + + return Task.FromResult(new Result()); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRacePreparationForLeavingHandler.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRacePreparationForLeavingHandler.cs new file mode 100644 index 0000000..620fd24 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRacePreparationForLeavingHandler.cs @@ -0,0 +1,31 @@ +using GameServer.Contents.GameMode.Manage.LeaveManage; +using GameServer.Contents.GameMode.Manage.PlayManage; +using MetaAssets; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Manage; + +public class RunRacePreparationForLeavingHandler : PreparationForLeavingGameBase +{ + public RunRacePreparationForLeavingHandler(Player player, IGameMode gameMode) : base(player, gameMode) + { + + } + + + public override async Task preparation() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + + public override async Task postPreparationForLeaving() + { + var result = new Result(); + + await Task.CompletedTask; + return result; + } + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceTransitionStrategy.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceTransitionStrategy.cs new file mode 100644 index 0000000..455ba75 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Manage/RunRaceTransitionStrategy.cs @@ -0,0 +1,143 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.Manage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.State; +using GameServer.Contents.GameMode.State; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Manage; + +public class RunRaceTransitionStrategy : GameModeTransitionStrategyBase +{ + public override IGameModeState? getNextStateByNone(IGameModeState currentState, IGameMode gameMode) + { + return new RunRaceStateCreated(gameMode); + } + + public override IGameModeState? getNextStateByCreate(IGameModeState currentState, IGameMode gameMode) + { + var created_state = currentState as RunRaceStateCreated; + NullReferenceCheckHelper.throwIfNull(created_state, () => $"create_state is null !!!"); + + var next_state_change_time = created_state.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + if (next_state_change_time < now) + { + return new GameModeStateLoadingWait(gameMode); + } + return null; + + } + + public override IGameModeState? getNextStateByWait(IGameModeState currentState, IGameMode gameMode) + { + var wait_state = currentState as RunRaceStateWait; + NullReferenceCheckHelper.throwIfNull(wait_state, () => $"run race wait_state is null !!!"); + + var next_state_change_time = wait_state.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + //시간 다 됐으면 ready로 변경 + if (next_state_change_time < now) + { + // (var result, var reason) = gameMode.isGameSustainable().Result; + // if (result.isFail()) + // { + // Log.getLogger().warn($"gameMode is not sustainable result : {result.toBasicString()}!!!"); + // return new RunRaceStateDestroyed(gameMode, reason); + // } + return new RunRaceStateReady(gameMode); + } + return null; + } + + public override IGameModeState? getNextStateByLoadingWait(IGameModeState currentState, IGameMode gameMode) + { + var next_state_change_time = currentState.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + if (next_state_change_time < now) + { + Log.getLogger().info($"loading wait time exceeded, so change state to Wait, userState : {JsonConvert.SerializeObject(game_mode_base.getUserState())}"); + return new RunRaceStateWait(gameMode, StateUpdateReasonType.LoadingWaitTimeExceeded); + } + if (game_mode_base.isUserLoadingCompleteAll()) + { + Log.getLogger().info($"All User loading complete, so change state to Wait, userState : {JsonConvert.SerializeObject(game_mode_base.getUserState())}"); + return new RunRaceStateWait(gameMode, StateUpdateReasonType.UserLoadComplete); + } + + return null; + } + + public override IGameModeState? getNextStateByReady(IGameModeState currentState, IGameMode gameMode) + { + var next_state_change_time = currentState.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + + return new RunRaceStatePlay(gameMode); + } + + public override IGameModeState? getNextStateByPlay(IGameModeState currentState, IGameMode gameMode) + { + var next_state_change_time = currentState.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + + return new RunRaceStateRewardSummary(gameMode); + } + + public override IGameModeState? getNextStateByRewardSummary(IGameModeState currentState, IGameMode gameMode) + { + var next_state_change_time = currentState.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + + return new RunRaceStateResult(gameMode); + } + + public override IGameModeState? getNextStateByEnd(IGameModeState currentState, IGameMode gameMode) + { + return null; + } + + public override IGameModeState? getNextStateByRounding(IGameModeState currentState, IGameMode gameMode) + { + return null;//Not use this Mode + } + + public override IGameModeState? getNextStateByRoundWait(IGameModeState currentState, IGameMode gameMode) + { + return null;//Not use this Mode + } + + public override IGameModeState? getNextStateByRoundEndAll(IGameModeState currentState, IGameMode gameMode) + { + return null;//Not use this Mode + } + + public override IGameModeState? getNextStateEventEnd(IGameModeState currentState, IGameMode gameMode) + { + return null;//Not use this Mode + } + + + public override IGameModeState? getNextStateByResult(IGameModeState currentState, IGameMode gameMode) + { + var next_state_change_time = currentState.getNextStateChangeTime(); + var now = DateTimeHelper.Current; + if (now < next_state_change_time) return null; + + return new RunRaceStateDestroyed(gameMode);//여기는 뭘 넘겨야 하나? + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Meta/RunRaceMetaData.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Meta/RunRaceMetaData.cs new file mode 100644 index 0000000..7e97b31 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Meta/RunRaceMetaData.cs @@ -0,0 +1,61 @@ +using ServerCommon; +using ServerCommon.Contents.GameMode; +using ServerCommon.GameMode; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Meta; + + +using PLAYER_COUNT = System.Int32; +using PLAYER_RANK = System.Int32; +using MIN_SPAWN_RANK = System.Int32; +using LAST_SAVE_CHECK_POINT = System.Int32; + +public class RunRaceMetaData +{ + public readonly Int32 m_game_mode_id; + public IReadOnlyDictionary<(PLAYER_COUNT, PLAYER_RANK), IGameModeRewardCondition> m_finish_rank_reward_conditions = new Dictionary<(PLAYER_COUNT, PLAYER_RANK), IGameModeRewardCondition>(); + public IReadOnlyDictionary<(PLAYER_COUNT, MIN_SPAWN_RANK), IGameModeRewardCondition> m_min_respawn_rank_reward_conditions = new Dictionary<(PLAYER_COUNT, MIN_SPAWN_RANK), IGameModeRewardCondition>(); + public IReadOnlyDictionary<(PLAYER_COUNT, LAST_SAVE_CHECK_POINT), IGameModeRewardCondition> m_unfinish_rank_reward_conditions = new Dictionary<(PLAYER_COUNT, LAST_SAVE_CHECK_POINT), IGameModeRewardCondition>(); + public int m_final_timer { get; private set; } = 20; + public int m_penalty_quit_count { get; private set;} = 3; + public int m_penalty_time { get; private set; } = 600;//초 + public int m_max_play_time { get; private set; } = 600;//초 + public int m_max_start_wait_time { get; private set; } = 20;//초 + public int m_max_loading_wait_time { get; private set; } = 60;//초 + public int m_match_count_deduction_point { get; private set; } = 15; + public int m_respawn_cnt_limit { get; private set; } = 3; + + public RunRaceMetaData(Int32 game_mode_id) + { + m_game_mode_id = game_mode_id; + loadMetaDatas(m_game_mode_id); + } + + private void loadMetaDatas(int gameModeId) + { + if(false == MetaData.Instance.m_game_mode_all_metas.TryGetValue(gameModeId, out var metaData)) + { + var err_msg = $"Not exist ffa metaData Data gameModeId : {gameModeId}"; + Log.getLogger().error(err_msg); + NullReferenceCheckHelper.throwIfNull(metaData, () => $"metaData is null !!!"); + } + + var run_race_meta = metaData.m_detail_meta as RunRaceMeta; + NullReferenceCheckHelper.throwIfNull(run_race_meta, () => $"run_race_meta is null !!!"); + + m_finish_rank_reward_conditions = run_race_meta.getFinishRankReward(); + m_min_respawn_rank_reward_conditions = run_race_meta.getRespawnReward(); + m_unfinish_rank_reward_conditions = run_race_meta.getUnFinishRankReward(); + m_final_timer = run_race_meta.m_final_timer; + m_penalty_quit_count = run_race_meta.m_penalty_quit_count; + m_penalty_time = run_race_meta.m_penalty_time; + + m_max_play_time = metaData.m_common_data.MaxPlayTime; + m_max_start_wait_time = metaData.m_common_data.MaxStartWaitTime; + m_max_loading_wait_time = metaData.m_common_data.MaxLoadingWaitTime; + m_match_count_deduction_point = run_race_meta.m_match_count_deduction_point; + m_respawn_cnt_limit = run_race_meta.m_respawn_cnt_limit; + } + +} diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/Reward/RewardRunRace.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Reward/RewardRunRace.cs new file mode 100644 index 0000000..7116c59 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/Reward/RewardRunRace.cs @@ -0,0 +1,25 @@ +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.Reward; + +public class RewardRunRace : RewardBase +{ + public RewardRunRace(Player player, string userGuid, List rewards) : base(player, userGuid, rewards) + { + + } + + public override Task prepareReward() + { + return Task.FromResult(new Result()); + } + + //===================================================================================== + // 보상처리 후 필요한 로직들 처리 + //===================================================================================== + public override Task finalizeReward() + { + var result = new Result(); + + + return Task.FromResult(result); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateDestroyed.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateDestroyed.cs deleted file mode 100644 index 39f3ba5..0000000 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateDestroyed.cs +++ /dev/null @@ -1,34 +0,0 @@ -using GameServer.Contents.GameMode.Manage.PlayManage; -using GameServer.Contents.GameMode.Manage.StateManage; - -namespace GameServer.Contents.GameMode.Mode_Running.Manage; - -public class RaceStateDestroyed: GameModeStateBase -{ - public RaceStateDestroyed(IGameMode gameMode) : base(gameMode, GameModeState.Destroyed) - { - - } - - public override void enter() - { - - } - - public override void update() - { - - } - - public override void exit() - { - - } - - public override GameModeState checkState() - { - - return getStateType(); - } - -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateEnd.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateEnd.cs deleted file mode 100644 index d27ca12..0000000 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateEnd.cs +++ /dev/null @@ -1,33 +0,0 @@ -using GameServer.Contents.GameMode.Manage.PlayManage; -using GameServer.Contents.GameMode.Manage.StateManage; - -namespace GameServer.Contents.GameMode.Mode_Running.Manage; - -public class RaceStateEnd : GameModeStateBase -{ - public RaceStateEnd(IGameMode gameMode) : base(gameMode, GameModeState.End) - { - - } - - public override void enter() - { - - } - - public override void update() - { - - } - - public override void exit() - { - - } - - public override GameModeState checkState() - { - return getStateType(); - } - -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateReady.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateReady.cs deleted file mode 100644 index 01efabe..0000000 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateReady.cs +++ /dev/null @@ -1,38 +0,0 @@ -using GameServer.Contents.GameMode.Manage.PlayManage; -using GameServer.Contents.GameMode.Manage.StateManage; -using ServerCore; - -namespace GameServer; - -public class RaceStateReady : GameModeStateBase -{ - public RaceStateReady(IGameMode gameMode) : base(gameMode, GameModeState.Ready) - { - } - - public override void enter() - { - - } - - public override void update() - { - - } - - public override void exit() - { - //Ready 상태에서 나갈때는 딱히 할게 없다. - } - - public override GameModeState checkState() - { - var now = DateTimeHelper.Current; - if (m_next_state_change_time <= now) - { - return GameModeState.Start; - } - - return getStateType(); - } -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateStart.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateStart.cs deleted file mode 100644 index b86133b..0000000 --- a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RaceStateStart.cs +++ /dev/null @@ -1,45 +0,0 @@ -using GameServer.Contents.GameMode.Helper; -using GameServer.Contents.GameMode.Manage.PlayManage; -using GameServer.Contents.GameMode.Manage.StateManage; -using ServerCore; - -namespace GameServer.Contents.GameMode.Mode_Running.Manage; - -public class RaceStateStart : GameModeStateBase -{ - public RaceStateStart(IGameMode gameMode) : base(gameMode, GameModeState.Start) - { - - } - - public override void enter() - { - getStateType(); - - - - var instance_room = m_game_mode_base.getInstanceRoom(); - - var next_update_time = DateTimeHelper.Current.AddMinutes(10); //kihoon todo : 이거 meta로 빼야된다. - - GameNotifyHelper.broadcast_GS2C_NTF_GAME_STATE_UPDATE(instance_room, getStateType(), next_update_time); - - } - - public override void update() - { - - } - - public override void exit() - { - - } - - public override GameModeState checkState() - { - - return getStateType(); - } - -} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateCreated.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateCreated.cs new file mode 100644 index 0000000..39d3468 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateCreated.cs @@ -0,0 +1,49 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.State; + +public class RunRaceStateCreated : GameModeStateBase +{ + //private DateTime m_next_state_change_time = DateTimeHelper.Current; + public RunRaceStateCreated(IGameMode gameMode) : base(GameModeState.Created, gameMode) + { + + } + + public override Task enterDetail() + { + extendStateTime(TimeSpan.FromSeconds(1), "RunRaceStateCreated" ); + return Task.CompletedTask; + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateDestroyed.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateDestroyed.cs new file mode 100644 index 0000000..4decba6 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateDestroyed.cs @@ -0,0 +1,47 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; + +namespace GameServer.Contents.GameMode.Mode_Running.Manage; + +public class RunRaceStateDestroyed: GameModeStateBase +{ + public RunRaceStateDestroyed(IGameMode gameMode, StateUpdateReasonType reason = StateUpdateReasonType.NormalFlow) : base(GameModeState.Destroyed, gameMode, reason) + { + + } + + public override Task enterDetail() + { + + return Task.CompletedTask; + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateEnd.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateEnd.cs new file mode 100644 index 0000000..bb4aa7b --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateEnd.cs @@ -0,0 +1,47 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; + +namespace GameServer.Contents.GameMode.Mode_Running.Manage; + +public class RunRaceStateEnd : GameModeStateBase +{ + public RunRaceStateEnd(IGameMode gameMode) : base(GameModeState.End, gameMode) + { + + } + + public override Task enterDetail() + { + + return Task.CompletedTask; + } + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStatePlay.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStatePlay.cs new file mode 100644 index 0000000..5a7ce14 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStatePlay.cs @@ -0,0 +1,107 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using ServerBase; +using ServerCommon.GameMode; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.Manage; + +public class RunRaceStatePlay : GameModeStateBase +{ + public RunRaceStatePlay(IGameMode gameMode) : base(GameModeState.Play, gameMode) + { + } + + private bool m_is_add_match_count = false; + + public override Task enterDetail() + { + var run_race = getGameMode() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"game_mode is null !!!"); + + var play_action = run_race.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + + play_action.onClear(); + play_action.registerPlayableUser(); + play_action.setStartTime(); + + var next_update_time = DateTimeHelper.Current.AddSeconds(run_race.m_run_race_meta.m_max_play_time); + setStateTime(next_update_time, "InitialPlayTime"); + return Task.CompletedTask; + } + + public void handleFirstPlayerFinish() + { + var run_race = getGameMode() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_race is null !!!"); + + var play_state = run_race.getState() as RunRaceStatePlay; + NullReferenceCheckHelper.throwIfNull(play_state, () => $"run_race is null !!!"); + + var now = DateTimeHelper.Current; + var next_state_change_time = play_state.getNextStateChangeTime(); + + int remain_second = Math.Max(0, (int)(next_state_change_time - now).TotalSeconds); + int min_remain_second = Math.Min(remain_second, run_race.m_run_race_meta.m_final_timer); + + + setStateTime(DateTimeHelper.Current.AddSeconds(min_remain_second), "FirstPlayerFinished", true); + } + public void handleAllPlayersFinish() + { + setStateTime(DateTimeHelper.Current.AddSeconds(1), "AllPlayersFinished", true); + } + + public override async Task updateDetail() + { + var game_mode_base = getGameMode() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode is null !!!"); + var buff_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(buff_action, () => $"buff_action is null !!!"); + + if (m_is_add_match_count == false) + { + var now = DateTimeHelper.Current; + var run_race_meta = game_mode_base.getMetas().m_detail_meta as RunRaceMeta; + NullReferenceCheckHelper.throwIfNull(run_race_meta, () => $"run_race_meta is null !!!"); + var match_add_time = m_old_time.AddSeconds(run_race_meta.m_match_count_deduction_point); + if (match_add_time < now) + { + var play_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + await play_action.matchCountCheck(); + m_is_add_match_count = true; + } + } + + return new Result(); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + + public override Task exitDetail() + { + return Task.CompletedTask; + } + public override void notifyDetailAfterExit() + { + + } + public override Task postProcess() + { + return Task.CompletedTask; + } + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateReady.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateReady.cs new file mode 100644 index 0000000..5287758 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateReady.cs @@ -0,0 +1,66 @@ +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using ServerCore; + +namespace GameServer; + +public class RunRaceStateReady : GameModeStateBase +{ + public RunRaceStateReady(IGameMode gameMode) : base(GameModeState.Ready, gameMode) + { + } + public override Task enterDetail() + { + var run_race = getGameMode() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"game_mode_base is null !!!"); + + + var pos_assign_action = run_race.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(pos_assign_action, () => $"pos_assign_action is null !!!"); + pos_assign_action.procceedReadyPosAssign(); + + var next_state_change_time = getNextStateChangeTime(); + var update_time = next_state_change_time.AddSeconds(run_race.getMetas().m_common_data.GameReadyCount); + + setStateTime(update_time, "RunRaceStateReady"); + + + + + return Task.CompletedTask; + } + + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } + + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateResult.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateResult.cs new file mode 100644 index 0000000..1985b44 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateResult.cs @@ -0,0 +1,56 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using ServerBase; +using ServerCommon.GameMode; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.State; + +public class RunRaceStateResult: GameModeStateBase +{ + public RunRaceStateResult(IGameMode gameMode) : base(GameModeState.Result, gameMode) + { + + } + + public override async Task enterDetail() + { + var run_race = getGameMode() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_race is null !!!"); + + var next_update_time = DateTimeHelper.Current.AddSeconds(run_race.getMetas().m_common_data.ResultUIWaitTime); + setStateTime(next_update_time, "InitialResultTime"); + await Task.CompletedTask; + } + + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateRewardSummary.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateRewardSummary.cs new file mode 100644 index 0000000..63d57cb --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateRewardSummary.cs @@ -0,0 +1,129 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using GameServer.EventInvokers; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCommon.GameMode; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.State; + +public class RunRaceStateRewardSummary : GameModeStateBase +{ + private bool m_is_reward_summary_done { get; set; } + private GameModeRunRaceRewardSummayLogInfo m_log_info; + + + public RunRaceStateRewardSummary(IGameMode gameMode) : base(GameModeState.RewardSummary, gameMode) + { + m_is_reward_summary_done = false; + m_log_info = new GameModeRunRaceRewardSummayLogInfo(); + } + + public override async Task enterDetail() + { + var game_mode_base = getGameMode() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + var race_meta = game_mode_base.getMetas().m_detail_meta as RunRaceMeta; + NullReferenceCheckHelper.throwIfNull(race_meta, () => $"race_meta is null !!!"); + + var play_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + play_action.setEndTime(); + + + var next_update_time = DateTimeHelper.Current.AddSeconds(race_meta.m_result_scene_tracsition_wait_time); + setStateTime(next_update_time, "InitialRewardSummaryTime"); + m_is_reward_summary_done = false; + await Task.CompletedTask; + } + + public override async Task updateDetail() + { + //보상 처리 로직 호출해서 처리 해준다 + var game_mode_base = getGameMode() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var reward_action = game_mode_base.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(reward_action, () => $"reward_action is null !!!"); + + (var result, m_log_info) = await reward_action.raceReward(); + if (result.isFail()) return result; + + await rankingProcess(); + m_is_reward_summary_done = true; + setStateTime(DateTimeHelper.Current, "RunRaceRewardDone", true); + + + return result; + } + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + var invokers = new List(); + var business_log = new RunRaceRewardSummaryBusinessLog(m_log_info); + invokers.Add(business_log); + var log_action = new LogActionEx(LogActionType.RunRaceResultSummary); + + var game_mode_base = getGameMode() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + BusinessLogger.collectLogs(log_action, game_mode_base, invokers); + } + + public override async Task exitDetail() + { + await notifyGameEventAction(); + // return Task.CompletedTask; + } + public override void notifyDetailAfterExit() + { + + } + + public override async Task postProcess() + { + await Task.CompletedTask; + } + + public async Task rankingProcess() + { + await Task.CompletedTask; + //랭킹으로 데이터 쏜다.. + var start_time = m_log_info.m_start_time; + var complete_times = m_log_info.m_completion_time; + + var game_mode_base = getGameMode() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + + Log.getLogger().info($" postProcess complete_times {complete_times.Count}"); + foreach (var complete_time in complete_times) + { + var rank_time = complete_time.Value; + var rank_time_diff = rank_time - start_time; + var rank_time_diff_msec = rank_time_diff.TotalMilliseconds; + var player_guid = complete_time.Key; + + if (false == game_mode_base.getInstanceRoom().tryGetInstanceMember(player_guid, out var player)) + { + Log.getLogger().error($"Run Race StateRewardSummary player not found !!! player_guid : {player_guid}"); + continue; + } + + await player.getGameEventCollector().collectEvent(new GameEventRunRaceCompleteTime(player, game_mode_base.getGameModeId(), game_mode_base.getGameRoomInfo().InstanceMetaId, rank_time_diff_msec)); + Log.getLogger().debug($"Run Race StateRewardSummary player complete time collect !!! player_guid : {player_guid} rank_time_diff_msec : {rank_time_diff_msec}"); + + } + } + + private Task notifyGameEventAction() + { + return Task.CompletedTask; + } +} diff --git a/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateWait.cs b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateWait.cs new file mode 100644 index 0000000..cf7f738 --- /dev/null +++ b/GameServer/Contents/GameMode/Mode-Running/ModeRace/State/RunRaceStateWait.cs @@ -0,0 +1,96 @@ +using GameServer.Contents.GameMode.Action; +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using ServerCore; + +namespace GameServer.Contents.GameMode.Mode_Running.ModeRace.State; + +public class RunRaceStateWait : GameModeStateBase +{ + private bool is_wait_all_user_loading = false; + public RunRaceStateWait(IGameMode gameMode, StateUpdateReasonType updateReason = StateUpdateReasonType.None) : base(GameModeState.Wait, gameMode, updateReason) + { + + } + + public override Task enterDetail() + { + var run_race = getGameMode() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_race is null !!!"); + + var update_time = DateTimeHelper.Current.AddSeconds(run_race.m_run_race_meta.m_max_start_wait_time + run_race.getMetas().m_common_data.MaxLoadingWaitTime); + setStateTime(update_time, "RunRaceStateWait"); + + return Task.CompletedTask; + + } + + public override Task updateDetail() + { + var now = DateTimeHelper.Current; + + var run_race = getGameMode() as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_race is null !!!"); + var duration_time = run_race.getMetas().m_common_data.MaxLoadingWaitTime * -1; + var none_buffer_wait_time = m_next_state_change_time.AddSeconds(duration_time); + + var result = new Result(); + + if (now < none_buffer_wait_time) return Task.FromResult(result); + + //기본 대기시간 다 지났고 모든 유저가 로딩이 완료 됐으면 여기서 시간 업데이트 처리 + if (false == is_wait_all_user_loading) + { + if (run_race.isUserLoadingCompleteAll()) + { + m_next_state_change_time.AddSeconds(duration_time); + setStateTime(m_next_state_change_time, "RunRaceStateWaitAllUserComplete", true); + is_wait_all_user_loading = true; + } + } + + + return Task.FromResult(result); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + //아직 로딩 안된 유저 가지고 와서 kick 날려 준다. + var run_race = m_current_game_mode as GameModeRunRace; + NullReferenceCheckHelper.throwIfNull(run_race, () => $"run_race is null !!!"); + var play_action = run_race.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(play_action, () => $"play_action is null !!!"); + play_action.kickUserIfNotLoaded(); + + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public void isGameSustainable() + { + + } + + public void deactiveAllUserLoadComplete() + { + is_wait_all_user_loading = false; + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/PacketHandler/GameLeavePacketHandler.cs b/GameServer/Contents/GameMode/PacketHandler/GameLeavePacketHandler.cs new file mode 100644 index 0000000..485b279 --- /dev/null +++ b/GameServer/Contents/GameMode/PacketHandler/GameLeavePacketHandler.cs @@ -0,0 +1,258 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using Newtonsoft.Json; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer.PacketHandler; + +[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_LEAVE_GAME_INSTANCE), typeof(GameLeavePacketHandler), typeof(GameLoginListener))] +public class GameLeavePacketHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"entity_player is null !!!"); + var req_msg = recvMessage as ClientToGame; + ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + var request = req_msg.Request.ReqLeaveGameInstance; + if (null == request) + { + err_msg = $"failed to get request message : Invalid Request message - {req_msg.Request.MsgCase}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.InvalidArgument, err_msg); + send_S2C_ACK_LEAVE_GAME_INSTANCE(player, result, null); + return result; + } + + var packet_create_time = request.PacketCreateTime; + if (packet_create_time is not null) + { + Log.getLogger().info($"Leave Battle Instance packet received, packetCreateTime : {packet_create_time}, player : {player.toBasicString()}"); + } + + var attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(attribute, () => $"LocationAttribute is null !!! - player:{player.toBasicString()}"); + + if (false == MetaData.Instance._IndunTable.TryGetValue(attribute.CurrentIndunLocation.InstanceMetaId, out var prev_indun_data) || null == prev_indun_data) + { + err_msg = $"failed to get indun data !!! - {player.toBasicString()}"; + result.setFail(ServerErrorCode.NotFoundIndunData, err_msg); + Log.getLogger().error(err_msg); + send_S2C_ACK_LEAVE_GAME_INSTANCE(player, result, null); + return result; + } + + List invokers = new(); + var departure_position_info = player.getCurrentPositionInfo(); + var departure_position_log_info = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Departure, departure_position_info); + var departure_position_business_log = new PositionBusinessLog(departure_position_log_info); + invokers.Add(departure_position_business_log); + + var server_logic = GameServerApp.getServerLogic(); + + // 1. 이동할 서버 조회 + var channel_server_name = attribute.LastestChannelServerLocation.ServerName; + var world_meta_id = attribute.LastestChannelServerLocation.WorldMetaId; + + var move_server_info = await server_logic.getReturnToServerInfo(channel_server_name, ServerType.Channel, player, (ushort)world_meta_id); + if (null == move_server_info) + { + err_msg = $"Failed to get balanced GameServer !!! - {player.toBasicString()}"; + result.setFail(ServerErrorCode.NoServerConnectable, err_msg); + Log.getLogger().error(err_msg); + send_S2C_ACK_LEAVE_GAME_INSTANCE(player, result, null); + return result; + } + + // 2. 이동 예약 요청 + var message = new ServerMessage.Types.GS2GS_REQ_RESERVATION_ENTER_TO_SERVER(); + message.MoveType = ServerMoveType.Force; + message.RequestUserGuid = player.getUserGuid(); + message.RequestServerName = GameServerApp.getServerLogic().getServerName(); + + var reserved = await server_logic.getReservationManager().registerReservationEnterToServer(message, move_server_info.Name); + + // 예약 실패 체크 + if (null == reserved) + { + err_msg = $"Failed to reservation enter to game server!!! - {nameof(LeaveInstancePacketHandler)}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.FailedToReservationEnter, err_msg); + send_S2C_ACK_LEAVE_GAME_INSTANCE(player, result, null); + return result; + } + + var location_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); + + var current_indun_location = location_action.getCurrentLocation() as IndunLocation; + NullReferenceCheckHelper.throwIfNull(current_indun_location, () => $"location_action is null !!! - {player.toBasicString()}"); + var room_id = current_indun_location.InstanceRoomId; + if (room_id == string.Empty) + { + err_msg = $"current_indun_location instance_room_Id is empty!!!! - {nameof(LeaveInstancePacketHandler)}"; + result.setFail(ServerErrorCode.ValidServerNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + send_S2C_ACK_LEAVE_GAME_INSTANCE(player, result, null); + return result; + } + + if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + { + err_msg = $"GameLeavePacketHandler game_instance_room is null : room_id - {room_id}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeClassIsNull, err_msg); + send_S2C_ACK_LEAVE_GAME_INSTANCE(player, result, null); + return result; + } + + + // 4. instance room 떠나기 + result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "LeaveGameInstance", leaveGameInstanceDelegate); + if (result.isFail()) + { + err_msg = $"Failed to runTransactionRunner() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + send_S2C_ACK_LEAVE_GAME_INSTANCE(player, result, null); + return result; + } + + return result; + + async Task leaveGameInstanceDelegate() => await leaveGameInstanceAsync(gameMode, player, move_server_info, invokers, room_id); + + } + + public async Task leaveGameInstanceAsync(IGameMode gameMode, Player player, ServerInfo serverInfo, List invokers, string roomId) + { + //ack packet은 성공인 경우만 여기서 처리 + var server_logic = GameServerApp.getServerLogic(); + string err_msg = string.Empty; + + (var result, var leave_handler) = GameModeHelper.getGameModeLeaveHandler(gameMode, player, serverInfo, invokers, roomId); + if (result.isFail() || leave_handler is null) + { + err_msg = $"tryLeaveBattelInstance instance_room_Id is Empty player : {player.toBasicString()}"; + result.setFail(ServerErrorCode.GameModeLeaveHandlerNotExist, err_msg); + return result; + } + + result = await leave_handler.gameModeLeave(); + if (result.isFail()) return result; + + + var location_action = player.getEntityAction(); + var current_indun_location = location_action.getCurrentLocation() as IndunLocation; + NullReferenceCheckHelper.throwIfNull(current_indun_location, () => $"current_indun_location is null !!! - {player.toBasicString()}"); + + var instance_room_Id = current_indun_location.InstanceRoomId; + if (instance_room_Id == string.Empty) + { + err_msg = $"tryLeaveBattelInstance instance_room_Id is Empty player : {player.toBasicString()}"; + result.setFail(ServerErrorCode.BattleInstanceInfoNotExist, err_msg); + return result; + } + + result = await location_action.tryMoveToChannel(); + if (result.isFail()) + { + err_msg = $"Fail to tryMoveToIndun"; + Log.getLogger().error(err_msg); + return result; + } + + var buff_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(buff_action, () => $"buff_action is null !!! - {player.toBasicString()}"); + + result = await buff_action.MoveServer(EPlaceType.World); + if (result.isFail()) + { + return result; + } + + // 3. otp 생성 + (result, var reserved_to_switch_server) = await ServerConnectionSwitchHelper.startServerSwitch(player, server_logic.getRedisConnector(), serverInfo.Name); + if (result.isFail() || null == reserved_to_switch_server) + { + err_msg = $"Fail to startServerSwitch() !!! : {result.toBasicString()}"; + Log.getLogger().error(result.toBasicString()); + return result; + } + + var game_login_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_login_action, () => $"game_login_action is null !!! - {player.toBasicString()}"); + + var login_cache = game_login_action.getLoginCacheRequest()?.getLoginCache(); + NullReferenceCheckHelper.throwIfNull(login_cache, () => $"LoginCache is null !!! - player:{player.toBasicString()}"); + + login_cache.ReservedToSwitchServer = reserved_to_switch_server; + + var gameServer_connection_info = new ServerConnectInfo + { + ServerAddr = serverInfo.Address, + ServerPort = serverInfo.Port, + Otp = reserved_to_switch_server.OneTimeKey, + }; + + var location_attribute = player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(location_attribute, () => $"location attribute is null !!! - {player.toBasicString()}"); + + var batch = new QueryBatchEx(player, LogActionType.LeaveInstance, server_logic.getDynamoDbClient()); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + batch.addQuery(new QueryFinal()); + } + + var server_name = ServerType.Indun.toServerName(gameServer_connection_info.ServerAddr, (ushort)gameServer_connection_info.ServerPort); + + var arrival_position_log_info = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Arrival, server_name, "" + , MapFileType.World, 0, new()); + var arrival_position_business_log = new PositionBusinessLog(arrival_position_log_info); + invokers.Add(arrival_position_business_log); + + //로그 추가 + batch.appendBusinessLogs(invokers); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) + { + err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return result; + } + + send_S2C_ACK_LEAVE_GAME_INSTANCE(player, result, gameServer_connection_info); + + Log.getLogger().debug($"LeaveGameInstance gameServer_connection_info : {JsonConvert.SerializeObject(gameServer_connection_info)}, player : {player.toBasicString()}"); + return result; + } + + public static bool send_S2C_ACK_LEAVE_GAME_INSTANCE(Player owner, Result result, ServerConnectInfo? server_info) + { + var ack_packet = new ClientToGame(); + ack_packet.Response = new ClientToGameRes(); + + ack_packet.Response.ErrorCode = result.ErrorCode; + ack_packet.Response.AckLeaveGameInstance = new ClientToGameRes.Types.GS2C_ACK_LEAVE_GAME_INSTANCE(); + + if (result.isSuccess() && null != server_info) + { + ack_packet.Response.AckLeaveGameInstance.GameServerAddr = server_info.ServerAddr; + ack_packet.Response.AckLeaveGameInstance.GameServerPort = server_info.ServerPort; + ack_packet.Response.AckLeaveGameInstance.Otp = server_info.Otp; + } + + if (false == GameServerApp.getServerLogic().onSendPacket(owner, ack_packet)) + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/PacketHandler/GameModeLoadCompletePacketHandler.cs b/GameServer/Contents/GameMode/PacketHandler/GameModeLoadCompletePacketHandler.cs new file mode 100644 index 0000000..5cda98d --- /dev/null +++ b/GameServer/Contents/GameMode/PacketHandler/GameModeLoadCompletePacketHandler.cs @@ -0,0 +1,79 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using Google.Protobuf; +using ServerBase; +using ServerCore; + +namespace GameServer.PacketHandler; + +[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_GAME_MODE_LOAD_COMPLETE), typeof(GameModeLoadCompletePacketHandler), typeof(GameLoginListener))] +public class GameModeLoadCompletePacketHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + await Task.CompletedTask; + var result = new Result(); + var err_msg = string.Empty; + + var player = session as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + + var request = req_msg.Request.ReqGameModeLoadComplete; + if (null == request) + { + err_msg = $"failed to get request message : Invalid Request message - {req_msg.Request.MsgCase}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.InvalidArgument, err_msg); + send_S2C_ACK_GAME_MODE_LOAD_COMPLETE(player, result); + return result; + } + var room_id = player.getCurrentInstanceRoomId(); + if(false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + { + err_msg = $"gameMode is null : room_id - {room_id}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeClassIsNull, err_msg); + send_S2C_ACK_GAME_MODE_LOAD_COMPLETE(player, result); + return result; + } + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_moce_base is null !!!"); + + var load_complete_action = GameModeHelper.getPlayerLoadCompleteAction(game_mode_base); + if (load_complete_action is null) + { + err_msg = $"load_complete_action invalid : room_id - {room_id},player : {player.toBasicString()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeInvalidAction, err_msg); + send_S2C_ACK_GAME_MODE_LOAD_COMPLETE(player, result); + return result; + } + + + result = await load_complete_action.loadComplete(player); + if (result.isFail()) + { + send_S2C_ACK_GAME_MODE_LOAD_COMPLETE(player, result); + return result; + } + + send_S2C_ACK_GAME_MODE_LOAD_COMPLETE(player, result); + + return result; + } + + + public bool send_S2C_ACK_GAME_MODE_LOAD_COMPLETE(Player player, Result result) + { + var ack_packet = new ClientToGame(); + ack_packet.Response = new ClientToGameRes(); + + ack_packet.Response.ErrorCode = result.ErrorCode; + ack_packet.Response.AckGameModeLoadComplete = new ClientToGameRes.Types.GS2C_ACK_GAME_MODE_LOAD_COMPLETE(); + + return GameServerApp.getServerLogic().onSendPacket(player, ack_packet); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/PacketHandler/GameObjectInteractionPacketHandler.cs b/GameServer/Contents/GameMode/PacketHandler/GameObjectInteractionPacketHandler.cs index 1a6d217..5973b1a 100644 --- a/GameServer/Contents/GameMode/PacketHandler/GameObjectInteractionPacketHandler.cs +++ b/GameServer/Contents/GameMode/PacketHandler/GameObjectInteractionPacketHandler.cs @@ -1,30 +1,31 @@ using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.GameMode.InteractionObject; using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.EventInvokers; +using GameServer.Global; using Google.Protobuf; +using MetaAssets; using ServerBase; using ServerCommon; using ServerCore; namespace GameServer.PacketHandler; -[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_GAME_OBJECT_INTERACTION), typeof(GameObjectInteractionPacketHandler), typeof(GameLoginListener))] +[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_GAME_OBJECT_INTERACTION), + typeof(GameObjectInteractionPacketHandler), typeof(GameLoginListener))] public class GameObjectInteractionPacketHandler : PacketRecvHandler { public override async Task onProcessPacket(ISession session, IMessage recvMessage) { - await Task.CompletedTask; var result = new Result(); - var err_msg = string.Empty; - - var server_logic = GameServerApp.getServerLogic(); - + var player = session as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - + var req_msg = recvMessage as ClientToGame; ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); - + var request = req_msg.Request.ReqGameObjectInteraction; if (null == request) { @@ -34,16 +35,18 @@ public class GameObjectInteractionPacketHandler : PacketRecvHandler send_S2C_ACK_GAME_OBJECT_INTERACTION(player, result, ""); return result; } - + var anchor_guid = request.AnchorGuid; - ArgumentNullReferenceCheckHelper.throwIfNull(anchor_guid, () => $"anchor_guid is null !!! - {player.toBasicString()}"); + ArgumentNullReferenceCheckHelper.throwIfNull(anchor_guid, + () => $"anchor_guid is null !!! - {player.toBasicString()}"); var paacket_create_time = request.PacketCreateTime; - ArgumentNullReferenceCheckHelper.throwIfNull(paacket_create_time, () => $"paacket_create_time is null !!! - {player.toBasicString()}"); - + ArgumentNullReferenceCheckHelper.throwIfNull(paacket_create_time, + () => $"paacket_create_time is null !!! - {player.toBasicString()}"); + DateTime interaction_time = paacket_create_time.ToDateTime(); - + var room_id = player.getCurrentInstanceRoomId(); - if(false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) { err_msg = $"gameMode is null : room_id - {room_id}"; Log.getLogger().error(err_msg); @@ -52,57 +55,75 @@ public class GameObjectInteractionPacketHandler : PacketRecvHandler return result; } - var game_mode_base = gameMode as GameModeBase; NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_moce_base is null !!!"); if (false == MapDataTable.Instance.getAnchor(anchor_guid, out var anchor)) { - err_msg = $"anchor_guid invalid : room_id - {room_id}, anchor_guid : {anchor_guid}, player : {player.toBasicString()}"; - Log.getLogger().error(err_msg); - result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_msg); - send_S2C_ACK_GAME_OBJECT_INTERACTION(player, result, anchor_guid); - return result; - } - - if(false == game_mode_base.getInstanceRoom().getMap().getAnchors().TryGetValue(anchor_guid, out var anchor2)) - { - err_msg = $"anchor_guid invalid : room_id - {room_id}, anchor_guid : {anchor_guid}, player : {player.toBasicString()}"; - Log.getLogger().error(err_msg); - result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_msg); - send_S2C_ACK_GAME_OBJECT_INTERACTION(player, result, anchor_guid); - return result; - } - - var anchor_info = game_mode_base.getInstanceRoom().getMap().findAnchorInfo(anchor_guid); - if(anchor_info is null) - { - err_msg = $"anchor_guid invalid : room_id - {room_id}, anchor_guid : {anchor_guid}, player : {player.toBasicString()}"; + err_msg = + $"getAnchor anchor_guid invalid : room_id - {room_id}, anchor_guid : {anchor_guid}, player : {player.toBasicString()}"; Log.getLogger().error(err_msg); result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_msg); send_S2C_ACK_GAME_OBJECT_INTERACTION(player, result, anchor_guid); return result; } - var interact_action = GameModeHelper.getObjectInteractAction(game_mode_base, anchor); //kihoon todo : 어떤 anchor를 가져다 쓸지 추후 확인 필요 + var anchor_info = game_mode_base.getInstanceRoom().getMap().findAnchorInfo(anchor_guid); + if (anchor_info is null) + { + err_msg = + $"findAnchorInfo anchor_info invalid : room_id - {room_id}, anchor_guid : {anchor_guid}, player : {player.toBasicString()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_msg); + send_S2C_ACK_GAME_OBJECT_INTERACTION(player, result, anchor_guid); + return result; + } + + if (false == MetaData.Instance._BattleObjectMetaTable.TryGetValue(anchor.TableID, out var tableMeta)) + { + err_msg = + $"getObjectInteractAction table_meta not exist!!!! table_meta : {anchor.TableID}, player : {player.toBasicString()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_msg); + send_S2C_ACK_GAME_OBJECT_INTERACTION(player, result, anchor_guid); + return result; + } + + var interact_action = GameModeHelper.getObjectInteractAction(game_mode_base, anchor, tableMeta.ObjectType); if (interact_action is null) { - err_msg = $"anchor_guid invalid : room_id - {room_id}, anchor_guid : {anchor_guid}, player : {player.toBasicString()}"; + err_msg = + $"interact_action is null : room_id - {room_id}, anchor_guid : {anchor_guid}, player : {player.toBasicString()}"; Log.getLogger().error(err_msg); result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_msg); send_S2C_ACK_GAME_OBJECT_INTERACTION(player, result, anchor_guid); return result; } - result = await interact_action.interact(anchor_guid, interaction_time); + var handler = GameModeHelper.getGameObjectInteractionDataHandler(player, game_mode_base, anchor, + tableMeta.ObjectType, interaction_time); + if (handler is null) + { + err_msg = + $"getGameObjectInteractionDataHandler is null : room_id - {room_id}, anchor_guid : {anchor_guid}, player : {player.toBasicString()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeInvalidAnchorGuid, err_msg); + send_S2C_ACK_GAME_OBJECT_INTERACTION(player, result, anchor_guid); + return result; + } + + result = await interact_action.interact(player, interaction_time, handler); if (result.isFail()) { send_S2C_ACK_GAME_OBJECT_INTERACTION(player, result, anchor_guid); return result; } - + + // 인터랙션 성공 이벤트 처리 + await OnInteractionEventAction(player, game_mode_base, tableMeta.ObjectType); + send_S2C_ACK_GAME_OBJECT_INTERACTION(player, result, anchor_guid); - - + interact_action.notifyAfterInteract(player, handler); + return result; } @@ -116,6 +137,18 @@ public class GameObjectInteractionPacketHandler : PacketRecvHandler ack_packet.Response.AckGameObjectInteraction.AnchorGuid = anchorGuid; return GameServerApp.getServerLogic().onSendPacket(player, ack_packet); - } -} \ No newline at end of file + + /// + /// 게임 오브젝트 인터랙션 이벤트 처리 + /// + /// + /// + /// + public async Task OnInteractionEventAction(Player player, GameModeBase gameModeBase, + EGameModeObjectType gameModeObjectType) + { + var eventActionInvoker = new GameEventFfaOnPickUpObject(player, gameModeBase.getGameModeId(), gameModeObjectType); + await EventActionScoreManager.It.ProcessScore(eventActionInvoker); + } +} diff --git a/GameServer/Contents/GameMode/PacketHandler/GamePlayerDeadPacketHandler.cs b/GameServer/Contents/GameMode/PacketHandler/GamePlayerDeadPacketHandler.cs new file mode 100644 index 0000000..e9e96ed --- /dev/null +++ b/GameServer/Contents/GameMode/PacketHandler/GamePlayerDeadPacketHandler.cs @@ -0,0 +1,93 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using Google.Protobuf; +using Microsoft.AspNetCore.Http.Timeouts; +using ServerBase; +using ServerCore; + +namespace GameServer.PacketHandler; + +[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_GAME_PLAYER_DEATH), typeof(GamePlayerDeadPacketHandler), typeof(GameLoginListener))] +public class GamePlayerDeadPacketHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + await Task.CompletedTask; + var result = new Result(); + + var err_msg = string.Empty; + + var player = session as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + var request = req_msg.Request.ReqGamePlayerDeath; + if (null == request) + { + err_msg = $"failed to get request message : Invalid Request message - {req_msg.Request.MsgCase}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.InvalidArgument, err_msg); + send_S2C_ACK_GAME_PLAYER_DEATH(player, result); + return result; + } + + var killer_user_guid = request.KillerUserGuid; + ArgumentNullReferenceCheckHelper.throwIfNull(killer_user_guid, () => $"killer_user_guid is null !!! - {player.toBasicString()}"); + var dead_user_guid = request.DeadUserGuid; + ArgumentNullReferenceCheckHelper.throwIfNull(dead_user_guid, () => $"dead_user_guid is null !!! - {player.toBasicString()}"); + var dead_type = request.DeadType; + ArgumentNullReferenceCheckHelper.throwIfNull(dead_type, () => $"dead_type is null !!! - {player.toBasicString()}"); + var packet_create_time = request.PacketCreateTime; + ArgumentNullReferenceCheckHelper.throwIfNull(packet_create_time, () => $"packet_create_time is null !!! - {player.toBasicString()}"); + + var room_id = player.getCurrentInstanceRoomId(); + if(false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + { + err_msg = $"gameMode is null : room_id - {room_id}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeClassIsNull, err_msg); + send_S2C_ACK_GAME_PLAYER_DEATH(player, result); + return result; + } + + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_moce_base is null !!!"); + + var dead_action = GameModeHelper.getPlayerDeadAction(game_mode_base); + if (dead_action is null) + { + err_msg = $"dead_action invalid : room_id - {room_id}, player : {player.toBasicString()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeInvalidAction, err_msg); + send_S2C_ACK_GAME_PLAYER_DEATH(player, result); + return result; + } + + result = await dead_action.dead(player, killer_user_guid, dead_user_guid, dead_type, packet_create_time.ToDateTime()); + if (result.isFail()) + { + send_S2C_ACK_GAME_PLAYER_DEATH(player, result); + return result; + } + + + send_S2C_ACK_GAME_PLAYER_DEATH(player, result); + dead_action.notifyAfterDead(player); + + return result; + } + + public bool send_S2C_ACK_GAME_PLAYER_DEATH(Player player, Result result) + { + var ack_packet = new ClientToGame(); + ack_packet.Response = new ClientToGameRes(); + + ack_packet.Response.ErrorCode = result.ErrorCode; + ack_packet.Response.AckGamePlayerDeath = new ClientToGameRes.Types.GS2C_ACK_GAME_PLAYER_DEATH(); + return GameServerApp.getServerLogic().onSendPacket(player, ack_packet); + } + + + +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/PacketHandler/GamePlayerRespawnPacketHandler.cs b/GameServer/Contents/GameMode/PacketHandler/GamePlayerRespawnPacketHandler.cs new file mode 100644 index 0000000..6368bfc --- /dev/null +++ b/GameServer/Contents/GameMode/PacketHandler/GamePlayerRespawnPacketHandler.cs @@ -0,0 +1,83 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using Google.Protobuf; +using ServerBase; +using ServerCore; + +namespace GameServer.PacketHandler; + +[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_GAME_PLAYER_RESPAWN), typeof(GamePlayerRespawnPacketHandler), typeof(GameLoginListener))] +public class GamePlayerRespawnPacketHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + await Task.CompletedTask; + var result = new Result(); + var err_msg = string.Empty; + + var player = session as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + + var request = req_msg.Request.ReqGamePlayerRespawn; + if (null == request) + { + err_msg = $"failed to get request message : Invalid Request message - {req_msg.Request.MsgCase}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.InvalidArgument, err_msg); + send_S2C_ACK_GAME_PLAYER_RESPAWN(player, result); + return result; + } + var room_id = player.getCurrentInstanceRoomId(); + if(false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + { + err_msg = $"gameMode is null : room_id - {room_id}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeClassIsNull, err_msg); + send_S2C_ACK_GAME_PLAYER_RESPAWN(player, result); + return result; + } + var game_mode_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_moce_base is null !!!"); + + + var respawn_action = GameModeHelper.getPlayerRespawnAction(game_mode_base); + if (respawn_action is null) + { + err_msg = $"respawn_action invalid : room_id - {room_id},player : {player.toBasicString()}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeInvalidAction, err_msg); + send_S2C_ACK_GAME_PLAYER_RESPAWN(player, result); + return result; + } + + var respawn_time = request.PacketCreateTime; + ArgumentNullReferenceCheckHelper.throwIfNull(respawn_time, () => $"respawn_time is null !!! - {player.toBasicString()}"); + result = await respawn_action.respawn(player, respawn_time.ToDateTime()); + if (result.isFail()) + { + send_S2C_ACK_GAME_PLAYER_RESPAWN(player, result); + return result; + } + + send_S2C_ACK_GAME_PLAYER_RESPAWN(player, result); + respawn_action.notifyAfterRespawn(player); + + return result; + } + + + public bool send_S2C_ACK_GAME_PLAYER_RESPAWN(Player player, Result result) + { + var ack_packet = new ClientToGame(); + ack_packet.Response = new ClientToGameRes(); + + ack_packet.Response.ErrorCode = result.ErrorCode; + ack_packet.Response.AckGamePlayerRespawn = new ClientToGameRes.Types.GS2C_ACK_GAME_PLAYER_RESPAWN(); + ack_packet.Response.AckGamePlayerRespawn.Pos = new Pos(); //이건 쓸지 안쓸지 모른다. + + return GameServerApp.getServerLogic().onSendPacket(player, ack_packet); + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/PacketHandler/PreparationForLeavingGameInstancePacketHandler.cs b/GameServer/Contents/GameMode/PacketHandler/PreparationForLeavingGameInstancePacketHandler.cs new file mode 100644 index 0000000..ce40f82 --- /dev/null +++ b/GameServer/Contents/GameMode/PacketHandler/PreparationForLeavingGameInstancePacketHandler.cs @@ -0,0 +1,81 @@ +using ServerBase; +using ServerCore; + +namespace GameServer.PacketHandler; + + +[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_PREPARATION_FOR_LEAVING_GAME_INSTANCE), typeof(PreparationForLeavingGameInstancePacketHandler), typeof(GameLoginListener))] +public class PreparationForLeavingGameInstancePacketHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) + { + + var result = new Result(); + var err_msg = string.Empty; + + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"entity_player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + var request = req_msg.Request.ReqPreparationForLeavingGameInstance; + if (null == request) + { + err_msg = $"failed to get request message : Invalid Request message - {req_msg.Request.MsgCase}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.InvalidArgument, err_msg); + send_S2C_ACK_PREPARE_FOR_LEAVING_GAME_INSTANCE(player, result); + return result; + } + + var packet_create_time = request.PacketCreateTime; + if (packet_create_time is not null) + { + Log.getLogger().info($"Preparation For leaving Instance Packet Received, packetCreateTime : {packet_create_time}, player : {player.toBasicString()}"); + } + + var room_id = player.getCurrentInstanceRoomId(); + if (false == GameModeManager.It.tryGetGameMode(room_id, out var gameMode)) + { + err_msg = $"PreparationForLeavingGameInstancePacketHandler game_instance_room is null : room_id - {room_id}"; + Log.getLogger().error(err_msg); + result.setFail(ServerErrorCode.GameModeClassIsNull, err_msg); + send_S2C_ACK_PREPARE_FOR_LEAVING_GAME_INSTANCE(player, result); + return result; + } + + (result, var preparation_handler) = GameModeHelper.getPreparationForLeavingGameHandler(player, gameMode, room_id); + if (result.isFail() || preparation_handler is null) + { + send_S2C_ACK_PREPARE_FOR_LEAVING_GAME_INSTANCE(player, result); + return result; + } + + result = await preparation_handler.preparationForLeaving(); + if (result.isFail()) + { + send_S2C_ACK_PREPARE_FOR_LEAVING_GAME_INSTANCE(player, result); + return result; + } + + send_S2C_ACK_PREPARE_FOR_LEAVING_GAME_INSTANCE(player, result); + return result; + } + + public bool send_S2C_ACK_PREPARE_FOR_LEAVING_GAME_INSTANCE(Player player, Result result) + { + var ack_packet = new ClientToGame(); + ack_packet.Response = new ClientToGameRes(); + + ack_packet.Response.ErrorCode = result.ErrorCode; + ack_packet.Response.AckPreparationForLeavingGameInstance = new ClientToGameRes.Types.GS2C_ACK_PREPARATION_FOR_LEAVING_GAME_INSTANCE(); + + + if (false == GameServerApp.getServerLogic().onSendPacket(player, ack_packet)) + { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameMode/State/GameModeStateLoadingWait.cs b/GameServer/Contents/GameMode/State/GameModeStateLoadingWait.cs new file mode 100644 index 0000000..6daa1a9 --- /dev/null +++ b/GameServer/Contents/GameMode/State/GameModeStateLoadingWait.cs @@ -0,0 +1,54 @@ +using GameServer.Contents.GameMode.Manage.PlayManage; +using GameServer.Contents.GameMode.Manage.StateManage; +using GameServer.Contents.GameMode.Mode_Running.ModeRace.Actions; +using ServerCore; + +namespace GameServer.Contents.GameMode.State; + +public class GameModeStateLoadingWait : GameModeStateBase +{ + public GameModeStateLoadingWait(IGameMode gameMode) : base(GameModeState.LoadingWait, gameMode) + { + } + public override Task enterDetail() + { + var game_mode_base = getGameMode() as GameModeBase; + NullReferenceCheckHelper.throwIfNull(game_mode_base, () => $"game_mode_base is null !!!"); + + var next_state_change_time = getNextStateChangeTime(); + var update_time = next_state_change_time.AddSeconds(86400); + setStateTime(update_time, "GameModeStateLoadingWait"); + + return Task.CompletedTask; + } + + + public override Task updateDetail() + { + return Task.FromResult(new Result()); + } + + public override void notifyDetail() + { + } + + public override void writeBusinessLog() + { + + } + + public override Task exitDetail() + { + return Task.CompletedTask; + } + + public override void notifyDetailAfterExit() + { + + } + + public override Task postProcess() + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/GameServer/Contents/GameZone/Action/GameZoneAction.cs b/GameServer/Contents/GameZone/Action/GameZoneAction.cs index 01542ea..f8bed93 100644 --- a/GameServer/Contents/GameZone/Action/GameZoneAction.cs +++ b/GameServer/Contents/GameZone/Action/GameZoneAction.cs @@ -72,7 +72,7 @@ internal class GameZoneAction : EntityActionBase var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); - if(true == isLinkedToMap()) + if (true == isLinkedToMap()) { var curr_map = getLinkedToMap(); NullReferenceCheckHelper.throwIfNull(curr_map, () => $"curr_map is null !!! - {player.toBasicStringWithMaster()}"); @@ -117,8 +117,6 @@ internal class GameZoneAction : EntityActionBase NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user_attribute is null !!! - {player.toBasicString()}"); var farming_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user_attribute is null !!! - {player.toBasicString()}"); - if (true == farming_action.isFarmingHere()) { var farming_effect = farming_action.getFarmingEffect(); @@ -140,9 +138,15 @@ internal class GameZoneAction : EntityActionBase result = curr_map.tryRemovePlayer(player); if(result.isFail()) { + err_msg = $"Failed to tryRemovePlayerg() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return result; } + if (!user_attribute.OccupiedAnchorGuid.isNullOrWhiteSpace()) + curr_map.tryEndOccupyAnchor(user_attribute.OccupiedAnchorGuid, player.getUserGuid()); + unlinkToMap(); var log_action = new LogActionEx(LogActionType.StageExit); @@ -164,13 +168,13 @@ internal class GameZoneAction : EntityActionBase var location_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(location_action, () => $"location action is null !!! - {player.toBasicString()}"); - if(true == isLinkedToMap()) + if (true == isLinkedToMap()) { var curr_map = getLinkedToMap(); NullReferenceCheckHelper.throwIfNull(curr_map, () => $"curr_map is null !!! - {player.toBasicString()}"); result = curr_map.tryRelocate(player, pos); - if(result.isFail()) + if (result.isFail()) { return result; } @@ -347,8 +351,6 @@ internal class GameZoneAction : EntityActionBase } var location_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); - var current_indun_location = location_action.getCurrentLocation() as IndunLocation; NullReferenceCheckHelper.throwIfNull(current_indun_location, () => $"current_indun_location is null !!! - {player.toBasicString()}"); @@ -385,7 +387,7 @@ internal class GameZoneAction : EntityActionBase MapManager.Instance.ExchangeMannequinDisplayItem(anchorGuid, displayItemMetaIds); - m_map.PropModifyNoti(anchorGuid); + m_map.broadcast_PropModify(anchorGuid); rabbit_mq.sendExchangeMannequinDisplayItemNoti(anchorGuid, displayItemMetaIds); @@ -397,36 +399,25 @@ internal class GameZoneAction : EntityActionBase var result = new Result(); var err_msg = string.Empty; - NullReferenceCheckHelper.throwIfNull(m_map, () => $"m_map is null"); - var res = new UseMountPropRes(); var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); - var server_logic = GameServerApp.getServerLogic(); - - var rabbit_mq = server_logic.getRabbitMqConnector() as RabbitMQ4Game; - NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit mq is null !!! - {player.toBasicString()}"); - - if (false == m_map.getAnchors().TryGetValue(anchorGuid, out var anchor_info)) + var curr_map = getLinkedToMap(); + if (curr_map == null) { - err_msg = $"Not Found AnchorInfo. anchorGuid:{anchorGuid}"; - result.setFail(ServerErrorCode.NotFoundAnchorGuid, err_msg); - Log.getLogger().error(err_msg); + err_msg = $"Entity not linked to Map !!! - {player.toBasicString()}"; + result.setFail(ServerErrorCode.EntityNotLinkedToMap, err_msg); + Log.getLogger().error(result.toBasicString()); return (result, res); } - if (!server_logic.getPlayerManager().tryGetUserByPrimaryKey(anchor_info.OccupyingUserGuid, out _)) + result = curr_map.tryOccupyAnchor(anchorGuid, player.getUserGuid()); + if (result.isFail()) { - anchor_info.OccupyingUserGuid = string.Empty; - } - - if (anchor_info.OccupyingUserGuid != string.Empty) - { - err_msg = $"Prop is Occupied"; - result.setFail(ServerErrorCode.PropIsOccupied, err_msg); + err_msg = $"Failed to tryOccupyAnchor() !!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); return (result, res); @@ -434,12 +425,10 @@ internal class GameZoneAction : EntityActionBase var user_attribute = player.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user attribute is null !!! - {player.toBasicString()}"); - - anchor_info.OccupyingUserGuid = player.getUserGuid(); - user_attribute.OccupiedAnchorGuid = anchor_info.AnchorGuid; - user_attribute.modifiedEntityAttribute(); - res.AnchorGuid = anchor_info.AnchorGuid; + user_attribute.OccupiedAnchorGuid = anchorGuid; + + res.AnchorGuid = anchorGuid; return (result, res); } @@ -449,31 +438,25 @@ internal class GameZoneAction : EntityActionBase var result = new Result(); var err_msg = string.Empty; - NullReferenceCheckHelper.throwIfNull(m_map, () => $"m_map is null"); - var res = new EndUseMountPropRes(); var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); - var server_logic = GameServerApp.getServerLogic(); - - var rabbit_mq = server_logic.getRabbitMqConnector() as RabbitMQ4Game; - NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit mq is null !!! - {player.toBasicString()}"); - - if (false == m_map.getAnchors().TryGetValue(anchorGuid, out var anchor_info)) + var curr_map = getLinkedToMap(); + if (curr_map == null) { - err_msg = $"Not Found AnchorInfo. anchorGuid:{anchorGuid}"; - result.setFail(ServerErrorCode.NotFoundAnchorGuid, err_msg); - Log.getLogger().error(err_msg); + err_msg = $"Entity not linked to Map !!! - {player.toBasicString()}"; + result.setFail(ServerErrorCode.EntityNotLinkedToMap, err_msg); + Log.getLogger().error(result.toBasicString()); return (result, res); } - if (anchor_info.OccupyingUserGuid != player.getUserGuid()) + result = curr_map.tryEndOccupyAnchor(anchorGuid, player.getUserGuid()); + if (result.isFail()) { - err_msg = $"Not usable"; - result.setFail(ServerErrorCode.NotUsablePlace, err_msg); + err_msg = $"Failed to tryEndOccupyAnchor() !!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); return (result, res); @@ -481,24 +464,21 @@ internal class GameZoneAction : EntityActionBase var user_attribute = player.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user attribute is null !!! - {player.toBasicString()}"); - - anchor_info.OccupyingUserGuid = string.Empty; + user_attribute.OccupiedAnchorGuid = string.Empty; - res.AnchorGuid = anchor_info.AnchorGuid; + res.AnchorGuid = anchorGuid; return (result, res); } - public async Task<(Result, UseRewardPropRes, UseRewardPropInfo?)> tryUseRewardProp(string anchorGuid) + public async Task<(Result, UseRewardPropInfo?)> tryUseRewardProp(string anchorGuid) { var result = new Result(); var err_msg = string.Empty; NullReferenceCheckHelper.throwIfNull(m_map, () => $"m_map is null"); - var res = new UseRewardPropRes(); - var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); @@ -511,36 +491,36 @@ internal class GameZoneAction : EntityActionBase { err_msg = $"Not Found AnchorInfo. anchorGuid:{anchorGuid}"; result.setFail(ServerErrorCode.NotFoundAnchorGuid, err_msg); - Log.getLogger().error(err_msg); + Log.getLogger().error(result.toBasicString()); - return (result, res, null); + return (result, null); } if (!MapHelper.isRewardProp(anchorGuid) && !MapHelper.isGroupProp(anchorGuid)) { - err_msg = $"Not Found AnchorInfo. anchorGuid:{anchorGuid}"; - result.setFail(ServerErrorCode.NotFoundAnchorGuid, err_msg); - Log.getLogger().error(err_msg); + err_msg = $"Anchor is not RewardProp or GroupProp !!! : anchorGuid:{anchorGuid}"; + result.setFail(ServerErrorCode.PropIsNotRewardPropOrGroupProp, err_msg); + Log.getLogger().error(result.toBasicString()); - return (result, res, null); + return (result, null); } if (anchor_info.PropState != PropState.Activation) { - err_msg = $"Not Found AnchorInfo. anchorGuid:{anchorGuid}"; - result.setFail(ServerErrorCode.NotFoundAnchorGuid, err_msg); - Log.getLogger().error(err_msg); + err_msg = $"Prop is not Activation !!! : anchorGuid:{anchorGuid}"; + result.setFail(ServerErrorCode.PropStateIsNotActivation, err_msg); + Log.getLogger().error(result.toBasicString()); - return (result, res, null); + return (result, null); } if (!MetaData.Instance._RewardPropMetaTable.TryGetValue(anchor_info.AnchorProp.TableId, out var rewardPropData)) { - err_msg = $"Not Found AnchorInfo. anchorGuid:{anchorGuid}"; - result.setFail(ServerErrorCode.NotFoundAnchorGuid, err_msg); - Log.getLogger().error(err_msg); + err_msg = $"Failed to _RewardPropMetaTable.TryGetValue() !!! : rewardPropId:{anchor_info.AnchorProp.TableId}, anchorGuid:{anchorGuid}"; + result.setFail(ServerErrorCode.RewardPropMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); - return (result, res, null); + return (result, null); } result = await player.checkRequirement(rewardPropData.UseRequired); @@ -549,23 +529,18 @@ internal class GameZoneAction : EntityActionBase err_msg = $"Failed to checkRequirement() !!! : {result.toBasicString()} : {this.getTypeName()}"; Log.getLogger().error(err_msg); - return (result, res, null); + return (result, null); } if (!MetaData.Instance._RewardMetaTable.TryGetValue(rewardPropData.UsedReward, out var rewardDatas)) { - err_msg = $"Not Found AnchorInfo. anchorGuid:{anchorGuid}"; - result.setFail(ServerErrorCode.NotFoundAnchorGuid, err_msg); - Log.getLogger().error(err_msg); + err_msg = $"Failed to _RewardMetaTable.TryGetValue() !!! : rewardGroupId:{rewardPropData.UsedReward}, rewardPropId:{rewardPropData.Id}, anchorGuid:{anchorGuid}"; + result.setFail(ServerErrorCode.RewardInfoNotExist, err_msg); + Log.getLogger().error(result.toBasicString()); - return (result, res, null); + return (result, null); } - ClientToGame clientToGame = new(); - clientToGame.Response = new(); - clientToGame.Response.UseRewardPropRes = res; - - List < MetaAssets.Reward > rewards = new(); foreach (var reward in rewardDatas) { @@ -576,7 +551,7 @@ internal class GameZoneAction : EntityActionBase result = await RewardManager.It.proceedRewardProcess(reward_proc); if (result.isFail()) { - return (result, res, null); + return (result, null); } if (MapHelper.isRewardProp(anchorGuid)) @@ -592,16 +567,9 @@ internal class GameZoneAction : EntityActionBase RewardManager.It.postRewardProcess(reward_proc); - //var reward_base = reward_proc as RewardBase; - //var rewarded_items = reward_base.getRewardedItems(); - //var rewarded_money = reward_base.getRewardedMoneys(); - //var items = RewardManager.Instance.convertItemAndMoneyToItem(rewarded_items, rewarded_money); - res.AnchorGuid = anchorGuid; - //res.Items.AddRange(items); - UseRewardPropInfo info = new UseRewardPropInfo(anchorGuid, anchor_info.AnchorProp.TableId, rewardPropData.UsedReward, rewardDatas); - return (result, res, info); + return (result, info); } diff --git a/GameServer/Contents/GameZone/Action/GameZoneMoveAction.cs b/GameServer/Contents/GameZone/Action/GameZoneMoveAction.cs index 5f448ff..4c6636b 100644 --- a/GameServer/Contents/GameZone/Action/GameZoneMoveAction.cs +++ b/GameServer/Contents/GameZone/Action/GameZoneMoveAction.cs @@ -20,7 +20,7 @@ namespace GameServer; public partial class GameZoneMoveAction : EntityActionBase { - public GameZoneMoveAction(Player owner) + public GameZoneMoveAction(Player owner) : base(owner) { } public override Task onInit() @@ -68,10 +68,10 @@ public partial class GameZoneMoveAction : EntityActionBase err_msg = $"Failed to get balanced GameServer !!! - {player.toBasicString()}"; result.setFail(ServerErrorCode.NoServerConnectable, err_msg); Log.getLogger().error(err_msg); - + return (result, null, null); } - + // 이동 예약 var message = new ServerMessage.Types.GS2GS_REQ_RESERVATION_ENTER_TO_SERVER(); message.MoveType = ServerMoveType.Force; @@ -79,14 +79,14 @@ public partial class GameZoneMoveAction : EntityActionBase message.RequestServerName = server_logic.getServerName(); var reserved = await server_logic.getReservationManager().registerReservationEnterToServer(message, server_info.Name); - + // 예약 실패 체크 if (null == reserved) { err_msg = $"Failed to Reservation Enter to server !!! : {server_info.Name} - {player.toBasicString()}"; result.setFail(ServerErrorCode.FailedToReservationEnter, err_msg); Log.getLogger().error(result.toBasicString()); - + return (result, null, null); } @@ -162,7 +162,7 @@ public partial class GameZoneMoveAction : EntityActionBase NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); var server_logic = GameServerApp.getServerLogic(); - + if (!MetaData.Instance._IndunTable.TryGetValue(instanceMetaId, out var indun_meta_data)) { err_msg = $"Failed to MetaData.TryGetValue() !!! : instanceMetaId:{instanceMetaId} - {player.toBasicString()}"; @@ -227,7 +227,7 @@ public partial class GameZoneMoveAction : EntityActionBase return (result, null, null); } - + var location_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); @@ -272,6 +272,7 @@ public partial class GameZoneMoveAction : EntityActionBase server_connect_info.ServerPort = instance_room_info.InstancePort; server_connect_info.Otp = reserved_to_switch_server.OneTimeKey; server_connect_info.RoomId = instance_room_info.roomId; + server_connect_info.InstanceId = instance_room_info.InstanceId; var start_pos = MapManager.Instance.GetStartPos(indun_meta_data.RoomFile); var arrival_position_log_info = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Arrival, server_name, instance_room_info.roomId, MapFileType.Instance, instance_room_info.InstanceId, start_pos); @@ -393,7 +394,7 @@ public partial class GameZoneMoveAction : EntityActionBase return (result, res, business_logs); } - public async Task<(Result, ClientToGameRes.Types.TaxiRes, List?)> tryTaxi(int taxiMetaId) + public async Task<(Result, ClientToGameRes.Types.TaxiRes, List?)> tryTaxi(int departureTaxiId, int arrivalTaxiMetaId) { var result = new Result(); var err_msg = string.Empty; @@ -405,7 +406,7 @@ public partial class GameZoneMoveAction : EntityActionBase NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); var server_logic = GameServerApp.getServerLogic(); - + var server_type = server_logic.getServerType().toServerType(); if (server_type != ServerType.Channel) { @@ -416,23 +417,34 @@ public partial class GameZoneMoveAction : EntityActionBase return (result, res, null); } - if (!MetaData.Instance._TaxiMetaTable.TryGetValue(taxiMetaId, out var taxi_meta_data)) + if (!MetaData.Instance._TaxiMetaTable.TryGetValue(departureTaxiId, out _)) { - err_msg = $"Failed to MetaData.TryGetValue() !!! : taxiMetaId:{taxiMetaId} - {player.toBasicString()}"; + err_msg = $"Failed to MetaData.TryGetValue() !!! : arrivalTaxiMetaId:{departureTaxiId} - {player.toBasicString()}"; result.setFail(ServerErrorCode.TaxiMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, res, null); } - var taxi_log_info = TaxiBusinessLogHelper.toTaxiLogInfo(taxi_meta_data); + var is_from_main_menu = departureTaxiId == MetaHelper.GameConfigMeta.MainMenuTaxiId; + + if (!MetaData.Instance._TaxiMetaTable.TryGetValue(arrivalTaxiMetaId, out var taxi_meta_data)) + { + err_msg = $"Failed to MetaData.TryGetValue() !!! : arrivalTaxiMetaId:{arrivalTaxiMetaId} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.TaxiMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, res, null); + } + + var taxi_cost = is_from_main_menu ? taxi_meta_data.MenuCost : taxi_meta_data.UnloadingCost; + + var taxi_log_info = TaxiBusinessLogHelper.toTaxiLogInfo(departureTaxiId, arrivalTaxiMetaId, taxi_cost); var taxi_business_log = new TaxiBusinessLog(taxi_log_info); business_logs.Add(taxi_business_log); var money_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(money_action, () => $"money action is null !!! - {player.toBasicString()}"); - - result = await money_action.changeMoney(CurrencyType.Gold, -taxi_meta_data.UnloadingCost); + result = await money_action.changeMoney(CurrencyType.Gold, -taxi_cost); if (result.isFail()) { err_msg = $"Failed to changeMoney() !!! : {result.toBasicString()}"; @@ -516,7 +528,7 @@ public partial class GameZoneMoveAction : EntityActionBase return (result, res, null); } } - + res.InstanceServerConnectInfo = server_connect_info; business_logs.Add(address_business_log); @@ -573,7 +585,7 @@ public partial class GameZoneMoveAction : EntityActionBase NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); var server_logic = GameServerApp.getServerLogic(); - + var departure_position_info = player.getCurrentPositionInfo(); var departure_position_log_info = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Departure, departure_position_info); var departure_position_business_log = new PositionBusinessLog(departure_position_log_info); @@ -661,7 +673,7 @@ public partial class GameZoneMoveAction : EntityActionBase return (result, res, null); } - if(null != world_meta_data) + if (null != world_meta_data) { // 요구 아이템 삭제 (result, var delete_items) = await deleteRequiredItem(player, world_meta_data); @@ -739,10 +751,10 @@ public partial class GameZoneMoveAction : EntityActionBase var business_logs = new List(); var server_logic = GameServerApp.getServerLogic(); - + var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); - + // 1. Dest Instance 정보 획득 (result, var room_map_tree, var address_business_log) = MapHelper.tryGetRoomMapTree(warpData.LandId, warpData.FloorId); if (result.isFail() || null == room_map_tree || null == address_business_log) @@ -766,13 +778,13 @@ public partial class GameZoneMoveAction : EntityActionBase ServerConnectInfo? server_connect_info; List? join_instance_business_logs; - + // 3. Indun 서버에서 이동 if (server_logic.getServerType().toServerType() == ServerType.Indun) { var check_same_location = checkSameIndunLocation(player, room_map_tree); - if(check_same_location.result.isFail()) return (check_same_location.result, res, null); - + if (check_same_location.result.isFail()) return (check_same_location.result, res, null); + // Dest == Current : 좌표 이동 if (check_same_location.isSame) { @@ -783,7 +795,7 @@ public partial class GameZoneMoveAction : EntityActionBase var location_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(location_action, () => $"LocationAction is null !!! - {player.toBasicString()}"); - + var newPos = ServerBase.EntityHelper.makePos(warpData.PositionX, warpData.PositionY, warpData.PositionZ, warpData.Rotate); // World 좌표 변환 @@ -796,11 +808,11 @@ public partial class GameZoneMoveAction : EntityActionBase (result, newPos) = MapHelper.getWorldPosFromRoomRelativePos(room_map_tree, newPos); if (result.isFail()) return (result, res, null); } - + // 좌표 설정 result = location_action.tryUpdateCurrentPos(newPos); if (result.isFail()) return (result, res, null); - + res.Pos = newPos; var arrival_position_info = player.getCurrentPositionInfo(); @@ -813,7 +825,7 @@ public partial class GameZoneMoveAction : EntityActionBase return (result, res, business_logs); } } - + // 4. Dest Type : MyHome if (indun_meta_data.ContentsType == ContentsType.MyHome) { @@ -843,7 +855,7 @@ public partial class GameZoneMoveAction : EntityActionBase business_logs.Add(address_business_log); business_logs.AddRange(join_instance_business_logs); - + return (result, res, business_logs); } @@ -888,7 +900,7 @@ public partial class GameZoneMoveAction : EntityActionBase NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); var server_logic = GameServerApp.getServerLogic(); - + var departure_position_info = player.getCurrentPositionInfo(); var departure_position_log_info = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Departure, departure_position_info); var departure_position_business_log = new PositionBusinessLog(departure_position_log_info); @@ -925,7 +937,7 @@ public partial class GameZoneMoveAction : EntityActionBase message.RequestServerName = server_logic.getServerName(); var reserved = await server_logic.getReservationManager().registerReservationEnterToServer(message, server_name); - + // 예약 실패 체크 if (null == reserved) { @@ -1035,7 +1047,7 @@ public partial class GameZoneMoveAction : EntityActionBase NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); var server_logic = GameServerApp.getServerLogic(); - + var server_type = server_logic.getServerType().toServerType(); if (server_type == ServerType.Channel) { @@ -1213,7 +1225,7 @@ public partial class GameZoneMoveAction : EntityActionBase message.RequestServerName = server_logic.getServerName(); var reserved = await server_logic.getReservationManager().registerReservationEnterToServer(message, server_name); - + // 예약 실패 체크 if (null == reserved) { @@ -1223,7 +1235,7 @@ public partial class GameZoneMoveAction : EntityActionBase continue; } - + var location_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(location_action, () => $"location action is null !!! - {player.toBasicString()}"); @@ -1381,7 +1393,7 @@ public partial class GameZoneMoveAction : EntityActionBase private (Result result, bool isSame) checkSameIndunLocation(Player player, RoomMapTree roomMapTree) { var result = new Result(); - + var location_attribute = player.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(location_attribute, () => $"LocationAttribute is null !!! - player:{player.toBasicString()}"); @@ -1559,7 +1571,7 @@ public partial class GameZoneMoveAction : EntityActionBase RoomId = instance_room_info.roomId, MyhomeInfo = myhome_info, }; - + // 이동 예약 걸기 var message = new ServerMessage.Types.GS2GS_REQ_RESERVATION_ENTER_TO_SERVER(); message.MoveType = ServerMoveType.Force; @@ -1567,7 +1579,7 @@ public partial class GameZoneMoveAction : EntityActionBase message.RequestServerName = server_logic.getServerName(); var reserved = await server_logic.getReservationManager().registerReservationEnterToServer(message, serverName); - + // 예약 실패 체크 if (null == reserved) { @@ -1577,7 +1589,7 @@ public partial class GameZoneMoveAction : EntityActionBase return (result, null); } - + return (result, server_connect_info); } @@ -1594,7 +1606,7 @@ public partial class GameZoneMoveAction : EntityActionBase NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); var server_logic = GameServerApp.getServerLogic(); - + (result, var myhome_attrib) = await MyhomeHelper.getEnterMyhomeAttribFromDynamoDb(ownerGuid, enterMyhomeGuid); if (result.isFail() || null == myhome_attrib) { @@ -1671,7 +1683,7 @@ public partial class GameZoneMoveAction : EntityActionBase message.MoveType = ServerMoveType.Force; message.RequestUserGuid = player.getUserGuid(); message.RequestServerName = server_logic.getServerName(); - + var reserved = await server_logic.getReservationManager().registerReservationEnterToServer(message, server_name); // 예약 실패 체크 @@ -1683,7 +1695,7 @@ public partial class GameZoneMoveAction : EntityActionBase return (result, null, null); } - + var start_pos_anchor_postion = myhome_ugc_info.getMyhomeStartPosAnchorPosition(); var myhome_start_pos = MapHelper.getMyhomeStartPos(start_pos_anchor_postion); @@ -1761,7 +1773,7 @@ public partial class GameZoneMoveAction : EntityActionBase NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); var server_logic = GameServerApp.getServerLogic(); - + var departure_position_info = player.getCurrentPositionInfo(); var departure_position_log_info = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Departure, departure_position_info); var departure_position_business_log = new PositionBusinessLog(departure_position_log_info); @@ -1870,4 +1882,212 @@ public partial class GameZoneMoveAction : EntityActionBase return (result, server_connect_info, business_logs); } -} \ No newline at end of file + + public async Task<(Result, ServerConnectInfo?, List?)> tryJoinInstance2(USER_GUID userGuid, int instanceMetaId) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_connect_info = new ServerConnectInfo(); + var business_logs = new List(); + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => "player is null !!!"); + + var server_logic = GameServerApp.getServerLogic(); + + if (!MetaData.Instance._IndunTable.TryGetValue(instanceMetaId, out var indun_meta_data)) + { + err_msg = $"Failed to MetaData.TryGetValue() !!! : instanceMetaId:{instanceMetaId} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.InstanceMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, null, null); + } + + result = player.checkInstanceAccess(instanceMetaId); + if (result.isFail()) + { + err_msg = $"Failed to checkInstanceAccess() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, null, null); + } + + var departure_position_info = player.getCurrentPositionInfo(); + var departure_position_log_info = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Departure, departure_position_info); + var departure_position_business_log = new PositionBusinessLog(departure_position_log_info); + business_logs.Add(departure_position_business_log); + + (result, var instance_room_id) = await InstanceRoomHandler.joinInstance(player.getUserGuid(), instanceMetaId); + if (result.isFail()) + { + err_msg = $"Failed to joinInstance() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, null, null); + } + + var instance_room_storage = new InstanceRoomStorage(); + instance_room_storage.Init(server_logic.getRedisDb(), ""); + + var instance_room_info = await instance_room_storage.GetInstanceRoomInfo(instance_room_id); + if (instance_room_info == null) + { + err_msg = $"Failed to GetInstanceRoomInfo() !!! : instanceRoomId:{instance_room_id} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.NotExistRoomInfoForEnter, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, null, null); + } + + var server_name = ServerType.Indun.toServerName(instance_room_info.InstanceAddress, (ushort)instance_room_info.InstancePort); + + // 이동 예약 걸기 + var message = new ServerMessage.Types.GS2GS_REQ_RESERVATION_ENTER_TO_SERVER(); + message.MoveType = ServerMoveType.Force; + message.RequestUserGuid = userGuid; + message.RequestServerName = server_logic.getServerName(); + + var reserved = await server_logic.getReservationManager().registerReservationEnterToServer(message, server_name); + + // 예약 실패 체크 + if (null == reserved) + { + err_msg = $"Failed to Reservation Enter to server !!! : {server_name} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.FailedToReservationEnter, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, null, null); + } + + var location_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); + + result = await location_action.tryMoveToIndun(instance_room_info); + if (result.isFail()) + { + err_msg = $"Failed to tryMoveToIndun() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, null, null); + } + + var buff_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(buff_action, () => $"buff_action is null !!! - {player.toBasicString()}"); + + result = await buff_action.MoveServer(indun_meta_data.placeType()); + if (result.isFail()) + { + err_msg = $"Failed to MoveServer() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, null, null); + } + + (result, var reserved_to_switch_server) = await ServerConnectionSwitchHelper.startServerSwitch(player, server_logic.getRedisConnector(), server_name); + if (result.isFail() || null == reserved_to_switch_server) + { + err_msg = $"Failed to startServerSwitch() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, null, null); + } + + var game_login_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_login_action, () => $"game_login_action is null !!! - {player.toBasicString()}"); + + var login_cache = game_login_action.getLoginCacheRequest()?.getLoginCache(); + NullReferenceCheckHelper.throwIfNull(login_cache, () => $"login_cache is null !! player: {player.toBasicString()}"); + login_cache.ReservedToSwitchServer = reserved_to_switch_server; + + server_connect_info.ServerAddr = instance_room_info.InstanceAddress; + server_connect_info.ServerPort = instance_room_info.InstancePort; + server_connect_info.Otp = reserved_to_switch_server.OneTimeKey; + server_connect_info.RoomId = instance_room_info.roomId; + + var start_pos = MapManager.Instance.GetStartPos(indun_meta_data.RoomFile); + var arrival_position_log_info = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Arrival, server_name, instance_room_info.roomId, MapFileType.Instance, instance_room_info.InstanceId, start_pos); + var arrival_position_business_log = new PositionBusinessLog(arrival_position_log_info); + business_logs.Add(arrival_position_business_log); + + return (result, server_connect_info, business_logs); + } + + public async Task<(Result, ClientToGameRes.Types.GS2C_ACK_CONTENTS_MOVE, List?)> tryContentsMove(int contentsMenuMetaId) + { + var result = new Result(); + var err_msg = string.Empty; + + var res = new ClientToGameRes.Types.GS2C_ACK_CONTENTS_MOVE(); + var business_logs = new List(); + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => "player is null !"); + + var server_logic = GameServerApp.getServerLogic(); + + if (!MetaData.Instance._ContentsMenuMetaTable.TryGetValue(contentsMenuMetaId, out var contents_menu_meta_data)) + { + err_msg = $"Failed to MetaData.TryGetValue() !!! : contentsMenuMetaId:{contentsMenuMetaId} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.ContentsMunuMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, res, null); + } + + // 1. 매칭 이동은 패스 + if (contents_menu_meta_data.ConnectionType == "Matching" || contents_menu_meta_data.GameModeType != GameModeType.None) + { + err_msg = $"ContentsMunu ConnectionType is Matching !!! : contentsMenuMetaId:{contentsMenuMetaId} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.ContentsMunuConnectionTypeInvalid, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, res, null); + } + + // 2. 시즌 패스 여부 체크 + var seasonpass = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(seasonpass); + + // 3. 비용 계산 + if (false == seasonpass.isChargedSeasonPass() && contents_menu_meta_data.Cost_Require) + { + var move_cost = contents_menu_meta_data.Contents_Cost; + var money_action = player.getEntityAction(); + result = await money_action.changeMoney(CurrencyType.Gold, -move_cost); + if (result.isFail()) + { + err_msg = $"Failed to changeMoney() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, res, null); + } + } + + (result, var warp_res, var warp_business_logs) = await tryWarp(contents_menu_meta_data.Warp_ID); + if (result.isFail()) + { + err_msg = $"Failed to tryWarp() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, res, null); + } + NullReferenceCheckHelper.throwIfNull(business_logs, () => $"business_logs is null !!! - {player.toBasicString()}"); + + switch(warp_res.PlaceCase) + { + case WarpRes.PlaceOneofCase.GameServerConnectInfo: + res.GameServerConnectInfo = warp_res.GameServerConnectInfo; + break; + case WarpRes.PlaceOneofCase.InstanceServerConnectInfo: + res.InstanceServerConnectInfo = warp_res.InstanceServerConnectInfo; + break; + case WarpRes.PlaceOneofCase.Pos: + res.Pos = warp_res.Pos; + break; + } + + return (result, res, business_logs); + } +} diff --git a/GameServer/Contents/GameZone/Helper/TaxiBusinessLogHelper.cs b/GameServer/Contents/GameZone/Helper/TaxiBusinessLogHelper.cs index 66c68a0..a2779f4 100644 --- a/GameServer/Contents/GameZone/Helper/TaxiBusinessLogHelper.cs +++ b/GameServer/Contents/GameZone/Helper/TaxiBusinessLogHelper.cs @@ -14,25 +14,18 @@ namespace GameServer { public static class TaxiBusinessLogHelper { - public static TaxiLogInfo toTaxiLogInfo(TaxiMetaData taxiMetaData) + public static TaxiLogInfo toTaxiLogInfo(int departureTaxiId, int arrivalTaxiId, int cost) { var taxi_log_info = new TaxiLogInfo(); - taxi_log_info.setTaxiInfo(taxiMetaData); + taxi_log_info.setTaxiInfo(departureTaxiId, arrivalTaxiId, cost); return taxi_log_info; } - public static void setTaxiInfo(this TaxiLogInfo logData, TaxiMetaData taxiMetaData) + public static void setTaxiInfo(this TaxiLogInfo logData, int departureTaxiId, int arrivalTaxiId, int cost) { - logData.TaxiMID = taxiMetaData.TaxiId; - logData.TaxiType = taxiMetaData.Type.ToString(); - logData.Cost = taxiMetaData.UnloadingCost; - logData.UnloadingWorldId = taxiMetaData.UnloadingWorldId; - logData.UnloadingLandId = taxiMetaData.UnloadingLandId; - logData.UnloadingFloor = taxiMetaData.UnloadingFloorId; - logData.UnloadingPos.X = taxiMetaData.UnloadingPositionX; - logData.UnloadingPos.Y = taxiMetaData.UnloadingPositionY; - logData.UnloadingPos.Z = taxiMetaData.UnloadingPositionZ; - logData.UnloadingPos.Angle = taxiMetaData.UnloadingRotate; + logData.DepartureTaxiMID = departureTaxiId; + logData.ArrivalTaxiMID = arrivalTaxiId; + logData.Cost = cost; } } } diff --git a/GameServer/Contents/GameZone/PacketHandler/ContentsMovePacketHandler.cs b/GameServer/Contents/GameZone/PacketHandler/ContentsMovePacketHandler.cs new file mode 100644 index 0000000..51952a0 --- /dev/null +++ b/GameServer/Contents/GameZone/PacketHandler/ContentsMovePacketHandler.cs @@ -0,0 +1,114 @@ +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ClientToGameRes.Types; + +namespace GameServer.PacketHandler +{ + [PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_CONTENTS_MOVE), typeof(ContentsMovePacketHandler), typeof(GameLoginListener))] + internal class ContentsMovePacketHandler : PacketRecvHandler + { + public static bool send_S2C_ACK_CONTENTS_MOVE(Player owner, Result result, GS2C_ACK_CONTENTS_MOVE res) + { + var ack_packet = new ClientToGame(); + ack_packet.Response = new ClientToGameRes(); + + ack_packet.Response.ErrorCode = result.ErrorCode; + ack_packet.Response.AckContentsMove = res; + + if (false == GameServerApp.getServerLogic().onSendPacket(owner, ack_packet)) + { + return false; + } + + return true; + } + + public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + NullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + var request = req_msg.Request.ReqContentsMove; + var res = new GS2C_ACK_CONTENTS_MOVE(); + + var game_zone_move_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_move_action, () => $"game_zone_move_action is null !!! - {player.toBasicString()}"); + + var game_zone_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!! - {player.toBasicString()}"); + + var fn_transaction_runner = async delegate () + { + var result = new Result(); + + (result, res, var business_logs) = await game_zone_move_action.tryContentsMove(request.ContentsMenuId); + if (result.isFail()) + { + err_msg = $"Failed to tryContentsMove() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + send_S2C_ACK_CONTENTS_MOVE(player, result, res); + return result; + } + NullReferenceCheckHelper.throwIfNull(business_logs, () => $"business_logs is null !!! - {player.toBasicString()}"); + + var batch = new QueryBatchEx(player, LogActionType.ContentsMove, server_logic.getDynamoDbClient()); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + batch.addQuery(new QueryFinal()); + } + + batch.appendBusinessLogs(business_logs); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) + { + send_S2C_ACK_CONTENTS_MOVE(player, result, res); + return result; + } + + var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); + NullReferenceCheckHelper.throwIfNull(found_transaction_runner, () => $"found_transaction_runner is null !!! - {player.toBasicString()}"); + res.CommonResult = found_transaction_runner.getCommonResult(); + + send_S2C_ACK_CONTENTS_MOVE(player, result, res); + if (res.PlaceCase == GS2C_ACK_CONTENTS_MOVE.PlaceOneofCase.Pos) + { + result = game_zone_action.tryMove(res.Pos); + if (result.isFail()) + { + err_msg = $"Failed to tryMove at {this.getTypeName()} !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + + return result; + }; + + result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "ContentsMove", fn_transaction_runner); + if (result.isFail()) + { + err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + } + + return result; + } + } +} diff --git a/GameServer/Contents/GameZone/PacketHandler/DestroyWeaponObjectPacketHandler.cs b/GameServer/Contents/GameZone/PacketHandler/DestroyWeaponObjectPacketHandler.cs new file mode 100644 index 0000000..d515aad --- /dev/null +++ b/GameServer/Contents/GameZone/PacketHandler/DestroyWeaponObjectPacketHandler.cs @@ -0,0 +1,120 @@ +using GameServer.EventInvokers; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ClientToGameRes.Types; + +namespace GameServer.PacketHandler +{ + [PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_DESTROY_WEAPON_OBJECT), typeof(DestroyWeaponObjectPacketHandler), typeof(GameLoginListener))] + internal class DestroyWeaponObjectPacketHandler : PacketRecvHandler + { + public static bool send_S2C_ACK_DESTROY_WEAPON_OBJECT(Player owner, Result result, GS2C_ACK_DESTROY_WEAPON_OBJECT res) + { + var ack_packet = new ClientToGame(); + ack_packet.Response = new ClientToGameRes(); + + ack_packet.Response.ErrorCode = result.ErrorCode; + ack_packet.Response.AckDestroyWeaponObject = res; + + if (false == GameServerApp.getServerLogic().onSendPacket(owner, ack_packet)) + { + return false; + } + + return true; + } + + public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + NullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + var request = req_msg.Request.ReqDestroyWeaponObject; + var res = new GS2C_ACK_DESTROY_WEAPON_OBJECT(); + + res.AnchorGuid = request.AnchorGuid; + + var game_zone_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!! - {player.toBasicString()}"); + + var fn_transaction_runner = async delegate () + { + var result = new Result(); + + (result, var reward_prop_log_info) = await game_zone_action.tryUseRewardProp(request.AnchorGuid); + if (result.isFail()) + { + err_msg = $"Failed to tryUseRewardProp() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + send_S2C_ACK_DESTROY_WEAPON_OBJECT(player, result, res); + return result; + } + NullReferenceCheckHelper.throwIfNull(reward_prop_log_info, () => $"reward_prop_log_info is null !!! - {player.toBasicString()}"); + + var batch = new QueryBatchEx(player, LogActionType.DestroyWeaponObject, server_logic.getDynamoDbClient()); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + batch.addQuery(new QueryFinal()); + } + + batch.appendBusinessLog(new UseRewardPropBusinessLog(reward_prop_log_info)); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) + { + send_S2C_ACK_DESTROY_WEAPON_OBJECT(player, result, res); + return result; + } + + var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); + ArgumentNullException.ThrowIfNull(found_transaction_runner, $"found_transaction_runner is null !!! - {player.toBasicString()}"); + var common_result = found_transaction_runner.getCommonResult(); + res.CommonResult = common_result; + + send_S2C_ACK_DESTROY_WEAPON_OBJECT(player, result, res); + player.send_S2C_NTF_REWARD_PROP_STATE(res.AnchorGuid, false); + + await player.getGameEventCollector().collectEvent(new GameEventInvokerRewadPropRewarded(player)); + + return result; + }; + + result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "UseRewardProp", fn_transaction_runner); + if (result.isFail()) + { + err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return result; + } + + var curr_map = game_zone_action.getLinkedToMap(); + NullReferenceCheckHelper.throwIfNull(curr_map, () => $"curr_map is null !!! - {player.toBasicString()}"); + + if (curr_map.getAnchors().TryGetValue(request.AnchorGuid, out var anchor_info)) + { + if (MetaData.Instance._RewardPropMetaTable.TryGetValue(anchor_info.AnchorProp.TableId, out var rewardPropData)) + { + await QuestManager.It.QuestCheck(player, new QuestReward(EQuestEventTargetType.REWARD, EQuestEventNameType.RECEIVED, rewardPropData.UsedReward)); + } + } + + return result; + } + } +} diff --git a/GameServer/Contents/GameZone/PacketHandler/LeaveInstancePacketHandler.cs b/GameServer/Contents/GameZone/PacketHandler/LeaveInstancePacketHandler.cs index 33a04d7..ad52966 100644 --- a/GameServer/Contents/GameZone/PacketHandler/LeaveInstancePacketHandler.cs +++ b/GameServer/Contents/GameZone/PacketHandler/LeaveInstancePacketHandler.cs @@ -46,206 +46,100 @@ internal class LeaveInstancePacketHandler : PacketRecvHandler var result = new Result(); var err_msg = string.Empty; - var entity_player = entityWithSession as Player; - NullReferenceCheckHelper.throwIfNull(entity_player, () => $"entity_player is null !!!"); + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); var server_logic = GameServerApp.getServerLogic(); - + var req_msg = recvMessage as ClientToGame; - ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {entity_player.toBasicString()}"); + ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); var request = req_msg.Request.LeaveInstanceReq; - if (null == request) - { - err_msg = $"failed to get request message : Invalid Request message - {req_msg.Request.MsgCase}"; - Log.getLogger().error(err_msg); - result.setFail(ServerErrorCode.InvalidArgument, err_msg); - return result; - } - + ArgumentNullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {player.toBasicString()}"); + + var game_zone_move_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(game_zone_move_action, () => $"game_zone_move_action is null !!! - {player.toBasicString()}"); + //battle instance 인 경우 해당 패킷 사용 못하게 예러 처리(치트로 나가는 유저들이 있어서 임시로 막는다.) - if (BattleInstanceManager.It.isBattleInstance(entity_player)) + if (BattleInstanceManager.It.isBattleInstance(player)) { - err_msg = $"LeaveInstanceReq not use in battle instance entity_player : {entity_player.toBasicString()}"; + err_msg = $"LeaveInstanceReq not use in battle instance entity_player : {player.toBasicString()}"; Log.getLogger().error(err_msg); result.setFail(ServerErrorCode.FailedToReservationEnter, err_msg); - send_S2C_ACK_LEAVE_INSTANCE(entity_player, result, null); - return result; - } - - - var location_action = entity_player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(location_action, () => $"LocationAction is null !!! - player:{entity_player.toBasicString()}"); - - bool prev_is_in_concert = location_action.isInConcert(); - var attribute = entity_player.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(attribute, () => $"LocationAttribute is null !!! - player:{entity_player.toBasicString()}"); - - if (false == MetaData.Instance._IndunTable.TryGetValue(attribute.CurrentIndunLocation.InstanceMetaId, out var prev_indun_data) || null == prev_indun_data) - { - err_msg = $"failed to get indun data !!! - {entity_player.toBasicString()}"; - result.setFail(ServerErrorCode.NotFoundIndunData, err_msg); - Log.getLogger().error(err_msg); - return result; - } - - // 1. 이동할 서버 조회 - var move_server_info = await searchMoveToChannelServer(entity_player); - if (null == move_server_info) - { - err_msg = $"Failed to get balanced GameServer !!! - {entity_player.toBasicString()}"; - result.setFail(ServerErrorCode.NoServerConnectable, err_msg); - Log.getLogger().error(err_msg); - return result; - } - - // 2. 이동 예약 요청 - var message = new ServerMessage.Types.GS2GS_REQ_RESERVATION_ENTER_TO_SERVER(); - message.MoveType = ServerMoveType.Force; - message.RequestUserGuid = entity_player.getUserGuid(); - message.RequestServerName = GameServerApp.getServerLogic().getServerName(); - - var reserved = await server_logic.getReservationManager().registerReservationEnterToServer(message, move_server_info.Name); - - // 예약 실패 체크 - if (null == reserved) - { - err_msg = $"Failed to reservation enter to game server!!! - {nameof(LeaveInstancePacketHandler)}"; - Log.getLogger().error(err_msg); - result.setFail(ServerErrorCode.FailedToReservationEnter, err_msg); - - send_S2C_ACK_LEAVE_INSTANCE(entity_player, result, null); - return result; - } - - // 4. instance room 떠나기 - result = await entity_player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "LeaveInstance", leaveInstanceDelegate); - if (result.isFail()) - { - err_msg = $"Failed to runTransactionRunner() !!! : {result.toBasicString()} - {entity_player.toBasicString()}"; - Log.getLogger().error(err_msg); - } - - - // if (prev_is_in_concert && prev_indun_data is not null) - // { - // await QuestManager.It.QuestCheck(entity_player, new QuestConcert(EQuestEventTargetType.CONCERT, EQuestEventNameType.ENDED, prev_indun_data.MapId)); - // } - - - return result; - - async Task leaveInstanceDelegate() => await leaveInstanceAsync(entity_player, move_server_info, prev_is_in_concert, prev_indun_data); - } - - public async Task leaveInstanceAsync(Player player, ServerInfo server_info, bool prevIsInConcert, InstanceMetaData prevIndunData) - { - var server_logic = GameServerApp.getServerLogic(); - - var game_zone_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!! - {player.toBasicString()}"); - - var result = new Result(); - string err_msg; - - // 1. leave room - (result, _) = await game_zone_action.tryLeaveInstanceRoom(); - if (result.isFail()) - { - err_msg = $"Failed to tryLeaveInstance() !!! : {result.toBasicString()} - {player.toBasicString()}"; - Log.getLogger().error(err_msg); - send_S2C_ACK_LEAVE_INSTANCE(player, result, null); return result; } - var location_action = player.getEntityAction(); - - // 2. move to channel - result = await location_action.tryMoveToChannel(); - if (result.isFail()) + var fn_transaction_runner = async delegate () { - err_msg = $"Fail to tryMoveToIndun"; - Log.getLogger().error(err_msg); + var result = new Result(); + + (result, var server_connect_info, var business_logs) = await game_zone_move_action.tryLeaveInstance(); + if (result.isFail()) + { + err_msg = $"Failed to tryLeaveInstance() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + send_S2C_ACK_LEAVE_INSTANCE(player, result, null); + return result; + } + NullReferenceCheckHelper.throwIfNull(business_logs, () => $"business_logs is null !!! - {player.toBasicString()}"); + + var batch = new QueryBatchEx(player, LogActionType.LeaveInstance, server_logic.getDynamoDbClient()); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + batch.addQuery(new QueryFinal()); + } + + batch.appendBusinessLogs(business_logs); + + await QuestEventTrigger(player); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) + { + err_msg = $"Failed to sendQueryAndBusinessLog() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + send_S2C_ACK_LEAVE_INSTANCE(player, result, null); + return result; + } + + send_S2C_ACK_LEAVE_INSTANCE(player, result, server_connect_info); - send_S2C_ACK_LEAVE_INSTANCE(player, result, null); return result; - } - - var buff_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(buff_action, () => $"buff_action is null !!! - {player.toBasicString()}"); - - result = await buff_action.MoveServer(EPlaceType.World); - if (result.isFail()) - { - send_S2C_ACK_LEAVE_INSTANCE(player, result, null); - return result; - } - - // 3. otp 생성 - (result, var reserved_to_switch_server) = await ServerConnectionSwitchHelper.startServerSwitch(player, server_logic.getRedisConnector(), server_info.Name); - if (result.isFail() || null == reserved_to_switch_server) - { - err_msg = $"Fail to startServerSwitch() !!! : {result.toBasicString()}"; - Log.getLogger().error(result.toBasicString()); - - send_S2C_ACK_LEAVE_INSTANCE(player, result, null); - return result; - } - - var game_login_action = player.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_login_action, () => $"game_login_action is null !!! - {player.toBasicString()}"); - - var login_cache = game_login_action.getLoginCacheRequest()?.getLoginCache(); - NullReferenceCheckHelper.throwIfNull(login_cache, () => $"LoginCache is null !!! - player:{player.toBasicString()}"); - - login_cache.ReservedToSwitchServer = reserved_to_switch_server; - - var gameServer_connection_info = new ServerConnectInfo - { - ServerAddr = server_info.Address, - ServerPort = server_info.Port, - Otp = reserved_to_switch_server.OneTimeKey, }; - - var batch = new QueryBatchEx(player, LogActionType.None, server_logic.getDynamoDbClient()); - { - batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); - batch.addQuery(new QueryFinal()); - } - if (prevIsInConcert && prevIndunData is not null) - { - await QuestManager.It.QuestCheckWithoutTransaction(player, new QuestConcert(EQuestEventTargetType.CONCERT, EQuestEventNameType.ENDED, prevIndunData.MapId)); - } - - result = await QueryHelper.sendQueryAndBusinessLog(batch); + result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "LeaveInstance", fn_transaction_runner); if (result.isFail()) { err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); - - send_S2C_ACK_LEAVE_INSTANCE(player, result, null); - return result; } - - send_S2C_ACK_LEAVE_INSTANCE(player, result, gameServer_connection_info); + return result; } - private async Task searchMoveToChannelServer(Player player) + async Task QuestEventTrigger(Player player) { - var server_logic = GameServerApp.getServerLogic(); - - var location_attribute = player.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(location_attribute, () => $"location_attribute is null !!! - {player.toBasicString()}"); + var result = new Result(); + var err_msg = string.Empty; - var channel_server_name = location_attribute.LastestChannelServerLocation.ServerName; - var world_meta_id = location_attribute.LastestChannelServerLocation.WorldMetaId; + var location_action = player.getEntityAction(); + var instance_meta_id = location_action.getCurrentMapMId(); - var server_info = await server_logic.getReturnToServerInfo(channel_server_name, ServerType.Channel, player, (ushort)world_meta_id); + if (!MetaData.Instance._IndunTable.TryGetValue(instance_meta_id, out var indun_meta_data)) + { + err_msg = $"Failed to MetaData.TryGetValue() !!! : instanceMetaId:{instance_meta_id} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); - return server_info; + return; + } + + if (indun_meta_data.ContentsType == ContentsType.Concert) + { + await QuestManager.It.QuestCheckWithoutTransaction(player, new QuestConcert(EQuestEventTargetType.CONCERT, EQuestEventNameType.ENDED, indun_meta_data.MapId)); + } } } diff --git a/GameServer/Contents/GameZone/PacketHandler/MoveToBeaconPacketHandler.cs b/GameServer/Contents/GameZone/PacketHandler/MoveToBeaconPacketHandler.cs new file mode 100644 index 0000000..233f715 --- /dev/null +++ b/GameServer/Contents/GameZone/PacketHandler/MoveToBeaconPacketHandler.cs @@ -0,0 +1,174 @@ +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ClientToGameRes.Types; + +namespace GameServer.PacketHandler +{ + [PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_MOVE_TO_BEACON), typeof(MoveToBeaconPacketHandler), typeof(GameLoginListener))] + internal class MoveToBeaconPacketHandler : PacketRecvHandler + { + public static bool send_S2C_ACK_MOVE_TO_BEACON(Player owner, Result result,GS2C_ACK_MOVE_TO_BEACON res) + { + var ack_packet = new ClientToGame(); + ack_packet.Response = new ClientToGameRes(); + + ack_packet.Response.ErrorCode = result.ErrorCode; + ack_packet.Response.AckMoveToBeacon = res; + + if (false == GameServerApp.getServerLogic().onSendPacket(owner, ack_packet)) + { + return false; + } + + return true; + } + + public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + NullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + var request = req_msg.Request.ReqMoveToBeacon; + var res = new GS2C_ACK_MOVE_TO_BEACON(); + + var player_action = player.getEntityAction(); + var ugc_npc = player_action.findUgcNpc(request.BeaconGuid); + if (ugc_npc == null) + { + err_msg = $"Not Found Beacon !!! : BeaconGuid:{request.BeaconGuid} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + send_S2C_ACK_MOVE_TO_BEACON(player, result, res); + return result; + } + + var ugc_npc_attribute = ugc_npc.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {player.toBasicString()}"); + + var fn_transaction_runner = async delegate () + { + var result = new Result(); + + ServerConnectInfo? server_connect_info; + List? business_logs; + + var move_cost = MetaHelper.GameConfigMeta.BeaconMoveCost; + var money_action = player.getEntityAction(); + result = await money_action.changeMoney(CurrencyType.Gold, -move_cost); + if (result.isFail()) + { + err_msg = $"Failed to changeMoney() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + send_S2C_ACK_MOVE_TO_BEACON(player, result, res); + return result; + } + + var game_zone_action = player.getEntityAction(); + var curr_map = game_zone_action.getLinkedToMap(); + NullReferenceCheckHelper.throwIfNull(curr_map, () => $"curr_map is null !!!"); + + // Beacon 이랑 같은 Map에 있는지 확인 + var curr_map_ugc_npc = curr_map.findUgcNpc(request.BeaconGuid); + if (curr_map_ugc_npc != null) + { + err_msg = $"Same Map as Beacon !!! : BeaconGuid:{request.BeaconGuid} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.SameMapAsBeacon, err_msg); + Log.getLogger().error(result.toBasicString()); + + send_S2C_ACK_MOVE_TO_BEACON(player, result, res); + return result; + } + + var game_zone_move_action = player.getEntityAction(); + switch (ugc_npc_attribute.State) + { + case EntityStateType.UsingByCrafting: + { + (result, server_connect_info, business_logs) = await game_zone_move_action.tryEnterMyhome(ugc_npc_attribute.OwnerGuid, ugc_npc_attribute.LocatedInstanceGuid); + } + break; + case EntityStateType.UsingByFarming: + { + var user_guid = player.getUserGuid(); + + (result, server_connect_info, business_logs) = await game_zone_move_action.tryJoinInstance(user_guid, (int)ugc_npc_attribute.LocatedInstanceMetaId); + } + break; + case EntityStateType.UsingByMyHome: + { + (result, server_connect_info, business_logs) = await game_zone_move_action.tryEnterMyhome(ugc_npc_attribute.OwnerGuid, ugc_npc_attribute.LocatedInstanceGuid); + } + break; + default: + { + err_msg = $"WarpType Invalid !!! : type:{ugc_npc_attribute.State}, npcGuid:{ugc_npc_attribute.UgcNpcMetaGuid} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.EntityStateTypeInvalid, err_msg); + Log.getLogger().error(err_msg); + + send_S2C_ACK_MOVE_TO_BEACON(player, result, res); + return result; + } + } + + if (result.isFail()) + { + err_msg = $"Failed to tryEnterMyhome() or tryJoinInstance() !!! : ugcNpcState:{ugc_npc_attribute.State}, {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + send_S2C_ACK_MOVE_TO_BEACON(player, result, res); + return result; + } + NullReferenceCheckHelper.throwIfNull(server_connect_info, () => $"server_connect_info is null !!! - {player.toBasicString()}"); + NullReferenceCheckHelper.throwIfNull(business_logs, () => $"business_logs is null !!! - {player.toBasicString()}"); + + var batch = new QueryBatchEx(player, LogActionType.MoveToBeacon, server_logic.getDynamoDbClient()); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + batch.addQuery(new QueryFinal()); + } + + batch.appendBusinessLogs(business_logs); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) + { + send_S2C_ACK_MOVE_TO_BEACON(player, result, res); + return result; + } + + var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); + NullReferenceCheckHelper.throwIfNull(found_transaction_runner, () => $"found_transaction_runner is null !!! - {player.toBasicString()}"); + res.CommonResult = found_transaction_runner.getCommonResult(); + res.ServerConnectInfo = server_connect_info; + + send_S2C_ACK_MOVE_TO_BEACON(player, result, res); + return result; + }; + + result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "MoveToBeacon", fn_transaction_runner); + if (result.isFail()) + { + err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + } + + return result; + } + } +} diff --git a/GameServer/Contents/GameZone/PacketHandler/TaxiPacketHandler.cs b/GameServer/Contents/GameZone/PacketHandler/TaxiPacketHandler.cs index c8dea15..3affa11 100644 --- a/GameServer/Contents/GameZone/PacketHandler/TaxiPacketHandler.cs +++ b/GameServer/Contents/GameZone/PacketHandler/TaxiPacketHandler.cs @@ -1,4 +1,6 @@ -using Google.Protobuf; +using GameServer.EventDefines; +using GameServer.EventInvokers; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -44,25 +46,24 @@ internal class TaxiPacketHandler : PacketRecvHandler var result = new Result(); var err_msg = string.Empty; - var player = entityWithSession as Player; - ArgumentNullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - var req_msg = recvMessage as ClientToGame; - ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); - var request = req_msg.Request.TaxiReq; - var server_logic = GameServerApp.getServerLogic(); - var game_zone_move_action = player.getEntityAction(); - ArgumentNullException.ThrowIfNull(game_zone_move_action, $"game_zone_move_action is null !!! - {player.toBasicString()}"); + var player = entityWithSession as Player; + ArgumentNullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + var request = req_msg.Request.TaxiReq; + var game_zone_move_action = player.getEntityAction(); var game_zone_action = player.getEntityAction(); - ArgumentNullException.ThrowIfNull(game_zone_action, $"game_zone_action is null !!! - {player.toBasicString()}"); var fn_transaction_runner = async delegate () { var result = new Result(); - (result, var ack_msg, var business_logs) = await game_zone_move_action.tryTaxi(request.TaxiId); + (result, var ack_msg, var business_logs) = await game_zone_move_action.tryTaxi(request.DepartureTaxiId, request.ArrivalTaxiId); if (result.isFail()) { err_msg = $"Failed to tryTaxi() !!! : {result.toBasicString()}"; @@ -115,10 +116,12 @@ internal class TaxiPacketHandler : PacketRecvHandler { err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); + return result; } - await QuestManager.It.QuestCheck(player, new QuestTaxi(EQuestEventTargetType.TAXI, EQuestEventNameType.ARRIVED, request.TaxiId)); - + await QuestManager.It.QuestCheck(player, new QuestTaxi(EQuestEventTargetType.TAXI, EQuestEventNameType.ARRIVED, request.ArrivalTaxiId)); + await player.getGameEventCollector().collectEvent(new GameEventInvokerTaxiArrived(player)); + return result; } } diff --git a/GameServer/Contents/GameZone/PacketHandler/UseMountPropPacketHandler.cs b/GameServer/Contents/GameZone/PacketHandler/UseMountPropPacketHandler.cs index 61ab344..d1e71ea 100644 --- a/GameServer/Contents/GameZone/PacketHandler/UseMountPropPacketHandler.cs +++ b/GameServer/Contents/GameZone/PacketHandler/UseMountPropPacketHandler.cs @@ -36,18 +36,16 @@ internal class UseMountPropPacketHandler : PacketRecvHandler public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) { - var player = entityWithSession as Player; - ArgumentNullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - var req_msg = recvMessage as ClientToGame; - ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); - var request = req_msg.Request.UseMountPropReq; - ArgumentNullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {player.toBasicString()}"); - var result = new Result(); var err_msg = string.Empty; - var server_logic = GameServerApp.getServerLogic(); + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + var req_msg = recvMessage as ClientToGame; + NullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + var request = req_msg.Request.UseMountPropReq; var res = new UseMountPropRes(); var game_zone_action = player.getEntityAction(); diff --git a/GameServer/Contents/GameZone/PacketHandler/UseRewardPropPacketHandler.cs b/GameServer/Contents/GameZone/PacketHandler/UseRewardPropPacketHandler.cs index 65a8cd1..6a81ac7 100644 --- a/GameServer/Contents/GameZone/PacketHandler/UseRewardPropPacketHandler.cs +++ b/GameServer/Contents/GameZone/PacketHandler/UseRewardPropPacketHandler.cs @@ -1,4 +1,5 @@ -using Google.Protobuf; +using GameServer.EventInvokers; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -64,20 +65,22 @@ internal class UseRewardPropPacketHandler : PacketRecvHandler public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) { - var player = entityWithSession as Player; - ArgumentNullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - var req_msg = recvMessage as ClientToGame; - ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); - var request = req_msg.Request.UseRewardPropReq; - ArgumentNullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {player.toBasicString()}"); - - var res = new UseRewardPropRes(); - var result = new Result(); var err_msg = string.Empty; var server_logic = GameServerApp.getServerLogic(); + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + NullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + var request = req_msg.Request.UseRewardPropReq; + var res = new UseRewardPropRes(); + + res.AnchorGuid = request.AnchorGuid; + var game_zone_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!! - {player.toBasicString()}"); @@ -85,7 +88,7 @@ internal class UseRewardPropPacketHandler : PacketRecvHandler { var result = new Result(); - (result, res, var reward_prop_log_info) = await game_zone_action.tryUseRewardProp(request.AnchorGuid); + (result, var reward_prop_log_info) = await game_zone_action.tryUseRewardProp(request.AnchorGuid); if (result.isFail()) { err_msg = $"Failed to tryUseRewardProp() !!! : {result.toBasicString()} - {player.toBasicString()}"; @@ -119,6 +122,8 @@ internal class UseRewardPropPacketHandler : PacketRecvHandler send_S2C_ACK_USE_REWARD_PROP(player, result, res); player.send_S2C_NTF_REWARD_PROP_STATE(res.AnchorGuid, false); + await player.getGameEventCollector().collectEvent(new GameEventInvokerRewadPropRewarded(player)); + return result; }; diff --git a/GameServer/Contents/ItemFunction/Action/ItemBuyAction.cs b/GameServer/Contents/ItemFunction/Action/ItemBuyAction.cs index 903ee18..ae1ec4a 100644 --- a/GameServer/Contents/ItemFunction/Action/ItemBuyAction.cs +++ b/GameServer/Contents/ItemFunction/Action/ItemBuyAction.cs @@ -1,14 +1,11 @@ using Google.Protobuf; using Google.Protobuf.WellKnownTypes; - - -using ServerCore; +using MetaAssets; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; -using MetaAssets; - - +using ServerCore; +using static ClientToLoginMessage.Types; using META_ID = System.UInt32; @@ -18,7 +15,7 @@ namespace GameServer; public class ItemBuyAction : EntityActionBase { public List? BuyItems => m_buy_items; - private List? m_buy_items { get; set; } + private List m_buy_items { get; set; } = new List(); public ItemBuyAction(Player owner) : base(owner) { @@ -40,6 +37,7 @@ public class ItemBuyAction : EntityActionBase public async Task tryBuyItem(META_ID item_id, int buy_count) { var result = new Result(); + m_buy_items.Clear(); var discount = checkPurchaseDiscountType((int)item_id); @@ -48,11 +46,11 @@ public class ItemBuyAction : EntityActionBase if (result.isFail()) return result; // 2. 구매 처리 - (result, var spent_currency_type, var spent_currency) = await processForItem(item_data!, buy_count, discount.discount_rate); - if (result.isFail()) return result; + (result, var spent_info) = await processForItem(item_data!, buy_count, discount.discount_rate); + if (result.isFail() || spent_info == null) return result; // 3. 재화 차감 - result = await processSpent(spent_currency_type, spent_currency); + result = await processSpent(spent_info); if (result.isFail()) return result; result = await updateItemFirstPurchaseHistory(discount.discount_type, (int)item_id); @@ -60,7 +58,7 @@ public class ItemBuyAction : EntityActionBase return result; } - private async Task<(Result result, CurrencyType spent_currency_type, double spent_currency)> processForItem( + private async Task<(Result result, SpentInfo? spent_info)> processForItem( MetaAssets.ItemMetaData item_data, int buy_count, int discountRate) { var result = new Result(); @@ -74,39 +72,101 @@ public class ItemBuyAction : EntityActionBase err_msg = $"Fail to get Inventory Action : {nameof(InventoryActionBase)}."; result.setFail(ServerErrorCode.EntityActionNotFound, err_msg); Log.getLogger().error(err_msg); - - return (result, CurrencyType.None, 0); + + return (result, null); } (result, var changed_items) = await inventory_action.tryTakalbleToBag((META_ID)item_data.ItemId, (ushort)buy_count); - if (result.isFail()) return (result, CurrencyType.None, 0); + if (result.isFail()) return (result, null); - m_buy_items ??= new(); m_buy_items.AddRange(changed_items); - var check_currency_type = ShopHelper.checkCurrencyTypeFromCurrencyId(item_data.Buy_id); - if (check_currency_type.result.isFail()) return (check_currency_type.result, CurrencyType.None, 0); - - var price = await ShopHelper.checkCaliumCurrency(check_currency_type.currencyType, item_data.BuyPrice, discountRate); - - return (result, check_currency_type.currencyType, buy_count * price); - } + var spent_info = new SpentInfo(); + spent_info.m_buy_type = item_data.Buy_Price_Type; - private async Task processSpent(CurrencyType spent_currency_type, double spent_currency) - { - var result = new Result(); - - var money_action = getOwner().getEntityAction(); - if (null == money_action) + switch (item_data.Buy_Price_Type) { - var err_msg = $"Fail to get Money Action : {nameof(MoneyAction)}."; - result.setFail(ServerErrorCode.EntityActionNotFound, err_msg); - Log.getLogger().error(err_msg); + case ShopBuyType.Currency: + { + var check_currency_type = ShopHelper.checkCurrencyTypeFromCurrencyId(item_data.Buy_id); + if (check_currency_type.result.isFail()) return (result, null); - return result; + var price = await ShopHelper.checkCaliumCurrency(check_currency_type.currencyType, item_data.BuyPrice, discountRate); + + spent_info.m_currency_type = check_currency_type.currencyType; + spent_info.m_spent = price * buy_count; + } + break; + case ShopBuyType.Item: + { + spent_info.m_item_id = item_data.ItemId; + spent_info.m_spent = buy_count * item_data.BuyPrice; + } + break; + default: + { + err_msg = $"fail to get shop product meta data !!! : invalid shop buy type {item_data.Buy_Price_Type}"; + result.setFail(ServerErrorCode.InvalidShopBuyType, err_msg); + Log.getLogger().error(err_msg); + return (result, null); + } } - result = await money_action.changeMoney(spent_currency_type, -1 * spent_currency); + return (result, spent_info); + } + + private async Task processSpent(SpentInfo spentInfo) + { + var result = new Result(); + var err_msg = string.Empty; + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + switch (spentInfo.m_buy_type) + { + case ShopBuyType.Currency: + { + var money_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(money_action, () => $"money_action is null !!!"); + + result = await money_action.changeMoney(spentInfo.m_currency_type, -1 * spentInfo.m_spent); + if (result.isFail()) + { + err_msg = $"Failed to changeMoney() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + break; + case ShopBuyType.Item: + { + var inventory_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!!"); + + (result, var delete_items) = await inventory_action.tryDeleteItemByMetaId((uint)spentInfo.m_item_id, (ushort)spentInfo.m_spent); + if (result.isFail()) + { + err_msg = $"Failed to tryDeleteItemByMetaId() !!! : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + m_buy_items.AddRange(delete_items); + } + break; + default: + { + err_msg = $"fail to spent purchase !!! : invalid shop buy type - {spentInfo.m_buy_type}"; + result.setFail(ServerErrorCode.InvalidShopBuyType, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + } + return result; } diff --git a/GameServer/Contents/LargePacket/Action/LargePacketHandleAction.cs b/GameServer/Contents/LargePacket/Action/LargePacketHandleAction.cs new file mode 100644 index 0000000..9390457 --- /dev/null +++ b/GameServer/Contents/LargePacket/Action/LargePacketHandleAction.cs @@ -0,0 +1,182 @@ +using Google.Protobuf; +using NeoSmart.AsyncLock; +using ServerBase; +using ServerCore; + +namespace GameServer.Contents.LargePacket.Action; + +public class LargePacketHandleAction : EntityActionBase +{ + private SortedSet m_large_packets = new(); // 하나의 패킷만 처리 + private static AsyncLock m_lock = new(); + public LargePacketHandleAction(EntityBase owner) : base(owner) + { + } + + public override Task onInit() + { + var result = new Result(); + return Task.FromResult(result); + } + + public override void onClear() + { + m_large_packets.Clear(); + } + + public async Task collectPacket(LargePacketProcess processType, string packetUid, Int32 totalPacketCount, Int32 packetIdx, ByteString datas) + { + Log.getLogger().info($"LargePacketCollect packetUid : {packetUid}, totalPacketCount : {totalPacketCount}, packetIdx : {packetIdx}, datasize : {datas.Length}"); + + var collected_packet = new CollectedPacketInfo(processType, packetUid, totalPacketCount, packetIdx, datas); + var result = await addCollectedPacket(collected_packet); + if (result.isFail()) return result; + + return result; + } + + private async Task addCollectedPacket(CollectedPacketInfo packet) + { + var result = new Result(); + using (m_lock.Lock()) + { + switch (packet.m_process_type) + { + case LargePacketProcess.Start: + onClear(); + m_large_packets.Add(packet); + break; + case LargePacketProcess.Processing: + m_large_packets.Add(packet); + break; + case LargePacketProcess.End: + m_large_packets.Add(packet); + result = await processPacket(); + return result; + default: + result.setFail(ServerErrorCode.LargePacketProcessTypeInvalid); + return result; + } + } + + return result; + } + + private async Task processPacket() + { + var result = validCheck(); + if (result.isFail()) return result; + + var player = getOwner() as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !! - :{toBasicString()}"); + + using (var stream = new MemoryStream()) + { + foreach (var packet in m_large_packets) + { + if (packet.m_datas == null) + { + var err_msg = "packet.m_datas is null"; + result.setFail(ServerErrorCode.LargePacketDataIsNull, err_msg); + return result; + } + packet.m_datas.WriteTo(stream); + } + + if (stream.Length == 0) + { + var err_msg = "stream is empty after writing packets."; + result.setFail(ServerErrorCode.LargePacketDataIsNull, err_msg); + return result; + } + try + { + stream.Position = 0; + var req = ClientToGame.Parser.ParseFrom(stream); + + var session = player.getListenSessionBase(); + NullReferenceCheckHelper.throwIfNull(session, () => $"session is null !! - :{toBasicString()}"); + + result = await session.onCallProtocolHandler(player, req); + if (result.isSuccess()) + { + var sorted_packet_idxs = m_large_packets.OrderBy(p => p.m_packet_idx).ToList(); + var total_packet_count = m_large_packets.Select(p => p.m_total_packet_count).FirstOrDefault(); + var packet_uid = m_large_packets.Select(p => p.m_packet_uid).FirstOrDefault(); + Log.getLogger().info($"large packet onCallProtocolHandler success sorted_packet_idxs : {sorted_packet_idxs}, total_packet_count : {total_packet_count}, packet_uid : {packet_uid}"); + onClear(); + } + return result; + } + catch (Exception e) + { + Log.getLogger().error($"Exception !!!, Failed to perform !!!, in LargePacketHandler.processPacket() : exception:{e}"); + var err_msg = "stream is empty after writing packets."; + result.setFail(ServerErrorCode.LargePacketException, err_msg); + return result; + } + } + } + + private Result validCheck() + { + var err_msg = string.Empty; + var result = new Result(); + + var sorted_packet_idxs = m_large_packets.OrderBy(p => p.m_packet_idx).ToList(); + var total_packet_count = m_large_packets.Select(p => p.m_total_packet_count).FirstOrDefault(); + + //아직 패킷이 다 안들어온거다. + if (sorted_packet_idxs.Count < total_packet_count) + { + err_msg = $"Not all packets were received !!! : recvPacketCount:{sorted_packet_idxs.Count} < totalPacketCount:{total_packet_count} - {getOwner().toBasicString()}"; + result.setFail(ServerErrorCode.LargePacketNotAllReceived, err_msg); + return result; + } + + return result; + } + +} + +public class CollectedPacketInfo : IComparable +{ + public string m_packet_uid { get; } = string.Empty; + public Int32 m_total_packet_count { get; } = 0; + public Int32 m_packet_idx { get; } = 0; + public ByteString? m_datas { get; } = null; + public DateTime m_last_recv_time { get; } = DateTime.Now; + + public LargePacketProcess m_process_type { get; } = LargePacketProcess.None; + + public CollectedPacketInfo(LargePacketProcess processType, string packetUid, Int32 totalPacketCount, Int32 packetIdx, ByteString datas) + { + m_process_type = processType; + m_packet_uid = packetUid; + m_total_packet_count = totalPacketCount; + m_packet_idx = packetIdx; + m_datas = datas; + } + + public Int32 CompareTo(CollectedPacketInfo? other) + { + if (other == null) return 1; + return this.m_packet_idx.CompareTo(other.m_packet_idx); + } + + public override bool Equals(object? obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + CollectedPacketInfo other = (CollectedPacketInfo)obj; + return m_packet_idx == other.m_packet_idx; + } + + public override int GetHashCode() + { + return HashCode.Combine(m_packet_uid, m_total_packet_count, m_packet_idx, m_last_recv_time); + } +} diff --git a/GameServer/Contents/LargePacket/PacketHandler/LargePacketHandler.cs b/GameServer/Contents/LargePacket/PacketHandler/LargePacketHandler.cs new file mode 100644 index 0000000..22e43e2 --- /dev/null +++ b/GameServer/Contents/LargePacket/PacketHandler/LargePacketHandler.cs @@ -0,0 +1,56 @@ +using GameServer.Contents.LargePacket.Action; +using Google.Protobuf; +using ServerBase; +using ServerCore; + +namespace GameServer.PacketHandler; + +[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_LARGE_PACKET), typeof(LargePacketHandler), typeof(GameLoginListener))] +public class LargePacketHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var result = new Result(); + + var player = session as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var err_msg = string.Empty; + var game_msg = recvMessage as ClientToGame; + if (game_msg == null) + { + err_msg = $"Failed to cast ClientToGame !!! : {nameof(ClientToGame.Request.ReqLargePacket)}"; + result.setFail(ServerErrorCode.ClassTypeCastIsNull, err_msg); + Log.getLogger().error(result.toBasicString()); + //send_S2C_ACK_BEACON_SHOP_GET_ITEM_INFOS(entity_player, result); + return result; + } + var request = game_msg.Request.ReqLargePacket; + + var type = request.ProcessType; + string packet_uid = request.Uid; + Int32 packet_index = request.PacketIndex; + Int32 total_packet_count = request.TotalPacketCount; + + var large_packet_handle_action = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(large_packet_handle_action, () => $"large_packet_handle_action is null !!!"); + + result = await large_packet_handle_action.collectPacket(type, packet_uid, total_packet_count, packet_index, request.Data); + + if (result.isFail()) + { + send_S2C_ACK_LARGE_PACKET(player, result); + } + + return result; + } + + public void send_S2C_ACK_LARGE_PACKET(Player player, Result result) + { + ClientToGame ntf = new ClientToGame(); + ntf.Message = new ClientToGameMessage(); + ntf.Message.NtfLargePacketProcessError = new ClientToGameMessage.Types.GS2C_NTF_LARGE_PACKET_PROCESS_ERROR(); + ntf.Message.NtfLargePacketProcessError.ErrorCode = result.getErrorCode(); + GameServerApp.getServerLogic().onSendPacket(player, ntf); + } +} \ No newline at end of file diff --git a/GameServer/Contents/Location/Action/LocationAction.cs b/GameServer/Contents/Location/Action/LocationAction.cs index 2c3f7a2..6691ff3 100644 --- a/GameServer/Contents/Location/Action/LocationAction.cs +++ b/GameServer/Contents/Location/Action/LocationAction.cs @@ -171,19 +171,36 @@ internal partial class LocationAction : EntityActionBase if (false == user_create_or_load_action.hasUserCreateTrigger()) { Pos? start_pos; - var lastest_pos = new Pos(); - location_attribute.LastestChannelServerLocation.toPos(lastest_pos); - - if (isFirstLogin) + + if (location_attribute.MoveToAccountStartPosAtLogin) { - if (!server_logic.getMap().GetNearestStartPoint(lastest_pos, out start_pos)) + if (!server_logic.getMap().getAccountStartPoint(out start_pos)) { - start_pos = lastest_pos; + err_msg = $"Failed to getAccountStartPoint : {this.getTypeName()}"; + result.setFail(ServerErrorCode.InstanceMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; } + + location_attribute.MoveToAccountStartPosAtLogin = false; } else - { - start_pos = lastest_pos; + { + var lastest_pos = new Pos(); + location_attribute.LastestChannelServerLocation.toPos(lastest_pos); + + if (isFirstLogin) + { + if (!server_logic.getMap().GetNearestStartPoint(lastest_pos, out start_pos)) + { + start_pos = lastest_pos; + } + } + else + { + start_pos = lastest_pos; + } } location_attribute.CurrentChannelServerLoaction.ServerName = server_logic.getServerName(); diff --git a/GameServer/Contents/Loginout/Action/GameLoginAction.cs b/GameServer/Contents/Loginout/Action/GameLoginAction.cs index 76ac820..5ab877d 100644 --- a/GameServer/Contents/Loginout/Action/GameLoginAction.cs +++ b/GameServer/Contents/Loginout/Action/GameLoginAction.cs @@ -87,17 +87,17 @@ public partial class GameLoginAction : EntityActionBase { return result; } - + // 3. 예약자 확인 result = await server_logic.getReservationManager().checkReservation(account_attribute.UserGuid); if (result.isFail()) { return result; } - + // 4. 리턴 유저 정리 _ = await server_logic.getReturnManager().checkReturnUser(account_attribute.UserGuid); - + var login_cache = login_cache_request.getLoginCache(); NullReferenceCheckHelper.throwIfNull(login_cache, () => $"login_cache is null !!! - {owner.toBasicString()}"); NullReferenceCheckHelper.throwIfNull(login_cache.ReservedToSwitchServer, () => $"login_cache.ReservedToSwitchServer is null !!! - {owner.toBasicString()}"); @@ -120,7 +120,7 @@ public partial class GameLoginAction : EntityActionBase { return result; } - + // LoginCacheRequest 보관 한다 !!! m_login_cache_request_nullable = login_cache_request; @@ -208,7 +208,7 @@ public partial class GameLoginAction : EntityActionBase { var result = new Result(); - // LoginCacheRequest 로딩 했는가? + // LoginCacheRequest 로딩 했는가? if (null != m_login_cache_request_nullable) { result = await m_login_cache_request_nullable.upsertLogin(); @@ -225,7 +225,7 @@ public partial class GameLoginAction : EntityActionBase { var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! - {toBasicString()}"); - + if (failedResult.isSuccess()) { return; diff --git a/GameServer/Contents/Loginout/Action/GameLogoutAction.cs b/GameServer/Contents/Loginout/Action/GameLogoutAction.cs index a318d99..fc0f429 100644 --- a/GameServer/Contents/Loginout/Action/GameLogoutAction.cs +++ b/GameServer/Contents/Loginout/Action/GameLogoutAction.cs @@ -43,6 +43,7 @@ public partial class GameLogoutAction : EntityActionBase var server_logic = GameServerApp.getServerLogic(); var db_client = server_logic.getDynamoDbClient(); + var ranking_manager = server_logic.getRankingManager(); var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); @@ -55,6 +56,8 @@ public partial class GameLogoutAction : EntityActionBase return result; } + ranking_manager.removeRanker(owner); + var game_login_action = owner.getEntityAction(); NullReferenceCheckHelper.throwIfNull(game_login_action, () => $"game_login_action is null !!! - {owner.toBasicString()}"); @@ -109,34 +112,13 @@ public partial class GameLogoutAction : EntityActionBase cancel_token.Cancel(); // 채널 or 던전 공간에서 나간다 !!! - var user_attribute = owner.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user_attribute is null !!! - {owner.toBasicString()}"); - var game_zone_action = owner.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(game_zone_action, () => $"game_zone_action is null !!! - {owner.toBasicString()}"); - - var curr_map = game_zone_action.getLinkedToMap(); - if(null != curr_map) - { - var opccupied_anchor_guid = user_attribute.OccupiedAnchorGuid; - if(true == curr_map.getAnchors().TryGetValue(opccupied_anchor_guid, out var anchor_info)) - { - anchor_info.OccupyingUserGuid = string.Empty; - } - } - if (server_logic.getServerType().toServerType() == ServerType.Channel) { await game_zone_action.tryLeaveGameZone(); } else { - var location_action = owner.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {owner.toBasicString()}"); - - var current_indun_location = location_action.getCurrentLocation() as IndunLocation; - NullReferenceCheckHelper.throwIfNull(current_indun_location, () => $"current_indun_location is null !!! - {owner.toBasicString()}"); - await game_zone_action.tryLeaveInstanceRoom(); } @@ -154,6 +136,9 @@ public partial class GameLogoutAction : EntityActionBase owner.setLogoutEndTime(DateTimeHelper.Current); + var user_attribute = owner.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user_attribute is null !!! - {owner.toBasicString()}"); + user_attribute.GameLogoutDateTime = owner.getLogoutEndTime(); user_attribute.modifiedEntityAttribute(); diff --git a/GameServer/Contents/Loginout/PacketHandler/GameLogoutPacketHandler.cs b/GameServer/Contents/Loginout/PacketHandler/GameLogoutPacketHandler.cs index 44f67c0..3b314b5 100644 --- a/GameServer/Contents/Loginout/PacketHandler/GameLogoutPacketHandler.cs +++ b/GameServer/Contents/Loginout/PacketHandler/GameLogoutPacketHandler.cs @@ -74,6 +74,8 @@ public class GameLogoutReqHandler : PacketRecvHandler server_logic.getProudNetListener().disconnectClient(entity_player.getSessionId()); return result; } + //kihoon : 이쪽에 disconnect 처리가 안되어 있어서 처리 필요 나중에 찬헌님께 물어볼것 + MatchManager.It.OnDisconnectEvent(entity_player); send_S2C_ACK_LOGOUT_TO_GAME(entity_player, result); diff --git a/GameServer/Contents/Match/CheatCommand/ChatCommandMatch.cs b/GameServer/Contents/Match/CheatCommand/ChatCommandMatch.cs new file mode 100644 index 0000000..cb67d8a --- /dev/null +++ b/GameServer/Contents/Match/CheatCommand/ChatCommandMatch.cs @@ -0,0 +1,139 @@ +using CommandLine; +using MetaAssets; +using ServerCommon; +using ServerCore; + +namespace GameServer; + +[ChatCommandAttribute("match", typeof(ChatCommandMatch), AuthAdminLevelType.Developer, + AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +public class ChatCommandMatch : ChatCommandBase +{ + public override async Task invoke(Player player, string token, string[] args) + { + int gameModeId = 0; + var cliMsg = new ClientToGame { Request = new ClientToGameReq() }; + var req = new ClientToGameReq.Types.C2GS_REQ_MATCH_CANCEL { GameModeId = gameModeId, }; + cliMsg.Request.ReqMatchCancel = req; + + var serverLogic = GameServerApp.getServerLogic(); + await serverLogic.onCallProtocolHandler(player, cliMsg); + try + { + var cmdName = args[0]; + var cmdArgs = args.Skip(1).ToArray(); + switch (cmdName) + { + case "reserve": + await execCmd(cmdArgs); + break; + case "cancel": + await execCmd(cmdArgs); + break; + case "reset_play_penalty": + await execCmd(cmdArgs); + break; + case "reset_match_count": + await execCmd(cmdArgs); + break; + case "check_event": + // 매칭 서버로 보내는 치트 + MatchManager.It.SendCheatCmd(args); + break; + default: + Log.getLogger().error($"Invalid match command arguments"); + break; + } + } + catch (Exception ex) + { + Log.getLogger().error($"Match Command Failed => {ex}"); + } + + return; + + async Task execCmd(string[] cmdArgs) where TCmd : ICmdCheat + { + try + { + var parserResult = CommandLine.Parser.Default.ParseArguments(cmdArgs); + var cmd = parserResult.Value as ICmdCheat; + var result = await cmd.ExecuteAsync(player); + MatchGuard.ResultFailException(result); + } + catch (ResultException ex) + { + Log.getLogger().error($"Match Command Result Failed => {ex}"); + } + catch (Exception ex) + { + Log.getLogger().error($"Match Command Failed => {ex}"); + } + } + } + + public interface ICmdCheat + { + Task ExecuteAsync(Player player); + } + + public class CmdReserve : ICmdCheat + { + [Option('g', "game", HelpText = "")] public int GameModeId { get; set; } = 1; + [Option('e', "event", HelpText = "")] public int EventId { get; set; } = 0; + [Option('r', "regin", HelpText = "")] public string RegionName { get; set; } = "ASIA"; + [Option('p', "ping", HelpText = "")] public int RegionPingMs { get; set; } = 10; + + [Option('i', "instance", HelpText = "")] + public int InstanceId { get; set; } = 0; + + public async Task ExecuteAsync(Player player) + { + var cliMsg = new ClientToGame { Request = new ClientToGameReq() }; + var req = new ClientToGameReq.Types.C2GS_REQ_MATCH_RESERVE + { + GameModeId = GameModeId, + EventId = EventId, + RegionName = RegionName, + RegionPingMs = RegionPingMs, + InstanceId = InstanceId + }; + cliMsg.Request.ReqMatchReserve = req; + + var listenSession = player.getListenSessionBase(); + MatchGuard.NotNull(listenSession); + var result = await listenSession.onCallProtocolHandler(player, cliMsg); + return result; + } + } + + public class CmdCancel : ICmdCheat + { + public async Task ExecuteAsync(Player player) + { + var cliMsg = new ClientToGame { Request = new ClientToGameReq() }; + var req = new ClientToGameReq.Types.C2GS_REQ_MATCH_CANCEL { }; + cliMsg.Request.ReqMatchCancel = req; + var listenSession = player.getListenSessionBase(); + MatchGuard.NotNull(listenSession); + var result = await listenSession.onCallProtocolHandler(player, cliMsg); + return result; + } + } + + public class CmdResetPlayPenalty : ICmdCheat + { + public async Task ExecuteAsync(Player player) + { + return await GameModeManager.It.resetPlayPenalty(player, GameModeType.RUN_RACE); + } + } + + public class CmdResetMatchCount : ICmdCheat + { + public async Task ExecuteAsync(Player player) + { + return await GameModeManager.It.resetMatchCount(player, GameModeType.RUN_RACE); + } + } +} diff --git a/GameServer/Contents/Match/Doc/Match_CS_Sequence.md b/GameServer/Contents/Match/Doc/Match_CS_Sequence.md new file mode 100644 index 0000000..68cec60 --- /dev/null +++ b/GameServer/Contents/Match/Doc/Match_CS_Sequence.md @@ -0,0 +1,57 @@ +```mermaid +--- +title: 매칭 CS 시퀀스 다이어그램 +--- +sequenceDiagram +participant Client +participant GameServer +participant MatchServer +participant InstanceServer + + Note over Client, InstanceServer: 매칭 예약 및 처리 플로우 + + %% 매칭 예약 요청 + Client->>GameServer: C2GS_REQ_MATCH_RESERVE + #Note right of Client: gameModeId, regionName, regionPingMs + + GameServer->>MatchServer: 매칭 요청 전달 + #Note right of GameServer: RabbitMQ를 통한 서버간 통신 + + MatchServer->>GameServer: 매칭 예약 확인 + GameServer->>Client: GS2C_ACK_MATCH_RESERVE + #Note right of GameServer: matchStatus 포함 + + %% 매칭 진행 상태 통지 + loop 매칭 진행 중 + MatchServer->>GameServer: 매칭 상태 업데이트 + GameServer->>Client: GS2C_NTF_MATCH_STATUS + #Note right of GameServer: matchStatusInfo 포함 + end + + %% 매칭 성공 시나리오 + alt 매칭 성공/실패 + Note right of MatchServer: 인스턴스 서버 생성 + #InstanceServer->>MatchServer: 서버 정보 응답 + + MatchServer->>GameServer: 매칭 완료 통지 + GameServer->>Client: GS2C_NTF_MATCH_RESULT + #Note right of GameServer: errorCode=SUCCESS
instanceServerConnectInfo
instanceId + + Client->>InstanceServer: 게임 인스턴스 접속 + + %% 매칭 취소 시나리오 + else 매칭 취소 + Client->>GameServer: C2GS_REQ_MATCH_CANCEL + #Note right of Client: gameModeId + + GameServer->>MatchServer: 매칭 취소 요청 + MatchServer->>GameServer: 취소 처리 완료 + GameServer->>Client: GS2C_ACK_MATCH_CANCEL + + %% 매칭 실패 시나리오 + %% else 매칭 실패 + %% MatchServer->>GameServer: 매칭 실패 통지 + %% GameServer->>Client: GS2C_NTF_MATCH_RESULT + %% #Note right of GameServer: errorCode=FAIL
instanceServerConnectInfo=null + end +``` diff --git a/GameServer/Contents/Match/Doc/게임 매칭 CS 시퀀스 .png b/GameServer/Contents/Match/Doc/게임 매칭 CS 시퀀스 .png new file mode 100644 index 0000000000000000000000000000000000000000..18de2d1f0a93663cd960d8297e86fafc59382c2f GIT binary patch literal 86968 zcmd?RcUTkM<1dIJAR?e5AYDa3L5fJ|MMR}Zk=_zini4vphNcLLG^I-K9fFilLI9-~ z=_L?)FCjqaojd6JeZSxLclUYr?%sWNpSzbol3|jZIdkUBIiGeWKwVXy@)F}EA|fJ6 zMTI9CL`1~mL_`-@E}jSWFpTBS5D}f8Dn60ba>cGx!(Y;v)tt&$EA>7m_KPyCRH#7u zXxRr=XxYm~XgC{+Q7pF25LbXV`C^8wkjEp=sdsDE&r?#v%5BtBrT|9K~VDaltRX1vDo=} za}T5rIe|c^cHd?cdUNGC4@OrQF9nYhXSS4Cw9`62F|~!m*e-UbSV6xOK>MK{$J-d) zLI{$dnTCd*J`2V{BvDXMaMUb#>((uH_B6F5X(g`nstP?+EaI@^1E)?xfZy}H3kT@# zEPu{y>gvr-iSV$n2-Vq6!c22sUf%u^9~gC)g={E=JySs8fz{CWo-}!$gq8$Zn+3XIq=brbzALyPENC`OKoCuNPv)cOxRMXVAQJZb?x~lyoCZKP}KvW@A`>7OC1F zGHIFiESOQ$?xlwZx;MawCU3xfs?IMqHa1Sg_Uuf4eS4t9<8qeRcFP`|sOsHnI*=91K_ zPqaIrYy6^1+I9Wqfp`Xt?xu>iHm#!JM2+WK&7n<3iD*h81irhPLB8A<9v&VULVPE? zu<#U9d-BvB)+M$3h3Otkb1TN!TV-O1jFJ(ykl@0dK%%gTFP}&hwSQmpL+zV@?BIJr zSciEDezfu-C+GY-GS(luQXsj679ooDIt%M7(|Y-~=NWlmBCEOY9AKBc$xuLa`D>^zoKv$?$X3yz|rA+sCZ@=eFj)?2pS`VdaCM_4M`M3f!=%J$B}s40-C0 zNRWc>bf?I%KMPV&Q`0>=Z8U(N!3j+(alHb}hnCO39HU1_gAQ9|D zZhz}Wd}zLFH&Wr&6)=Uxh%e&lrPHGY85|B*Qc}`bzREqYCX2c4ryR|vt);b&qH}FN z55|2Ez%h!QMEaY1q10Y`tP_(vbqqesJI>riU@OvHt-nA6E6$4iS%~nf!~;JkQ3Pw| zPMnqf9+45s%JQd>moOC$LNfI0`DYj+rW;`5%P(P+qU6so6`xR|&*y%Bf&dd+DJ(O7 zgkpTu8c&Z|bN?MD|HAB1Va|9V{_APKK&2 zDHqtR1W%9m;<$IB9K(yzyYX{={q64H(Bm<1ow1mP;_c6!nb4iIh0diFwgo#j2KG|+ z_lRM8=ISShzfx>Qr+YgnG{n9lNGh);9e>>buFd1-WyVwF6OSK`%=Ud)19CO5 zGp~IOD6HJI=Vgt7{DyW0l((SVs@%YdF}PRu`X|gg6rFnEvO3D7E@@abcqSTbrB~$@ zy)Mq>aiO8WzymCMh}#ME=~=juI7`-ck|Ob-da}Xbb41z8Te~`yqP2VVIB$Y_=uPfa zs5BNrhBH`7rKyz2`Yc}=<-pIMn|M!rPmGlNL*w2h`UF>pReC~Bc3Hx-=qElt0fb8# z^Q+SO2PHo-F64#yg1ckGMkU@cCAeDAa!%}y?8TRaFW%vVOz7t=_^m;Z*8y@#BFS+g z62XPsH`o>xL_s3&tXw^rtm1<&T(fAux^}&5;8KpbHqZo)J5Riv6RQxFAsQ8wjrc2! z*%fBMElrwv&{Q$>O*(*FiR{HqNb{1xO?qRL7uh*6GYA4W1L!!EZdc z%91V>>J5=mKhm&fyIbBPJN1xua{AV@tKiL^9dQGUF!jB*QK>Qg*3v%X$w9Qq-D@Zi zhMzbjb^*Kl*sz|amd{e`=xTYvtBTUp9p9n``%Wm_@jj>+A0Lgo7qKIGj*rz6y%c-< z7(%uf+x_B`^~jt$&$2~>{4-2i`l1lWsjLl@h!xH)pRRNi(Ahb3RTi_dZJ4l@cZ~zq zF@8IbK47O*Aaumdog-XanK4z+F@8nT3-G&8gzoj4a)c2+^pb6{3dME35MFNfsFnKeL zM!va0Q3%1lBgPR^<=GQ?;JzX}KQ(TMf-sGb>r|JEl+%7LACYER2O35L6S2H2{uqNT z1Z^2-NQVUBZ7V&$fc*@oABU(81J(cb3}Y1PsOo@)sR%8za-atLUzd~)!R%u{R3!To za_UpPt;-Fdwt8=FSH`K*&@g#)(drW-Zh$x4U{-8kM;3{%-Jo)s9LZi0h?vOF!qG#r zHujQ0MsXP4U$ zN$3veK>A3n)h4fg({xIs3MK--D?V$Q-R=p$4c&F%?nCB+ca#qoV;6nzAGIoa|B$ed zzcYyZAraM?4jo5;p=?f@{h-dOd-5rWVVg0OmCbb4;WCZTgP`i5M)Mgsk4W~&^4Dau znnd>L>3jD0SSZG%cD-&m2^E6a9{{lgb}Vx>tgCg zcj}U%JH)-KOOa{}EEbQ!+j)46TMG<4OFhU*r(hb&Mh2kKLqVl;F~t@(s{v0RCowNh z-+G!4{k#i*)d#X46Hm`P-T)C=)ejBDR>@RiP=jI;w`DQUf*ELBn|pI$DhD?>oh-7W zd035n5D(bGKRBLC?8c}xI`LW&u2($lA1D_s|HyecS`5Xx?ZdYe@?11v@|c*Tr$a1? zThGdf2h9g1bYB$)Q+&t!$zfIw-7OAYl2k8o6KWMP9z98~?4mSSh^b@j*>w%^%7 znyglK%gZL(EW=7%?_R~lP9H;toH`2ppK9Zs%{5_=fjESl7y}llwB6K-P(5@{PPg~VHEk-B;5roJj&Kl}R)62IP6bwhKQcZGk z1Pu@E_^rj*1`u{Nn$peMcC}_sQa2LsPNMN6@FP`gcuM@^V@L(8V*owkl8PHDXy)at zQS(PR=_95GhQ)8kU$(A4G{ZnxXQv#TB|F+5IyHKWcYSpk8ha-D-jGu3n%a~x$gcXZ z8s_@7B#uHIBU&CB)?TfP7;&nqlszEk81ASX1>XgR(5s&1jIKI4?Fbjo5ATi{*rpGs z!Bo{@)En#0g8ld@DjRS%3?9D48(npAj-ORf{IGx&0^CkJX*fAPY)DXiwzX-kPAp`F z`OYQ+8jm4!gP}q$mNOD@arYzpSHY18T9WB05jl1e5+{{n{O_#^6y3ZRbu9zqy+;@y zn(akKjI;A2KRM{DPhK*rjrV1I+}Q1}-lz&oqZ>N+b^^pAn1Q`IF;A3v+XT5AD4-3J$Uc z;5SeWE%CsFowB~9BsCG2!82G=ucaY5`I{N-6zOj*EmOx}oAWBmve|5} z^yHMGP@~0~dst@5=KxvFrG2J@4wX3>dR^2l!hDcOldIy>UFUr@I5;>#*xJCzC_YrI zesD)MLELF;v8TVkpY3RpOi7n|y=|)gV^_QwyW6`j8pq?SV^vF6*6Z0$1UNXJE-jzy z{njzBCUYFYnO)ywi8T5lp__;xu)FrBz2NH2Qe(?drW{SNg&TVxgFN}8;TlHCWv#C( z-}X86+~Hb$-_`e@^dojs_l7-TuaAbDoZMy1i9Sfh?@Jzwr0W-Pb@Az5drWiuD;s!! ziYB*_N2ijM%3)jja5ABviqeHqHp*|os|0Sq!;lH27r5ATkw_3X+WL@ChZ z)9_RKh+>&TjV}#>$tX|Sn0)5rDhRbPf zyc+1^+(GmgAEO%@8l*iB))(r0Zx!=V1xC1TDW=RIhOMHuzrJ`ilBRYH(0TXm<$)3l zHQFYc841Yq=a)TA54Lgid+WZptqW^vYRbyWDl5gBCgP{^rASqiq^(PaA3L3fYgmVd zd=pPVWD&EK+#6qep(8DxC{9 zWhqg`#SSfBZdvKNpSxn+ExQ|MTcfC^)?9zy=f3mWxJvK-4BM&R(#@M;qVSy+pdZTv zT`92c5eAUES^)0%7ri^*4O(O*68xJ1#y+y>M!x(^1SWn&*A4nX3ZoRb!VTVpFJ&DB zXn|);$k-BL_;&tUG`?&EDCxraB`ly$0R1c(`iL&cV}pDN8^rK1g6jNj@?Ipnd`QKZ zN-L?Y)mNPL)f{L!cG9+(BJ*N2zwx}2^_h-CPvLWM@>k45G-8UY)8~F$TblyL!NK9| z_W_OzodgtJOP@3P)+YliE2|*ljKD^%&!750+mFZJLj`1y{w2{5*a#8W!Lm=-V7C7(h>BsnD`Xio)bcDKnLK+A}knVHD5nyEX9 z9({_f!K8mcc)HmJRy-(c=|c_<>R{crS_^AYPnUj+jh`-GbC>MqE%J`$1MfW$&X{Ro z-+&clWca}ffNoDXn8W}A3PJDF4dKl`f+>gy`TaeCVF~$`vp1n=a@a^ z!TWUWz=)hbuK`>s;LoET7t1+S^Tab(H4E39q1-U$c;zb!kj_xfB~x;Lf5#w*PkF-g zk%xz*42l$td!&FdFfh1Jrz|HY*B*TO4sq`6up;jf9Lt(C*RCZ)S*Qx3M~jRw%Clp5 zy+imh{VB@y9GF6+8+5j}1<)I8{>Y_YNMO99k1%I@Z&EB8RQxFq=rhRf?q2tH=REDK zp1lMb6zQW+MZy^2MvmQ2Gpfu+)xmMquu%m<)db5#Y09nkxklv-D3#sAO$XxRpL2AZ zF1#pKfD&6|VwTh%I!0q%3-2S+W0arwL-mChpvPr&o4Xx4g@H{= z@J6T@kX`E)QQfJRzz+vRni`w76rZn4aKR{ju4tZM$e9joM?@b)a(dMtd#$N) zeV&l2^*%A7PjYr6>?wx}JD%>d@f4?HETo|KWm+j?UJAfy=TGn86OJk^j4-xP>75t* zgU4B+LYykhNe^&Y>>5afTe^EZXsS(Wop`Z%ZR@U0`Y7b3#n7^lMy8*mjp?c?yfePYo16B+N~ zh0m~>eno0Y>02+3KJPdDRC#*nqHSDY{rxm&P3rE$g^z&?p{jnz=Wc$yvTQKfbj=_F z1G+ZAL#ZMp>zBp7F7*(&gw89g+k~Q>+GgyWq6UUrCb2TJOe=mVTft{?9O@o7#}39f?usSMV=dTf86{>OOb~^n{;9ZdKiuXNc z3k|u8FO8q*he6GRE%=X@X2LZz@NGk{Yg`eqV`GdrbkDO@jf>NP$cKnL645Fc)m@hg zRpAv@4pgDfQv5_=De&d9$|=?cIXM2k`-y`{0w?F7%YF)!=sP)q6Z(iWjDt78{#toV|(#VYICMCbB*64a&LNIgoy)?lt zi^Jy&oL&F(7^HB1RrzT7bpMJI|A~A2daN$Er0Zzv6AP|sm?e+U5;*K#P@2SWM^UNG zWdKxlprv-OQl94b^Px#AE;P}rh40zPkzx&E|2ZooxWu^2|wJFwgX0EkZ!~Tz zS4I-v{e5NEt12$6=2``r^nSOa(5s%TGux8UL0kI8h9Q68Y#^iZdTfOhWEEnxN;d>dfO})xUf}iRHTT$8HfEUpHMDQkrq%c^MZuvie>7Sj&Jwv>(WTLZtX)k z4AD!jm*5#h;>fAzT0s2_n6CV*n%z<4A+vWv4bwEJV$=_7Ta<-xlN)rztQd;L<10`T zCq!R)0un{S#ouPTzwK|ZA5;@=hxOz@0?zdAoB73f-|`JXBV=butenJ?zb=XLXwQ=>ODwn{wfE z(ulhF>s2yk`8S2E9y=Bzg&l6GivhCk!4ZhDqoojzPA1~zu6QYMGg!jO$i*9C?#B;y zgrEQ9Q(1;MrQr)*xH7ffkiCsAI;z-v%y1ZdF_Wz$CXO#LPq%5-%Ylva@*~(aZ1b$c zI|k)9z>WXX#?)<*G6^Yy3QMH|=Mu_#saekA-q0CdTch z^d3P|d6%i6bhXNuzq7TpYeoh0p*f{eqq6=ugiNGC=9ZoL=|klZHKW}%N86dxc{b3r zcr78&B5~{$^Vi*jwI+tW@=oByhVdZ%)03Z$qy=3I2z!E5^LS!}pxv9D{-S`YUjv#8;5lmv(~omAJVEeE;UJRWnor@U8_~gr&``+-U9A(u$+Oly+J`x$G$X! zC0o=;(zo2`iVdrlF0Yfrk1VKpug`rN)mgc>#_)VES~cT=;p_chNaw?iwKS;wVD-Ul zkA30k>d7yNHQj6I1Fx-e3onbdF}*Kri?;sGJ_voL5d3(pO+9^+#KyQ+kNSSqwDw2$ zt`qIf8U5;Q2k(S?Fcha(-6tVke*FHtg!jl-$wqQ8@y)x*&?&%oe)Q?qN71w*D1~f0qRsMy*QLw5cm`dKjMjBM9`K$cwk^vNo>xdJfld&2S$eF;WD|@e_pkL3^dKQ@*xE2*LuUYPbMPtkYM8Pv;LpEiyeP~3{nGPfOk&2R0 z=&qC$c&YB(m2>eDE(jFLz2VbUQ>)+QTO@f7j*cEp`ciYVTzGPgp3f*dHCghEz}h24NovVeQi~pFD7`S-kX_QH{s`}C@TYYMZnBtO>-y~*pq01W(cIsIK3P1b`VpE-Y8teiBWbMi3 zA%*_EfR{Q?Q9Ex+14aZJQSn_K9(HhhYwL^O6bsM- zfCF8ndath*M5Yw|-0SKPEGq*E;)5-CT8CU_6dP!6-l3CTcLnIsv=bg^Zt7Q(6L%r- zPaj=>#Y=m}Vn7GQd3CO@uc&Dgr1xiOdXWqd&9fgSwn6zVX8>C_3WNO0E>Fr3s#`kw zR3!&%(u2fu2F_)wV4jA@CsRnj(G0}9r}=TZI}dO)ynSz+lJA-hjf{+x^S=xjJ8eyx zZunb}>rL$7!}&Pd-o8Gv%MY>z&PtD+(Ow;a_r2aHgnH+9x^fp*X9vrpoPk)uS1PR= z{P9RGLyQ7(p^(sKpXaq1U;1T~IW!IF$BD9>37baBB2;W7Yf+h2l93qJ>8GzGWicA0 zR64JKe<4E65)R4nFkT5R`2>O%1pb1FUjdAJj{MjI3PRjsoo*#cgMMnJqdt4R0=%C8 z>W(+ao(6<_vl)`})3+d^Gvp|LDCUY9U6sq4jg1Xe0K)0XS;_rP6cCKK5JX||2NWYt z^A5mPj~?Yi&-&4mH%^pK$Zz!^JI}!=QKUYX|2BL%iNL;yb2o|q_Jqm*Pvroj|8I&w zsxQYb6;xK@pxBc(h;Ai!-f_o9-7k)+^a%&_vBrGZ-I+d1Thk>b)5_B@0$k#7^R%6Pl zjp9c*nQ@~rV3gat!aW3TtE$KO3CBiF*4}IPFJ(htl{Uu8#O+r|0?gU_7gGDYeEmrO zvlSm_Ktp^>Tg;i8oe&+tD{GAO65yYNsFdSwIKp)G7ZSwUyj@zhzt3#MJNsW=E{<`9&*3(2$INzF?xdtpqqlVl4vSbq z(Bg)43^^1!CLtz}-3XYzM}$^s#bIotwnfsrO_uvGfR!@=(hU(z1sJRu!89SUIbr-L zwtz`3mHEF{kQ-oUIwNwAYB0Tq{pey6aO5K zUu-_EB<2d|GOvYh?gv>@>r^YePII=+?%W0IWij!Jf30%Nb|Va z{mc&ydj@%Pg)ZaZpR@T?FKXKM93cA{g&=6kWGo^u-=~ zk?(0%hjZ$93x(quylM6%O%1wPUf@uaZsBmcQdophBcc+Ha#YRDpsxU}-S})I%*|>P z(Kd7M!3k7ii%SlyYI=w@mfpQS(%QXqRX+NT&Dvv(PaAM7Aop;-8>tRhOOGPTV=xZm zRHq0`oJCdW;{k=5Ig(Zh@=^ZQUG!FhvEv)q9|Lo%Y$_>Zl(CuTcC>>Ne?UomFKWK3 zta|kERi725amAYur2`#ejD!Gs_1=;pC3ff2Y!zZRqGG5FIzV#m{+)kZwDBHLYv681 zK}&NteIa|IOXz`tFRpRVOXMmySUDqx<+tNl>J+NATDI&^qt?>{ijKQ{q!U>6JkAgYQ2I!BFb^a+p!e zF0i}lBC@dTm6f-a{3Ms$ewA@F`gF%c?ukTsN2FM}AmGFGbN=Bk7R@ zt!${$Kq5D+Lu=v&JyRdogIh;&R8KJH1GwD2!^Dsa?o?Xn>}dH2^3EyeMvX%aw1;cx z!dqUOuG%k-7TL*>&qB`$B(=vbw?pDKLM7;v###&PYinN>Hsp@732&_CBPs_H z7h=Z}!P9-8k5=vtV-RAhp;YY?8{^?}V;1mW!)fu7+o;S*E|Du|bI#*DXpassM63Cs zGPUsc3ZZv$bs1ydyuYNTePzDjZ~*Bu)9Sh!J7G4)%EUt`Eh&>bykJZ~gDd$ZGlY{3 z?~inmmVu3Ef8eQ9<@YzyC%TNPp6mVWOlp~K($`fE(sM@~VGDWX&KJN;g0OGzlZt!1~D^ohZ85ure*#-Ir zbEST6D-~4*#qOwV9=t%euSpa76PJJ(44Y{vCaUe`@u=?Z)HB_Ht7}HKNS03sw~@;6 zq^hz&47+Wu(d*Hpt46 z8b+B{0YtnqAZayEt-C?M964m=5i^!tx2w=Cm_!@J)D?%wxszxB?b6!N7>=NYkU2zz zWNlyrS6sPN6LJx`N}$U)kwszJGgR=@5zS84W@~W2&*GQ5#h=-=%4^s(KgWC!*{KEp zyc8>tx=5~=H;u{+1K@#FLtve2R!~!Jn!J<~klg|%_8w+_S8(7wHW2HI;fw8L)rDaC z)!}VPmpS$f)E8RS&1UxVr1~T_HwKkYu8ZS{%g={$xrO!13VXjuP=a=iFtn;4F#_)P z8&^3|265Ps-M=b!CGm1_-C41g5)8R7@l!g!>SLD4mjTT5inN^ZCM#3Lz$1<)I+CN7 zH=CpK{2l_dq*qj}c9ktn(otR@6gQTE{3 zQz4lj&9mIJ{8z3}6Y2s+OdJO2)Gklwa6?+y!kMtHDI|o)v})FaD zmQg~K{0s#+Qh=KV*a1{G1bl$Q>io{_0kdKU0t#bhd}tm7#9GzETEn>v@6!=@?pU_A zw$|=7^0RuZ%T7K0&(pQTGZHx~V1AiDloq`TC@*UY(^ciqQv(nQQJ~M{uVbtND=tWZo zkZ>=-O4{5P_Uxvh*I^nRFxD&kpZ>`yIUmywAHliN;_}VE~L% z6T~&q0W@4x%%a^Nvk4bb!Y@CaLw)Of*uIF060{Srq$ZpX zb(wu6wq)R}8X`Ued@EHL@&z>*V+la<8(=xfhVXOGFjqo(Sn2Lp>6DzT)9^7yt7&iANbO4gE++@i{lj!2Q1r+Ws#V;9r>kwj3sZ9{UXJ5Edm- z<~gTh(^-rbB2*#gm;wvUD$#l+p4)UbI~(XP%vaNe4N9!d+$Nqha>b<14i8O9$yi)XqS zTcj0D_ib$WJ}WtO7bX$$8sq zqa0Iqzm@+1>X)#Y+uNV6_HK~~SGIqQN!hBHl~rU}=JA?U6_?^N_8qb^uYIMY^qY$A z{e%AN)yLHJ=xHZMjpIcFyu4p%wbr0#FM7DIS968mNCI78+Acd5zs*D;xZN4^VOs?9 zaKPRg=fW5XXzSFCEM;onxXaiko4T0SzOnnV#XIV^L3;7}wsbOr3VM2ZYJ(%eS5c(m z_x>Isvo$bS|F#cr6~QI3&<>sE+iP<&ZtmkD^s57Q&NGU=P>{3}Uhl=~(Z$XCaU`vL9T^Viqhq%~bTq7gCUv2k$IlM~P|tA%th6I%?J zuqbc)>$!81clGK%3)8~Zl;y7NeXNNL=56jMM5ZqqF<=9C$1YP)OmKsRVjou5-Z|wx ze5cIw5+~x_I^$`v@oMu9p(Oc71mfo zvy(SQReR{scxMe|{?H~~2A;(`AnMwa2pT?k=4c)6nT&YdQCcn!mtZjPUQR9=H0`zM zYR_$IRqqyicg9_SvA^Z!YZ$%~+{y5at`r_LP%($uFYNE&_vGo?>&@qgoQbBdju3f* zThJkPbeqcOXc`S}+-GoC83`12+=o&`udvqIH*Yf31?rfzrdHYl!u6?lmW{W^&WIuj zROeT_LUHX3&NREjXlF#fD=E>~01%oSTc>hx!qU-=wk-<ia#I`+%O-gbk! z6@iLnKL$)&+hH5zvJfyzazsCZsr+<(=i~Z`UbO+0`Pp?`9#a#7Yut2MlQKV8iNmw@E@uQWH^4n8fX^Q#9eU2Hhy> zIRwLrsiSz*k@!V zPo-G~2@TrjIq@C_^smNK8{BjlRTtqQl_V<>=+|(pvqoewz0}nf`g>+WRskSSWZ+7<})n!`J z*s+j{PAUm(lnuB~u9kfu5@_gCOss)b6kQyjYWNlU{+Bmt<4%xcmIc+XQUYde#8sYK zttl{AOL@vcX?vex@? zPtBsGcv4;;x|AphOS#8=9vQcQx8>6*e?eu=hmuYM+SmV83UCG~QU%5XG_!K^ay2OH z4=7P8euQ|Wd{o&zyqdRry6s2U$j{ZQ?IJL+Hyb0N|3# zadE4jW=B3WA!p}r+qCg7j`4}!kD$sBtSukvz8tGGEe7q9!yJ?~N}$cLb?k1oM<=a> zI%$EOxb^uS3>RU3B|8m#vO&Tx!F50}nMIDV61Mk^4xg`%2OR56&5OG>~wB z)$*|d5&__G*l<4-_x5k|=rHXGPT1IT_UlH zRex?hc*OwG8B3aOedGj0UVrMt2T4_|LTjpF*BnK?A1X;Y(`s1hII>v#oPOouKdUwH z142Z|M?=aBTItZ7u_}LW+&f(CZNkxeV9+&}bW+hKTW0%zCd`Ejr%a?e7v;5KU&gMZN95#9rOp^&H&eE{OZW0Q*=drUVn|c4z6n|4W zG@^$r>uiCc#kt&_4H(4NjK;>sj(9Q0akSgl2QaWB>#l6QMRW;$oPQgO!N^$@jlbR>YcMrlei=A!S%=@>A zLIdM9n|%2%kpfUp0AI<`p`Sr*?stLxXR^r+aQ!0V^sU6p0w{l1E5h09W#E<52Z}l4 zO~40dZ$<9_djlX>2rv!6n*bU`#4y>Z{)2S&GRb9N9ifems@l*z zl%y-k8BA4Ev9IX-;?{^;NI&QWIqom(E1%e?;4L4#&CG{;Yezv0F!_hZ`>iExuq6F; znEB@ct+J_Y!DGiYTaP=D7-TUV5+f4J^yL9ZL1;S`T2?TsF<04hPIH5v@nkgNZ z&O1Ej*%owJ%0l{RxA=eoH*Op?cNa*U!hXx|zXy&_7r{J?tJjOm-o6FXG!^lo<7(Hp z(4i1C=H%nEr1~#OGPZDEnr~*V z!&Kn*70onvYQn%g;tb2oM2_)@0+-(5sDu z_R~bnIY*qV4R_JMv~0HG=N|S2#hG^@++Ux@u9UIGBE~jmOYxd}RsfA*`0Wk-=2s3* z*7yetmxpf$6eW%p!+LlyC)Kb`wIIKeC@oVyen^zX;<7x(S(SG1k&c!ro4|~uQ}Dwz z-gso`?PG$)g4DMrmeGSYsA1{8p$Nag`s4W-)9gwId|jjA{h7?}%b9H(%Qc&U#)jVp z2WFx^o!v^+QZI)ywJUQBIxV?*Jv@+ZJs)jnx2KY!$&UY)BxaXm?F38l_f4&bmyhbK zw`Yv+Lmsw2$kuVGFm-zMVr~z_#@lr;x&TC^4qB_`=drxJxcCnU`yC0n_VN4UsD&01 zPi(|WrP{B`zSD#hYrPJMOI)=L1=td9Lt8Zq>rx2()V^Fou0#HFqA|LtZe=e`P3LEO z-hA+!2%vr@oLdCsyWdli_*aBIQogN_vQ0_Z0X*Jh&ln={UDyRw4kQNA%?DuriWOhG zD(rYGWFZ9#StpDlKO;+Yq6pL*Z_wtsGg~?0fvr{p%bFI|8u7(C4s(MjgNF+ha*wqr zfeS(Y?+bAohBq78Bnj7ibFg03rTcs%7piKdGxLsjq&zZmz{v5afJwl4V>X6bCO#%N za`ZC@e3gIKTAe`v#ozZgIfL;BxqO z=gqLt)`Cbc0sep7&Fa+%ed^lXjU5`iH}yC4o2e&(F5tYenmsNa70pW<3u+9}sU@|q zE1lPUQ!&Ci76`ca(J0lCK5qwM(IZaE+GH0r}K zIZGxz`Xu+=Op5gmTY@E%&t@jnYs1y;R7quciXxHUc%)>9wu8ckXEzm^`)4`zU!p;B z$x0c5Ot9V z+0d8XBmajYV!9kV;QBIYKmZsoBe;fbFM=yy751Dw)B>~6?};o6W=ey4(}R5IgU00d8G@ zwVKoubxGRgSPf*>e`KpKo@NsFfJpv|DFAEjF3tP_x&?@*hG$;QpI@L6U$Y@^-=var zOKQu0D__;cLy3=3nkn?q8V>&$6svmPNoC zfft5Av0bneBS6}HYpGWDU)E9T^9nN=n~P+xOI<7dW#Kp11YCRW0KhAfL*ET@neNY7 z&dt@T|L)~$0|2pqWjc;B8wEX^E<63UkF3$SXqBc|OY^g6N7gq4Is50tFQwdw|0t&I zO}@B$zb59+Ow9Al{(c{u=Rsj-o(!O33!Q~Is+@aZI~5zKOneDz^a*x(?|eSPc#&|-x~&wjR_soq@tFY>l!YKCpf?$rHr(ww?Ef9X=Atl{m= zo7X{!RloxuLcNdDG+t=yo%v(4p{W{g@CJzme2}{=4B77iQ{pq07@$Tij{#fu`k&P*c74c$g9Ct=^hKo*J0}g0Ck7I<-A85T;ofTK-McdIvVgo;yO6dUK5_X$0xhBy5fTBBzplae=-uk z`Ait9$?@c9pVzSL2yg2h7#wVEWtFbWsg{T~?_kVeC)Nhst$&k%NjZ!wu-r&`Z_*cV zX7UD<*z&e<7#^U)=l>H564#)iO?fYOK`VUd6_5xsKdM2TmpY`^E+wCkK-jIuNN z+Co0?kO_{X-;bF%U|a|bR2Jb^pe@ut;%hc5%rlB?B>9vxTQrJnu2{iv>#A(zS9_k? zyS%%ihz1|hxv21aT0r0-)qXKu_>Cebd3?-?polHrE;==U-X-uSM(@!1%s#0sduBN| zO#)taGkteAz1m-FHw+7XY2mzNxOoVlsO&V@sClu(U<9qloj8qouHvG-FqIW131Zi_ zm$EM$4pKsNr17VRueXmYoGA&{KLL*#`8O+LakIF)#xPx^aD zi|jg$TwPr&Tcf~~O&K_aj7`Dke)>qronr#{q5!Z#0xVz3f`*usEPM$&cI_gN83dT* z%B5$H6tsg$8Xk@~&3>qQt5|b4A;?(U%W2i|8GXGYhGLf*~kqyqa3j4NCsoSuRL}@qV8%JM>dI>6eEI zP(8ZfIpulz*@0G?MaoLO!@DX*3<4zgS*z#{+Q8u zH4q%-!)#vU*Rrxlsqx!a4;eps^m)aLOEz|Kt7xl$)FyAc%sa!D^TxdzhoYv1SWn8V zeTbU?Fhu@sebb%vfY`DG-HH?(t4w}OS-wTg-2IgXwJ~%NnthVK*FV=-g^-N*HA<=T(|>Vbcy{KHYSwdULT5Jk#ZM3Er@)bhKhz zi9xuvyoL{B?3G6?0lBXKY?>hg+BSdG>_lbqqPtd zz z$1kA$WO`%)haGy7CLm_Ha$T|fBs@Gu8=(!4tp~~a+4B|@LS$U66(f?UYPF!tE~B1} zpg8Dw3i2u4cBOc0ah4NYYk7 zloZBXO0INFc}rudwAp4JL~SvD;dNf?H6?pxjby zpEKl>NxupsmjCxMteGM>1+Rm7i$C@UPmdpMVd!Q|Kv;AU__f-(kzsXu<7D9uL ztfKs%vGnmkxdEh|)BLgg*?C(S`8fl+kKX!Np`$Py2J+pAQ5;9_0DuNk5`UXM6Zlzs|)^VgTgR z9{>BvK0!bz%~*BZ4@UbvFKqwg<-mu~sHdhss)_FCt9j}WT?6WAe;J^+eMK-glsdww zznkHkGhtduB~6opzW>;kw|xb$O1_=s<)4e)weT=fc519h{d+(l^52lnbZnZ-g9e^7iNKM2GH5aqFkP94G9Vgs&hSp=jYdW{Bp6eVPo_FO(;Mz zG3(>UHM?rC_~m6~Bv|N4y<&x7+UW>7)Ls$I)yYa`#$&Ko!#5ZuV3}y3!MI07b+{(LfgF+X{)SvJj?1`2a^@ zu!=-}cFf*lUSVN&B0*e8UJ8A3g|N1iJou^uAVo0&qCwUB=HqMiSN^10Mi=Pw*ddfm%lb_A1GcAY9$b5W#La}M!xx090 z+hoGJ?ChALcbnPFb>#YH!|gSBrU|AxVHA^JZdHa5-F}MAOq9K|?#b|vg>n?PvByib zn0O~j8jfw`{p2bIvlvX za^Q#wBw9F%t3-b~t>fWX=HwX!e(Qktqrp$#rFkdzd^%MFhuX2zm_U7diV7k}Ys6Mw zR!Lb|lpjM{5x0Z;UbyYKz+0B@+Qc}=*3Ud9Y>g6$`hRe5S}-(wk5En9BcLO)@{W9# zQ_t4U1TN2Q*YZ5}Gw;hsoDYiMADn$oLlfZA1H>%Xm^_bL?kg~#0C~;95?he$6OW+G zMin$NqCW6hTwJs>N#L=kKR{gWJ--x5+4jK|kf4~Eu5-;bZaCYj$tBPBPmPTg!~O+w zh9g@vui^!vhu}e$<1GBaI7?)fHl-aDi)%*d)D)@_kU+VOA--Z>6y zTxr+9@EadIPU-wUJ(Ls5dFECXevex?K$iX~VZv>hR?n66fR#7Ggu(&uZc;jV9mW_&B;mg+0S|zwQ}te&?`RSg3m8@BUV`|T#qdC9_G=y#4V-!Yyk<<3-Hf- zUHKS=)}OpCK%>(xFr!c|2`FNG+#DKvNw8J5O!@g;QNsnY0nJV#GdbGf{CL-n?8~qcG9cCCMW(<2d7Pm%QZ25fdUQ{yIx_#eO2GOg6VVN77lS z3MkO7=E%B!n7B6M<+wQ^YA>OQm4&iBSs9_^Wz{%>(V~@1-OBZ2zic1>k+>75 zZXW-snmp#NdE-cIj_8R+W7$p9=jcOTez?>#UMCd2T)JjbBN=?;Eam)WZwVqeq6J_C z@%nrBU)7^1u4i+;Ld(+sxp;?+MHx*QW5z2tI48rTyv!z#Cr_{*Wsf(}s!?fFX_g?u zg?s{S4wriK!GyFM>c{H}tS9I_;4!XRa8{yvEXyHXVF&sE71-0-8a-J|=`>;!Z$n7T zILn4la6b-sognXp4mW#_y!hCsbK7L?)P!M#pcPx~yeVr;7_)iYi)1BH@Y5({ts=+u ziD(+-+i-=EO8S(JTNER?!&&BFy~GhY>sbcZ4*VaJj#v**zJ7u2&-mKjB?Rl z(_DQZBrdtOWFoZ2x@scy{XT_iM|Dm^X6AKi#bv1!YWw~3G)()VG6F`ajK&DEA)eKp zmjQYcx9z{2>)v^8Cc%*Pi0Po#t|x6Cn*x2sNi6B(HM_F3>DY(zEIeg<2C;^?c6M1i zN;l{+4l1IH5G%xHP+er}>%CX762(mOs(=WiaxgoG2F$2rjx9~=|B z4`+3&T7f18+!k(KUG=&x(VEq)5P4!V*mKT8PqDY6)*2Twag74!bn^GtpyiN@~1gRuy&=z8txjv)Mfh;gm)JmhkayF+$<6w;4Zq6K8bh zC>Y^*9^%8m;;wp0j|#_#Jjwp~lW?w3OUXdVsyTd#;e{5Hhv#5;=J)ov5C_`ld{3tb z&oAa2Iev9Vzz08`+_pAt{M0`+H$~2(Lp9y?ghO`%R;JN!vx^-+GdPF-bi>3Nb;+@; z61_(iBM4&?l8&-RIQPx#yFk1W;U@3I@=e9s%v?Ba*5u7$O<;JDHkt6DWuqGie^^?v z$B%AC%?v6L(Z@yLe!O}qI^P}j z*1U4W(~7!B(g(PnXW!+ff@W?Pj07meNs8-u+`#dYKmjEWoNhWbVr^nB;un(FKiMgy zJw#e_MT9SV`nkQ)2qqwWg;wQca{4nwDBNwciz@aRfey<d^5)+%UT7|HjpT-6AF z3tdnsht%L&lPV6KmeH^x66(MGI(*(B<+*$E?K9`KjC(WH)b&Z?t674gpgVx40lq7{ zD>qsW8Vw%!04l8YO06|J=qin_wVg=E-1Ag&rowsp!7L#%`OaI4U~`cZAP;WAB$ctPcwM_+VtFL$lNbNqa=URvp2<5^qN#%mpO0|ucdwG{J{PhK4;JTsE6`pT9d0wI$V}C^HG4X*B${@=Kfjad zPPqq(Qb*7z-#5NA==wb5eG8h1=z;UjwwB0M*yzq8xRN!k>Rr1FC*lJFeSG^T2!(!_Nq#yltI3ICC~xfArW`x@?_wbzvq6Z6nX z9!EeWd9*Lw)11BY{s2nh3G^&2oVPt&&vN+lOAL3LE6YjgZsy%Aa9WNr-J+3^w(9Nu z4uOKoIjiM|sn4_*>+85EaOt9xXGd}s+avDZhnS6kf+%^{K$@?}&ROYA(8f5_$bj8@ zZujBq@sF@___qlYi}awsq?_mGcWFdi*Ekqe)`1S4%i%`uL+DxiRw^p`s_SEH$u0b% zQS@9Gg}m!|nVF*{O~|qcf-*%NQduwnhh6gL?SJ=-6Cl-4j;_%)=nA=Sk^JQNT^YRU zD{sx}j7L44`@x0kN7FjS*HaabJ$v<-M2lb?nryd&5euYUnCx+}G*4*WV9$&mc$69L zWo#*w8yB*xL|dw9*5+TNE|w`tDi6Nbv8;W^x*bHb_tBVS--5k*RpZkX*xK;FI~ojY zLI3*L$jFq(*(T`sBN#(A4J#mc6gTYwt5%+Z)e6dUf^NOBvsm{}`2HNqUHG`=U?bfb zG1MuFV1G)Zs54$+CVA&FV`i|tO(fr_h(A()=+(^Wp|FpbS?~i$-rpFV_?WwNG zc}XtZr(JIR_6Jtf+B3To`8v~2xT{qZlX;IAOZBv-YoEw2gpKfWnHLt)GEN*@s<&3{ zFW#?@T>fH>k_j6m>OWdZocrA&jckKXxtxGZd;{`)dDWxW5OAtgFI;Hms@ugx`Q@k0 zIrrUG?LS4=eQK2!&TD73#>DGHaGgCvqt9dQh3Mf@s+zn!w^$0*u+5q2rC6cmQIV$ZRY++YPpYkj>t ze>MRDHh`b=l0#AyNr+@{A}f_v&K>swKftD`=@~$Trp@&C0ln^5+Ev zjG?+u{b*@V{ZI&!Z+bM5onQGzHpbE;bT1EPA0Vaj6sno}c_l>}Gr*L4Z#Khtk4pu+ zL$k#4M5!|Sm1b&kc@yVyP!;La?0f8OTiM!5>+r#ul=ONlO#An}ANsNPmT_otC>Kyj zz?WZ7(LtOGP!m_96ZnF{!laTpudVQ)+^sm7@fE2Lzv}ybz68b3&SfhMld2a@?~kUE zLm$sR$v3^MhZ%e_T;XqDDEQdr9#EuKK4IAMb=M!B{Hw{zRnddZzR~$U?$0+(^?hsS z70X^<>VQ%w8xXni!z4a#U%0pg;~d=X+Sgxx;4mHfC~aj*w=ZmpNfi9Rq`R==`sc1i zy+MBOWsGX^G5(M*&vkz>u243Wb}u?AomaKaFwLnGJEEP_ujuKiqA-gNdS@?=F=hJ= zU9&)Cl(u@Q_?~+4NJe0jbk}$6{m6@766yBG6e)*KxLI+!xf8ZHqRoM#rmcmzZf& zlj453L>Ag-D`%W7Eq>F)!R@Ln4^H#sAZ+@l0OEP`2gJLdeKvClukKPw82S(70CYFD>u~nNML!88Lx@ zC6M=Li9`PLwJDorkIGQ(vC|Dc!9u@`m47ZogJD@IS*F0Azg_L9Cas}i(J^CT5kZF< zG`qyT%g+yQVAZics>@d`QrWubg)M)qeny%%eX#I;uRGj=S%s(J@?cKH^#fTvhCSm{ zV+jlc$k)>P7QseGM|DNDDWH!6y0$1OGi7kO!@pO2$aQ;g@ddN!Jz+JUb58XI_5Pp;r3E^`B+TeYxYk@ z-6U;rb!Lq3I}b&g-^N4P#;L6zQWAm*x5!fo(r~K)o zQ_bz@vB2oDlaLqwoYJy}OIf1ps+WeRUj}>jSWG2nCW=qqL6j2hA1;Bm>RI$~sEC?Z zwHTEf)CYpCvM?HxE>q3?X9M=J9KUD;yge!+#|9V-Vtt zvqt8(W8WK40&`*`xW(-d;nctH>d>^jrd(v-Yo89~pQFHMdFFtvhjR5sY!{S}*14rz zbf;VtZztIYcG$PXr`8j-ix8`JVD`yvzvgz*N5bg#VZz7NOY824^P0$Zw+P~?bU2j= z*$Bj!;-5%1QLDqbI1af0IyAqv0Wnhp>mwO>9Dk>({{6W7Yetkk1@4g^ZWPBEM8k<2#$jkD3 zdeYyV2|ZT&{SY6&VcQ=o)C5dINJIqC`h8{%Nq9vaBiYLN1{V(VYP@HUjVyk>3?6_Q>t_^VjJC3p{kqkHACf zlnz_E;Em<|W%II;(m}$m*PQd{@DTk-j4~C>{unjk0E{-c0~)v(lY4MrAjU{~xfeoP zx5?>xh&Ui$+cJH2od}tWxqf0)PGC+t@r^*>W)Z~IA6Gdg9(`@m&qRfYNFvf4pV=;9 zNhlOsMy->DwAvhQsQWa%i;YFk5c7b%-!((AE7u+Q&x`ig!M_87yQGAbfq?-6iHUN0 z3(A$~DRJ$?@+aoC5rRsZ2X#BwZ{sTC<$SvXWhV>cvYJ-PAN}z%RL5t|M^$wE4TM2K zw0?rnBteNDO%+ut6Fpyke)RgHnNVJx;M-ka;)tPgdG0E{Q1Pbz3Cv|DlzTNq{^i6K zyT>14_r@s6q3aOMP@@nn`k>fTdltN4>`RTUeVA+Ljxgb)xWGr}%+G^}NvUFk3M;kE zL1~YbRYXY0-Ea5$X1jL=7rWzTkCONBok9wYPPeC4>%ZS*p3BgU)emw^MEiLYsfL6a zE0%KYN&D!7)Xt&)y@?hll77M$h^@}AsyzimZ)piZwy5ZG95g4kI3yZVLoUj&1@C!+PFp_AfVB9TEIex+ZA)? zQG-EA@-yo>ppiQKNj4%l_@Q4maaDOaVWO~@SQ{7wv%1Hs{*(eeYVZ6BzWxqp!n)H! zG3mKfIDAkP`awgHxMVAkQf!WH7Bm+k zuQOvO{S~^oD~x#dl^iO9=X?FWuwOCi2h!kTuYl?{LV$^Y(3GQt0}&AsJpon#lRN_N zE6pFOm)-VBw-V?35bV`S^;=?QG{{Ep|} zZvc0tFQV`Bk0*}2DO=_8n0AfK`xsd3W}KsHG0QO0#$~!k&DXA+ia#*3kG+t_w=pXH zFQ5UGvNl&%;yEqIw9UL8T>SAOLN#V4CSFh*F)?(+OMJ?=S5DOy_wQ@hM&A5m?h|D` zu_wF&>fHraQ6Ud7(0+e2`u&6beV`o;47YU(!7Tojafbl3`T*sIJ+|J>e|c(c!B4}B z%ggK+CL&-NKm0N6K(aS6DT$q(T~R^di%jqi#+7eLbOj6edli3-L;he-NSN60q^s*f zM^8tG#Io$a*0~%ft?m)`6K*D^@PC$Rb{ul!)`Qw1h~lTQ967qz6Q7S9Miz@SSVcrH zm0a}^;jtNE5zF7**N2*V&qSqk)RULH6-bumLxnz-cNEJ1=!Q7wr!puw^SZ9zdEhx& z5Ek-%BjPDF^b zBH#s6Y~81GJLdofzAQaE_IcfUkWbG=2=3t6-gWKI`+L%d?sAoK$TEHdu1c? z;ou9Mh-aU#{ex(r^=?%)HLifrcY00heT*AWU16~A^$xqH_3!uA`g&sV*zEZTrAm66chMu%^b*~ho=cH^}T_5i@NWqGH|K3^+Gx1KjzXFauC(-_if+ruNX~*L5DXqOKl=1G$1_E zutA5)YV_SMPu|zXvFH4xWIHkB<0UY@DGnBGmN~Q+2w!3o zIwProCJg2~gg;Gkziyv!xAD3jn!CE5m6;Cf>+2^sY{(~RWLwrhbzEyF(QG?i*%`Imaech#{+ZihdYx=`l>q6f0+k3;0-F$BW?_qNSzXt5 z#1Uy2+zxwU6u<*8PLYj`jju24LlSx$rWoI)y);Ytaw+^Rdc~Sm6aqlvbFQ(zNot=? zEM@{WQ{m{I{wnVFJ=+b9VkGha+idGKRK5@znI5`a4i73DeQl8-nk#=;Zv6H`%R+|K zAP6f-qg&J1J2#{Kg!eiVb+j}OxB72Z2kIGD%D6h-16CS2J013d9+3e7+w>?vs_~|`( znAhDch*)CQ>KH$CBdclamqwjOdzAX2nmTLZ?!{pF#f8T)SF*O1o! zb&fs0hN8`^+o)a}xwDdmtD)$@9Mq~vvU23t{N+i*#q;mX;TQc4<8ZP!Ll7N|UxVem zoL@Pg1<@QcItI3ZdnIVTUt`4CqVo#?w9vY1;ZP*}rg=tIW0ZG#eBg87cq#Lt)T|XN z6P$NStzRofy;GwNY69CA1e(ky=DcT>6jqygjN=!bA0Iy1`+4)DM$OOFs;DRnMSHG$ zlb3CpXNC=Bse1PU#h<^*CewVTk?Zni)Ihavtla)}o>I1ZySeA|qhf>*u1q;d90u%vEHT+xqmwg9q#vJdwLXXpS@oKMiwQ z_(#!;8@9ey?mT_ajyVTEn75*-O*vynVAM%(R71@gMA+| z;8cOKSlG}~rMi+gttWGi(-+R^!8}K%a`&goY;3>J)!kHKohkP=rH`tZtJ4AM{3C~% zNx@Ueq6b2Vus)`1z8Gh;Z9Jw=2(w%6~DM~-NNuYb}pSob;Jgxu6^9emaG=p2qDcY?!BL(=5}Gk zKSXlyh<_^9PHCXw zAboc8S&%u2okqyir`Kr47h^UdZVYyBtczg4suv*`VkFy!A4rUQU%}260Hf{*9_PX^ zF8_t7z^KrjOh#sAStjEzl0^2{LT=Xquncg8!b&1jQ{f6|zgDw5@UH)6Q+}_=e{d+d zxt+Q!Uhg^$v3#EW4J*GO${j3RlPWC6i}e7g{Rf_71CSwcZTBSt{M0*l?^cC}?pz0W zw)i)+9-ElB?GEY*9zA^YDCM~*63Ff-AUMc+fj|@(rFjX|u&gOaXVF0PRZ~+_TFMSM zv|UHYor>SfHw9q#1A$q2L|_h>^V(s~i5w0K7z{0OF(;INuyTH#+GOE67_q9aq!j&U znJw+Yn9PVm(54gEUUvc7>I?G5j;hhaKIP`p{f__t6Z?i%FFiH$1vMEzK$puR4_hlMLmK*gXVBJs6%zTTAucviLA zs0^Qwu+{0-^Z)ogX)s;W5F$ozofqG~f7cQhaq9KFSoW+Qgxt`(akVk%oMY3|+Db|U zckj-h(_KXu^PK~CX=FAg%JV^9u?R8Tz-KX02AWxHk;@12S#c7N;dX`T=_nKZU##cZ z*x9T8jt(@Oot-r`CnG%eA0Z=+i}@u5A28h&l!7&DR}~fYn{&4CmKygXr#fA2-;n{L zaTK(uq}>OTdguPoI)HoK0+bKfza=qBpo@ID5GDIne|MoXs?utv&)(3N_(vGuU&lUF zEVETVC_LOhCE(9?Xirmv@yBi_i*bNWLjLvpo2ti*MW~}*W-Gv>9`b=h=}la1wAElx zHOw3*;mvA$X|;eq;&kD&%c4!2zD|ZaKiK3{8%#V-?(D0HjH4;n$W+VQF`>hDEopEtOLz;rtcB^f5b4D6JG zfLu=9rnad#{jG32Nog4AixeE$yU_U2GXrv(iczsCynMr3{+NHU9;1t|-Noj;MMFsDo!K22lR+kI-oDIa#J;@1 zURt8OJpnE+PLP6Pimf|aq+K^LJj`y?L(*oP^ku00T`S-+0L`&1=%WF(o%Jq-?>_#` z;8YG#G`cbFkx95%sIC~Ij6*J5(KAYg_xA|33G0%~YG z&QTm$*%c2TPQu#UJ6N99hWUVm*=rZPdYf`pLnFC)JAw7bvO55R{j$izz}i>cmhVJx zUQqWYoVfAq&qhwKX{^8^T%mWdDGjjR=X|bbwHhNvpo~ikt*IqOF@1 zYF>@m5)9eY0SEFw@)7@7Vij#|_^6q2Z=xSyJHC4UCR~tRb09+%cK^8m14H*eu2Mx_ zesFqPz;@|X+j`62Hw=6LsK@M%V~zkdqoAnyA1BbuueCO@i;c!inSRlw>29*kZdw)r zaZROXs^U0C@WXK3znpKihq=b`Yb&%L8N*)T`kVWsp)N9 zw`f`GH}=cIF2>R>PJ#mN=JUvIvIiL%>zwq=t=jF|LR0K5tcNi}+{sqQTI&U|@IKmNtMJmOvAeV3(B_E};$T;qK9WH>2YEiR|8 zrhPif8xF%93m15L%I$vfnmkn@f&!|bUm1u3Q&-mMk;4;%mJ%8lu5*1`Pn=YqH&t0k z3TWGU%0#*;7qDR$HjCLKig^#2z1RVW@K54x0L{+cUOM?%Bo|1)PT!ntG9ib5S!XaW z3#xcAc!+A?I1oMw1m$oF3m7lVP#|1Rwkak_%H8*555Jj-^eQn<`x|QTNJ01XM_GD< zhxX0Ibd2+1SWCpO{!o8_tQsH3fLfcG(f|7-->hDmTS(zAqmstt-w55N%-=KXVCQN-UW6y2?>=Q@*h2uY)$FdQQGf0ybv9ubv3T}Vc}1<6s-rhK>O z#um-_hOu9>;RyFX5D(z3PXo)!IL0EJa9P4#^q$Ljrgu{w-we@!z_O`N-oKl{jroy88UY@M){;kxFZCvaajGfI(~WOh7p#0Jyr4xFaJ@)6B- z3%&AOuBs%@0$)>f5xGH2WjvgO7U|13U#&Ev-Q6!y?>nke>zH%F!2gw_`2+C0d|o}) zDm_O$C7SN9qyoG*NP5#?eO4396YFmy>i(RG;Ixei>d-5tjW>i0jXvOhMj7urX&!$~qPI7iAVZ{clB|-IubhhuoTH6_t$( z`8)~U=>pL}K z7p{vcu7f^fEnp)ybCdlg7R2EBR*lj2ZPb?1pJV7chz<$1yxNh6@!4IoWBOF}+lgn& z^ZF`1;%E1gldM-|j^s<@td_}-o$KkxWdH1!Qr7qL4X2F=`I(fU5S zpAbNPIvi2<&9mvOWDm=DZX?BJr&n;DtcAsG{oxB7uB`HILAsD6pO>tdsPSGYO!4i2HW`W*tcSKb64r z)Liw*`(>eb6*g~Ou4n*S&(pKv74=d+HtrR90ud}c;Q^6%j6PnH4jv1Vpq_M+s=m$Uzg|B-Kb3%Jog-qUjG3yS*- zF)=Yi!>m78|IaBjHZ}&wQ!+a^BxL@di~LV_@^R3_#nn|j{9ceaNMrnkO+dE!@bGY9 zVd41L>EDLqgB`X@6fl_ZM~^V#uCg}&BP{`XV;@GQ1j+-y820r z7(?g_GRFc!VIKjN%t1p#!yhr}7!zqEUzqT`-vDTa+{CfTNh!dEIFa4>Yx%eIMj%eM{i*z( zygQWww)gQN!j_g>?e{%<>g*<*W<_>{bSd}qb#uXt5^C9&!wN|@`-Mz@@U^10{L*17?4I4uN`ZS8|mN?-+X2JFKE zc2{)yv|DY9V8yDICMG06Kszpu?BC&L9sF%h#hU9Z8UbbAgRfFYIk2g=Jq+-1D*yk; z{?|r|lZ6}M%A@OI9)DR2~2bX#1JbhzSg@}@rjA_ zQJoxQVPKSNPmizXETk57yXCWn)ps`07j0i#$@i-zN?FyjgM)(JIssyWQozj#Jm|Kz z>;;jbp;)Eg+~(V`tF&-B^(t6wJJ4_GSr-rZetisW_#!oFv<05*myr@#6G>bTAL9qn zwkNp{Uw;m-81pDan|nfMXu)MQ)Q!eK3&^+Vkb;$p3Qn~cL-#>dJ4M^l zria0*Pm)3_TkBj*Udb`V-~Q;w-RLG!)!~bWOaCDn(}TTH@~tu|+S@r=ef=;6XIV#t zg^y4|JZ+SCBw@-kbG2ZF!MwqDHj(JBn2Ajr_X)9W{ZB+}y~ReyN3yt%L{R->9mF7 zQsAf$M%p-gDs5PZ$jUl8+L=Ec(UVn0Y-+CuQ$`ZZd>a#+VOhnFw|kZ)oJK~$*<%#; z5jump`@(T6zGiZfjnn{@8nkwpnWb$%g)0_J$kvpQ-;%lW{VBGv4+&a4dd4VL>7P-pn8=WgsD1CI(X zDr#`rovS;V#;ofB)vf6bN!+&7J$v~@_1-wUp!v^_X%&qA_S!lCZp1fbI9Kr#o0!q- zqQOzO!k8rqQ(6_IRl_;Lj1Y6KxJ+1J#(kIuR%ZR?d|Q4zB}q@h%K!K z2ak=#o_psGvX+cKcf+mGi^ro5NN5Z zbw7AsR4F}lKaLF0(>Vs(ra1}-~5Nmb7Kz=4;6>LlTu?y;!^EP+^=mkBdJW=gX`%N zdbZEpj#&yY)F_G-bVoz)ao-6Rxm?wemq$7Kq+V*&B(Mr5cXLeiZ8xs@$E3cCf0nK* zSy{1po-jP{I89G*&r8ha^4C#Ic$h~TaqRV)1<;_|621nGi!K#F9X|dydvVETS6qLZ zfgW!M9N&4(1iJlna0N+uzdAbaq$$= zQ=C5l(qQ=N-%K-YG)Cf*(7Qz;x;s=kctc+4!D$(~z~$ud9>-81e^n5?&M#PWq;!2-icQ?oz6C|qAD0SPfg?OPw+JL)qF?6YV zK)F$!rlpIU#*9Z93_NNb_XcR3_2);a%^g0XeXscFHJiIsX@?3^{5_*P6omY2ur#e= z3^lwutzuR5v$qm>SaU*)A~Z5xXtU`Ua(J{jbi@RB!MFVU9!A+T1^X*w-ECg;N3bx& zQ`Kq7g~=tz!QqxrC56v?^wpJ{AHS)Tkl^`GoaY$|wwp z(INF2FRNCN|DiEBz?QCMC@Ny$LoMDVs=^VSUze$vnii};8@gFYty#5#9sm375~$P+ zWJ_3k0oj5S!hY|N{*#Uv=}4Qp{60!SbU%Yj>iQ4tr18J#T8Om16|DAJ$9QscTKo9A zTQBg9Hr!mgYQ)829sVy>%38p`IOu>YbT@01TV^b*%qp>FwcaIy1_%|=Xo6SDGrn`_ z#*_C%MPt@uMNNTTTkNPmH*MCqZzCiG5Q(II^p4`JjBH$eP13Yix|STF995RqKA|+v zz0=^2R`JCIIc@<+?`#F!o3%+J@ZdI0htnek_??;a%ANR?2T&h!>Rh}4n@H@a5)?&p zYV|?6+sACLggIX+Ag_cSMdOO?+31848W2Y&w2ZFn8$cB+_A6xQPk}kO)KwAsXE?s<`hDM$ zEJj8~Fl4h61mpevxI&NR$^wN3#^0r1#q!7)4ix!#k3#0mh{$I=&KT|PF65$vAXp*S z4m8<|fm-JhKp!7n)f_f{ppT(vWi0`Dl1T^htd$iL6I$vhIvSdrM&knm528B7!tZ?p zG1AmjffBukrCSs--ylqbbow&+^QZlO51ErRqZSSA%G4=Ik{`L{uGZ5DcOx) zL7@~`iO;MbKCR2h$S5tfUFu1|Bdl)j=;*lrYek$VWh>ymhs4X52^^*hii*RnA;eVZ z1nD0?qU(b+FzFCT1uGOGKu}Fizku}DoqE)Av0$A*LPa*71?4le+U^!7Zcf?Snpq$e zc%aUP%L`Z9i(2PBko8DYNxuVRML^Sn1E?L(VU^R)$!1W6H)0;&o%W6Rfw=>rC01$` z!R5aZ)$&SLg+=7e>v0@C{Ne||dJ_QSw6f^!gm))HST_)RedXe8JHT7eI!#WF!=)=+}ZX02YqQ#&q-z3Bs#fW*?g*J*AgQbj3h%<^~u>^c+@k3qv zD-6wG0_yX!w19?*&w(pgF+eIdNrr}m@Oqp-E5pmdGtmX04eI804v-EoaJdVMi=eKL zl%}1nL)b`&c|daG|Bf zW9aqx-Ax4{P6-TdDvQyRyx1|K4%}=nn&`TLh77k&si1zP54vMwChuuFbLv>x<3 zRE}NlInJW@Yi83*=~ngM(MF$KhJa%Ee+U z*d!BFWnv~#Sx8%!=y!k#BONOTUVIEkljGHj7Sl31)^8}K<9Sg2!px#EOKU(fy>N(} z<&RYcDvu(|^Tm4AW{m?hg8hPI#UfS7?HQ+Ss?40qo`NrLjkZ;7sjN9g+D4 z+i$(iMeixM_PtKEPF5zfqL*$OY%bfHK240B8fnMGTf2RZ##NNd?Zi!W&HaYCQ}O&; z;$r@mts4~7&gRLYB923yCVQZ4{m2@m%o6zYE_DU_?oLX1<98 z$mUeI&(B#lE_I{)W!~|PUgNy=v*re;6nL}A(ST>9; z!sAQn%YGJ=L%>Dz6YoDQmS@!Zf=7Rd3;eBDUvC)@WxIV3V}a6&w19$u5~8HE3W(B5NemzjL&MOaG*S`* zf`EuL3=%^OB}hm&LpMWr_j`@#^LWnxciu02Vdk#6uYK*k_F8L8ya&gA^1724OZO45 zss7ajAROnThP*_&j|Awn0@qOXVhbI=e^56s`ly-cW&*;xJgXfTuM)jx)d-eNaM=21G z*u0hK9Xk6M4Y3lB(jHI|e186@^fSqAJ|92j3-LFdGS4|;FVTMI|IFcKK{$ASE1`q& z@$PP@$I*ooM(~;43B^6aV}J6Nx8RXE%g1oFfOl)ZV@)TZ11eUGM|ty#TIYL$YapqG zuxurK)l?w6b2SoghoCQwrysM)E0Hf<_*n%K=Vp2W3e289=!H!ry;XVc$31UwM0dil z2Al8B-fq}VnU4|6>!b?I-|AmiM354&pBuc8ZEXC#;|WMCguV$<8h=-%GQBs*qtj1Q zqxF`1Tq2O{F*%X>wH=Rdf^SuD%}M*%vb9wSVw{Kl!IBat19!%)(;oqTSOtRqgI9zZ z*T2+|;SABJbxyxIr`Dwp35c#CyWf_)-f*b_#|EgerG9Wf*7+HTI&1U zEO$W8B1Zd6g6jU!8dfw|V>Y*mnl8mLmou@(1}T9*W-*u;G9wx}W~yiL{r$OckR}h@ zyk+uT{|CtbLE1zRv-SP!xgThv__$?jhCbRDkbATxp&Q@%!p=dxvyJm#KPlD8sYWsg zf3pSUI6XnklGC)V6}<|oE0;fvuIb;Y0mB}}s{6k?SL2n+QQ3KCL)R`09`tX-@l6=r zC=o>kV80Q80uXzx;E{zJTlU34N>l)GEe@vFgvk$p9P)Zi508z{+W|AC&=f8Td{B%{ z3G`rf7y&OG#@n~wzyYHMXy1%~>DmD1{ZXn~4zdRzvDFOVYvf7w*XY`>h|r~#bSw)5A`a9ssV?|?%HCF(3unX7$7{y&l7f&lr^Seb= zWeb-4%7^*m9e&N-b98(wYCDe0nN9444>Ap?ar^v7Gl5iKTS!!`V8gMqtvI^NFlAs< z$$Gh|Oi`_B^KGot!HeeQG+0gS!kXs=muREG*3)0I0&C$B3~AwAk(gRKc~bV=XUy~Z z4%qujO8#||o}TC75!f_9xd*ZzWLa67TznHO{hR{o{ViE5yVgt<$?;Q{K6T-i%)62+ z?#3>Pwdv3q#ySn@oUsRs+&NDT)bqOzBzHFAhoV!Rl0dI}HXYiR>|DX*8_=xnq2DeJ zxBxDQ@in2rLRANLr^&@SI4ox}XR>vyzukVPUhEPM4XicOjS;psU$d-m;hc6_(mC*3 z0^S=&;tYha)r$amu?5czBXcAXf|O;ce7-B#Kp4|84mE@nB=8lMDor)8CYB+OHlq#I ztA!LNc`c)~{#0=2O4 z_2};I{`G3-LIJxJXtD7jdnr(_m=Jj4x>Q|Pd_y03NeuG^68ewah18W%x;|z;e4OUrn%B^fe@Z=lf>p5=VaExffOHR;p=sDb(+@LtQqKaz$6% z=f6jlUU49o5~Y+9Z7h>3Y(1z9j7S2iP6|&<6As|`(6DE>9qm8^?0hlOUU%fx;zW8B z!oE*8V`#lLpHo>Yej@4ELoJM5FE)(?nY=h#{-Jzkc7ed-rTMA8bXa)#vK{BvJ^NJU z-O=z5?9k8(8@_tT*m;5GQAdkb)c%m@0C%40A_+7k6mobFH4_mzxLzSUHSDtTyl zX)jL%y2ICqFFtX9XN<4yew11oRhfNvmNcFcrwfD&W(p%S%|wT)<~-zuqS`zVg3I)Q zDtAfgo`sLzdoUTX2}rs2|>_FUPm>JsI>iu|tve6E&CeUVTbTY=?DyW^jezE%oyp(iUVd zIlc&MxYf7=BA*zQ)_iO_A~Tz-Y>qZ%q=k(>Y&%9p;`zE^C#zqd0NtzA9#c}54?z#E z3?0Jx@ij?0;p#Bm?`vk@yZ+RG%y~%~!nM0AePNc8OauG*ctXN}QQo76UoeVW!vcN< zd7jVPF$uYXdKV9}KMfD-e_&BSlC2F+?Kjmq4%a6ys*g_+n8{Yfn*`#-ZloF?FO)W} zJ#S6e7HxrLfR`H{1-e~cDPuiF11FJ2a-X=2){{fYzBUfmb;l3w9_)xl_u?n4D`hf? z>}_pUX=*)eIdQ>;4KTaxxT!)9Q!L<)yGFzkXcDse-KEEFymenVLv_yY6|lJsFNDe3d&V zCo4O1TkFvpCs3Yt?@$H1kvV-8CuQFiHO?LAdVRkpP~1mIon_s{z0a{&7X2*Xp&ZrK zwLIv_;$XD4+lQE_kJ(aE(m}i0D9;xzLfWMip+jeVdqPd2#Xf!4oF!Go?Da?(j`nHx z%8Flxt*Wr9zFiJdbsjAKBBQ?Gen{$%R_hn(pYrL7gu7sQ{i-PMLd zMxVMjx8vr2;9cgN?;&-O=B=<)-`;f~Q>^bRv8;<=L{1`D1L%;n$kC}USKDNjMG+{Xm1;&D(JwHaHAbid8d{x}O;Zd8T(}W4mg!&Y? z$&Qest5w^{al7FVYE*R0ZE{S*!^w5Z4DI-hJvwN(>a^7Cu(-D@LDR;4AaA*@sjNB2 z{a|mA$U&e-3)aZcUQ{7Jkro!wCl-#F>=gMBFGIxNEUX3WS-gGIp`&le`XiM=iXvp8!ifO>B?&PU=gu`T& z5X)hty@6x<$F@E_jn4z;C&#qaDpKr66GgOy-|{D*e;Cwx$d2u;kPR7py`Y#OI`qv( zae0!6DCdhk!R)BxH2Ey~>H2bFQ0Q`+Y5Ql95|bHpn=%lXsqhciczI5UARVlWZPbDx zQyxP=Q$Z0bA#~aRzmmG+Eoz^Wm)EItf6sw)3;q$P!-B}s>rWoo2Vd##eYZ-$Z@a0C zuZJ=t_qK4aQW9RHV1dNd-$}f`OJ1d3rSI7V6N2MtZMNQow=jFDAk+) zQL(__p*_1dhFlBSB*}2fU=^qE=8@8n`=;`Hs=f!{vWB-@$3q{{Kz^`_h24_91ILGj zVuX^)QcN$XC`LFGIX#xnTc%MRjeZaA^CV*HxT(sBKe|Jni!V__RKAKaPzc^`R9IY{ z^a(Kp`=>X5l4GqP%3zvi&)?y;W;L~w0x;$P2Ah=R{Kv&`*(Gd zzo;6RoJn1W?#Ya3I`rpzY>=}6wPX!Y1z`3VKz+gYznV+CPS|?y)-90GP`(4v`4@aP z{sN^#&OsK)ET7QwXAcldKf*c4E-wdDs7p0NKpw&WA6YO85lGHaBM3C;vjUMH09^#o z)`y2Rzd)}A0Imn6`d^0z>iBtg@^P7A*?(|A!2TKiNICYfYNV;bc;C}(7I~t?MHyS2P}S&Ndqv5ST(Tl zEgWQBe?P7BD4yg_$C)P5csVf6qlDPV7z*{G8T$76$IF*5)4fdf%c+o5oOXZxs3r*Y zJyi^&jeG90A`0sZFn_yJ7@#@l(%}081yD3+XXo!JUy_qAl-Rwx@lZ`ym%j8hjx|q) z(z~hN_esAvO=mol;3q$kPknPirtj$nx^=YPS4OW6gjBrHE`u$qfMy!t4?T)KzkZ)y zU&rv9_(y^a0viPa2mxi+pWgwz8{jqx4^>2lvsn8ttDt`FD*)dI(2N5lb5J^nOG*mT z=|IiRg#ot(*orQ>pU>B9>c7o<=Y^Q6m^W}5sUiGxDc_JxUm$e2T~CD?OTX#Z{0py+ ze`#z~*x-1~ZESP-KVI(Y6Plf+ml=^@75V+8P&hAQbSHkm4BLcZV_LHxcn~LuxH$vd zb*dqhNJ(e`E6#YW)>uJ;-4RgWANm1i3E=KJ+JO7rayej_Ye7L?-yyS%@|Pf5vpi=05}tdq?ewqJYZqz@X{6&y$Ix3`q!{ zy^!cH)La3{%fcKD==ytVE)zG}Js`>0bh~N?Kfn=ng%0jA^{wyOXPon6jShxy$Fe^y zkhu3jT>4mm;&Pp@2fM=oLij0|<6ru0qO~IXGCE!9xHs zbK=HonO+K0au@=j+*}{o`{KB_H3|=g)(l^cPgZ|2c$-yW5A+?jLZ&x~d4h%x9k+@w zvIjr~IvV3H4+7dpwUyyN4i#61BHW$^`a}+#yW{Fw7__bSde#wmTe!Ko0ZR9^R43%$ zQUy0LF>_g2b^DdI7B<>?j2VM%`NBrNNV0YH7YVjWa5XV9<}|X$WF)2XB#6y$aeq=y zldt4YzF%%hFF=WFF^f0T_xEXOdsMC1CvF~2P=>yuTCz{p<1BZ2ADUHk=2L;}1cpGo zF$Gye(}V%CKbjr?rpW+ETj#-F!ii5K@013lHtKiH#~yBtJ(ZBm6Zz@wjqjDD$`+u=)XLnxz3%uXIB(fWh;m{5 z>MD5bp1{mv+)9vz+8IS!bb)%^+W~`Z5<5n5fKe7c36P=$;O`hnx^64O0M`boMxGlWQ z%*@;~lUB_$S}1X^1`4m+nM?I>AGU;39ibp{sT&p%&BbRca6cK^m-V<64Rlr=2=^)A zEpZ|?A1h>UAtQs|$zus8A{{6erX6(%&SH-yN0KRlfQ*kV2IAB&p5y2omFLat7@sd1 zkVd*WbklS{2S=C!PNJaV0}MHmz&ZxWkZEKsX4^&otFudozLxlD$L@Mo!2_?YZ+YqI zcm8r`0RCa5x4IErMr{CsXpCUCg-l|qp-qn^%o)!%>6&-%=hf#L;MO2Z0ER$6>}ko# z8~PPMtUvRB_gu=!@xm^=C`LleR)&#}of%aCk1dUpk+#7JRu6%vb9T@O{3#pA+gR)5e=q4r8Vao>rX&whY_LJUU z@H6t}H#+eGvK)@hJ}zrzTELb!vI=(I6&=}kv-!5mne8uUp*ClO!W36!KHqMMuPupV zCXqqw^LhAjygC)ym_n4DvR5l!<15hZ;nCPQwU4M}ywwdh<;$lvM{e<(@5T0z|C*6p z&Vi2%@AtIj`*192iCAGRbe%>DL3pM%MP*(9iIv!P^QLHyM&-tL4}6CcV!P$|;m52t z--ByQ;+aXRdwIH54{3*mCzGp0V$AtiQ+?D;wc-ofw?K~?{XC~(nozU2(n-uM-4@rw zjzHcQ*AHY)@HY4%U5R#*j^jw_h33pLx7~=Q zVi5i(O*2@ugix_ zJvJTd))LF<0&OiIFTV(lyIfv}&I=_%q?wJhb3wkkV zsC=ATrKQ@DcdF{qcZ4I0V&iiVsVH!suhKuA>E4vD{B}5oE!nlcq;I@;(-i`>wB-9J z19*fTDt}S0uMS2ep-G0M++j`|EJvtm;oJNvORz*z(~GF;`>9@n6o zc>JR(_XZtf{#>ZV;Qqq?)%Fv^&UYDCyrS>yIdb-tIdSUnG%F&n60Mx9CAr`iBZ_l) znKF-DCnPB?fENV^jnL98T7M>IA==}_gsTLV9IxSlEM}XXkgcLr++7sq{DhC_?LYkD zM|9L4#GN(qVMq8N;#BKnV$OIx9BgnkRe=u;Zk;6Z;2%t=_Wh1Zk{%?iJVetjmy13 zB77RCEg!WZi?UjZwNl9Hb*`Z8d z*Anu9Q^w)}G4z`F8(U~@*DpE=d=@;JpE@;gnkh)R z#Jtmb=rz6Ta$NxH#is{b~cQxt7zn@)Od9cbt0j-Zl5Oy3vPm=H9e@Qe8W#_TvK~ zgRe|{!^!M;nIv^@(S$X9n9|31wWH}8Bl6Tc611S%a1Lx?eV~=!-5n7T!4UBvhzzW` zGe@FoANYe2Ui{nBNMtroQTj~@bRF9#opMn%tOQdX?U#e!HBcq)m!Xm8@adHb!4|3zWilGsjT8Clujpdh=GR6ZnG&p-LtKWgTN5Act?3d{t{ z6cim#N90;)o|DYt(+QcOt?0u;lql&KBfiC?kzbTSiT8RqZkA>?dblo2UV0u$6^k&? z7TS%I4qZ|&3qK$=EGgg4s6gO(sBf$eWIop8LS#8q9HDk4*+8MczY;2YkPO`_0&q`k zT+Z%`=YYt{C6-`GrB$z&3?0-;&TFR+jZFV?VXfcqm%hi%U;N7xTJ{fn!lGl7PCY#T zDs}DH-IRELo0@6O>^2+*)psn!ZihuTXu9$?OuUF0t+ArFc^{c36~#_D;bH#Z>O%;L znuY4BU?I#QSp9ljYGDF?FJeJLkLgIa^$X)v@=~Q!7JB(q#C~pxT`zl^)>6PcO)gSl zt^Y?{0$oXcA3-*(HEDTY)IRt$&Nq79wU4LcyUMZohVv`4$kX~kry>3OYJLw`^t-(8 zsOVi8t_L$5=2uoKS1S)eZ5m&-`k($U0DznRtuxNL$8iA?S7nuyyMBb^RJtEy0d+~q zbToNWmBFV$gv;dgsv-4WMH_v%L>&uh!mN}|4wTRw;62KpGKnSq@o-~$U7neNM+odo zctn93d?Hi_Q0a%?0Mv}O%u(|OMC>7hz9dR01TuA+dNn&4>V3)!>UY7JWTLR9Pejb!W4GL?n-Ipi(d2YQ0ki5Skl6xeR*A&u$$)rMIj5iLDIRP6AudmPv zuQgasP7c9=Ki~>Ur{ELK{1eb7LgCf!xYt3!(XP+=wy?TtjrrlH@m+1Pt9b-gJpHpx z`kX*Ma9RU(WTrp@-O~72A$L2GA`K>e1Punyi^KqBpmATIGB7hN7|<146U5`)B$Agls^n{^JEy?9!ysvWi#1Bb&CTA*60t;6B*1u+>=_ki1^T2cpk7COiAbFP`^fa};F5QY}E416PW z^Be~YO@4!9|5g_NLWRJ9Q{m}HnR>@ik$;MX-zVp0+2dnjdG}v<%#$i(OS^w4eM$d> zSkM{4J2Ml#Q3f0yfHxuc2)2p0<14em(*hebD9Jla5db?22?(%0>7j<;nF^g^0FlUl z^+0&paGx{F34p0&?f@TH;$9Uuj`BA(Vx$*cPtxQQEi5e)ALwBV{x3L%;|=o63sh)f z5T)$_*ndWCH{>RL@(ugv)N==PxizL=J}>Oa^?IvdwuiDw(;m|ECeNfO6~8gzvG4Y7 z_9<$Q_?L-Q&3)QV1Q5yQ+t@h%);LxQs+e+89rqx<<@~mY*wjz|<>dJv3ZVAr`~T$; zu{b~fAg)y^r7KOE;0f6ukF0G10t63_$w283u7W?O05Zyhp!!RJWb0fQmxn(#5f*ki zSy|F-WDRw7S1NZNcJnOwf8n~BbB;}@448DVfsYaa(+wP|o`qOE@=vf}<}>^UU`u$j z7<*T9K5i5TDxbCtm_SW|LC23wZ$0xPTU=NG4fpE${}e$9P{u|G#{Nc-INmUe{lFMk zCNxAOeZ#?llz}ATlD11tRnK=}jAiBzN0GPDR60WT<0;reuZ`=bhJuBB`%|zVv$(2% zqNmujG%Gl&UwSikXLzEB_{@VSB19lu#N7*6pD z3Hv}vOs~j23Udw$B=7A(+A#|Cb+C9`AG47e85-JL8I4+hbiWHTbIjm&2n=?g?t99Kxclc%PLuf?m z7E#>Terk~{vtkH~=EAVjJP{H@mDk0=-_5 z@7l+A4>>`JmLLi9x$sLjfXxPmL879q4Y~!kwzj{S250^>6xaUpHe86*+mvKg)r7cj z&3x<=y)C8e*%E)>WQUCv?tHLEy@@05t7INm@U-x_9~pozIj`w6tp2UOWF!Y(`Vh(M zGL=M*_px&IJ@8Swdft_~to|0*xqt`8%8EsQ*?p1ozxy0 z46^Yd4evli)AV}|{ZwWms$e*(?E|Vh@o5Ec6R1=>l;VksTcNxBpuK1)y!2RPfr|bj z=B1zlk~u;bE6{y81p?cPNfb@Lod#8NI?8o89{ji2VDjDwZp6V4(&m%Tar-Ud`;F$3 zJF9mQLOX4Ti+CT33L*q~OmLG8>v_NE66^fvEw$Z@!A_dtfH}SJgv~4agp#K|aODk~ zIDdI@S60Iq=Lqb5Ib>|k;8&lqLw4^-`ns2Iv zWsMpi&L>rbX7UN;rGC+wYEvaMVilqr<1|@h*{Q5VXBBSeo-E}x4sdF$>h>aH(?a2_ zU+;~5^;>`2hwREkzeVx5^!<3L(k@H8CwN@x{?wl6vuB^Z4 z**!Uxxn^%`eJd90+P(ZY*z3fPuRZp>x&Gw4CCU6_GuiL&2I&)|LWCcAG`vY8Zdd)R zO^`sPC@q#%o&5c-kII!C>`R`tpNFMnu&$83lIvo-Vs>wdiP}AJ zn;`D!DkQhZETS-x+){H~D5KBy%U18v7X}mRUAUa+k~B*5pg&zxbappc`dz~P9-3Te z(Z{9wpaTRgx!ChJHA#$hq7?Tcl}e-uH9)T5;54$y3XkZhyG^l2Vq#9Z9||{Gk!OSc z9M$+VqGtmJ|6b~_50*ScV&-z8(P+@O>VNfC>m}xzynXG0A0%cQ-}sntWA()Ci&U6+ zIBtQKfpb~QTwQH^dUqW&qy6fOtsl&(w;3tAIcHOEICYAlKJg{0vu_(pn>`7Cyy}>h z)2ZLdjL*oyc`>bO*(W81{5HC?q{Ok?bA`)3L=noIIB*!XUc4D~qkW(fmKVl)!H&Qf z>p4WwYW$J7xVSU2YFX%z13ace$c3BUTX+x+OYM(*L)O;QwK%L>z@sLigO9
Fi5hAg@%;=R^pm&gge;Bu3tEc8C^w zfdjcf1zs88g7#Cp+FhB)cLxS`#=w^3aW`yyJAyY@FDl?ybT=}fA#y$T!XGzt{&D3W$RW69OK|Zody=#u&#d~Eca+fc7sZ_K_36VAUe!atSHgJMcI{GWKquXu= zvL`x++MZ0m1d@*EHFrN)5A7;m(d=_Gn%mFJ>@O_5wK9enFcY9@gP$|MnE{>jb7W6) zgZ!vdzbk`=gw(pE{vIwGCyae%rtZN&I+YM zNPrSeX2G+IToh?(locAiKw#t|p*I@kxzu?tT68&z#F8D7&oSb_((c{C(=k?Er$D>B zIGCr`0M{(#G#}&!bBG_UZOKCldX;GlDY?S^E+Zhe!^Fsy;={!op-JyxqqrTZ@=kO6 z<)nCu_?1krlw%NZO~=Rw#g5%ISb{5Sgk}d{@1G(>6O6leg$BQOy=upoZ*@QUP#$#a zMCKe4yYS6dxZBD8Ea(P+A$}=3 zN+ssM<|n&B#D;jVo?Nyg=m0~9uYX>q3f+M%w|wLR`Gdo#6EkRMKUxD{qydM1`xVo7 z51La4le4;>8-NANLYA}E$Ou_!F~D74Ugs7S9Nq+-3E8q9Ic1jC;1JR0Jml>z~K z;oUp`%gUF_xWvA8h<%x}3YPTF&dD(cPE9>VDE;8HjfGpZlTPEAP|x*;FNX3Bq*YbP z_xlA`%e}xqNK8SFyZ}7aoy`FcGBmykQT}}l-Ie|mXv2h^qSKwa5HCTVR^Dm z99k=AW98x3OLYx`_XC`$e0&IQlInbJk*f2%Z`9)O+WWXKUVMG%#Ufq}W=j8Fi8@J~ zp_V$qQ9yEJCu@c{j7T^br4NIdK*F_RLPU-b=^GE;g{D=#X8y8M#$Hw6Fu?y}Dh8PS z<$4@Lm5A1#l}CI4?)RLtt%^oMA5=lc`g~!qGg(qCDxTXU9G#VRTmXGs1XZFVxis*4)#BYr_==fRk;g_&N0xFrqj8yiEr|68!D`bI0&?V{KFjt*|o~ z>uS=ST31)+6zT&%M@&plPE!~+c`Fp_^U|ML;)X9gn73%d-aywcO_ z0(?M<`2)@JBGmyVgg@IUvtQ`R$k8aK1iG;QAY4Fgf?ve@iHuz!DU6+Rz8OB+!YI zkqO#;g<0xgG5-EKuq=CQw_JI+q9~BD1ioJjX6X_WgXM^WxzG?H%!L9|0^T_GTeohp zur#@_{kl4GoRpr^#|@o3wbC z6%QT_A@~f~HGpIPk9@O-OCjL9#%0)#fVb7(u>ufE-n-QTo(48P#kepTm{I?row5K8CY|UV9>i>>$04B*PD2OPbQu{Kh zm-HVu4cgSVJP5G8Fy|#j_6@*(xF<`~Q4Z{iE{DSJQnb?%6RjUyQ-vtL7E&daS6+XT z1)WgAl&3XCP$^}3QlufWkBqdMu=Fm|-g750vlcVn_Lv6@_wKd1WQ!u1pylI_g=!b2 zH8B&?H6IHp*Wu-W8hjg6d|%;V+bs0-0L&2!b94*XKt2^mB&EdTzyHY0czJ&dxO)b` zTR-tfz?Wl(*F7(;6X%9bz|6N|@>qfzlG_$Q&!2cfokHbTNKjW(1MYd#V01$Ev+~Mp z{9ri3VK67&-c3ngMoEGBLZewk)ebcTt-?zA%)eVQ|7vx+$D?&|J6W{j_9^f(nlL$8 zZjqJ5?ShCFi;8Zikhcz;6^MH7ZoS)A_xxqjC+$)O z%iY6LzFn2v%B{w1;g8dc_>@)w3pM4kl6_Q{8b;T|xGLfzwwWS| zZJ{gAqFtfD@pdUDx>cSKR(m)`lmJ;X%u%36J+U5JxH276_H}fSGuNZkA`@C3xGK8c zZeTm&fx?RzZ`lwX9Cb?Kb~wpx$DLk)R8~f3>y0*}!#{~>qAJ9o1se?7r};XDwKEBf zlP?SLQusIV7^SPPhO4I^6OPc71sT`E}+NGdc8Lq z-bmeUjdkLEx%E&Uumc?gq}V-NVU^rFwBzx9`l%WgH@?`eXxpD`9A43vImT%h+HEt> zjm({}(B-vsLC;Te-D@yO^W*=@0^wFIo@m|oQ09<9XQ8&n_y-bf#;?;j6mUBUdeimV zE)3E%)~9HUM0&Q&X9~BN)I;KsqM9y)ds6#V^44l4__O*!D14i1Q=C(Butj^OMI(;1 z)3|sJb-rkkBb0>4$Fk%t$z%yK`1cl@0}0cUz{AEG;oXMWguSI|qSM3D2cB`hbK%Q< z2%6Png;{mshQXTmg^CO(yHm4dCos7DDcL=r2IQ=kFm79dpsFGyUjK&6;?E&hHchI7 zk&sL4=Yt*c^S}HsjX#`AUI+pkHP{|gofRSG!TxZd*bA!yI?mYK1YKl(;)^ZcXVA+= z32+7?{;B0d4@y>!qyA0*wnVk(*K9mT^Ms9j>tPLoGO6{~mre)l{H&(gouR`Vtf!mj zq8;UK-FJ>RwwCPhn}fB6F7UgDIX%c-$+pf4)OtU_ri<>YC~c5MmC%jwpXBWO{3zl7 z(7`jy|IJ!tZ0ShIW3XMI0>*CHsf~Uyr!Mc?q%9NO#nZ_+#8qCv&D@1>7Dn>=lpaMV z3MG}m?=0QSM$TzzwDRbocc%1<%rUd0pLJ8!c* zWzxuiTLWx+p%<_oa0X@Jn(I3iuBY#)sflvqmJ6Icizc$1NwZd38IU`PM2pl?u@3v!U)vp{I4EEzYAYKjXxE>d9LjdU-0T+Opsk z>&PtT@jHZKBMkOx1(Hr_W%Zkfy6LEvoqHTBy2eiEw$qc&XWH82YWbi{E6ZYbf6xQF z`)+}XWFas~uRbIK)|`@RY0u_QZd^{Rg-?LVWrP|7ll+M2n0~RdOe>uSt9l=Ossw*F zVirCZJ1N>Eq(6Enf8DKYx?=3)A%E(IXxG=YC5A}FgplhL5c#@~o}D}y3$WA4{7rt= znBb8yMv_zgm9W``Vg2UqrO)4U@p={K^t5JRt~y{{OKULRZb!8Uq^h6_Vq_+tGFIwC z+6%W;+LNF!+6Eq6%7ETFS4?%2mZqfl5#v^_qv_($;;d$H-s%22TM>X3X zeCd1=5@wzXoyx?rYc1GzlTUsy)@ysUR=MSA+UNI2F0_-8d8%>41W(7YFTIAa(sI~G zbynn6;a#&?no_wCmeRL*rMN{T+Dx>U=VQ_E%`le+Psg|PesCZ8v$ziQtRIhPc+Fzy z!PYSY3!dk1xD%0%eM+3!QNQUVLt5(HFz>Ti5iDZ~J@|3@AU=@1wiBs}61pdX?PTBT zu)U{!TYDDnCy?tMez&Z*+IBL;NoY~UPp1u@QdS>P?!6#fAk0TuQivBRP{xGR7q-hO zbH-7TKcq~40+~WK8t4z%c;T!ok#bCrO#AX`89xTzy^0fJ>5-WUEOxRgAiXF9l3`Hg zENnZk=HUu?H$}_%FM1A<^1T(LqfGJ=_MYzD*6OOlTr7&cg1grWlJ7Zt$z@C_9^QB$_DFC1mJ%jyIz-1= z`okHSnOT456?fKK6zCu!>qv!J3qx8xN#~uD@dpO=Q%nj~tM~>&E^N|rmB~?I+Ne+Q z4)<*vNc)6(HuH_-D%w+=9AxR|?p!Ml^p!(7!_79xxh1ml#u6Dd3R;Y&gyyK`8Zc>? zDYPv0CaB!})d#SquhsQT_4Do&;)=xN4Rm`<<$MwznTqkPmkm57+Q7h)~C!ZjCYEesJk#a18 zgD3>302;k&<1elh-`p~dXgH+RqbYH)T|HGK5UTs>bSq!pu62aVx$v%DqDyGL(8?^H zo8UVapNa%iVou1Slj{>h2SuT3=0{sv-?tI^!l~$EiHSoBO*9@7y9boq3ZF5F1}J`k zUR@;%3_Ps9fQ`RKoAn8Pu7l%Gg5|M-Z=kl*r(vo5f2EB@P$6)cpy|ZJ$2Vr%F8mfu zS=1}h*0p6+%x=eXmJy-DU+rWFXqwx6vd#5xItPXUw&X8?IkO*r-6s#C2y)@G_Mc?d zxU3!|vkZd%p#KG+C6&2}43G++KmR+~yp3s11>YFHl$&6Jzq}ax@b7<~WukxxYQh@_ zm|FpXNmv(&{z*eIxs14YEkmf#?=1EzH3O0tc>9!;JY3ED`#85@!P%<7M-DVSRQ|6U z4@|<`cwe7N*@%0$ET4q9xZ_zR@jIag$O0g_>8bVU+`n`7YR>(<@Nd38_tOiMy()@| zVoQRIjEn*Tga4gwGe}wi%j;p=(Epva{*(2t`8$A0;h)E|47BXit3SgSem~t`Xw&a} z_}n0TU134pNL4kmBRI;gKc14_hxVF;A5`eeC@AT~f7%Je?=l~E@`xm~8j^hS_s#Ni z^q+m31%HUgs3y8@1S=9L!E0$BLlR}dvv zyKl4b@~L=12Y5;fm?f>2OP=lnDn(6%*`d|Y&h8j!mr&U+<^VT(JabU$60H-rU(iBb zlMH(K8dLdxnu^cPzKM+Dw@eV2$|!e!O)dZOmvrFjRclbQn1KCCwCw4KeXA+LeQ&~# zQKP6|5IijN>3BdTbz7^(N&15wyeWYCOOg{Ap@>82wbsm0+={}>#So~eY5ss2m zFvnYu>x-c3HDbVp@pvP;>S(K5zEo1x$@L)C%aXu&c0X**M1j`?|XRKUiWwK9I3K{h4@~` zI}?G}P6jrPlYtzgj#C6FMwhV|{{%5j{25TP(g-_m0TVu;1r7`h+@uXO@;(1g0SwkF zM%Uk*Utrk!^8Z@!X9Y4@U{Qn0VSRO_R?IR%Tp z$4{fRo~2FK(?JyPCx`;%X`2y_Os#Q*-%QAy^ufpiFzeID9Q7;ZXtm2>XYr)5Q4G34 zA_^w20vi;}R3kmTiUvUE$*;$wtraV;tW`!Sfl-gOipu333+g4xXIR&4qDNc)62K@) z15nX-a-rf%)arwcSRb8UdcKzz{aFOHX~4S%zAl1pY1C_|s(3h`_(k4*tqvjSv9tCn zz!5FRDAC*4oFFTId0KU@lJP}FcUj-EJicM}9JLI_K0~(nbbNP?8e$;+J5Y?^}5Hp;es*7Kre--VfbwQ(rwga;XJji++=0 zeyTjhAHFU7J8;o-$KmCNjPBB%YO5Tsx|fWqe>*kmE}KFm_G|6MEi02}AB35NibfY( zg(n^2t)lfmw2mCRTZE5q4K8Ggj4G-?<({>*d~#@KzdEumUZq^N*lXT0l~v%gGm2i= z$mQVAl;m{@M0nxfP7|@2RWRD#nN1@LO^ZpD-mV-z+B+RtMcm-j8ja`sa5df?(nu1j zdaScLv`L~=ZbkExeD!;!OK%kOb}d&?G&gRnfy$zSo!@BqrsiYuCC3B z+~B}KK6V{ka($YrRZ;iYf&A?ZZB97|L(i%sy0n$%^i%O94iTZruqyKPRKnNj)55#W z*0IlJ%G;vRLNEFs^4r!wJKn~_*-fA*nvVa{@RWbDw<>y31I4nT7|lf;dcB2=Vs~Jn zN42drRs_DQbg5#-es%cy1KH>W2>+iHIb z*Gh`al!dz<4C8j1*Z-TW6#!Ql*?ktUdnWr zITs!8+p0vW)uZ`S4F`){O-yL^#Wj*me&&zPlLQNDBNlgkBwTV0vfZk5|NybeU3csoM@e4z87ZQo5D(Dq#)BxA}w*^Ia5R~Rqyd_x_c{p zlH6Hj1yt|lw)>uAuev(7SR1o{TvAY*{|;$Q;c`41m8%Qz{%nA)h?f}yoFM|QhAX2aaH7!zg+lIVgdO>h6o96h)(;=*$86+Q!3KFw?dvg)3F*167P zZJ~-Cy@=!q@?@@ickXC~g%Qsmv(ww#sw@>!7^2NJBNna7EX-2naienwRMO-dhcv?l z`_{=E9&j@ZSl0wzZ#NXM-q6)u(XiyWF`ooICsPm&mWX_Lbcvl_^<)Bre0|eOM9(1vYdd zv(8HRi*sE zfrgoYm}m72Lqa%8L6w|q*qAuhf5cE^ES);8c0ZyNnY~ahhdO-hWxWw2ZZSz~RCSb) zdU(e-U~5S{C(d_pLtrE*<0%c_QaKiD(^?swR8$tp!|k_z3g>V^9TvohBqNlx`d#U{ zzUEgcbX5y5I2mGO?Q*UKQwDFiy^g#hHy0w5cnsg4Ta)=x%zM|Y)evgtnHCTL$;Q9j z&Kf6@y1J!6_lrBw`Tbu1&12l(J&Xk5=6sBm?1fKOr(%alGFQ+sts#jE?sQssNjD!$ z{lP~qH;U_;e)ed5bRWsfS18ZeRE?`Yx8$c+AUwT#u{Fo=EADRcOm+lVCQAMGvUfqk z42&7{>B1r-y$e9=A(#zmsIizLB8Q|dyfjSM;wNVv5yUq~?`5Weo~=_zw86#X&HL)U6R*|ku`SE>{Nl~eKeV+Iz=`U zziZZ--Dlf{9T;dWoxC|K{$pdoeDNn{Y+$VXSq=LTm^zJ}>=c7Hh_J%RJ3&zK()n6z zW^ck`Dy*Bqpf$nZ2x@TQ-QncFI!Rw8QT1SIj@&%kXVHHf$T|!9+-uFmTM0JqATc$ z#PZ;ogRGbK_XjbJ^jI;!2P+eXU_5B4d@Yooa z4!Uk9+}-^gr}zziqrEbnZAHSuzv=oEeHHw5-^L4e}lbil}ID30*xJ zqJy8Ut;e9)5_MeDS#kT_if=W{553te@~bPD1_hI!L04Z{;4T&x7I7aw#r-$NjYf%X z^*oXQ6adqYfFl_wMV02qF|>fqv}LMJz~+VDjlcfJ{gJe^G)U1uKlZ{V>~ZItyy+by z!&EjRVl(|L#S2S$4fyJpW@R-`$xfQ;H-tw-6gaNyuF!z1Na{hf!Qpq3sM+Vg337?& zo24_&QLP^BZvrd`_yGd*rqRT$wB^|BKkaYLp?@I@^D;7v-fpdO(AL#pBRiRq1-F;Yyn6K1hl=?VKAQNe85vkLuW}QL(%qGh#zn|;*Z41L|bK}g${#pgW3Ay=vQts zO8On$cx<4-c9;4rHw|i799-*sN?Nbzv|YnL8s>E@+tSXq!|G|q3P8BkF>Xri*k*+hE)`mP?QEq0hR7X6a+*-L`q^nNnz+1 zK-vK525D)jK{|#IX{2jtq$CHV8{TUG&(U+v`+U#u{ruy29Jq6?*n91@)}HKey0-*#JCtLZLRKhbN@z z!J+pSQ?lQ@(NWtI%MLl#GiTk+tjFj*W`RaIqWW#4ubzWB3%4O%5WI;^j&`yWck5!x;%xZjs7ab+<>sycyxPalkAnUA83)f6E5o#;XrXZZrrWFTAdU9B z;nFCimu`)eZMs;}3@QOZ0i`bQ7Vwh605vNPIlJnx%EEb1%S!&}JD6KA5T{dk`=wvy-RZ(2p=Gu9+JI z)?{M*b_Dtn!|cy32-&f!wdh5xAgcgh=!UR8yTBA41Th`1(PF^mU zm^xP-NU5j*!C@=J9kfJqKG;C&x!I4RipSdcD}DlC!FNsJ<*+HP>)~3wv_liSgJ*eG zPR`NlH@2oPpy6W4#TW`vmze#9dzc0-A1pz^ovY22z=W54M`9H*MK5hT+d;#5?`e>U zo?BK4=wNvh1K>0_cbdMW#k&ml^-1<29oBvXMSyWNQspX2N^;j}8*Cxsm;h0MXlTe_ zf|b-0ms}F_?$N&W(j3*4C?v0|xTq*RA|fP0-17=BVs~shvvYY>T<`l5VwsZ1n1Ga; zqvX22fkCiozj5K=G@C088~*j{sq*n{{6`mQ^TjdIFb#)pJ$u_)lwNq#nSsFfOd%{N zfHBdsUA%IGAKJ&vd9PW*TN1M|H-4|(D>RfK+!AKrDsq2+GC-7J=+=&4F%K^{w~~rV z@Yz-TDbL0)pl}Dw1>`Y-_Mpj%4ug)wE3JF3q&`g*u9^Pn6E%jOWaDC>JHSJJ0N-~# zw-)9|ea?Hl+u`9vPqCJ?U480S2Mpsm+A5t=Wh2Ef0Vx8oJ;X7A1MG{YFYXu@LF4tJnjhUcH5)=4WG#+8-eFt8X)JK$o?!fB(K@Kv6LQYMM)Ipj^ zMD1+jmpW|X07{`pn2qz*v4~Zz^h3~lG^h&m&A9tTN{eh7wacZh0{#?2lD^(JfrMdM za6PwutQ{7Y6k5`U*Eyeala+=vKd1sC*~T+FISd>eL6($iO*ex4-kESt(MKwBR_Q$l zeNO3Ec7%6GirwvVl*aZ(w@79}zDp}NeaS8!Z5}Du0E0Z}2f`-{j!^RL z@xuw+2+Z;WjWmEOFPt3hBYR8s!#&JK*EL`g8@u&LBAiSO1_$QJd3rmXZ9*MQIKl3~ zh5rgE=UbdWQ*RTssmAr$q=4XvWd7s_ubBuPnl{KJiZt^Mz?Vt5Dv<{X0Xkede7X>N z4BMIcL)g4I_$9TWCPDd4NAvT@&ylR1HTq((oY!~yJMiuOZ^T3_0#it0kn>c z)nVQQfuTV-H$+~A-rxaW8|NLKNs}?TO>ezx7KS{H&G$AV!3hUM11ru~iPB+5F`KOh zFNQS5MdAd8xo2JX&3AnD6rb~+3Q68z;MqJKRN?1eE_s51p`eWcLIb=u< zFKA*jdP=oKIc5>sh}FP<_4$;!tW8UWB8A3W9%5e3E^EX!-$K%1>_0tJY4%dO> zIHBNU6ZK)2kQ+vgj{~!ehU4g2d0BZ{b2I5RZgSkJFLj(?j$Gs^IbgWMX?SajY0776 zVR+p8#z-lIMNKxQBamwPt;ZsiUr0d7@>5d&o5l1}5p1tkaO-(Lzz^a|bgvF*TMr2j z+zA{J)D{P~eMMET?t60J;!XZLbc)|`0tg~Q)h$vck^`9;9%Joi!tF+Obmi~F0Y_@}Er!{g14DYOm3Oa0)DO@nPPX31H^9yv8TXBj8Va}|5Wh%X>iQV` z4miaP-zL?@uhS}w%eWz9iYq*HnRseJ55?MUHerD;zv@MByR}#ivO_mzb(9^vl`(b^ z6~>?n5%7aui}1zL;sCGl8;y=N;3XYxZSFs+Lk6~BN8Vhn5V`j1F;)foqv@m3IPG*{ zURm%VI4q_8U|~AV?e~|q ztV0^V$h4#aOw%lhX;smuet9@bDA=_@X;-f=t_6*uc**vzn>|(;&}`6iu9`d-^z|4a zt~of`c>;>Gzu3JXcm~lo=)akRbUZ13-k*;W4<5A{{`6sIH|U(MQQQ94!O@_|D+2gE zJl#dpGBOl)XE?(pLnK7$n!XFG#;vR_m8S#J&QT1x-Bi#Vmrj{vvV}nY%EM|C_pNWQ zQmIotzhIv{4%p{g^O%v<-bJ92;{_Oc)YM`hq2+WC>N1slUwlrNXHSreyfXQ9KR(>+ z-4&>kV`SBunq~LtB)yI!Sk(satqcpxKgvZ>v2tKBtbBYi6zo7w7JmwbCZXHbm$47+-1vSuF1Z9g(6+3OFo7t~r?RLKe$(FPHd=`)ubZoXrroD6 zu+rB>kMN6z5zwJp9Gn(@zTV>1yRssJ{l)z6hX6cTwd}s?Hy5qjrPcro`rf_uckl|O zz5^p9gAPolZb2s8H8{^`W zX^9-dr99F6B>tg!BM7hcM%>t;&OyN1Bu%)rc@gI7|Z{`P$fS*(&XlpTF0MGwfbgyLbjtTF>K`D<5irLZ(= z9I%l}myKT4@CT+9yF>cDW$)@-)gv=>#nPaRnk4|U`^AmK8iL`23j$Ic}t&t^&9*5$cU18G6`Wpi0-1Ta@Z~+4e=@zU#R9m zR8d7qfsVmS>V>%7b;tnIHJ48q;5M2}FD2Mw|a)r~zMZm+-D#D}1JX)vUkw+?5+GJmhE`_ykZZ&Vl zmh>z+#j@Ykh5Z=L@}06zn1}30Df+N*utwD))~mbd2?t!Z5I<6&l?Luawyc_Jlkb3T z`7^q_W@?Hg$fEpTP(7=p%0U}KIGlCWoa>s!@>QQ%^|4xz3Io_%_`40te`;!|-Z$a> z%n!lT)vqjs;c*I^aMxW?Cd!HhAMN3zXrXtusAuCb@r$H~wOs9nqWm6f`9G}V&BmUT zC>FO&6Fsh;=L!9R*(C>egRk^LU%6Vcm!K^R6umHZ8f%a zdiHtdqMtI`b4dPJiGum~VXnRhnGUU5bCwDsHrnuNW6M?VP6cg3B44Q=K^sBc5%3ND z)52O<{!Cl1lIktehEc&)UT^zKx^%wLghI9D(Zan0iP18Z732BUB3vo0R*R#IY?He6 zEe2RIWKCIQt;jI$&}M1rNQ*fO>VHuy)?%k5y46W<&4(=%$91E_ z!_$VgH}mLw`bNG+ajkfE-Mi`SS z$crdvvoW3V(XfYb?V4K^iJ-wEzqdVY1T|t3va{)f-N}_oW_!*Qd4uomkE+cEd?M$s zPu|4ks?WE!G!`rpW33u&-Tegl{0i5#QKL+>V!QR-Aq0moQ|lqC2WQ6d938LgIA(l= zl#{4yX+5?U464P?&Gr;nA9< zlNT;e&}dOjW<$@`Sv%J5$sSb^v#%<#=>^!U&U_8y`K>0CHkgr;C@ZXmVunbAc4-vv9rSp2`(XS8~Negaxtl7K4ycQnuc z@NdfcCg?+%%PeHvbNl2BEC6{rbLJPK`Q*hAPfAB69H?u61zX~1MSwXBXoY^~%RBF( zikZLplX6L(@Cfh$v*@p%qxnN%8*7Ubf#yDAko`cted!-M1GM-KkVw4nZ=GVZ|H0P> z@NHodFer;QPjv)@`!DE_1+4@Cq>lUn*+T|8y7RmvF7JQom3}?`2{B9vw)o82dJSas z;H|KuGS|HK+8mG*>| z19n=`Gxenyi(4BvNA-V^kbr=R(RT)GBArp|r7g>F24EeCt-iN2RSJ+qwxCp?o)A%L zP8vz7S-T3OKR8++(}`s^4|4#x|<-w*#xIUmqCG7yLaZx?c6uFBOr3GN9RYT1HU`Y9RqS;p0PN3De%^~p_lqNNsdhrI_o>1-?78~dy^A0EZGFba>X&HI-F4JiZ1M2Tz4 z#fW&K!!tXL=#*eT^H|vcy->-zI|SGx6-GAE{!p(Urh<8W`m$T{>xXb?_OL~)iB;UHHrpIgB5w%}Fm ztCGmth?_@y{`6K-Rw$#Z^r%gCyL8*k{MvPTF)`%5DzE_F{&K9(R0j>2N6dgpWY&{3 znAKcVn!7{!oI|K>KW4Y{+f7T_=4qyLo))fGhQ9LtSnYmV#*lR-Zt3>^(Bl`)V>ZvYY)15%pk+4o@Q(J99?SM*6{hr8`y}O8r-z@ZGpL?k?5_ zJP}ib#Wj)7Amu{>B_`k22MgCt))^KdsAZsecO$<=lR8*m$nQ+hhnLm2Lt;Bju;!vN z7coEc?|1MVh2PW`Pm?ut?>GybzJ#DvOg?!2SD@Xf>#y&3pX;KpmGq zoe^MY;NQJQ@77&Gf`pmC1tBOS4MZ5wDkU4+{ygu1%yuuXhty z_PPb&LNE?6!PGrtdaTp~Ky2=S$(xH;L09v2%FOivo*V?0Q)a-j5eypMBNw$~m@sU{ ziMQ*9T=sl`3~pQ&%<;5R6%%P^cZiW)H5*JR%ZJ7R>r;F$jm}|sSQffRdTj?!q#(LnUGB1Ly=}AB79D0MjKxjzc`f^9i%4Ep zn*)95vj{1Kh~Az54{vRT5h*tIPB;=xAX9x zS=l!wW~0#_=3>o(`wsN4BC+(FMjOKv8X*4$2 zKO35w?0PsOE7>?E+n#)sIC)|r+j)X-dBBSKbd{=Akf?%cU9vN;kI%P^>$HSs{n*0A z5@XrgIc(ZTWb`x1?>DhGv5pPD^~%JFk4N&nNVi(PFQ*Tx-b2AMeDB^K$`*m5}ifWh3cy>t}oc3 zbm1e>Ica2ch>cVl5-mozX!cSj)c`n0<*xbK{L_BV4`r&%q4GRGb8k4`&8W7!=;-vA zZHP!+o+MtL=eed+uB?5i=HFH5*t^bAfN^PF1vweFit98=FKQ0=ol-xtwXd8>`BTt* zP4(v9dkymn+b?cs|0xwja}XNGKn)try;Mqo#zOcO`O4PCC`=puM9Wg23%gqQ%q$E> zj55s@j=k2xa=@&#GnsBSV%^I_SZ{p+Ez~F| z1I15}Au)ozPH6+2I~8iGaBVR#T4(Bmz3m1cps81Ysi&)R9+1S~e?_wYmL&o{va+%= z^Ecpa!gxK0O+gD#k$DRWLe6roC7-8>cLDqJls_>Y*q@zoa-c~KzRE9zDQ#5%yZZkr ztt=`k0{nll{^UKV5M@q;{+)UJCAWJAioKu-x7~B?(pF>Kr^v5GAa`xIS$SOo+B*~k zR=rNs8RdZ}hw|^b-(hr@!)^BXgDPB&W@$i2)7+ zj9}Xaf&w>Ik4?4HNau~Ut!;1GV~2$(z??#tZ~s(zUF$BaI^4(s)+V4E-bbLj-miOd zPW3|MW0va>(8?NA`3y8QW41i$**uyl{>;8ktIk@N*gQO5p!t?lbsZIsTfl_(?s|tf z5X5bo^Wa@XOPwYzoHygSCoE`@>HcU|8?aD=q(l+D_fjL}$w-(Q)Nw^4 zWas4t&jR^!)PIQ{PU$+JM16DX{T46<{*ynw?@$YP7ShtqcSlRv!1f7b=D26R{K|{a zqn`j>toRHx#tr@yncZ{$;)Dzis)y^>j6Y33%{y*@Iw~j)f5}qIx3acgE?;}BP4(sv z-7$C+VDvfavWI$AF^Xotobm7JQ8N4ToSp90Ezyl0MbHB|eyia$Qvf<<&`wY-oN(u= zBG(BPc*ga+t)K39fZ1QXde|A@!VP@zky{C;nZ@6Gl6v;kmz!3Gv!Dzs#}LvbefCN4 z>Z;W;Uz3V8sH7Si8lo?z+5YZt^$8#bO)YmMh@WaeEbSpoXwC-q8RG|O-M~=Zd3P>t zSXb*;5_58?sgADWZJz)%I_pPmg5u}xSb{V3JHX}z(EG-BuUz8^*C%FCU!Mw*2Nlpj zNBqAn*5v_npgMw9P z$%lVB=A+*LkWa&5!pTtG;tX8C{Y0+a&yi6bOfW8X?Jj?cA zhyfS^v>>EHiwS1vZRM7J-Eldb-k&7pEBL1te}j;k+I<@q>rr9bW0K6VJpF256;L5` zp|Kqp3!vH`EKpqot`((|XlhDl-En^Ro)|FXT9xGYOp&aRGLS_|zV%&N1d8=jhl@Y8 zGJustU&aeeP0^+tq+n&fJWPsp@80*eC-*M?t%eya9&;J$>I#mDVg46#Az7ctEMz(H z-~>0pJpftTUrGLnbp$A|gQl-$rPJY!58YMV|D>3J_A5dApktiUf5}HqVinN$fRB$a zEhVL{zW&ue^XawKin9b!jt-m0OVz`qZa>=V9WNlUNXVT2Q-Q z*i-IBm(BpUi_**ac(V1*Mb%VbK^?UX1%)M_AQ`dqwOH(&lDl<`Ka&Mt$Mq+!AD)oI z4tZeHIFiq(13X{jO8fPeF+pm_Ox;<@&L!okF>80WQObJxNCHUItro;81ZYb~he z>e7-vezL*1jY40dcf;sdo?g~a6Zf~n{R>Pj-wxMdn|L$OyROr_0?-eyD>A!C3=hx0 zxl)G7jdhZ20Zv6H5;z+XGXv4xjtS0O4o|DsEg*<&N`nxXCXA*c`0 zloA~svPh)lz)r?@#G5Vy3ey;of<{!ThZR}Mz@}HH_kyj~*5YD~dn9%{c)och5{EC6 zRTwoeNh9K1_A8QKZ)vy6{xmg7-LlSW&fhZAnzfB~n{ zBRK}M?Wz>Q3olWyi8o=&Tp+E1o?vHaYfwP8nwXsI1l+UgZgU${O5W1d;UHutw5ZJV zsK`cqEpcSo0GX}!dH>2OCL8~*l`5B_}|gUIli)b zZJcRLlKOJK(%GmKlO6FLWoaX9?PnE>3CW{ufmDN~E(aR=g&f#phm+0R)zKp4&1JLj40L_v_L*{q|p1oxxU_cx|@gm4_%G84fBkv?^E z`O!k^Q^)z@>v`b`3b*fbRb}DabpIaftBCK!M5BMJuAJyh5i+-q{Hq7#Cup`bM>nfQ=*N(%qZd3y0 zBH+Gf!{Pq>JY;_Ea>Z1@8R(>tKW`(47hh(;2#G0#7D301ZnQbFAF+H&BkNhxEY2RB zo(zv#m1mxuNh?hse)}$S1|jmp?I?V%5l#lWb6Ck!(MZ9RIgAFsD%&rSW_;E)LW)1UyKre;qey(Aaw~^tz`SC|#CRD$gSp|6${{ppd|I5G zvP)V{hT~*w_{&V|PYIxx=fgis#Y!YOCsKieg3h45weGRf?us#<2Hb>&qEMu*s7G_x z%e?tp2Es>O8Sn6M6Y+UW9W9OOWRXAi6N$xBKEmT1v|k)*ATn6lFN@>8sy7A())nSb zxKV*@!@!;U6;h-Y+enzO2%?zrDx-s^yRhifC0L^zhn4L9`dl}W&omsdlvv57^mko2 z3Tv`-;yrSJjFjUxjn)*y>m^VA&KFQ#4cp5e&!UzwHAD;zX^WOTUp6UL1hRsQsEa<^ z!eb)<6kXJ8XAXp$Zm~Xmt}`@5cN8r-~5Jz$qvw-eBTlt*8H1zx&^0(WhQp%lYKwtxCUYszx&& zp8r)+y}}*G22qqgPI|wxj}BPR7-wXL`0AQd%=Hziq7!U%uKwRPF1WV9$V*wC12Wlu z`;~Yy&s#3n_1R9)Z(cTj%{%9xObv=p7dD(Fc|A@ql=gH**rnsqW%wd|i z6cJM*J>Zb!>utkgMBO{`Gq`(saSY)46j?Z8*+Qm=Tv7F$`k z&~~c&wkn$ZkQGQjGCV8`yPcbRj@1_^ZSe47;1W_&j#L5n?Ey1nja$MvcKIHD-_mtP6a$hYMAQK8|R8 z`6u9hj&_>(LkWLkmz4wc^Ya6ugba`cV~mlf{qY`JA1nr(K!GJ0Floq3K88O}>o|Z* z|3jvS|77|37fK0MUbHIwJ7`;EsWADQyBQ!>qff!5WqHu6<{1Ze}S5_wcmPc99^zu50YXL3o@ATy)y$90o*x1*K_Me=fpnHJwO-@dZ zcHaP%=il5n21hF#z2G9NjW_>G-+rR2k%ZyF5F)(1;g?GODNhHw2+1o;uPJQNt{-H7 zB-FsG@|Qrwg=6-(KpK0HSi>_42)$9!IiuF?dh=#h|5Je!d~={~2&^(IwzU2iu8u*~ z^6VeM3{5pTUG6AzpfytD%BRbT)Rm>>&+6id&==1DZg!H!g5~ojS$~HWfFZ3EZGP4d zI*s%hEYM-(mVMfO2?3xnXN6Bi!18Y4Cx z4twG`qWuYy*UVAv^3F{Mv067$l}}PrmwL2Yn=-E*@OP80pyCL-8>yVX{;*nhhH1hY z#ak@>fUInzEpo~)!*&Z>rQLw>sJEnt`%)@jj+_UDt(d?VNH-?PxYyB^i6HAR$YF98Oq*ra)hx4Jn25tSsH9I{ z8i{GJ8}v84~-C<~a_WUnj>&AANOnTnF}xjP(num!T^`8+;iw&>fYCSdCm(zg9+vK-c2@BEH@ zo@m#SBQ)L7Rk|&QnTtp?F0%hwQANS(R(g5lTAeEp+Y5B|x3vEx(R=+2FmQUKq7NNNK{9Fcy!+AbcJ-P>+vTI(ZIXSL- zjU&;A1)M2g6@yJ?n0Tv7i;{J|e=UBbk}4RJW;U~$lB0|pGp1`_DUfTq8GN-H>xL=| zMYxK6+1^15(s2-e4Ket&q_n5QSHC?~Xh|Qhmq&`-8c3)&zKKe!m;Q)sU?8^*62Z^X zDzK9vOX8v+#MrC_5j$>NiCnXi6G>UDN#brGj zw!+t4>JL;f+`G+1wtIM_x|R^qOvv_~gW2H3TR6523sI&F2#zF|Y#Gq?Ns zIzo^KU7FrrQ^E+>2Taz}^5wDn=L1w3ta5#axY(BO$9%yDva%crG7mb^r{SiNb{77b zbk}lqZOY=vuQB!|6Zu(lUt4f7%^#q3YgYU6IJNv91VB$dHzFohJ!uUw#={0#le5z(rqHmK;=H95pRcz(lF3J`7tnKg zc2H_=Kb;NgEa%r`%oWS-r%{l+hP&dE@Orz9t7ioJgd}aFr$`7AB(UM39S7eazV~wZ z{XLu@NX?b-d_0hx8&3w$*s(il@HGhKu&EtZ(kxE~zZ0+Bh3JCBQqj-j?Nm!kL)T%Y ziW2g@6^o(PR(TCU8Rl2q6M&B5u2Vno`2<2#Vqj}ai2{jLVsgoF;Z>aOAPA2K;4LAe znLQ1eA;C+XuTW0P$|mZAc*R>Lro51>$K^M#dYPDAT8I+zZ(t8a3VARWPEQjDzgjY4 zS4L%V2!~czklfW+6u@=9e8NrE*VhMirvTyA_rFwezO#eAq+An!NoG;A>Hy?R5cH{~ z!>OM$s()5*E|v~0Ds)u6Tp-*{I)U3pWD7jYfh`cbndIP0WS}Yqpa5ZhpDA22J=rB~^hH6NWfQ zjW+Yfd-awP?MFK+U-rWaV0><)xb>9NRT{iO6(B_114gyBB9qlPq z0C+R$QE`~`vme7FzlCuY3FgB(Ql?p?a<<=I*z+*0Ef|LopGwh3x!#19{tjR-^B zSq^Vyn0}Npu`ts5YB}j)UQuSaoLOGjb}4L0j$YPfJ$*eJ(%sZe zR#;tqzdY~N#yp_ttJC*-6sk6^azc?<*_&7@T2B!kHd8PJ`mK7EYwA=$-YB3ddEVj@ zP$~zmOs18>ScX=+-mczeYxo4qPAz&IModE5N6J&}O~~Z3nIEr~*d2F#zL!X>&)|G< zBYkkSXO5gO(?+VFI~b!AiFp$R3>XR~`?$WCj+jpUA2i2VtQ@=8svc{Zn=dyuHiDGL z+s8*&H0BF@S~9K`8nAj>-t#kbZDC=iuTO0BbNkkU>S24_2;yQ*z|~IYlDxdtIL9UF zl}7i@&hN9I8c?iOY06K7x+EbABUn0(+6S5R90oo^-zhp?U}8&$ zU}L^nVh?ZpM&CEYgY3Vd8}4hodo`gsqkKI+0fLWTpUqzKCt=+?Pu~=IkBhp zAH|KBO^xTlH%(q3^O}JH=PO6YWyEr&x3_n{-3}h3m~6FFIlchs$V+?wDFXKW%h}?g z3Zrg@%rFCrnW4hk4)`@4tU(^yizu?7FCJGD^02_ChlegzW7=o~Xq$8v6d6tGN61Yp z@SB%|2Ma-eo8lo601uy@VI2v-CZ_Zf`Tj-$^ualDI3yo^2GDqZYio;5tGoo*A#6eh zdBkMzBP6H5d9KqHqDiw2WBT91)v~!4_{ZS%9xy*VMR0xraGLX)h|~!u1h5DTOGron z0{5`c(Ar8Fgyj1>rx$sh48tXDLcxGwT;t>fmf#*q-@^PF!8Pc<5AqPs4DoOvcpW!Q zYCM&Ge?sbXw9WdLF<@TtUoXH7id2d*N4t)9kBwEm?4;Eqee`hm*NvrXf}YRz;mm3} zgI^u0ZG$T(|)hDNefqHMtxu8aTn zJm=sLMa#-#9A#qxiOf<{)Y78dWBtc_2PBa%lPf0upR0{i)EahReo*A4CTj4`uYfx^ zi-f0x7kS~${e1~*vVXlTabXkR@j)BUaVNx&tv!YMi3y$JA&Nct1sJMpLEZ^f2iu6( z0R1FuHU_tZw4EnbB6_#Ti;gz^%v*Q|i(U5Md{qjBM}U$fu==%LNrFeTYBh2M?vZ9! zv7)D3ZU?~}8`>XkHq=zm?%CNzQn{|!42v5zKU?bxBJOK&BD_+fs(MSVJ$t`Rt96S} z`-Pi9!>qAMkCW=lEfK$04hfuP>!#Ooio|Gpi8vY4EuB5E6J0raj2(+!Y6w)XNGR8S zbYfVCfBU4ubs3D(H!#14fjw~Ct5pSR(*&%Zi6Xn8P;VaVfh zTUeN>o@ue(xV}7SDP;Gir|9CN*~o&j?^gxq$p_^dp51>yG zK~=u0@!kX8@c5UA@TqMtTvBu)A3YV~aFl}zpoh;t^S8}OTt4>#S3)vh+v??XLJHdK zAX}Z@IsJb9)H})UD<3>u9V3c13zleLd5k(YzY-bB)HeCyt{s{6eaf6OU)nR1>Jg6l zv5=_c%ha;bWlp?^GgN)_s)~`#q-x$q>%m^4%I92Ft}GFSaFng1=9qV!^6smK*JZ@o zzIk#JOfZ)1^wkuFTtmGl>*1FrQZi9R7VkJl_$ec->ctzhAtp)JsN5I zBtnYly7 z-Z&kRs*%lYghoLcTf8mGetvOMeJDAOH~X@Axy4WO@R!i`FCR&sjrOl%AyUJ(=B_Tw z7$evH1w1SkomV^oEa{p>l_{Uio;l2oHP&zx8VEzef^K#P-X3yeXm+{b$J(f%fRb;Q-`cGX)K1Hs-tdYvcXKVh(>6| z_ry5cgBo%kQDmEyMC;VS^J%VP?U%btf^w)jvd}3aE*)gS>f-}cSu%`e1t;dZW5n3# zVe1jkQG@4ELn?`b?on1J>qz&dK8mrAn_Zuwk@KY)kZ3`KV5SIPD~5qk?vps+9j6iX z0!dbER+uInjnt}c1}_n$rSmWZh>a(<2dxhm)uW(D&Rg=48$0cpZ_AfNjt;dY`o1+} zjRj=g3aCi_La5fg`NlTT?krehiZu?G?vpxnatXG!!<~LiXjdInRS7UDIc|r?vp!^x zBnVQcQrGCCyvftFI2b)PYwt20CsnWV!@0fi=KiDFya$dsfzbj7@h*n}M+T2y4-|H= zn#$AXeYgWjf37(=qq>wO!@cFzzqemlwcmHx0IS%DP}beRk7V`x)Z5?j5|Jw65;IWP zFN0g}%V~(@oqU2l17aNg{Bue0@xubS!wYkIojq?Pd!mgth z$p{W@Tab8VGHU3u`OFJp4KCO_BjE-7NLwHE+eFsNx|-Y)_188{1Xh)#wq;)y=zGQK zE7i@Jn*SbghS8p~bhq1J+IOyMQlCjsn9prX3;VIai z5KcR*-@iWx3^Hl2YNH=9qm4J)QljRmQJm%EkuaRTQKYy}Le8V{<|j!$x09A`ywKB` zfRN5}I^4Wk7xA5Gv&VF~e_@bgQ+}Gd~6XTAOlxnY87lN1||Px^_KmRJb&Xw!UNoz z#I|A5Q3)NK^gTGK`~wL!TI($c`Cj?>DyP=@0tZ$FhCBNHCJ%8<7lqdeI7P5kzc2`!jDZdkXl^bCLL8tei4$`t$i!5^6fVxekGlI%nLs!OoE6a&Ru2 z7^XL~+c#8A-c-U+)!Tf01W)(WNIv)XW;TvtR_wUBn7l>Y>WB^xY79ND{}q;gwvZGeTbCxbXKGUYy^u7Y1f0IlYha{nt=Y*&vCzzdz(lukj+BXyx&c z{`k5kLuYJ8Uq|3P#xsg5r$>G%gffD+iGhLPQbnaDx0H_g7LVmLs6fYyll8d8Ll9uA zeG>A7ll&gZS)ii4*HGx*L5{Cp_2p=5D=1w1SKI>bseN)qc>DXC({BzWn}FyyRA8i- z0EgV93;7jZNu$t(f)_|$^HG!ky4p{@PvB}%?K9Zt&!wcKP$o%z>iR8s1)-hzlj{1= zu&~ppl5pQo_)-(yuL#L}DglI*9tkO6fv)o=^jM1iJ?I1%PZ0MUHrtBgf&LFM8fXhj zN=X?fXlo1%MivnT(6=96pN5?ejgjo{Czk<;a(Xs&1TSW!`#m8N5;l!KrWLPb%-P~3rWGpj-@JmNk=#jQ~F^Xg&|to7?) zNG??T(5;>7ClPTNVHywVd!0%1=iL2V-)ripu*`#oLR@xiK@N!UxB=LxIQgm`Uq2nJ zi5~2#f<8-)?_FQM#LEH9K%j8YhzvvLw;&IsSsdKo-7PIFZ*qG?zT8a@8 zs|bw~%n(OhA40nk?`LOZSUI60i=ZF@hab}9ia0x|1!+9ZWW&SL?{oh;rlvyctkRz) zERNt67T*5>$os3A+B*%tQP10kqv8+edo`Q$9Z(8dXPbo_51lli(B5GOt{*Lh3FzID z=9|z!m+-`z=|E9y+^UHd$nA29GxL)w(6yZReuFWMWZE_x&wLo z>A)db5(WGqRzU7HG&V+sgK0~}%EFYHX-=X#wbB?)W-GFI^*t(hg4RgGUy}UFd7QCC zkQ8Q=-%(7>f*^SN;H8`+*3V_~@816D%nAjPW*NdRr6M2~jbMUVE^+}+QjJg>339&Ndro7&D@ zIxppjX|AhlW|O zJg9Qmbm`IN`3N%V2Ut5%9Eo#R$`fyu=n`Y&8xM|8s=38M=o1J0S#Q{ zqvZfRsGA?`3h6x_P6-G(_{fw|g0M!@w{24^@D}o)XdQ>Ku7g{*wYQs`rWtQjn$q{&?z)eZP*YB zZ7838nhgH2+J7;-CC*a212eU-V8^=zx|1clq@Sgsqx@}AUg%@l=P6%%gU)Ssz7kB$ zY*dQF^gx642G+$|#J22&Rl*=-TqD0;<^6+F&~f&sqAVimMQN>nqGmexI%qAiLOhfn z%48%iPELU3;DMXXqFRpC8OF46CXW5ZM2Thn^mW45x^V*5K+a+ST=Qw&R%k~bb#x3` zB^-8Qc}!oY;Icde{igqnX3)xuLKov1O9Zc5p%7WPjMBsTvpCXt;X0)c&fI$W(zL-> zmBzSqLiyoqEBY(XO;fK=i}BNplY}#AiO2I>o}(&RuG}ji#fn-Tj~9+&jRX%v3JBSj{n4}`TeGhg;D?wW7#@$%AcKOgUOpmh3qzqSGFe&2tF zvuV3*E~@ocgGGqE+6M4Q02OB#c)#&8xJc2mR_*rYB#Q3O$-9a@H|_tNkeRsvis$V9 zpk0MLupSe$0P_+^lZn<`uL}P>qhkqnnfHUBNnYYN)L51F@cl#J78mcbYh-GQ0>qcc zpsCT}zQfxefI0y@IIc~Da>S*wb*iInsdb`I?1W)Jjs-$kfOTq@+h41ac~Bb2w8C|v z^!ojy>Wfq#BN8=dANOC2ok{Sd6vK5|@09wS4Pp(p%5&NU_A3*>H4gax7XU2$V0YtF z%E2;gIP##YWw!OG`^YtqY9H=w-s+TfM+IdhkL)&*==6cxi`U?#;X= z$K?vo!*a6`K_y~FTy1x@*G6~kmWBan{5^vPLx=kESJ?6>ZqWAh+e&AEssTar6Fh>n zgoLwI#Xv^sF#h!d0g;4cwStoq$;kFq*KG{Wub`KjFQb^m_-B55DX9ivo#|oxKtJ)3 zLT=z$j^(POyTO+-D4KMDV_~A^O9F1tsLeW@6@vBVb+Vg>_?q)-(=Bb6o#}c%@}Pt# zmml8H-f33t7=CHPEpqN&C^n7zmwWcq5Ahf;k-WuyEhW278-u6x@|*(eR5}l|kNPK` z%S&c%QX4T;hH@q_;&Z5^gjt zIvCHuR63K?b6e<&Pv8BNasJ%1zJubAs`(P{Dr6=mNSpz)%-gCClYJ2TY`Mdl!L={9 zd74?9>B-d3vm0Nwr9Vf4>(_tp?fWxCxGd*La^JqcPNB$MaOrt1B`qH6-5E*Q#0#DN ziMrlrB-d#@IwxM*J>b#NIM2>__N?!vPE7XQ3$Hw9KV+w=x5oxx9UpXnt$Q_Dj9m|k zLGteZGJABCPhH$ND-w8qI9Al)zCz`A|(<9MJWO4p@>K~v``dKdKC}|APERT z1f(NPN+3v)8i9a_6w&K_J9xeCdvE5OH}n4azL`u;COLbbefHitYyH;xt+k*-gW5x; zx}99qahJ8O9iy)T%r5FU+t9c?`YI>VI2F(Fs{+jwH;8P|DvZ6kM8v|vm8t+tw@8R? zr$w=njy3|^pP`SD8&yGyyqc(5Rj8i6iQe6@b6Cb%P)hW{W;<^bb$MSiqgq6cWZF4e zQ8RXoWZKhi?N3$G7oIcPC52Y=?rhOShxUZ<%){4gKz6%QF@ zIZI<5cQ%^#*a^LNWumd*Sp$9sliYJC$%URrlzCd0S4;b3_HBJ0B^d=dB$jw*AuOy4 z!FW?lR7XtDla5b>>0?-~zF#|@1ZBMVRmn%rbvB3bh>lzXTZ2-dsu#7Mr32>=4XsY? zKF>|hHUu1lFzGMI4XWVlu%#ujyZLH6rI zF@CrD&mz;%G(8#Z`oyz?6IH55!luI-y>v>@(72> z1&+h0TXVXNkzoaUF7@Dik)B8vbTCGm_XQU>Gap;{3%y8fAswg`o%Xper}tG3i@;cQ zhf`O5KE9vEBK?_Tu`u}Ne4R?w43K{M6W=LK0e+HYV;D$WV^qoXD&l005y5SOw47AW_g<| zndamK7I3juVl%+4-psJ1jE=h8jWq663RYU2#`+2KwTN8k^Qm<{DLX`-K*er z8TRJ!X3Z3;BaTv(E5E0&l1%HeV4|^h0GqCQ5h|W5HR2UbHI`~f`GRP4(8=i@afK8~ zUUc};d9!?5q7fJxmX7#ret6(zXA!pW0D#}Lh+JR2I z_>k|$DVt9tZhY_%xS$isUo_U9s`IX$Tc?hN9j^uITnN~S?9EYp)&kA>dGqmSU8oTH zspFJgeEwx_8f89;jFpK=x@n;mNWS{qlWQ;<0FKzMdQddztw_5n;0Dl2X zJJWCX1aJ)yAruI!^%pK1#FP$Vh5(GD`EAEX8vi##i7A9{F@(1ZnLgK{Yvqsmux@l* z&NrZR9KyUOE()0uGFS#3%|-i9EaEV5qnTn;)y#Ie<{s0nS8-#Hf%X zK^dLGa?F*$pYifOcJ7plpI*DAPYf`jz|KwF9)ZwMhysc5>SV6LLEZc)TpU!Q$FA zi1(6_K+kZpb#~``{XAM#N=oXAX706W!sO&Co-E)Se@fl1-^G-Mr9o6w^o(VBRdM48 zVqkUKBvW}P>+@S=*ZuJD@Va|b3W5b&CCfMPNb+2JtjNc4vl}a7my~U6@lhWhe9k*{ysDQf!(NHB=3|bl2m`LNkdl zI@&ERIPAO&f+dxaYsb#@^!5U?o~^B|tM8@(%1+K7*rm0IX%yF_5qk1xHP5+<{C$^u=da&pPfxg`e@KAXwC$)TdS4#&-+p7q|T+0^O z)KSkkJ5A}rLV-%!|4gxSq-kl5*m>e%5U6np>OT!+oF7=Z!g)I|Fi>NkB%07!1C%*X zamz-!c+eT-+8HQa>`mKZ!MU-gHZ(T=0IZ2xSZtVbw7lnB+xLw(GcM9%G9Wl{eP!k4 zXox>>3WJ$tI-})9e~Uh8emhWJFM~J~0T(!SZ2Zl`KLM^e;Xg}V)@#V&35@n2t3XvJ z4E;1d8x+h2jN6JaD2L}SJ-GbF>p&26YKdSH4hnhjl}+0&&Msk1vE|g)!NtiOkXapx zL}IL~^Fb{5_G_bYNTH$_4)_^0D!Z3Z6JfvfOzVoGT=v*YuCHWbNX-KRKf02xngYA;%{>Y0xg)%$PezgmtoZhhJ|zcrw#38EC?@F|dh@#;)( z1f26`NGPBbj7Ew_vn^{o_NC*3zIm15xRd|~*BODB7vcEs4V>>Fo^?cnKw6uAjV~!7 zRd)*2z!(i$0dd!3YpJiJLr=9_O$96r0#R0B($AsY_9SMaTb2l6fa33RIuX7c`uoWk zFFA;rpd;dfLjQg!iyp=z&V?eSZ~pTkjG`f@8|T7%dH?y)&DZ*W*_GWH{L)c0yJ&$!_c>XHH7qT36Wtuu}U%@BNuu^jq z$2-~ib9C>qsI03+ZAQ+`Z*i+dqMEid-&QyiHl|*#_PJXa1nYZy5ASaai2L=QYaD4l z^~KAM02O|H{ST|RJ2Oq2@e?-tFC?Mrq&v9Bi|hAmbXsg(1`qtq)iO_Lw>Om3>%OSo z$4w}#3#X^U1qhu3nSBGoy&dW%q^Z{{Z)*8Zcb~~bmR@xF`k_X*Np(5U;oY!Kn;|PS zLSNQ6BeEi6^%LA(&1UiaI8Nz}n7zhJAps)pMUPh*qMasS0*9D$j7ByyC%j_4J(R5u z{e!f&(^Z-EL_cuyRvuh2?sYj4r;eewnn#mPItQ<>s;ZjQ5yTK}T*0Ic(}a(x(IDM% z)rQ*Q&6O~L(;{kO~`-^?OvZss@pT;5K4~DaP ztF!6qx_uYbHn^X{d*^j@@TrZZb6G5-ndUL?-bEjMvk<5kP$f1g7W-0_Rs9m3&f zXNkBcFI~ppXIcI^Q8KPvy)g`tZECc7Fja)4cC$bbxOHwE^$kTwc>6~kmm2xhe{Ujq zNB5G8|CdVJ%5#WqgsS_ODMj^gYq_I&97s39RmT%ETPS4estrL?ZFC3HH_Mw{V&=!m zjZxVw#K!Jej93>$Fboyf`8Ag7pf60a|MW1jiWsuPkNU$1J_gx4)~P}<+MRh=%MD1DA2 zouK`g?xzd0Kl>RZffLb>dv!aMom)4lW!x?^R>1w^>y^&_0NosDG|9ko*J$1c|NPY9 zib*uT3nV~KRY5NyIeqv^zG%WBEh9s>QEPF}{Nd&w z7hb?cY7pt@@}I+ZKD1;#Lpc%VD&Vp28rq2vtDs^dyx#k^7#w1#R08`M+Fd+UgD@bc z*Q+pX^Ew*;IKrK=)FD<6EsGU-tMvl!m`H;~3cYD*3dib(lH<(+}r1YfMX3R7R&1FWVDw@)p`BY?_{{^YI5F%7O=#oC%o+ zV~MlZvx@4l*<=F_IRj2+3`_jZb(tw+&K0A64Bu34uX0kU1pAzyWVjOVG~)a4aoHD@ z8ANq&u6#H%Zae#sso$C>c~H6=a$$73dg@)bnJE6Q^7;z~&-T~CwwWp!gWDBJhBm5w zYuN@Mi!D=}7kd5O^QA@?H9XI=U>lU>AwcitJJz2u=~zD5h+a! z(C3#ATtBzO@wiPw+xPStqQ8nX*f8HaJWbbu!!h%jeQ>ay!$~; zm9Yu@fvoNe>>Cax>q|!(xLgF?qk_+@NKsCW1E)41ETU-JBb!1jO9)%ydc(fEA#lc!qB&A;$x!hzI3O~hrkQ-m|y z0LCVHmmV-$8t z;XaTNi2b zo`G_Vz%^xFhGT1I724<7WUN@u9wuO0Q`ia6@z1Jm)3pil_lY{(7jEt4% zY3pLe_;fOdTj(FAO-8H|Dn*NL7s$jN=Xu4JqIJ>(#Axp9AYChgD++L#EziV+M4++g zO!Q30^Wd<@f+4|EfbuXosS46+T`RM+k(QuYn}2j77-j#uwifyG$2TM8S{j}cOvM!y ztdnI{mB2;PP&(B&0r3DeXMy>4s_A&3YinCHC#d>cfuZK0A*0YMyN|MiT8p07&Vho! zBn1!p@oZ47$VzmAuF!;~Zg2JF>VQD?3u*!G#k@buQ?yQU zL!t-BvSn+ejHD^0zl}Y{2rTp$9Qy@p^ySJ%5aI2^b8CG+3J%M6Q{i-tC{w2VDr5Lw+PnldPVzF!!#K;|1wHd84wfP;82!Zkhg+;P=GfR)bzZ*JTurNOT1hRB&Xwg#acq zaD=+D0%ICDqACF81{@iyA;16!j>5GRel9q+jRD9)X*|yVH$8#4ZNmqg{gjvE8NhWu zY@98<23ziHxs`CAc>InfGoUWl@cw;9dGf#T2<$43^%SUm*E(pgA5hy04fx-9_t1ox zoWF?onv3Q_zhBvp7yiwUzoyv!;9pqqe_i}wkVhUnK7(I%PDzfn> ZEixrG=acb}E2wZrWuR-KgMr$G{R7D}?GOL} literal 0 HcmV?d00001 diff --git a/GameServer/Contents/Match/IMatchStrategy.cs b/GameServer/Contents/Match/IMatchStrategy.cs new file mode 100644 index 0000000..922ad3a --- /dev/null +++ b/GameServer/Contents/Match/IMatchStrategy.cs @@ -0,0 +1,7 @@ +namespace GameServer; + +public interface IMatchStrategy +{ + public MatchUserInfo MakeMatchUserInfo(Player player, string serverName, int gameModeId, string region); + public string MakeMatchGroupId(int gameModeId, string region = "KR"); +} diff --git a/GameServer/Contents/Match/MatchJoinGameRoomReserve.cs b/GameServer/Contents/Match/MatchJoinGameRoomReserve.cs new file mode 100644 index 0000000..5ad2682 --- /dev/null +++ b/GameServer/Contents/Match/MatchJoinGameRoomReserve.cs @@ -0,0 +1,241 @@ +using Newtonsoft.Json; +using NLog; +using ServerBase; +using ServerCommon; +using ServerCore; +using ServerCommon.BusinessLogDomain; + +namespace GameServer; + +/// +/// 게임 매치 참여 옵션 클래스 +/// +public class MatchJoinOptions +{ + /// + /// 사용자 식별자 + /// + public string UserGuid { get; } + + /// + /// 매치 결과 키 + /// + public string GameRoomId { get; } + + public string GameTeamId { get; } + + /// + /// 게임 모드 ID + /// + public int GameModeId { get; } + + public MatchJoinOptions( + string userGuid, + string gameRoomId, + string gameTeamId, + int gameModeId) + { + UserGuid = userGuid; + GameRoomId = gameRoomId; + GameTeamId = gameTeamId; + GameModeId = gameModeId; + } +} + +/// +/// 게임 룸 입장을 예약하는 클래스 +/// +public class MatchJoinGameRoomReserve +{ + private readonly GameServerLogic _serverLogic; + private readonly MatchJoinOptions _option; + private readonly GameInstanceRoomStorage _instanceRoomStorage; + + public MatchJoinGameRoomReserve(MatchJoinOptions joinOption) + { + _option = joinOption ?? throw new ArgumentNullException(nameof(joinOption)); + _serverLogic = GameServerApp.getServerLogic(); + _instanceRoomStorage = new GameInstanceRoomStorage(); + _instanceRoomStorage.Init(_serverLogic.getRedisDb(), ""); + } + + /// + /// 게임 모드 인스턴스의 입장을 예약 + /// ServerConnectInfo 반환 + /// + public async Task<(Result, ServerConnectInfo?)> RoomReserve() + { + // 기본 변수 초기화 + string userGuid = _option.UserGuid; + string instanceRoomId = _option.GameRoomId; + var result = new Result(); + try + { + _serverLogic.getPlayerManager().tryGetUserByPrimaryKey(userGuid, out Player? player); + var errMsg = $"Failed to GetUserByPrimaryKey() !!! : userGuid:{userGuid}"; + MatchGuard.NotNull(player, () => errMsg); + + // 매칭 서버에서 사전에 만든 InstanceRoomInfo를 읽는다. + var instanceRoomInfo = await _instanceRoomStorage.GetInstanceRoomInfo(instanceRoomId); + errMsg = $"Failed to GetInstanceRoomInfo() !!! : instanceRoomId:{instanceRoomId}"; + MatchGuard.NotNull(instanceRoomInfo, () => errMsg); + + var instanceMetaId = instanceRoomInfo.InstanceId; + + (result, ServerConnectInfo serverConnectInfo, List businessLogs) = + await TargetRoomReserve(instanceRoomInfo, userGuid); + MatchGuard.ResultFailException(result); + + serverConnectInfo.InstanceId = instanceMetaId; + + var batch = new QueryBatch(player, nameof(LogActionType.JoinInstance), + _serverLogic.getDynamoDbClient(), true); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + batch.addQuery(new QueryFinal()); + } + + if (businessLogs.Count > 0) batch.appendBusinessLogs(businessLogs); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + MatchGuard.ResultFailException(result); + + Log.getLogger().info($"MatchJoinGameRoomReserve OK"); + // 성공 + return (result, serverConnectInfo); + } + catch (ResultException se) + { + return (se.Result, null); + } + catch (Exception e) + { + var errMsg = $"Failed to RoomReserve() !!! : {e.Message}"; + result.setFail(ServerErrorCode.MatchServerException, errMsg); + Log.getLogger().error(result.toBasicString()); + return (result, null); + } + } + + + private async Task<(Result, ServerConnectInfo, List)> TargetRoomReserve( + InstanceRoomInfo instanceRoomInfo, + string userGuid) + { + var serverConnectInfo = new ServerConnectInfo + { + ServerAddr = instanceRoomInfo.InstanceAddress, + ServerPort = instanceRoomInfo.InstancePort, + RoomId = instanceRoomInfo.roomId, + InstanceId = instanceRoomInfo.InstanceId + }; + + _serverLogic.getPlayerManager().tryGetUserByPrimaryKey(userGuid, out Player? player); + NullReferenceCheckHelper.throwIfNull(player, + () => $"Failed to GetUserByPrimaryKey() !!! : userGuid:{userGuid}"); + + var businessLogs = new List(); + //현재 포지션은 departure pos 가 된다. 관련한 로그 처리 + PositionInfo departurePositionInfo = player.getCurrentPositionInfo(); + PositionLogInfo departurePositionLogInfo = + PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Departure, departurePositionInfo); + var departurePositionBusinessLog = new PositionBusinessLog(departurePositionLogInfo); + businessLogs.Add(departurePositionBusinessLog); + + //========================================= + // 이동 예약 걸기 + var message = new ServerMessage.Types.GS2GS_REQ_RESERVATION_ENTER_TO_SERVER + { + MoveType = ServerMoveType.Force, + RequestUserGuid = userGuid, + RequestServerName = _serverLogic.getServerName() + }; + + string serverName = ServerType.Indun.toServerName(instanceRoomInfo.InstanceAddress, + (ushort)instanceRoomInfo.InstancePort); + + ServerMessage.Types.GS2GS_ACK_RESERVATION_ENTER_TO_SERVER? reserved = await _serverLogic + .getReservationManager() + .registerReservationEnterToServer(message, serverName); + var result = new Result(); + // 예약 실패 체크 + if (null == reserved) + { + string errMsg = $"Failed to Reservation Enter to server !!! : {serverName} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.FailedToReservationEnter, errMsg); + Log.getLogger().error(result.toBasicString()); + + return (result, serverConnectInfo, businessLogs); + } + //========================================= + + //========================================= + // 이동 가능 여부 체크 + var locationAction = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(locationAction, + () => $"location_action is null !!! - {player.toBasicString()}"); + + // 팀아이디 설정 추가 + result = await locationAction.tryMoveToBattleIndun(instanceRoomInfo, _option.GameTeamId); + if (result.isFail()) + { + string errMsg = $"Failed to tryMoveToIndun() !!! : {result.toBasicString()}"; + Log.getLogger().error(errMsg); + return (result, serverConnectInfo, businessLogs); + } + //========================================= + + //========================================= + // 이동할 곳의 버프 적용 + var buffAction = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(buffAction, () => $"buff_action is null !!! - {player.toBasicString()}"); + result = await buffAction.MoveServer(instanceRoomInfo.InstancePlaceType); + if (result.isFail()) + { + string errMsg = $"Failed to MoveServer() !!! : {result.toBasicString()}"; + Log.getLogger().error(errMsg); + + return (result, serverConnectInfo, businessLogs); + } + + //======================================== + // 로그인 캐시 확인 및 이동할 서버에 대한 otp 발행 + (result, LoginCache.ReservationToSwitchServer? reservedToSwitchServer) = + await ServerConnectionSwitchHelper.startServerSwitch(player, _serverLogic.getRedisConnector(), + serverName); + if (result.isFail() || null == reservedToSwitchServer) + { + string errMsg = $"Failed to startServerSwitch() !!! : {result.toBasicString()}"; + Log.getLogger().error(errMsg); + + return (result, serverConnectInfo, businessLogs); + } + + //========================================= + // 이동할 서버에 opt 정보 저장 + var gameLoginAction = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(gameLoginAction, + () => $"game_login_action is null !!! - {player.toBasicString()}"); + + LoginCache? loginCache = gameLoginAction.getLoginCacheRequest()?.getLoginCache(); + NullReferenceCheckHelper.throwIfNull(loginCache, + () => $"login_cache is null !! player: {player.toBasicString()}"); + loginCache.ReservedToSwitchServer = reservedToSwitchServer; + + // opt 전달 + serverConnectInfo.Otp = reservedToSwitchServer.OneTimeKey; + + //배틀 인스턴스 같은 경우는 실제 인스턴스 진입시에 Pos를 정해야 하기 때문에 여기서는 빈값으로 처리 + //var start_pos = MapManager.Instance.GetBattleInstnaceStartPos(indun_meta_data.RoomFile, instance_room_id); + PositionLogInfo arrivalPositionLogInfo = PositionBusinessLogHelper.toPositionLogInfo(PositionMoveType.Arrival, + serverName, instanceRoomInfo.roomId, MapFileType.Instance, instanceRoomInfo.InstanceId, new()); + var arrivalPositionBusinessLog = new PositionBusinessLog(arrivalPositionLogInfo); + businessLogs.Add(arrivalPositionBusinessLog); + + Log.getLogger() + .debug($"[MatchIndunRoomOk] MatchRoomId:{instanceRoomInfo.roomId}: {JsonConvert.SerializeObject(serverConnectInfo)}"); + + return (result, serverConnectInfo, businessLogs); + } +} + diff --git a/GameServer/Contents/Match/MatchManager.Log.cs b/GameServer/Contents/Match/MatchManager.Log.cs new file mode 100644 index 0000000..779980a --- /dev/null +++ b/GameServer/Contents/Match/MatchManager.Log.cs @@ -0,0 +1,124 @@ +using GameServer.Match; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer; + +/// +/// 비즈니스 로그 담당 +/// +public partial class MatchManager +{ + private void BusinessLogMatchReserve(Player player, MatchUserInfo matchUser, MatchPoolInfo matchPoolInfo) + { + try + { + var matchPoolLog = new MatchPoolLog + { + GameModeId = matchPoolInfo.GameModeId, + EventId = matchPoolInfo.GameEventId, + Region = matchPoolInfo.Region, + MatchGroupId = matchPoolInfo.MatchGroupId, + }; + + var logAction = new LogActionEx(LogActionType.MatchReserve); + var log = new BusinessLogInvoker(LogDomainType.MatchUser, + logInvoker => new MatchUserLog(logInvoker) + { + MatchPool = matchPoolLog, MatchStatus = matchUser.Status, + }); + BusinessLogger.collectLogs(logAction, player, [log]); + } + catch (Exception ex) + { + Log.getLogger().error($"LogMatchReserve => {ex}"); + } + } + + private void BusinessLogMatchCancel(Player player, PlayerMatchInfo playerMatchInfo) + { + try + { + var matchPoolInfo = MatchPoolInfo.ConvertByMatchGroupId(playerMatchInfo.MatchGroupId); + var matchPoolLog = new MatchPoolLog + { + GameModeId = matchPoolInfo.GameModeId, + EventId = matchPoolInfo.GameEventId, + Region = matchPoolInfo.Region, + MatchGroupId = matchPoolInfo.MatchGroupId, + }; + + var logAction = new LogActionEx(LogActionType.MatchCancel); + var log = new BusinessLogInvoker(LogDomainType.MatchUser, + logInvoker => new MatchUserLog(logInvoker) + { + MatchPool = matchPoolLog, + MatchStatus = playerMatchInfo.MatchStatusInfo.Status + }); + BusinessLogger.collectLogs(logAction, player, [log]); + } + catch (Exception ex) + { + Log.getLogger().error($"LogMatchCancel => {ex}"); + } + } + + private void BusinessLogMatchResultSuccess(Player player, Result result, MatchPoolInfo matchPoolInfo, + PlayerMatchInfo playerMatchInfo, string roomId, string teamId, + ServerConnectInfo? connectInfo) + { + try + { + var matchPoolLog = new MatchPoolLog + { + GameModeId = matchPoolInfo.GameModeId, + EventId = matchPoolInfo.GameEventId, + Region = matchPoolInfo.Region, + MatchGroupId = matchPoolInfo.MatchGroupId, + }; + var logAction = new LogActionEx(LogActionType.MatchResult); + var log = new BusinessLogInvoker(LogDomainType.MatchUser, + logInvoker => new MatchUserLog(logInvoker) + { + MatchPool = matchPoolLog, + MatchStatus = playerMatchInfo.MatchStatusInfo.Status, + RoomId = roomId, + TeamId = teamId, + ConnectInfo = connectInfo, + ErrorCode = result.ErrorCode, + }); + BusinessLogger.collectLogs(logAction, player, [log]); + } + catch (Exception ex) + { + Log.getLogger().error($"LogMatchResultSuccess => {ex}"); + } + } + + private void BusinessLogMatchResultFail(Player player, MatchPoolInfo matchPoolInfo, PlayerMatchInfo playerMatchInfo) + { + try + { + var matchPoolLog = new MatchPoolLog + { + GameModeId = matchPoolInfo.GameModeId, + EventId = matchPoolInfo.GameEventId, + Region = matchPoolInfo.Region, + MatchGroupId = matchPoolInfo.MatchGroupId, + }; + var logAction = new LogActionEx(LogActionType.MatchResult); + var log = new BusinessLogInvoker(LogDomainType.MatchUser, + logInvoker => new MatchUserLog(logInvoker) + { + MatchPool = matchPoolLog, MatchStatus = playerMatchInfo.MatchStatusInfo.Status, + }); + BusinessLogger.collectLogs(logAction, player, [log]); + } + catch (Exception ex) + { + Log.getLogger().error($"LogMatchResultFail => {ex}"); + } + } +} diff --git a/GameServer/Contents/Match/MatchManager.cs b/GameServer/Contents/Match/MatchManager.cs new file mode 100644 index 0000000..6735010 --- /dev/null +++ b/GameServer/Contents/Match/MatchManager.cs @@ -0,0 +1,310 @@ +using GameServer.Contents.GameMode; +using GameServer.Match; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using ServerCore; +using ServerBase; +using ServerCommon; +using ServerCommon.GameMode; +using Enum = System.Enum; + +namespace GameServer; + +/// +/// The MatchManager class is a singleton responsible for managing matchmaking operations, +/// player reservations, cancellations, match results, backend updates, and event handling in the game server. +/// +public partial class MatchManager : Singleton +{ + private readonly GameServerLogic _serverLogic; + private readonly RabbitMQ4Game? _mq; + private readonly RabbitMqRpc _mqRpc; + + private readonly MatchRouter _matchRouter = new(); + + private readonly MatchRoomEventHandler _matchRoomEventHandler; + public RabbitMqRpc GetMqRpc() => _mqRpc; + public MatchRoomEventHandler RoomEventHandler => _matchRoomEventHandler; + + private readonly ServiceType _serviceType; + private readonly GameEventManager _gameEventManager; + public GameEventManager GameEventManager => _gameEventManager; + + public MatchManager() + { + _serverLogic = GameServerApp.getServerLogic(); // as GameServerLogic; + Enum.TryParse(_serverLogic.getServerConfig().ServiceType, out ServiceType serviceType); + + _serviceType = serviceType; + NullReferenceCheckHelper.throwIfNull(_serverLogic, () => $"server_logic is null !!!"); + var mq = GameServerApp.getServerLogic().getRabbitMqConnector(); + _mq = mq as RabbitMQ4Game; + NullReferenceCheckHelper.throwIfNull(_mq, () => $"RabbitMQ4Game is null !!!"); + _mqRpc = new RabbitMqRpc(mq); + _matchRoomEventHandler = new MatchRoomEventHandler(this, Log.getLogger()); + _gameEventManager = new GameEventManager(_serverLogic.getDynamoDbClient()); + } + + // 매칭 예약 요청 처리 + public async Task<(Result, MatchStatusInfo?)> ReserveAsync(Player player, int gameModeId, int eventMetaId, + string region, int pingMs, int instanceId) + { + var serverName = _serverLogic.getServerName(); + + Result result; + MatchStatusInfo? matchStatusInfo = null; + if (string.IsNullOrEmpty(region)) + { + region = "ASIA"; + } + + try + { + // dev qa 환경에서는 0번 이벤트 허용 + if (eventMetaId == 0 && _serviceType is ServiceType.Stage or ServiceType.Live) + { + MatchGuard.InvalidMatchEventTime(eventMetaId, player.getUserGuid()); + } + + // 이벤트 시간 적용 - 이벤트 시간이 아니면 매칭 예약 실패 + if (eventMetaId > 0 && !_gameEventManager.IsValidGameEventTime(eventMetaId)) + { + MatchGuard.InvalidMatchEventTime(eventMetaId, player.getUserGuid()); + } + + // 플레이 페널티 적용 + result = await CheckValidGamePlayLimitAsync(player, gameModeId); + MatchGuard.ResultFailException(result); + + var matchPoolInfo = new MatchPoolInfo(gameModeId, region, eventMetaId); + var matchUserInfo = MakeMatchUserInfo(player, serverName, instanceId, matchPoolInfo); + + // 응답 처리 추적을 위한 아이디 생성 + string traceId = MakeTraceId(); + var serverMessage = new ServerMessage + { + ReqMatchReserve = new ServerMessage.Types.GS2MS_REQ_MATCH_RESERVE + { + MatchUserInfo = matchUserInfo, TraceId = traceId + } + }; + + var matchPoolId = matchPoolInfo.ToString(); + var matchServerName = _matchRouter.getBackend(matchPoolId); + + // 메시지를 전송하고, mq에서 응답을 받을 때까지 대기 + (result, IMessage? ackMessage) = await _mqRpc.reqMessageWaitOnAck(matchServerName, serverMessage, traceId); + // mq의 응답이 없으면 에러 처리 + MatchGuard.ResultFailException(result); + + var ackServerMessage = ackMessage as ServerMessage; + MatchGuard.NotNull(ackServerMessage); + if (ackServerMessage.AckMatchReserve.ErrorCode != ServerErrorCode.Success) + { + result.setFail(ackServerMessage.AckMatchReserve.ErrorCode); + MatchGuard.ResultFailException(result); + } + + matchStatusInfo = ackServerMessage.AckMatchReserve.MatchStatusInfo; + MatchGuard.NotNull(matchStatusInfo); + UpdateMatchUserStatus(player, MatchStatusType.Reserved); + + // 비즈니스 로그 + BusinessLogMatchReserve(player, matchUserInfo, matchPoolInfo); + } + catch (ResultException ex) + { + result = ex.Result; + } + + return (result, matchStatusInfo); + } + + + // 매칭 취소 요청 처리 + public async Task CancelAsync(Player player, PlayerMatchInfo playerMatchInfo, + MatchCancelType matchCancelType) + { + Result result = new(); + try + { + var userGuid = player.getUserGuid(); + // 응답 처리 추적을 위한 아이디 생성 + var traceId = MakeTraceId(); + var reqMessage = new ServerMessage + { + ReqMatchCancel = new ServerMessage.Types.GS2MS_REQ_MATCH_CANCEL + { + ServerName = _serverLogic.getServerName(), + UserGuid = userGuid, + MatchCancelType = matchCancelType, + TraceId = traceId + } + }; + + string matchGroupId = playerMatchInfo.MatchGroupId; + string matchServerName = _matchRouter.getBackend(matchGroupId); + + // 이 응답은 mq에서 수신하면 대기가 풀림 + (result, IMessage? ackServerMessageTemp) = + await _mqRpc.reqMessageWaitOnAck(matchServerName, reqMessage, traceId); + MatchGuard.ResultFailException(result); + + var ackServerMessage = ackServerMessageTemp as ServerMessage; + MatchGuard.NotNull(ackServerMessage); + + if (ackServerMessage.AckMatchCancel.ErrorCode != ServerErrorCode.Success) + { + result.setFail(ServerErrorCode.MatchUserNotFound, $"MatchUserNotFound => user_guid:{userGuid}"); + MatchGuard.ResultFailException(result); + } + + var status = matchCancelType == MatchCancelType.Disconnected + ? MatchStatusType.CancelByDisconnect + : MatchStatusType.Cancel; + UpdateMatchUserStatus(player, status); + + BusinessLogMatchCancel(player, playerMatchInfo); + } + catch (ResultException ex) + { + result = ex.Result; + } + + return result; + } + + public void SendCheatCmd(string[] args) + { + var message = new ServerMessage { NtfMatchCheatCmd = new ServerMessage.Types.GS2MS_NTF_MATCH_CHEAT_CMD() }; + message.NtfMatchCheatCmd.Args.AddRange(args); + SendToMatchServer(message); + } + + internal void SendToMatchServer(ServerMessage message) + { + _matchRouter.GetBackends().ToList().ForEach(backend => + { + _mq?.SendMessage(backend, message); + }); + } + + public Result UpdateMatchStatus(Player player, MatchStatusInfo matchStatusInfo) + { + // 에러 + var result = new Result(); + var matchAction = player.getEntityAction(); + matchAction.PlayerMatchInfo.MatchStatusInfo = matchStatusInfo; + return result; + } + + public async Task<(Result, ServerConnectInfo? connectInfo)> ProcessMatchResultAsync(Player player, + MatchPoolInfo matchPoolInfo, PlayerMatchInfo playerMatchInfo, string roomId, string teamId) + { + var userGuid = player.getUserGuid(); + var matchJoinOption = new MatchJoinOptions(userGuid, roomId, teamId, matchPoolInfo.GameModeId); + var joinGameRoomReserve = new MatchJoinGameRoomReserve(matchJoinOption); + var (result, connectInfo) = await joinGameRoomReserve.RoomReserve(); + BusinessLogMatchResultSuccess(player, result, matchPoolInfo, playerMatchInfo, roomId, teamId, connectInfo); + return (result, connectInfo); + } + + public void ProcessMatchResultFailAsync(Player player, MatchPoolInfo matchPoolInfo, PlayerMatchInfo playerMatchInfo) + { + BusinessLogMatchResultFail(player, matchPoolInfo, playerMatchInfo); + } + + private string MakeTraceId() + { + return Guid.NewGuid().ToString("N"); + } + + private void UpdateMatchUserStatus(Player player, MatchStatusType matchStatusType) + { + var matchAction = player.getEntityAction(); + matchAction.PlayerMatchInfo.MatchStatusInfo.Status = matchStatusType; + } + + // getGameModePlayPenalty + private async Task CheckValidGamePlayLimitAsync(Player player, int gameModeId) + { + var result = new Result(); + // todo @heon 수정 예정 - 임시 코드 (GameMode 별로 처리해야 할 수 있다) + // 현재는 모든 게임 모드에 대해 동일한 횟수 적용 중 + MetaData.Instance.Meta.GameModeMetaTable.GameModeMetaDataListbyId.TryGetValue(gameModeId, out var gameModeData); + if (gameModeData == null) + { + return result.Fail(ServerErrorCode.MatchInvalidGameModeId); + } + + // 게임 플레이 패널티 적용 중 이라 입장 불가 + var action = player.getEntityAction(); + var gameModePlayPenalty = await action.getGameModePlayPenalty(gameModeData.GameModeType); + if (gameModePlayPenalty.m_penalty_end_time >= DateTime.UtcNow) + { + return result.Fail(ServerErrorCode.MatchCantCausePlayPenaltyTime); + } + + // 일일 매칭 횟수 제한 처리 + var gameModeMatchCount = await action.getGameModeMatchRestriction(gameModeData.GameModeType); + MetaData.Instance.m_game_mode_all_metas.TryGetValue(gameModeId, out GameModeAllMeta? gameModeAllMetaData); + MatchGuard.NotNull(gameModeAllMetaData, () => $"해당 GameMode의 StartMeta 데이터가 없음 => {gameModeId}"); + var gameModeStartMeta = gameModeAllMetaData.m_start_meta; + if (gameModeStartMeta.DailyJoinLimit == 0) + { + return result; + } + + if (gameModeMatchCount.m_current_matched_count >= gameModeStartMeta.DailyJoinLimit) + { + return result.Fail(ServerErrorCode.MatchCantCauseDailyJoinLimit); + } + + return result; + } + + /// + /// 매칭 상태에서 세션이 종료될 때 처리 + /// + /// + public void OnDisconnectEvent(Player player) + { + var matchAction = player.getEntityAction(); + + // 유저의 매칭 정보 갱신은 경쟁 상태가 아니므로 트랜잭션 처리를 하지 않음 + if (false == matchAction.PlayerMatchInfo.IsOnMatching()) + { + return; + } + + // 매칭 예약 상태면 매칭 취소를 요청한다. + _ = Task.Run(async () => + { + await CancelAsync(player, matchAction.PlayerMatchInfo, MatchCancelType.Disconnected); + }); + } + + //============================================ + // MatchRouter 업데이트 + public void UpdateBackend(string[] backends) + { + if (backends.Length > 0) + { + _matchRouter.updateBackends(backends); + } + } + + private MatchUserInfo MakeMatchUserInfo(Player player, string serverName, int instanceId, MatchPoolInfo matchPoolInfo) + { + return new MatchUserInfo + { + UserGuid = player.getUserGuid(), + Nickname = player.getUserNickname(), + ServerName = serverName, + MatchGroupId = matchPoolInfo.ToString(), + InstanceId = instanceId, + Status = MatchStatusType.None, + StartTime = DateTime.UtcNow.ToTimestamp(), + }; + } +} diff --git a/GameServer/Contents/Match/MatchRoomEventHandler.cs b/GameServer/Contents/Match/MatchRoomEventHandler.cs new file mode 100644 index 0000000..2f7de39 --- /dev/null +++ b/GameServer/Contents/Match/MatchRoomEventHandler.cs @@ -0,0 +1,111 @@ +using NLog; +using ServerBase; +using ServerCore; + +namespace GameServer; + +// 커스텀 delegate 정의 +public delegate void MatchGameMemberJoinDelegate(string userGuid, string roomId, string teamId); + +public class MatchRoomEventHandler +{ + private readonly MatchManager _matchManager; + + private readonly Dictionary _gameMemberJoinActions = new(); + + public MatchRoomEventHandler(MatchManager matchManager, ILogger logger) + { + _matchManager = matchManager; + } + + /// + /// 매칭 중인 게임의 상태 변경을 매치 서버로 송신 + /// + /// + /// + public void ChangeGameState(string roomId, MatchGameStateType state) + { + var message = new ServerMessage + { + NtfMatchChangeGameState = new ServerMessage.Types.GS2MS_NTF_MATCH_CHANGE_GAME_STATE + { + RoomId = roomId, State = state + } + }; + _matchManager.SendToMatchServer(message); + } + + /// + /// 게임 인던에서 호출 + /// 게임 중인 멤버가 퇴장한 경우 호출 + /// + /// + /// + public void GameMemberQuit(string roomId, string userGuid) + { + var message = new ServerMessage + { + NtfMatchGameQuit = new ServerMessage.Types.GS2MS_NTF_MATCH_GAME_QUIT + { + RoomId = roomId, UserGuid = userGuid + } + }; + _matchManager.SendToMatchServer(message); + Log.getLogger().debug($"GameMemberQuit RoomId:{roomId} UserGuid:{userGuid}"); + } + + /// + /// 게임 인던에서 호출 + /// 게임에 실제로 멤버가 입장하면 호출 + /// + /// + /// + public void GameMemberJoin(string roomId, string userGuid) + { + var message = new ServerMessage + { + NtfMatchGameJoin = new ServerMessage.Types.GS2MS_NTF_MATCH_GAME_JOIN + { + RoomId = roomId, UserGuid = userGuid + } + }; + _matchManager.SendToMatchServer(message); + Log.getLogger().debug($"GameMemberJoin RoomId:{roomId} UserGuid:{userGuid}"); + } + + /// + /// 특정 게임 룸에 매칭 된 유저가 입장 예정이라는 정보를 알림 + /// 게임 시작 전 대기 상태라면 이 유저의 입장을 대기한 후에 게임 시작 + /// + /// + /// + public void AddGameMemberJoinReserveAction(string roomId, MatchGameMemberJoinDelegate action) + { + if (!_gameMemberJoinActions.TryAdd(roomId, action)) + { + Log.getLogger().error($"_gameMemberJoinActions.TryAdd() failed !!! - roomId : {roomId}가 존재함"); + } + } + + public void DeleteGameMemberJoinReserveAction(string roomId) + { + _gameMemberJoinActions.Remove(roomId); + } + + //=============================================================================== + + /// + /// 게임 서버의 매칭 시스템에서 호출 + /// 게임 중 난입을 인던 게임 룸에 알림 + /// + /// + /// + /// + public void GameMemberJoinReserve(string userGuid, string roomId, string teamId) + { + if (_gameMemberJoinActions.TryGetValue(roomId, out var gameMemberJoinAction)) + { + gameMemberJoinAction.Invoke(userGuid, roomId, teamId); + } + } +} diff --git a/GameServer/Contents/Match/PacketHandler/MatchCancelPacketHandler.cs b/GameServer/Contents/Match/PacketHandler/MatchCancelPacketHandler.cs new file mode 100644 index 0000000..bfa99b9 --- /dev/null +++ b/GameServer/Contents/Match/PacketHandler/MatchCancelPacketHandler.cs @@ -0,0 +1,93 @@ +using GameServer.Match; +using Google.Protobuf; +using static ClientToGameRes.Types; +using ServerCore; +using ServerBase; + +namespace GameServer.PacketHandler; + +[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_MATCH_CANCEL), + typeof(MatchCancelPacketHandler), typeof(GameLoginListener))] +public class MatchCancelPacketHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession entityWithSession, IMessage recvMessage) + { + var player = entityWithSession as Player; + ArgumentNullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + var req_msg = recvMessage as ClientToGame; + ArgumentNullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + var request = req_msg.Request.ReqMatchCancel; + ArgumentNullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {player.toBasicString()}"); + var matchAction = player.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(matchAction, () => $"matchAction is null !!! - {player.toBasicString()}"); + + var errorCode = ServerErrorCode.Success; + + Func> fn = async delegate() + { + Result result = await MatchManager.It.CancelAsync(player, matchAction.PlayerMatchInfo, + MatchCancelType.Normal); + return result; + }; + + var result = + await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "MATCH_CANCEL", fn); + if (result.isFail()) + { + var errMsg = $"[MATCH_CANCEL] ACK return error : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(errMsg); + if (result.ErrorCode == ServerErrorCode.MqResponseTimeout) + { + errorCode = ServerErrorCode.MatchCancelFail; + } + } + + send_GS2C_ACK_GAME_MATCH_CANCEL(player, errorCode); + return result; + } + + public override async Task onProcessPacketException(ISession entityWithSession, IMessage recvMessage + , Result errorResult) + { + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! - {entityWithSession.toBasicString()}"); + + send_GS2C_ACK_GAME_MATCH_CANCEL(player, ServerErrorCode.MatchCancelFail); + var errMsg = $"[MATCH_CANCEL] ACK Exception : {errorResult.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(errMsg); + await Task.CompletedTask; + } + + // private static bool send_GS2C_ACK_GAME_MATCH_CANCEL(Player player, Result result) + // { + // var ack_packet = new ClientToGame(); + // ack_packet.Response = new ClientToGameRes(); + // + // ack_packet.Response.ErrorCode = result.ErrorCode; + // + // var ack_msg = new GS2C_ACK_MATCH_CANCEL(); + // ack_packet.Response.AckMatchCancel = ack_msg; + // + // if (result.isSuccess()) + // { + // // ack_msg.CommonResult = player.getCommonResult(); + // } + // + // if (false == GameServerApp.getServerLogic().onSendPacket(player, ack_packet)) + // { + // return false; + // } + // + // Log.getLogger().Info($"[MATCH_CANCEL ACK] {result.ErrorCode} - {player.toBasicString()}"); + // return true; + // } + + private static bool send_GS2C_ACK_GAME_MATCH_CANCEL(Player player, ServerErrorCode errorCode) + { + var ackPacket = new ClientToGame { Response = new ClientToGameRes { ErrorCode = errorCode } }; + var ackMsg = new GS2C_ACK_MATCH_CANCEL(); + ackPacket.Response.AckMatchCancel = ackMsg; + + return GameServerApp.getServerLogic().onSendPacket(player, ackPacket); + } +} diff --git a/GameServer/Contents/Match/PacketHandler/MatchNotifyHelper.cs b/GameServer/Contents/Match/PacketHandler/MatchNotifyHelper.cs new file mode 100644 index 0000000..40f7965 --- /dev/null +++ b/GameServer/Contents/Match/PacketHandler/MatchNotifyHelper.cs @@ -0,0 +1,28 @@ + +using ServerBase; +using static ClientToGameMessage.Types; +namespace GameServer.PacketHandler; + +/// +/// A static helper class for managing and sending match region notification information to players. +/// +public static class MatchNotifyHelper +{ + /// + /// 클라이언트에서 참조하는 게임 지역 핑 정보 처리 용 데이터 + /// TODO Config로 변경해야할 듯 + /// + /// + /// + public static Task send_S2C_NTF_MATCH_REGION_INFO(this Player player, ServerConfig serverConfig) + { + // TODO CONFIG의 값을 적용할 것 + var notiMessage = new ClientToGame(); + notiMessage.Message = new(); + var message = new GS2C_NTF_MATCH_REGION_INFO(); + message.MatchRegionInfos.AddRange(serverConfig.MatchRegionInfos.ToArray()); + notiMessage.Message.NtfMatchRegionInfo = message; + GameServerApp.getServerLogic().onSendPacket(player, notiMessage); + return Task.FromResult(new Result()); + } +} diff --git a/GameServer/Contents/Match/PacketHandler/MatchReservePacketHandler.cs b/GameServer/Contents/Match/PacketHandler/MatchReservePacketHandler.cs new file mode 100644 index 0000000..61a3cc8 --- /dev/null +++ b/GameServer/Contents/Match/PacketHandler/MatchReservePacketHandler.cs @@ -0,0 +1,92 @@ +using Google.Protobuf; +using ServerCore; +using ServerBase; +using ServerCommon; +using static ClientToGameRes.Types; + +namespace GameServer.PacketHandler; + +[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_MATCH_RESERVE), + typeof(MatchReservePacketHandler), typeof(GameLoginListener))] +public class MatchReservePacketHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession entityWithSession, IMessage recvMessage) + { + var player = entityWithSession as Player; + ArgumentNullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + var reqMsg = recvMessage as ClientToGame; + ArgumentNullReferenceCheckHelper.throwIfNull(reqMsg, () => $"req_msg is null !!! - {player.toBasicString()}"); + ClientToGameReq.Types.C2GS_REQ_MATCH_RESERVE? request = reqMsg.Request.ReqMatchReserve; + ArgumentNullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {player.toBasicString()}"); + + var errorCode = ServerErrorCode.Success; + MatchStatusInfo? matchStatusInfo = null; + + int gameModeId = request.GameModeId; + string? region = request.RegionName; + int pingMs = request.RegionPingMs; + int eventId = request.EventId; + int instanceId = request.InstanceId; + Func> fn = async delegate() + { + (Result result, matchStatusInfo) = + await MatchManager.It.ReserveAsync(player, gameModeId, eventId, region, pingMs, instanceId); + return result; + }; + + var result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "MATCH_RESERVE", fn); + if (result.isFail()) + { + var errMsg = $"[MATCH_RESERVE] ACK return error : {result.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(errMsg); + if (result.ErrorCode == ServerErrorCode.MqResponseTimeout) + { + errorCode = ServerErrorCode.MatchReserveFail; + } + } + + send_GS2C_ACK_GAME_MATCH_RESERVE(player, errorCode, matchStatusInfo); + return result; + } + + // 예외 발생 시 처리 + public override async Task onProcessPacketException(ISession entityWithSession, IMessage recvMessage + , Result errorResult) + { + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! - {entityWithSession.toBasicString()}"); + + send_GS2C_ACK_GAME_MATCH_RESERVE(player, ServerErrorCode.MatchReserveFail); + var errMsg = $"[MATCH_RESERVE] ACK Exception : {errorResult.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(errMsg); + await Task.CompletedTask; + } + + // private static bool send_GS2C_ACK_GAME_MATCH_RESERVE(Player player, Result result, MatchStatusInfo? matchStatusInfo = null!) + // { + // var ackPacket = new ClientToGame { Response = new ClientToGameRes { ErrorCode = result.ErrorCode } }; + // + // var ackMsg = new GS2C_ACK_MATCH_RESERVE(); + // ackPacket.Response.AckMatchReserve = ackMsg; + // if (matchStatusInfo != null) + // { + // ackMsg.MatchStatus = matchStatusInfo; + // } + // return GameServerApp.getServerLogic().onSendPacket(player, ackPacket); + // } + + private static bool send_GS2C_ACK_GAME_MATCH_RESERVE(Player player, ServerErrorCode errorCode, + MatchStatusInfo? matchStatusInfo = null!) + { + var ackPacket = new ClientToGame { Response = new ClientToGameRes { ErrorCode = errorCode } }; + + var ackMsg = new GS2C_ACK_MATCH_RESERVE(); + if (matchStatusInfo != null) + { + ackMsg.MatchStatus = matchStatusInfo; + } + + ackPacket.Response.AckMatchReserve = ackMsg; + return GameServerApp.getServerLogic().onSendPacket(player, ackPacket); + } +} diff --git a/GameServer/Contents/MyHome/Action/MyhomeAgentAction.cs b/GameServer/Contents/MyHome/Action/MyhomeAgentAction.cs index 35d3c87..0ce4227 100644 --- a/GameServer/Contents/MyHome/Action/MyhomeAgentAction.cs +++ b/GameServer/Contents/MyHome/Action/MyhomeAgentAction.cs @@ -301,15 +301,6 @@ internal class MyhomeAgentAction : EntityActionBase } NullReferenceCheckHelper.throwIfNull(delete_myhome_business_log, () => $"delete_myhome_business_log is null !!!"); - result = await myhome_action.tryDeleteMyhomeUgcInfoFolderFileFromS3(); - if (result.isFail()) - { - err_msg = $"Failed to tryDeleteMyhomeUgcInfoFolderFileFromS3() !!! : {result.toBasicString()} : {this.getTypeName()}"; - Log.getLogger().error(err_msg); - - return (result, null, null); - } - m_myhomes.Remove(myhomeGuid, out _); business_logs.AddRange(delete_myhome_business_log); diff --git a/GameServer/Contents/MyHome/PacketHandler/DeleteMyhomePacketHandler.cs b/GameServer/Contents/MyHome/PacketHandler/DeleteMyhomePacketHandler.cs index 8550efa..0244b21 100644 --- a/GameServer/Contents/MyHome/PacketHandler/DeleteMyhomePacketHandler.cs +++ b/GameServer/Contents/MyHome/PacketHandler/DeleteMyhomePacketHandler.cs @@ -103,6 +103,15 @@ internal class DeleteMyhomePacketHandler : PacketRecvHandler return result; } + result = await MyhomeHelper.deleteMyhomeUgcInfoFolderFile(request.MyhomeGuid); + if (result.isFail()) + { + err_msg = $"Failed to deleteMyhomeUgcInfoFolderFile() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); NullReferenceCheckHelper.throwIfNull(found_transaction_runner, () => $"found_transaction_runner is null !!! - {player.toBasicString()}"); diff --git a/GameServer/Contents/Quest/Action/QuestTaskUpdateAction.cs b/GameServer/Contents/Quest/Action/QuestTaskUpdateAction.cs index dd8901c..2b3d441 100644 --- a/GameServer/Contents/Quest/Action/QuestTaskUpdateAction.cs +++ b/GameServer/Contents/Quest/Action/QuestTaskUpdateAction.cs @@ -143,9 +143,8 @@ namespace GameServer } return result; } - - - public Result taskUpdateConditionCheck(ref QuestTaskUpdateHandler questTaskUpdateHandler) + + public Result taskUpdateConditionCheck(ref QuestTaskUpdateHandler questTaskUpdateHandler, bool isCheckClientSide = true) { ArgumentNullReferenceCheckHelper.throwIfNull(questTaskUpdateHandler.m_quest_meta_info, () => $"questTaskUpdateHandler.m_quest_meta_info is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(questTaskUpdateHandler.m_quest, () => $"questTaskUpdateHandler.m_quest is null !!!"); @@ -194,8 +193,8 @@ namespace GameServer EQuestEventTargetType targetType = EnumHelper.convertEnumTypeAndValueStringToEnum(questEventInfo.EventTarget, EQuestEventTargetType.NONE); EQuestEventNameType nameType = EnumHelper.convertEnumTypeAndValueStringToEnum(questEventInfo.EventName, EQuestEventNameType.NONE); - - if (false == checkClientSideEvent(targetType, nameType)) + + if (isCheckClientSide && false == checkClientSideEvent(targetType, nameType)) { string err_msg = $"scriptDataList does not match EQuestEventTargetType questId : {quest_id}, targetType : {targetType.ToString()}, nameType : {nameType.ToString()}"; result.setFail(ServerErrorCode.QuestInvalidValue, err_msg); @@ -230,6 +229,7 @@ namespace GameServer if (target.Equals(EQuestEventTargetType.BEACONDIALOGUE) && nameType.Equals(EQuestEventNameType.RECEIVED)) return true; if (target.Equals(EQuestEventTargetType.WAITING)) return true; if (target.Equals(EQuestEventTargetType.PROP) && nameType.Equals(EQuestEventNameType.SWITCHED)) return true; + if (target.Equals(EQuestEventTargetType.PHOTOMODE) && nameType.Equals(EQuestEventNameType.CAPTURE)) return true; return false; } diff --git a/GameServer/Contents/Quest/PacketHandler/QuestRewardPacketHandler.cs b/GameServer/Contents/Quest/PacketHandler/QuestRewardPacketHandler.cs index d2d2bfe..3a8279a 100644 --- a/GameServer/Contents/Quest/PacketHandler/QuestRewardPacketHandler.cs +++ b/GameServer/Contents/Quest/PacketHandler/QuestRewardPacketHandler.cs @@ -1,4 +1,5 @@  +using GameServer.EventInvokers; using ServerCore; using ServerBase; using ServerCommon; @@ -119,6 +120,7 @@ public class QuestRewardPacketHandler : PacketRecvHandler await quest_reward_action.normalQuestCheckInitialize(); await player.send_GS2C_NTF_UGQ_DAILY_REWARD(); + await player.getGameEventCollector().collectEvent(new GameEventInvokerQuestRewarded(player, quest_id)); return result; } diff --git a/GameServer/Contents/QuestUGQ/Action/UgqAssignAction.cs b/GameServer/Contents/QuestUGQ/Action/UgqAssignAction.cs index e10f0ae..a874b76 100644 --- a/GameServer/Contents/QuestUGQ/Action/UgqAssignAction.cs +++ b/GameServer/Contents/QuestUGQ/Action/UgqAssignAction.cs @@ -183,7 +183,7 @@ public class UgqAssignAction : EntityActionBase var language_type = account_attribute.LanguageType; //퀘스트 assign 후 api 서버 쪽으로 set-quest-accepted 전달 - var api_result = await ugq_api_manager.setUgqQuestAccepted(user_guid, questId, questRevision, language_type); //임시 테스트 를 위해서 TestQeustData를 가져온다. + var api_result = await ugq_api_manager.setUgqQuestAccepted(user_guid, questId, questRevision, language_type); if(api_result.isFail()) { //로그만 남긴다. diff --git a/GameServer/Contents/Reward/RewardBase.cs b/GameServer/Contents/Reward/RewardBase.cs index 579ce2f..702e6af 100644 --- a/GameServer/Contents/Reward/RewardBase.cs +++ b/GameServer/Contents/Reward/RewardBase.cs @@ -13,11 +13,6 @@ namespace GameServer private Player m_owner; private string m_user_guid = string.Empty; private List m_rewards = new(); - //List m_currency_invokers = new(); - //List m_item_invokers = new(); - //List m_currency_max_noties = new(); - //private List obtained_reward = new(); - private List m_rewarded_moneys = new(); private List m_rewarded_items = new(); @@ -25,20 +20,12 @@ namespace GameServer { m_owner = owner; m_user_guid = user_guid; - - //m_currency_invokers = new(); - //m_item_invokers = new(); - //m_currency_max_noties = new(); - //m_rewarda = rewards; } public RewardBase(Player owner, string user_guid, List rewards) { m_owner = owner; m_user_guid = user_guid; - //m_currency_invokers = new(); - //m_item_invokers = new(); - //m_currency_max_noties = new(); m_rewards = rewards; } diff --git a/GameServer/Contents/Reward/RewardHelper.cs b/GameServer/Contents/Reward/RewardHelper.cs new file mode 100644 index 0000000..fd75336 --- /dev/null +++ b/GameServer/Contents/Reward/RewardHelper.cs @@ -0,0 +1,53 @@ +using MetaAssets; +using ServerCommon; + +namespace GameServer.Contents.Reward; + +public static class RewardHelper +{ + public static MetaAssets.Reward convertRewardMetaToMetaAssetreward(RewardMetaData rewards) + { + RewardMutable reward_metable = new(); + reward_metable.Item = new(); + reward_metable.Item.Id = rewards.Reward.Item.Id; + reward_metable.Item.Count = rewards.Reward.Item.Count; + + MetaAssets.Reward reward = new MetaAssets.Reward(reward_metable); + + return reward; + } + + public static List convertRewardMetaToMetaAssetrewards(List rewardList) + { + var ret_rewards = new List(); + foreach (var rewards in rewardList) + { + if (rewards.Reward.Item is not null) + { + var reward = ItemMetaHelper.convertFromItemIdToReward(rewards.Reward.Item.Id, rewards.Reward.Item.Count); + ret_rewards.Add(reward); + } + else if (rewards.Reward.Currency is not null) + { + var currency_reward = convertCurrencyRewardMetaToMetaAssetreward(rewards.Reward.Currency.Id, rewards.Reward.Currency.Value); + + ret_rewards.Add(currency_reward); + } + } + + return ret_rewards; + } + + private static MetaAssets.Reward convertCurrencyRewardMetaToMetaAssetreward(int currencyId, double value) + { + CurrencyRewardMutable currency_reward_mutable = new(); + currency_reward_mutable.Id = currencyId; + currency_reward_mutable.Value = value; + + RewardMutable reward_mutable = new(); + reward_mutable.Currency = currency_reward_mutable; + + MetaAssets.Reward reward = new(reward_mutable); + return reward; + } +} \ No newline at end of file diff --git a/GameServer/Contents/SeasonPass/Action/SeasonPassAction.cs b/GameServer/Contents/SeasonPass/Action/SeasonPassAction.cs index b42ebb4..2bfc691 100644 --- a/GameServer/Contents/SeasonPass/Action/SeasonPassAction.cs +++ b/GameServer/Contents/SeasonPass/Action/SeasonPassAction.cs @@ -1,4 +1,5 @@ -using GameServer.PacketHandler; +using GameServer.EventInvokers; +using GameServer.PacketHandler; using MetaAssets; using ServerCommon; using ServerCore; using ServerBase; @@ -284,7 +285,11 @@ namespace GameServer { err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); + return result; } + + await player.getGameEventCollector().collectEvent(new GameEventInvokerSeasonPassRewarded(player)); + return result; } diff --git a/GameServer/Contents/Shop/Action/ShopAction.cs b/GameServer/Contents/Shop/Action/ShopAction.cs index 87f147d..92a3d2c 100644 --- a/GameServer/Contents/Shop/Action/ShopAction.cs +++ b/GameServer/Contents/Shop/Action/ShopAction.cs @@ -156,7 +156,7 @@ public class ShopAction : EntityActionBase private void setMyShopProduct(int shop_id, MyShopProduct product) => m_my_shop_products.TryAdd(shop_id, product); - public async Task<(Result result, GameServer.Item? changed_item, CurrencyType spent_currency_type, double spent_currency)> processRePurchase(MetaAssets.ItemMetaData item_data, SoldProduct sold) + public async Task<(Result result, List? changed_items, CurrencyType spent_currency_type, double spent_currency)> processRePurchase(MetaAssets.ItemMetaData item_data, SoldProduct sold) { var result = new Result(); var player = getOwner() as Player; @@ -175,8 +175,8 @@ public class ShopAction : EntityActionBase var item_attrib = sold.ItemDoc.getAttrib(); NullReferenceCheckHelper.throwIfNull(item_attrib, () => $"item_attrib is null !!! - {player?.toBasicString()}"); - (result, var changed_item) = await inventory_action.tryTakableToBag(sold.ItemDoc); - if(result.isFail() || null == changed_item) return (result, null, CurrencyType.None, 0); + (result, var changed_items) = await inventory_action.tryTakableToBagWithNewItem(sold.ItemDoc); + if(result.isFail() || changed_items?.Count <= 0) return (result, null, CurrencyType.None, 0); // 2. sold item list 갱신 var sold_action = player?.getEntityAction().getMySoldProduct().getEntityAction(); @@ -189,12 +189,12 @@ public class ShopAction : EntityActionBase return (result, null, CurrencyType.None, 0); } - result = await sold_action.updateSoldProduct(item_attrib.ItemGuid, -1 * item_attrib.ItemStackCount); + (result, _) = await sold_action.updateSoldProduct(item_attrib.ItemGuid, -1 * item_attrib.ItemStackCount); var check_currency_type = ShopHelper.checkCurrencyTypeFromCurrencyId(item_data.SellId); if (check_currency_type.result.isFail()) return (check_currency_type.result, null, CurrencyType.None, 0); - return (result, changed_item, check_currency_type.currencyType, item_data.SellPrice * item_attrib.ItemStackCount); + return (result, changed_items, check_currency_type.currencyType, item_data.SellPrice * item_attrib.ItemStackCount); } public async Task processSpent(CurrencyType spent_currency_type, double spent_currency) diff --git a/GameServer/Contents/Shop/Action/ShopSoldProductAction.cs b/GameServer/Contents/Shop/Action/ShopSoldProductAction.cs index 623e3d6..879d925 100644 --- a/GameServer/Contents/Shop/Action/ShopSoldProductAction.cs +++ b/GameServer/Contents/Shop/Action/ShopSoldProductAction.cs @@ -28,6 +28,8 @@ public class SoldProduct // ============================================================== public class ShopSoldProductAction : EntityActionBase { + private List m_sold_products { get; set; } + public ShopSoldProductAction(MySoldProduct owner) : base(owner) { m_sold_products = new List(); @@ -47,20 +49,24 @@ public class ShopSoldProductAction : EntityActionBase return; } - public async Task setSoldProduct(ITEM_GUID item_guid, ItemDoc itemDoc) + public async Task<(Result result, ushort sellCount)> setSoldProduct(ITEM_GUID item_guid, ItemDoc itemDoc, ushort deltaCount) { var result = new Result(); - + var is_exist = m_sold_products.Any(product => product.ItemGuid == item_guid); - if (is_exist) return result; + if (is_exist) + { + (result, var sellCount) = await updateSoldProduct(item_guid, deltaCount); + return (result, sellCount); + } var sold = new SoldProduct { ItemGuid = item_guid, ItemDoc = itemDoc}; m_sold_products.Add(sold); - return await Task.FromResult(result); + return (result, deltaCount); } - public async Task updateSoldProduct(ITEM_GUID item_guid, int delta_count) + public async Task<(Result result, ushort sellCount)> updateSoldProduct(ITEM_GUID item_guid, int delta_count) { var result = new Result(); var sold = findSoldProduct(item_guid); @@ -69,7 +75,7 @@ public class ShopSoldProductAction : EntityActionBase var err_msg = $"Fail to get item info : {nameof(updateSoldProduct)} - {item_guid}"; result.setFail(ServerErrorCode.NotFoundItemTableId, err_msg); Log.getLogger().error(err_msg); - return result; + return (result, 0); } var item_attrib = sold.ItemDoc.getAttrib(); @@ -79,12 +85,12 @@ public class ShopSoldProductAction : EntityActionBase if (delta <= 0) { deleteSoldProduct(item_guid); - return result; + return (result, 0); } item_attrib.ItemStackCount = (ushort)delta; - return await Task.FromResult(result); + return await Task.FromResult((result, item_attrib.ItemStackCount)); } private void deleteSoldProduct(ITEM_GUID item_guid) @@ -96,8 +102,6 @@ public class ShopSoldProductAction : EntityActionBase } } - private List m_sold_products { get; set; } - public List getSoldList() => m_sold_products; public SoldProduct? findSoldProduct(ITEM_GUID item_guid) => m_sold_products.FirstOrDefault(sold => sold.ItemGuid == item_guid); diff --git a/GameServer/Contents/Shop/Helper/ShopHelper.cs b/GameServer/Contents/Shop/Helper/ShopHelper.cs index 98a76df..a350366 100644 --- a/GameServer/Contents/Shop/Helper/ShopHelper.cs +++ b/GameServer/Contents/Shop/Helper/ShopHelper.cs @@ -167,6 +167,8 @@ public static class ShopHelper break; } + if (false == productInfo.Enable) continue; + var sub_attribute = new ShopProductTradingMeterSubAttribute { ProductId = productInfo.ID, @@ -217,6 +219,8 @@ public static class ShopHelper // 이미 추가되어 있으면 패스 var breakCheck = added_list.Any(randomProduct => randomProduct.ProductId == productInfo.ID); if (breakCheck) continue; + + if (false == productInfo.Enable) continue; weight += productInfo.Weight; diff --git a/GameServer/Contents/Shop/PacketHandler/RePurchaseItemPacketHandler.cs b/GameServer/Contents/Shop/PacketHandler/RePurchaseItemPacketHandler.cs index 13aae09..e463e4e 100644 --- a/GameServer/Contents/Shop/PacketHandler/RePurchaseItemPacketHandler.cs +++ b/GameServer/Contents/Shop/PacketHandler/RePurchaseItemPacketHandler.cs @@ -8,6 +8,7 @@ using ServerCommon; using ServerCommon.BusinessLogDomain; using MetaAssets; +using ITEM_GUID = System.String; namespace GameServer.PacketHandler; @@ -15,7 +16,7 @@ namespace GameServer.PacketHandler; [PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.RePurchaseItemReq), typeof(RePurchaseItemPacketHandler), typeof(GameLoginListener))] public class RePurchaseItemPacketHandler : PacketRecvHandler { - private static void send_S2C_ACK_SHOP_RE_PURCHASE_LIST(Player? owner, Result result, List? rePurchaseItems) + private static void send_S2C_ACK_SHOP_RE_PURCHASE_LIST(Player? owner, Result result, ITEM_GUID rePurchaseItemGuid, List? rePurchaseItems) { var ack_packet = new ClientToGame { @@ -73,6 +74,8 @@ public class RePurchaseItemPacketHandler : PacketRecvHandler IsIntroComplete = 1 }; ack_packet.Response.RePurchaseItemRes.CurrencyInfo = char_info; + + ack_packet.Response.RePurchaseItemRes.RepurchaseItemGuid = rePurchaseItemGuid; } GameServerApp.getServerLogic().onSendPacket(owner!, ack_packet); @@ -93,7 +96,7 @@ public class RePurchaseItemPacketHandler : PacketRecvHandler result.setFail(ServerErrorCode.InvalidArgument, err_msg); Log.getLogger().error(result.toBasicString()); - send_S2C_ACK_SHOP_RE_PURCHASE_LIST(entity_player, result, null); + send_S2C_ACK_SHOP_RE_PURCHASE_LIST(entity_player, result, ITEM_GUID.Empty, null); return result; } @@ -103,7 +106,7 @@ public class RePurchaseItemPacketHandler : PacketRecvHandler err_msg = $"Failed to runTransactionRunnerSafely()!!! : {result.toBasicString()} - {entity_player.toBasicString()}"; Log.getLogger().error(err_msg); - send_S2C_ACK_SHOP_RE_PURCHASE_LIST(entity_player, result, null); + send_S2C_ACK_SHOP_RE_PURCHASE_LIST(entity_player, result, ITEM_GUID.Empty, null); } return result; @@ -142,7 +145,7 @@ public class RePurchaseItemPacketHandler : PacketRecvHandler var process_re_purchase = await shop_action.processRePurchase(item_data, sold_item); if (process_re_purchase.result.isFail()) return process_re_purchase.result; - NullReferenceCheckHelper.throwIfNull(process_re_purchase.changed_item, () => $"process_re_purchase.changed_item is null !!! - {entity_player.toBasicString()}"); + NullReferenceCheckHelper.throwIfNull(process_re_purchase.changed_items, () => $"process_re_purchase.changed_item is null !!! - {entity_player.toBasicString()}"); // 4. 재화 감소 result = await shop_action.processSpent(process_re_purchase.spent_currency_type, process_re_purchase.spent_currency); @@ -158,7 +161,7 @@ public class RePurchaseItemPacketHandler : PacketRecvHandler result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) return result; - send_S2C_ACK_SHOP_RE_PURCHASE_LIST(entity_player, result, new List { process_re_purchase.changed_item } ); + send_S2C_ACK_SHOP_RE_PURCHASE_LIST(entity_player, result, item_guid, process_re_purchase.changed_items); return result; } diff --git a/GameServer/Contents/Shop/PacketHandler/SellItemPacketHandler.cs b/GameServer/Contents/Shop/PacketHandler/SellItemPacketHandler.cs index edb50de..e59b8d6 100644 --- a/GameServer/Contents/Shop/PacketHandler/SellItemPacketHandler.cs +++ b/GameServer/Contents/Shop/PacketHandler/SellItemPacketHandler.cs @@ -181,9 +181,10 @@ public class SellItemPacketHandler : PacketRecvHandler var attrib = item_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(attrib, () => $"attrib is null !!!"); - attrib.ItemStackCount = (ushort)sell_count; - result = await sold_action.setSoldProduct(delete_item_attribute.ItemGuid, item_doc); + (result, var last_sellCount) = await sold_action.setSoldProduct(delete_item_attribute.ItemGuid, item_doc, (ushort)sell_count); if (result.isFail()) return result; + + attrib.ItemStackCount = last_sellCount; // 6. DB 갱신 var batch = new QueryBatchEx( player, LogActionType.ShopSell, GameServerApp.getServerLogic().getDynamoDbClient()); diff --git a/GameServer/Contents/UserCreateOrLoading/Action/UserCreateOrLoadAction.cs b/GameServer/Contents/UserCreateOrLoading/Action/UserCreateOrLoadAction.cs index 3a18f1e..57fa27a 100644 --- a/GameServer/Contents/UserCreateOrLoading/Action/UserCreateOrLoadAction.cs +++ b/GameServer/Contents/UserCreateOrLoading/Action/UserCreateOrLoadAction.cs @@ -1,4 +1,7 @@ -using Google.Protobuf; +using GameServer.Contents.GameMode.Helper; +using GameServer.Contents.WorldEvent; +using GameServer.PacketHandler; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -132,7 +135,7 @@ public class UserCreateOrLoadAction : EntityActionBase { return result; } - + user_attribute.GameLoginDateTime = owner.getLoginStartTime(); bool is_first_login = login_cache.ServerSwitchCount == 1 ? true : false; @@ -663,7 +666,7 @@ public class UserCreateOrLoadAction : EntityActionBase var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); - var user_loader = new UserLoader(player); + var user_loader = new UserLoader(player); result = await user_loader.onPrepareLoad(); if (result.isFail()) { @@ -749,7 +752,7 @@ public class UserCreateOrLoadAction : EntityActionBase return result; } - public async Task tryLoadUser() + public async Task tryLoadUser() { var player = getOwner(); NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); @@ -836,7 +839,7 @@ public class UserCreateOrLoadAction : EntityActionBase { return result; } - + var ugq_test_action = owner.getEntityAction(); var ugq_result = await ugq_test_action.makeQuestFromTestUgq(); if (ugq_result.isFail()) @@ -889,6 +892,8 @@ public class UserCreateOrLoadAction : EntityActionBase owner.send_GS2C_NTF_FARMING_ALL_LOAD(); owner.send_S2C_NTF_CRAFT_INFOS(); owner.send_S2C_NTF_SEASON_PASS_INFOS(); + owner.send_S2C_NTF_BANNER_INFOS(); + owner.send_S2C_NTF_RANKING_SCHEDULE_INFOS(); if (m_is_first_login == true) { @@ -907,6 +912,13 @@ public class UserCreateOrLoadAction : EntityActionBase await owner.send_GS2C_NTF_QUEST_UPDATE_BY_SERVER_MOVE(); + // 매칭 UI에 필요한 핑서버 정보 전달 + var server_logic = GameServerApp.getServerLogic(); + NullReferenceCheckHelper.throwIfNull(server_logic, () => $"server_logic is null !!! - {owner.toBasicString()}"); + var server_config = server_logic.getServerConfig(); + NullReferenceCheckHelper.throwIfNull(server_config, () => $"server_config is null !!! - {owner.toBasicString()}"); + await owner.send_S2C_NTF_MATCH_REGION_INFO(server_config); + updateCompletedLoadUser(); Log.getLogger().info($"Step Notify To Client Success : notifyUserInfo() - {owner.toBasicString()}"); @@ -987,7 +999,7 @@ public class UserCreateOrLoadAction : EntityActionBase await owner.send_S2C_NTF_TARGET_CRAFT_INFOS(myhome_owner_guid); } } - + } await owner.sendLoginNotiToFriend(m_is_first_login); @@ -995,10 +1007,13 @@ public class UserCreateOrLoadAction : EntityActionBase await owner.send_GS2C_NTF_QUEST_UPDATE_ALL(); await QuestManager.It.PeriodRepeatQuestCheck(owner); await QuestManager.It.newAssignableQuestCheck(owner); - - + + await GameModeManager.It.gameModePlayRegulationCheck(owner); BattleRoomNotifyHelper.send_GS2C_NTF_BATTLE_EVENT(owner); + // 월드 이벤트 스케줄 전송 + WorldEventManager.It.SendWorldEventSchedule(owner); + Log.getLogger().info($"Step Enter To Zone Success : onNotifyAfterUserActive() - {owner.toBasicString()}"); return result; @@ -1011,7 +1026,7 @@ public class UserCreateOrLoadAction : EntityActionBase { if(typeof(TSimpleEventTriggerBase) == create_trigger.GetType()) { - var trigger = create_trigger as TSimpleEventTriggerBase; + var trigger = create_trigger as TSimpleEventTriggerBase; return trigger; } } @@ -1020,7 +1035,7 @@ public class UserCreateOrLoadAction : EntityActionBase { if (typeof(TSimpleEventTriggerBase) == load_trigger.GetType()) { - var trigger = load_trigger as TSimpleEventTriggerBase; + var trigger = load_trigger as TSimpleEventTriggerBase; return trigger; } } diff --git a/GameServer/Contents/UserCreateOrLoading/Event/Creation/Normal/DefaultUserCreator.cs b/GameServer/Contents/UserCreateOrLoading/Event/Creation/Normal/DefaultUserCreator.cs index 98e7b20..5d73877 100644 --- a/GameServer/Contents/UserCreateOrLoading/Event/Creation/Normal/DefaultUserCreator.cs +++ b/GameServer/Contents/UserCreateOrLoading/Event/Creation/Normal/DefaultUserCreator.cs @@ -521,7 +521,21 @@ namespace GameServer NullReferenceCheckHelper.throwIfNull(season_pass_doc, () => $"season_pass_doc is null !!! - {owner.toBasicString()}"); pushWriteDocBase(season_pass_doc); + + + //23. GameModePlayRegulation + var regulation_attribute = owner.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(regulation_attribute, () => $"regulation_attribute is null !!! - {owner.toBasicString()}"); + regulation_attribute.newEntityAttribute(); + (result, var play_regulation_doc) = await regulation_attribute.toDocBase(); + if (result.isFail()) + { + return result; + } + NullReferenceCheckHelper.throwIfNull(play_regulation_doc, () => $"play_regulation_doc is null !!! - {owner.toBasicString()}"); + pushWriteDocBase(play_regulation_doc); + setCompetedPrepareCreate(true); // UserCreate 로그 설정 diff --git a/GameServer/Contents/UserCreateOrLoading/Event/Creation/Tester/TestUserCreator.cs b/GameServer/Contents/UserCreateOrLoading/Event/Creation/Tester/TestUserCreator.cs index e278f1a..38390d9 100644 --- a/GameServer/Contents/UserCreateOrLoading/Event/Creation/Tester/TestUserCreator.cs +++ b/GameServer/Contents/UserCreateOrLoading/Event/Creation/Tester/TestUserCreator.cs @@ -476,6 +476,20 @@ namespace GameServer NullReferenceCheckHelper.throwIfNull(season_pass_doc, () => $"season_pass_doc is null !!! - {owner.toBasicString()}"); pushWriteDocBase(season_pass_doc); + + //23. GameModePlayRegulation + var regulation_attribute = owner.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(regulation_attribute, () => $"regulation_attribute is null !!! - {owner.toBasicString()}"); + regulation_attribute.newEntityAttribute(); + (result, var play_regulation_doc) = await regulation_attribute.toDocBase(); + if (result.isFail()) + { + return result; + } + NullReferenceCheckHelper.throwIfNull(play_regulation_doc, () => $"play_regulation_doc is null !!! - {owner.toBasicString()}"); + pushWriteDocBase(play_regulation_doc); + + setCompetedPrepareCreate(true); diff --git a/GameServer/Contents/UserCreateOrLoading/Event/Loading/UserLoader.cs b/GameServer/Contents/UserCreateOrLoading/Event/Loading/UserLoader.cs index 2302ed9..e83dbae 100644 --- a/GameServer/Contents/UserCreateOrLoading/Event/Loading/UserLoader.cs +++ b/GameServer/Contents/UserCreateOrLoading/Event/Loading/UserLoader.cs @@ -20,6 +20,7 @@ using CHARACTER_GUID = System.String; using ITEM_GUID = System.String; using PARTY_GUID = System.String; using Amazon.S3.Model; +using GameServer.Contents.GameMode.DbQuery; namespace GameServer; @@ -204,20 +205,14 @@ public class UserLoader : EntityLoaderBase { var result = new Result(); - var owner = getOwner() as Player; - NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); + var server_logic = GameServerApp.getServerLogic(); + var owner = getOwner(); result = await onLoadUserFromDoc(owner); - if (result.isFail()) - { - return result; - } + if (result.isFail()) return result; result = await onLoadUgcNpcFromDoc(owner); - if (result.isFail()) - { - return result; - } + if (result.isFail()) return result; await onLoadFarmingEffectFromDoc(owner); @@ -225,16 +220,10 @@ public class UserLoader : EntityLoaderBase NullReferenceCheckHelper.throwIfNull(user_land_auction_action, () => $"user_land_auction_action is null !!! - {owner.toBasicString()}"); result = await user_land_auction_action.tryLoadLandAuctionBidPriceAllFromDb(); - if (result.isFail()) - { - return result; - } + if (result.isFail()) return result; result = await onLoadMyhomeFromDoc(owner); - if (result.isFail()) - { - return result; - } + if (result.isFail()) return result; result = await onLoadPackageFromDoc(owner); if(result.isFail()) return result; @@ -245,34 +234,33 @@ public class UserLoader : EntityLoaderBase result = await onLoadSeasonPassFromDoc(owner); if (result.isFail()) return result; - var server_logic = GameServerApp.getServerLogic(); + var buff_action = owner.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(buff_action, () => $"buff_action is null !!! - {owner.toBasicString()}"); + + result = await buff_action.loadBuff(); + if (result.isFail()) return result; var nickname_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(nickname_attribute, () => $"nickname_attribute is null !!! - {owner.toBasicString()}"); var player_manager = server_logic.getPlayerManager(); - var buff_action = owner.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(buff_action, () => $"buff_action is null !!! - {owner.toBasicString()}"); - - result = await buff_action.loadBuff(); - if (result.isFail()) - { - return result; - } - - - // 유저의 Nickname을 PlayerManager에 SubKey로 등록 한다. result = player_manager.trySubKeyBindToPrimaryKey(nickname_attribute.Nickname, owner.getUserGuid(), owner); - if (result.isFail()) - { - return result; - } + if (result.isFail()) return result; + + var ranking_manager = server_logic.getRankingManager(); + result = await ranking_manager.loadRanker(owner); + if (result.isFail()) return result; return result; } + public async Task onFakeLoadUserFromDoc(Player owner) + { + return await onLoadUserFromDoc(owner); + } + protected virtual async Task onLoadUserFromDoc(Player owner) { var result = new Result(); @@ -343,7 +331,8 @@ public class UserLoader : EntityLoaderBase batch.addQuery(new DBQUgqDailyRewardCountRead(user_guid)); batch.addQuery(new DBQBeaconShopSoldReadAll(user_guid)); - + batch.addQuery(new DBQGameModePlayRegulationReadAll(user_guid)); + batch.addQuery(new QueryFinal()); } diff --git a/GameServer/Contents/WorldEvent/PacketHandler/ChatCommandEventAction.cs b/GameServer/Contents/WorldEvent/PacketHandler/ChatCommandEventAction.cs new file mode 100644 index 0000000..62fd04f --- /dev/null +++ b/GameServer/Contents/WorldEvent/PacketHandler/ChatCommandEventAction.cs @@ -0,0 +1,213 @@ +using CommandLine; +using GameServer.EventDefines; +using GameServer.EventInvokers; +using GameServer.Global; +using MetaAssets; +using ServerCommon; +using ServerCore; + +namespace GameServer; + +/// +/// ReqChat event_action fake +/// +[ChatCommandAttribute("event_action", typeof(ChatCommandEventAction), AuthAdminLevelType.Developer, + AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +public class ChatCommandEventAction : ChatCommandBase +{ + public override async Task invoke(Player player, string token, string[] args) + { + var serverLogic = GameServerApp.getServerLogic(); + try + { + var cmdName = args[0]; + var cmdArgs = args.Skip(1).ToArray(); + switch (cmdName) + { + case "fake": + await execCmd(cmdArgs); + break; + default: + Log.getLogger().error($"Invalid match command arguments"); + break; + } + } + catch (Exception ex) + { + Log.getLogger().error($"Match Command Failed => {ex}"); + } + + return; + + async Task execCmd(string[] cmdArgs) where TCmd : ICmdCheat + { + try + { + var parserResult = CommandLine.Parser.Default.ParseArguments(cmdArgs); + var cmd = parserResult.Value as ICmdCheat; + var result = await cmd.ExecuteAsync(player); + MatchGuard.ResultFailException(result); + } + catch (ResultException ex) + { + Log.getLogger().error($"Match Command Result Failed => {ex}"); + } + catch (Exception ex) + { + Log.getLogger().error($"Match Command Failed => {ex}"); + } + } + } + + public interface ICmdCheat + { + Task ExecuteAsync(Player player); + } + + internal class CmdGetContribute : ICmdCheat + { + [Option('e', "event", HelpText = "")] public int EventId { get; set; } = 0; + + public async Task ExecuteAsync(Player player) + { + var cliMsg = new ClientToGame { Request = new ClientToGameReq() }; + var req = new ClientToGameReq.Types.C2GS_REQ_WORLD_EVENT_CONTRIBUTION() { WorldEventId = EventId, }; + cliMsg.Request.ReqWorldEventContribution = req; + var listenSession = player.getListenSessionBase(); + NullReferenceCheckHelper.throwIfNull(listenSession); + var result = await listenSession.onCallProtocolHandler(player, cliMsg); + return result; + } + } + + internal class CmdCallFakeInvokers : ICmdCheat + { + [Option('t', "type", HelpText = "")] public string FakeType { get; set; } = string.Empty; + private Player _player = null!; + + public async Task ExecuteAsync(Player player) + { + _player = player; + var result = new Result(); + if (FakeType == "game") + { + var invokers = MakeFakeGameModeInvokers(); + foreach (IGameEventInvoker gameEventInvoker in invokers) + { + await EventActionScoreManager.It.ProcessScore(gameEventInvoker); + } + } + else + { + var invokers = MakeFakeInvokers(); + foreach (IGameEventInvoker gameEventInvoker in invokers) + { + await EventActionScoreManager.It.ProcessScore(gameEventInvoker); + } + } + + await Task.CompletedTask; + return result; + } + + private IEnumerable MakeFakeInvokers() + { + List invokers = new List(); + var list = MetaData.Instance.Meta.EventActionScoreMetaTable.EventActionScoreMetaDataList; + foreach (EventActionScoreMetaData eventActionScoreMetaData in list) + { + var invoker = createInvoker(eventActionScoreMetaData); + if (invoker != null) + { + invokers.Add(invoker); + } + } + + return invokers; + + IGameEventInvoker? createInvoker(EventActionScoreMetaData metaData) + { + var eventTarget = Enum.Parse(metaData.EventTarget); + switch (eventTarget) + { + case GameEventType.CRAFTING: + var craftItemId = string.IsNullOrEmpty(metaData.EventCondition1) + ? 0 + : int.Parse(metaData.EventCondition1); + return new GameEventInvokerCraftingCompleted(_player, craftItemId); + case GameEventType.QUEST: + uint questId = string.IsNullOrEmpty(metaData.EventCondition1) + ? 0 + : uint.Parse(metaData.EventCondition1); + return new GameEventInvokerQuestRewarded(_player, questId); + case GameEventType.CLAIM: + return new GameEventInvokerClaimRewarded(_player); + case GameEventType.FARMING: + return new GameEventInvokerFarmingCompleted(_player.getEntityGuid()); + case GameEventType.REWARDPROP: + return new GameEventInvokerRewadPropRewarded(_player); + case GameEventType.SEASONPASS: + return new GameEventInvokerSeasonPassRewarded(_player); + case GameEventType.TAXI: + return new GameEventInvokerTaxiArrived(_player); + case GameEventType.GAMEMODE: + var gameModeId = string.IsNullOrEmpty(metaData.EventCondition1) ? 0 : int.Parse(metaData.EventCondition1); + var gameModeData = MetaData.Instance.Meta.GameModeMetaTable.GameModeMetaDataList.First(x => + x.GameModeID == gameModeId); + if (gameModeData.GameModeType is GameModeType.RUN_RACE && metaData.Event == nameof(GameEventActionType.ENDED)) + { + var instanceId = string.IsNullOrEmpty(metaData.EventCondition2) ? 0 : int.Parse(metaData.EventCondition2); + return new GameEventRunRaceCompleteTime(_player, gameModeId, instanceId, + Random.Shared.Next(60 * 1000, 120 * 1000)); + } + else if (gameModeData.GameModeType is GameModeType.TPS_FFA && metaData.Event == nameof(GameEventActionType.PICKUP)) + { + return new GameEventFfaOnPickUpObject(_player, gameModeId, Enum.Parse(metaData.EventCondition1)); + } + break; + } + return null; + } + } + + private IEnumerable MakeFakeGameModeInvokers() + { + List invokers = new List(); + var list = MetaData.Instance.Meta.EventActionScoreMetaTable.EventActionScoreMetaDataList; + foreach (EventActionScoreMetaData eventActionScoreMetaData in list) + { + var invoker = createInvoker(eventActionScoreMetaData); + if (invoker != null) + { + invokers.Add(invoker); + } + } + + return invokers; + + IGameEventInvoker? createInvoker(EventActionScoreMetaData metaData) + { + var eventTarget = Enum.Parse(metaData.EventTarget); + switch (eventTarget) + { + case GameEventType.GAMEMODE: + var gameModeId = string.IsNullOrEmpty(metaData.EventCondition1) ? 0 : int.Parse(metaData.EventCondition1); + var gameModeData = MetaData.Instance.Meta.GameModeMetaTable.GameModeMetaDataList.First(x => + x.GameModeID == gameModeId); + if (gameModeData.GameModeType is GameModeType.RUN_RACE && metaData.Event == nameof(GameEventActionType.ENDED)) + { + var instanceId = string.IsNullOrEmpty(metaData.EventCondition2) ? 0 : int.Parse(metaData.EventCondition2); + return new GameEventRunRaceCompleteTime(_player, gameModeId, instanceId, + Random.Shared.Next(60 * 1000, 120 * 1000)); + } + else if (gameModeData.GameModeType is GameModeType.TPS_FFA && metaData.Event == nameof(GameEventActionType.PICKUP)) + { + return new GameEventFfaOnPickUpObject(_player, gameModeId, Enum.Parse(metaData.EventCondition1)); + } + break; + } + return null; + } + } + } +} diff --git a/GameServer/Contents/WorldEvent/PacketHandler/ChatCommandWorldEvent.cs b/GameServer/Contents/WorldEvent/PacketHandler/ChatCommandWorldEvent.cs new file mode 100644 index 0000000..b5633ba --- /dev/null +++ b/GameServer/Contents/WorldEvent/PacketHandler/ChatCommandWorldEvent.cs @@ -0,0 +1,157 @@ +using CommandLine; +using GameServer.EventInvokers; +using GameServer.Global; +using ServerCommon; +using ServerCore; + +namespace GameServer; + +/// +/// 월드 이벤트 관련 치트 명령어 클래스입니다. +/// GM 권한이 있는 유저가 채팅창에 '/world_event [sub-command] [options]' 형식으로 입력하여 사용합니다. +/// +/// 사용 가능한 서브 커맨드: +/// 1. schedule +/// - 설명: 현재 진행중이거나 예정된 월드 이벤트의 스케줄 정보를 요청합니다. +/// - 사용법: /world_event schedule +/// +/// 2. contribute (또는 contribution) +/// - 설명: 특정 월드 이벤트에 대한 자신의 기여도 정보를 요청합니다. +/// - 사용법: /world_event contribute -e [EventId] +/// - 옵션: +/// -e, --event: (필수) 조회할 월드 이벤트의 ID +/// +/// 3. event_invoker +/// - 설명: 월드 이벤트 보상 획득과 관련된 로직을 테스트합니다. (GameEventInvokerClaimRewarded) +/// - 사용법: /world_event event_invoker +/// +[ChatCommandAttribute("world_event", typeof(ChatCommandWorldEvent), AuthAdminLevelType.Developer, + AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +public class ChatCommandWorldEvent : ChatCommandBase +{ + public override async Task invoke(Player player, string token, string[] args) + { + try + { + var cmdName = args[0]; + var cmdArgs = args.Skip(1).ToArray(); + switch (cmdName) + { + case "schedule": + WorldEventManager.It.SendWorldEventSchedule(player); + break; + case "contribute": + case "contribution": + await execCmd(cmdArgs); + break; + case "event_invoker": + await execCmd(cmdArgs); + break; + // case "reset_play_penalty": + // await execCmd(cmdArgs); + // break; + // case "reset_match_count": + // await execCmd(cmdArgs); + // break; + // case "check_event": + // // 매칭 서버로 보내는 치트 + // MatchManager.It.SendCheatCmd(args); + // break; + default: + Log.getLogger().error($"Invalid match command arguments"); + break; + } + } + catch (Exception ex) + { + Log.getLogger().error($"Match Command Failed => {ex}"); + } + return; + + async Task execCmd(string[] cmdArgs) where TCmd : ICmdCheat + { + try + { + var parserResult = CommandLine.Parser.Default.ParseArguments(cmdArgs); + var cmd = parserResult.Value as ICmdCheat; + var result = await cmd.ExecuteAsync(player); + MatchGuard.ResultFailException(result); + } + catch (ResultException ex) + { + Log.getLogger().error($"Match Command Result Failed => {ex}"); + } + catch (Exception ex) + { + Log.getLogger().error($"Match Command Failed => {ex}"); + } + } + } + + public interface ICmdCheat + { + Task ExecuteAsync(Player player); + } + + // public class CmdSchedule : ICmdCheat + // { + // [Option('e', "event", HelpText = "")] public int EventId { get; set; } = 0; + // + // public async Task ExecuteAsync(Player player) + // { + // var cliMsg = new ClientToGame { Request = new ClientToGameReq() }; + // var req = new ClientToGameReq.Types.C2GS_REQ_MATCH_RESERVE + // { + // GameModeId = GameModeId, EventId = EventId, RegionName = RegionName, RegionPingMs = RegionPingMs, + // }; + // cliMsg.Request.ReqMatchReserve = req; + // + // var listenSession = player.getListenSessionBase(); + // MatchGuard.NotNull(listenSession); + // var result = await listenSession.onCallProtocolHandler(player, cliMsg); + // return result; + // } + // } + + internal class CmdGetContribute : ICmdCheat + { + [Option('e', "event", HelpText = "")] public int EventId { get; set; } = 0; + public async Task ExecuteAsync(Player player) + { + var cliMsg = new ClientToGame { Request = new ClientToGameReq() }; + var req = new ClientToGameReq.Types.C2GS_REQ_WORLD_EVENT_CONTRIBUTION() + { + WorldEventId = EventId, + }; + cliMsg.Request.ReqWorldEventContribution = req; + var listenSession = player.getListenSessionBase(); + NullReferenceCheckHelper.throwIfNull(listenSession); + var result = await listenSession.onCallProtocolHandler(player, cliMsg); + return result; + } + } + + internal class CmdCallInvoker : ICmdCheat + { + // [Option('e', "event", HelpText = "")] public int EventId { get; set; } = 0; + public async Task ExecuteAsync(Player player) + { + var result = new Result(); + GameEventInvokerClaimRewarded eventInvoker = new GameEventInvokerClaimRewarded(player); + await EventActionScoreManager.It.ProcessScore(eventInvoker); + await Task.CompletedTask; + return result; + + // var cliMsg = new ClientToGame { Request = new ClientToGameReq() }; + // var req = new ClientToGameReq.Types.C2GS_REQ_WORLD_EVENT_CONTRIBUTION() + // { + // WorldEventId = EventId, + // }; + // cliMsg.Request.ReqWorldEventContribution = req; + // var listenSession = player.getListenSessionBase(); + // NullReferenceCheckHelper.throwIfNull(listenSession); + // var result = await listenSession.onCallProtocolHandler(player, cliMsg); + // return result; + } + } +} diff --git a/GameServer/Contents/WorldEvent/PacketHandler/WorldEventContributionHandler.cs b/GameServer/Contents/WorldEvent/PacketHandler/WorldEventContributionHandler.cs new file mode 100644 index 0000000..8ffc15b --- /dev/null +++ b/GameServer/Contents/WorldEvent/PacketHandler/WorldEventContributionHandler.cs @@ -0,0 +1,37 @@ +using GameServer.Contents.WorldEvent; +using Google.Protobuf; +using ServerBase; +using ServerCore; + +namespace GameServer.PacketHandler; + +/// +/// C2S_WORLD_EVENT_CONTRIBUTION의 패킷 핸들러 구현 +/// +[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_WORLD_EVENT_CONTRIBUTION), + typeof(WorldEventContributionHandler), typeof(GameLoginListener))] +public class WorldEventContributionHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var player = session as Player; + ArgumentNullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + var reqMsg = recvMessage as ClientToGame; + ArgumentNullReferenceCheckHelper.throwIfNull(reqMsg, () => $"req_msg is null !!! - {player.toBasicString()}"); + ClientToGameReq.Types.C2GS_REQ_WORLD_EVENT_CONTRIBUTION? request = reqMsg.Request.ReqWorldEventContribution; + ArgumentNullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {player.toBasicString()}"); + + return await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "WORLD_EVENT_CONTRIBUTION", + async () => await WorldEventManager.It.SendContributionPointAsync(player, request.WorldEventId)); + } + + public override async Task onProcessPacketException(ISession entityWithSession, IMessage recvMessage + , Result errorResult) + { + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! - {entityWithSession.toBasicString()}"); + string errMsg = $"[WORLD_EVENT_CONTRIBUTION] ACK Exception : {errorResult.toBasicString()} - {player.toBasicString()}"; + Log.getLogger().error(errMsg); + await Task.CompletedTask; + } +} diff --git a/GameServer/Entity/BuildingFloor/Action/BuildingFloorAction.cs b/GameServer/Entity/BuildingFloor/Action/BuildingFloorAction.cs index 94d82d4..eac264c 100644 --- a/GameServer/Entity/BuildingFloor/Action/BuildingFloorAction.cs +++ b/GameServer/Entity/BuildingFloor/Action/BuildingFloorAction.cs @@ -48,7 +48,7 @@ namespace GameServer return result; } - public bool isRentalFinish() + public bool isRentalFinish(int deltaSecond = 0) { var building_floor = getOwner() as BuildingFloor; NullReferenceCheckHelper.throwIfNull(building_floor, () => $"building_floor is null !!!"); @@ -56,7 +56,7 @@ namespace GameServer var building_floor_attribute = building_floor.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(building_floor_attribute, () => $"building_floor_attribute is null !!!"); - if (DateTime.UtcNow > building_floor_attribute.RentalFinishTime) + if (DateTime.UtcNow > building_floor_attribute.RentalFinishTime.AddSeconds(deltaSecond)) return true; return false; diff --git a/GameServer/Entity/BuildingFloor/Action/BuildingFloorAgentAction.cs b/GameServer/Entity/BuildingFloor/Action/BuildingFloorAgentAction.cs index 9694229..2575f03 100644 --- a/GameServer/Entity/BuildingFloor/Action/BuildingFloorAgentAction.cs +++ b/GameServer/Entity/BuildingFloor/Action/BuildingFloorAgentAction.cs @@ -1,5 +1,6 @@ using ServerCommon; -using ServerCore; using ServerBase; +using ServerCore; +using ServerBase; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -7,6 +8,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using static ClientToGameReq.Types; +using System.Diagnostics.CodeAnalysis; namespace GameServer { @@ -121,5 +123,10 @@ namespace GameServer } public ConcurrentDictionary getBuildingFloors() => m_building_floors; + + public bool tryGetBuildingFloor(int floor, [MaybeNullWhen(false)] out BuildingFloor buildingFloor) + { + return m_building_floors.TryGetValue(floor, out buildingFloor); + } } } diff --git a/GameServer/Entity/CaliumStorage/Action/CaliumStorageAction.cs b/GameServer/Entity/CaliumStorage/Action/CaliumStorageAction.cs index eb4d392..4130f1f 100644 --- a/GameServer/Entity/CaliumStorage/Action/CaliumStorageAction.cs +++ b/GameServer/Entity/CaliumStorage/Action/CaliumStorageAction.cs @@ -488,7 +488,10 @@ public class CaliumStorageAction : EntityActionBase return mis_pivot_calium; } + // Converter 누적 물량 체크용 + Log.getLogger().debug($"Converter Cumulative Calium - origin: converter - {caliumStorageAttrib.ConverterStorage.ConverterCumulativeCalium} / rollup - {rollup.m_convertor_volume}"); caliumStorageAttrib.ConverterStorage.ConverterCumulativeCalium = CaliumStorageHelper.AddDoubleByLong(caliumStorageAttrib.ConverterStorage.ConverterCumulativeCalium, rollup.m_convertor_volume); + Log.getLogger().debug($"Converter Cumulative Calium - addition: converter - {caliumStorageAttrib.ConverterStorage.ConverterCumulativeCalium}"); // 1일을 추가한 이유 : create_time 이 전일 23:00:00 ~ 23:59:59 에 생성되기 때문 var check_date = new DateTime(rollup.m_create_time.Year, rollup.m_create_time.Month, rollup.m_create_time.Day, 0, 0, 0); diff --git a/GameServer/Entity/CaliumStorage/CaliumStorageHelper.cs b/GameServer/Entity/CaliumStorage/CaliumStorageHelper.cs index 0c6cf11..db9ccea 100644 --- a/GameServer/Entity/CaliumStorage/CaliumStorageHelper.cs +++ b/GameServer/Entity/CaliumStorage/CaliumStorageHelper.cs @@ -80,14 +80,21 @@ public static class CaliumStorageHelper return value_long / (double)EchoSystemHelper.DoubleCalculateDigitsLong; } - public static double AddDoubleByLong(double first, double second) + public static double AddDoubleByLong(double first, double second, bool isLogging = false) { var first_long = (long)(first * EchoSystemHelper.DoubleCalculateDigitsLong); var second_long = (long)(second * EchoSystemHelper.DoubleCalculateDigitsLong); var add = first_long + second_long; - return add / (double)EchoSystemHelper.DoubleCalculateDigitsLong; + var value = add / (double)EchoSystemHelper.DoubleCalculateDigitsLong; + + if (isLogging) + { + Log.getLogger().debug($"AddDoubleByLong: origin [{first} , {second}] / Add [{first_long} + {second_long} = {add} / {value}]"); + } + + return value; } public static double SubTractDoubleByLong(double first, double second) diff --git a/GameServer/Entity/CustomDefinedUi/Action/CustomDefinedUiAction.cs b/GameServer/Entity/CustomDefinedUi/Action/CustomDefinedUiAction.cs index bf53439..b460ea6 100644 --- a/GameServer/Entity/CustomDefinedUi/Action/CustomDefinedUiAction.cs +++ b/GameServer/Entity/CustomDefinedUi/Action/CustomDefinedUiAction.cs @@ -133,7 +133,7 @@ namespace GameServer return currCustomDefinedUi; }; - m_custom_defined_uis.AddOrUpdate(uiKey, add_custom_defined_ui, update_custom_defined_ui); + m_custom_defined_uis.AddOrUpdate(uiKey, add_custom_defined_ui!, update_custom_defined_ui); return result; } @@ -246,7 +246,7 @@ namespace GameServer return currValue; }; - m_custom_defined_uis.AddOrUpdate(ui_key, add_custom_defined_ui, update_custom_defined_ui); + m_custom_defined_uis.AddOrUpdate(ui_key, add_custom_defined_ui!, update_custom_defined_ui); return result; } diff --git a/GameServer/Entity/Effect/Farming/Action/FarmingEffectAction.cs b/GameServer/Entity/Effect/Farming/Action/FarmingEffectAction.cs index 7d276bb..de53fe6 100644 --- a/GameServer/Entity/Effect/Farming/Action/FarmingEffectAction.cs +++ b/GameServer/Entity/Effect/Farming/Action/FarmingEffectAction.cs @@ -1,4 +1,5 @@ -using ServerCore; +using GameServer.EventInvokers; +using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; @@ -706,7 +707,7 @@ public class FarmingEffectAction : EntityActionBase origin_entity_attribute.copyEntityAttributeFromDoc(farming_effect_doc); - curr_map.PropModifyNoti(curr_anchor_info.AnchorGuid); + curr_map.broadcast_PropModify(curr_anchor_info.AnchorGuid); var farming_summary = owner.toFarmingSummary(); @@ -742,6 +743,10 @@ public class FarmingEffectAction : EntityActionBase err_msg = $"Failed to tryRemoveInGameZone() !!! : {remove_result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); } + + await GameEventTriggerManager.It.getGlobalGameEventCollector().collectEvent(new GameEventInvokerFarmingCompleted(master_profile.MasterGuid)); + + } else { @@ -780,6 +785,7 @@ public class FarmingEffectAction : EntityActionBase if(ServerCommon.Constant.FARMING_LAST_UPDATE_STORE_INTERVAL_MSEC < DateTimeHelper.differFromToMilliSeconds(farming_effect_attribute.FarmingLastUpdateTime, to_update_time)) { + farming_effect_attribute.FarmingLastUpdateTime = to_update_time; farming_effect_attribute.modifiedEntityAttribute(); (result, var doc_base) = await farming_effect_attribute.toDocBase(); diff --git a/GameServer/Entity/Inventory/Base/BagInven.cs b/GameServer/Entity/Inventory/Base/BagInven.cs index 697e481..35f1365 100644 --- a/GameServer/Entity/Inventory/Base/BagInven.cs +++ b/GameServer/Entity/Inventory/Base/BagInven.cs @@ -223,10 +223,10 @@ namespace GameServer // 4. 아이템 Stack 개수가 큰 순서로 정렬 시킨다. var remain_count = toTakeInCount; - target_item_attributes.OrderBy(x => { return x.ItemStackCount; }); + var item_attributes = target_item_attributes.OrderByDescending(x => x.ItemStackCount); // 5. 동일한 종류의 아이템에 최대 Stack 개수이내로 추가 한다. - foreach (var item_attribute in target_item_attributes) + foreach (var item_attribute in item_attributes) { if (remain_count <= 0) { diff --git a/GameServer/Entity/Item/Owner/Myhome/MyhomeItemAction.cs b/GameServer/Entity/Item/Owner/Myhome/MyhomeItemAction.cs index e443659..a3f5a77 100644 --- a/GameServer/Entity/Item/Owner/Myhome/MyhomeItemAction.cs +++ b/GameServer/Entity/Item/Owner/Myhome/MyhomeItemAction.cs @@ -6,14 +6,19 @@ using System.Threading.Tasks; -namespace GameServer -{ - internal class MyhomeItemAction : ItemAction - { - public MyhomeItemAction(MyhomeItem owner) - : base(owner) - { +namespace GameServer; - } +public class MyhomeItemAction : ItemAction +{ + public MyhomeItemAction(MyhomeItem owner) + : base(owner) + { + + } + + public override async Task onTick() + { + // Do Nothing !!! + await Task.CompletedTask; } } diff --git a/GameServer/Entity/Match/MatchAction.cs b/GameServer/Entity/Match/MatchAction.cs new file mode 100644 index 0000000..a16da4a --- /dev/null +++ b/GameServer/Entity/Match/MatchAction.cs @@ -0,0 +1,41 @@ +using ServerBase; + +namespace GameServer.Match; +public class MatchAction: EntityActionBase +{ + // 선택한 매칭 그룹 + public PlayerMatchInfo PlayerMatchInfo { get; init; } = new(); + + public MatchAction(EntityBase parent) : base(parent) + { + } + + public override Task onInit() + { + return Task.FromResult(new Result()); + } + + public override void onClear() + { + PlayerMatchInfo.Clear(); + } +} + +public class PlayerMatchInfo +{ + public string MatchGroupId { get; set; } = string.Empty; + public int GameModeId { get; set; } = 0; + public MatchStatusInfo MatchStatusInfo { get; set; } = new(); + + public void Clear() + { + MatchGroupId = string.Empty; + GameModeId = 0; + MatchStatusInfo = new MatchStatusInfo(); + } + + public bool IsOnMatching() + { + return MatchStatusInfo.Status is MatchStatusType.Reserved or MatchStatusType.Progress; + } +} diff --git a/GameServer/Entity/Player/FakePlayer.cs b/GameServer/Entity/Player/FakePlayer.cs new file mode 100644 index 0000000..61ee016 --- /dev/null +++ b/GameServer/Entity/Player/FakePlayer.cs @@ -0,0 +1,70 @@ +using ServerBase; +using ServerCommon; + +namespace GameServer; + +public class FakePlayer : Player +{ + private readonly string m_account_id; + private readonly GameServerLogic m_server_logic; + private readonly DynamoDbClient m_dynamoDbClient; + + private bool m_isEnableFakePlayer { get; set; } = false; + private UserLoader? m_user_loader { get; set; } = null; + public FakePlayer(string accountId) : base() + { + m_account_id = accountId; + + m_server_logic = GameServerApp.getServerLogic(); + m_dynamoDbClient = m_server_logic.getDynamoDbClient(); + } + + public async Task onFakeInit() + { + // 1. 기본 Player Init + await base.onInit(); + + // 2. account attribute 설정 + var isload = await loadUserInfo(); + if (false == isload) return; + + // user loader + m_user_loader = new UserLoader(this); + var result = await m_user_loader.onPrepareLoad(); + if (result.isFail()) return; + + result = await m_user_loader.onFakeLoadUserFromDoc(this); + if (result.isFail()) return; + + m_isEnableFakePlayer = true; + } + + private async Task loadUserInfo() + { + // 1. account_base + var (result, make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey(m_account_id); + if (result.isFail() || null == make_primary_key) return false; + + var query_config = m_dynamoDbClient.makeQueryConfigForReadByPKOnly(make_primary_key.PK); + (result, var account_base_doc) = await m_dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig(query_config, false); + if (result.isFail()) return false; + + var account_attribute = getEntityAttribute(); + if(null == account_attribute) return false; + account_attribute.copyEntityAttributeFromDoc(account_base_doc); + + // 2. user_base + (result, make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey(account_attribute.UserGuid); + if (result.isFail() || null == make_primary_key) return false; + + query_config = m_dynamoDbClient.makeQueryConfigForReadByPKOnly(make_primary_key.PK); + (result, var user_base_doc) = await m_dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig(query_config, false); + if (result.isFail()) return false; + + var user_attribute = getEntityAttribute(); + if(null == user_attribute || null == user_base_doc) return false; + user_attribute.copyEntityAttributeFromDoc(user_base_doc); + + return true; + } +} \ No newline at end of file diff --git a/GameServer/Entity/Player/Helper/PlayerHelper.cs b/GameServer/Entity/Player/Helper/PlayerHelper.cs index 1ed47b1..ca1635c 100644 --- a/GameServer/Entity/Player/Helper/PlayerHelper.cs +++ b/GameServer/Entity/Player/Helper/PlayerHelper.cs @@ -222,10 +222,10 @@ namespace GameServer foreach (var item_base in inven_accessor.getHasItemBases()) { var item = item_base as Item; - ArgumentNullException.ThrowIfNull(item, $"item is null !!! - {player.toBasicString}"); + ArgumentNullException.ThrowIfNull(item, $"item is null !!! - {player.toBasicString()}"); var item_attribute = item.getOriginEntityAttribute(); - ArgumentNullException.ThrowIfNull(item_attribute, $"item_attribute is null !!! - {player.toBasicString}"); + ArgumentNullException.ThrowIfNull(item_attribute, $"item_attribute is null !!! - {player.toBasicString()}"); if (item_attribute.ItemMetaId == requireItem.ID) { diff --git a/GameServer/Entity/Player/Player.cs b/GameServer/Entity/Player/Player.cs index eb9c091..27b545d 100644 --- a/GameServer/Entity/Player/Player.cs +++ b/GameServer/Entity/Player/Player.cs @@ -1,36 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.Concurrent; -using System.Numerics; - - -using Newtonsoft.Json; +using GameServer.Action; +using GameServer.Contents.GameMode; +using GameServer.Contents.LargePacket.Action; +using GameServer.Match; using Nettention.Proud; -using StackExchange.Redis; -using Pipelines.Sockets.Unofficial.Buffers; - - using ServerCore; using ServerBase; using ServerCommon; -using ServerCommon.BusinessLogDomain; -using MetaAssets; - - -using SESSION_ID = System.Int32; -using WORLD_META_ID = System.UInt32; -using META_ID = System.UInt32; -using ENTITY_GUID = System.String; -using ACCOUNT_ID = System.String; using OWNER_GUID = System.String; -using USER_GUID = System.String; -using CHARACTER_GUID = System.String; -using ITEM_GUID = System.String; - - namespace GameServer; @@ -46,6 +22,11 @@ public partial class Player : UserBase, IEntityWithSession, IMergeWithInventory, } + public Player() : base(EntityType.Player) + { + + } + public override async Task onInit() { //===================================================================================== @@ -64,7 +45,7 @@ public partial class Player : UserBase, IEntityWithSession, IMergeWithInventory, addEntityAttribute(new CraftHelpAttribute(this)); addEntityAttribute(new PartyInvitePartyRecvsAttribute(this)); - + addEntityAttribute(new FriendFolderAttribute(this)); addEntityAttribute(new RepeatQuestAttribute(this)); @@ -77,7 +58,7 @@ public partial class Player : UserBase, IEntityWithSession, IMergeWithInventory, addEntityAttribute(new UgqDailyRewardCountAttribute(this)); addEntityAttribute(new RentalInstanceVisitAttribute(this)); - + addEntityAttribute(new DailyQuestCheckAttribute(this));//legacy addEntityAttribute(new SwitchingPropAttribute(this)); addEntityAttribute(new QuestPeriodRepeatCheckAttribute(this)); @@ -86,7 +67,20 @@ public partial class Player : UserBase, IEntityWithSession, IMergeWithInventory, // Game Domain Attribute //===================================================================================== addEntityAttribute(new CaliumAttribute(this)); - + + //===================================================================================== + // GameMode Attribute + //===================================================================================== + addEntityAttribute(new GameModePlayRegulationAttribute(this)); + addEntityAttribute(new GameModeBestRecordAttribute(this)); + + + //===================================================================================== + // WorldEvent 기여도 관련 Action + //===================================================================================== + addEntityAttribute(new WorldEventScoreAttribute(this)); + + //===================================================================================== // User Domain Action //===================================================================================== @@ -122,11 +116,11 @@ public partial class Player : UserBase, IEntityWithSession, IMergeWithInventory, // room 관련 action addEntityAction(new RoomAction(this)); - - + + // shop 관련 action addEntityAction(new ShopAction(this)); - + // Party 관련 action addEntityAction(new PersonalPartyAction(this)); addEntityAction(new PartyInvitePartyRecvAction(this)); @@ -234,7 +228,7 @@ public partial class Player : UserBase, IEntityWithSession, IMergeWithInventory, // Custom Ui or Data 관련 Action //===================================================================================== addEntityAction(new CustomDefinedUiAction(this)); - + //===================================================================================== // Calium 관련 Action //===================================================================================== @@ -250,6 +244,27 @@ public partial class Player : UserBase, IEntityWithSession, IMergeWithInventory, //===================================================================================== addEntityAction(new UserLandAuctionAction(this)); + //===================================================================================== + // LargePacket 관련 Action + //===================================================================================== + addEntityAction(new LargePacketHandleAction(this)); + + //===================================================================================== + // GameMode 관련 Action + //===================================================================================== + addEntityAction(new GameModePlayRegulationAction(this)); + addEntityAction(new GameModeBestRecordAction(this)); + + + //===================================================================================== + // GameEventCollector 관련 Action + //===================================================================================== + addEntityAction(new GameEventCollectorAction(this)); + + //===================================================================================== + // Match 관련 Action + //===================================================================================== + addEntityAction(new MatchAction(this)); return await base.onInit(); } diff --git a/GameServer/Entity/Player/PlayerCheat.cs b/GameServer/Entity/Player/PlayerCheat.cs index ec5dcc5..ffc294a 100644 --- a/GameServer/Entity/Player/PlayerCheat.cs +++ b/GameServer/Entity/Player/PlayerCheat.cs @@ -2,6 +2,7 @@ using ServerCore; using ServerBase; using ServerCommon; +using DataCopyHelper = ServerBase.DataCopyHelper; namespace GameServer; @@ -90,3 +91,137 @@ internal class ChatCommandAttributeResetAll : ChatCommandBase GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } } + +[ChatCommandAttribute("makefakeplayer", typeof(ChatCommandMakeFakePlayer), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] +internal class ChatCommandMakeFakePlayer : ChatCommandBase +{ + public override async Task invoke(Player player, string token, string[] args) + { + Log.getLogger().info($"Call ChatCommandMakeFakePlayer !!! - {player.toBasicString()}"); + + if (args.Length < 1) + { + var err_msg = $"Not enough argument !!! : argCount:{args.Length} - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + return; + } + + var accountId = args[0]; + + var quest = string.Empty; + var questTaskNum = 0; + + if (args.Length > 1) + { + quest = args[1]; + } + + if (args.Length > 2) + { + int.TryParse(args[2], out questTaskNum); + } + + var fakePlayer = new FakePlayer(accountId); + await fakePlayer.onFakeInit(); + + var result = await fakePlayer.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "TestFakePlayer", testFakePlayerDelegate); + + return; + + async Task testFakePlayerDelegate() => + await testFakePlayer(fakePlayer, quest, questTaskNum); + } + + private async Task testFakePlayer(FakePlayer player, string questInfo, int questTaskNum) + { + var result = new Result(); + + if(string.IsNullOrEmpty(questInfo) || questTaskNum <= 0) + { + result.setFail(ServerErrorCode.QuestNotComplete); + return result; + } + var questData = questInfo.Split("#"); + var questId = int.Parse(questData[0]); + var questRevision = int.Parse(questData[1]); + + var updateAction = player.getEntityAction(); + + var questAction = player.getEntityAction(); + (result, var quest) = await questAction.getQuest((uint)questId, (uint)questRevision); + if (result.isFail() || null == quest) return result; + + var questAttribute = quest.getEntityAttribute(); + if (null == questAttribute || questAttribute.ActiveEvents.Count <= 0) + { + result.setFail(ServerErrorCode.QuestIdNotFound); + return result; + }; + + var current_task_num = questAttribute.CurrentTaskNum; + if (questTaskNum != current_task_num) + { + result.setFail(ServerErrorCode.QuestIdNotFound); + return result; + } + + do + { + if (current_task_num != questAttribute.CurrentTaskNum) break; + if (questAttribute.ActiveEvents.Count <= 0) break; + + // QuestTaskUpdateHandler 생성 + var questHandler = new QuestTaskUpdateHandler(player, (uint)questId, (uint)questRevision, questAttribute.ActiveEvents.FirstOrDefault() ?? string.Empty); + result = await questHandler.init(); + if (result.isFail()) break; + + // Quest Data 재배치 + result = replaceQuestData(questAttribute, questHandler); + if (result.isFail()) break; + + var quest_task_update_action = player.getEntityAction(); + result = quest_task_update_action.taskUpdateConditionCheck(ref questHandler, false); + if (result.isFail()) break; + + result = await updateAction.questTaskUpdate(questHandler); + + } while (true); + + if (result.isFail()) return result; + + var batch = new QueryBatchEx( player, LogActionType.QuestTaskUpdate + , GameServerApp.getServerLogic().getDynamoDbClient(), true); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + batch.addQuery(new QueryFinal()); + } + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + return result; + } + + private Result replaceQuestData(QuestAttribute quest, QuestTaskUpdateHandler questHandler) + { + var result = new Result(); + + // meta data 획득 + if (null == questHandler.m_quest_meta_info || false == questHandler.m_quest_meta_info.QuestTaskGroupList.TryGetValue(quest.CurrentTaskNum, out var questTaskGroup)) + { + result.setFail(ServerErrorCode.QuestIdNotFound); + return result; + } + + // data check + var activeEvent = quest.ActiveEvents.FirstOrDefault(); + if (string.IsNullOrEmpty(activeEvent) || questTaskGroup.EventStringList.Contains(activeEvent)) + return result; + + // handler 수정 + questHandler.m_active_event = questTaskGroup.EventStringList.FirstOrDefault() ?? string.Empty; + + // attribute 수정 + quest.ActiveEvents = questTaskGroup.EventStringList; + + return result; + } +} diff --git a/GameServer/Entity/Rank/Action/RankAction.cs b/GameServer/Entity/Rank/Action/RankAction.cs new file mode 100644 index 0000000..8e1eb87 --- /dev/null +++ b/GameServer/Entity/Rank/Action/RankAction.cs @@ -0,0 +1,156 @@ +using Amazon.OpenSearchService.Model.Internal.MarshallTransformations; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal class RankAction : EntityActionBase + { + public RankAction(Rank owner) + : base(owner) + { } + + public override async Task onInit() + { + var result = new Result(); + return await Task.FromResult(result); + } + + public override void onClear() + { + return; + } + + public Result tryLoadRankFromDoc(RankDoc rankDoc) + { + var result = new Result(); + var err_msg = string.Empty; + + var rank = getOwner() as Rank; + NullReferenceCheckHelper.throwIfNull(rank, () => $"rank is null !!!"); + + var rank_attribute = rank.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(rank_attribute, () => $"rank_attribute is null !!!"); + + if (!rank_attribute.copyEntityAttributeFromDoc(rankDoc)) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!! : from:{rankDoc.getTypeName()}, to:{rank_attribute.getTypeName()}"; + result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + return result; + } + + public Result fillupAttribute(string rankerGuid, EntityType rankerEntityType, int prevRankNum, int rankNum, int score, ScoreType scoreType) + { + var result = new Result(); + var err_msg = string.Empty; + + var rank = getOwner() as Rank; + NullReferenceCheckHelper.throwIfNull(rank, () => $"rank is null !!!"); + + var ranking = rank.getDirectParent() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_guid = ranking.getRankingGuid(); + + var rank_attribute = rank.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(rank_attribute, () => $"rank_attribute is null !!!"); + + rank_attribute.RankingGuid = ranking_guid; + rank_attribute.RankerGuid = rankerGuid; + rank_attribute.RankerEntityType = rankerEntityType; + rank_attribute.PrevRankNum = prevRankNum; + rank_attribute.RankNum = rankNum; + rank_attribute.Score = score; + rank_attribute.ScoreType = scoreType; + rank_attribute.newEntityAttribute(); + + return result; + } + + public async Task tryUpsertRankDocToDb() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var rank = getOwner() as Rank; + NullReferenceCheckHelper.throwIfNull(rank, () => $"rank is null !!!"); + + var rank_attribute = rank.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(rank_attribute, () => $"rank_attribute is null !!!"); + + (result, var rank_doc) = await rank_attribute.toDocBase(); + if (result.isFail() || rank_doc == null) + { + err_msg = $"Failed to toDocBase() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + result = await dynamo_db_client.simpleUpsertDocumentWithDocType(rank_doc); + if (result.isFail()) + { + err_msg = $"Failed to simpleUpsertDocumentWithDocType() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + + public async Task tryDeleteRankDocToDb() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var rank = getOwner() as Rank; + NullReferenceCheckHelper.throwIfNull(rank, () => $"rank is null !!!"); + + var rank_attribute = rank.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(rank_attribute, () => $"rank_attribute is null !!!"); + + rank_attribute.deleteEntityAttribute(); + + (result, var rank_doc) = await rank_attribute.toDocBase(); + if (result.isFail() || rank_doc == null) + { + err_msg = $"Failed to toDocBase() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + result = await dynamo_db_client.simpleDeleteDocumentWithDocType(rank_doc); + if (result.isFail()) + { + + err_msg = $"Failed to simpleDeleteDocumentWithDocType() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + } +} diff --git a/GameServer/Entity/Rank/Action/RankAgentAction.cs b/GameServer/Entity/Rank/Action/RankAgentAction.cs new file mode 100644 index 0000000..71c665a --- /dev/null +++ b/GameServer/Entity/Rank/Action/RankAgentAction.cs @@ -0,0 +1,309 @@ +using Amazon.EC2.Model; +using MongoDB.Driver.Core.Misc; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal class RankAgentAction : EntityActionBase + { + ConcurrentDictionary m_ranks = new(); + + public RankAgentAction(Ranking owner) + : base(owner) + { } + + public override async Task onInit() + { + var result = new Result(); + return await Task.FromResult(result); + } + + public override void onClear() + { + return; + } + + public async Task tryLoadAllRanksFromDb() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var db_client = server_logic.getDynamoDbClient(); + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_guid = ranking.getRankingGuid(); + + var doc = new RankDoc(); + doc.setCombinationKeyForPKSK(ranking_guid, DynamoDbClient.SK_EMPTY); + + var error_code = doc.onApplyPKSK(); + if (error_code.isFail()) + { + err_msg = $"Failed to onApplyPKSK() !!!"; + result.setFail(error_code, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var query_config = db_client.makeQueryConfigWithPKSKBySKBeginWith(doc.getPK(), RankDoc.getPrefixOfSK()); + (result, var read_docs) = await db_client.simpleQueryDocTypesWithQueryOperationConfig(query_config); + if (result.isFail()) + { + err_msg = $"Failed to simpleQueryDocTypesWithQueryOperationConfig() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var ranks = new ConcurrentDictionary(); + + foreach (var read_doc in read_docs) + { + var rank_attrib = read_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(rank_attrib, () => $"rank_attrib is null !!!"); + + var rank = new Rank(ranking); + await rank.onInit(); + + var rank_action = rank.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(rank_action, () => $"rank_action is null !!!"); + + result = rank_action.tryLoadRankFromDoc(read_doc); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadRankFromDoc() !!! : rankingGuid:{ranking_guid}, rankerGuid:{rank_attrib.RankerGuid}, rankerType:{rank_attrib.RankerEntityType}- {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var rank_attribute = rank.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(rank_attribute, () => $"ranker_attribute is null !!!"); + + if (!ranks.TryAdd(rank_attribute.RankNum, rank)) + { + err_msg = $"Failed to TryAdd() !!! : {rank.toBasicString()}"; + result.setFail(ServerErrorCode.RankerDocLoadDuplicatedRanker, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + } + + m_ranks = ranks; + + return result; + } + + public async Task tryRefreshRanks(Dictionary rankers) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var redis_connector = server_logic.getRedisConnector(); + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_guid = ranking.getRankingGuid(); + if (!ranking.tryGetRankingMetaData(out var ranking_meta_data)) + { + err_msg = $"Failed to tryGetRankingMetaData() !!! : {ranking.toBasicString()}"; + result.setFail(ServerErrorCode.RankingMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var ranking_cache_request = new RankingCacheRequest(ranking_guid, redis_connector); + (result, var redis_ranks) = await ranking_cache_request.getRedisRanks(0, 9, ranking_meta_data.SortType); + if (result.isFail()) + { + err_msg = $"Failed to getRanks() !!! : rankingGuid{ranking_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var ranks = new ConcurrentDictionary(); + + foreach (var (rank_num, ranker_guid, score) in redis_ranks) + { + if (!rankers.TryGetValue(ranker_guid, out var ranker)) + { + err_msg = $"Failed to TryGetValue() !!! : rankerGuid:{ranker_guid}"; + Log.getLogger().error(err_msg); + + continue; + } + + var ranker_attribute = ranker.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranker_attribute, () => $"ranker_attribute is null !!!"); + + if (ranker_attribute.Score == 0) + continue; + + var ranker_entity_type = ranker_attribute.RankerEntityType; + var old_rank_num = getRankNumFromRankerGuid(ranker_guid); + + var rank = new Rank(ranking); + await rank.onInit(); + + var rank_action = rank.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(rank_action, () => $"rank_action is null !!!"); + + result = rank_action.fillupAttribute(ranker_guid, ranker_entity_type, old_rank_num, rank_num, score, ranking_meta_data.ScoreType); + if (result.isFail()) + { + err_msg = $"Failed to fillupAttribute() !!! : rankerGuid:{ranker_guid}, rankerEntityType:{ranker_entity_type} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + + result = await rank_action.tryUpsertRankDocToDb(); + if (result.isFail()) + { + err_msg = $"Failed to tryUpsertRankDocToDb() !!! : {rank.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + + if (!ranks.TryAdd(rank_num, rank)) + { + err_msg = $"Failed to TryAdd() !!! : {rank.toBasicString()}"; + result.setFail(ServerErrorCode.RankDocLoadDuplicatedRank, err_msg); + Log.getLogger().error(result.toBasicString()); + + continue; + } + } + + m_ranks = ranks; + + await RankingNotifyHelper.send_GS2GS_NTF_REFRESH_RANK(ranking_guid); + + return result; + } + + public async Task tryDeleteRankDocs() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var db_client = server_logic.getDynamoDbClient(); + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_guid = ranking.getRankingGuid(); + + var doc = new RankDoc(); + doc.setCombinationKeyForPKSK(ranking_guid, DynamoDbClient.SK_EMPTY); + + var error_code = doc.onApplyPKSK(); + if (error_code.isFail()) + { + err_msg = $"Failed to onApplyPKSK() !!!"; + result.setFail(error_code, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var query_config = db_client.makeQueryConfigWithPKSKBySKBeginWith(doc.getPK(), RankDoc.getPrefixOfSK()); + (result, var read_docs) = await db_client.simpleQueryDocTypesWithQueryOperationConfig(query_config); + if (result.isFail()) + { + err_msg = $"Failed to simpleQueryDocTypesWithQueryOperationConfig() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + foreach (var read_doc in read_docs) + { + result = await db_client.simpleDeleteDocumentWithDocType(read_doc); + if (result.isFail()) + { + err_msg = $"Failed to simpleDeleteDocumentWithDocType() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + + await RankingNotifyHelper.send_GS2GS_NTF_REFRESH_RANK(ranking_guid); + + return result; + } + + public bool tryGetRankFromRankerGuid(string rankerGuid, [MaybeNullWhen(false)] out Rank outRank) + { + outRank = default; + + foreach (var rank in m_ranks.Values) + { + var rank_attribute = rank.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(rank_attribute, () => $"ranker_attribute is null !!!"); + + if (rankerGuid == rank_attribute.RankerGuid) + { + outRank = rank; + return true; + } + } + + return false; + } + + public int getRankNumFromRankerGuid(string rankerGuid) + { + var rankNum = 0; + + foreach (var rank in m_ranks.Values) + { + var rank_attribute = rank.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(rank_attribute, () => $"ranker_attribute is null !!!"); + + if (rankerGuid == rank_attribute.RankerGuid) + { + rankNum = rank_attribute.RankNum; + break; + } + } + + return rankNum; + } + + public async Task> getRankInfos() + { + var rank_infos = new List(); + + foreach (var rank in m_ranks.Values) + { + var rank_info = await rank.toRankerInfo(); + rank_infos.Add(rank_info); + } + + return rank_infos; + } + } +} diff --git a/GameServer/Entity/Rank/Helper/RankHelper.cs b/GameServer/Entity/Rank/Helper/RankHelper.cs new file mode 100644 index 0000000..8001a52 --- /dev/null +++ b/GameServer/Entity/Rank/Helper/RankHelper.cs @@ -0,0 +1,79 @@ +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ZstdSharp.Unsafe; + +namespace GameServer +{ + internal static class RankHelper + { + public static async Task toRankerInfo(this Rank ranker) + { + var rank_attribute = ranker.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(rank_attribute, () => $"rank_attribute is null !!!"); + + var rank_info = new RankInfo(); + + rank_info.RankerGuid = rank_attribute.RankerGuid; + rank_info.RankerEntityType = rank_attribute.RankerEntityType; + rank_info.RankNum = rank_attribute.RankNum; + rank_info.PrevRankNum = rank_attribute.PrevRankNum; + rank_info.Score = rank_attribute.Score; + rank_info.ScoreType = rank_attribute.ScoreType; + + if (rank_attribute.RankerGuid != string.Empty || rank_attribute.RankerEntityType == EntityType.Player) + { + var (result, nickname_attrib) = await NicknameDoc.findNicknameFromGuid(rank_attribute.RankerGuid); + if (result.isSuccess() && nickname_attrib != null) + { + rank_info.RankerName = nickname_attrib.Nickname; + } + } + + return rank_info; + } + + public static async Task tryDeleteRankDocTodb(string rankingGuid, string rankerGuid) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var ranking_manager = server_logic.getRankingManager(); + + if (!ranking_manager.tryGetRanking(rankingGuid, out var ranking)) + { + err_msg = $"Failed to tryGetRanking() !!! rankingGuid:{rankingGuid}"; + result.setFail(ServerErrorCode.RankingNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var rank_agent_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(rank_agent_action, () => $"rank_agent_action is null !!!"); + + if (rank_agent_action.tryGetRankFromRankerGuid(rankerGuid, out var rank)) + { + var rank_action = rank.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(rank_action, () => $"rank_action is null !!!"); + + result = await rank_action.tryDeleteRankDocToDb(); + if (result.isFail()) + { + err_msg = $"Failed to tryDeleteRankDocToDb() !!! : rankingGuid:{rankingGuid}, rankerGuid:{rankerGuid}"; + Log.getLogger().error(err_msg); + + return result; + } + } + + return result; + } + } +} diff --git a/GameServer/Entity/Rank/Rank.cs b/GameServer/Entity/Rank/Rank.cs new file mode 100644 index 0000000..1c19ebe --- /dev/null +++ b/GameServer/Entity/Rank/Rank.cs @@ -0,0 +1,38 @@ +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal class Rank : EntityBase + { + public Rank(Ranking ranking) + : base(EntityType.Ranker, ranking) + { + } + + public override async Task onInit() + { + addEntityAttribute(new RankAttribute(this)); + + addEntityAction(new RankAction(this)); + + return await base.onInit(); + } + + public override string toBasicString() + { + return $"{this.getTypeName()}, RankingGuid:{getOriginEntityAttribute()?.RankingGuid}, RankerGuid:{getOriginEntityAttribute()?.RankerGuid}, RankerEntityType:{getOriginEntityAttribute()?.RankerEntityType}, rankNum:{getOriginEntityAttribute()?.RankNum}"; + } + + public override string toSummaryString() + { + return $"{this.getTypeName()}, {getEntityAttribute()?.toBasicString()}"; + } + } +} diff --git a/GameServer/Entity/Ranker/Action/RankerAction.cs b/GameServer/Entity/Ranker/Action/RankerAction.cs new file mode 100644 index 0000000..1a500cf --- /dev/null +++ b/GameServer/Entity/Ranker/Action/RankerAction.cs @@ -0,0 +1,382 @@ +using Amazon.EC2.Model; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal class RankerAction : EntityActionBase + { + public RankerAction(Ranker owner) + : base(owner) + { } + + public override async Task onInit() + { + var result = new Result(); + return await Task.FromResult(result); + } + + public override void onClear() + { + return; + } + + public async Task<(Result, bool)> tryLoadRankerFromDb(string rankerGuid) + { + var result = new Result(); + var err_msg = string.Empty; + + var is_loaded = false; + + var server_logic = GameServerApp.getServerLogic(); + var db_client = server_logic.getDynamoDbClient(); + + var ranker = getOwner() as Ranker; + NullReferenceCheckHelper.throwIfNull(ranker, () => $"ranker is null !!!"); + + var ranking = ranker.getDirectParent() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_guid = ranking.getRankingGuid(); + + var doc = new RankerDoc(); + doc.setCombinationKeyForPKSK(ranking_guid, rankerGuid); + + var error_code = doc.onApplyPKSK(); + if (error_code.isFail()) + { + err_msg = $"Failed to onApplyPKSK() !!!"; + result.setFail(error_code, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, is_loaded); + } + + var query_config = db_client.makeQueryConfigForReadByPKSK(doc.getPK(), doc.getSK()); + (result, var read_docs) = await db_client.simpleQueryDocTypesWithQueryOperationConfig(query_config); + if (result.isFail()) + { + err_msg = $"Failed to simpleQueryDocTypesWithQueryOperationConfig() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, is_loaded); + } + + if (read_docs.Count > 0) + { + var ranker_doc = read_docs[0]; + + var ranker_attrib = ranker_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(ranker_attrib, () => $"ranker_attrib is null !!!"); + + result = tryLoadRankerFromDoc(ranker_doc); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadRankerFromDoc() !!! : rankingGuid:{ranking_guid}, rankerGuid:{ranker_attrib.RankerGuid}, rankerType:{ranker_attrib.RankerEntityType}- {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, is_loaded); + } + + is_loaded = true; + } + + return (result, is_loaded); + } + + public Result tryLoadRankerFromDoc(RankerDoc rankerDoc) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranker = getOwner() as Ranker; + NullReferenceCheckHelper.throwIfNull(ranker, () => $"ranker is null !!!"); + + var ranker_attribute = ranker.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranker_attribute, () => $"ranker_attribute is null !!!"); + + if (!ranker_attribute.copyEntityAttributeFromDoc(rankerDoc)) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!! : from:{rankerDoc.getTypeName()}, to:{ranker_attribute.getTypeName()}"; + result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + return result; + } + + public async Task tryUpdateRedis() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var redis_connector = server_logic.getRedisConnector(); + + var ranker = getOwner() as Ranker; + NullReferenceCheckHelper.throwIfNull(ranker, () => $"ranker is null !!!"); + + var ranker_attribute = ranker.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranker_attribute, () => $"ranker_attribute is null !!!"); + + var ranking_guid = ranker_attribute.RankingGuid; + var ranker_guid = ranker_attribute.RankerGuid; + var score = ranker_attribute.Score; + + if (score != 0) + { + if (!tryGetCalculateScore(out var update_score)) + { + err_msg = $"Failed to tryGetCalculateScore() !!! : {ranker.toBasicString()}"; + result.setFail(ServerErrorCode.RankingMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var ranking_cache_request = new RankingCacheRequest(ranking_guid, redis_connector); + result = await ranking_cache_request.setRankScore(ranker_guid, update_score); + if (result.isFail()) + { + err_msg = $"Failed to setRankScore() !!! : rankingGuid:{ranking_guid}, rankerGuid:{ranker_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + else + { + var ranking_cache_request = new RankingCacheRequest(ranking_guid, redis_connector); + result = await ranking_cache_request.removeRank(ranker_guid); + if (result.isFail()) + { + err_msg = $"Failed to removeRank() !!! : rankingGuid:{ranking_guid}, rankerGuid:{ranker_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + result = await RankHelper.tryDeleteRankDocTodb(ranking_guid, ranker_guid); + if (result.isFail()) + { + err_msg = $"Failed to tryDeleteRankDocTodb() !!! : rankingGuid:{ranking_guid}, rankerGuid:{ranker_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + + return result; + } + + public async Task tryRemoveRedis() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var redis_connector = server_logic.getRedisConnector(); + + var ranker = getOwner() as Ranker; + NullReferenceCheckHelper.throwIfNull(ranker, () => $"ranker is null !!!"); + + var ranker_attribute = ranker.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranker_attribute, () => $"ranker_attribute is null !!!"); + + var ranking_guid = ranker_attribute.RankingGuid; + var ranker_guid = ranker_attribute.RankerGuid; + + var ranking_cache_request = new RankingCacheRequest(ranking_guid, redis_connector); + result = await ranking_cache_request.removeRank(ranker_guid); + if (result.isFail()) + { + err_msg = $"Failed to removeRank() !!! : rankingGuid:{ranking_guid}, rankerGuid:{ranker_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + + bool tryGetCalculateScore([MaybeNullWhen(false)] out double score) + { + var err_msg = string.Empty; + + score = 0.0d; + + var ranker = getOwner() as Ranker; + NullReferenceCheckHelper.throwIfNull(ranker, () => $"ranker is null !!!"); + + var ranker_attribute = ranker.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranker_attribute, () => $"ranker_attribute is null !!!"); + + var ranking = ranker.getDirectParent() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + if (!ranking.tryGetRankingMetaData(out var ranking_meta_data)) + { + err_msg = $"Failed to tryGetRankingMetaData() !!! : {ranking.toBasicString()}"; + Log.getLogger().error(err_msg); + + return false; + } + + var under_point_score = 0.0d; + switch (ranking_meta_data.SortType) + { + case SortType.Ascending: + { + under_point_score = ((double)ranker_attribute.UpdateTime.Ticks / (double)DateTime.MaxValue.Ticks); + } + break; + case SortType.Descending: + { + under_point_score = 1 - ((double)ranker_attribute.UpdateTime.Ticks / (double)DateTime.MaxValue.Ticks); + } + break; + default: + break; + } + + score = ranker_attribute.Score + under_point_score; + + return true; + } + + public async Task trySnapshot(DateTime snapshotTime) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var redis_connector = server_logic.getRedisConnector(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var ranker = getOwner() as Ranker; + NullReferenceCheckHelper.throwIfNull(ranker, () => $"ranker is null !!!"); + + var ranker_attribute = ranker.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranker_attribute, () => $"ranker_attribute is null !!!"); + + if (ranker_attribute.Score == 0) + return result; + + var ranker_guid = ranker_attribute.RankerGuid; ; + + var ranking = ranker.getDirectParent() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + if (!ranking.tryGetRankingMetaData(out var ranking_meta_data)) + { + err_msg = $"Failed to tryGetRankingMetaData() !!! : {ranking.toBasicString()}"; + result.setFail(ServerErrorCode.RankingMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + var ranking_guid = ranking_attribute.RankingGuid; + var snapshot_index = ranking_attribute.LastestSnapshotIndex + 1; + + var ranking_cache_request = new RankingCacheRequest(ranking_guid, redis_connector); + (result, var rank_num) = await ranking_cache_request.getRedisRank(ranker_guid, ranking_meta_data.SortType); + if (result.isFail()) + { + err_msg = $"Failed to getRedisRank() !!! : rankingGuid:{ranking_guid}, rankerGuid:{ranker_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var ranker_snapshot_doc = new RankerSnapshotDoc(ranking_guid, snapshot_index, ranker_guid); + var ranker_snapshot_attrib = ranker_snapshot_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(ranker_snapshot_attrib, () => $"rankier_snapshot_attrib is null !!!"); + + ranker_snapshot_attrib.RankingGuid = ranking_guid; + ranker_snapshot_attrib.SnapshotIndex = snapshot_index; + ranker_snapshot_attrib.SnapshotTime = snapshotTime; + + ranker_snapshot_attrib.RankerGuid = ranker_guid; + ranker_snapshot_attrib.RankerEntityType = ranker_attribute.RankerEntityType; + ranker_snapshot_attrib.Score = ranker_attribute.Score; + ranker_snapshot_attrib.ScoreType = ranker_attribute.ScoreType; + ranker_snapshot_attrib.UpdateTime = ranker_attribute.UpdateTime; + ranker_snapshot_attrib.RankNum = rank_num; + + result = await dynamo_db_client.simpleInsertDocumentWithDocType(ranker_snapshot_doc); + if (result.isFail()) + { + err_msg = $"Failed to simpleInsertDocumentWithDocType() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + + public async Task tryInitAndUpdateRankerDocToDb(DateTime initTime) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var ranker = getOwner() as Ranker; + NullReferenceCheckHelper.throwIfNull(ranker, () => $"ranker is null !!!"); + + var ranker_attribute = ranker.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranker_attribute, () => $"ranker_attribute is null !!!"); + + initScore(initTime); + + (result, var ranker_doc) = await ranker_attribute.toDocBase(); + if (result.isFail() || ranker_doc == null) + { + err_msg = $"Failed to toDocBase() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + result = await dynamo_db_client.simpleUpdateDocumentWithDocType(ranker_doc); + if (result.isFail()) + { + err_msg = $"Failed to simpleUpdateDocumentWithDocType() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + + public void initScore(DateTime initTime) + { + var ranker = getOwner() as Ranker; + NullReferenceCheckHelper.throwIfNull(ranker, () => $"ranker is null !!!"); + + var ranker_attribute = ranker.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranker_attribute, () => $"ranker_attribute is null !!!"); + + ranker_attribute.Score = 0; + ranker_attribute.UpdateTime = initTime; + ranker_attribute.modifiedEntityAttribute(); + } + } +} diff --git a/GameServer/Entity/Ranker/Action/RankerAgentAction.cs b/GameServer/Entity/Ranker/Action/RankerAgentAction.cs new file mode 100644 index 0000000..331f19b --- /dev/null +++ b/GameServer/Entity/Ranker/Action/RankerAgentAction.cs @@ -0,0 +1,89 @@ +using MongoDB.Driver.Core.Misc; +using ServerBase; +using ServerCommon; +using ServerCore; +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 +{ + internal class RankerAgentAction : EntityActionBase + { + ConcurrentDictionary m_rankers = new(); + + public RankerAgentAction(Ranking owner) + : base(owner) + { } + + public override async Task onInit() + { + var result = new Result(); + return await Task.FromResult(result); + } + + public override void onClear() + { + return; + } + + public async Task tryLoadRankerFromDb(string rankerGuid) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_guid = ranking.getRankingGuid(); + + var ranker = new Ranker(ranking); + await ranker.onInit(); + + var ranker_action = ranker.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranker_action, () => $"ranker_action is null !!!"); + + (result, var is_loaded) = await ranker_action.tryLoadRankerFromDb(rankerGuid); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadRankerFromDb() !!! : rankingGuid:{ranking_guid}, rankerGuid:{rankerGuid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + if (is_loaded) + { + if (!m_rankers.TryAdd(rankerGuid, ranker)) + { + err_msg = $"Failed to TryAdd() !!! : {ranker.toBasicString()}"; + result.setFail(ServerErrorCode.RankerDocLoadDuplicatedRanker, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + } + + return result; + } + + public bool tryGetRanker(string rankerGuid, [MaybeNullWhen(false)] out Ranker ranker) + { + return m_rankers.TryGetValue(rankerGuid, out ranker); + } + + public void addRanker(string rankerGuid, Ranker ranker) + { + m_rankers[rankerGuid] = ranker; + } + + public void removeRanker(string rankerGuid) + { + m_rankers.TryRemove(rankerGuid, out _); + } + } +} diff --git a/GameServer/Entity/Ranker/Helper/RankerBusinessLogHelper.cs b/GameServer/Entity/Ranker/Helper/RankerBusinessLogHelper.cs new file mode 100644 index 0000000..dd7fbb7 --- /dev/null +++ b/GameServer/Entity/Ranker/Helper/RankerBusinessLogHelper.cs @@ -0,0 +1,22 @@ +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal static class RankerBusinessLogHelper + { + public static RankerLogInfo toRankerLogInfo(string rankingGuid, string rankerGuid, EntityType rankerEntityType, int modifyScore, ScoreModifyType scoreModifyType) + { + var ranker_log_info = new RankerLogInfo(); + ranker_log_info.setLogProperty(rankingGuid, rankerGuid, rankerEntityType, modifyScore, scoreModifyType); + + return ranker_log_info; + } + } +} diff --git a/GameServer/Entity/Ranker/Helper/RankerHelper.cs b/GameServer/Entity/Ranker/Helper/RankerHelper.cs new file mode 100644 index 0000000..673f43e --- /dev/null +++ b/GameServer/Entity/Ranker/Helper/RankerHelper.cs @@ -0,0 +1,227 @@ +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using Amazon.EC2.Model; +using MongoDB.Driver.Core.Misc; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal static class RankerHelper + { + public static async Task<(Result, Ranker?)> tryMakeRanker(Ranking ranking, string rankerGuid, EntityType rankerEntityType, int score) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranking_guid = ranking.getRankingGuid(); + + if (!ranking.tryGetRankingMetaData(out var ranking_meta_data)) + { + err_msg = $"Failed to tryGetRankingMetaData() !!! {ranking.toBasicString()}"; + result.setFail(ServerErrorCode.RankingMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, null); + } + + var ranker = new Ranker(ranking); + await ranker.onInit(); + + var ranker_attribute = ranker.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranker_attribute, () => $"ranking_attribute is null !!!"); + + ranker_attribute.RankingGuid = ranking_guid; + ranker_attribute.RankerGuid = rankerGuid; + ranker_attribute.RankerEntityType = rankerEntityType; + ranker_attribute.Score = score; + ranker_attribute.ScoreType = ranking_meta_data.ScoreType; + ranker_attribute.UpdateTime = DateTime.UtcNow; + ranker_attribute.newEntityAttribute(); + + return (result, ranker); + } + + public static async Task tryUpdateRedisRankers(Dictionary rankers, string rankingGuid) + { + var result = new Result(); + var err_msg = string.Empty; + + foreach (var ranker in rankers.Values) + { + var ranker_action = ranker.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranker_action, () => $"ranker_action is null !!!"); + + result = await ranker_action.tryUpdateRedis(); + if (result.isFail()) + { + err_msg = $"Failed to tryUpdateRedis() !!! : {ranker.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + } + + return result; + } + + public static async Task trySnapshotRankers(Dictionary rankers, DateTime updateTime) + { + var result = new Result(); + var err_msg = string.Empty; + + foreach (var ranker in rankers.Values) + { + var ranker_action = ranker.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranker_action, () => $"ranker_action is null !!!"); + + result = await ranker_action.trySnapshot(updateTime); + if (result.isFail()) + { + err_msg = $"Failed to trySnapshot() !!! : {ranker.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + } + + return result; + } + + public static async Task tryInitializeRankers(Dictionary rankers, DateTime initTime) + { + var result = new Result(); + var err_msg = string.Empty; + + foreach (var ranker in rankers.Values) + { + var ranker_action = ranker.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranker_action, () => $"ranker_action is null !!!"); + + result = await ranker_action.tryInitAndUpdateRankerDocToDb(initTime); + if (result.isFail()) + { + err_msg = $"Failed to tryInitAndUpdateRankerDocToDb() !!! : {ranker.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + + result = await ranker_action.tryRemoveRedis(); + if (result.isFail()) + { + err_msg = $"Failed to tryRemoveRedis() !!! : {ranker.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + } + + return result; + } + + public static (Result, DynamoDbItemRequestQueryContext?) tryMakeUpdateItemRequestFromRanker(string rankingGuid, string rankerGuid, int score, ScoreType scoreType, ScoreModifyType scoreModifyType, SortType sortType) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var doc = new RankerDoc(rankingGuid, rankerGuid); + var primary_key = doc.getPrimaryKey(); + var attrib_path_json_string = doc.toJsonStringOfAttribs(); + + var query_builder = new DynamoDbItemRequestHelper.UpdateItemRequestBuilder(dynamo_db_client.getTableFullName(doc.TableName)); + query_builder.withKeys(primary_key.toKeyWithAttributeValue()); + + var propertyNames = new List(); + propertyNames.Add(nameof(RankerAttrib.Score)); + propertyNames.Add(nameof(RankerAttrib.UpdateTime)); + + var target_keys = JsonHelper.getJsonPropertyNames(propertyNames); + (var is_success, var attribute_expressions) = DynamoDbClientHelper.toAttributeExpressionsFromJson(attrib_path_json_string, target_keys); + if (false == is_success) + { + err_msg = $"Failed to DynamoDbClientHelper.toAttributeExpressionFromJson() !!! : attribPath:{attrib_path_json_string}, targetKeys:{target_keys}"; + result.setFail(ServerErrorCode.AttribPathMakeFailed, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, null); + } + + var attributeNames = DynamoDbClientHelper.toExpressionAttributeNamesFromJson(attrib_path_json_string, target_keys); + query_builder.withExpressionAttributeNames(attributeNames); + + var update_expression = string.Empty; + var condition_expression = string.Empty; + var expression_attribute_values = new Dictionary(); + + switch (scoreModifyType) + { + case ScoreModifyType.Overwrite: + { + // update + update_expression = $"SET {attribute_expressions[nameof(RankerAttrib.Score)]} = :changeValue"; + update_expression += $", {attribute_expressions[nameof(RankerAttrib.UpdateTime)]} = :updateTime"; + + // condition + condition_expression = (sortType == SortType.Ascending) + ? $"{attribute_expressions[nameof(RankerAttrib.Score)]} >= :changeValue" + : $"{attribute_expressions[nameof(RankerAttrib.Score)]} <= :changeValue"; + condition_expression += $" OR {attribute_expressions[nameof(RankerAttrib.Score)]} = :InitValue"; + query_builder.withConditionExpression(condition_expression); + + // attribute_values + expression_attribute_values.TryAdd(":changeValue", new AttributeValue { N = Math.Abs(score).ToString() }); + expression_attribute_values.TryAdd(":updateTime", new AttributeValue { S = DateTime.UtcNow.toStringWithUtcIso8601() }); + expression_attribute_values.TryAdd(":InitValue", new AttributeValue { N = "0" }); + } + break; + case ScoreModifyType.Increase: + { + // update + update_expression = (score >= 0) + ? $"SET {attribute_expressions[nameof(RankerAttrib.Score)]} = if_not_exists({attribute_expressions[nameof(RankerAttrib.Score)]}, :start) + :changeValue" + : $"SET {attribute_expressions[nameof(RankerAttrib.Score)]} = if_not_exists({attribute_expressions[nameof(RankerAttrib.Score)]}, :start) - :changeValue"; + update_expression += $", {attribute_expressions[nameof(RankerAttrib.UpdateTime)]} = :updateTime"; + + // attribute_values + expression_attribute_values.TryAdd(":changeValue", new AttributeValue { N = Math.Abs(score).ToString() }); + expression_attribute_values.TryAdd(":updateTime", new AttributeValue { S = DateTime.UtcNow.toStringWithUtcIso8601() }); + expression_attribute_values.TryAdd(":start", new AttributeValue { N = "0" }); + } + break; + default: + break; + } + + query_builder.withUpdateExpression(update_expression); + query_builder.withConditionExpression(condition_expression); + query_builder.withExpressionAttributeValues(expression_attribute_values); + query_builder.withReturnValues(ReturnValue.ALL_NEW); + + (result, var update_item_request) = query_builder.build(); + if (result.isFail()) + { + err_msg = $"Failed to DynamoDbItemRequestHelper.build() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, null); + } + NullReferenceCheckHelper.throwIfNull(update_item_request, () => $"update_item_request is null !!! - {primary_key.toBasicString()}"); + + // ItemRequest 쿼리 실행시, 오류리턴에 대한 에러코드를 서버에러코드로 변경 처리 + // 조건 불일치에 대한 에러 ( DynamoDbQueryExceptionNotifier.ConditionalCheckFailed -> ServerErrorCode.DynamoDbItemRequestConditionFail ) + var exception_handler = new DynamoDbQueryExceptionNotifier.ExceptionHandler(DynamoDbQueryExceptionNotifier.ConditionalCheckFailed, ServerErrorCode.DynamoDbItemRequestConditionFail); + + return (result, update_item_request.createItemRequestQueryContext(QueryType.Update, exception_handler)); + } + } +} diff --git a/GameServer/Entity/Ranker/Ranker.cs b/GameServer/Entity/Ranker/Ranker.cs new file mode 100644 index 0000000..0ad463c --- /dev/null +++ b/GameServer/Entity/Ranker/Ranker.cs @@ -0,0 +1,46 @@ +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + public class Ranker : EntityBase + {public Ranker(Ranking ranking) + : base(EntityType.Ranker, ranking) + { + } + + public override async Task onInit() + { + addEntityAttribute(new RankerAttribute(this)); + + addEntityAction(new RankerAction(this)); + + return await base.onInit(); + } + + public override string toBasicString() + { + return $"{this.getTypeName()}, RankingGuid:{getOriginEntityAttribute()?.RankingGuid}, RankerGuid:{getOriginEntityAttribute()?.RankerGuid}, RankerEntityType:{getOriginEntityAttribute()?.RankerEntityType}"; + } + + public override string toSummaryString() + { + return $"{this.getTypeName()}, {getEntityAttribute()?.toBasicString()}"; + } + + + public string getRankerGuid() + { + var ranker_attribute = getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranker_attribute, () => $"ranker_attribute is null !!!"); + + return ranker_attribute.RankerGuid; + } + } +} diff --git a/GameServer/Entity/Ranker/RankerCheat.cs b/GameServer/Entity/Ranker/RankerCheat.cs new file mode 100644 index 0000000..1ca8c3f --- /dev/null +++ b/GameServer/Entity/Ranker/RankerCheat.cs @@ -0,0 +1,53 @@ +using ServerBase; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + [ChatCommandAttribute("updaterankingscore", typeof(RankerCheatUpdateRankingScore), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] + internal class RankerCheatUpdateRankingScore : ChatCommandBase + { + public override async Task invoke(Player player, string token, string[] args) + { + Log.getLogger().info($"Call updaterankingscore !!! - {player.toBasicString()}"); + + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + + if (args.Length != 2) + { + err_msg = $"Not enough argument !!! : argCount:{args.Length} != 2 - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + + var ranking_guid = args[0]; + + if (!int.TryParse(args[1], out var score)) + { + err_msg = $"addrankingschedule param parsing Error args : {args[1]}"; + Log.getLogger().error(err_msg); + + return; + } + + var ranker_guid = player.getUserGuid(); + + result = await RankingHelper.tryUpdateRankingScore(ranking_guid, ranker_guid, EntityType.Player, score); + if (result.isFail()) + { + err_msg = $"Failed to tryUpdateRankingScore() !!! : rankingGuid:{ranking_guid}, rankerGuid:{ranker_guid}, rankerEntityType:{EntityType.Player} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + } + } +} diff --git a/GameServer/Entity/Ranking/Action/RankingAction.cs b/GameServer/Entity/Ranking/Action/RankingAction.cs new file mode 100644 index 0000000..f9100ac --- /dev/null +++ b/GameServer/Entity/Ranking/Action/RankingAction.cs @@ -0,0 +1,576 @@ +using Org.BouncyCastle.Tls; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal class RankingAction : EntityActionBase + { + public RankingAction(Ranking owner) + : base(owner) + { } + + public override async Task onInit() + { + var result = new Result(); + return await Task.FromResult(result); + } + + public override void onClear() + { + return; + } + + public async Task tryLoadRankingFromDb(string rankingGuid) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + var db_client = server_logic.getDynamoDbClient(); + + var doc = new RankingDoc(); + doc.setCombinationKeyForPK(rankingGuid); + + var error_code = doc.onApplyPKSK(); + if (error_code.isFail()) + { + err_msg = $"Failed to onApplyPKSK() !!!"; + result.setFail(error_code, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var query_config = db_client.makeQueryConfigForReadByPKSK(doc.getPK()); + (result, var read_docs) = await db_client.simpleQueryDocTypesWithQueryOperationConfig(query_config); + if (result.isFail()) + { + err_msg = $"Failed to simpleQueryDocTypesWithQueryOperationConfig() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + if (read_docs.Count == 0) + { + // Attribute 채우기 + result = fillupAttributeFromRankingGuid(rankingGuid); + if (result.isFail()) + { + err_msg = $"Failed to fillupAttributeFromRankingGuid() !!! : rankingGuid:{rankingGuid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(rankingGuid, server_name, 10); + if (is_success) + { + // Insert RankingDoc + // RankingDoc 다른 서버 동기화 불필요 + result = await tryInsertRankingDocToDb(); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(rankingGuid, server_name); + + if (result.isFail()) + { + err_msg = $"Failed to tryInsertRankingDocToDb() !!! : rankingGuid:{rankingGuid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + } + else + { + if (!ranking_attribute.copyEntityAttributeFromDoc(read_docs[0])) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!! : from:{read_docs[0].getTypeName()}, to:{ranking_attribute.getTypeName()}"; + result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + if (!RankingScheduleHelper.tryGetRankingScheduleAttribute(rankingGuid, out var ranking_schedule_attribute)) + { + err_msg = $"Failed to tryGetRankingScheduleAttribute() !!! : rankingGuid:{rankingGuid}"; + result.setFail(ServerErrorCode.RankingScheduleNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var current_time = DateTime.UtcNow; + bool is_next_time_update = false; + foreach (IntervalType interval_type in Enum.GetValues(typeof(IntervalType))) + { + if (!ranking_schedule_attribute.IntervalTimes.TryGetValue(interval_type, out var interval_time)) + continue; + + if (!ranking_attribute.NextTimes.TryGetValue(interval_type, out var loaded_next_time)) + continue; + + if (current_time <= loaded_next_time) + continue; + + var new_next_time = RankingHelper.getNextTime(current_time, loaded_next_time, interval_time); + + ranking_attribute.NextTimes[interval_type] = new_next_time; + ranking_attribute.modifiedEntityAttribute(); + is_next_time_update = true; + } + + if (is_next_time_update) + { + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(rankingGuid, server_name, 10); + if (is_success) + { + // Update RankingDoc + // RankingDoc 다른 서버 동기화 불필요 + result = await tryUpsertRankingDocToDb(); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(rankingGuid, server_name); + + if (result.isFail()) + { + err_msg = $"Failed to tryUpsertRankingDocToDb() !!! : rankingGuid:{rankingGuid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + } + } + + return result; + } + + public Result fillupAttributeFromRankingGuid(string rankingGuid) + { + var result = new Result(); + var err_msg = string.Empty; + + if (!RankingScheduleHelper.tryGetRankingScheduleAttribute(rankingGuid, out var ranking_schedule_attribute)) + { + err_msg = $"Failed to tryGetRankingScheduleAttribute() !!! : rankingGuid:{rankingGuid}"; + result.setFail(ServerErrorCode.RankingScheduleNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + result = fillupAttributeFromRankingScheduleAttribute(ranking_schedule_attribute); + if (result.isFail()) + { + err_msg = $"Failed to fillupAttributeFromRankingScheduleAttribute() !!! : rankingGuid:{rankingGuid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + + public Result fillupAttributeFromRankingScheduleAttribute(RankingScheduleAttribute rankingScheduleAttribute) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + ranking_attribute.RankingGuid = rankingScheduleAttribute.RankingGuid; + ranking_attribute.RankingMetaId = rankingScheduleAttribute.RankingMetaId; + + var update_time = DateTime.UtcNow; + foreach (IntervalType interval_type in Enum.GetValues(typeof(IntervalType))) + { + if (!rankingScheduleAttribute.IntervalTimes.TryGetValue(interval_type, out var interval_time)) + continue; + + var next_time = RankingHelper.getNextTime(update_time, rankingScheduleAttribute.IntervalBaseTime, interval_time); + ranking_attribute.NextTimes[interval_type] = next_time; + } + + ranking_attribute.newEntityAttribute(); + + return result; + } + + public async Task tryInsertRankingDocToDb() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + (result, var ranking_doc) = await ranking_attribute.toDocBase(); + if (result.isFail() || ranking_doc == null) + { + err_msg = $"Failed to toDocBase() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + result = await dynamo_db_client.simpleInsertDocumentWithDocType(ranking_doc); + if (result.isFail()) + { + err_msg = $"Failed to simpleInsertDocumentWithDocType() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + + public async Task tryUpsertRankingDocToDb() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + (result, var ranking_doc) = await ranking_attribute.toDocBase(); + if (result.isFail() || ranking_doc == null) + { + err_msg = $"Failed to toDocBase() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + result = await dynamo_db_client.simpleUpsertDocumentWithDocType(ranking_doc); + if (result.isFail()) + { + err_msg = $"Failed to simpleUpsertDocumentWithDocType() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + + public async Task tryUpdateRanking(DateTime updateTime) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + var ranking_guid = ranking_attribute.RankingGuid; + + if (!checkUpdateRanking(updateTime, out var interval_updates)) + return result; + + // DB 에서 모든 Ranker Load + (result, var db_all_rankers) = await RankingHelper.tryLoadAllRankersFromDb(ranking); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadAllRankersFromDb() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + bool is_ranking_attribute_modify = false; + foreach (var (interval_type, interval_next_time) in interval_updates) + { + result = await tryIntervalUpdate(interval_type, db_all_rankers, updateTime); + if (result.isFail()) + { + err_msg = $"Failed to tryIntervalUpdate() !!! : {ranking.toBasicString()}, intervalType:{interval_type} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + + if (interval_type == IntervalType.Snapshot) + ++ranking_attribute.LastestSnapshotIndex; + + ranking_attribute.NextTimes[interval_type] = interval_next_time; + ranking_attribute.modifiedEntityAttribute(); + is_ranking_attribute_modify = true; + } + + if (is_ranking_attribute_modify) + { + // Update RankingDoc + // RankingDoc 다른 서버 동기화 필요 + result = await tryUpsertRankingDocToDb(); + if (result.isFail()) + { + err_msg = $"Failed to tryInsertRankingDocToDb() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + await RankingNotifyHelper.send_GS2GS_NTF_MODIFY_RANKING_INFO(ranking_guid); + } + + return result; + } + + public bool checkUpdateRanking(DateTime checkTime, [MaybeNullWhen(false)] out Dictionary IntervalNextTime) + { + var err_msg = string.Empty; + + IntervalNextTime = new(); + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + var ranking_guid = ranking_attribute.RankingGuid; + + if (!RankingScheduleHelper.tryGetRankingScheduleAttribute(ranking_guid, out var ranking_schedule_attribute)) + { + err_msg = $"Failed to tryGetRankingScheduleAttribute() !!! : rankingGuid:{ranking_guid}"; + Log.getLogger().error(err_msg); + + return false; + } + + var update = false; + var ranking_end_time = ranking_schedule_attribute.EndTime; + + foreach (IntervalType interval_type in Enum.GetValues(typeof(IntervalType))) + { + if (!ranking_schedule_attribute.IntervalTimes.TryGetValue(interval_type, out var interval_time)) + continue; + + if (!ranking_attribute.NextTimes.TryGetValue(interval_type, out var old_next_time)) + continue; + + if (old_next_time <= ranking_end_time && old_next_time <= checkTime) + { + var new_next_time = RankingHelper.getNextTime(checkTime, old_next_time, interval_time); + IntervalNextTime[interval_type] = new_next_time; + update = true; + } + } + + return update; + } + + public async Task tryIntervalUpdate(IntervalType intervalType, Dictionary rankers, DateTime updateTime) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_guid = ranking.getRankingGuid(); + + switch (intervalType) + { + case IntervalType.Refresh: // 랭킹 갱신 + { + result = await RankerHelper.tryUpdateRedisRankers(rankers, ranking_guid); + if (result.isFail()) + { + err_msg = $"Failed to tryRefreshRankers() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var rank_agent_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(rank_agent_action, () => $"rank_agent_action is null !!!"); + + result = await rank_agent_action.tryRefreshRanks(rankers); + if (result.isFail()) + { + err_msg = $"Failed to tryRefreshRank() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + break; + case IntervalType.Snapshot: // 랭킹 저장 + { + result = await trySnapshot(updateTime); + if (result.isFail()) + { + err_msg = $"Failed to trySnapshot() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + result = await RankerHelper.trySnapshotRankers(rankers, updateTime); + if (result.isFail()) + { + err_msg = $"Failed to trySnapshotRankers() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + break; + case IntervalType.Initialization: // 랭킹 초기화 + { + result = await RankerHelper.tryInitializeRankers(rankers, updateTime); + if (result.isFail()) + { + err_msg = $"Failed to tryInitializeRankers() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var rank_agent_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(rank_agent_action, () => $"rank_agent_action is null !!!"); + + result = await rank_agent_action.tryDeleteRankDocs(); + if (result.isFail()) + { + err_msg = $"Failed to tryInitializeRanks() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + break; + default: + break; + } + + return result; + } + + public async Task trySnapshot(DateTime snapshotTime) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + var ranking_guid = ranking_attribute.RankingGuid; + var snapshot_index = ranking_attribute.LastestSnapshotIndex + 1; + + var ranking_snapshot_doc = new RankingSnapshotDoc(ranking_guid, snapshot_index); + var ranking_snapshot_attrib = ranking_snapshot_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(ranking_snapshot_attrib, () => $"ranking_snapshot_attrib is null !!!"); + + ranking_snapshot_attrib.RankingGuid = ranking_guid; + ranking_snapshot_attrib.SnapshotIndex = snapshot_index; + ranking_snapshot_attrib.SnapshotTime = snapshotTime; + + result = await dynamo_db_client.simpleInsertDocumentWithDocType(ranking_snapshot_doc); + if (result.isFail()) + { + err_msg = $"Failed to simpleInsertDocumentWithDocType() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + + public async Task tryUpdateRankingByIntervalType(IntervalType intervalType) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranking = getOwner() as Ranking; + NullReferenceCheckHelper.throwIfNull(ranking, () => $"ranking is null !!!"); + + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + var ranking_guid = ranking_attribute.RankingGuid; + var update_time = DateTime.UtcNow; + + // DB 에서 모든 Ranker Load + (result, var db_all_rankers) = await RankingHelper.tryLoadAllRankersFromDb(ranking); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadAllRankersFromDb() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + result = await tryIntervalUpdate(intervalType, db_all_rankers, update_time); + if (result.isFail()) + { + err_msg = $"Failed to tryIntervalUpdate() !!! : {ranking.toBasicString()}, intervalType:{intervalType} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + if (intervalType == IntervalType.Snapshot) + { + ++ranking_attribute.LastestSnapshotIndex; + ranking_attribute.modifiedEntityAttribute(); + + // Update RankingDoc + // RankingDoc 다른 서버 동기화 필요 + result = await tryUpsertRankingDocToDb(); + if (result.isFail()) + { + err_msg = $"Failed to tryInsertRankingDocToDb() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + await RankingNotifyHelper.send_GS2GS_NTF_MODIFY_RANKING_INFO(ranking_guid); + } + + return result; + } + } +} diff --git a/GameServer/Entity/Ranking/Helper/RankingBusinessLogHelper.cs b/GameServer/Entity/Ranking/Helper/RankingBusinessLogHelper.cs new file mode 100644 index 0000000..eb05e71 --- /dev/null +++ b/GameServer/Entity/Ranking/Helper/RankingBusinessLogHelper.cs @@ -0,0 +1,33 @@ +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal static class RankingBusinessLogHelper + { + public static RankingLogInfo toRankingLogInfo(this Ranking ranking) + { + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + var ranking_log_info = new RankingLogInfo(); + ranking_log_info.setRankingInfo(ranking_attribute); + + return ranking_log_info; + } + + public static void setRankingInfo(this RankingLogInfo log, RankingAttribute rankingAttribute) + { + log.setLogProperty( + rankingAttribute.RankingGuid, + rankingAttribute.RankingMetaId + ); + } + } +} diff --git a/GameServer/Entity/Ranking/Helper/RankingCacheHelper.cs b/GameServer/Entity/Ranking/Helper/RankingCacheHelper.cs new file mode 100644 index 0000000..90a73c1 --- /dev/null +++ b/GameServer/Entity/Ranking/Helper/RankingCacheHelper.cs @@ -0,0 +1,127 @@ +using ServerBase; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal static class RankingCacheHelper + { + static string makeRankingLockKey(string rankingGuid) + { + return $"ranking_update:write_lock:{rankingGuid}"; + } + + static string makeRankingLockerId(string requestorId) + { + return $"ranking_update:locker_id:{requestorId}"; + } + + public static async Task tryAcquireWriteLockWithRankingUpdate(string rankingGuid, string requestorId, int ttlSec) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var redis_connector = server_logic.getRedisConnector(); + var redis_lua_executor = server_logic.getRedisWithLuaScriptExecutor(); + + var lock_key = makeRankingLockKey(rankingGuid); + var locker_id = makeRankingLockerId(requestorId); + + var is_success = await redis_lua_executor.tryAcquireLock(lock_key, locker_id, ttlSec); + if (false == is_success) + { + err_msg = $"Failed to tryAcquireLock() !!! : LockKey:{lock_key}, LockerId:{locker_id}, TTLSec:{ttlSec} - requstorId:{requestorId}"; + Log.getLogger().warn(err_msg); + + return false; + } + + return true; + } + + public static async Task tryReleaseWriteLockWithRankingUpdate(string rankingGuid, string requestorId) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var redis_connector = server_logic.getRedisDb(); + var redis_lua_executor = server_logic.getRedisWithLuaScriptExecutor(); + + var lock_key = makeRankingLockKey(rankingGuid); + var locker_id = makeRankingLockerId(requestorId); + + var is_success = await redis_lua_executor.tyrReleaseLock(lock_key, locker_id); + if (false == is_success) + { + err_msg = $"Failed to tyrReleaseLock() !!! : LockKey:{lock_key}, LockerId:{locker_id} - requstorId:{requestorId}"; + Log.getLogger().warn(err_msg); + return false; + } + + return true; + } + + static string makeRankerLockKey(string rankingGuid, string rankerGuid) + { + return $"ranker_update:write_lock:{rankingGuid}:{rankerGuid}"; + } + + static string makeRankerLockerId(string requestorId) + { + return $"ranker_update:locker_id:{requestorId}"; + } + + public static async Task tryAcquireWriteLockWithRankerUpdate(string rankingGuid, string rankerGuid, string requestorId, int ttlSec) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var redis_connector = server_logic.getRedisConnector(); + var redis_lua_executor = server_logic.getRedisWithLuaScriptExecutor(); + + var lock_key = makeRankerLockKey(rankingGuid, rankerGuid); + var locker_id = makeRankerLockerId(requestorId); + + var is_success = await redis_lua_executor.tryAcquireLock(lock_key, locker_id, ttlSec); + if (false == is_success) + { + err_msg = $"Failed to tryAcquireLock() !!! : LockKey:{lock_key}, LockerId:{locker_id}, TTLSec:{ttlSec} - requstorId:{requestorId}"; + Log.getLogger().warn(err_msg); + + return false; + } + + return true; + } + + public static async Task tryReleaseWriteLockWithRankerUpdate(string rankingGuid, string rankerGuid, string requestorId) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var redis_connector = server_logic.getRedisDb(); + var redis_lua_executor = server_logic.getRedisWithLuaScriptExecutor(); + + var lock_key = makeRankerLockKey(rankingGuid, rankerGuid); + var locker_id = makeRankerLockerId(requestorId); + + var is_success = await redis_lua_executor.tyrReleaseLock(lock_key, locker_id); + if (false == is_success) + { + err_msg = $"Failed to tyrReleaseLock() !!! : LockKey:{lock_key}, LockerId:{locker_id} - requstorId:{requestorId}"; + Log.getLogger().warn(err_msg); + return false; + } + + return true; + } + } +} diff --git a/GameServer/Entity/Ranking/Helper/RankingHelper.cs b/GameServer/Entity/Ranking/Helper/RankingHelper.cs new file mode 100644 index 0000000..e20bc12 --- /dev/null +++ b/GameServer/Entity/Ranking/Helper/RankingHelper.cs @@ -0,0 +1,232 @@ +using Org.BouncyCastle.Asn1.Ocsp; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal static class RankingHelper + { + public static async Task<(Result, Ranking?)> tryMakeRanking(RankingScheduleAttribute rankingScheduleAttribute) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranking = new Ranking(); + await ranking.onInit(); + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + result = ranking_action.fillupAttributeFromRankingScheduleAttribute(rankingScheduleAttribute); + if (result.isFail()) + { + err_msg = $"Failed to fillupAttributeFromRankingScheduleAttribute() !!! : - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, null); + } + + return (result, ranking); + } + + public static async Task<(Result, Dictionary)> tryLoadAllRankersFromDb(Ranking ranking) + { + var result = new Result(); + var err_msg = string.Empty; + + var rankers = new Dictionary(); + + var server_logic = GameServerApp.getServerLogic(); + var db_client = server_logic.getDynamoDbClient(); + + var ranking_guid = ranking.getRankingGuid(); + + var doc = new RankerDoc(); + doc.setCombinationKeyForPKSK(ranking_guid, DynamoDbClient.SK_EMPTY); + + var error_code = doc.onApplyPKSK(); + if (error_code.isFail()) + { + err_msg = $"Failed to onApplyPKSK() !!!"; + result.setFail(error_code, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, rankers); + } + + var query_config = db_client.makeQueryConfigWithPKSKBySKBeginWith(doc.getPK(), RankerDoc.getPrefixOfSK()); + (result, var read_docs) = await db_client.simpleQueryDocTypesWithQueryOperationConfig(query_config); + if (result.isFail()) + { + err_msg = $"Failed to simpleQueryDocTypesWithQueryOperationConfig() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, rankers); + } + + foreach (var read_doc in read_docs) + { + var ranker_attrib = read_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(ranker_attrib, () => $"ranker_attrib is null !!!"); + + var ranker = new Ranker(ranking); + await ranker.onInit(); + + var ranker_action = ranker.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranker_action, () => $"ranker_action is null !!!"); + + result = ranker_action.tryLoadRankerFromDoc(read_doc); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadRankerFromDoc() !!! : rankingGuid:{ranking_guid}, rankerGuid:{ranker_attrib.RankerGuid}, rankerType:{ranker_attrib.RankerEntityType}- {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, rankers); + } + + var ranker_guid = ranker_attrib.RankerGuid; + if (!rankers.TryAdd(ranker_guid, ranker)) + { + err_msg = $"Found Duplicated Ranker !!! : rankerGuid:{ranker_attrib.RankerGuid}, rankerType:{ranker_attrib.RankerEntityType}"; + Log.getLogger().warn(err_msg); + } + } + + return (result, rankers); + } + + + public static async Task tryUpdateRankingScore(string rankingGuid, string rankerGuid, EntityType rankerEntityType, int score) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var ranking_manager = server_logic.getRankingManager(); + + if (!ranking_manager.tryGetRanking(rankingGuid, out var ranking)) + { + err_msg = $"Failed to tryGetRanking() !!! rankingGuid:{rankingGuid}"; + result.setFail(ServerErrorCode.RankingNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + if (!ranking.tryGetRankingMetaData(out var ranking_meta_data)) + { + err_msg = $"Failed to tryGetRankingMetaData() !!! : {ranking.toBasicString()}"; + result.setFail(ServerErrorCode.RankingMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var ranker_agent_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranker_agent_action, () => $"ranker_agent_action is null !!!"); + + var fn_transaction_runner = async delegate () + { + var result = new Result(); + var is_new_ranker = false; + + DynamoDbItemRequestQueryContext? ranker_update_item_query_context = null; + + if (!ranker_agent_action.tryGetRanker(rankerGuid, out var ranker)) + { + (result, ranker) = await RankerHelper.tryMakeRanker(ranking, rankerGuid, rankerEntityType, score); + if (result.isFail()) + { + err_msg = $"Failed to tryMakeRanker() !!! : {ranking.toBasicString()}, rankerGuid:{rankerGuid}, rankerEntityType:{rankerEntityType} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + NullReferenceCheckHelper.throwIfNull(ranker, () => $"ranker is null !!!"); + + is_new_ranker = true; + } + else + { + (result, ranker_update_item_query_context) = RankerHelper.tryMakeUpdateItemRequestFromRanker(rankingGuid, rankerGuid, score, ranking_meta_data.ScoreType, ranking_meta_data.ScoreModifyType, ranking_meta_data.SortType); + if (result.isFail()) + { + err_msg = $"Failed to tryMakeUpdateItemRequestFromRanker() !!! : rankingGuid:{rankingGuid}, rankerGuid:{rankerGuid}, score:{score} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + + var ranker_action = ranker.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranker_agent_action, () => $"ranker_agent_action is null !!!"); + + var ranking_log_info = ranking.toRankingLogInfo(); + var ranking_business_log = new RankingBusinessLog(ranking_log_info); + + var ranker_log_info = RankerBusinessLogHelper.toRankerLogInfo(rankingGuid, rankerGuid, rankerEntityType, score, ranking_meta_data.ScoreModifyType); + var ranker_business_log = new RankerBusinessLog(ranker_log_info); + + var batch = new QueryBatchEx(ranking, LogActionType.RankingScoreUpdate, server_logic.getDynamoDbClient()); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + if (ranker_update_item_query_context != null) + { + batch.addQuery(new DBQWithItemRequestQueryContext(ranker_update_item_query_context)); + } + } + + batch.appendBusinessLog(ranking_business_log); + batch.appendBusinessLog(ranker_business_log); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.ErrorCode == ServerErrorCode.DynamoDbItemRequestConditionFail) // ServerErrorCode.DynamoDbItemRequestConditionFail 에러일 경우 정상임 + { + result.setSuccess(); + } + else if (result.isFail()) + { + return result; + } + + if (is_new_ranker) + ranker_agent_action.addRanker(rankerGuid, ranker); + + return result; + }; + + result = await ranking.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "RankingScoreUpdate", fn_transaction_runner); + if (result.isFail()) + { + err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {ranking.toBasicString()}"; + Log.getLogger().error(err_msg); + } + + + return result; + } + + public static DateTime getNextTime(DateTime checkTime, DateTime intervalBaseTime, int intervalSec) + { + var interval_repeated_count = 0; + if (intervalBaseTime < checkTime) + interval_repeated_count = getIntervalRepeatedCount(checkTime, intervalBaseTime, intervalSec); + + return intervalBaseTime.AddSeconds((interval_repeated_count + 1) * intervalSec); + } + + public static int getIntervalRepeatedCount(DateTime checkTime, DateTime intervalBaseTime, int intervalSec) + { + var diff_time = checkTime - intervalBaseTime; + return (int)(diff_time.TotalSeconds / intervalSec); + } + } +} diff --git a/GameServer/Entity/Ranking/Helper/RankingNotifyHelper.cs b/GameServer/Entity/Ranking/Helper/RankingNotifyHelper.cs new file mode 100644 index 0000000..dac87b2 --- /dev/null +++ b/GameServer/Entity/Ranking/Helper/RankingNotifyHelper.cs @@ -0,0 +1,54 @@ +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ClientToGameMessage.Types; +using static ServerMessage.Types; + +namespace GameServer +{ + internal static class RankingNotifyHelper + { + public static async Task send_GS2GS_NTF_MODIFY_RANKING_INFO(string rankingGuid) + { + var server_logic = GameServerApp.getServerLogic(); + var rabbit_mq = server_logic.getRabbitMqConnector() as RabbitMqConnector; + NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit_mq is null !!!"); + + var message = new ServerMessage(); + message.NtfModifyRankingInfo = new GS2GS_NTF_MODIFY_RANKING_INFO(); + message.NtfModifyRankingInfo.RankingGuid = rankingGuid; + + (var result, var server_infos) = await server_logic.getServerInfoAll(); + foreach (var server_info in server_infos) + { + rabbit_mq.SendMessage(server_info.Name, message); + } + + return true; + } + + public static async Task send_GS2GS_NTF_REFRESH_RANK(string rankingGuid) + { + var server_logic = GameServerApp.getServerLogic(); + var rabbit_mq = server_logic.getRabbitMqConnector() as RabbitMqConnector; + NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit_mq is null !!!"); + + var message = new ServerMessage(); + message.NtfRefreshRank = new GS2GS_NTF_REFRESH_RANK(); + message.NtfRefreshRank.RankingGuid = rankingGuid; + + (var result, var server_infos) = await server_logic.getServerInfoAll(); + foreach (var server_info in server_infos) + { + rabbit_mq.SendMessage(server_info.Name, message); + } + + return true; + } + } +} diff --git a/GameServer/Entity/Ranking/PacketHandler/RankingInfoPacketHandler.cs b/GameServer/Entity/Ranking/PacketHandler/RankingInfoPacketHandler.cs new file mode 100644 index 0000000..fe7ea48 --- /dev/null +++ b/GameServer/Entity/Ranking/PacketHandler/RankingInfoPacketHandler.cs @@ -0,0 +1,87 @@ +using Google.Protobuf.WellKnownTypes; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ClientToGameRes.Types; + +namespace GameServer.PacketHandler +{ + [PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_RANKING_INFO), typeof(RankingInfoPacketHandler), typeof(GameLoginListener))] + internal class RankingInfoPacketHandler : PacketRecvHandler + { + public static bool send_S2C_ACK_RANKING_INFO(Player owner, Result result, GS2C_ACK_RANKING_INFO res) + { + var ack_packet = new ClientToGame(); + ack_packet.Response = new ClientToGameRes(); + + ack_packet.Response.ErrorCode = result.ErrorCode; + ack_packet.Response.AckRankingInfo = res; + + if (false == GameServerApp.getServerLogic().onSendPacket(owner, ack_packet)) + { + return false; + } + + return true; + } + + public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + + var player = entityWithSession as Player; + NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); + + var req_msg = recvMessage as ClientToGame; + NullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); + + var request = req_msg.Request.ReqRankingInfo; + var res = new GS2C_ACK_RANKING_INFO(); + + var ranking_manager = server_logic.getRankingManager(); + if (!ranking_manager.tryGetRanking(request.RankingGuid, out var ranking)) + { + err_msg = $"Failed to tryGetRanking() !!! : rankingGuid:{request.RankingGuid} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.RankingNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + send_S2C_ACK_RANKING_INFO(player, result, res); + return result; + } + + var ranking_attribute = ranking.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + if (!ranking_attribute.NextTimes.TryGetValue(IntervalType.Refresh, out var next_refresh_time)) + { + err_msg = $"Failed to tryGetRanking() !!! : rankingGuid:{request.RankingGuid} - {player.toBasicString()}"; + result.setFail(ServerErrorCode.RankingNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + send_S2C_ACK_RANKING_INFO(player, result, res); + return result; + } + + var rank_agent_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(rank_agent_action, () => $"rank_agent_action is null !!!"); + + var rank_infos = await rank_agent_action.getRankInfos(); + + res.RankingGuid = request.RankingGuid; + res.NextRefreshTime = next_refresh_time.ToTimestamp(); + res.RankInfos.AddRange(rank_infos); + + send_S2C_ACK_RANKING_INFO(player, result, res); + + return result; + } + } +} diff --git a/GameServer/Entity/Ranking/Ranking.cs b/GameServer/Entity/Ranking/Ranking.cs new file mode 100644 index 0000000..69099f3 --- /dev/null +++ b/GameServer/Entity/Ranking/Ranking.cs @@ -0,0 +1,97 @@ +using MetaAssets; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + public class Ranking : EntityBase, IWithLogActor + { + public Ranking() + : base(EntityType.Ranking) + { + } + + public override async Task onInit() + { + addEntityAttribute(new RankingAttribute(this)); + + addEntityAction(new RankingAction(this)); + addEntityAction(new RankerAgentAction(this)); + addEntityAction(new RankAgentAction(this)); + + return await base.onInit(); + } + + public override string toBasicString() + { + return $"{this.getTypeName()}, RankingGuid:{getOriginEntityAttribute()?.RankingGuid}, RankingMetaId:{getOriginEntityAttribute()?.RankingMetaId}"; + } + + public override string toSummaryString() + { + return $"{this.getTypeName()}, {getEntityAttribute()?.toBasicString()}"; + } + + public virtual ILogActor toLogActor() + { + var server_logic = ServerLogicApp.getServerLogicApp(); + + var ranking_attribute = getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + var log_info = new RankingActorLog(); + log_info.initLogInfo( + server_logic.getServerConfig().getRegionId(), + server_logic.getServerConfig().getWorldId(), + server_logic.getServerType().toServerType(), + ranking_attribute.RankingGuid, + ranking_attribute.RankingMetaId + ); + + return log_info; + } + + public string getRankingGuid() + { + var ranking_attribute = getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + return ranking_attribute.RankingGuid; + } + + public int getRankingMetaId() + { + var ranking_attribute = getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + return ranking_attribute.RankingMetaId; + } + + public bool tryGetRankingMetaData([MaybeNullWhen(false)] out RankingData rankingMetaData) + { + var err_msg = string.Empty; + + var ranking_attribute = getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_attribute, () => $"ranking_attribute is null !!!"); + + var ranking_meta_id = ranking_attribute.RankingMetaId; + + if (!MetaData.Instance._RankingMetaTable.TryGetValue(ranking_meta_id, out rankingMetaData)) + { + err_msg = $"Failed to _RankingMetaTable.TryGetValue() !!! : rankingMetaId:{ranking_meta_id}, errCode:{ServerErrorCode.RankingMetaDataNotFound}"; + Log.getLogger().error(err_msg); + + return false; + } + + return true; + } + } +} diff --git a/GameServer/Entity/Ranking/RankingCheat.cs b/GameServer/Entity/Ranking/RankingCheat.cs new file mode 100644 index 0000000..98a231e --- /dev/null +++ b/GameServer/Entity/Ranking/RankingCheat.cs @@ -0,0 +1,242 @@ +using Org.BouncyCastle.Tls; +using ServerBase; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + [ChatCommandAttribute("refreshranking", typeof(RankingCheatRefreshRanking), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] + internal class RankingCheatRefreshRanking : ChatCommandBase + { + public override async Task invoke(Player player, string token, string[] args) + { + Log.getLogger().info($"Call refreshranking !!! - {player.toBasicString()}"); + + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + var ranking_manager = server_logic.getRankingManager(); + + if (args.Length != 1) + { + err_msg = $"Not enough argument !!! : argCount:{args.Length} != 1 - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + + var ranking_guid = args[0]; + + if (!ranking_manager.tryGetRanking(ranking_guid, out var ranking)) + { + err_msg = $"Failed to tryGetRanking() !!! rankingGuid:{ranking_guid}"; + result.setFail(ServerErrorCode.RankingNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return; + } + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(ranking_guid, server_name, 10); + if (is_success) + { + result = await ranking_action.tryUpdateRankingByIntervalType(IntervalType.Refresh); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(ranking_guid, server_name); + + if (result.isFail()) + { + err_msg = $"Failed to tryUpdateRankingByIntervalType() !!! : rankingGuid:{ranking_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + } + } + } + + [ChatCommandAttribute("refreshrankingall", typeof(RankingCheatRefreshRankingAll), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] + internal class RankingCheatRefreshRankingAll : ChatCommandBase + { + public override async Task invoke(Player player, string token, string[] args) + { + Log.getLogger().info($"Call refreshrankingall !!! - {player.toBasicString()}"); + + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var ranking_manager = server_logic.getRankingManager(); + + result = await ranking_manager.tryUpdateRankingsByIntervalType(IntervalType.Refresh); + if (result.isFail()) + { + err_msg = $"Failed to tryRefreshRankings() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + } + } + + [ChatCommandAttribute("snapshotranking", typeof(RankingSnapshotRanking), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] + internal class RankingSnapshotRanking : ChatCommandBase + { + public override async Task invoke(Player player, string token, string[] args) + { + Log.getLogger().info($"Call snapshotranking !!! - {player.toBasicString()}"); + + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + var ranking_manager = server_logic.getRankingManager(); + + if (args.Length != 1) + { + err_msg = $"Not enough argument !!! : argCount:{args.Length} != 1 - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + + var ranking_guid = args[0]; + + if (!ranking_manager.tryGetRanking(ranking_guid, out var ranking)) + { + err_msg = $"Failed to tryGetRanking() !!! rankingGuid:{ranking_guid}"; + result.setFail(ServerErrorCode.RankingNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return; + } + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(ranking_guid, server_name, 10); + if (is_success) + { + result = await ranking_action.tryUpdateRankingByIntervalType(IntervalType.Snapshot); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(ranking_guid, server_name); + + if (result.isFail()) + { + err_msg = $"Failed to trySnapshotRanking() !!! : rankingGuid:{ranking_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + } + } + } + + [ChatCommandAttribute("snapshotrankingall", typeof(RankingCheatSnapshotRankingAll), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] + internal class RankingCheatSnapshotRankingAll : ChatCommandBase + { + public override async Task invoke(Player player, string token, string[] args) + { + Log.getLogger().info($"Call snapshotrankingall !!! - {player.toBasicString()}"); + + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var ranking_manager = server_logic.getRankingManager(); + + result = await ranking_manager.tryUpdateRankingsByIntervalType(IntervalType.Snapshot); + if (result.isFail()) + { + err_msg = $"Failed to trySnapshotRankings() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + } + } + + [ChatCommandAttribute("initializeranking", typeof(RankingInitializeRanking), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] + internal class RankingInitializeRanking : ChatCommandBase + { + public override async Task invoke(Player player, string token, string[] args) + { + Log.getLogger().info($"Call initializeranking !!! - {player.toBasicString()}"); + + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + var ranking_manager = server_logic.getRankingManager(); + + if (args.Length != 1) + { + err_msg = $"Not enough argument !!! : argCount:{args.Length} != 1 - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + + var ranking_guid = args[0]; + + if (!ranking_manager.tryGetRanking(ranking_guid, out var ranking)) + { + err_msg = $"Failed to tryGetRanking() !!! rankingGuid:{ranking_guid}"; + result.setFail(ServerErrorCode.RankingNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return; + } + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(ranking_guid, server_name, 10); + if (is_success) + { + result = await ranking_action.tryUpdateRankingByIntervalType(IntervalType.Initialization); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(ranking_guid, server_name); + + if (result.isFail()) + { + err_msg = $"Failed to tryInitializeRanking() !!! : rankingGuid:{ranking_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + } + } + } + + [ChatCommandAttribute("initializerankingall", typeof(RankingCheatInitializeRankingAll), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] + internal class RankingCheatInitializeRankingAll : ChatCommandBase + { + public override async Task invoke(Player player, string token, string[] args) + { + Log.getLogger().info($"Call initializerankingall !!! - {player.toBasicString()}"); + + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var ranking_manager = server_logic.getRankingManager(); + + result = await ranking_manager.tryUpdateRankingsByIntervalType(IntervalType.Initialization); + if (result.isFail()) + { + err_msg = $"Failed to tryInitializeRankings() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + } + } +} diff --git a/GameServer/Entity/RankingSchedule/Action/RankingScheduleAction.cs b/GameServer/Entity/RankingSchedule/Action/RankingScheduleAction.cs new file mode 100644 index 0000000..bc69473 --- /dev/null +++ b/GameServer/Entity/RankingSchedule/Action/RankingScheduleAction.cs @@ -0,0 +1,111 @@ +using Microsoft.VisualBasic; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal class RankingScheduleAction : EntityActionBase + { + public RankingScheduleAction(RankingSchedule owner) + : base(owner) + { } + + public override async Task onInit() + { + var result = new Result(); + return await Task.FromResult(result); + } + + public override void onClear() + { + return; + } + + public Result tryLoadRankingScheduleFromDoc(RankingScheduleDoc rankingScheduleDoc) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranking_schedule = getOwner() as RankingSchedule; + NullReferenceCheckHelper.throwIfNull(ranking_schedule, () => $"ranking_schedule is null !!!"); + + var ranking_schedule_attribute = ranking_schedule.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attribute, () => $"ranking_schedule_attribute is null !!!"); + + if (!ranking_schedule_attribute.copyEntityAttributeFromDoc(rankingScheduleDoc)) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!! to:{ranking_schedule_attribute.getTypeName()}, from:{rankingScheduleDoc.getTypeName()} : {this.getTypeName()}"; + result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + foreach (IntervalType interval_type in Enum.GetValues(typeof(IntervalType))) + { + if (!ranking_schedule_attribute.IntervalTimes.TryGetValue(interval_type, out var interval_time)) + continue; + + if (interval_time <= 0) + ranking_schedule_attribute.IntervalTimes.Remove(interval_type); + } + + ranking_schedule_attribute.RankingScheduleState = RankingScheduleHelper.checkRankingScheduleState(ranking_schedule_attribute); + + return result; + } + + public async Task tryUpsertRankingScheduleDocToDb() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var ranking_schedule = getOwner() as RankingSchedule; + NullReferenceCheckHelper.throwIfNull(ranking_schedule, () => $"ranking_schedule is null !!!"); + + var ranking_schedule_attribute = ranking_schedule.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attribute, () => $"ranking_schedule_attribute is null !!!"); + + var ranking_guid = ranking_schedule_attribute.RankingGuid; + + (result, var ranking_doc) = await ranking_schedule_attribute.toDocBase(); + if (result.isFail() || ranking_doc == null) + { + err_msg = $"Failed to toDocBase() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + // 하나의 서버에서만 Insert 하도록 Redis WriteLock + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(ranking_guid, server_name, 10); + if (is_success) + { + result = await dynamo_db_client.simpleUpsertDocumentWithDocType(ranking_doc); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(ranking_guid, server_name); + + if (result.isFail()) + { + err_msg = $"Failed to simpleUpsertDocumentWithDocType() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + Log.getLogger().info($"RankingScheduleDoc Upsert Sucess !!! : rankingGuid:{ranking_schedule_attribute.RankingGuid}"); + } + + return result; + } + } +} diff --git a/GameServer/Entity/RankingSchedule/Helper/RankingScheduleHelper.cs b/GameServer/Entity/RankingSchedule/Helper/RankingScheduleHelper.cs new file mode 100644 index 0000000..02687ee --- /dev/null +++ b/GameServer/Entity/RankingSchedule/Helper/RankingScheduleHelper.cs @@ -0,0 +1,138 @@ +using Google.Protobuf.WellKnownTypes; +using MetaAssets; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal static class RankingScheduleHelper + { + public static RankingScheduleStateType checkRankingScheduleState(RankingScheduleAttribute rankingScheduleAttribute) + { + var current_time = DateTime.UtcNow; + if (current_time < rankingScheduleAttribute.StartTime) + return RankingScheduleStateType.Waiting; + + if (rankingScheduleAttribute.StartTime <= current_time && current_time < rankingScheduleAttribute.EndTime) + return RankingScheduleStateType.Started; + + if (rankingScheduleAttribute.EndTime <= current_time) + return RankingScheduleStateType.Ended; + + return RankingScheduleStateType.None; + } + + public static RankingScheduleInfo toRankingScheduleInfo(this RankingSchedule rankingSchedule) + { + var ranking_schedule_attribute = rankingSchedule.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attribute, () => $"ranking_schedule_attribute is null !!!"); + + var ranking_schedule_info = new RankingScheduleInfo(); + + ranking_schedule_info.RankingGuid = ranking_schedule_attribute.RankingGuid; + ranking_schedule_info.RankingMetaId = ranking_schedule_attribute.RankingMetaId; + ranking_schedule_info.StartTime = ranking_schedule_attribute.StartTime.ToTimestamp(); + ranking_schedule_info.EndTime = ranking_schedule_attribute.EndTime.ToTimestamp(); + + return ranking_schedule_info; + } + + public static bool tryGetRankingScheduleAttribute(string rankingGuid, [MaybeNullWhen(false)] out RankingScheduleAttribute rankingScheduleAttribute) + { + var err_msg = string.Empty; + + rankingScheduleAttribute = default; + + var server_logic = GameServerApp.getServerLogic(); + var ranking_schedule_manager = server_logic.getRankingScheduleManager(); + + if (!ranking_schedule_manager.tryGetRankingSchedule(rankingGuid, out var ranking_schedule)) + { + err_msg = $"Failed to tryGetRankingSchedule() !!! : rankingGuid:{rankingGuid}"; + Log.getLogger().error(err_msg); + + return false; + } + + rankingScheduleAttribute = ranking_schedule.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(rankingScheduleAttribute, () => $"rankingAttribute is null !!!"); + + return true; + } + + public static bool tryGetRankingMetaId(string rankingGuid, [MaybeNullWhen(false)] out int rankingMetaId) + { + var err_msg = string.Empty; + + rankingMetaId = 0; + + if (!tryGetRankingScheduleAttribute(rankingGuid, out var ranking_schedule_attribute)) + { + err_msg = $"Failed to tryGetRankingScheduleAttribute() !!! : rankingGuid:{rankingGuid}"; + Log.getLogger().error(err_msg); + + return false; + } + + rankingMetaId = ranking_schedule_attribute.RankingMetaId; + + return true; + } + + public static bool tryGetRankingMetaData(string rankingGuid, [MaybeNullWhen(false)] out RankingData rankingMetaData) + { + var err_msg = string.Empty; + + rankingMetaData = default; + + if (!tryGetRankingMetaId(rankingGuid, out var ranking_meta_id)) + { + err_msg = $"Failed to tryGetRankingMetaId() !!! : rankingGuid:{rankingGuid}"; + Log.getLogger().error(err_msg); + + return false; + } + + if (!MetaData.Instance._RankingMetaTable.TryGetValue(ranking_meta_id, out rankingMetaData)) + { + err_msg = $"Failed to _RankingMetaTable.TryGetValue() !!! : rankingMetaId:{ranking_meta_id}, errCode:{ServerErrorCode.RankingMetaDataNotFound}"; + Log.getLogger().error(err_msg); + + return false; + } + + return true; + } + + public static async Task<(Result, RankingSchedule?)> tryMakeTestRankingSchedule(int rankingMetaId, int eventActionScoreGroupId) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranking_schedule = new RankingSchedule(); + await ranking_schedule.onInit(); + + var ranking_schedule_attribute = ranking_schedule.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attribute, () => $"ranking_schedule_attribute is null !!!"); + + ranking_schedule_attribute.RankingGuid = Guid.NewGuid().ToString("N"); + ranking_schedule_attribute.RankingMetaId = rankingMetaId; + ranking_schedule_attribute.EventActionScoreGroupId = eventActionScoreGroupId; + ranking_schedule_attribute.StartTime = DateTime.UtcNow.Date; + ranking_schedule_attribute.EndTime = ranking_schedule_attribute.StartTime.AddDays(1); + ranking_schedule_attribute.IntervalBaseTime = ranking_schedule_attribute.StartTime; + ranking_schedule_attribute.IntervalTimes[IntervalType.Refresh] = 5 * 60; // 5분 + ranking_schedule_attribute.IntervalTimes[IntervalType.Snapshot] = 60 * 60; // 1시간 + ranking_schedule_attribute.IntervalTimes[IntervalType.Initialization] = 4 * 60 * 60; // 4시간; + ranking_schedule_attribute.newEntityAttribute(); + + return (result, ranking_schedule); + } + } +} diff --git a/GameServer/Entity/RankingSchedule/Helper/RankingScheduleNotifyHelper.cs b/GameServer/Entity/RankingSchedule/Helper/RankingScheduleNotifyHelper.cs new file mode 100644 index 0000000..13286af --- /dev/null +++ b/GameServer/Entity/RankingSchedule/Helper/RankingScheduleNotifyHelper.cs @@ -0,0 +1,80 @@ +using GameServer.PacketHandler; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ClientToGameMessage.Types; +using static ServerMessage.Types; + +namespace GameServer +{ + internal static class RankingScheduleNotifyHelper + { + public static bool send_S2C_NTF_RANKING_SCHEDULE_INFOS(this Player player) + { + var server_logic = GameServerApp.getServerLogic(); + var ranking_schedule_manager = server_logic.getRankingScheduleManager(); + + var ntf_packet = new ClientToGame(); + ntf_packet.Message = new ClientToGameMessage(); + ntf_packet.Message.NtfRankingScheduleInfos = new GS2C_NTF_RANKING_SCHEDULE_INFOS(); + + var ranking_schedule_infos = ranking_schedule_manager.tryGetRankingScheduleInfos(); + ntf_packet.Message.NtfRankingScheduleInfos.RankingScheduleInfos.AddRange(ranking_schedule_infos); + + if (false == GameServerApp.getServerLogic().onSendPacket(player, ntf_packet)) + { + return false; + } + + return true; + } + + public static bool broadcast_S2C_NTF_RANKING_SCHEDULE_INFOS() + { + var server_logic = GameServerApp.getServerLogic(); + var ranking_schedule_manager = server_logic.getRankingScheduleManager(); + 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(); + ntf_packet.Message.NtfRankingScheduleInfos = new GS2C_NTF_RANKING_SCHEDULE_INFOS(); + + var ranking_schedule_infos = ranking_schedule_manager.tryGetRankingScheduleInfos(); + ntf_packet.Message.NtfRankingScheduleInfos.RankingScheduleInfos.AddRange(ranking_schedule_infos); + + if (false == GameServerApp.getServerLogic().onSendPacket(players, ntf_packet)) + { + return false; + } + + return true; + } + + public static async Task send_MOS2GS_NTF_UPDATE_RANKING_SCHEDULE() + { + var server_logic = GameServerApp.getServerLogic(); + var rabbit_mq = server_logic.getRabbitMqConnector() as RabbitMqConnector; + NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit_mq is null !!!"); + + var message = new ServerMessage(); + message.NtfUpdateRankingSchedule = new MOS2GS_NTF_UPDATE_RANKING_SCHEDULE(); + + (var result, var server_infos) = await server_logic.getServerInfoAll(); + foreach (var server_info in server_infos) + { + rabbit_mq.SendMessage(server_info.Name, message); + } + + return true; + } + } +} diff --git a/GameServer/Entity/RankingSchedule/RankingSchedule.cs b/GameServer/Entity/RankingSchedule/RankingSchedule.cs new file mode 100644 index 0000000..bfa05b2 --- /dev/null +++ b/GameServer/Entity/RankingSchedule/RankingSchedule.cs @@ -0,0 +1,46 @@ +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + public class RankingSchedule : EntityBase + { + public RankingSchedule() + : base(EntityType.Ranking) + { + } + + public override async Task onInit() + { + addEntityAttribute(new RankingScheduleAttribute(this)); + + addEntityAction(new RankingScheduleAction(this)); + + return await base.onInit(); + } + + public override string toBasicString() + { + return $"{this.getTypeName()}, RankingGuid:{getOriginEntityAttribute()?.RankingGuid}, RankingMetaId:{getOriginEntityAttribute()?.RankingMetaId}"; + } + + public override string toSummaryString() + { + return $"{this.getTypeName()}, {getEntityAttribute()?.toBasicString()}"; + } + + public string getRankingGuid() + { + var ranking_schedule_attribute = getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attribute, () => $"ranking_schedule_attribute is null !!!"); + + return ranking_schedule_attribute.RankingGuid; + } + } +} diff --git a/GameServer/Entity/RankingSchedule/RankingScheduleCheat.cs b/GameServer/Entity/RankingSchedule/RankingScheduleCheat.cs new file mode 100644 index 0000000..4c368cc --- /dev/null +++ b/GameServer/Entity/RankingSchedule/RankingScheduleCheat.cs @@ -0,0 +1,94 @@ +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace GameServer +{ + [ChatCommandAttribute("addtestrankingschedule", typeof(RankingScheduleCheatAddTestRankingSchedule), AuthAdminLevelType.Developer, AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)] + internal class RankingScheduleCheatAddTestRankingSchedule : ChatCommandBase + { + public override async Task invoke(Player player, string token, string[] args) + { + Log.getLogger().info($"Call addrankingschedule !!! - {player.toBasicString()}"); + + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var ranking_schedule_manager = server_logic.getRankingScheduleManager(); + + if (args.Length != 2) + { + err_msg = $"Not enough argument !!! : argCount:{args.Length} != 2 - {player.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + + if (!int.TryParse(args[0], out var ranking_meta_Id)) + { + err_msg = $"addrankingschedule param parsing Error args : {args[0]}"; + Log.getLogger().error(err_msg); + + return; + } + + if (!int.TryParse(args[1], out var event_action_score_group_id)) + { + err_msg = $"addrankingschedule param parsing Error args : {args[1]}"; + Log.getLogger().error(err_msg); + + return; + } + + if (!MetaData.Instance._RankingMetaTable.TryGetValue(ranking_meta_Id, out var ranking_meta)) + { + err_msg = $"Failed to _RankingMetaTable.TryGetValue() !!! : rankingMetaId{ranking_meta_Id}"; + result.setFail(ServerErrorCode.RankingMetaDataNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return; + } + + var event_action_score_meta_data = MetaData.Instance.Meta.EventActionScoreMetaTable.EventActionScoreMetaDataList.Where(x => x.GroupId == event_action_score_group_id); + if (event_action_score_meta_data.Count() == 0) + { + err_msg = $"Not Found EventActionScore !!! : eventActionScoreGroupId:{event_action_score_group_id}"; + result.setFail(ServerErrorCode.MetaIdInvalid, err_msg); + Log.getLogger().error(result.toBasicString()); + + return; + } + + (result, var ranking_schedule) = await RankingScheduleHelper.tryMakeTestRankingSchedule(ranking_meta_Id, event_action_score_group_id); + if (result.isFail() || ranking_schedule == null) + { + err_msg = $"Failed to tryMakeTestRankingSchedule() !!! : rankingMetaId:{ranking_meta_Id}, eventActionScoreGroupId:{event_action_score_group_id} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + + var ranking_schedule_action = ranking_schedule.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_action, () => $"ranking_schedule_action is null !!! - {player.toBasicString()}"); + + result = await ranking_schedule_action.tryUpsertRankingScheduleDocToDb(); + if (result.isFail()) + { + err_msg = $"Failed to tryUpsertRankingDocToDb() !!! : rankingMetaId:{ranking_meta_Id}, eventActionScoreGroupId:{event_action_score_group_id} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return; + } + + await RankingScheduleNotifyHelper.send_MOS2GS_NTF_UPDATE_RANKING_SCHEDULE(); + } + } +} diff --git a/GameServer/Event/Action/GameEventCollectorAction.cs b/GameServer/Event/Action/GameEventCollectorAction.cs new file mode 100644 index 0000000..8630710 --- /dev/null +++ b/GameServer/Event/Action/GameEventCollectorAction.cs @@ -0,0 +1,28 @@ +using ServerBase; + +namespace GameServer.Action; + +public class GameEventCollectorAction : EntityActionBase +{ + GameEventCollector m_game_event_collector; + public GameEventCollectorAction(EntityBase owner) : base(owner) + { + m_game_event_collector = new GameEventCollector(owner, new GameEventAnalyzer()); + } + + public override Task onInit() + { + var result = new Result(); + + return Task.FromResult(result); + } + + public override void onClear() + { + } + + public GameEventCollector getGameEventCollector() + { + return m_game_event_collector; + } +} \ No newline at end of file diff --git a/GameServer/Event/EventDefines/GameEventDefines.cs b/GameServer/Event/EventDefines/GameEventDefines.cs new file mode 100644 index 0000000..cf76a8e --- /dev/null +++ b/GameServer/Event/EventDefines/GameEventDefines.cs @@ -0,0 +1,103 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace GameServer.EventDefines; + +public class GameEventDefines +{ + +} + + +[JsonConverter(typeof(StringEnumConverter))] +public enum GameEventType +{ + NONE = 0, + AVATARPROFILE = 1, + BEACONDIALOGUE, + BUFF, + CHAIR, + CHANNEL, + CHAT, + CLAIM, + CONCERT, + COSTUME, + COUNTER, //쓸지 안쓸지 모르겠는데? + CRAFTING, + CUTSCENE, + DIALOGUE, + DRESSROOM, + FARMING, + FRIEND, + GAMEMODE, + GUI, + INSTANCE, + INTERIORMODE, + ITEM, + MAIL, + MANNEQUIN, + MOVEMENT, + MYHOME, + NPC, + PARTY, + PHOTOMODE, + PROP, + QUEST, + RENTAL, + REWARD, + REWARDPROP, + SEASONPASS, + SOCIALACTION, + SPECIALGUIDE, + TASK, + TATTOO, + TAXI, + TOOL, + TRIGGER, + TIMER, + TPS, + UGQ, + VIDEO, + WAITING, +} + +[JsonConverter(typeof(StringEnumConverter))] +public enum GameEventActionType +{ + NONE = 0, + ACTIVED, + ADDED , + ARRIVED, + BOUGHT, + CAPTURE, + CHANGED, + COMPLETED, + CONVERSIONED, + DELETED, + EDITED, + ENDED, + ENHANCED, + EQUIPED, + EXITED, + ENTERED, + JUMP, + MAX, + PICTURED, + POPUP, + RECEIVED, + REGISTERED, + REQUESTED, + REWARDED, + SENDED, + SOLD, + SPRINT, + STARTED, + STOPPED, + SWITCHED, + USED, + VISITED, + PLAYERKILL, + FEVER, + PICKUP, +} + diff --git a/GameServer/Event/EventInvokers/GameEventFfaOnPickUpObject.cs b/GameServer/Event/EventInvokers/GameEventFfaOnPickUpObject.cs new file mode 100644 index 0000000..1a072ed --- /dev/null +++ b/GameServer/Event/EventInvokers/GameEventFfaOnPickUpObject.cs @@ -0,0 +1,18 @@ +using GameServer.EventDefines; +using MetaAssets; + +namespace GameServer.EventInvokers; + +public class GameEventFfaOnPickUpObject : GameEventInvokerBase +{ + public int m_game_mode_id { get; } + public EGameModeObjectType GameModeObjectType { get; } + + public GameEventFfaOnPickUpObject(Player player, int gameModeId, EGameModeObjectType gameModeObjectType) : base( + GameEventType.GAMEMODE, GameEventActionType.PICKUP, + player.getUserGuid()) + { + m_game_mode_id = gameModeId; + GameModeObjectType = gameModeObjectType; + } +} diff --git a/GameServer/Event/EventInvokers/GameEventInvokerBase.cs b/GameServer/Event/EventInvokers/GameEventInvokerBase.cs new file mode 100644 index 0000000..dfc308e --- /dev/null +++ b/GameServer/Event/EventInvokers/GameEventInvokerBase.cs @@ -0,0 +1,32 @@ +using GameServer.EventDefines; + +namespace GameServer.EventInvokers; + +public abstract class GameEventInvokerBase : IGameEventInvoker +{ + public GameEventType m_event_type{ get; } + public GameEventActionType m_action_type{ get; } + public string m_user_guid { get; } = string.Empty; + + public GameEventInvokerBase(GameEventType eventType, GameEventActionType actionType, string userGuid) + { + m_event_type = eventType; + m_action_type = actionType; + m_user_guid = userGuid; + } + + public GameEventType getGameEventType() + { + return m_event_type; + } + + public GameEventActionType getGameEventActionType() + { + return m_action_type; + } + + public string getUserGuid() + { + return m_user_guid; + } +} \ No newline at end of file diff --git a/GameServer/Event/EventInvokers/GameEventInvokerClaimRewarded.cs b/GameServer/Event/EventInvokers/GameEventInvokerClaimRewarded.cs new file mode 100644 index 0000000..3f4007a --- /dev/null +++ b/GameServer/Event/EventInvokers/GameEventInvokerClaimRewarded.cs @@ -0,0 +1,10 @@ +using GameServer.EventDefines; + +namespace GameServer.EventInvokers; + +public class GameEventInvokerClaimRewarded : GameEventInvokerBase +{ + public GameEventInvokerClaimRewarded(Player player) : base(GameEventType.CLAIM, GameEventActionType.REWARDED, player.getUserGuid()) + { + } +} \ No newline at end of file diff --git a/GameServer/Event/EventInvokers/GameEventInvokerCraftingCompleted.cs b/GameServer/Event/EventInvokers/GameEventInvokerCraftingCompleted.cs new file mode 100644 index 0000000..6cb7f6b --- /dev/null +++ b/GameServer/Event/EventInvokers/GameEventInvokerCraftingCompleted.cs @@ -0,0 +1,15 @@ +using GameServer.EventDefines; + +namespace GameServer.EventInvokers; + +public class GameEventInvokerCraftingCompleted : GameEventInvokerBase +{ + + + public Int32 m_item_id { get; } + + public GameEventInvokerCraftingCompleted(Player player, Int32 itemId) : base(GameEventType.CRAFTING, GameEventActionType.COMPLETED, player.getUserGuid()) + { + m_item_id = itemId; + } +} \ No newline at end of file diff --git a/GameServer/Event/EventInvokers/GameEventInvokerFarmingCompleted.cs b/GameServer/Event/EventInvokers/GameEventInvokerFarmingCompleted.cs new file mode 100644 index 0000000..59eab2a --- /dev/null +++ b/GameServer/Event/EventInvokers/GameEventInvokerFarmingCompleted.cs @@ -0,0 +1,11 @@ +using GameServer.EventDefines; + +namespace GameServer.EventInvokers; + +public class GameEventInvokerFarmingCompleted: GameEventInvokerBase +{ + public GameEventInvokerFarmingCompleted(string playerGuid) : base(GameEventType.FARMING, GameEventActionType.COMPLETED, playerGuid) + { + + } +} \ No newline at end of file diff --git a/GameServer/Event/EventInvokers/GameEventInvokerQuestRewarded.cs b/GameServer/Event/EventInvokers/GameEventInvokerQuestRewarded.cs new file mode 100644 index 0000000..0df989a --- /dev/null +++ b/GameServer/Event/EventInvokers/GameEventInvokerQuestRewarded.cs @@ -0,0 +1,13 @@ +using GameServer.EventDefines; + +namespace GameServer.EventInvokers; + +public class GameEventInvokerQuestRewarded : GameEventInvokerBase +{ + public UInt32 m_quest_id { get; } + + public GameEventInvokerQuestRewarded(Player player, UInt32 questId) : base(GameEventType.QUEST, GameEventActionType.REWARDED, player.getUserGuid()) + { + m_quest_id = questId; + } +} \ No newline at end of file diff --git a/GameServer/Event/EventInvokers/GameEventInvokerRewadPropRewarded.cs b/GameServer/Event/EventInvokers/GameEventInvokerRewadPropRewarded.cs new file mode 100644 index 0000000..6aef905 --- /dev/null +++ b/GameServer/Event/EventInvokers/GameEventInvokerRewadPropRewarded.cs @@ -0,0 +1,11 @@ +using GameServer.EventDefines; + +namespace GameServer.EventInvokers; + +public class GameEventInvokerRewadPropRewarded : GameEventInvokerBase +{ + public GameEventInvokerRewadPropRewarded(Player player) : base(GameEventType.REWARDPROP, GameEventActionType.REWARDED, player.getUserGuid()) + { + + } +} \ No newline at end of file diff --git a/GameServer/Event/EventInvokers/GameEventInvokerSeasonPassRewarded.cs b/GameServer/Event/EventInvokers/GameEventInvokerSeasonPassRewarded.cs new file mode 100644 index 0000000..aedf1d4 --- /dev/null +++ b/GameServer/Event/EventInvokers/GameEventInvokerSeasonPassRewarded.cs @@ -0,0 +1,10 @@ +using GameServer.EventDefines; + +namespace GameServer.EventInvokers; + +public class GameEventInvokerSeasonPassRewarded : GameEventInvokerBase +{ + public GameEventInvokerSeasonPassRewarded(Player player) : base(GameEventType.SEASONPASS, GameEventActionType.REWARDED, player.getUserGuid()) + { + } +} \ No newline at end of file diff --git a/GameServer/Event/EventInvokers/GameEventInvokerTaxiArrived.cs b/GameServer/Event/EventInvokers/GameEventInvokerTaxiArrived.cs new file mode 100644 index 0000000..36b20d4 --- /dev/null +++ b/GameServer/Event/EventInvokers/GameEventInvokerTaxiArrived.cs @@ -0,0 +1,14 @@ +using GameServer.EventDefines; + +namespace GameServer.EventInvokers; + +public class GameEventInvokerTaxiArrived : GameEventInvokerBase +{ + + public GameEventInvokerTaxiArrived(Player player) : base(GameEventType.TAXI, GameEventActionType.ARRIVED, player.getUserGuid()) + { + } + + + +} \ No newline at end of file diff --git a/GameServer/Event/EventInvokers/GameEventRunRaceCompleteTime.cs b/GameServer/Event/EventInvokers/GameEventRunRaceCompleteTime.cs new file mode 100644 index 0000000..afd3688 --- /dev/null +++ b/GameServer/Event/EventInvokers/GameEventRunRaceCompleteTime.cs @@ -0,0 +1,18 @@ +using GameServer.EventDefines; + +namespace GameServer.EventInvokers; + +public class GameEventRunRaceCompleteTime : GameEventInvokerBase +{ + public int m_game_mode_id { get; } + public double m_completion_time { get; } + public int m_instance_meta_id { get; } + + public GameEventRunRaceCompleteTime(Player player, int gameModeId, int instanceMetaId, double completionTime) : + base(GameEventType.GAMEMODE, GameEventActionType.ENDED, player.getUserGuid()) + { + m_game_mode_id = gameModeId; + m_completion_time = completionTime; + m_instance_meta_id = instanceMetaId; + } +} diff --git a/GameServer/Event/EventInvokers/IGameEventInvoker.cs b/GameServer/Event/EventInvokers/IGameEventInvoker.cs new file mode 100644 index 0000000..b7af12f --- /dev/null +++ b/GameServer/Event/EventInvokers/IGameEventInvoker.cs @@ -0,0 +1,12 @@ +using GameServer.EventDefines; + +namespace GameServer.EventInvokers; + +public interface IGameEventInvoker +{ + public GameEventType getGameEventType(); + public GameEventActionType getGameEventActionType(); + + public string getUserGuid(); + +} \ No newline at end of file diff --git a/GameServer/Event/EventManager.cs b/GameServer/Event/EventManager.cs index abe651a..5cb5a0d 100644 --- a/GameServer/Event/EventManager.cs +++ b/GameServer/Event/EventManager.cs @@ -16,7 +16,7 @@ namespace GameServer private ConcurrentDictionary AcceptableClaim = new(); //private ConcurrentDictionary NextDeletableClaim = new(); - public EventManager() + public EventManager() { OnInit(); } @@ -25,12 +25,12 @@ namespace GameServer { DateTime now_dt = DateTimeHelper.Current; AcceptableClaim = new(); - + foreach (MetaAssets.ClaimMetaData claim in MetaData.Instance._ClaimTable) { if (now_dt > claim.EndTime) continue; // 이미 종료된 이벤트는 무시 - + if (AcceptableClaim.TryAdd(claim.ClaimId, claim) == false) { Log.getLogger().error($"{claim.ClaimId} Add Error"); @@ -45,7 +45,7 @@ namespace GameServer List deletables = new List(); List currents = new List(); - + foreach (var claime in AcceptableClaim) { //삭제 가능 이벤트 중에 지난 것들 deletables에 추가 @@ -55,7 +55,7 @@ namespace GameServer foreach (int idx in deletables) { - if (AcceptableClaim.TryRemove(idx, out var claimData) == false) + if (AcceptableClaim.TryRemove(idx, out var claimData) == false) { Log.getLogger().error($"NextAcceptableClaim TryRemove Error idx : idx"); } diff --git a/GameServer/Event/EventRunners/GameEventRankRunRaceFinishTime.cs b/GameServer/Event/EventRunners/GameEventRankRunRaceFinishTime.cs new file mode 100644 index 0000000..df229fd --- /dev/null +++ b/GameServer/Event/EventRunners/GameEventRankRunRaceFinishTime.cs @@ -0,0 +1,6 @@ +namespace GameServer.EventRunners; + +public class GameEventRankRunRaceFinishTime : GameEventRunnerBase +{ + +} \ No newline at end of file diff --git a/GameServer/Event/EventRunners/GameEventRunnerBase.cs b/GameServer/Event/EventRunners/GameEventRunnerBase.cs new file mode 100644 index 0000000..cfb4995 --- /dev/null +++ b/GameServer/Event/EventRunners/GameEventRunnerBase.cs @@ -0,0 +1,6 @@ +namespace GameServer.EventRunners; + +public abstract class GameEventRunnerBase : IGameEventRunner +{ + +} \ No newline at end of file diff --git a/GameServer/Event/EventRunners/IGameEventRunner.cs b/GameServer/Event/EventRunners/IGameEventRunner.cs new file mode 100644 index 0000000..9eb3b9b --- /dev/null +++ b/GameServer/Event/EventRunners/IGameEventRunner.cs @@ -0,0 +1,6 @@ +namespace GameServer.EventRunners; + +public interface IGameEventRunner +{ + +} \ No newline at end of file diff --git a/GameServer/Event/GameEventAnalyzer.cs b/GameServer/Event/GameEventAnalyzer.cs new file mode 100644 index 0000000..ca67b5f --- /dev/null +++ b/GameServer/Event/GameEventAnalyzer.cs @@ -0,0 +1,67 @@ +using GameServer.EventInvokers; +using GameServer.EventRunners; + +namespace GameServer; + +public class GameEventAnalyzer +{ + + public Task registerEventRunner(IGameEventRunner runner) + { + //, GameEventRunner.FnCompletedEvent fnCompletedEvent, GameEventRunner.FnCompletedCondition fnCompletedCondition todo : fn은 받을지 말지 고민 필요 + var ret = addEventRunner(runner); + + //ret 예외처리 필요 + + return Task.FromResult(new Result()); + } + public Task unregisterEventRunner(Int32 eventMetaId, bool isWithDelegate = true) + { + removeEventRunner(eventMetaId); + return Task.FromResult(new Result()); + } + + private bool addEventRunner(IGameEventRunner runner) + { + return true; + } + + private void removeEventRunner(Int32 eventMetaId) + { + return; + } + + public void onReceive(IGameEventInvoker invoker) + { + + } + + public void checkRunner(IGameEventRunner runner) + { + + } + + // public string makeInvokerKey(InvokerType invokerType, InvokerActionType invokerActionType) + // { + // return ""; + // } + + // public Dictionary> findGroup(InvokerType invokerType, InvokerActionType invokerActionType) + // { + // //여기서 findEventRunnerByEventMetaId() 호출 + // return new(); + // } + // + // public Dictionary> findAndInsertGroup(InvokerType invokerType, InvokerActionType invokerActionType) + // { + // //여기서 findEventRunnerByEventMetaId() 호출 + // return new(); + // } + // + // public EventRunner findEventRunnerByEventMetaId(Int32 eventMetaId) + // { + // return null; + // } + + +} \ No newline at end of file diff --git a/GameServer/Event/GameEventCollector.cs b/GameServer/Event/GameEventCollector.cs new file mode 100644 index 0000000..667d938 --- /dev/null +++ b/GameServer/Event/GameEventCollector.cs @@ -0,0 +1,57 @@ +using GameServer.EventInvokers; +using GameServer.Global; +using ServerBase; +using ServerCore; + +namespace GameServer; + +public class GameEventCollector : EntityBase, IInitializer +{ + GameEventAnalyzer m_event_analyzer; + + public override Task onInit() + { + Log.getLogger().debug("GameEventColloctorBase onInit"); + + return Task.FromResult(new Result()); + } + + public GameEventCollector(EntityBase parent, GameEventAnalyzer analyer) : base(EntityType.GameEventCollector, parent) + { + m_event_analyzer = analyer; + } + public async Task collectEvent(IGameEventInvoker invoker) + { + var result = await collectPrivateGameEvent(invoker); + if(result.isFail()) return result; + + + result = await collectGlobalGameEvent(invoker); + if(result.isFail()) return result; + + return result; + } + + private Task collectPrivateGameEvent(IGameEventInvoker invoker) + { + var result = new Result(); + return Task.FromResult(result); + } + private async Task collectGlobalGameEvent(IGameEventInvoker invoker) + { + var invoker_base = invoker as GameEventInvokerBase; + NullReferenceCheckHelper.throwIfNull(invoker_base, () => $"invoker_base is null !!! - {toBasicString()}"); + Log.getLogger().debug($"collectGlobalGameEvent - EventType: {invoker.getGameEventType()}, EventAction: {invoker.getGameEventActionType()}"); + + await EventActionScoreManager.It.ProcessScore(invoker); + return new Result(); + } + + // public GameEventAnalyzer getEventAnalyzer() + // { + // return m_event_analyzer; + // } + + + +} diff --git a/GameServer/Event/GameEventHelper.cs b/GameServer/Event/GameEventHelper.cs new file mode 100644 index 0000000..ad577ec --- /dev/null +++ b/GameServer/Event/GameEventHelper.cs @@ -0,0 +1,15 @@ +using GameServer.Action; +using GameServer.EventRunners; + +namespace GameServer; + +public static class GameEventHelper +{ + public static GameEventCollector getGameEventCollector(this Player player) + { + var event_collector_action = player.getEntityAction(); + return event_collector_action.getGameEventCollector(); + } + + +} \ No newline at end of file diff --git a/GameServer/Event/GameEventTriggerManager.cs b/GameServer/Event/GameEventTriggerManager.cs new file mode 100644 index 0000000..c0e0768 --- /dev/null +++ b/GameServer/Event/GameEventTriggerManager.cs @@ -0,0 +1,17 @@ +using ServerCore; + +namespace GameServer; + +public class GameEventTriggerManager : Singleton +{ + GameEventCollector m_game_event_collector; + public GameEventTriggerManager() + { + m_game_event_collector = new GameEventCollector(new GlobalGameEvent(), new GameEventAnalyzer()); + } + + public GameEventCollector getGlobalGameEventCollector() + { + return m_game_event_collector; + } +} \ No newline at end of file diff --git a/GameServer/Event/GlobalGameEventInvokerManager.cs b/GameServer/Event/GlobalGameEventInvokerManager.cs new file mode 100644 index 0000000..39840fb --- /dev/null +++ b/GameServer/Event/GlobalGameEventInvokerManager.cs @@ -0,0 +1,32 @@ +using ServerCore; + +namespace GameServer; + +public class GlobalGameEventInvokerManager : Singleton +{ + private GameEventAnalyzer m_game_event_analyzer; + public GlobalGameEventInvokerManager() + { + m_game_event_analyzer = new GameEventAnalyzer(); + + // IGmaeEventRunner event_runner = new(); + // m_game_event_analyzer.registerEventRunner(); + onInit(); + } + + private void onInit() + { + registRankingEventInvokers(); + } + + private void registRankingEventInvokers() + { + //여기에 랭킹이벤트 별로 추가 + } + + public GameEventAnalyzer getGameEventAnalyzer() + { + return m_game_event_analyzer; + } + +} \ No newline at end of file diff --git a/GameServer/Event/Manage/GlobalGameEvent.cs b/GameServer/Event/Manage/GlobalGameEvent.cs new file mode 100644 index 0000000..a5f09f2 --- /dev/null +++ b/GameServer/Event/Manage/GlobalGameEvent.cs @@ -0,0 +1,12 @@ +using ServerBase; + +namespace GameServer; + +public class GlobalGameEvent : EntityBase +{ + public GlobalGameEvent() : base(EntityType.GameEvent) + { + } + + +} \ No newline at end of file diff --git a/GameServer/GameServer.csproj b/GameServer/GameServer.csproj index cd560f7..6f6db8e 100644 --- a/GameServer/GameServer.csproj +++ b/GameServer/GameServer.csproj @@ -1,57 +1,49 @@ - - - - Exe - net8.0 - enable - enable - true - true - Debug;Release;Shipping - true + + + Exe + net8.0 + enable + enable + true + true + Debug;Release;Shipping + true true - - - - - - - - - - - - full - $(DefineConstants);SEQUENCE; - True - - - - full - $(DefineConstants);SEQUENCE; - True - - - - full - $(DefineConstants);SEQUENCE; - true - True - - - - - - - - - ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetClient.dll - True - - - ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetServer.dll - True - - - - + + + + + + + + + + full + $(DefineConstants);SEQUENCE; + True + + + full + $(DefineConstants);SEQUENCE; + True + + + full + $(DefineConstants);SEQUENCE; + true + True + + + + + + + ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetClient.dll + True + + + ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetServer.dll + True + + + \ No newline at end of file diff --git a/GameServer/GameServerApp.cs b/GameServer/GameServerApp.cs index 9e8d88c..d6f0623 100644 --- a/GameServer/GameServerApp.cs +++ b/GameServer/GameServerApp.cs @@ -3,8 +3,7 @@ using Microsoft.Extensions.Configuration; using CommandLine; - - +using ControlCenter.NamedPipe.Model; using ServerCore; using ServerBase; using ServerCommon; @@ -42,6 +41,10 @@ public class Options [Option("defaultMaxUser", Default = (UInt16)2000 )] public UInt16 DefaultMaxUser { get; set; } = 2000; + + // 디버그용 옵션 추가 + [Option("debug", Default = false)] + public bool Debug { get; set; } = false; } public static class GameServerApp @@ -102,7 +105,8 @@ public static class GameServerApp { "serverType", argOptions.ServerTypeString }, { "worldId", argOptions.WorldId.ToString() }, { "channelNo", argOptions.ChannelNo.ToString() }, - { "defaultMaxUser", argOptions.DefaultMaxUser.ToString() } + { "defaultMaxUser", argOptions.DefaultMaxUser.ToString() }, + { "debug", argOptions.Debug.ToString() } }; var configuration = new ConfigurationBuilder().AddInMemoryCollection(key_options!).Build(); @@ -113,7 +117,17 @@ public static class GameServerApp m_server_logic.setServerType(argOptions.ServerTypeString); m_server_logic.setConfiguration(configuration); - result = await m_server_logic.onRunServer(); + var config = m_server_logic.getConfiguration(); + + var namedOptionBuilder = new NamedPipeClientOptionBuilder(); + namedOptionBuilder.setIP(NetworkHelper.getEthernetLocalIPv4()) + .setPort(config.getPort()) + .setType(config.getServerType().toServerTypeString()) + .setServiceCategory(nameof(ServiceCategory.Caliverse)) + .setWorldID((int)config.getWorldId()) + .setChannelNo((int)config.getChannelNo()); + + result = await m_server_logic.onRunServer(namedOptionBuilder); if (result.isFail()) { return result; diff --git a/GameServer/GameServerLogic.cs b/GameServer/GameServerLogic.cs index 3688d88..0cc0c46 100644 --- a/GameServer/GameServerLogic.cs +++ b/GameServer/GameServerLogic.cs @@ -1,14 +1,10 @@ -using Nettention.Proud; +using GameServer.Contents.WorldEvent; +using Nettention.Proud; using Google.Protobuf; using Microsoft.Extensions.Configuration; - - using ServerCore; using ServerBase; using ServerCommon; -using ServerControlCenter; - - using MODULE_ID = System.UInt32; using WORLD_META_ID = System.UInt32; using CHANNEL_NO = System.UInt32; @@ -16,8 +12,8 @@ using CHANNEL_NO = System.UInt32; namespace GameServer; - -public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVerifier, IWithConfiguration, IWithServerMetrics, IWithLogActor +public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVerifier, IWithConfiguration, + IWithServerMetrics, IWithLogActor { private readonly PlayerManager m_player_manager = new(); private readonly SystemMetaMailManager m_system_mail_manager = new(); @@ -27,7 +23,11 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri private readonly UgqApiManager m_ugq_api_manager = new(); private readonly LandManager m_land_manager = new(); private readonly BuildingManager m_building_manager = new(); - + private readonly BannerManager m_banner_manager = new(); + private readonly RankingScheduleManager m_ranking_schedule_manager = new(); + private readonly RankingManager m_ranking_manager = new(); + private readonly WorldEventScheduleManager _worldEventScheduleManager = new(); + private ReservationManager? m_reservation_manager { get; set; } private ReturnManager? m_return_manager { get; set; } @@ -35,6 +35,8 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri private Map m_map = new(); + private IConfiguration? m_key_options; + public GameServerLogic(ServerConfig config) : base(config) { @@ -45,6 +47,7 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri var result = new Result(); var key_options = configuration; + m_key_options = key_options; var port = ushort.Parse(key_options["port"] ?? "0"); var config_file = key_options["config"]; @@ -75,7 +78,7 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri Log.getLogger().error(err_msg); return result; } - + await onSetAccountLoginBlock(getServerConfig().AccountLoginBlockEnable); MapDataTable.Instance.Load(); @@ -105,12 +108,14 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri var listen_port = server_config.toClientListenPort(); if (listen_port <= 0) { - err_msg = $"Client listen port invalid !!! : appParamPort:{listen_port}, configListenPort:{server_config.ClientListenPort} - {server_type.ToString()}"; + err_msg = + $"Client listen port invalid !!! : appParamPort:{listen_port}, configListenPort:{server_config.ClientListenPort} - {server_type.ToString()}"; result.setFail(ServerErrorCode.ClientListenPortInvalid, err_msg); return result; } - base.setServerName(server_type.toServerName(server_config.toClientListenIP(), listen_port, (int)getWorldId(), (int)getChannelNo())); + base.setServerName(server_type.toServerName(server_config.toClientListenIP(), listen_port, (int)getWorldId(), + (int)getChannelNo())); return result; } @@ -126,20 +131,21 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri var config = getServerConfig(); var load_config_info = config.getLoadedConfig(); - NullReferenceCheckHelper.throwIfNull(load_config_info, () => $"load_config_info is null !!! - {toBasicString()}"); + NullReferenceCheckHelper.throwIfNull(load_config_info, + () => $"load_config_info is null !!! - {toBasicString()}"); { result = await tryCreateAndRegisterModule(() => { var config_param = new DynamoDbClient.ConfigParam(); result = config_param.tryReadFromJsonOrDefault(load_config_info); - if(result.isFail()) + if (result.isFail()) { return (null, result); } var module_context = new ModuleContext((MODULE_ID)ModuleId.DynamoDbConnector - , 0, 0, config_param); + , 0, 0, config_param); var created_module = new DynamoDbClient(module_context); return (created_module, result); }); @@ -154,22 +160,21 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri return (null, result); } - var module_context = new ModuleContext( (MODULE_ID)ModuleId.RedisConnector - , 0, 0, config_param ); + var module_context = new ModuleContext((MODULE_ID)ModuleId.RedisConnector + , 0, 0, config_param); var created_module = new RedisConnector(module_context); return (created_module, result); }); } { - result = await tryCreateAndRegisterModule(() => + result = await tryCreateAndRegisterModule(() => { var found_redis_connector = getModule((MODULE_ID)ModuleId.RedisConnector); - if(null == found_redis_connector) + if (null == found_redis_connector) { return ( - null - , new Result() { ErrorCode = ServerErrorCode.ModuleNotFound - , ResultString = $"RedisConnector not found !!! : moduleId:{ModuleId.RedisConnector}" } + null + , new Result() { ErrorCode = ServerErrorCode.ModuleNotFound, ResultString = $"RedisConnector not found !!! : moduleId:{ModuleId.RedisConnector}" } ); } @@ -181,24 +186,26 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri } var module_context = new ModuleContext((MODULE_ID)ModuleId.RedisWithLuaScriptExecutor - , 0, 0, config_param); + , 0, 0, config_param); var created_module = new RedisWithLuaScriptExecutor(found_redis_connector, module_context); return (created_module, result); }); } { - result = await tryCreateAndRegisterModule(() => { + result = await tryCreateAndRegisterModule(() => + { var config_param = new RabbitMqConnector.ConfigParam(); result = config_param.tryReadFromJsonOrDefault(load_config_info); if (result.isFail()) { return (null, result); } + config_param.ServiceName = getServerName(); - var module_context = new ModuleContext( (MODULE_ID)ModuleId.RabbitMqConnector - , 0, 0, config_param ); + var module_context = new ModuleContext((MODULE_ID)ModuleId.RabbitMqConnector + , 0, 0, config_param); var created_module = new RabbitMQ4Game(module_context); config_param.fnServerMessageRecvFromConsumer = created_module.onRecvProtocol; @@ -216,7 +223,7 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri } var module_context = new ModuleContext((MODULE_ID)ModuleId.MongoDbConnector - , 0, 0, config_param); + , 0, 0, config_param); var created_module = new MongoDbConnector(module_context); return (created_module, result); }); @@ -232,7 +239,7 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri } var module_context = new ModuleContext((MODULE_ID)ModuleId.S3Connector - , 0, 0, config_param); + , 0, 0, config_param); var created_module = new S3Connector(module_context); return (created_module, result); }); @@ -246,11 +253,12 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri { return (null, result); } + config_param.ListenIp = config.toClientListenIP(); config_param.ListenPort = config.toClientListenPort(); - var module_context = new ModuleContext( (MODULE_ID)ModuleId.ProudNetListener - , 0, 0, config_param ); + var module_context = new ModuleContext((MODULE_ID)ModuleId.ProudNetListener + , 0, 0, config_param); var created_module = new GameLoginListener(this, module_context); created_module.FnSendPacketToHost = onSendPacketToHost; created_module.FnSendPacketToHosts = onSendPacketToHosts; @@ -267,17 +275,14 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri var result = await syncServerInfoToCache(); if (result.isFail()) { - Log.getLogger().error($"Failed to syncServerInfoToCache() !!! : {result.toBasicString()} - {toBasicString()}"); + Log.getLogger() + .error($"Failed to syncServerInfoToCache() !!! : {result.toBasicString()} - {toBasicString()}"); } } protected override List onAppendRuleAll() { - return new List() - { - new InventoryRule(), - - }; + return new List() { new InventoryRule(), }; } public override async Task onCreateTickerAll() @@ -287,7 +292,9 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri result = await this.createTimeEventForMinuteTicker(); if (result.isFail()) { - Log.getLogger().error($"Failed to createTimeEventForMinuteTicker() !!! : {result.toBasicString()} - {toBasicString()}"); + Log.getLogger() + .error( + $"Failed to createTimeEventForMinuteTicker() !!! : {result.toBasicString()} - {toBasicString()}"); return result; } @@ -297,25 +304,56 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri if (ServerType.Channel == server_type) { - entity_ticker_initializers.appendInitializer(new ChannelUpdateTicker(ServerCommon.Constant.CHANNEL_UPDATE_TIME, null)); - entity_ticker_initializers.appendInitializer(new ShopProductCheckTicker(ServerCommon.Constant.SHOP_PRODUCT_CHECK_INTERVAL_MSEC, null)); - entity_ticker_initializers.appendInitializer(new CaliumEventTicker(ServerCommon.Constant.CALIUM_EVENT_CHECK_INTERVAL_MSEC, null)); - entity_ticker_initializers.appendInitializer(new CaliumStorageRetryTicker(ServerCommon.Constant.CALIUM_RETRY_CHECK_INTERVAL_MSEC, null)); - entity_ticker_initializers.appendInitializer(new BuildingUpdateTicker(ServerCommon.Constant.BUILDING_UPDATE_INTERVAL_MSEC, null)); - entity_ticker_initializers.appendInitializer(new LandAuctionReservationConfigureTicker(ServerCommon.Constant.LAND_AUCTION_RESERVATION_CONFIGURE_INTERVAL_MSEC, null)); - entity_ticker_initializers.appendInitializer(new LandAuctionCheckTicker(ServerCommon.Constant.LAND_AUCTION_CHECK_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new ChannelUpdateTicker(ServerCommon.Constant.CHANNEL_UPDATE_TIME, null)); + entity_ticker_initializers.appendInitializer( + new ShopProductCheckTicker(ServerCommon.Constant.SHOP_PRODUCT_CHECK_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new CaliumEventTicker(ServerCommon.Constant.CALIUM_EVENT_CHECK_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new CaliumStorageRetryTicker(ServerCommon.Constant.CALIUM_RETRY_CHECK_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new BuildingUpdateTicker(ServerCommon.Constant.BUILDING_UPDATE_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new LandAuctionReservationConfigureTicker( + ServerCommon.Constant.LAND_AUCTION_RESERVATION_CONFIGURE_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new LandAuctionCheckTicker(ServerCommon.Constant.LAND_AUCTION_CHECK_INTERVAL_MSEC, null)); } - entity_ticker_initializers.appendInitializer(new EntityUpdateTicker(ServerCommon.Constant.ENTITY_UPDATE_INTERVAL_MSEC, null)); - entity_ticker_initializers.appendInitializer(new EventUpdateTicker(ServerCommon.Constant.EVENT_UPDATE_INTERVAL_3_SEC, null)); - entity_ticker_initializers.appendInitializer(new UserLoginCacheRefreshTicker(ServerCommon.Constant.KEEP_LOGIN_UPDATE_TIME, null)); - entity_ticker_initializers.appendInitializer(new NoticeChatTicker(ServerCommon.Constant.NOTICE_CHAT_UPDATE_TIME, null)); + entity_ticker_initializers.appendInitializer( + new EntityUpdateTicker(ServerCommon.Constant.ENTITY_UPDATE_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new EventUpdateTicker(ServerCommon.Constant.EVENT_UPDATE_INTERVAL_3_SEC, null)); + entity_ticker_initializers.appendInitializer( + new UserLoginCacheRefreshTicker(ServerCommon.Constant.KEEP_LOGIN_UPDATE_TIME, null)); + entity_ticker_initializers.appendInitializer(new NoticeChatTicker(ServerCommon.Constant.NOTICE_CHAT_UPDATE_TIME, + null)); - entity_ticker_initializers.appendInitializer(new PartyCacheRefreshTicker(ServerCommon.Constant.KEEP_PARTY_TIME / 2, null)); - entity_ticker_initializers.appendInitializer(new ReservationCheckTicker(ServerCommon.Constant.RESERVATION_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new PartyCacheRefreshTicker(ServerCommon.Constant.KEEP_PARTY_TIME / 2, null)); + entity_ticker_initializers.appendInitializer( + new ReservationCheckTicker(ServerCommon.Constant.RESERVATION_INTERVAL_MSEC, null)); - entity_ticker_initializers.appendInitializer(new NormalQuestCheckTicker(ServerCommon.Constant.NORMAL_QUEST_CHECK_INTERVAL_MSEC, null)); - entity_ticker_initializers.appendInitializer(new SystemMailCheckTicker(ServerCommon.Constant.SYSTEM_MAIL_CHECK_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new NormalQuestCheckTicker(ServerCommon.Constant.NORMAL_QUEST_CHECK_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new SystemMailCheckTicker(ServerCommon.Constant.SYSTEM_MAIL_CHECK_INTERVAL_MSEC, null)); + + entity_ticker_initializers.appendInitializer( + new RankingScheduleUpdateTicker(ServerCommon.Constant.RANKING_SCHESULE_CHECK_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new RankingUpdateTicker(ServerCommon.Constant.RANKING_CHECK_INTERVAL_MSEC, null)); + + // 월드 이벤트 + entity_ticker_initializers.appendInitializer( + new WorldEventScheduleUpdateTicker(ServerCommon.Constant.GAME_EVENT_CHECK_INTERVAL)); + + // Match 서버 목록 갱신 + entity_ticker_initializers.appendInitializer( + new MatchServerListTicker(ServerCommon.Constant.MATCH_SERVER_LIST_INTERVAL_MSEC, null)); + entity_ticker_initializers.appendInitializer( + new GameEventCheckTicker(ServerCommon.Constant.GAME_EVENT_CHECK_INTERVAL)); result = await entity_ticker_initializers.init("EntityTickers"); if (result.isFail()) @@ -347,7 +385,9 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri public async Task setupServerMetrics() { - var result = await ServerBase.ServerMetricsHelper.setupDefaultServerMetrics(this, (MODULE_ID)ModuleId.RedisConnector); + var result = + await ServerBase.ServerMetricsHelper.setupDefaultServerMetrics(this, + (MODULE_ID)ModuleId.RedisConnector); if (result.isFail()) { return result; @@ -366,7 +406,7 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri if (getServerType().toServerType() == ServerType.Channel) { config.setChannelNo(getChannelNo()); - config.setWorldId((ushort)getWorldId()); + config.setWorldId((ushort)getWorldId()); } MetaTextStringFuncHelper.registerFuncAll(); @@ -384,14 +424,17 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri return result; } } - + + Log.getLogger().info($"tryLoadGlobalContent start !!! : {server_name} - {toBasicString()}"); result = await tryLoadGlobalContent(); - if(result.isFail()) + if (result.isFail()) { Log.getLogger().error(result.toBasicString()); return result; } + Log.getLogger().info($"tryLoadGlobalContent finish !!! : {server_name} - {toBasicString()}"); + result = await base.onStartServer(); if (result.isFail()) { @@ -406,7 +449,8 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri if (getServerType().toServerType() == ServerType.Channel) { - if (ServerCommon.MetaData.Instance._WorldMetaTable.TryGetValue((int)getWorldId(), out var worldMetaData) == false) + if (ServerCommon.MetaData.Instance._WorldMetaTable.TryGetValue((int)getWorldId(), out var worldMetaData) == + false) { err_msg = $"Failed to onInit() !!! : Failed to read WorldMetaData !!! worldMetaId:{getWorldId()}"; result.setFail(ServerErrorCode.NotFoundTable, err_msg); @@ -434,56 +478,121 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri { var result = new Result(); - result = await m_system_mail_manager.LoadDB(); { if (result.isFail()) return result; } - result = await m_notice_chat_manager.LoadDB(); { if (result.isFail()) return result; } - result = await m_land_manager.loadLands(); { if (result.isFail()) return result; } - result = await m_building_manager.loadBuildings(); { if (result.isFail()) return result; } - result = await LandAuctionManager.It.tryLoadLandAuctionAll(); { if (result.isFail()) return result; } + result = await m_system_mail_manager.LoadDB(); + { + if (result.isFail()) return result; + } + Log.getLogger().Info("tryLoadGlobalContent - m_system_mail_manager.LoadDB() OK"); + + result = await m_notice_chat_manager.LoadDB(); + { + if (result.isFail()) return result; + } + Log.getLogger().Info("tryLoadGlobalContent - m_notice_chat_manager.LoadDB()OK"); + + result = await m_land_manager.loadLands(); + { + if (result.isFail()) return result; + } + Log.getLogger().Info("tryLoadGlobalContent - m_land_manager.loadLands() OK"); + + var isDebugMode = this.isDebugMode(); + if (isDebugMode == false) + { + result = await m_building_manager.loadBuildings(); + { + if (result.isFail()) return result; + } + Log.getLogger().Info("tryLoadGlobalContent - m_building_manager.loadBuildings() OK"); + } + + if (isDebugMode == false) + { + result = await LandAuctionManager.It.tryLoadLandAuctionAll(); + { + if (result.isFail()) return result; + } + Log.getLogger().Info("tryLoadGlobalContent - LandAuctionManager.It.tryLoadLandAuctionAll() OK"); + } + + result = await m_banner_manager.loadBanners(); + { + if (result.isFail()) return result; + } + Log.getLogger().Info("tryLoadGlobalContent - m_banner_manager.loadBanners() OK"); + + result = await m_ranking_schedule_manager.loadRankingSchedules(); + { + if (result.isFail()) return result; + } + Log.getLogger().Info("tryLoadGlobalContent - m_ranking_schedule_manager.loadRankingSchedules() OK"); + + result = await m_ranking_manager.loadRankings(); + { + if (result.isFail()) return result; + } + Log.getLogger().Info("tryLoadGlobalContent - m_ranking_manager.loadRankings() OK"); + + result = await _worldEventScheduleManager.LoadSchedulesAsync(this.getDynamoDbClient()); + { + if (result.isFail()) return result; + } + Log.getLogger().Info("tryLoadGlobalContent - m_world_event_scheduler.LoadEventsFromDatabaseAsync() OK"); m_season_pass_manager.Init(); if (getServerConfig().OfflineMode == false) { await AIChatServerConnector.onInit(); } + + Log.getLogger().Info("tryLoadGlobalContent - m_season_pass_manager.Init() OK"); + await m_ugq_api_manager.onInit(); - + Log.getLogger().Info("tryLoadGlobalContent - m_ugq_api_manager.Init() OK"); + // calium converter entity var calium_storage_entity = findGlobalEntity(); NullReferenceCheckHelper.throwIfNull(calium_storage_entity, () => $"CaliumStorageEntity is null !!!"); var calium_storage_action = calium_storage_entity.getEntityAction(); NullReferenceCheckHelper.throwIfNull(calium_storage_action, () => $"CaliumStorageAction is null !!!"); - result = await calium_storage_action.initStorageAttribute(); { if (result.isFail()) return result; } + result = await calium_storage_action.initStorageAttribute(); + { + if (result.isFail()) return result; + } // ugcnpcrank entity var ugc_npc_rank_entity = findGlobalEntity(); NullReferenceCheckHelper.throwIfNull(ugc_npc_rank_entity, () => $"UgcNpcRankEntity is null !!!"); var ugc_npc_rank_action = ugc_npc_rank_entity.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_rank_action, () => $"UgcNpcRankManageAction is null !!!"); - result = await ugc_npc_rank_action.initRank(); { if (result.isFail()) return result; } + result = await ugc_npc_rank_action.initRank(); + { + if (result.isFail()) return result; + } - - //Battle 처리 + + //Battle 처리 var server_logic = GameServerApp.getServerLogic(); var server_config = server_logic.getServerConfig(); - + bool is_battle_system_active = server_config.BattleSystemEnable; if (is_battle_system_active) { await BattleInstanceManager.It.onInit(); } - - + + return result; } protected override List onAppendGlobalEntityAll() { var list = new List(); - + list.Add(new GlobalParty()); list.Add(new UgcNpcRankEntity()); list.Add(new CaliumStorageEntity()); - + return list; } @@ -493,7 +602,8 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri NullReferenceCheckHelper.throwIfNull(client_to_game, () => $"client_to_game is null !!! - {toBasicString()}"); var proud_net_listener = this.getProudNetListener(); - var found_proxy = proud_net_listener.findProxy(); ; + var found_proxy = proud_net_listener.findProxy(); + ; NullReferenceCheckHelper.throwIfNull(found_proxy, () => $"found_proxy is null !!! - {toBasicString()}"); return found_proxy.Message(hostId, RmiContext.ReliableSend, client_to_game); @@ -505,7 +615,8 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri NullReferenceCheckHelper.throwIfNull(client_to_game, () => $"client_to_game is null !!! - {toBasicString()}"); var proud_net_listener = this.getProudNetListener(); - var found_proxy = proud_net_listener.findProxy(); ; + var found_proxy = proud_net_listener.findProxy(); + ; NullReferenceCheckHelper.throwIfNull(found_proxy, () => $"found_proxy is null !!! - {toBasicString()}"); return found_proxy.Message(hostIds, rmiContext, client_to_game); @@ -538,16 +649,16 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri room_capacity = InstanceRoomManager.Instance.Capacity; } - result = await ServerBase.ServerMetricsHelper.syncServerInfoToCache( this - , server_name - , listen_ip, listen_port - , user_count, proud_net_listener.getMaxConnectionCount() - , reservation_count, return_count - , ugc_npc_count - , awsInstanceId - , (Int32)getWorldId() - , (Int32)getChannelNo() - , room_capacity ); + result = await ServerBase.ServerMetricsHelper.syncServerInfoToCache(this + , server_name + , listen_ip, listen_port + , user_count, proud_net_listener.getMaxConnectionCount() + , reservation_count, return_count + , ugc_npc_count + , awsInstanceId + , (Int32)getWorldId() + , (Int32)getChannelNo() + , room_capacity); if (result.isFail()) { return result; @@ -556,18 +667,20 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri return result; } - public bool isValidPacketNamespace(string toCheckNamespace, IPacketCommand packetCommand) + public bool isValidPacketNamespace(string? toCheckNamespace, IPacketCommand packetCommand) { var packet_namespace = "GameServer.PacketHandler"; - if ( null != toCheckNamespace + if (null != toCheckNamespace && true == toCheckNamespace.Contains(packet_namespace)) { return true; } else { - ServerCore.Log.getLogger().error($"Invalid GameServer PacketNamespace !!!, not included Namespace : {packet_namespace} ⊆ {toCheckNamespace}, packetCommnad:{packetCommand.toBasicString()}"); + ServerCore.Log.getLogger() + .error( + $"Invalid GameServer PacketNamespace !!!, not included Namespace : {packet_namespace} ⊆ {toCheckNamespace}, packetCommnad:{packetCommand.toBasicString()}"); } return false; @@ -589,4 +702,28 @@ public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVeri { return this.toLogActor(); } + + // 디버깅을 위한 코드 추가 + private bool isDebugMode() + { + if (getServerConfig().ServiceType != Enum.GetName(ServiceType.Dev)) + { + return false; + } + + try + { + bool.TryParse(m_key_options?["debug"], out bool isDebugMode); + return isDebugMode; + } + catch (Exception) + { + return false; + } + } + + protected override Task? onRunning() + { + return base.onRunning(); + } } diff --git a/GameServer/Global/Banner/Action/BannerAction.cs b/GameServer/Global/Banner/Action/BannerAction.cs new file mode 100644 index 0000000..38449e2 --- /dev/null +++ b/GameServer/Global/Banner/Action/BannerAction.cs @@ -0,0 +1,52 @@ +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal class BannerAction : EntityActionBase + { + public BannerAction(Banner owner) + : base(owner) + { } + + public override async Task onInit() + { + var result = new Result(); + return await Task.FromResult(result); + } + + public override void onClear() + { + return; + } + + public Result tryLoadBannerFromDoc(BannerDoc bannerDoc) + { + var result = new Result(); + var err_msg = string.Empty; + + var banner = getOwner() as Banner; + NullReferenceCheckHelper.throwIfNull(banner, () => $"banner is null !!!"); + + var banner_attribute = banner.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(banner_attribute, () => $"banner_attribute is null !!!"); + + if (!banner_attribute.copyEntityAttributeFromDoc(bannerDoc)) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!! to:{banner_attribute.getTypeName()}, from:{bannerDoc.getTypeName()} : {this.getTypeName()}"; + result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + return result; + } + } +} diff --git a/GameServer/Global/Banner/Banner.cs b/GameServer/Global/Banner/Banner.cs new file mode 100644 index 0000000..7707151 --- /dev/null +++ b/GameServer/Global/Banner/Banner.cs @@ -0,0 +1,38 @@ +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal class Banner : EntityBase + { + public Banner() + : base(EntityType.Banner) + { + } + + public override async Task onInit() + { + addEntityAttribute(new BannerAttribute(this)); + + addEntityAction(new BannerAction(this)); + + return await base.onInit(); + } + + public override string toBasicString() + { + return $"{this.getTypeName()}, BuildingMetaId:{getOriginEntityAttribute()?.BannerId}"; + } + + public override string toSummaryString() + { + return $"{this.getTypeName()}, {getEntityAttribute()?.toBasicString()}"; + } + } +} diff --git a/GameServer/Global/Banner/BannerManager.cs b/GameServer/Global/Banner/BannerManager.cs new file mode 100644 index 0000000..de1acc4 --- /dev/null +++ b/GameServer/Global/Banner/BannerManager.cs @@ -0,0 +1,112 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + public class BannerManager + { + ConcurrentDictionary m_banners = new(); + + public async Task loadBanners() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var doc = new BannerDoc(); + doc.setCombinationKeyForPK(DynamoDbClient.PK_GLOBAL); + + var error_code = doc.onApplyPKSK(); + if (error_code.isFail()) + { + err_msg = $"Failed to onApplyPKSK() !!! : {error_code.toBasicString()}"; + result.setFail(error_code, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var query_config = dynamo_db_client.makeQueryConfigForReadByPKOnly(doc.getPK()); + + (result, var read_docs) = await dynamo_db_client.simpleQueryDocTypesWithQueryOperationConfig(query_config); + if (result.isFail()) + { + err_msg = $"Failed to simpleQueryDocTypesWithQueryOperationConfig() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var banners = new ConcurrentDictionary(); + + foreach (var read_doc in read_docs) + { + var banner_attrib = read_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(banner_attrib, () => $"banner_attrib is null !!!"); + + if (banner_attrib.EndTime < DateTime.UtcNow) + continue; + + var banner = new Banner(); + await banner.onInit(); + + var banner_action = banner.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(banner_action, () => $"banner_action is null !!!"); + + result = banner_action.tryLoadBannerFromDoc(read_doc); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadBannerFromDoc() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var banner_attribute = banner.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(banner_attribute, () => $"banner_attribute is null !!!"); + + if (!banners.TryAdd(banner_attribute.BannerId, banner)) + { + err_msg = $"Failed to TryAdd() !!! : {banner.toBasicString()} : {this.getTypeName()}"; + result.setFail(ServerErrorCode.BannerDocLoadDuplicatedBanner, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + } + + m_banners = banners; + + return result; + } + + public List tryGetBannerInfos() + { + var banner_infos = new List(); + + foreach (var banner in m_banners.Values) + { + var banner_attribute = banner.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(banner_attribute, () => $"banner_attribute is null !!!"); + + if (banner_attribute.EndTime < DateTime.UtcNow) + continue; + + var banner_info = banner.toBannerInfo(); + banner_infos.Add(banner_info); + } + + return banner_infos; + } + } +} diff --git a/GameServer/Global/Banner/Helper/BannerHelper.cs b/GameServer/Global/Banner/Helper/BannerHelper.cs new file mode 100644 index 0000000..8a756e2 --- /dev/null +++ b/GameServer/Global/Banner/Helper/BannerHelper.cs @@ -0,0 +1,40 @@ +using Google.Protobuf.WellKnownTypes; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal static class BannerHelper + { + public static BannerInfo toBannerInfo(this Banner banner) + { + var banner_attribute = banner.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(banner_attribute, () => $"banner_attribute is null !!!"); + + var banner_info = new BannerInfo(); + + banner_info.BannerId = banner_attribute.BannerId; + banner_info.BannerType = banner_attribute.BannerType; + banner_info.StartTime = banner_attribute.StartTime.ToTimestamp(); + banner_info.EndTime = banner_attribute.EndTime.ToTimestamp(); + banner_info.OrderId = banner_attribute.OrderId; + + foreach (var (language_type, image_url) in banner_attribute.ImageUrl) + { + banner_info.ImageUrl.Add((int)language_type, image_url); + } + + foreach (var (language_type, link_address) in banner_attribute.LinkAddress) + { + banner_info.LinkAddress.Add((int)language_type, link_address); + } + + return banner_info; + } + } +} diff --git a/GameServer/Global/Banner/Helper/BannerNotifyHelper.cs b/GameServer/Global/Banner/Helper/BannerNotifyHelper.cs new file mode 100644 index 0000000..742824b --- /dev/null +++ b/GameServer/Global/Banner/Helper/BannerNotifyHelper.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ClientToGameMessage.Types; + +namespace GameServer +{ + internal static class BannerNotifyHelper + { + public static bool send_S2C_NTF_BANNER_INFOS(this Player player) + { + var result = new Result(); + + var server_logic = GameServerApp.getServerLogic(); + var banner_manager = server_logic.getBannerManager(); + + var ntf_packet = new ClientToGame(); + ntf_packet.Message = new ClientToGameMessage(); + ntf_packet.Message.NtfBannerInfos = new GS2C_NTF_BANNER_INFOS(); + + var banner_infos = banner_manager.tryGetBannerInfos(); + ntf_packet.Message.NtfBannerInfos.BannerInfos.AddRange(banner_infos); + + if (false == GameServerApp.getServerLogic().onSendPacket(player, ntf_packet)) + { + return false; + } + + return true; + } + } +} diff --git a/GameServer/Global/EventActionScore/EventActionScoreHelper.cs b/GameServer/Global/EventActionScore/EventActionScoreHelper.cs new file mode 100644 index 0000000..bedbf23 --- /dev/null +++ b/GameServer/Global/EventActionScore/EventActionScoreHelper.cs @@ -0,0 +1,78 @@ +using GameServer.EventDefines; +using GameServer.EventInvokers; +using MetaAssets; +using ServerCommon; +using ServerCore; + +namespace GameServer.Global; + +public class EventActionScoreHelper : IEventActionScoreHelper +{ + public (bool, int) FindMatchedAndGetEventActionScore(IGameEventInvoker eventInvoker, EventActionScoreMetaData eventActionScoreMeta) + { + // Enum.Parse는 유효하지 않은 문자열에 대해 예외를 발생시킬 수 있으므로, 더 안전한 TryParse를 사용합니다. + if (!Enum.TryParse(eventActionScoreMeta.EventTarget, out var targetType) || + !Enum.TryParse(eventActionScoreMeta.Event, out var actionType)) + { + // 메타데이터에 오류 + Log.getLogger().error($"Invalid EventActionScoreMetaData. GroupId: {eventActionScoreMeta.GroupId}, EventTarget: '{eventActionScoreMeta.EventTarget}', Event: '{eventActionScoreMeta.Event}'"); + return (false, 0); + } + + var isValidAction = + eventInvoker.getGameEventType() == targetType && + eventInvoker.getGameEventActionType() == actionType; + + if (!isValidAction) return (false, 0); + + switch (eventInvoker) + { + // 여기에 IGameEventInvoker를 구현한 모든 클래스 case 작성 + case GameEventInvokerCraftingCompleted craftingInvoker: + if (string.IsNullOrEmpty(eventActionScoreMeta.EventCondition1) || + craftingInvoker.m_item_id.ToString() == eventActionScoreMeta.EventCondition1) + { + return (true, eventActionScoreMeta.Score); + } + break; + case GameEventInvokerQuestRewarded questInvoker: + if (string.IsNullOrEmpty(eventActionScoreMeta.EventCondition1) || + questInvoker.m_quest_id.ToString() == eventActionScoreMeta.EventCondition1) + { + return (true, eventActionScoreMeta.Score); + } + break; + case GameEventRunRaceCompleteTime raceCompleteTimeInvoker: + if (raceCompleteTimeInvoker.m_game_mode_id.ToString() == eventActionScoreMeta.EventCondition1 && + raceCompleteTimeInvoker.m_instance_meta_id.ToString() == eventActionScoreMeta.EventCondition2) + { + var completionTimeMs = raceCompleteTimeInvoker.m_completion_time; + // 초 단위로 변경해서 전달. + var timeSecondes = (int)Math.Round(completionTimeMs / 1000); + return (true, timeSecondes); + } + break; + case GameEventFfaOnPickUpObject pickupObjectInvoker: + if (pickupObjectInvoker.m_game_mode_id.ToString() == eventActionScoreMeta.EventCondition1 && + nameof(pickupObjectInvoker.GameModeObjectType) == eventActionScoreMeta.EventCondition2) + { + return (true, eventActionScoreMeta.Score); + } + break; + case GameEventInvokerRewadPropRewarded: + case GameEventInvokerClaimRewarded: + case GameEventInvokerFarmingCompleted: + case GameEventInvokerTaxiArrived: + case GameEventInvokerSeasonPassRewarded: + return (true, eventActionScoreMeta.Score); + } + // 유효한 액션이었지만 점수 획득 조건에 맞지 않는 경우 + return (false, 0); + } + + public IEnumerable GetEventActionScoreMetas(int groupId) + { + return MetaData.Instance.Meta.EventActionScoreMetaTable.EventActionScoreMetaDataList.Where(x => + x.GroupId == groupId); + } +} diff --git a/GameServer/Global/EventActionScore/EventActionScoreManager.cs b/GameServer/Global/EventActionScore/EventActionScoreManager.cs new file mode 100644 index 0000000..8a5206f --- /dev/null +++ b/GameServer/Global/EventActionScore/EventActionScoreManager.cs @@ -0,0 +1,35 @@ +using GameServer.EventInvokers; +using ServerCore; + +namespace GameServer.Global; + + +public class EventActionScoreManager : Singleton +{ + private readonly Lazy _processor = new(Create); + + private static EventActionScoreProcessor Create() + { + var eventActionScoreProcessor = new EventActionScoreProcessor(); + var helper = new EventActionScoreHelper(); + { + var worldEventScheduleInfo = new WorldEventScheduleInfo(); + var worldEventScoreRepo = new WorldEventScoreRepo(); + var worldEventHandler = new WorldEventActionScore(helper, worldEventScheduleInfo, worldEventScoreRepo); + eventActionScoreProcessor.AddHandler(worldEventHandler); + } + { + var rankingScheduleInfo = new RankingEventEventScheduleInfo(); + var rankingEventHandler = new RankingEventActionScore(helper, rankingScheduleInfo); + eventActionScoreProcessor.AddHandler(rankingEventHandler); + } + return eventActionScoreProcessor; + } + + private EventActionScoreProcessor GetProcessor() => _processor.Value; + + public async Task ProcessScore(IGameEventInvoker invoker) + { + await GetProcessor().ProcessScore(invoker); + } +} diff --git a/GameServer/Global/EventActionScore/EventActionScoreProcessor.Log.cs b/GameServer/Global/EventActionScore/EventActionScoreProcessor.Log.cs new file mode 100644 index 0000000..a4a0e0a --- /dev/null +++ b/GameServer/Global/EventActionScore/EventActionScoreProcessor.Log.cs @@ -0,0 +1,164 @@ +using GameServer.EventInvokers; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace GameServer.Global; + +public partial class EventActionScoreProcessor +{ + private sealed class EventActionScoreLogActor : IWithLogActor + { + private readonly string _userGuid; + private UserActorLog _userActorLog = null!; + + public EventActionScoreLogActor(string userGuid) + { + _userGuid = userGuid; + } + + public async Task Init() + { + var serverLogic = ServerLogicApp.getServerLogicApp(); + + var dynamoDbClient = serverLogic.getDynamoDbClient(); + + var userActorLog = new UserActorLog(); + (var userQueryResult, UserBaseAttrib? userBaseAttrib) = await dynamoDbClient + .simpleQueryDocTypeToAttrib(_userGuid); + NullReferenceCheckHelper.throwIfNull(userBaseAttrib, () => $"UserBaseAttrib is null !!!"); + + (var accountQueryResult, AccountBaseAttrib? accountAttrib) = await dynamoDbClient + .simpleQueryDocTypeToAttrib(_userGuid); + NullReferenceCheckHelper.throwIfNull(accountAttrib, () => $"AccountBaseAttrib is null !!!"); + + userActorLog.initLogInfo( + // 서버 정보 + serverLogic.getServerConfig().getRegionId() + , serverLogic.getServerConfig().getWorldId() + , serverLogic.getServerType().toServerType() + , serverLogic.getServerName() + + // 계정 정보 + , accountAttrib.AccountId + , accountAttrib.AccountId + + // 유저 정보 + , accountAttrib.UserGuid + , string.Empty + , 0 + + // 캐릭터 정보 + , 0 + , CharRace.None + + // 기타 + , accountAttrib.AuthAdminLevelType + , accountAttrib.AccountType + , accountAttrib.AccountCreationType + , Google.Protobuf.ByteString.Empty + ); + + (var result, NicknameAttrib? foundNicknameAttrib) = dynamoDbClient + .simpleQueryDocTypeToAttrib(_userGuid).GetAwaiter().GetResult(); + NullReferenceCheckHelper.throwIfNull(foundNicknameAttrib, () => $"foundNicknameAttrib is null !!!"); + if (result.isSuccess()) + { + userActorLog.UserNickname = foundNicknameAttrib.Nickname; + } + + _userActorLog = userActorLog; + } + + public ILogActor toLogActor() + { + return _userActorLog; + } + } + + private async Task GetLogActor(string userGuid) + { + GameServerApp.getServerLogic().getPlayerManager().tryGetUserByPrimaryKey(userGuid, out Player? player); + if (player is not null) + { + return player; + } + + // 오프라인 유저인 경우 이렇게 처리한다. + var logActor = new EventActionScoreLogActor(userGuid); + await logActor.Init(); + return logActor; + } + + private async Task BusinessLogEventActionScore(IGameEventInvoker eventInvoker, + IEnumerable results) + { + try + { + var userGuid = eventInvoker.getUserGuid(); + var logActor = await GetLogActor(userGuid); + foreach (var eventResult in results) + { + ILogInvoker logInvoker; + var logAction = new LogActionEx(LogActionType.RankingEventActionScore); + switch (eventResult) + { + case IWorldEventActionScoreResult worldEventResult: + logAction = new LogActionEx(LogActionType.WorldEventActionScore); + logInvoker = CreateWorldEventBusinessLog(worldEventResult); + break; + default: + logInvoker = CreateBusinessLog(eventResult); + break; + } + BusinessLogger.collectLogs(logAction, logActor, [logInvoker]); + } + } + catch (Exception ex) + { + Log.getLogger().error($"BusinessLogEventActionScore Failed => {ex}"); + } + } + + private ILogInvoker CreateBusinessLog(IEventActionScoreResult result) + { + var metaData = result.EventActionScoreMeta; + var businessLog = new BusinessLogInvoker(LogDomainType.RankingEventActionScore, + logInvoker => new RankingEventActionScoreLog(logInvoker) + { + RankingScheduleGuid = result.RankingScheduleGuid, + EventActionScoreMetaId = metaData.Index, + RankType = metaData.RankType, + WorldEventType = metaData.WorldEventType, + EventTarget = metaData.EventTarget, + EventAction = metaData.Event, + EventCondition1 = metaData.EventCondition1, + EventCondition2 = metaData.EventCondition2, + EventCondition3 = metaData.EventCondition3, + Score = result.Score, + }); + return businessLog; + } + + private ILogInvoker CreateWorldEventBusinessLog(IWorldEventActionScoreResult result) + { + var metaData = result.EventActionScoreMeta; + var businessLog = new BusinessLogInvoker(LogDomainType.WorldEventActionScore, + logInvoker => new WorldEventActionScoreLog(logInvoker) + { + WorldEventScheduleId = result.WorldEventScheduleId, + EventActionScoreMetaId = metaData.Index, + RankType = metaData.RankType, + WorldEventType = metaData.WorldEventType, + EventTarget = metaData.EventTarget, + EventAction = metaData.Event, + EventCondition1 = metaData.EventCondition1, + EventCondition2 = metaData.EventCondition2, + EventCondition3 = metaData.EventCondition3, + Score = result.Score, + ScoreTotal = result.ScoreTotal, + }); + return businessLog; + } +} diff --git a/GameServer/Global/EventActionScore/EventActionScoreProcessor.cs b/GameServer/Global/EventActionScore/EventActionScoreProcessor.cs new file mode 100644 index 0000000..bb640ad --- /dev/null +++ b/GameServer/Global/EventActionScore/EventActionScoreProcessor.cs @@ -0,0 +1,44 @@ +using GameServer.EventInvokers; +using ServerCore; + +namespace GameServer.Global; + +/// +/// 이벤트 액션 스코어 처리기 +/// +public partial class EventActionScoreProcessor +{ + private readonly List _handlers = new(); + + /// + /// 스코어를 처리할 핸들러 등록 + /// + /// + public void AddHandler(IEventToScoreHandler handler) + { + _handlers.Add(handler); + } + + /// + /// 유저의 이벤트 행동을 IGameEventInvoker 형식으로 수신한 후 등록된 핸들에게 전달 + /// + /// + public async Task ProcessScore(IGameEventInvoker eventInvoker) + { + // 여기에 모든 invoker가 호출된다. + Log.getLogger().debug( + $"EventActionScore Process Call => EventTarget:{eventInvoker.getGameEventType().ToString()}, EventAction:{eventInvoker.getGameEventActionType().ToString()}, UserGuid: {eventInvoker.getUserGuid()}"); + foreach (IEventToScoreHandler eventToScoreHandler in _handlers) + { + var processResults = await eventToScoreHandler.ProcessScore(eventInvoker); + // 이벤트 처리 결과 로그 + foreach (var processResult in processResults) + { + Log.getLogger().info($"EventActionScore Process => {processResult}"); + } + + await this.BusinessLogEventActionScore(eventInvoker, processResults); + } + } +} + diff --git a/GameServer/Global/EventActionScore/EventActionScoreResult.cs b/GameServer/Global/EventActionScore/EventActionScoreResult.cs new file mode 100644 index 0000000..673692c --- /dev/null +++ b/GameServer/Global/EventActionScore/EventActionScoreResult.cs @@ -0,0 +1,76 @@ +using GameServer.EventInvokers; +using MetaAssets; +using System.Text; + +namespace GameServer.Global; + +public interface IEventActionScoreResult +{ + string RankingScheduleGuid { get; init; } + string HandlerName { get; init; } + IGameEventInvoker Invoker { get; init; } + EventActionScoreMetaData EventActionScoreMeta { get; init; } + int Score { get; init; } + Result Result { get; init; } +} + +public interface IWorldEventActionScoreResult : IEventActionScoreResult +{ + int WorldEventScheduleId { get; init; } + long ScoreTotal { get; init; } +} + +public class EventActionScoreResult : IEventActionScoreResult +{ + public required string RankingScheduleGuid { get; init; } + public required string HandlerName { get; init; } + public required IGameEventInvoker Invoker { get; init; } + public required EventActionScoreMetaData EventActionScoreMeta { get; init; } + public int Score { get; init; } + public required Result Result { get; init; } + + public override string ToString() + { + var sb = new StringBuilder(); + sb.Append("HandlerName: ").Append(HandlerName) + .Append(", UserGuid: ").Append(Invoker.getUserGuid()) + .Append( + $", EventType: {Invoker.getGameEventType().ToString()}, EventAction: {Invoker.getGameEventActionType().ToString()}") + .Append($", EventActionScoreMeta GroupId: {EventActionScoreMeta.GroupId} => {EventActionScoreMeta.desc_}") + .Append( + $", EventActionScoreMeta Index: {EventActionScoreMeta.Index}, Condition1: {EventActionScoreMeta.EventCondition1}, Condition2: {EventActionScoreMeta.EventCondition2}, Condition3: {EventActionScoreMeta.EventCondition3}") + .Append(", Score: ").Append(Score) + .Append(", Result: ").Append(Result); + + return sb.ToString(); + } +} + +public class WorldEventActionScoreResult : IWorldEventActionScoreResult +{ + public int WorldEventScheduleId { get; init; } + public required string RankingScheduleGuid { get; init; } + public required string HandlerName { get; init; } + public required IGameEventInvoker Invoker { get; init; } + public required EventActionScoreMetaData EventActionScoreMeta { get; init; } + public int Score { get; init; } + public long ScoreTotal { get; init; } + public required Result Result { get; init; } + + public override string ToString() + { + var sb = new StringBuilder(); + sb.Append("HandlerName: ").Append(HandlerName) + .Append(", UserGuid: ").Append(Invoker.getUserGuid()) + .Append( + $", EventType: {Invoker.getGameEventType().ToString()}, EventAction: {Invoker.getGameEventActionType().ToString()}") + .Append($", EventActionScoreMeta GroupId: {EventActionScoreMeta.GroupId} => {EventActionScoreMeta.desc_}") + .Append( + $", EventActionScoreMeta Index: {EventActionScoreMeta.Index}, Condition1: {EventActionScoreMeta.EventCondition1}, Condition2: {EventActionScoreMeta.EventCondition2}, Condition3: {EventActionScoreMeta.EventCondition3}") + .Append(", Score: ").Append(Score) + .Append(", ScoreTotal: ").Append(ScoreTotal) + .Append(", Result: ").Append(Result); + + return sb.ToString(); + } +} diff --git a/GameServer/Global/EventActionScore/Interfaces/IEventActionScoreHelper.cs b/GameServer/Global/EventActionScore/Interfaces/IEventActionScoreHelper.cs new file mode 100644 index 0000000..8f38e0d --- /dev/null +++ b/GameServer/Global/EventActionScore/Interfaces/IEventActionScoreHelper.cs @@ -0,0 +1,11 @@ +using GameServer.EventInvokers; +using MetaAssets; + +namespace GameServer.Global; + + +public interface IEventActionScoreHelper +{ + (bool, int) FindMatchedAndGetEventActionScore(IGameEventInvoker eventInvoker, EventActionScoreMetaData eventActionScoreMeta); + IEnumerable GetEventActionScoreMetas(int groupId); +} diff --git a/GameServer/Global/EventActionScore/Interfaces/IEventToScoreHandler.cs b/GameServer/Global/EventActionScore/Interfaces/IEventToScoreHandler.cs new file mode 100644 index 0000000..9944069 --- /dev/null +++ b/GameServer/Global/EventActionScore/Interfaces/IEventToScoreHandler.cs @@ -0,0 +1,12 @@ +using GameServer.EventInvokers; + +namespace GameServer.Global; + + +/// +/// GameEventInvoker 객체를 넘겨 받어서 스코처리 +/// +public interface IEventToScoreHandler +{ + Task> ProcessScore(IGameEventInvoker invoker); +} diff --git a/GameServer/Global/EventActionScore/Interfaces/IRankingEventScheduleInfo.cs b/GameServer/Global/EventActionScore/Interfaces/IRankingEventScheduleInfo.cs new file mode 100644 index 0000000..d34feb7 --- /dev/null +++ b/GameServer/Global/EventActionScore/Interfaces/IRankingEventScheduleInfo.cs @@ -0,0 +1,9 @@ +using ServerCommon; + +namespace GameServer.Global; + + +public interface IRankingEventScheduleInfo +{ + IEnumerable GetValidRankingSchedules(); +} diff --git a/GameServer/Global/EventActionScore/Interfaces/IWorldEventScheduleInfo.cs b/GameServer/Global/EventActionScore/Interfaces/IWorldEventScheduleInfo.cs new file mode 100644 index 0000000..c8f21ed --- /dev/null +++ b/GameServer/Global/EventActionScore/Interfaces/IWorldEventScheduleInfo.cs @@ -0,0 +1,9 @@ +using ServerCommon; + +namespace GameServer.Global; + + +public interface IWorldEventScheduleInfo +{ + IEnumerable GetValidWorldEventSchedules(); +} diff --git a/GameServer/Global/EventActionScore/Interfaces/IWorldEventScoreRepo.cs b/GameServer/Global/EventActionScore/Interfaces/IWorldEventScoreRepo.cs new file mode 100644 index 0000000..92ff0c3 --- /dev/null +++ b/GameServer/Global/EventActionScore/Interfaces/IWorldEventScoreRepo.cs @@ -0,0 +1,24 @@ +namespace GameServer.Global; + +/// +/// 월드 이벤트 기여도의 총합을 관리하는 리포지토리의 인터페이스입니다. +/// +public interface IWorldEventScoreRepo +{ + /// + /// 특정 월드 이벤트에 사용자의 기여도를 추가하고, 업데이트된 월드 총점을 반환합니다. + /// 이 작업은 사용자 개인 점수와 월드 총점 업데이트를 하나의 트랜잭션으로 처리합니다. + /// + /// 월드 이벤트 ID + /// 사용자 GUID + /// 추가할 점수 + /// 업데이트된 월드 총점 + Task<(Result, long)> AddGlobalScore(int worldEventId, string userGuid, long score); + + /// + /// 특정 월드 이벤트의 총점을 조회합니다. 캐시를 우선적으로 조회하고, 캐시에 없는 경우 데이터베이스에서 읽어옵니다. + /// + /// 월드 이벤트 ID + /// 월드 이벤트 총점 + Task GetGlobalScore(int worldEventId); +} diff --git a/GameServer/Global/EventActionScore/RankingEventActionScore.cs b/GameServer/Global/EventActionScore/RankingEventActionScore.cs new file mode 100644 index 0000000..39488e4 --- /dev/null +++ b/GameServer/Global/EventActionScore/RankingEventActionScore.cs @@ -0,0 +1,83 @@ +using GameServer.EventInvokers; +using MetaAssets; +using ServerCommon; +using ServerCore; + +namespace GameServer.Global; + + +public class RankingEventActionScore : IEventToScoreHandler +{ + private string _serviceType => GameServerApp.getServerLogic().getServerConfig().ServiceType; + private readonly IEventActionScoreHelper _helper; + private readonly IRankingEventScheduleInfo _rankingEventScheduleInfo; + + public RankingEventActionScore(IEventActionScoreHelper helper, + IRankingEventScheduleInfo rankingEventScheduleInfo) + { + _helper = helper; + _rankingEventScheduleInfo = rankingEventScheduleInfo; + } + + public async Task> ProcessScore(IGameEventInvoker invoker) + { + var actionScoreResults = new List(); + foreach (var rankingScheduleMeta in _rankingEventScheduleInfo.GetValidRankingSchedules()) + { + var rankingScheduleGuid = rankingScheduleMeta.RankingGuid; + var eventActionScoreGroupId = rankingScheduleMeta.EventActionScoreGroupId; + var eventActionScoreMetas = GetEventActionScoreMetas(eventActionScoreGroupId); + foreach (var eventActionScoreMeta in eventActionScoreMetas) + { + var (isValid, score) = _helper.FindMatchedAndGetEventActionScore(invoker, eventActionScoreMeta); + if (isValid) + { + Log.getLogger().info($"NotifyRankScoreUpdate EventTarget: {eventActionScoreMeta.EventTarget}, EventAction:{eventActionScoreMeta.Event}, Score: {score}"); + try + { + var result = await TryUpdateRankScore(rankingScheduleMeta.RankingGuid, invoker.getUserGuid(), + score); + Log.getLogger().info( + $"NotifyRankScoreUpdate Result => {result.ResultString}, EventTarget: {eventActionScoreMeta.EventTarget}, EventAction:{eventActionScoreMeta.Event}"); + actionScoreResults.Add(new EventActionScoreResult + { + RankingScheduleGuid = rankingScheduleGuid, + HandlerName = this.getTypeName(), + Invoker = invoker, + EventActionScoreMeta = eventActionScoreMeta, + Score = score, + Result = result, + }); + } + catch (Exception ex) + { + actionScoreResults.Add(new EventActionScoreResult + { + RankingScheduleGuid = rankingScheduleGuid, + HandlerName = this.getTypeName(), + Invoker = invoker, + EventActionScoreMeta = eventActionScoreMeta, + Score = score, + Result = new Result { ErrorCode = ServerErrorCode.Success, ResultString = ex.Message}, + }); + Log.getLogger().error($"Failed to increase ranking score. EventTarget: {eventActionScoreMeta.EventTarget}, EventAction:{eventActionScoreMeta.Event}, Score: {score}, ex:{ex.Message}"); + } + } + } + } + return actionScoreResults; + } + + private IEnumerable GetEventActionScoreMetas(int groupId) + { + var values = MetaData.Instance.Meta.EventActionScoreMetaTable.EventActionScoreMetaDataList.Where(x => + x.GroupId == groupId); + return values; + } + + private async Task TryUpdateRankScore(string rankingScheduleGuid, string userGuid, long score) + { + return await RankingHelper.tryUpdateRankingScore(rankingScheduleGuid, userGuid, EntityType.Player, + (int)score); + } +} diff --git a/GameServer/Global/EventActionScore/RankingEventEventScheduleInfo.cs b/GameServer/Global/EventActionScore/RankingEventEventScheduleInfo.cs new file mode 100644 index 0000000..ec08f40 --- /dev/null +++ b/GameServer/Global/EventActionScore/RankingEventEventScheduleInfo.cs @@ -0,0 +1,16 @@ +using ServerCommon; + +namespace GameServer.Global; + + +public class RankingEventEventScheduleInfo : IRankingEventScheduleInfo +{ + private GameServerLogic _serverLogic => GameServerApp.getServerLogic(); + + public IEnumerable GetValidRankingSchedules() + { + return _serverLogic.getRankingScheduleManager().getRankingSchedules() + .Select(x => x.getEntityAttribute()).OfType() + .Where(x => x.StartTime <= DateTime.UtcNow && x.EndTime >= DateTime.UtcNow); + } +} diff --git a/GameServer/Global/EventActionScore/WorldEventActionScore.cs b/GameServer/Global/EventActionScore/WorldEventActionScore.cs new file mode 100644 index 0000000..9e809b5 --- /dev/null +++ b/GameServer/Global/EventActionScore/WorldEventActionScore.cs @@ -0,0 +1,82 @@ +using GameServer.EventInvokers; +using MetaAssets; +using ServerCommon; +using ServerCore; + +namespace GameServer.Global; + + +public class WorldEventActionScore : IEventToScoreHandler +{ + private readonly IWorldEventScoreRepo _worldEventScoreRepo; + private readonly IEventActionScoreHelper _eventActionScoreHelper; + private readonly IWorldEventScheduleInfo _worldEventScheduleInfo; + + public WorldEventActionScore(IEventActionScoreHelper eventActionScoreHelper, + IWorldEventScheduleInfo worldEventScheduleInfo, + IWorldEventScoreRepo worldEventScoreRepo) + { + _eventActionScoreHelper = eventActionScoreHelper; + _worldEventScheduleInfo = worldEventScheduleInfo; + _worldEventScoreRepo = worldEventScoreRepo; + } + + public async Task> ProcessScore(IGameEventInvoker invoker) + { + var actionScoreResults = new List(); + foreach (var worldEventScheduleAttrib in _worldEventScheduleInfo.GetValidWorldEventSchedules()) + { + var eventActionScoreGroupId = worldEventScheduleAttrib.GlobalEventActionGroupId; + var eventActionScoreMetas = GetEventActionScoreMetas(eventActionScoreGroupId); + foreach (var eventActionScoreMeta in eventActionScoreMetas) + { + var (isMatched, score) = _eventActionScoreHelper.FindMatchedAndGetEventActionScore(invoker, eventActionScoreMeta); + if (isMatched) + { + var worldEventId = worldEventScheduleAttrib.Id; + var userGuid = invoker.getUserGuid(); + try + { + var (result, scoreTotal) = await _worldEventScoreRepo.AddGlobalScore(worldEventId, userGuid, score); + var eventActionScoreResult = new WorldEventActionScoreResult + { + WorldEventScheduleId = worldEventScheduleAttrib.Id, + RankingScheduleGuid = string.Empty, + HandlerName = this.getTypeName(), + Invoker = invoker, + EventActionScoreMeta = eventActionScoreMeta, + Score = score, + ScoreTotal = scoreTotal, + Result = result + }; + actionScoreResults.Add(eventActionScoreResult); + } + catch (Exception ex) + { + Log.getLogger().error($"Failed to increase world event score. WorldEventId: {worldEventId}, UserGuid: {userGuid}, Score: {score} => ex:{ex.Message}"); + } + } + } + } + return actionScoreResults; + } + + private IEnumerable GetEventActionScoreMetas(int groupId) + { + var values = MetaData.Instance.Meta.EventActionScoreMetaTable.EventActionScoreMetaDataList.Where(x => + x.GroupId == groupId); + if (!values.Any()) + { + var index = groupId; + var data = MetaData.Instance.Meta.EventActionScoreMetaTable.EventActionScoreMetaDataList.FirstOrDefault(x => x.Index == index); + if (data != null) + { + return MetaData.Instance.Meta.EventActionScoreMetaTable.EventActionScoreMetaDataList.Where(x => + x.GroupId == data.GroupId); + } + + return []; + } + return values; + } +} diff --git a/GameServer/Global/EventActionScore/WorldEventScheduleInfo.cs b/GameServer/Global/EventActionScore/WorldEventScheduleInfo.cs new file mode 100644 index 0000000..0b77fca --- /dev/null +++ b/GameServer/Global/EventActionScore/WorldEventScheduleInfo.cs @@ -0,0 +1,13 @@ +using ServerCommon; + +namespace GameServer.Global; + + +public class WorldEventScheduleInfo : IWorldEventScheduleInfo +{ + private GameServerLogic _serverLogic => GameServerApp.getServerLogic(); + public IEnumerable GetValidWorldEventSchedules() + { + return _serverLogic.getWorldEventScheduleManager().GetValidWorldEventSchedules(); + } +} diff --git a/GameServer/Global/EventActionScore/WorldEventScoreRepo.cs b/GameServer/Global/EventActionScore/WorldEventScoreRepo.cs new file mode 100644 index 0000000..0fc54be --- /dev/null +++ b/GameServer/Global/EventActionScore/WorldEventScoreRepo.cs @@ -0,0 +1,295 @@ +using Amazon.DynamoDBv2.Model; +using ServerBase; +using ServerCore; +using ServerCommon; +using StackExchange.Redis; + +namespace GameServer.Global; + + +/// +/// 월드 이벤트 기여도의 총합을 래디스에 업데이트 한다. +/// 개인 월드 이벤트는 저장하지 않는다. +/// +public class WorldEventScoreRepo : IWorldEventScoreRepo +{ + private IDatabase _redisClient => GameServerApp.getServerLogic().getRedisDb(); + private DynamoDbClient _dbClient => GameServerApp.getServerLogic().getDynamoDbClient(); + private string redisKeyBase = "WorldEventScore:"; + private string makeRedisKey(int worldEventId) => $"{redisKeyBase}{worldEventId}"; + + // Lua 스크립트: 현재 값보다 새로운 값이 클 경우에만 SET을 수행합니다. + // KEYS[1]: a redis key + // ARGV[1]: a new score value + private static readonly LuaScript _setIfGreaterScript = LuaScript.Prepare(@" + local current = redis.call('GET', KEYS[1]) + if not current or tonumber(ARGV[1]) > tonumber(current) then + redis.call('SET', KEYS[1], ARGV[1]) + return 1 + end + return 0 + "); + + /// + /// WorldEventScoreDoc과 WorldEventGlobalScoreDoc이 없으면 생성한다. + /// TODO Upsert 방식으로 수정 + /// + /// + /// + /// + private async Task FindOrCreateScoreDocs(int worldEventId, string userGuid) + { + var result = new Result(); + { + var doc = new WorldEventScoreDoc(worldEventId, userGuid); + var attrib = doc.getAttrib(); + if (attrib is not null) + { + attrib.Score = 0; + attrib.WorldEventId = worldEventId; + attrib.UserGuid = userGuid; + attrib.UpdateTime = DateTime.UtcNow; + } + + var queryConfig = _dbClient.makeQueryConfigForReadByPKSK(doc.getPK(), doc.getSK()); + var (queryResult, newAttrib) = + await _dbClient.simpleQueryDocTypeWithQueryOperationConfig(queryConfig); + if (queryResult.isFail() || newAttrib is null) + { + var insertResult = await _dbClient.simpleInsertDocumentWithDocType(doc); + if (insertResult.isFail()) + { + return insertResult; + } + } + } + { + var doc = new WorldEventGlobalScoreDoc(worldEventId); + var attrib = doc.getAttrib(); + if (attrib is not null) + { + attrib.Score = 0; + attrib.WorldEventId = worldEventId; + attrib.UpdateTime = DateTime.UtcNow; + } + var queryConfig = _dbClient.makeQueryConfigForReadByPKSK(doc.getPK(), doc.getSK()); + var (queryResult, newAttrib) = + await _dbClient.simpleQueryDocTypeWithQueryOperationConfig(queryConfig); + if (queryResult.isFail() || newAttrib is null) + { + var insertResult = await _dbClient.simpleInsertDocumentWithDocType(doc); + if (insertResult.isFail()) + { + return insertResult; + } + } + } + return result; + } + + /// + /// 기여도를 증가한다. + /// + /// + /// + /// + /// + public async Task<(Result, long)> AddGlobalScore(int worldEventId, string userGuid, long score) + { + int invalidScore = -1; + var result = new Result(); + if (score == 0) + { + return (result, invalidScore); + } + + var amazonDynamoDbClient = _dbClient.getDbClient(); + NullReferenceCheckHelper.throwIfNull(amazonDynamoDbClient, () => "dynamoDbClient is null"); + + Log.getLogger().info( + $"Attempting transactional update for World Event. WorldEventId: {worldEventId}, User: {userGuid}, Score: {score}"); + + result = await FindOrCreateScoreDocs(worldEventId, userGuid); + if (result.isFail()) + { + Log.getLogger().error("WorldEventScore FindOrCreateScoreDocs failed."); + return (result, invalidScore); + } + + var (userUpdate, _) = CreateUserScoreUpdate(worldEventId, userGuid, (int)score); + var (totalUpdate, _) = CreateTotalScoreUpdate(worldEventId, (int)score); + + // 3. 두 업데이트를 하나의 트랜잭션으로 묶어 실행 + var transactItems = new List + { + new() { Update = userUpdate }, + new() { Update = totalUpdate } + }; + + try + { + await amazonDynamoDbClient.TransactWriteItemsAsync( + new TransactWriteItemsRequest { TransactItems = transactItems }); + } + catch (TransactionCanceledException e) + { + var reasons = string.Join(", ", e.CancellationReasons.Select(r => $"Code: {r.Code}, Message: {r.Message}")); + var errorMsg = + $"DynamoDB transaction was canceled. WorldEventId: {worldEventId}, User: {userGuid} => Reasons: {reasons}. Exception: {e.Message}"; + Log.getLogger().error(errorMsg); + result.setFail(ServerErrorCode.DynamoDbTransactException, errorMsg); + return (result, invalidScore); + } + catch (Exception e) + { + var errorMsg = + $"An exception occurred during TransactWriteItemsAsync. WorldEventId: {worldEventId}, User: {userGuid}. Exception: {e}"; + Log.getLogger().error(errorMsg); + result.setFail(ServerErrorCode.DynamoDbTransactException, errorMsg); + return (result, invalidScore); + } + + // 4. 트랜잭션 성공 후, 업데이트된 총 점수를 DB에서 직접 읽어옴 (TransactWriteItems는 값을 반환하지 않음) + var (getResult, newGlobalScore) = await GetTotalScoreDirtyFromDb(worldEventId); + if (getResult.isFail()) + { + Log.getLogger().error( + $"Failed to get total score after successful transaction. Result: {getResult.toBasicString()}"); + return (result, invalidScore); // 트랜잭션은 성공했지만 값을 읽어오지 못한 경우 + } + + // // 5. 트랜잭션 성공 후, Redis 캐시를 업데이트합니다. + // // 경쟁 상태로 인한 데이터 손상을 방지하기 위해, 현재 Redis 값보다 새로운 값이 클 경우에만 덮어쓰는 Lua 스크립트를 사용합니다. + // var redisKey = makeRedisKey(worldEventId); + // await _redisClient.ScriptEvaluateAsync(_setIfGreaterScript, + // new { key = (RedisKey)redisKey, value = newTotalScore }); + + return (result, newGlobalScore); + } + + public async Task GetGlobalScore(int worldEventId) + { + // // 1. Redis 캐시에서 먼저 조회합니다. + // var redisKey = makeRedisKey(worldEventId); + // var totalScoreFromCache = await _redisClient.StringGetAsync(redisKey, CommandFlags.PreferReplica); + // if (totalScoreFromCache.HasValue) + // { + // return (long)totalScoreFromCache; + // } + + // 2. 캐시에 없으면 DB에서 직접 읽어옵니다 (Cache-Miss). + var (result, totalScoreFromDb) = await GetTotalScoreDirtyFromDb(worldEventId); + if (result.isFail()) + { + Log.getLogger().error( + $"Failed to GetTotalScoreDirtyFromDb. WorldEventId: {worldEventId}, Result: {result.toBasicString()}"); + return 0; // DB 조회 실패 시 0 반환 + } + + // // 3. DB에서 읽은 값을 캐시에 저장하고 반환합니다 (Cache-Aside 패턴). + // await _redisClient.StringSetAsync(redisKey, totalScoreFromDb); + return totalScoreFromDb; + } + + private (Update, UpdateItemRequest) CreateTotalScoreUpdate(int worldEventId, int scoreToAdd) + { + var table = _dbClient.getTableByDoc(); + // var updateExpression = + // $"SET #{nameof(WorldEventTotalScoreAttrib)}.#score = #{nameof(WorldEventTotalScoreAttrib)}.#score + :scoreToAdd"; //, #{timeAttribName} = :updateTime"; + var worldEventAttribTypeName = nameof(WorldEventGlobalScoreAttrib); + var updateExpression = + $"SET #{worldEventAttribTypeName}.#score = #{worldEventAttribTypeName}.#score + :scoreToAdd"; //, #{timeAttribName} = :updateTime"; + + var keys = new WorldEventGlobalScoreDoc(worldEventId).getPrimaryKey().toKeyWithAttributeValue(); + var expNames = new Dictionary() + { + { $"#{worldEventAttribTypeName}", worldEventAttribTypeName }, + { "#score", "score" }, + // { "#updateTime", nameof(WorldEventTotalScoreAttrib.UpdateTime) } + }; + var expValues = new Dictionary + { + { ":scoreToAdd", new AttributeValue { N = scoreToAdd.ToString() } }, + }; + + var update = new Update + { + TableName = table.TableName, + Key = keys, + UpdateExpression = updateExpression, + ExpressionAttributeNames = expNames, + ExpressionAttributeValues = expValues + }; + + var updateRequest = new UpdateItemRequest + { + TableName = table.TableName, + Key = keys, + UpdateExpression = updateExpression, + ExpressionAttributeNames = expNames, + ExpressionAttributeValues = expValues, + ConditionExpression = "attribute_exists(WorldEventTotalScoreAttrib.score)", + }; + return (update, updateRequest); + } + + private (Update, UpdateItemRequest) CreateUserScoreUpdate(int worldEventId, string userGuid, int scoreToAdd) + { + var table = _dbClient.getTableByDoc(); + var attribTypeName = nameof(WorldEventScoreAttrib); + var updateExpression = + $"SET #{attribTypeName}.#score = #{attribTypeName}.#score + :scoreToAdd"; //, #{timeAttribName} = :updateTime"; + + var keys = new WorldEventScoreDoc(worldEventId, userGuid).getPrimaryKey().toKeyWithAttributeValue(); + var expNames = new Dictionary() + { + { $"#{attribTypeName}", attribTypeName }, + { "#score", "score" }, + // { "#updateTime", nameof(WorldEventTotalScoreAttrib.UpdateTime) } + }; + var expValues = new Dictionary + { + { ":scoreToAdd", new AttributeValue { N = scoreToAdd.ToString() } }, + }; + + var update = new Update + { + TableName = table.TableName, + Key = keys, + UpdateExpression = updateExpression, + ExpressionAttributeNames = expNames, + ExpressionAttributeValues = expValues + }; + + var updateRequest = new UpdateItemRequest + { + TableName = table.TableName, + Key = keys, + UpdateExpression = updateExpression, + ExpressionAttributeNames = expNames, + ExpressionAttributeValues = expValues, + ConditionExpression = $"attribute_exists({attribTypeName}.score)", + }; + return (update, updateRequest); + } + + private async Task<(Result, long)> GetTotalScoreDirtyFromDb(int worldEventId) + { + // Doc 인스턴스를 통해 키를 생성하는 표준 방식으로 변경하여 일관성과 유지보수성 증가 + var doc = new WorldEventGlobalScoreDoc(worldEventId); + var queryConfig = _dbClient.makeQueryConfigForReadByPKSK(doc.getPK(), doc.getSK()); + var (queryResult, readDoc) = + await _dbClient.simpleQueryDocTypeWithQueryOperationConfig(queryConfig); + if (queryResult.isFail() || readDoc is null) + { + return (queryResult, 0); + } + + var readAttrib = readDoc.getAttrib(); + if (readAttrib is null) + { + Log.getLogger().error("[GetTotalScoreDirtyFromDb] readAttrib is null"); + } + return (queryResult, readAttrib?.Score ?? 0); + } +} diff --git a/GameServer/Global/Ranking/RankingManager.cs b/GameServer/Global/Ranking/RankingManager.cs new file mode 100644 index 0000000..3b72135 --- /dev/null +++ b/GameServer/Global/Ranking/RankingManager.cs @@ -0,0 +1,186 @@ +using MongoDB.Driver.Core.Misc; +using ServerBase; +using ServerCommon; +using ServerCore; +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 RankingManager + { + ConcurrentDictionary m_rankings = new(); + + public async Task loadRankings() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var ranking_schedule_manager = server_logic.getRankingScheduleManager(); + + var ranking_schedules = ranking_schedule_manager.getRankingSchedules(); + foreach (var ranking_schedule in ranking_schedules) + { + var ranking_schedule_attribute = ranking_schedule.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attribute, () => $"ranking_schedule_attribute is null !!!"); + + if (ranking_schedule_attribute.RankingScheduleState != RankingScheduleStateType.Started) + continue; + + var ranking_guid = ranking_schedule_attribute.RankingGuid; + + var ranking = new Ranking(); + await ranking.onInit(); + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + result = await ranking_action.tryLoadRankingFromDb(ranking_guid); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadRankingFromDb() !!! : rankingGuid:{ranking_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var rank_agent_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(rank_agent_action, () => $"rank_agent_action is null !!!"); + + result = await rank_agent_action.tryLoadAllRanksFromDb(); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadRankFromDb() !!! : rankingGuid:{ranking_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + addRanking(ranking_guid, ranking); + } + + return result; + } + + public async Task loadRanker(Player player) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranker_guid = player.getUserGuid(); + + foreach (var ranking in m_rankings.Values) + { + var ranker_agent_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranker_agent_action, () => $"ranker_agent_action is null !!!"); + + result = await ranker_agent_action.tryLoadRankerFromDb(ranker_guid); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadRankerFromDb() !!! : {ranking.toBasicString()}, rankerGuid:{ranker_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + + return result; + } + + public bool tryGetRanking(string rankingGuid, [MaybeNullWhen(false)] out Ranking ranking) + { + return m_rankings.TryGetValue(rankingGuid, out ranking); + } + + public void addRanking(string rankingGuid, Ranking ranking) + { + m_rankings[rankingGuid] = ranking; + } + + public void removeRanker(Player player) + { + var ranker_guid = player.getUserGuid(); + + foreach (var ranking in m_rankings.Values) + { + var ranker_agent_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranker_agent_action, () => $"ranker_agent_action is null !!!"); + + ranker_agent_action.removeRanker(ranker_guid); + } + } + + public async Task tryUpdateRankings(string requestorId) + { + var result = new Result(); + var err_msg = string.Empty; + + var update_time = DateTime.UtcNow; + + foreach (var ranking in m_rankings.Values) + { + var ranking_guid = ranking.getRankingGuid(); + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(ranking_guid, requestorId, 10); + if (is_success) + { + result = await ranking_action.tryUpdateRanking(update_time); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(ranking_guid, requestorId); + + if (result.isFail()) + { + err_msg = $"Failed to tryUpdateRanking() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + } + } + + return result; + } + + public async Task tryUpdateRankingsByIntervalType(IntervalType intervalType) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + + foreach (var ranking in m_rankings.Values) + { + var ranking_guid = ranking.getRankingGuid(); + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(ranking_guid, server_name, 10); + if (is_success) + { + result = await ranking_action.tryUpdateRankingByIntervalType(intervalType); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(ranking_guid, server_name); + + if (result.isFail()) + { + err_msg = $"Failed to tryUpdateRankingByIntervalType() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + } + } + + return result; + } + } +} diff --git a/GameServer/Global/RankingSchedule/RankingScheduleManager.cs b/GameServer/Global/RankingSchedule/RankingScheduleManager.cs new file mode 100644 index 0000000..6c0ca6d --- /dev/null +++ b/GameServer/Global/RankingSchedule/RankingScheduleManager.cs @@ -0,0 +1,188 @@ +using MetaAssets; +using Org.BouncyCastle.Tls; +using ServerBase; +using ServerCommon; +using ServerCore; +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 RankingScheduleManager + { + ConcurrentDictionary m_ranking_schedules = new(); + + public async Task loadRankingSchedules() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var dynamo_db_client = server_logic.getDynamoDbClient(); + + var doc = new RankingScheduleDoc(); + doc.setCombinationKeyForPK(DynamoDbClient.PK_GLOBAL); + + var error_code = doc.onApplyPKSK(); + if (error_code.isFail()) + { + err_msg = $"Failed to onApplyPKSK() !!! : {error_code.toBasicString()}"; + result.setFail(error_code, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var query_config = dynamo_db_client.makeQueryConfigForReadByPKOnly(doc.getPK()); + + (result, var read_docs) = await dynamo_db_client.simpleQueryDocTypesWithQueryOperationConfig(query_config); + if (result.isFail()) + { + err_msg = $"Failed to simpleQueryDocTypesWithQueryOperationConfig() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var ranking_schedules = new ConcurrentDictionary(); + + foreach (var read_doc in read_docs) + { + var ranking_schedule_attrib = read_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attrib, () => $"ranking_schedule_attrib is null !!!"); + + if (ranking_schedule_attrib.EndTime < DateTime.UtcNow) + continue; + + var ranking_schedule = new RankingSchedule(); + await ranking_schedule.onInit(); + + var ranking_schedule_action = ranking_schedule.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_action, () => $"ranking_schedule_action is null !!!"); + + result = ranking_schedule_action.tryLoadRankingScheduleFromDoc(read_doc); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadRankingScheduleFromDoc() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + if (!ranking_schedules.TryAdd(ranking_schedule_attrib.RankingGuid, ranking_schedule)) + { + err_msg = $"Failed to TryAdd() !!! : {ranking_schedule.toBasicString()} : {this.getTypeName()}"; + result.setFail(ServerErrorCode.RankingScheduleDocLoadDuplicatedRankingSchedule, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + } + + m_ranking_schedules = ranking_schedules; + + return result; + } + + public async Task checkRankingSchedule() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + var ranking_manager = server_logic.getRankingManager(); + + List start_rankings = new(); + + foreach (var ranking_schedule in m_ranking_schedules.Values) + { + var ranking_schedule_attribute = ranking_schedule.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attribute, () => $"ranking_schedule_attribute is null !!!"); + + var old_ranking_schedule_state = ranking_schedule_attribute.RankingScheduleState; + var new_ranking_schedule_state = RankingScheduleHelper.checkRankingScheduleState(ranking_schedule_attribute); + var ranking_guid = ranking_schedule_attribute.RankingGuid; + + if (old_ranking_schedule_state == new_ranking_schedule_state) + continue; + + if (new_ranking_schedule_state == RankingScheduleStateType.Started) + { + (result, var ranking) = await RankingHelper.tryMakeRanking(ranking_schedule_attribute); + if (result.isFail() || ranking == null) + { + err_msg = $"Failed to tryMakeRanking() !!! : rankingGuid:{ranking_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + + ranking_manager.addRanking(ranking_guid, ranking); + start_rankings.Add(ranking); + } + + ranking_schedule_attribute.RankingScheduleState = new_ranking_schedule_state; + } + + foreach (var ranking in start_rankings) + { + var ranking_guid = ranking.getRankingGuid(); + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(ranking_guid, server_name, 10); + if (is_success) + { + // Insert RankingDoc + // RankingDoc 다른 서버 동기화 불필요 + result = await ranking_action.tryInsertRankingDocToDb(); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(ranking_guid, server_name); + + if (result.isFail()) + { + err_msg = $"Failed to tryInsertRankingDocToDb() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + } + } + } + + return result; + } + + public List getRankingSchedules() + { + return m_ranking_schedules.Values.ToList(); + } + + public bool tryGetRankingSchedule(string rankingGuid, [MaybeNullWhen(false)] out RankingSchedule rankingSchedule) + { + return m_ranking_schedules.TryGetValue(rankingGuid, out rankingSchedule); + } + + public List tryGetRankingScheduleInfos() + { + var ranking_schedule_infos = new List(); + + foreach (var ranking_schedule in m_ranking_schedules.Values) + { + var ranking_schedule_attribute = ranking_schedule.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attribute, () => $"ranking_schedule_attribute is null !!!"); + + if (ranking_schedule_attribute.EndTime < DateTime.UtcNow) + continue; + + var ranking_schedule_info = ranking_schedule.toRankingScheduleInfo(); + ranking_schedule_infos.Add(ranking_schedule_info); + } + + return ranking_schedule_infos; + } + } +} diff --git a/GameServer/Global/Rental/RentalManager.cs b/GameServer/Global/Rental/RentalManager.cs deleted file mode 100644 index 6ad49fe..0000000 --- a/GameServer/Global/Rental/RentalManager.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace GameServer -{ - internal class RentalManager - { - } -} diff --git a/GameServer/Global/WorldEvent/WorldEventManager.cs b/GameServer/Global/WorldEvent/WorldEventManager.cs new file mode 100644 index 0000000..e9ca37b --- /dev/null +++ b/GameServer/Global/WorldEvent/WorldEventManager.cs @@ -0,0 +1,107 @@ +using GameServer.Contents.WorldEvent; +using GameServer.Global; +using Google.Protobuf.WellKnownTypes; +using ServerCommon; +using ServerCore; + +namespace GameServer; + +public class WorldEventManager : Singleton +{ + private GameServerLogic _serverLogic => GameServerApp.getServerLogic(); + private WorldEventScheduleManager WorldEventScheduleManager => _serverLogic.getWorldEventScheduleManager(); + private readonly IWorldEventScoreRepo _worldEventScoreRepo = new WorldEventScoreRepo(); + + public WorldEventManager() + { + WorldEventScheduleManager.OnUpdateWorldEventSchedule += (validWorldEventSchedules) => + { + BroadcastWorldEventScheduleToAllPlayersAsync(); + }; + } + + public async Task SendContributionPointAsync(Player player, int requestWorldEventId) + { + var validWorldEventId = requestWorldEventId; + var worldEventScheduleAttrib = WorldEventScheduleManager.GetWorldEventSchedule(requestWorldEventId); + if (worldEventScheduleAttrib is null) + { + worldEventScheduleAttrib = WorldEventScheduleManager.GetValidWorldEventSchedules().FirstOrDefault(); // default world event schedule + if (worldEventScheduleAttrib is null) + { + Log.getLogger().error($"[WorldEvent] Failed to find world event schedule. => requestWorldEventId:{requestWorldEventId}"); + SendContributionPoint(player, requestWorldEventId, 0, 100); + return new Result(); + } + validWorldEventId = worldEventScheduleAttrib.Id; + } + var point = await _worldEventScoreRepo.GetGlobalScore(validWorldEventId); + var pointMax = WorldEventScheduleManager.GetWorldEventSchedule(validWorldEventId) + ?.GlobalEventContributionPointMax ?? 0; + var ack = new ClientToGame { Response = new ClientToGameRes() }; + return SendContributionPoint(player, validWorldEventId, (int)point, pointMax); + } + + private Result SendContributionPoint(Player player, int requestWorldEventId, int point, int pointMax) + { + var ack = new ClientToGame { Response = new ClientToGameRes() }; + var ackMsg = new ClientToGameRes.Types.GS2C_ACK_WORLD_EVENT_CONTRIBUTION + { + WorldEventId = requestWorldEventId, ContributionPoint = (int)point, ContributionPointMax = pointMax, + }; + ack.Response.AckWorldEventContribution = ackMsg; + player.sendPacket(ack); + return new Result(); + } + + public Result SendWorldEventSchedule(Player player) + { + return SendWorldEventSchedule(player, WorldEventScheduleManager.GetValidWorldEventSchedules()); + } + + private Result SendWorldEventSchedule(Player player, IEnumerable worldEventSchedules) + { + var worldEventScheduleNtfDataList = new List(); + foreach (var attrib in worldEventSchedules) + { + var ntfSchedule = new WordEventSchedule + { + WorldEventId = attrib.Id, + StartTime = attrib.StartTime.ToTimestamp(), + EndTime = attrib.EndTime.ToTimestamp(), + GlobalContributionPointMax = attrib.GlobalEventContributionPointMax, + GlobalEventActionScoreGroupId = attrib.GlobalEventActionGroupId, + PersonalEventActionScoreGroupId = attrib.PersonalEventActionGroupId + }; + worldEventScheduleNtfDataList.Add(ntfSchedule); + } + + // WorldEvent notify 메시지 생성 + var ntf = new ClientToGame { Message = new ClientToGameMessage() }; + var msg = new ClientToGameMessage.Types.GS2C_NTF_WORLD_EVENT_SCHEDULE(); + msg.Schedules.Add(worldEventScheduleNtfDataList); + ntf.Message.NtfWorldEventSchedule = msg; + + _serverLogic.onSendPacket(player, ntf); + return new Result(); + } + + private void BroadcastWorldEventScheduleToAllPlayersAsync() + { + _ = Task.Run(() => + { + var players = _serverLogic.getPlayerManager().getUsers().Values; + foreach (var player in players) + { + try + { + SendWorldEventSchedule(player); + } + catch (Exception ex) + { + Log.getLogger().error($"[WorldEvent] Failed to send world event schedule to player. => ex:{ex.Message}"); + } + } + }); + } +} diff --git a/GameServer/Global/WorldEvent/WorldEventScheduleManager.cs b/GameServer/Global/WorldEvent/WorldEventScheduleManager.cs new file mode 100644 index 0000000..485193f --- /dev/null +++ b/GameServer/Global/WorldEvent/WorldEventScheduleManager.cs @@ -0,0 +1,163 @@ +using System.Collections.Immutable; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace GameServer.Contents.WorldEvent; + +public class WorldEventScheduleManager +{ + // ImmutableDictionary로 레퍼런스만 교체한다. ImmutableDictionary의 레퍼런스 쿄체는 volatile과 같은 효과. + private ImmutableDictionary? _worldEventScheduleAttribs; + public event Action>? OnUpdateWorldEventSchedule; + + public WorldEventScheduleAttrib? GetWorldEventSchedule(int worldEventId) + { + GetWorldEventScheduleAttribs().TryGetValue(worldEventId, out var attrib); + return attrib; + } + + public async Task LoadSchedulesAsync(DynamoDbClient dynamoDbClient) + { + // DynamoDB 쿼리 설정 생성 + var doc = new WorldEventScheduleDoc(); + doc.onApplyPKSK(); + var queryConfig = dynamoDbClient.makeQueryConfigForReadByPKOnly(doc.getPK()); + + // 실제 데이터베이스 쿼리 실행 + try + { + var (result, readDocList) = + await dynamoDbClient.simpleQueryDocTypesWithQueryOperationConfig(queryConfig); + + // doc이 없어도 에러가 발생? + if (result.isFail()) + { + var errMsg = $"Failed to simpleQueryDocTypesWithQueryOperationConfig() !!! : {result.toBasicString()}"; + // 에러 로그 + Log.getLogger().error( + $"{errMsg} - {result.toBasicString()}"); + return new Result(); + } + + var attribs = readDocList.Select(x => x.getAttrib()) + .Where(attrib => attrib != null) // null이 아닌 유효한 attrib만 필터링 + .ToImmutableList(); + + if (attribs.Count == 0) + { + return result; + } + bool isUpdated = UpdateWorldEventSchedules(attribs); + if (isUpdated) + { + OnUpdateWorldEventSchedule?.Invoke(GetValidWorldEventSchedules()); + } + } + catch (Exception ex) + { + Log.getLogger().error($"[WorldEvent] Failed to simpleQueryDocTypesWithQueryOperationConfig() !!! => {ex}"); + } + + // if (!GetValidWorldEventSchedules().Any()) + // { + // SetFakeWorldEventSchedule(); + // } + return new Result(); + } + + public IEnumerable GetValidWorldEventSchedules() => GetWorldEventScheduleAttribs().Values.Where(x => + x.IsActive && x.StartTime <= DateTime.UtcNow && + x.EndTime >= DateTime.UtcNow); + + /// + /// _worldEventScheduleAttribs와 dbWorldEventScheduleAttribs을 비교해서 변경된 항목이 있을 때만 리스트를 수정 + /// + /// + private bool UpdateWorldEventSchedules(ImmutableList dbWorldEventScheduleAttribs) + { + var dbScheduleMap = dbWorldEventScheduleAttribs + .Where(a => a != null) + .Select(a => a!) + .ToDictionary(a => a.Id); + + var builder = ImmutableDictionary.CreateBuilder(); + int updatedCount = 0; + int removedCount = 0; + + // 기존 목록을 순회하며 업데이트되거나 삭제된 항목을 처리합니다. + foreach (var localAttrib in GetWorldEventScheduleAttribs().Values) + { + if (dbScheduleMap.TryGetValue(localAttrib.Id, out var dbAttrib)) + { + // DB에 존재하는 항목: 변경되었는지 확인합니다. + // WorldEventScheduleAttrib에 구현된 IEquatable.Equals를 사용합니다. + if (!localAttrib.Equals(dbAttrib)) + { + // 변경된 경우: 새 속성으로 리스트에 추가합니다. + builder.Add(dbAttrib.Id, dbAttrib); + updatedCount++; + } + else + { + // 변경되지 않은 경우: 기존 속성을 그대로 리스트에 추가합니다. + builder.Add(localAttrib.Id, localAttrib); + } + + // 처리된 항목은 맵에서 제거하여, 남은 항목이 '추가'된 항목임을 알 수 있게 합니다. + dbScheduleMap.Remove(localAttrib.Id); + } + else + { + // DB에 더 이상 존재하지 않는 항목: 삭제된 것으로 처리합니다. + removedCount++; + } + } + + // 맵에 남아있는 모든 항목은 새로 추가된 항목입니다. + int addedCount = dbScheduleMap.Count; + if (addedCount > 0) + { + builder.AddRange(dbScheduleMap); + } + + if (updatedCount > 0 || removedCount > 0 || addedCount > 0) + { + _worldEventScheduleAttribs = builder.ToImmutable(); + // 모든 클라이언트에게 Notify 전 + Log.getLogger().Info( + $"[WorldEvent] Schedule update complete. Added: {addedCount}, Updated: {updatedCount}, Removed: {removedCount}"); + + return true; + } + + return false; + } + + private void SetFakeWorldEventSchedule() + { + var builder = ImmutableDictionary.CreateBuilder(); + // 테스트를 위해 일단 1개의 데이터를 등록한다. (라이브 서버 제외) + var serviceType = GameServerApp.getServerLogic().getServerType(); + if (!string.Equals(serviceType, nameof(ServiceType.Live), StringComparison.CurrentCultureIgnoreCase)) + { + var defaultWorldEventSchedule = new WorldEventScheduleAttrib { StartTime = DateTime.UtcNow.AddDays(-1) }; + defaultWorldEventSchedule.EndTime = defaultWorldEventSchedule.StartTime.AddMonths(1); + defaultWorldEventSchedule.GlobalEventActionGroupId = 21100; + defaultWorldEventSchedule.PersonalEventActionGroupId = 21200; + defaultWorldEventSchedule.IsActive = true; + builder.Add(defaultWorldEventSchedule.Id, defaultWorldEventSchedule); + } + _worldEventScheduleAttribs = builder.ToImmutable(); + } + + private ImmutableDictionary GetWorldEventScheduleAttribs() + { + if (_worldEventScheduleAttribs is null) + { + _worldEventScheduleAttribs = ImmutableDictionary.Empty; + } + + return _worldEventScheduleAttribs; + } +} diff --git a/GameServer/Map/Helper/MapHelper.cs b/GameServer/Map/Helper/MapHelper.cs index 61bebaa..306edc0 100644 --- a/GameServer/Map/Helper/MapHelper.cs +++ b/GameServer/Map/Helper/MapHelper.cs @@ -604,6 +604,23 @@ public static class MapHelper return room_map_tree; } + public static bool tryGetFloorByMyhomeGuid(this BuildingMapTree buildingMapTree, string myhomeGuid, [MaybeNullWhen(false)] out int findFloor) + { + findFloor = 0; + + foreach (var (floor, room_map_tree) in buildingMapTree.ChildRoomMapTrees) + { + if (room_map_tree.MyhomeGuid == myhomeGuid) + { + findFloor = floor; + return true; + } + + } + + return false; + } + //========================================================================================= // X축,Y축,Z축에 대한 회전을 순차적으로 적용하는 함수 - kangms //========================================================================================= diff --git a/GameServer/Map/Helper/MapNotifyHelper.cs b/GameServer/Map/Helper/MapNotifyHelper.cs index 07a98bb..8733710 100644 --- a/GameServer/Map/Helper/MapNotifyHelper.cs +++ b/GameServer/Map/Helper/MapNotifyHelper.cs @@ -1,6 +1,6 @@ -using Org.BouncyCastle.Asn1.Ocsp; -using ServerCommon; -using ServerCore; using ServerBase; +using ServerCommon; +using ServerCore; +using ServerBase; using System; using System.Collections.Generic; using System.Linq; @@ -8,6 +8,7 @@ using System.Text; using System.Threading.Tasks; using static ClientToGameMessage.Types; using static ServerMessage.Types; +using Google.Protobuf.WellKnownTypes; namespace GameServer { @@ -153,5 +154,38 @@ namespace GameServer return true; } + + public static void broadcast_PropModify(this Map map, string anchorGuid) + { + if (false == map.getAnchors().TryGetValue(anchorGuid, out var anchorInfo)) + return; + + var propInfo = new PropInfo(); + propInfo.AnchorGuid = anchorInfo.AnchorGuid; + propInfo.OccupiedActorGuid = anchorInfo.OccupyingUserGuid; + propInfo.TableId = anchorInfo.AnchorProp.TableId; + propInfo.ItemGuid = anchorInfo.AnchorProp.GuidByType; + + if (true == map.getFarmingEffects().TryGetValue(anchorGuid, out var found_farming_effect)) + { + var farming_effect_attribute = found_farming_effect.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - AnchorGuid:{anchorInfo.AnchorGuid}"); + + propInfo.StartTime = farming_effect_attribute.FarmingStartTime.ToTimestamp(); + propInfo.EndTime = farming_effect_attribute.FarmingEndTime.ToTimestamp(); + propInfo.RespawnTime = anchorInfo.respawnTime; + } + + if (anchorInfo.AnchorProp.Mannequins != null) + propInfo.Mannequins.AddRange(anchorInfo.AnchorProp.Mannequins); + + var clientToGame = new ClientToGame(); + clientToGame.Message = new ClientToGameMessage(); + clientToGame.Message.PropModify = new PropModify(); + + clientToGame.Message.PropModify.PropList.Add(propInfo); + + map.Broadcast(anchorInfo.AnchorPos, clientToGame); + } } } diff --git a/GameServer/Map/Map.cs b/GameServer/Map/Map.cs index aa6b1fb..52bcddc 100644 --- a/GameServer/Map/Map.cs +++ b/GameServer/Map/Map.cs @@ -191,8 +191,8 @@ public class Map Dictionary<(int, int), Grid> Grids = new Dictionary<(int, int), Grid>(); PropGroupManager m_prop_group_manager = new(); - public ConcurrentDictionary m_anchors = new(); - public List WorldAccountStartPos = new(); + ConcurrentDictionary m_anchors = new(); + List m_world_account_start_poses = new(); //ENTITY_INSTANCE_GUID는 UGC_NPC_META_GUID와 동일하다 !!! private ConcurrentDictionary m_ugc_npc_entities = new(); @@ -266,7 +266,7 @@ public class Map getAnchors().TryAdd(anchorInfo.AnchorGuid, anchorInfo); if (MapHelper.isStartPoint(anchorInfo.AnchorGuid, StartPointType.WorldAccountStartPoint)) - WorldAccountStartPos.Add(anchorInfo.AnchorPos); + m_world_account_start_poses.Add(anchorInfo.AnchorPos); if (MapHelper.isGroupProp(anchorInfo.AnchorGuid)) m_prop_group_manager.addAnchor(anchorInfo); @@ -431,7 +431,7 @@ public class Map } CellPos cellPos = GetCellPos(current_pos.X, current_pos.Y); - List sightCells = GetSightCells(cellPos, District.ALL_DISTRICT); + List sightCells = GetSightCellPoses(cellPos, District.ALL_DISTRICT); List inSightActors = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); List inSightPlayers = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); @@ -604,7 +604,7 @@ public class Map // 카운트 감소 m_current_player_count.decCount(); - List sightCells = GetSightCells(cellPos, District.ALL_DISTRICT); + List sightCells = GetSightCellPoses(cellPos, District.ALL_DISTRICT); List removeSightActors = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); List removeSightPlayers = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); @@ -967,7 +967,7 @@ public class Map // 내 주변 player들에게 내 좌표 변경을 알림 List inSightPlayers = new List(); - List sightCells = GetSightCells(newCellPos, District.ALL_DISTRICT); + List sightCells = GetSightCellPoses(newCellPos, District.ALL_DISTRICT); foreach (CellPos cellPos in sightCells) { GetCell(cellPos)?.CallFunc((Player actor) => @@ -995,10 +995,71 @@ public class Map return result; } + public Result tryOccupyAnchor(string anchorGuid, string userGuid) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + + if (false == getAnchors().TryGetValue(anchorGuid, out var anchor_info)) + { + err_msg = $"Not Found AnchorInfo !!! - mapFileType:{MapFileType.ToString()}, mapId:{MapMId}, anchorGuid:{anchorGuid}"; + result.setFail(ServerErrorCode.NotFoundAnchorGuid, err_msg); + Log.getLogger().error(err_msg); + + return result; + } + + if (!server_logic.getPlayerManager().tryGetUserByPrimaryKey(anchor_info.OccupyingUserGuid, out _)) + { + anchor_info.OccupyingUserGuid = string.Empty; + } + + if (anchor_info.OccupyingUserGuid != string.Empty) + { + err_msg = $"Prop is Occupied !!!"; + result.setFail(ServerErrorCode.PropIsOccupied, err_msg); + Log.getLogger().error(err_msg); + + return result; + } + + anchor_info.OccupyingUserGuid = userGuid; + + return result; + } + + public Result tryEndOccupyAnchor(string anchorGuid, string userGuid) + { + var result = new Result(); + var err_msg = string.Empty; + + if (false == getAnchors().TryGetValue(anchorGuid, out var anchor_info)) + { + err_msg = $"Not Found AnchorInfo !!! - mapFileType:{MapFileType.ToString()}, mapId:{MapMId}, anchorGuid:{anchorGuid}"; + result.setFail(ServerErrorCode.NotFoundAnchorGuid, err_msg); + Log.getLogger().error(err_msg); + + return result; + } + + if (anchor_info.OccupyingUserGuid != userGuid) + { + err_msg = $"Prop is Not Occupied !!!"; + result.setFail(ServerErrorCode.PropIsNotOccupied, err_msg); + Log.getLogger().error(err_msg); + + return result; + } + + anchor_info.OccupyingUserGuid = string.Empty; + + return result; + } + public async Task tryLocateUgcNpc(UgcNpc toLocateUgcNpc) { - await Task.CompletedTask; - var result = new Result(); var err_msg = string.Empty; @@ -1052,7 +1113,7 @@ public class Map var current_pos = ugc_npc_action.getCurrentPos(); var cell_pos = GetCellPos(current_pos.X, current_pos.Y); - var sight_cells = GetSightCells(cell_pos, District.ALL_DISTRICT); + var sight_cells = GetSightCellPoses(cell_pos, District.ALL_DISTRICT); // Npc를 볼 수 있는 plyaer 목록을 구성 한다. var insight_players = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); @@ -1122,7 +1183,7 @@ public class Map } } - var sight_cells = GetSightCells(cell_pos, District.ALL_DISTRICT); + var sight_cells = GetSightCellPoses(cell_pos, District.ALL_DISTRICT); var insight_players = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); foreach (var sight_cell_pos in sight_cells) @@ -1313,7 +1374,7 @@ public class Map } - List GetSightCells(CellPos cellPos, District district) + List GetSightCellPoses(CellPos cellPos, District district) { var cells = new List<(int, int)>(9); @@ -1420,8 +1481,8 @@ public class Map } } - List remoeSight = GetSightCells(oldCellPos, (District)removeDistrict); - List newSight = GetSightCells(newCellPos, (District)newDistrict); + List remoeSight = GetSightCellPoses(oldCellPos, (District)removeDistrict); + List newSight = GetSightCellPoses(newCellPos, (District)newDistrict); return (remoeSight, newSight); } @@ -1429,66 +1490,50 @@ public class Map public void Broadcast(Player player, ClientToGame message, bool dontSendblockUser = false) { var location_action = player.getEntityAction(); - - var current_pos = location_action.getCurrentPos(); - CellPos cellPos = GetCellPos(current_pos.X, current_pos.Y); - List sightCells = GetSightCells(cellPos, District.ALL_DISTRICT); - List hostIDs = new List(); - foreach (CellPos cell in sightCells) - { - GetCell(cell)?.CallFunc((Player actor) => - { - var user_block_action = actor.getEntityAction(); - NullReferenceCheckHelper.throwIfNull(user_block_action, () => $"user_block_action is null !!!"); + var cell_pos = GetCellPos(current_pos.X, current_pos.Y); + var sight_cell_poses = GetSightCellPoses(cell_pos, District.ALL_DISTRICT); - if (dontSendblockUser == true && user_block_action.isBlockUser(player.getUserGuid())) return; - hostIDs.Add(actor.getHostId()); - + var host_ids = new List(); + foreach (var sight_cell_pos in sight_cell_poses) + { + GetCell(sight_cell_pos)?.CallFunc((Player player) => + { + var user_block_action = player.getEntityAction(); + + if (dontSendblockUser && user_block_action.isBlockUser(player.getUserGuid())) + return; + + host_ids.Add(player.getHostId()); }); } - HostID[] allClients = hostIDs.ToArray(); + HostID[] allClients = host_ids.ToArray(); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), message); } - public void Broadcast( Pos pos, ClientToGame message, HostID excludeId = 0) + public void Broadcast(Pos pos, ClientToGame message, HostID excludeHostId = 0) { - CellPos cellPos = GetCellPos(pos.X, pos.Y); - List sightCells = GetSightCells(cellPos, District.ALL_DISTRICT); - List hostIDs = new List(); - foreach (CellPos cell in sightCells) + var cell_pos = GetCellPos(pos.X, pos.Y); + var sight_cell_poses = GetSightCellPoses(cell_pos, District.ALL_DISTRICT); + + var host_ids = new List(); + foreach (var sight_cell_pos in sight_cell_poses) { - GetCell(cell)?.CallFunc((Player actor) => + GetCell(sight_cell_pos)?.CallFunc((Player player) => { - if( excludeId != actor.getHostId() ) - { - hostIDs.Add(actor.getHostId()); - } + if (player.getHostId() == excludeHostId) + return; + + host_ids.Add(player.getHostId()); }); } - HostID[] allClients = hostIDs.ToArray(); + HostID[] allClients = host_ids.ToArray(); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), message); } - public void CallSightCells(Player player, Action action) - { - var location_action = player.getEntityAction(); - - - var current_pos = location_action.getCurrentPos(); - - CellPos cellPos = GetCellPos(current_pos.X, current_pos.Y); - List sightCells = GetSightCells(cellPos, District.ALL_DISTRICT); - - foreach (CellPos cell in sightCells) - { - GetCell(cell)?.CallFunc(action); - } - } - Grid? GetGrid(int gridX, int gridY) { if (gridX < MapRangeGridX.min || gridX > MapRangeGridX.max @@ -1569,39 +1614,6 @@ public class Map return true; } - public void PropModifyNoti(string anchorGuid) - { - if (false == getAnchors().TryGetValue(anchorGuid, out var anchorInfo)) - return; - - ClientToGame clientToGame = new(); - clientToGame.Message = new(); - clientToGame.Message.PropModify = new(); - - var propInfo = new PropInfo(); - propInfo.AnchorGuid = anchorInfo.AnchorGuid; - propInfo.OccupiedActorGuid = anchorInfo.OccupyingUserGuid; - propInfo.TableId = anchorInfo.AnchorProp.TableId; - propInfo.ItemGuid = anchorInfo.AnchorProp.GuidByType; - - if (true == getFarmingEffects().TryGetValue(anchorGuid, out var found_farming_effect)) - { - var farming_effect_attribute = found_farming_effect.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - AnchorGuid:{anchorInfo.AnchorGuid}"); - - propInfo.StartTime = farming_effect_attribute.FarmingStartTime.ToTimestamp(); - propInfo.EndTime = farming_effect_attribute.FarmingEndTime.ToTimestamp(); - propInfo.RespawnTime = anchorInfo.respawnTime; - } - - if (anchorInfo.AnchorProp.Mannequins != null) - propInfo.Mannequins.AddRange(anchorInfo.AnchorProp.Mannequins); - - clientToGame.Message.PropModify.PropList.Add(propInfo); - - Broadcast(anchorInfo.AnchorPos, clientToGame); - } - public void DestroyP2PGroup() { foreach (var grid in Grids.Values) @@ -1699,12 +1711,12 @@ public class Map { accountStartPos = default; - if (WorldAccountStartPos.Count < 1) + if (m_world_account_start_poses.Count < 1) return false; - int index = AccountStartPosIndex % WorldAccountStartPos.Count; - Interlocked.Add(ref AccountStartPosIndex, AccountStartPosIndex < WorldAccountStartPos.Count ? 1 : 1 - WorldAccountStartPos.Count); - accountStartPos = WorldAccountStartPos[index].Clone(); + int index = AccountStartPosIndex % m_world_account_start_poses.Count; + Interlocked.Add(ref AccountStartPosIndex, AccountStartPosIndex < m_world_account_start_poses.Count ? 1 : 1 - m_world_account_start_poses.Count); + accountStartPos = m_world_account_start_poses[index].Clone(); return true; } diff --git a/GameServer/Map/MapManager.cs b/GameServer/Map/MapManager.cs index 227e6d6..b87b7e4 100644 --- a/GameServer/Map/MapManager.cs +++ b/GameServer/Map/MapManager.cs @@ -154,7 +154,16 @@ namespace GameServer anchor_tree.AnchorProp.Mannequins = anchor.MannequinItems.ToList(); world_map_tree.Anchors.TryAdd(anchor.GUID, anchor_tree); - AnchorTrees.TryAdd(anchor.GUID, anchor_tree); + if (false == AnchorTrees.TryAdd(anchor.GUID, anchor_tree)) + { + AnchorTrees.TryGetValue(anchor.GUID, out var origin_anchor_tree); + string firtst_file_name = string.Empty; + if (origin_anchor_tree is not null) + { + firtst_file_name = origin_anchor_tree.ParentMapFileName; + } + Log.getLogger().error($"world_map_data Exist AnchorGuid:{anchor.GUID} - fileName:{world_map_file_name}, first file name : {firtst_file_name}"); + } } // Land @@ -200,7 +209,17 @@ namespace GameServer anchor_tree.AnchorProp.Mannequins = anchor.MannequinItems.ToList(); land_map_tree.Anchors.TryAdd(anchor.GUID, anchor_tree); - AnchorTrees.TryAdd(anchor.GUID, anchor_tree); + if (false == AnchorTrees.TryAdd(anchor.GUID, anchor_tree)) + { + AnchorTrees.TryGetValue(anchor.GUID, out var origin_anchor_tree); + string firtst_file_name = string.Empty; + if (origin_anchor_tree is not null) + { + firtst_file_name = origin_anchor_tree.ParentMapFileName; + } + + Log.getLogger().error($"land_map_data Exist AnchorGuid:{anchor.GUID} - fileName:{land_map_file_name}, first file name : {firtst_file_name}"); + } } // Building @@ -222,7 +241,7 @@ namespace GameServer if (!BuildingMapTrees.TryAdd(building_map_file_name, building_map_tree)) { BuildingMapTrees.TryGetValue(building_map_file_name, out var duplicate_map_tree); - Log.getLogger().error($"Exist BuildingMapFile:{building_map_file_name} - filename:{land_map_file_name}, fileName:{duplicate_map_tree?.ParentLandMapTree.LandMapFileName}"); + Log.getLogger().error($"Exist BuildingMapFile:{building_map_file_name} - filename:{building_map_file_name}, fileName:{duplicate_map_tree?.ParentLandMapTree.LandMapFileName}"); continue; } @@ -252,11 +271,22 @@ namespace GameServer anchor_tree.AnchorProp.Mannequins = anchor.MannequinItems.ToList(); building_map_tree.Anchors.TryAdd(anchor.GUID, anchor_tree); - AnchorTrees.TryAdd(anchor.GUID, anchor_tree); + if (false == AnchorTrees.TryAdd(anchor.GUID, anchor_tree)) + { + AnchorTrees.TryGetValue(anchor.GUID, out var origin_anchor_tree); + string firtst_file_name = string.Empty; + if (origin_anchor_tree is not null) + { + firtst_file_name = origin_anchor_tree.ParentMapFileName; + } + + Log.getLogger().error($"building_map_data Exist AnchorGuid:{anchor.GUID} - fileName:{building_map_file_name}, first file name : {firtst_file_name}"); + } } foreach (var room_data in building_map_data.RoomDatas) { + var room_map_file_name = Path.GetFileName(room_data.RoomName); if (!MapDataTable.Instance.getMapData(room_map_file_name, out var room_map_data)) { @@ -319,7 +349,17 @@ namespace GameServer anchor_tree.AnchorProp.Mannequins = anchor.MannequinItems.ToList(); room_map_tree.Anchors.TryAdd(anchor.GUID, anchor_tree); - AnchorTrees.TryAdd(anchor.GUID, anchor_tree); + if (false == AnchorTrees.TryAdd(anchor.GUID, anchor_tree)) + { + AnchorTrees.TryGetValue(anchor.GUID, out var origin_anchor_tree); + string firtst_file_name = string.Empty; + if (origin_anchor_tree is not null) + { + firtst_file_name = origin_anchor_tree.ParentMapFileName; + } + + Log.getLogger().error($"room_map_data Exist AnchorGuid:{anchor.GUID} - fileName:{room_map_file_name}, first file name : {firtst_file_name}"); + } } } } diff --git a/GameServer/MessageQueue/PacketHandler/ExchangeMannequinDisplayItemMQPacketHandler.cs b/GameServer/MessageQueue/PacketHandler/ExchangeMannequinDisplayItemMQPacketHandler.cs index af56a9d..0b2276b 100644 --- a/GameServer/MessageQueue/PacketHandler/ExchangeMannequinDisplayItemMQPacketHandler.cs +++ b/GameServer/MessageQueue/PacketHandler/ExchangeMannequinDisplayItemMQPacketHandler.cs @@ -24,7 +24,7 @@ public class ExchangeMannequinDisplayItemMQPacketHandler : PacketRecvHandler NullReferenceCheckHelper.throwIfNull(msg, () => $"msg is null !!!"); MapManager.Instance.ExchangeMannequinDisplayItem(msg.ExchangeMannequinDisplayItemNoti.AnchorGuid, msg.ExchangeMannequinDisplayItemNoti.DisplayItemIds.ToList()); - GameServerApp.getServerLogic().getMap().PropModifyNoti(msg.ExchangeMannequinDisplayItemNoti.AnchorGuid); + GameServerApp.getServerLogic().getMap().broadcast_PropModify(msg.ExchangeMannequinDisplayItemNoti.AnchorGuid); await Task.CompletedTask; diff --git a/GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchCancelHandler.cs b/GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchCancelHandler.cs new file mode 100644 index 0000000..82819cb --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchCancelHandler.cs @@ -0,0 +1,23 @@ +namespace GameServer.PacketHandler; + +using Google.Protobuf; + +using ServerBase; + +using ServerCore; + +[PacketHandler("", + typeof(ServerMessage.Types.MS2GS_ACK_MATCH_CANCEL), + typeof(AckMatchCancelHandler), typeof(RabbitMQ4Game))] +public class AckMatchCancelHandler: PacketRecvHandler +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var msg = recvMessage as ServerMessage; + NullReferenceCheckHelper.throwIfNull(msg, () => $"msg is null !!!"); + + MatchManager.It.GetMqRpc().setAckResult(msg.AckMatchCancel.TraceId, msg); + await Task.CompletedTask; + return new(); + } +} diff --git a/GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchReserveHandler.cs b/GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchReserveHandler.cs new file mode 100644 index 0000000..c58e523 --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchReserveHandler.cs @@ -0,0 +1,21 @@ +using Google.Protobuf; + +using ServerCore; +using ServerBase; + +namespace GameServer.PacketHandler; +[PacketHandler("", + typeof(ServerMessage.Types.MS2GS_ACK_MATCH_RESERVE), + typeof(AckMatchReserveHandler), typeof(RabbitMQ4Game))] +public class AckMatchReserveHandler: PacketRecvHandler +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var msg = recvMessage as ServerMessage; + NullReferenceCheckHelper.throwIfNull(msg, () => $"msg is null !!!"); + + MatchManager.It.GetMqRpc().setAckResult(msg.AckMatchReserve.TraceId, msg); + await Task.CompletedTask; + return new(); + } +} diff --git a/GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchRoomInfoHandler.cs b/GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchRoomInfoHandler.cs new file mode 100644 index 0000000..ff098f6 --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/GameMatch/AckMatchRoomInfoHandler.cs @@ -0,0 +1,22 @@ +// using Google.Protobuf; +// using ServerBase; +// using ServerCore; +// +// namespace GameServer.PacketHandler; +// +// [PacketHandler("", +// typeof(ServerMessage.Types.MS2GS_ACK_MATCH_ROOM_INFO), +// typeof(AckMatchRoomInfoHandler), typeof(RabbitMQ4Game))] +// public class AckMatchRoomInfoHandler: PacketRecvHandler +// { +// public override async Task onProcessPacket(ISession session, IMessage recvMessage) +// { +// var msg = recvMessage as ServerMessage; +// NullReferenceCheckHelper.throwIfNull(msg, () => $"msg is null !!!"); +// +// MatchManager.It.GetMqRpc().setAckResult(msg.AckMatchRoomInfo.TraceId, msg); +// await Task.CompletedTask; +// return new(); +// } +// } +// diff --git a/GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchGameJoinReserveHandler.cs b/GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchGameJoinReserveHandler.cs new file mode 100644 index 0000000..2fd39e1 --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchGameJoinReserveHandler.cs @@ -0,0 +1,43 @@ +using Google.Protobuf; +using ServerBase; +using ServerCore; + +namespace GameServer.PacketHandler; + +[PacketHandler("", + typeof(ServerMessage.Types.MS2GS_NTF_MATCH_GAME_JOIN_RESERVE), + typeof(NtfMatchGameJoinReserveHandler), typeof(RabbitMQ4Game))] +// 난입 예약 처리 +public class NtfMatchGameJoinReserveHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var msg = recvMessage as ServerMessage; + NullReferenceCheckHelper.throwIfNull(msg, () => $"msg is null !!!"); + + var serverMessage = recvMessage as ServerMessage; + NullReferenceCheckHelper.throwIfNull(serverMessage, () => $"serverMessage is null !!!"); + + var message = serverMessage.NtfMatchGameJoinReserve; + MatchManager.It.RoomEventHandler.GameMemberJoinReserve(message.UserGuid, message.RoomId, message.TeamId); + + await Task.CompletedTask; + return new(); + } +} + +// [PacketHandler("", +// typeof(ServerMessage.Types.MS2GS_ACK_MATCH_RESERVE), +// typeof(AckMatchReserveHandler), typeof(RabbitMQ4Game))] +// public class AckMatchReserveHandler: PacketRecvHandler +// { +// public override async Task onProcessPacket(ISession session, IMessage recvMessage) +// { +// var msg = recvMessage as ServerMessage; +// NullReferenceCheckHelper.throwIfNull(msg, () => $"msg is null !!!"); +// +// MatchManager.It.GetMqRpc().setAckResult(msg.AckMatchReserve.TraceId); +// await Task.CompletedTask; +// return new(); +// } +// } diff --git a/GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchResultHandler.cs b/GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchResultHandler.cs new file mode 100644 index 0000000..f9cb853 --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchResultHandler.cs @@ -0,0 +1,105 @@ +using GameServer.Match; +using Google.Protobuf; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace GameServer.PacketHandler; + +[PacketHandler("", + typeof(ServerMessage.Types.MS2GS_NTF_MATCH_RESULT), + typeof(NtfMatchResultHandler), typeof(RabbitMQ4Game))] +public class NtfMatchResultHandler : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var result = new Result(); + + var msg = recvMessage as ServerMessage; + ArgumentNullException.ThrowIfNull(msg, $"msg is null !!!"); + + var ntfMatchResultFromMq = msg.NtfMatchResult; + ArgumentNullException.ThrowIfNull(ntfMatchResultFromMq, $"ntf_game_match_status is null !!!"); + + var server_logic = GameServerApp.getServerLogic(); + var player_manager = server_logic.getPlayerManager(); + ArgumentNullException.ThrowIfNull(player_manager, $"player_manager is null !!!"); + + var matchUserInfoFromMq = ntfMatchResultFromMq.MatchUserInfo; + var roomId = ntfMatchResultFromMq.MatchRoomKey; + var teamId = ntfMatchResultFromMq.MatchUpTeamId; + if (player_manager.tryGetUserByPrimaryKey(matchUserInfoFromMq.UserGuid, out var player) == false) + { + var err_msg = + $"NtfMatchResultHandler Failed to tryGetUserByPrimaryKey() !!!, Not found Player !!! : UserGuid:{matchUserInfoFromMq}"; + result.setFail(ServerErrorCode.UserNotLogin, err_msg); + Log.getLogger().info(result.toBasicString()); + return result; + } + + var matchAction = player.getEntityAction(); + var playerMatchInfo = matchAction.PlayerMatchInfo; + playerMatchInfo.MatchStatusInfo.Status = ntfMatchResultFromMq.MatchStatus; + // playerMatchInfo.MatchStatusInfo.WaitTimeSec = ntfMatchResultFromMq. + + + var fn = async () => + { + var matchPoolInfo = MatchPoolInfo.ConvertByMatchGroupId(matchUserInfoFromMq.MatchGroupId); + + var ntf2PlayerNtfMatchResult = new ClientToGameMessage.Types.GS2C_NTF_MATCH_RESULT + { + MatchStatus = ntfMatchResultFromMq.MatchStatus, GameModeId = matchPoolInfo.GameModeId + }; + + if (ntfMatchResultFromMq.MatchStatus != MatchStatusType.Success) + { + send2Client(ntf2PlayerNtfMatchResult); + MatchManager.It.ProcessMatchResultFailAsync(player, matchPoolInfo, playerMatchInfo); + return result; + } + + (result, var serverConnectionInfo) = + await MatchManager.It.ProcessMatchResultAsync(player, matchPoolInfo, playerMatchInfo, roomId, teamId); + if (result.isFail()) + { + ntf2PlayerNtfMatchResult.MatchStatus = MatchStatusType.Fail; + send2Client(ntf2PlayerNtfMatchResult); + return result; + } + + //todo @heon 게임 모드 입장 퀘스트 + // var floorAttribute = player.getEntityAttribute(); + // if (floorAttribute is not null) + // { + // var landId = (int)floorAttribute.LandMetaId; + // var floorId = (int)floorAttribute.Floor; + // await QuestManager.It.QuestCheckWithoutTransaction(player, + // new QuestInstance(EQuestEventTargetType.INSTANCE, EQuestEventNameType.ENTERED, + // landId, floorId)); + // } + // else + // { + // Log.getLogger().info($"No Check Quest {nameof(EQuestEventTargetType.INSTANCE)}, {nameof(EQuestEventNameType.ENTERED)}"); + // } + + ntf2PlayerNtfMatchResult.InstanceServerConnectInfo = serverConnectionInfo; + + // 플레이어에게 서버 입장 메시지 발송 + send2Client(ntf2PlayerNtfMatchResult); + return result; + }; + + return await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "MatchResult", fn); + + void send2Client(ClientToGameMessage.Types.GS2C_NTF_MATCH_RESULT ntfMatchResult) + { + var ntf2PlayerMessage = new ClientToGame + { + Message = new ClientToGameMessage { NtfMatchResult = ntfMatchResult } + }; + // 플레이어에게 서버 입장 메시지 발송 + server_logic.onSendPacket(player, ntf2PlayerMessage); + } + } +} diff --git a/GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchStatusHandler.cs b/GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchStatusHandler.cs new file mode 100644 index 0000000..287b14e --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/GameMatch/NtfMatchStatusHandler.cs @@ -0,0 +1,79 @@ +using Google.Protobuf; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace GameServer.PacketHandler; + +[PacketHandler("", + typeof(ServerMessage.Types.MS2GS_NTF_MATCH_STATUS), + typeof(NtfMatchStatus), typeof(RabbitMQ4Game))] +public class NtfMatchStatus : PacketRecvHandler +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var result = new Result(); + + try + { + var msg = recvMessage as ServerMessage; + MatchGuard.NotNull(msg); + + var ntfGameMatchStatus = msg.NtfMatchStatus; + MatchGuard.NotNull(ntfGameMatchStatus); + + var serverLogic = GameServerApp.getServerLogic(); + var playerManager = serverLogic.getPlayerManager(); + MatchGuard.NotNull(playerManager); + + var userGuid = ntfGameMatchStatus.UserGuid; + if (playerManager.tryGetUserByPrimaryKey(userGuid, out var player) == false) + { + var errMsg = $"Failed to tryGetUserByPrimaryKey() !!!, Not found Player !!! : UserGuid:{userGuid}"; + result.setFail(ServerErrorCode.UserNotLogin, errMsg); + MatchGuard.ResultFailException(result); + } + + MatchGuard.NotNull(player); + + var fnLogic = async () => + { + await Task.CompletedTask; + + Result fnResult = MatchManager.It.UpdateMatchStatus(player, msg.NtfMatchStatus.MatchStatusInfo); + if (fnResult.isSuccess()) + { + var clientToGame = new ClientToGame + { + Message = new ClientToGameMessage + { + NtfMatchStatus = new ClientToGameMessage.Types.GS2C_NTF_MATCH_STATUS + { + MatchStatusInfo = new MatchStatusInfo + { + Status = msg.NtfMatchStatus.MatchStatusInfo.Status, + MatchStep = msg.NtfMatchStatus.MatchStatusInfo.MatchStep, + WaitTimeSec = msg.NtfMatchStatus.MatchStatusInfo.WaitTimeSec, + } + } + } + }; + GameServerApp.getServerLogic().onSendPacket(player, clientToGame); + } + else + { + Log.getLogger().error($"MatchManager.UpdateMatchStatus => {fnResult.toBasicString()}"); + } + return fnResult; + }; + + await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "NtfMatchStatusHandler", + fnLogic); + return await Task.FromResult(result); + } + catch (ResultException ex) + { + throw new Exception(ex.ResultString); + } + } +} diff --git a/GameServer/MessageQueue/PacketHandler/NtfModifyRankingInfoMQPackerHandler.cs b/GameServer/MessageQueue/PacketHandler/NtfModifyRankingInfoMQPackerHandler.cs new file mode 100644 index 0000000..5a60306 --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/NtfModifyRankingInfoMQPackerHandler.cs @@ -0,0 +1,57 @@ +using Google.Protobuf; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer.PacketHandler +{ + [PacketHandler("", typeof(ServerMessage.Types.GS2GS_NTF_MODIFY_RANKING_INFO), typeof(NtfModifyRankingInfoMQPackerHandler), typeof(RabbitMQ4Game))] + internal class NtfModifyRankingInfoMQPackerHandler : PacketRecvHandler + { + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var ranking_manager = server_logic.getRankingManager(); + + var message = recvMessage as ServerMessage; + ArgumentNullException.ThrowIfNull(message, $"message is null !!!"); + + var ntf_modify_ranking_info = message.NtfModifyRankingInfo; + + Log.getLogger().info($"MQ - NtfModifyRankingInfo"); + + var ranking_guid = ntf_modify_ranking_info.RankingGuid; + + if (!ranking_manager.tryGetRanking(ranking_guid, out var ranking)) + { + err_msg = $"Failed to tryGetRanking() !!! rankingGuid:{ranking_guid}"; + result.setFail(ServerErrorCode.RankingNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + result = await ranking_action.tryLoadRankingFromDb(ranking_guid); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadRankingFromDb() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + } +} diff --git a/GameServer/MessageQueue/PacketHandler/NtfQuestTaskForceCompleteMQPacketHandler.cs b/GameServer/MessageQueue/PacketHandler/NtfQuestTaskForceCompleteMQPacketHandler.cs new file mode 100644 index 0000000..2ff8ec1 --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/NtfQuestTaskForceCompleteMQPacketHandler.cs @@ -0,0 +1,203 @@ +using Google.Protobuf; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer.PacketHandler +{ + [PacketHandler("", typeof(ServerMessage.Types.MOS2GS_NTF_QUEST_TASK_FORCE_COMPLETE), typeof(NtfQuestTaskForceCompleteMQPacketHandler), typeof(RabbitMQ4Game))] + internal class NtfQuestTaskForceCompleteMQPacketHandler : PacketRecvHandler + { + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var message = recvMessage as ServerMessage; + ArgumentNullException.ThrowIfNull(message, $"message is null !!!"); + + var ntf_quest_task_force_complete = message.NtfQuestTaskForceComplete; + var account_id = ntf_quest_task_force_complete.AccountId; + var quest_data = ntf_quest_task_force_complete.QuestKey.Split("#"); + var quest_id = int.Parse(quest_data[0]); + var quest_revision = int.Parse(quest_data[1]); + var task_id = ntf_quest_task_force_complete.TaskId; + + Log.getLogger().info($"MQ - NtfQuestTaskForceComplete !!! - reqId:{ntf_quest_task_force_complete.ReqId}, questId:{quest_id}, questRevision:{quest_revision}, taskId:{task_id}"); + + var fake_player = new FakePlayer(account_id); + await fake_player.onFakeInit(); + + var account_attribute = fake_player.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(account_attribute, () => $"account_attribute is null !!!"); + + var user_guid = account_attribute.UserGuid; + + var server_logic = GameServerApp.getServerLogic(); + + var login_cache_request = new LoginCacheOtherUserRequest(server_logic, server_logic.getRedisConnector(), user_guid); + await login_cache_request.fetchLogin(); + var login_cache = login_cache_request.getLoginCache(); + if (login_cache != null) + { + err_msg = $"Already Login User !!! : userGuid:{user_guid}"; + result.setFail(ServerErrorCode.UserDuplicatedLogin, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + result = await fake_player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "QuestTaskForceComplete", questTaskForceCompleteFunction); + if (result.isFail()) + { + err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + } + + return result; + + async Task questTaskForceCompleteFunction() => + await questTaskForceComplete(fake_player, quest_id, quest_revision, task_id); + } + + private async Task questTaskForceComplete(FakePlayer fakePlayer, int questId, int questRevision, int taskId) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + + var updateAction = fakePlayer.getEntityAction(); + + var questAction = fakePlayer.getEntityAction(); + (result, var quest) = await questAction.getQuest((uint)questId, (uint)questRevision); + if (result.isFail() || null == quest) + { + err_msg = $"Failed to getQuest() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var questAttribute = quest.getEntityAttribute(); + if (null == questAttribute || questAttribute.ActiveEvents.Count <= 0) + { + err_msg = $"QuestAttribute is null !!! or ActiveEvents.Count == 0 !!!"; + result.setFail(ServerErrorCode.QuestIdNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + }; + + var current_task_num = questAttribute.CurrentTaskNum; + if (taskId != current_task_num) + { + err_msg = $"Not Matched taskId !!! : reqTaskId:{taskId}, dbTaskId:{current_task_num}"; + result.setFail(ServerErrorCode.QuestIdNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var business_logs = new List(); + + do + { + if (current_task_num != questAttribute.CurrentTaskNum) break; + if (questAttribute.ActiveEvents.Count <= 0) break; + + var questHandler = new QuestTaskUpdateHandler(fakePlayer, (uint)questId, (uint)questRevision, questAttribute.ActiveEvents.FirstOrDefault() ?? string.Empty); + result = await questHandler.init(); + if (result.isFail()) + { + err_msg = $"Failed to QuestTaskUpdateHandler.init() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + break; + } + + result = replaceQuestData(questAttribute, questHandler); + if (result.isFail()) + { + err_msg = $"Failed to replace quest data !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + break; + } + + var quest_task_update_action = fakePlayer.getEntityAction(); + result = quest_task_update_action.taskUpdateConditionCheck(ref questHandler, false); + if (result.isFail()) + { + err_msg = $"Failed to taskUpdateConditionCheck() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + break; + } + + result = await updateAction.questTaskUpdate(questHandler); + if (result.isFail()) + { + err_msg = $"Failed to taskUpdateConditionCheck() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + break; + } + + business_logs.Add(new QuestTaskUpdateBusinessLog(questHandler)); + + } while (true); + + if (result.isFail()) return result; + + var batch = new QueryBatchEx(fakePlayer, LogActionType.AdminToolQuestTaskForceComplete + , GameServerApp.getServerLogic().getDynamoDbClient(), true); + { + batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); + batch.addQuery(new QueryFinal()); + } + + batch.appendBusinessLogs(business_logs); + + result = await QueryHelper.sendQueryAndBusinessLog(batch); + if (result.isFail()) + { + err_msg = $"Failed to sendQueryAndBusinessLog() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + } + + return result; + } + + private Result replaceQuestData(QuestAttribute quest, QuestTaskUpdateHandler questHandler) + { + var result = new Result(); + + // meta data 획득 + if (null == questHandler.m_quest_meta_info || false == questHandler.m_quest_meta_info.QuestTaskGroupList.TryGetValue(quest.CurrentTaskNum, out var questTaskGroup)) + { + result.setFail(ServerErrorCode.QuestIdNotFound); + return result; + } + + // data check + var activeEvent = quest.ActiveEvents.FirstOrDefault(); + if (string.IsNullOrEmpty(activeEvent) || questTaskGroup.EventStringList.Contains(activeEvent)) + return result; + + // handler 수정 + questHandler.m_active_event = questTaskGroup.EventStringList.FirstOrDefault() ?? string.Empty; + + // attribute 수정 + quest.ActiveEvents.Clear(); + quest.ActiveEvents.AddRange(questTaskGroup.EventStringList); + + return result; + } + } +} diff --git a/GameServer/MessageQueue/PacketHandler/NtfRefreshRankMQPacketHandler.cs b/GameServer/MessageQueue/PacketHandler/NtfRefreshRankMQPacketHandler.cs new file mode 100644 index 0000000..c7345a1 --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/NtfRefreshRankMQPacketHandler.cs @@ -0,0 +1,56 @@ +using Google.Protobuf; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer.PacketHandler +{ + [PacketHandler("", typeof(ServerMessage.Types.GS2GS_NTF_REFRESH_RANK), typeof(NtfRefreshRankMQPacketHandler), typeof(RabbitMQ4Game))] + internal class NtfRefreshRankMQPacketHandler : PacketRecvHandler + { + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var ranking_manager = server_logic.getRankingManager(); + + var message = recvMessage as ServerMessage; + ArgumentNullException.ThrowIfNull(message, $"message is null !!!"); + + var ntf_refresh_rank = message.NtfRefreshRank; + var ranking_guid = ntf_refresh_rank.RankingGuid; + + Log.getLogger().info($"MQ - NtfRefreshRank !!! rankingGuid:{ranking_guid}"); + + if (!ranking_manager.tryGetRanking(ranking_guid, out var ranking)) + { + err_msg = $"Failed to tryGetRanking() !!! rankingGuid:{ranking_guid}"; + result.setFail(ServerErrorCode.RankingNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var rank_agent_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(rank_agent_action, () => $"rank_agent_action is null !!!"); + + result = await rank_agent_action.tryLoadAllRanksFromDb(); + if (result.isFail()) + { + err_msg = $"Failed to tryLoadAllRanksFromDb() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + } +} diff --git a/GameServer/MessageQueue/PacketHandler/NtfUpdateBannerMQPacketHandler.cs b/GameServer/MessageQueue/PacketHandler/NtfUpdateBannerMQPacketHandler.cs new file mode 100644 index 0000000..99cb9b0 --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/NtfUpdateBannerMQPacketHandler.cs @@ -0,0 +1,39 @@ +using Google.Protobuf; +using ServerBase; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer.PacketHandler +{ + [PacketHandler("", typeof(ServerMessage.Types.MOS2GS_NTF_UPDATE_BANNER), typeof(NtfUpdateBannerMQPacketHandler), typeof(RabbitMQ4Game))] + internal class NtfUpdateBannerMQPacketHandler : PacketRecvHandler + { + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + + var message = recvMessage as ServerMessage; + ArgumentNullException.ThrowIfNull(message, $"message is null !!!"); + + Log.getLogger().info($"MQ - NtfUpdateBanner"); + + result = await server_logic.getBannerManager().loadBanners(); + if (result.isFail()) + { + err_msg = $"Failed to getBannerManager() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } + } +} diff --git a/GameServer/MessageQueue/PacketHandler/NtfUpdateRankingScheduleMQPackerHandler.cs b/GameServer/MessageQueue/PacketHandler/NtfUpdateRankingScheduleMQPackerHandler.cs new file mode 100644 index 0000000..a57312b --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/NtfUpdateRankingScheduleMQPackerHandler.cs @@ -0,0 +1,94 @@ +using Google.Protobuf; +using Org.BouncyCastle.Tls; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer.PacketHandler +{ + [PacketHandler("", typeof(ServerMessage.Types.MOS2GS_NTF_UPDATE_RANKING_SCHEDULE), typeof(NtfUpdateRankingScheduleMQPackerHandler), typeof(RabbitMQ4Game))] + internal class NtfUpdateRankingScheduleMQPackerHandler : PacketRecvHandler + { + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + var ranking_schedule_manager = server_logic.getRankingScheduleManager(); + var ranking_manager = server_logic.getRankingManager(); + + var message = recvMessage as ServerMessage; + ArgumentNullException.ThrowIfNull(message, $"message is null !!!"); + + Log.getLogger().info($"MQ - NtfUpdateRankingSchedule"); + + result = await ranking_schedule_manager.loadRankingSchedules(); + if (result.isFail()) + { + err_msg = $"Failed to getRankingScheduleManager() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + List start_rankings = new(); + + var ranking_schedules = ranking_schedule_manager.getRankingSchedules(); + foreach (var ranking_schedule in ranking_schedules) + { + var ranking_schedule_attribute = ranking_schedule.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attribute, () => $"ranking_schedule_attribute is null !!!"); + + var ranking_guid = ranking_schedule_attribute.RankingGuid; + if (ranking_manager.tryGetRanking(ranking_guid, out var ranking)) + continue; + + (result, ranking) = await RankingHelper.tryMakeRanking(ranking_schedule_attribute); + if (result.isFail() || ranking == null) + { + err_msg = $"Failed to tryMakeRanking() !!! : rankingGuid:{ranking_guid} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + continue; + } + + ranking_manager.addRanking(ranking_guid, ranking); + start_rankings.Add(ranking); + } + + foreach (var ranking in start_rankings) + { + var ranking_guid = ranking.getRankingGuid(); + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(ranking_guid, server_name, 10); + if (is_success) + { + // Insert RankingDoc + // RankingDoc 다른 서버 동기화 불필요 + result = await ranking_action.tryInsertRankingDocToDb(); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(ranking_guid, server_name); + + if (result.isFail()) + { + err_msg = $"Failed to tryInsertRankingDocToDb() !!! : {ranking.toBasicString()} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + } + } + } + + RankingScheduleNotifyHelper.broadcast_S2C_NTF_RANKING_SCHEDULE_INFOS(); + + return result; + } + } +} diff --git a/GameServer/MessageQueue/PacketHandler/ReqUpdateRankingIntervalMQPacketHandler.cs b/GameServer/MessageQueue/PacketHandler/ReqUpdateRankingIntervalMQPacketHandler.cs new file mode 100644 index 0000000..7f294a9 --- /dev/null +++ b/GameServer/MessageQueue/PacketHandler/ReqUpdateRankingIntervalMQPacketHandler.cs @@ -0,0 +1,66 @@ +using Google.Protobuf; +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer.PacketHandler +{ + [PacketHandler("", typeof(ServerMessage.Types.MOS2GS_REQ_UPDATE_RANKING_INTERVAL), typeof(ReqUpdateRankingIntervalMQPacketHandler), typeof(RabbitMQ4Game))] + internal class ReqUpdateRankingIntervalMQPacketHandler : PacketRecvHandler + { + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var result = new Result(); + var err_msg = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_name = server_logic.getServerName(); + var ranking_manager = server_logic.getRankingManager(); + + var message = recvMessage as ServerMessage; + ArgumentNullException.ThrowIfNull(message, $"message is null !!!"); + + + var req_update_ranking_interval = message.ReqUpdateRankingInterval; + + Log.getLogger().info($"MQ - ReqUpdateRankingInterval"); + + var ranking_guid = req_update_ranking_interval.RankingGuid; + var interval_type = req_update_ranking_interval.IntervalType; + + if (!ranking_manager.tryGetRanking(ranking_guid, out var ranking)) + { + err_msg = $"Failed to tryGetRanking() !!! rankingGuid:{ranking_guid}"; + result.setFail(ServerErrorCode.RankingNotFound, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + var ranking_action = ranking.getEntityAction(); + NullReferenceCheckHelper.throwIfNull(ranking_action, () => $"ranking_action is null !!!"); + + var is_success = await RankingCacheHelper.tryAcquireWriteLockWithRankingUpdate(ranking_guid, server_name, 10); + if (is_success) + { + result = await ranking_action.tryUpdateRankingByIntervalType(interval_type); + await RankingCacheHelper.tryReleaseWriteLockWithRankingUpdate(ranking_guid, server_name); + + if (result.isFail()) + { + err_msg = $"Failed to tryUpdateRankingByIntervalType() !!! : rankingGuid:{ranking_guid}, intervalType:{interval_type} - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + } + + return result; + } + } +} diff --git a/GameServer/Network/GameLoginListener.cs b/GameServer/Network/GameLoginListener.cs index 529d5c3..c9427ce 100644 --- a/GameServer/Network/GameLoginListener.cs +++ b/GameServer/Network/GameLoginListener.cs @@ -92,6 +92,8 @@ public partial class GameLoginListener : ProudNetListener var game_logout_action = disconnected_user.getEntityAction(); await game_logout_action.logoutUser(); + // 매치 유저 디스커넥트 시에 매칭 캔슬 처리 + MatchManager.It.OnDisconnectEvent(disconnected_user); }, "from onDisconnectedEntityWithSession()"); } diff --git a/GameServer/Properties/launchSettings.json b/GameServer/Properties/launchSettings.json index bd92718..2b49a13 100644 --- a/GameServer/Properties/launchSettings.json +++ b/GameServer/Properties/launchSettings.json @@ -6,7 +6,7 @@ }, "GameServer_single": { "commandName": "Project", - "commandLineArgs": "--port 9001 --serverType Channel --channelNo 1 --defaultMaxUser 2000" + "commandLineArgs": "--port 9001 --serverType Channel --worldId 1 --channelNo 1 --defaultMaxUser 2000" }, "GameServer_ko": { "commandName": "Project", @@ -22,19 +22,27 @@ }, "TMLServer": { "commandName": "Project", - "commandLineArgs": "--port 9005 --serverType Channel --channelNo 1 --worldid 3 --defaultMaxUser 5" + "commandLineArgs": "--port 9005 --serverType Channel --channelNo 1 --worldId 3 --defaultMaxUser 5" }, "TMLServer_Dual": { "commandName": "Project", - "commandLineArgs": "--port 9006 --serverType Channel --channelNo 2 --worldid 3 --defaultMaxUser 5" + "commandLineArgs": "--port 9006 --serverType Channel --channelNo 2 --worldId 3 --defaultMaxUser 5" }, "TPSServer": { "commandName": "Project", - "commandLineArgs": "--port 9006 --serverType Channel --channelNo 1 --worldid 4 --defaultMaxUser 50" + "commandLineArgs": "--port 9006 --serverType Channel --channelNo 1 --worldId 4 --defaultMaxUser 50" + }, + "TestWorld": { + "commandName": "Project", + "commandLineArgs": "--port 9007 --serverType Channel --channelNo 1 --worldId 5 --defaultMaxUser 50" + }, + "MatchServer": { + "commandName": "Project", + "commandLineArgs":"" }, "WSL": { "commandName": "WSL2", "distributionName": "" } } -} \ No newline at end of file +} diff --git a/GameServer/Room/InstanceRoom.cs b/GameServer/Room/InstanceRoom.cs index 6fd72ab..7e263a9 100644 --- a/GameServer/Room/InstanceRoom.cs +++ b/GameServer/Room/InstanceRoom.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; - - +using GameServer.Contents.GameMode.Manage.PlayManage; using Google.Protobuf.WellKnownTypes; +using MetaAssets; using Nettention.Proud; @@ -130,6 +130,8 @@ public partial class InstanceRoom } } + + if (_placeType == EPlaceType.Concert) { if (MetaData.Instance._ConcertTable.TryGetValue(instanceMetaData.MapId, out var concertInfo) == false) @@ -184,7 +186,6 @@ public partial class InstanceRoom Log.getLogger().error($"Failed to setInstanceRoomExtraInfo() !!! : {result.toBasicString()} - instanceRoomId:{_roomId}"); return false; } - } if (_placeType == EPlaceType.Movie) @@ -198,18 +199,30 @@ public partial class InstanceRoom _startBuffID = concertInfo.BuffID; } - //GameMode - (var gamemode_init_result, var gamemode_init_handler) = GameModeHelper.getGameModeInitHandler(this, 1, _roomId); - if (gamemode_init_result.isSuccess()) - { - var gamemode_validation_result = gamemode_init_handler.gamedModeInstanceInitValidate(); - if (gamemode_validation_result.isFail()) return false; - var init_result = await gamemode_init_handler.gamedModeInstanceInit(); - if (init_result.isFail()) + if (_placeType == EPlaceType.GameRoom) + { + + var storage = new GameInstanceRoomStorage(); + storage.Init(server_logic.getRedisDb(), ""); + (result, var game_room_info) = await storage.GetInstanceRoomGameInfoAsync(_roomId); + + if (result.isFail()) return false; + NullReferenceCheckHelper.throwIfNull(game_room_info, () => $"player is null !!!"); + + var game_mode_id = game_room_info.GameModeId; + (var gamemode_init_result, var gamemode_init_handler) = GameModeHelper.getGameModeInitHandler(this, game_mode_id, _roomId); + if (gamemode_init_result.isSuccess()) { - Log.getLogger().error($"battleInstanceInit error roomId : {_roomId}"); - return false; + var gamemode_validation_result = gamemode_init_handler.gamedModeInstanceInitValidate(); + if (gamemode_validation_result.isFail()) return false; + + var init_result = await gamemode_init_handler.gamedModeInstanceInit(game_mode_id); + if (init_result.isFail()) + { + Log.getLogger().error($"battleInstanceInit error roomId : {_roomId}"); + return false; + } } } @@ -243,6 +256,7 @@ public partial class InstanceRoom var result = game_zone_action.tryEnterGameZone(_map); if (result.isFail()) { + Log.getLogger().error($"Failed to tryEnterGameZone() !!! : {result.toBasicString()} - instanceRoomId:{_roomId}"); return false; } @@ -264,30 +278,47 @@ public partial class InstanceRoom // CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - - //GameMode - (var get_mode_result, var gamemode_join_handler) = GameModeHelper.getGameModeJoinHandler(this, 1, _roomId); - if (get_mode_result.isFail()) return true; //fail 이면 gamemode 인스턴스가 아니므로 그냥 정상 리턴 처리 - - if (get_mode_result.isSuccess()) + if (_placeType == EPlaceType.BattleRoom || _placeType == EPlaceType.ArcadeRunning || _placeType == EPlaceType.GameRoom) { - get_mode_result = gamemode_join_handler.gamedModeInstanceJoinValidate(); + var storage = new GameInstanceRoomStorage(); + storage.Init(GameServerApp.getServerLogic().getRedisDb(), ""); + (result, var game_room_info) = storage.GetInstanceRoomGameInfoAsync(_roomId).Result; + if (result.isFail()) + { + Log.getLogger().error($"Failed to getGameRoomInfo() !!! : {result.toBasicString()} - instanceRoomId:{_roomId}"); + return false; + } + + + NullReferenceCheckHelper.throwIfNull(game_room_info, () => $"player is null !!!"); + + var game_mode_id = game_room_info.GameModeId; + (var get_mode_result, var gamemode_join_handler) = GameModeHelper.getGameModeJoinHandler(this, game_mode_id, _roomId); + if (get_mode_result.isFail()) + { + Log.getLogger().error($"Failed to getGameModeJoinHandler() !!! : {get_mode_result.toBasicString()} - instanceRoomId:{_roomId}"); + return false; + } + + if (get_mode_result.isSuccess()) { - //successs 면 gamemod join 설정 - var join_result = gamemode_join_handler.gamedModeInstanceJoin(player); - if (join_result.isFail()) + get_mode_result = gamemode_join_handler.gamedModeInstanceJoinValidate(); + if (get_mode_result.isSuccess()) { - Log.getLogger().error($"gamedModeInstanceJoin error!!! _placeType : {_placeType}, roomId : {_roomId}, player : {player.toBasicString()}"); - return false; + //successs 면 gamemod join 설정 + var join_result = gamemode_join_handler.gamedModeInstanceJoin(player); + if (join_result.isFail()) + { + Log.getLogger().error($"gamedModeInstanceJoin error!!! _placeType : {_placeType}, roomId : {_roomId}, player : {player.toBasicString()}"); + return false; + } } } } - - Log.getLogger().info($"{player.getUserGuid()} JoinInstanceRoom {_roomId}"); // LogActionType.StageConcertStart 로그 작성은 인스턴룸이 실제로 시작 상태로 전환될 때 작성한다. 추후 LogActionType.StageConcertEnd 도 정의해야 한다. //List invokers = new List(); @@ -411,10 +442,31 @@ public partial class InstanceRoom } - //GameMode - (var handler_result, var gamemode_join_success_handler) = GameModeHelper.getGameModeJoinSuccessHandler(player, this, 1, _roomId); - if (handler_result.isFail() || gamemode_join_success_handler == null) return; - await gamemode_join_success_handler.joinSuccess(); + if (_placeType == EPlaceType.BattleRoom || _placeType == EPlaceType.ArcadeRunning || _placeType == EPlaceType.GameRoom) + { + var storage = new GameInstanceRoomStorage(); + storage.Init(GameServerApp.getServerLogic().getRedisDb(), ""); + (var result, var game_room_info) = await storage.GetInstanceRoomGameInfoAsync(_roomId); + if (result.isFail()) return; + NullReferenceCheckHelper.throwIfNull(game_room_info, () => $"player is null !!!"); + + var game_mode_id = game_room_info.GameModeId; + + (var handler_result, var gamemode_join_success_handler) = GameModeHelper.getGameModeJoinSuccessHandler(player, this, game_mode_id, _roomId); + if (handler_result.isFail() || gamemode_join_success_handler == null) return; + + if (false == GameModeManager.It.tryGetGameMode(_roomId, out var gameMode)) return; + var gamd_mod_base = gameMode as GameModeBase; + NullReferenceCheckHelper.throwIfNull(gamd_mod_base, () => $"gamd_mod_base is null !!! casting error"); + + using (var releaser = await gamd_mod_base.getAsyncLock()) + { + await gamemode_join_success_handler.joinSuccess(gamd_mod_base); + } + } + + + } async Task SendConcertStartPacket() @@ -716,7 +768,7 @@ public partial class InstanceRoom { try { - await Task.Delay(ServerCommon.Constant.KEEP_INSTANCE_ROOM_TIME, cancelToken); + await Task.Delay(ServerCommon.Constant.INSTANCE_ROOM_CACHE_REFRESH_INTERVAL_MSEC, cancelToken); await instance_room_storage.keepInstanceRoom(_roomId); @@ -803,6 +855,8 @@ public partial class InstanceRoom Log.getLogger().info($"InstanceRoom.destroyRoom() Complete !!! - instanceRoomId:{_roomId}"); } + + //public async Task CheckSession(CancellationToken cancelToken) //{ // List errorPlayer = new(); diff --git a/GameServer/Room/InstanceRoomHandler.cs b/GameServer/Room/InstanceRoomHandler.cs index 7ee29ce..4c9a62d 100644 --- a/GameServer/Room/InstanceRoomHandler.cs +++ b/GameServer/Room/InstanceRoomHandler.cs @@ -589,6 +589,12 @@ namespace GameServer await instance_room_storage.decreaseInstanceRoomScore(instance_room_id_base, instanceRoomId); } + if (GameModeManager.It.isGameModeRoom(instanceRoomId)) + { + //kihoon todo : 게임모드일 경우 roomlist를 따로 지워준다. + await instance_room_storage.decreaseInstanceRoomScore(instanceRoomId, instanceRoomId); + } + return result; } @@ -937,7 +943,7 @@ namespace GameServer var instance_room = await instance_room_storage.GetInstanceRoomInfo(instanceRoomId); if (instance_room == null) { - err_msg = $"Fail to increase instance room score for ugc npc: instance room id - {instanceRoomId}"; + err_msg = $"Fail to decrease instance room score for ugc npc: instance room id - {instanceRoomId}"; result.setFail(ServerErrorCode.InstanceRoomException, err_msg); Log.getLogger().error(result.toBasicString()); diff --git a/GameServer/Room/InstanceRoomManager.cs b/GameServer/Room/InstanceRoomManager.cs index 66ef84f..0b5a01a 100644 --- a/GameServer/Room/InstanceRoomManager.cs +++ b/GameServer/Room/InstanceRoomManager.cs @@ -63,7 +63,7 @@ public class InstanceRoomManager ServerBase.Monitor.It.incRoomCounter(); } } - + if (room.Join(player) == false) { Log.getLogger().error($"Failed to InstanceRoom.Join() !!! - instanceRoomId:{roomId}, {player.toBasicString()}"); @@ -89,7 +89,7 @@ public class InstanceRoomManager } await room.SendJoinSuccess(player); - + return true; } @@ -102,7 +102,13 @@ public class InstanceRoomManager } await room.Leave(player, disconnected); - + + if (GameModeManager.It.isGameModeRoom(roomId)) + { + + await GameModeManager.It.LeaveGameModeRoom(player, roomId); + } + if (room.isDestroy) { DestroyRoom(roomId); @@ -117,7 +123,7 @@ public class InstanceRoomManager { if (m_instance_rooms.ContainsKey(roomId)) { - Log.getLogger().fatal("Failed to Remove InstanceRoom !!! - instanceRoomId:{roomId}"); + Log.getLogger().fatal($"Failed to Remove InstanceRoom !!! - instanceRoomId:{roomId}"); return; } @@ -129,6 +135,25 @@ public class InstanceRoomManager NamedPipeMonitor.SetCurrentCapacity(_capacity); ServerBase.Monitor.It.decRoomCounter(); + if (GameModeManager.It.isGameModeRoom(roomId)) + { + if (false == GameModeManager.It.tryGetGameMode(roomId, out var gameMode)) + { + Log.getLogger().info($"DestroyRoom not exist gameMode - instanceRoomId:{roomId}"); + return; + } + + var game_mode_base = GameModeHelper.toGameModeBase(gameMode); + if (game_mode_base.getInstanceRoom().isDestroy) + { + (var result, var destroy_handler) = GameModeHelper.getGameModeDestroyHandler(game_mode_base); + if (result.isSuccess() && destroy_handler is not null) + { + destroy_handler.gameModeDestroy().Wait(); + } + MatchManager.It.RoomEventHandler.ChangeGameState(roomId, MatchGameStateType.Destroy); + } + } Log.getLogger().info($"InstanceRoomManager.DestroyRoom() Complete !!! - instanceRoomId:{roomId}"); } @@ -250,7 +275,7 @@ public class InstanceRoomManager return room.changeScreenPage(isCustom, isNext, customPage); } - + public int getRoomId(string roomId) { if (m_instance_rooms.TryGetValue(roomId, out var room) == false) @@ -260,10 +285,10 @@ public class InstanceRoomManager return room.getInstanceId(); } - public InstanceRoom? getInstanceRoomByRoomId(string roomId) + public InstanceRoom? getInstanceRoomByRoomId(string roomId) => m_instance_rooms.GetValueOrDefault(roomId); - public ConcurrentDictionary getInstanceRooms() + public ConcurrentDictionary getInstanceRooms() { return m_instance_rooms; } diff --git a/GameServer/Ticker/GameEventCheckTicker.cs b/GameServer/Ticker/GameEventCheckTicker.cs new file mode 100644 index 0000000..8ccb253 --- /dev/null +++ b/GameServer/Ticker/GameEventCheckTicker.cs @@ -0,0 +1,21 @@ +using ServerBase; +using ServerCore; + +namespace GameServer; + +public class GameEventCheckTicker : EntityTicker +{ + public GameEventCheckTicker(double onTickIntervalMilliseconds, CancellationTokenSource? toCancelToken = null!) : + base(EntityType.GameEventCheckTicker, onTickIntervalMilliseconds, toCancelToken) + { + } + + public override async Task onTaskTick() + { + var result = await MatchManager.It.GameEventManager.RefreshEvent(); + if (result.isFail()) + { + Log.getLogger().error($"GameEvent Refresh Fail => {result.ResultString}"); + } + } +} diff --git a/GameServer/Ticker/MatchServerListTicker.cs b/GameServer/Ticker/MatchServerListTicker.cs new file mode 100644 index 0000000..e1d2bb5 --- /dev/null +++ b/GameServer/Ticker/MatchServerListTicker.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +using ServerCore; +using ServerBase; + +namespace GameServer +{ + // 주기적으로 Match 서버 목록을 읽어옵니다. + public class MatchServerListTicker : EntityTicker + { + public MatchServerListTicker(double onTickIntervalMilliseconds, CancellationTokenSource? cts) + : base(EntityType.MatchServerListTicker, onTickIntervalMilliseconds, cts) + { + } + + public override async Task onTaskTick() + { + var server_logic = GameServerApp.getServerLogic(); + try + { + // serverLogic이 IWithServerMetrics를 구현한다고 가정 + var (result, match_servers) = await ((IWithServerMetrics)server_logic) + .getServerInfosByServerType(ServerType.Match); + if (result.isFail()) + { + Log.getLogger().warn($"[MatchServerListTicker] 서버 목록 조회 실패 : {result.toBasicString()}"); + return; + } + + MatchManager.It.UpdateBackend(match_servers.Select(x=>x.Name).ToArray()); + } + catch (Exception e) + { + Log.getLogger().error($"[MatchServerListTicker] Exception: {e.Message}"); + } + } + + public override string toBasicString() + { + return $"{this.getTypeName()}"; + } + + public override string toSummaryString() + { + return $"{this.getTypeName()}"; + } + } +} diff --git a/GameServer/Ticker/RankingScheduleUpdateTicker.cs b/GameServer/Ticker/RankingScheduleUpdateTicker.cs new file mode 100644 index 0000000..975bcc6 --- /dev/null +++ b/GameServer/Ticker/RankingScheduleUpdateTicker.cs @@ -0,0 +1,66 @@ +using ServerBase; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal class RankingScheduleUpdateTicker : EntityTicker + { + public RankingScheduleUpdateTicker(double onTickIntervalMilliseconds, CancellationTokenSource? cts) + : base(EntityType.RankingScheduleUpdateTicker, onTickIntervalMilliseconds, cts) + { + } + + public override async Task onTaskTick() + { + 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(); + + if (true == server_config.PerformanceCheckEnable) + { + event_tid = System.Guid.NewGuid().ToString("N"); + stopwatch = Stopwatch.StartNew(); + } + + var requestor_id = server_logic.getServerName(); + + var ranking_schedule_manager = server_logic.getRankingScheduleManager(); + result = await ranking_schedule_manager.checkRankingSchedule(); + if (result.isFail()) + { + err_msg = $"Fail to tryUpdateRanking() !!! : {result.toBasicString()} - requestorId:{requestor_id}, {toBasicString()}"; + Log.getLogger().error(err_msg); + } + + if (null != stopwatch) + { + var elapsed_msec = stopwatch.ElapsedMilliseconds; + stopwatch.Stop(); + Log.getLogger().debug($"{GetType()} Ticker Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsed_msec}, TickIntervalMSec:{getOnTickIntervalMilliseconds()}"); + } + + await Task.CompletedTask; + } + + public override string toBasicString() + { + return $"{this.getTypeName()}"; + } + + public override string toSummaryString() + { + return $"{this.getTypeName()}"; + } + } +} diff --git a/GameServer/Ticker/RankingUpdateTicker.cs b/GameServer/Ticker/RankingUpdateTicker.cs new file mode 100644 index 0000000..c907a02 --- /dev/null +++ b/GameServer/Ticker/RankingUpdateTicker.cs @@ -0,0 +1,67 @@ +using ServerBase; +using ServerCommon; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameServer +{ + internal class RankingUpdateTicker : EntityTicker + { + public RankingUpdateTicker(double onTickIntervalMilliseconds, CancellationTokenSource? cts) + : base(EntityType.RankingUpdateTicker, onTickIntervalMilliseconds, cts) + { + } + + public override async Task onTaskTick() + { + 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(); + + if (true == server_config.PerformanceCheckEnable) + { + event_tid = System.Guid.NewGuid().ToString("N"); + stopwatch = Stopwatch.StartNew(); + } + + var requestor_id = server_logic.getServerName(); + + var ranking_manager = server_logic.getRankingManager(); + result = await ranking_manager.tryUpdateRankings(requestor_id); + if (result.isFail()) + { + err_msg = $"Fail to tryUpdateRanking() !!! : {result.toBasicString()} - requestorId:{requestor_id}, {toBasicString()}"; + Log.getLogger().error(err_msg); + } + + if (null != stopwatch) + { + var elapsed_msec = stopwatch.ElapsedMilliseconds; + stopwatch.Stop(); + Log.getLogger().debug($"{GetType()} Ticker Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsed_msec}, TickIntervalMSec:{getOnTickIntervalMilliseconds()}"); + } + + await Task.CompletedTask; + } + + public override string toBasicString() + { + return $"{this.getTypeName()}"; + } + + public override string toSummaryString() + { + return $"{this.getTypeName()}"; + } + } +} diff --git a/GameServer/Ticker/WorldEventScheduleUpdateTicker.cs b/GameServer/Ticker/WorldEventScheduleUpdateTicker.cs new file mode 100644 index 0000000..24e70a9 --- /dev/null +++ b/GameServer/Ticker/WorldEventScheduleUpdateTicker.cs @@ -0,0 +1,53 @@ +using System.Diagnostics; +using GameServer.Contents.WorldEvent; +using ServerBase; +using ServerCore; + +namespace GameServer; + +public class WorldEventScheduleUpdateTicker : EntityTicker +{ + public WorldEventScheduleUpdateTicker(double onTickIntervalMilliseconds, CancellationTokenSource? cts = null) + : base(EntityType.RankingScheduleUpdateTicker, onTickIntervalMilliseconds, cts) + { + } + + public override async Task onTaskTick() + { + Stopwatch? stopwatch = null; + var event_tid = string.Empty; + + var server_logic = GameServerApp.getServerLogic(); + var server_config = server_logic.getServerConfig(); + + if (server_config is { PerformanceCheckEnable: true }) + { + event_tid = System.Guid.NewGuid().ToString("N"); + stopwatch = Stopwatch.StartNew(); + } + + await server_logic.getWorldEventScheduleManager().LoadSchedulesAsync(server_logic.getDynamoDbClient()); + + if (null != stopwatch) + { + var elapsedMsec = stopwatch.ElapsedMilliseconds; + stopwatch.Stop(); + Log.getLogger() + .debug( + $"{GetType()} Ticker Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsedMsec}, TickIntervalMSec:{getOnTickIntervalMilliseconds()}"); + } + + await Task.CompletedTask; + } + + public override string toBasicString() + { + return $"{this.getTypeName()}"; + } + + public override string toSummaryString() + { + return $"{this.getTypeName()}"; + } +} + diff --git a/GameServer/z.Backup/ClientSession.cs b/GameServer/z.Backup/ClientSession.cs index ea67caa..c047f7e 100644 --- a/GameServer/z.Backup/ClientSession.cs +++ b/GameServer/z.Backup/ClientSession.cs @@ -3738,7 +3738,7 @@ // await InstanceRoomManager.Instance.LeaveRoom(this, InstanceRoomId); // } -// var gameServerInfo = await GetGameServerInfoToConnect(1, _loginInfo.Language); //임시 +// var gameServerInfo = await GetGameServerInfoToConnect(1, _loginInfo.Language); // if (gameServerInfo == null) // { // Log.getLogger().error("Need GameServer !!! GameServer is full or not exsit"); diff --git a/GameServer/z.Backup/ClientSession_Friend.cs b/GameServer/z.Backup/ClientSession_Friend.cs deleted file mode 100644 index 95f8d1d..0000000 --- a/GameServer/z.Backup/ClientSession_Friend.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Google.Protobuf.WellKnownTypes; -using Nettention.Proud; -using Newtonsoft.Json; -using ServerCommon; -using ServerCommon.BusinessLogDomain; -using ServerCore; using ServerBase; -using System.Collections.Concurrent; -using static ClientToGameReq.Types; - - -namespace GameServer -{ - public partial class ClientSession - { - - - // - // - // public void getStateNotiFromFriend(string friendId, string friendGuid, string friendrNickName, int friendState, int friendMapId) - // { - // - // ClientSession? friendSession = ClientSessionManager.Instance.GetSessionByName(friendId); - // - // UserLocationInfo info = new(); - // if (friendSession is null || friendSession._selectedChar is null) - // { - // Log.getLogger().warn($"{friendId} session is null so UserLocationInfo is empty", ToString()); - // } - // else - // { - // friendSession._selectedChar.getLocationInfo(out var friendLocation); - // if (friendLocation is null) - // { - // Log.getLogger().warn($"{friendId} UserLocationInfo is null", ToString()); - // - // } - // else - // { - // info = friendLocation; - // } - // - // } - // - // - - // - // Send(RmiContext.ReliableSend, friendStateToMe); - // } - // - // - // - // - // - - - - - public string getAccountId() - { - throw new NotImplementedException(); - } - - - } -} diff --git a/LoginServer/LoginServerApp.cs b/LoginServer/LoginServerApp.cs index 98095e7..fabad7b 100644 --- a/LoginServer/LoginServerApp.cs +++ b/LoginServer/LoginServerApp.cs @@ -1,8 +1,7 @@  using Microsoft.Extensions.Configuration; using CommandLine; - - +using ControlCenter.NamedPipe.Model; using ServerCore; using ServerBase; using ServerCommon; @@ -63,7 +62,7 @@ public static class LoginServerApp } catch (Exception e) { - var err_msg = $"Exception !!!, Failed to perform in Main() : exception:{e}"; + var err_msg = $"Exception !!!, Failed to perform in Main() !!! : exception:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); } finally @@ -100,7 +99,13 @@ public static class LoginServerApp m_server_logic.setServerType(ServerType.Login.toServerTypeString()); m_server_logic.setConfiguration(configuration); - result = await m_server_logic.onRunServer(); + var namedOptionBuilder = new NamedPipeClientOptionBuilder(); + namedOptionBuilder.setIP(NetworkHelper.getEthernetLocalIPv4()) + .setPort(m_server_logic.getConfiguration().getPort()) + .setType(nameof(ServerType.Login)) + .setServiceCategory(nameof(ServiceCategory.Caliverse)); + + result = await m_server_logic.onRunServer(namedOptionBuilder); if(result.isFail()) { return result; diff --git a/LoginServer/LoginServerLogic.cs b/LoginServer/LoginServerLogic.cs index 877e056..05c4645 100644 --- a/LoginServer/LoginServerLogic.cs +++ b/LoginServer/LoginServerLogic.cs @@ -281,7 +281,7 @@ public class LoginServerLogic : ServerLogicBase, IWithPacketNamespaceVerifier, I return m_reservation_manager; } - public bool isValidPacketNamespace(string toCheckNamespace, IPacketCommand packetCommand) + public bool isValidPacketNamespace(string? toCheckNamespace, IPacketCommand packetCommand) { var packet_namespace = "LoginServer.PacketHandler"; diff --git a/LoginServer/Manager/LoginQueueManager.cs b/LoginServer/Manager/LoginQueueManager.cs index 17c84c1..a4e9633 100644 --- a/LoginServer/Manager/LoginQueueManager.cs +++ b/LoginServer/Manager/LoginQueueManager.cs @@ -1,11 +1,9 @@ -using System.Collections.Concurrent; - - -using ServerCore; +using Amazon.S3.Model; +using Microsoft.AspNetCore.Identity; using ServerBase; using ServerCommon; - - +using ServerCore; +using System.Collections.Concurrent; using USER_GUID = System.String; @@ -148,7 +146,16 @@ public class LoginQueueManager account_attribute.ToConnectGameServerAddress = found_server_info.toNetworkAddress(); account_attribute.ToConnectGameServerName = serverName; - + + result = await moveToDefaultWorldAccountStartPosWhenWorldNotExist(owner, found_server_info); + if (result.isFail()) + { + err_msg = $"Failed to moveToDefaultWorldAccountStartPosWhenWorldNotExist() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + // 3. DB 갱신 var batch = new QueryBatchEx( owner , LogActionType.ReservationEnterToServer @@ -181,4 +188,62 @@ public class LoginQueueManager return result; } + + async Task moveToDefaultWorldAccountStartPosWhenWorldNotExist(UserBase userBase, ServerInfo serverInfo) + { + var result = new Result(); + var err_msg = string.Empty; + + var location_attribute = userBase.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(location_attribute, () => "location_attribute is null !!!"); + + if (MetaData.Instance._WorldMetaTable.TryGetValue(location_attribute.LastestChannelServerLocation.WorldMetaId, out _)) + return result; + + if (!MetaData.Instance._GameConfigMetaTable.TryGetValue(ConstantKeyType.DefaultEntryWorldIdWhenLoginToAuth.ToString(), out var found_value)) + { + err_msg = $"ConstantKeyType.DefaultEntryWorldIdWhenLoginToAuth not found !!! - {userBase.toBasicString()}"; + result.setFail(ServerErrorCode.GameConfigMetaDataNotFound, err_msg); + Log.getLogger().fatal(result.toBasicString()); + + return result; + } + + var default_world_meta_id = Convert.ToInt32(found_value); + if (serverInfo.WorldId != default_world_meta_id) + { + err_msg = $"Failed to Move to Default World, Not Exist WorldMetaId:{serverInfo.WorldId} !!!"; + Log.getLogger().debug(err_msg); + return result; + } + + var server_logic = LoginServerApp.getServerLogic(); + var redis_connector = server_logic.getRedisConnector(); + NullReferenceCheckHelper.throwIfNull(redis_connector, () => $"redis_connector is null !!! - {userBase.toBasicString()}"); + + var location_cache_request = new LocationCacheRequest(userBase, redis_connector); + result = await location_cache_request.fetchLocation(); + if (result.isFail()) + { + err_msg = $"Failed to fetchLocation() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var location_cache = location_cache_request.getLocationCache(); + location_cache.copyCacheFromEntityAttribute(location_attribute); + location_cache.MoveToAccountStartPosAtLogin = true; + + result = await location_cache_request.upsertLocation(); + if (result.isFail()) + { + err_msg = $"Failed to upsertLocation() !!! - {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + return result; + } } \ No newline at end of file diff --git a/MatchServer/Common/IMatchMq.cs b/MatchServer/Common/IMatchMq.cs new file mode 100644 index 0000000..45b4cc8 --- /dev/null +++ b/MatchServer/Common/IMatchMq.cs @@ -0,0 +1,8 @@ +namespace MatchServer; + +public interface IMatchMq +{ + public void SendBatchMessages(string to, IEnumerable messages); + + public void SendMessage(string to, ServerMessage message); +} diff --git a/MatchServer/Common/ISeqWorkQue.cs b/MatchServer/Common/ISeqWorkQue.cs new file mode 100644 index 0000000..19ec616 --- /dev/null +++ b/MatchServer/Common/ISeqWorkQue.cs @@ -0,0 +1,62 @@ +public interface ISeqWorkQue +{ + // /// + // /// Action을 등록합니다. + // /// + // Task AddAsync(Action action, CancellationToken cancellationToken = default); + // + // /// + // /// Action을 등록합니다. + // /// + // + // Task AddAsync(Action action, T parameter, CancellationToken cancellationToken = default); + // /// + // /// Func을 등록합니다. + // /// + // Task AddAsync(Func func, CancellationToken cancellationToken = default); + // + // /// + // /// Func을 등록합니다. + // /// + // Task AddAsync(Func func, T parameter, CancellationToken cancellationToken = default); + + /// + /// Task을 등록합니다. + /// + Task AddAsync(Task task, CancellationToken cancellationToken = default); + + // /// + // /// Task을 등록합니다. + // /// + // Task AddAsync(Task task, CancellationToken cancellationToken = default); + + // /// + // /// Func을 등록합니다. + // /// + // Task AddAsync(Func taskFunc, CancellationToken cancellationToken = default); + + // /// + // /// Func>을 등록합니다. + // /// + // Task AddAsync(Func> taskFunc, CancellationToken cancellationToken = default); + + /// + /// 모든 작업에 우선 실행되도록 Func을 등록합니다. + /// + Task AddWorkFirstAsync(Func taskFunc, CancellationToken cancellationToken = default); + + /// + /// 다음 작업을 읽어서 실행합니다. 외부에서 반복 호출해야 합니다. + /// + Task?> ExecuteNextAsync(CancellationToken cancellationToken = default); + + /// + /// 현재 대기 중인 작업 수를 반환합니다. + /// + int PendingCount { get; } + + /// + /// 대기 중인 작업이 있는지 확인합니다. + /// + bool HasPendingWork { get; } +} diff --git a/MatchServer/Common/ISeqWorker.cs b/MatchServer/Common/ISeqWorker.cs new file mode 100644 index 0000000..cf44b34 --- /dev/null +++ b/MatchServer/Common/ISeqWorker.cs @@ -0,0 +1,26 @@ +namespace MatchServer.Common; + +/// +/// SequenceWorkQue를 처리하는 워커의 인터페이스 +/// +public interface ISeqWorker : IDisposable +{ + /// + /// 추가 업데이트 루프를 등록합니다. + /// + /// + /// + void RegisterUpdateWorker(Func onUpdate, int intervalSec = 10); + + /// + /// 워커를 시작합니다. + /// + /// 시작 작업 + Task StartAsync(); + + /// + /// 워커를 중지합니다. + /// + /// 중지 작업 + Task StopAsync(); +} diff --git a/MatchServer/Common/LazyServiceExtensions.cs b/MatchServer/Common/LazyServiceExtensions.cs new file mode 100644 index 0000000..671af3c --- /dev/null +++ b/MatchServer/Common/LazyServiceExtensions.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace LazyService; +public static class LazyServiceExtensions +{ + public static IServiceCollection AddLazyResolution(this IServiceCollection services) + { + services.AddTransient(typeof(Lazy<>), typeof(LazyResolution<>)); + return services; + } +} + +public class LazyResolution : Lazy where T : class +{ + public LazyResolution(IServiceProvider serviceProvider) + : base(() => serviceProvider.GetRequiredService()) + { + } +} diff --git a/MatchServer/Common/PeriodicWorker.cs b/MatchServer/Common/PeriodicWorker.cs new file mode 100644 index 0000000..ef76fa2 --- /dev/null +++ b/MatchServer/Common/PeriodicWorker.cs @@ -0,0 +1,51 @@ +// using Microsoft.Extensions.Hosting; +// using Microsoft.Extensions.Logging; +// using ServerCommon.NLog; +// +// namespace MatchServer.Common; +// +// public class PeriodicWorker : BackgroundService +// { +// /// +// /// 주기적 작업 정보를 담는 내부 클래스 +// /// +// public class PeriodicTask +// { +// public required Func TaskFunc { get; init; } +// public required TimeSpan Interval { get; init; } +// public required string TaskName { get; init; } +// public DateTime LastExecutionTime { get; set; } +// } +// +// private readonly ILogger _logger; +// private readonly PeriodicTask _task; +// +// public PeriodicWorker(Func taskFunc, int intervalMs, ILogger logger) +// { +// _task = new PeriodicTask +// { +// TaskFunc = taskFunc, +// Interval = TimeSpan.FromMilliseconds(intervalMs), +// TaskName = "", +// LastExecutionTime = DateTime.MinValue +// }; +// +// _logger = logger; +// } +// +// protected override async Task ExecuteAsync(CancellationToken stoppingToken) +// { +// using var timer = new PeriodicTimer(_task.Interval); +// do +// { +// try +// { +// await _task.TaskFunc(); +// } +// catch (Exception ex) +// { +// _logger.Error($"Task Failed => {ex}"); +// } +// } while (await timer.WaitForNextTickAsync(stoppingToken)); +// } +// } diff --git a/MatchServer/Common/SeqWorkQue.cs b/MatchServer/Common/SeqWorkQue.cs new file mode 100644 index 0000000..ac9e8e2 --- /dev/null +++ b/MatchServer/Common/SeqWorkQue.cs @@ -0,0 +1,515 @@ +using System.Collections.Concurrent; + +namespace MatchServer.Common; + +/// +/// 완전 Lock-Free 순차 작업 큐 +/// 두 개의 ConcurrentQueue를 사용한 우선순위 처리 +/// +public class SeqWorkQue : ISeqWorkQue, IDisposable +{ + public class WorkItem + { + public Func Action { get; set; } + public TaskCompletionSource CompletionSource { get; set; } + public bool HasResult { get; set; } + public bool IsHighPriority { get; set; } + + public WorkItem(Func action, bool hasResult = false, bool isHighPriority = false) + { + Action = action; + HasResult = hasResult; + IsHighPriority = isHighPriority; + CompletionSource = new TaskCompletionSource(); + } + } + + // 완전 Lock-Free: 두 개의 ConcurrentQueue 사용 + private readonly ConcurrentQueue _highPriorityQueue = new(); + private readonly ConcurrentQueue _normalQueue = new(); + + // 대기 상태 관리를 위한 카운터들 (Interlocked 사용) + private volatile int _pendingWorkCount = 0; + private volatile int _waitingReaderCount = 0; + + // 대기 중인 읽기 작업들을 위한 TaskCompletionSource 큐 + private readonly ConcurrentQueue> _waitingReaders = new(); + + // 결과 저장 (lock 사용하지만 선택적 기능) + private readonly List _results = []; + private readonly object _resultsLock = new(); + + // 상태 관리 + private volatile bool _disposed = false; + private volatile bool _completed = false; + + public SeqWorkQue(int capacity = 10000) + { + // capacity는 현재 구현에서 제한하지 않음 (메모리 효율성을 위해) + // 필요시 향후 백프레셔 기능 추가 가능 + } + + #region Public API - 작업 추가 + + /// + /// Action을 일반 우선순위로 등록합니다. + /// + public Task AddAsync(Action action, CancellationToken cancellationToken = default) + { + if (_disposed || _completed) + return Task.FromResult(false); + + var workItem = new WorkItem(() => + { + action.Invoke(); + return Task.CompletedTask; + }); + + return AddWorkItemAsync(workItem, false); + } + + /// + /// Action을 일반 우선순위로 등록합니다. + /// + public async Task AddAsync(Action action, T parameter, CancellationToken cancellationToken = default) + { + if (_disposed || _completed) + return false; + + var workItem = new WorkItem(() => + { + action.Invoke(parameter); + return Task.CompletedTask; + }); + + return await AddWorkItemAsync(workItem, false); + } + + /// + /// Func을 일반 우선순위로 등록합니다. + /// + public async Task AddAsync(Func func, CancellationToken cancellationToken = default) + { + if (_disposed || _completed) + return false; + + var workItem = new WorkItem(async () => + { + var result = func.Invoke(); + await AddResultAsync(result); + }, hasResult: true); + + return await AddWorkItemAsync(workItem, false); + } + + /// + /// Func을 일반 우선순위로 등록합니다. + /// + public async Task AddAsync(Func func, T parameter, + CancellationToken cancellationToken = default) + { + if (_disposed || _completed) + return false; + + var workItem = new WorkItem(async () => + { + var result = func.Invoke(parameter); + await AddResultAsync(result); + }, hasResult: true); + + return await AddWorkItemAsync(workItem, false); + } + + /// + /// Task을 일반 우선순위로 등록합니다. + /// + public async Task AddAsync(Task task, CancellationToken cancellationToken = default) + { + if (_disposed || _completed) + return false; + + var workItem = new WorkItem(() => task); + return await AddWorkItemAsync(workItem, false); + } + + /// + /// Task을 일반 우선순위로 등록합니다. + /// + public async Task AddAsync(Task task, CancellationToken cancellationToken = default) + { + if (_disposed || _completed) + return false; + + var workItem = new WorkItem(async () => + { + var result = await task; + await AddResultAsync(result); + }, hasResult: true); + + return await AddWorkItemAsync(workItem, false); + } + + /// + /// Func을 일반 우선순위로 등록합니다. + /// + public async Task AddAsync(Func taskFunc, CancellationToken cancellationToken = default) + { + if (_disposed || _completed) + return false; + + var workItem = new WorkItem(taskFunc); + return await AddWorkItemAsync(workItem, false); + } + + /// + /// Func>을 일반 우선순위로 등록합니다. + /// + public async Task AddAsync(Func> taskFunc, CancellationToken cancellationToken = default) + { + if (_disposed || _completed) + return false; + + var workItem = new WorkItem(async () => + { + var result = await taskFunc(); + await AddResultAsync(result); + }, hasResult: true); + + return await AddWorkItemAsync(workItem, false); + } + + /// + /// Func을 높은 우선순위로 등록합니다. (매칭 로직 등에 사용) + /// + public async Task AddWorkFirstAsync(Func taskFunc, CancellationToken cancellationToken = default) + { + if (_disposed || _completed) + return; + + var workItem = new WorkItem(taskFunc, isHighPriority: true); + await AddWorkItemAsync(workItem, true); + } + + #endregion + + #region Core Lock-Free Implementation + + /// + /// 완전 Lock-Free 작업 추가 + /// + private async Task AddWorkItemAsync(WorkItem workItem, bool highPriority) + { + try + { + // 1. 작업을 적절한 큐에 추가 (완전 lock-free) + if (highPriority) + { + _highPriorityQueue.Enqueue(workItem); + } + else + { + _normalQueue.Enqueue(workItem); + } + + // 2. 대기 중인 작업 카운트 증가 (atomic) + Interlocked.Increment(ref _pendingWorkCount); + + // 3. 대기 중인 리더가 있으면 깨우기 + NotifyWaitingReader(); + + return await Task.FromResult(true); + } + catch (Exception) + { + return await Task.FromResult(false); + } + } + + /// + /// 대기 중인 리더 하나를 깨웁니다. (Lock-Free) + /// + private void NotifyWaitingReader() + { + if (_waitingReaders.TryDequeue(out var tcs)) + { + // 비동기로 완료 설정 (현재 스레드 블로킹 방지) + Task.Run(() => tcs.TrySetResult(true)); + } + } + + /// + /// 다음 작업 실행 + /// + public async Task?> ExecuteNextAsync(CancellationToken cancellationToken = default) + { + await WaitForWorkAsync(cancellationToken); + WorkItem? workItem = TryDequeueWork(); + if (workItem is not null) + { + return workItem.Action; + } + + return null; + } + + /// + /// 완전 Lock-Free 작업 큐에서 가져오기 (우선순위 처리) + /// + private WorkItem? TryDequeueWork() + { + // 1. 높은 우선순위 큐 먼저 확인 + if (_highPriorityQueue.TryDequeue(out var highPriorityItem)) + { + Interlocked.Decrement(ref _pendingWorkCount); + return highPriorityItem; + } + + // 2. 일반 우선순위 큐 확인 + if (_normalQueue.TryDequeue(out var normalItem)) + { + Interlocked.Decrement(ref _pendingWorkCount); + return normalItem; + } + + return null; + } + + /// + /// 작업이 추가될 때까지 비동기 대기 + /// + private async Task WaitForWorkAsync(CancellationToken cancellationToken) + { + var tcs = new TaskCompletionSource(); + + // 취소 토큰 등록 + using var registration = cancellationToken.Register(() => + tcs.TrySetCanceled(cancellationToken)); + + // 대기자 큐에 등록 + Interlocked.Increment(ref _waitingReaderCount); + _waitingReaders.Enqueue(tcs); + + try + { + // 등록 후 다시 한 번 작업 확인 (race condition 방지) + if (TryDequeueWork() != null || (_completed && PendingCount == 0)) + { + tcs.TrySetResult(true); + return; + } + + // 타임아웃과 함께 대기 (무한 대기 방지) + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(1)); + using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource( + cancellationToken, timeoutCts.Token); + + try + { + await tcs.Task.WaitAsync(combinedCts.Token); + } + catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested) + { + // 타임아웃은 정상 동작 (주기적으로 상태 확인) + } + } + finally + { + Interlocked.Decrement(ref _waitingReaderCount); + } + } + + #endregion + + #region Properties and Status + + /// + /// 현재 대기 중인 작업 수를 반환합니다. + /// + public int PendingCount => _pendingWorkCount; + + /// + /// 대기 중인 작업이 있는지 확인합니다. + /// + public bool HasPendingWork => _pendingWorkCount > 0; + + /// + /// 높은 우선순위 작업 수를 반환합니다. + /// + public int HighPriorityCount => _highPriorityQueue.Count; + + /// + /// 일반 우선순위 작업 수를 반환합니다. + /// + public int NormalPriorityCount => _normalQueue.Count; + + /// + /// 현재 대기 중인 리더 수를 반환합니다. + /// + public int WaitingReaderCount => _waitingReaderCount; + + /// + /// 큐가 완료되었는지 확인합니다. + /// + public bool IsCompleted => _completed; + + /// + /// 작업 추가를 완료하고 큐를 닫습니다. + /// + public void CompleteAdding() + { + _completed = true; + + // 모든 대기 중인 리더들을 깨움 + while (_waitingReaders.TryDequeue(out var tcs)) + { + Task.Run(() => tcs.TrySetResult(false)); + } + } + + #endregion + + #region Result Management (Optional Feature) + + /// + /// 실행 결과를 반환합니다. + /// + public IReadOnlyList GetResults() + { + lock (_resultsLock) + { + return _results.ToList().AsReadOnly(); + } + } + + /// + /// 특정 인덱스의 결과를 반환합니다. + /// + public T GetResult(int index) + { + lock (_resultsLock) + { + if (index < 0 || index >= _results.Count) + throw new ArgumentOutOfRangeException(nameof(index)); + return (T)_results[index]; + } + } + + /// + /// 결과를 초기화합니다. + /// + public void ClearResults() + { + lock (_resultsLock) + { + _results.Clear(); + } + } + + private Task AddResultAsync(object result) + { + return Task.Run(() => + { + lock (_resultsLock) + { + _results.Add(result); + } + }); + } + + #endregion + + #region Advanced Methods (Optional) + + /// + /// 즉시 실행 가능한 작업이 있는지 확인하고 실행합니다. + /// + public bool TryExecuteNext() + { + var workItem = TryDequeueWork(); + if (workItem == null) + return false; + + try + { + // 동기 실행 (비동기 작업의 경우 백그라운드에서 실행) + var task = workItem.Action(); + if (task.IsCompleted) + { + workItem.CompletionSource.TrySetResult(true); + } + else + { + _ = Task.Run(async () => + { + try + { + await task; + workItem.CompletionSource.TrySetResult(true); + } + catch (Exception ex) + { + workItem.CompletionSource.TrySetException(ex); + } + }); + } + + return true; + } + catch (Exception ex) + { + workItem.CompletionSource.TrySetException(ex); + return true; // 작업은 처리했음 (실패했지만) + } + } + + /// + /// 모든 큐를 비웁니다. + /// + public void Clear() + { + while (_highPriorityQueue.TryDequeue(out var item)) + { + item.CompletionSource.TrySetCanceled(); + } + + while (_normalQueue.TryDequeue(out var item)) + { + item.CompletionSource.TrySetCanceled(); + } + + Interlocked.Exchange(ref _pendingWorkCount, 0); + } + + #endregion + + #region Dispose + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + CompleteAdding(); + Clear(); + } + } + + #endregion + + #region Debug Information + + /// + /// 디버그 정보를 반환합니다. + /// + public override string ToString() + { + return $"SequenceWorkQue: " + + $"Total={PendingCount}, " + + $"High={HighPriorityCount}, " + + $"Normal={NormalPriorityCount}, " + + $"Waiting={WaitingReaderCount}, " + + $"Completed={IsCompleted}"; + } + + #endregion +} diff --git a/MatchServer/Common/SequenceWorkQue.cs b/MatchServer/Common/SequenceWorkQue.cs new file mode 100644 index 0000000..1d441fc --- /dev/null +++ b/MatchServer/Common/SequenceWorkQue.cs @@ -0,0 +1,141 @@ +using System.Collections.Concurrent; +using System.Threading.Channels; + +public class WorkItem +{ + public Func Action { get; set; } + public TaskCompletionSource CompletionSource { get; set; } + public bool HasResult { get; set; } + + public WorkItem(Func action, bool hasResult = false) + { + Action = action; + HasResult = hasResult; + CompletionSource = new TaskCompletionSource(); + } +} + +public class SequenceWorkQue : IDisposable +{ + private readonly Channel _channel; + private readonly ChannelWriter _writer; + private readonly ChannelReader _reader; + private readonly ConcurrentQueue _firstActions = []; + private readonly SemaphoreSlim _workAvailable = new(0); + private bool _disposed = false; + + public SequenceWorkQue(int capacity = 10000) + { + var options = new BoundedChannelOptions(capacity) + { + FullMode = BoundedChannelFullMode.Wait, SingleReader = true, SingleWriter = false + }; + + _channel = Channel.CreateBounded(options); + _writer = _channel.Writer; + _reader = _channel.Reader; + } + + public async Task AddAsync(Func taskFunc, CancellationToken cancellationToken = default) + { + var workItem = new WorkItem(taskFunc); + await TryWriteAsync(workItem, cancellationToken); + _workAvailable.Release(); + return true; + } + + public void AddWorkFirstAsync(Func taskFunc, CancellationToken cancellationToken = default) + { + var workItem = new WorkItem(taskFunc); + _firstActions.Enqueue(workItem); + _workAvailable.Release(); + // return Task.CompletedTask; + // return AddAsync(taskFunc, cancellationToken); + } + + public async Task ExecuteNextAsync(CancellationToken cancellationToken = default) + { + try + { + // 작업이 있을 때까지 대기 + await _workAvailable.WaitAsync(cancellationToken); + + if (_firstActions.TryDequeue(out WorkItem? workItem) || _reader.TryRead(out workItem)) + { + try + { + await workItem.Action(); + workItem.CompletionSource.SetResult(true); + return true; + } + catch (Exception ex) + { + workItem.CompletionSource.SetException(ex); + throw; // 예외를 다시 던져서 외부에서 처리할 수 있도록 함 + } + } + // + // _reader.TryRead(out workItem); + // + // // 읽을 게 있을 때까지 대기 + // workItem = await _reader.ReadAsync(cancellationToken); + // try + // { + // await workItem.Action(); + // workItem.CompletionSource.SetResult(true); + // return true; + // } + // catch (Exception ex) + // { + // workItem.CompletionSource.SetException(ex); + // throw; // 예외를 다시 던져서 외부에서 처리할 수 있도록 함 + // } + } + catch (OperationCanceledException) + { + return false; + } + catch (InvalidOperationException) + { + // 채널이 닫혔고 더 이상 읽을 데이터가 없음 + return false; + } + return false; + } + + /// + /// 작업 추가를 완료하고 채널을 닫습니다. + /// + public void CompleteAdding() + { + _writer.Complete(); + } + + private async Task TryWriteAsync(WorkItem workItem, CancellationToken cancellationToken) + { + try + { + await _writer.WriteAsync(workItem, cancellationToken); + } + catch (OperationCanceledException) + { + return false; + } + catch (InvalidOperationException) + { + // 채널이 닫혔을 때 + return false; + } + + return true; + } + + public void Dispose() + { + if (!_disposed) + { + // CompleteAdding(); + _disposed = true; + } + } +} diff --git a/MatchServer/Common/SequenceWorker.cs b/MatchServer/Common/SequenceWorker.cs new file mode 100644 index 0000000..1b02199 --- /dev/null +++ b/MatchServer/Common/SequenceWorker.cs @@ -0,0 +1,85 @@ +using Microsoft.Extensions.Hosting; +using ServerCore; + +public class SequenceWorker : BackgroundService +{ + private readonly SequenceWorkQue _sequenceWorkQue; + private readonly NLog.Logger _logger; + private Func? _updateLoop; + + public SequenceWorker( + SequenceWorkQue sequenceWorkQue) + { + _sequenceWorkQue = sequenceWorkQue ?? throw new ArgumentNullException(nameof(sequenceWorkQue)); + _logger = Log.getLogger(); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.info("QueExecutor BackgroundService가 시작되었습니다."); + + try + { + while (!stoppingToken.IsCancellationRequested) + { + try + { + // QueExecutor에서 다음 작업을 실행 + bool hasWork = await _sequenceWorkQue.ExecuteNextAsync(stoppingToken); + + if (!hasWork) + { + // 작업이 없으면 잠시 대기 + await Task.Delay(1, stoppingToken); + } + } + catch (OperationCanceledException) + { + // 정상적인 취소, 루프를 벗어남 + break; + } + catch (Exception ex) + { + _logger.error($"QueExecutor 작업 처리 중 오류가 발생했습니다. => {ex}"); + + // 오류 발생 시 잠시 대기 후 계속 처리 + await Task.Delay(1, stoppingToken); + } + } + } + catch (OperationCanceledException) + { + // 정상적인 취소 + } + catch (Exception ex) + { + _logger.warn($"QueExecutor BackgroundService에서 예상치 못한 오류가 발생했습니다 => {ex}"); + } + finally + { + _logger.info("QueExecutor BackgroundService가 종료되었습니다."); + } + } + + public void RegisterUpdateLoop(Func updateLoop) + { + _updateLoop = updateLoop; + } + + public override async Task StopAsync(CancellationToken cancellationToken) + { + _logger.info("QueExecutor BackgroundService 중지가 요청되었습니다."); + + // 더 이상 새로운 작업을 받지 않도록 채널을 닫음 + _sequenceWorkQue.CompleteAdding(); + + await base.StopAsync(cancellationToken); + } + + public override void Dispose() + { + _logger.info("QueExecutor BackgroundService가 해제되었습니다."); + _sequenceWorkQue?.Dispose(); + base.Dispose(); + } +} diff --git a/MatchServer/Controllers/PacketHandler/NtfMatchChangeGameStateHandler.cs b/MatchServer/Controllers/PacketHandler/NtfMatchChangeGameStateHandler.cs new file mode 100644 index 0000000..3c9e092 --- /dev/null +++ b/MatchServer/Controllers/PacketHandler/NtfMatchChangeGameStateHandler.cs @@ -0,0 +1,21 @@ +using Google.Protobuf; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace MatchServer.PacketHandler; + +[PacketHandler("", typeof(ServerMessage.Types.GS2MS_NTF_MATCH_CHANGE_GAME_STATE), + typeof(NtfMatchChangeGameStateHandler), typeof(RabbitMq4Match))] +public class NtfMatchChangeGameStateHandler : PacketRecvHandler4Match +{ + public override async Task onProcessPacket(ISession session, IMessage message) + { + var recvServerMessage = message as ServerMessage; + NullReferenceCheckHelper.throwIfNull(recvServerMessage, () => $"server_message is null !!!"); + + MatchGuard.NotNull(MatchService); + await MatchService.AddWorkAsync(recvServerMessage.NtfMatchChangeGameState); + return new Result(); + } +} diff --git a/MatchServer/Controllers/PacketHandler/NtfMatchGameJoinHandler.cs b/MatchServer/Controllers/PacketHandler/NtfMatchGameJoinHandler.cs new file mode 100644 index 0000000..a8c8967 --- /dev/null +++ b/MatchServer/Controllers/PacketHandler/NtfMatchGameJoinHandler.cs @@ -0,0 +1,20 @@ +using Google.Protobuf; +using ServerBase; +using ServerCore; + +namespace MatchServer.PacketHandler; + +[PacketHandler("", typeof(ServerMessage.Types.GS2MS_NTF_MATCH_GAME_JOIN), + typeof(NtfMatchGameJoinHandler), typeof(RabbitMq4Match))] +public class NtfMatchGameJoinHandler : PacketRecvHandler4Match +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var recvServerMessage = recvMessage as ServerMessage; + NullReferenceCheckHelper.throwIfNull(recvServerMessage, () => $"server_message is null !!!"); + + var message = recvServerMessage.NtfMatchGameJoin; + await MatchService.AddWorkAsync(message); + return await Task.FromResult(new Result()); + } +} diff --git a/MatchServer/Controllers/PacketHandler/NtfMatchGameQuitHandler.cs b/MatchServer/Controllers/PacketHandler/NtfMatchGameQuitHandler.cs new file mode 100644 index 0000000..5e2658a --- /dev/null +++ b/MatchServer/Controllers/PacketHandler/NtfMatchGameQuitHandler.cs @@ -0,0 +1,21 @@ +using Google.Protobuf; +using ServerBase; +using ServerCore; + +namespace MatchServer.PacketHandler; + +[PacketHandler("", typeof(ServerMessage.Types.GS2MS_NTF_MATCH_GAME_QUIT), + typeof(NtfMatchGameQuitHandler), typeof(RabbitMq4Match))] +public class NtfMatchGameQuitHandler : PacketRecvHandler4Match +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var recvServerMessage = recvMessage as ServerMessage; + NullReferenceCheckHelper.throwIfNull(recvServerMessage, () => $"server_message is null !!!"); + + // 매시지를 등록만 하고 처리는 ... + var message = recvServerMessage.NtfMatchGameQuit; + await MatchService.AddWorkAsync(message); + return await Task.FromResult(new Result()); + } +} diff --git a/MatchServer/Controllers/PacketHandler/NtfMatchTestCheatHandler.cs b/MatchServer/Controllers/PacketHandler/NtfMatchTestCheatHandler.cs new file mode 100644 index 0000000..13cbb2d --- /dev/null +++ b/MatchServer/Controllers/PacketHandler/NtfMatchTestCheatHandler.cs @@ -0,0 +1,20 @@ +using Google.Protobuf; +using ServerBase; +using ServerCore; + +namespace MatchServer.PacketHandler; +[PacketHandler("", typeof(ServerMessage.Types.GS2MS_NTF_MATCH_CHEAT_CMD), + typeof(NtfMatchTestCheatHandler), typeof(RabbitMq4Match))] +public class NtfMatchTestCheatHandler : PacketRecvHandler4Match +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var recv_server_message = recvMessage as ServerMessage; + NullReferenceCheckHelper.throwIfNull(recv_server_message, () => $"server_message is null !!!"); + + // 매시지를 등록만 하고 처리는 ... + var message = recv_server_message.NtfMatchCheatCmd; + await MatchService.AddWorkAsync(message); + return await Task.FromResult(new Result()); + } +} diff --git a/MatchServer/Controllers/PacketHandler/ReqGameMatchCancelHandler.cs b/MatchServer/Controllers/PacketHandler/ReqGameMatchCancelHandler.cs new file mode 100644 index 0000000..d293766 --- /dev/null +++ b/MatchServer/Controllers/PacketHandler/ReqGameMatchCancelHandler.cs @@ -0,0 +1,20 @@ + +using Google.Protobuf; +using ServerBase; + +using ServerCore; +namespace MatchServer.PacketHandler; + +[PacketHandler("", typeof(ServerMessage.Types.GS2MS_REQ_MATCH_CANCEL), + typeof(ReqGameMatchCancelHandler), typeof(RabbitMq4Match))] +public class ReqGameMatchCancelHandler: PacketRecvHandler4Match +{ + public override async Task onProcessPacket(ISession session, IMessage message) + { + var recvServerMessage = message as ServerMessage; + NullReferenceCheckHelper.throwIfNull(recvServerMessage, () => $"server_message is null !!!"); + + await MatchService.AddWorkAsync(recvServerMessage.ReqMatchCancel); + return new Result(); + } +} diff --git a/MatchServer/Controllers/PacketHandler/ReqGameMatchReserveHandler.cs b/MatchServer/Controllers/PacketHandler/ReqGameMatchReserveHandler.cs new file mode 100644 index 0000000..77f6ae3 --- /dev/null +++ b/MatchServer/Controllers/PacketHandler/ReqGameMatchReserveHandler.cs @@ -0,0 +1,21 @@ +using Google.Protobuf; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace MatchServer.PacketHandler; +[PacketHandler("", typeof(ServerMessage.Types.GS2MS_REQ_MATCH_RESERVE), + typeof(ReqGameMatchReserveHandler), typeof(RabbitMq4Match))] +public class ReqGameMatchReserveHandler: PacketRecvHandler4Match +{ + public override async Task onProcessPacket(ISession session, IMessage recvMessage) + { + var serverMessage = recvMessage as ServerMessage; + NullReferenceCheckHelper.throwIfNull(serverMessage, () => $"server_message is null !!!"); + + MatchGuard.NotNull(MatchService); + await MatchService.AddWorkAsync(serverMessage.ReqMatchReserve); + return new Result(); + } +} + diff --git a/MatchServer/Controllers/PacketHandler/ReqMatchGameInfoHandler.cs b/MatchServer/Controllers/PacketHandler/ReqMatchGameInfoHandler.cs new file mode 100644 index 0000000..a3cd49b --- /dev/null +++ b/MatchServer/Controllers/PacketHandler/ReqMatchGameInfoHandler.cs @@ -0,0 +1,20 @@ +// using Google.Protobuf; +// using ServerBase; +// using ServerCore; +// +// namespace MatchServer.PacketHandler; +// +// [PacketHandler("", typeof(ServerMessage.Types.GS2MS_REQ_MATCH_ROOM_INFO), +// typeof(ReqMatchGameInfoHandler), typeof(RabbitMq4Match))] +// public class ReqMatchGameInfoHandler : PacketRecvHandler4Match +// { +// public override async Task onProcessPacket(ISession session, IMessage recvMessage) +// { +// var serverMessage = recvMessage as ServerMessage; +// NullReferenceCheckHelper.throwIfNull(serverMessage, () => $"server_message is null !!!"); +// +// await MatchService.AddWorkAsync(serverMessage.ReqMatchRoomInfo); +// +// return new Result(); +// } +// } diff --git a/MatchServer/Controllers/PacketRecvHandlerEx.cs b/MatchServer/Controllers/PacketRecvHandlerEx.cs new file mode 100644 index 0000000..6c5afdf --- /dev/null +++ b/MatchServer/Controllers/PacketRecvHandlerEx.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.DependencyInjection; +using ServerBase; + +namespace MatchServer; + +public abstract class PacketRecvHandler4Match: PacketRecvHandler +{ + protected MatchService? MatchService => ServiceProviderHelper.Services?.GetRequiredService(); +} diff --git a/MatchServer/Controllers/RabbitMq4Match.cs b/MatchServer/Controllers/RabbitMq4Match.cs new file mode 100644 index 0000000..aa443c1 --- /dev/null +++ b/MatchServer/Controllers/RabbitMq4Match.cs @@ -0,0 +1,88 @@ +using System.Diagnostics; +using System.Text; +using Google.Protobuf; +using RabbitMQ.Client; +using ServerBase; +using ServerCore; + +namespace MatchServer; +public class RabbitMq4Match : RabbitMqConnector, IWithPacketNamespaceVerifier, IMatchMq +{ + public RabbitMq4Match(ModuleContext moduleContext) : base(moduleContext) + { + } + + protected override Result onCreateExchangeChannel() + { + var result = new Result(); + return result; + } + + public void SendBatchMessages(string to, IEnumerable messages) + { + var con = getConnection(); + if (null == con) + { + Log.getLogger().error("GetConnection return null"); + return; + } + + using (var channel = con.CreateModel()) + { + Stopwatch? stopwatch = null; + var eventTid = string.Empty; + + var serverLogic = ServerLogicApp.getServerLogicApp(); + var serverConfig = serverLogic.getServerConfig(); + + if (serverConfig.PerformanceCheckEnable) + { + eventTid = System.Guid.NewGuid().ToString("N"); + stopwatch = Stopwatch.StartNew(); + } + + channel.QueueDeclare(queue: to, + durable: true, + exclusive: false, + autoDelete: true, + arguments: null); + + // 배치 퍼블리시 생성 + var batch = channel.CreateBasicPublishBatch(); + var messageCount = 0; + + foreach (var message in messages) + { + message.MessageTime = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.UtcNow); + message.MessageSender = getServiceName(); + + string messageJson = JsonFormatter.Default.Format(message); + ReadOnlyMemory body = Encoding.UTF8.GetBytes(messageJson); + + // 배치에 메시지 추가 + batch?.Add(exchange: "", + routingKey: to, + mandatory: false, + properties: null, + body: body); + + Log.getLogger().info($"added to batch !!!, msg:{messageJson} - receiver:{to}"); + messageCount++; + } + + // 배치로 모든 메시지 전송 + batch.Publish(); + + Log.getLogger().info($"batch published {messageCount} messages to MQS - receiver:{to}"); + + if (null != stopwatch) + { + var elapsedMsec = stopwatch.ElapsedMilliseconds; + stopwatch.Stop(); + Log.getLogger() + .debug( + $"{GetType()} SMQSBatch Stopwatch Stop : ETID:{eventTid}, ElapsedMSec:{elapsedMsec}, MessageCount:{messageCount} - receiver:{to}"); + } + } + } +} diff --git a/MatchServer/Directory.Build.props b/MatchServer/Directory.Build.props new file mode 100644 index 0000000..181094c --- /dev/null +++ b/MatchServer/Directory.Build.props @@ -0,0 +1,8 @@ + + + false + false + ..\..\obj\AnyCPU\$(MSBuildProjectName)\ + ..\..\bin\ + + \ No newline at end of file diff --git a/MatchServer/Infra/Config/IServerConfigService.cs b/MatchServer/Infra/Config/IServerConfigService.cs new file mode 100644 index 0000000..cd8f9fb --- /dev/null +++ b/MatchServer/Infra/Config/IServerConfigService.cs @@ -0,0 +1,34 @@ +using CommonInfra; + +public interface IServerConfigService +{ + // 기본 서버 설정 + string GetLogDir(); + bool IsLocalServer(); + string GetServiceType(); + int GetDefaultMaxUser(); + + // 데이터베이스 설정 + string GetSsoAccountDbConnectionString(); + string GetAccountNftDbConnectionString(); + string GetRedisConnectionString(); + MongoDbConfig GetMongoDbConfig(); + + // AWS 설정 + AwsConfig GetAwsConfig(); + bool IsAwsEnabled(); + string GetAwsRegion(); + + // 외부 API 설정 + string GetAiChatBaseAddress(); + string GetBillingBaseAddress(); + UgqConfig GetUgqConfig(); + + // 게임 설정 + GameConfigSettings GetGameConfig(); + bool IsBattleSystemEnabled(); + bool IsCheatCommandAlwaysAllow(); + + // 전체 설정 반환 + ServerConfig GetServerConfig(); +} diff --git a/MatchServer/Infra/Config/ServerConfig.cs b/MatchServer/Infra/Config/ServerConfig.cs new file mode 100644 index 0000000..b289e37 --- /dev/null +++ b/MatchServer/Infra/Config/ServerConfig.cs @@ -0,0 +1,714 @@ +namespace CommonInfra; + +/// +/// 서버 전체 설정을 관리하는 구성 클래스 +/// JSON 파일(ServerConfig-XXX.json 등)로부터 로드되는 서버 운영에 필요한 모든 설정 정보를 포함 +/// +public class ServerConfig +{ + /// + /// 로그 파일이 저장될 디렉토리 경로 + /// 예: "./Logs" + /// + public string LogDir { get; set; } = string.Empty; + + /// + /// 덤프 파일이 저장될 디렉토리 경로 + /// 예: "./" + /// + public string DumpDir { get; set; } = string.Empty; + + /// + /// 로컬 서버 모드 여부 + /// true인 경우 로컬 개발 환경에서 실행됨을 의미 + /// + public bool LocalServer { get; set; } + + /// + /// 단일 스레드 모드 여부 + /// true인 경우 서버가 단일 스레드로 동작 + /// + public bool SingleThreaded { get; set; } + + /// + /// 서비스 타입 (예: "Dev", "Prod", "Test") + /// 환경별 구분을 위한 식별자 + /// + public string ServiceType { get; set; } = string.Empty; + + /// + /// 독립 실행 모드 여부 + /// true인 경우 서버가 독립적으로 실행됨 + /// + public bool StandaloneMode { get; set; } + + /// + /// 오프라인 모드 여부 + /// true인 경우 네트워크 연결 없이 동작 + /// + public bool OfflineMode { get; set; } + + /// + /// 기본 최대 사용자 수 + /// 예: 500명 + /// + public int DefaultMaxUser { get; set; } + + /// + /// 클라이언트 리스닝 설정 + /// 클라이언트 연결을 받을 IP와 포트 정보 + /// + public ClientListenConfig ClientListen { get; set; } = new(); + + /// + /// 세션 유지 시간 (초 단위) + /// 예: 3600초 (1시간) + /// + public int SessionKeepAliveTimeSec { get; set; } + + /// + /// 최소 워커 스레드 개수 + /// 예: 100개 + /// + public int MinWorkerThreadCount { get; set; } + + /// + /// 최소 I/O 스레드 개수 + /// 예: 0개 (자동 설정) + /// + public int MinIoThreadCount { get; set; } + + /// + /// 계정 로그인 차단 기능 활성화 여부 + /// true인 경우 특정 조건하에 로그인 차단 + /// + public bool AccountLoginBlockEnable { get; set; } + + /// + /// SSO 계정 데이터베이스 연결 문자열 + /// MySQL 서버 정보를 포함한 연결 문자열 + /// + public string SsoAccountDb { get; set; } = string.Empty; + + /// + /// SSO 계정 인증을 위한 JWT 비밀 키 + /// 토큰 생성 및 검증에 사용되는 비밀 키 + /// + public string SsoAccountAuthJwtSecretKey { get; set; } = string.Empty; + + /// + /// 계정 NFT 데이터베이스 연결 문자열 + /// NFT 관련 계정 정보 저장용 데이터베이스 + /// + public string AccountNftDb { get; set; } = string.Empty; + + /// + /// Redis 서버 연결 문자열 + /// 캐싱 및 세션 저장을 위한 Redis 서버 설정 + /// + public string Redis { get; set; } = string.Empty; + + /// + /// DynamoDB 서버 URL + /// AWS DynamoDB 또는 로컬 DynamoDB 서버 주소 + /// 예: "http://localhost:8000" + /// + public string Dynamodb { get; set; } = string.Empty; + + /// + /// MongoDB 연결 설정 + /// MongoDB 서버 연결 정보 및 풀 설정 + /// + public MongoDbConfig MongoDb { get; set; } = new(); + + /// + /// AWS 서비스 관련 설정 + /// AWS 액세스 키, CloudWatch, S3 등의 설정 + /// + public AwsConfig AWS { get; set; } = new(); + + /// + /// 클라이언트 프로그램 버전 체크 여부 + /// true인 경우 클라이언트 버전을 검증 + /// + public bool ClientProgramVersionCheck { get; set; } + + /// + /// 클라이언트 최소 요구 로직 버전 + /// 최소한으로 요구되는 클라이언트 로직 버전 번호 + /// + public int ClientMinimumRequiredLogicVersion { get; set; } + + /// + /// 프로그램 버전 파일들이 위치한 경로 + /// 예: ".\\Version\\" + /// + public string ProgramVersionPath { get; set; } = string.Empty; + + /// + /// 프로그램 버전별 파일명 설정 + /// 각 버전 타입별 JSON 파일명 정의 + /// + public ProgramVersionConfig ProgramVersion { get; set; } = new(); + + /// + /// 치트 명령어 항상 허용 여부 + /// true인 경우 개발/디버깅용 치트 명령어가 항상 활성화 + /// + public bool CheatCommandAlwaysAllow { get; set; } + + /// + /// 인증 규칙 설정 + /// 클라이언트 인증 방식 및 플랫폼 허용 규칙 + /// + public AuthRuleConfig AuthRule { get; set; } = new(); + + /// + /// 로드 밸런싱 규칙 설정 + /// 인증 서버에서 게임 서버로의 부하 분산 규칙 + /// + public LoadBalancingRuleConfig LoadBalancingRule { get; set; } = new(); + + /// + /// 서버 API URL 카탈로그 + /// 언어별 다양한 외부 API 서버 URL 목록 + /// 빌링, AI 채팅, S3 리소스 등의 URL 포함 + /// + public List>> ServerApiUrlCatalog { get; set; } = new(); + + /// + /// NFT 관련 규칙 설정 + /// NFT 데이터베이스 접근 및 API URL 설정 + /// + public NftRuleConfig NftRule { get; set; } = new(); + + /// + /// 에코 시스템 설정 + /// 외부 에코 시스템 서버 및 Slack 알림 설정 + /// + public EchoSystemConfig EchoSystem { get; set; } = new(); + + /// + /// AI 채팅 서비스 설정 + /// AI 채팅 API 서버 주소 및 암호화 키 + /// + public AIChatConfig AIChat { get; set; } = new(); + + /// + /// 빌링 서비스 설정 + /// 결제 관련 API 서버 주소 + /// + public BillingConfig Billing { get; set; } = new(); + + /// + /// UGQ(User Generated Quest) 설정 + /// UGQ API 서버 주소 및 인게임 URL 접두사 + /// + public UgqConfig Ugq { get; set; } = new(); + + /// + /// RabbitMQ 메시지 큐 설정 + /// 메시지 큐 서버 연결 정보 + /// + public RabbitmqConfig Rabbitmq { get; set; } = new(); + + /// + /// 게임 설정 + /// 예약 대기 시간, 로그인 캐시 만료 시간 등 + /// + public GameConfigSettings GameConfig { get; set; } = new(); + + /// + /// 제어 에이전트 활성화 여부 + /// 서버 제어 및 모니터링 에이전트 사용 여부 + /// + public bool ControlAgentEnable { get; set; } + + /// + /// 성능 체크 활성화 여부 + /// 서버 성능 모니터링 기능 사용 여부 + /// + public bool PerformanceCheckEnable { get; set; } + + /// + /// 배틀 시스템 활성화 여부 + /// 게임 내 배틀 시스템 사용 여부 + /// + public bool BattleSystemEnable { get; set; } + + /// + /// 메타버스 브로커 설정 + /// 메타버스 브로커 서비스 JWT 키 및 데이터베이스 설정 + /// + public MetaverseBrokerConfig MetaverseBroker { get; set; } = new(); +} + +/// +/// 클라이언트 리스닝 설정 +/// 클라이언트 연결을 받기 위한 네트워크 설정 +/// +public class ClientListenConfig +{ + /// + /// 리스닝할 IP 주소 + /// 빈 문자열인 경우 모든 인터페이스에서 리스닝 + /// + public string Ip { get; set; } = string.Empty; + + /// + /// 리스닝할 포트 번호 + /// 0인 경우 자동으로 포트 할당 + /// + public int Port { get; set; } +} + +/// +/// MongoDB 연결 설정 +/// MongoDB 서버 연결 및 커넥션 풀 설정 +/// +public class MongoDbConfig +{ + /// + /// MongoDB 연결 문자열 + /// 예: "mongodb://root:root@localhost:27017?authSource=admin" + /// + public string ConnectionString { get; set; } = string.Empty; + + /// + /// 사용할 데이터베이스명 + /// 예: "Metaverse-Db-Dev" + /// + public string DatabaseName { get; set; } = string.Empty; + + /// + /// 최소 커넥션 풀 크기 + /// 예: 0 (필요에 따라 동적 생성) + /// + public int MinConnectionPoolSize { get; set; } + + /// + /// 최대 커넥션 풀 크기 + /// 예: 100개 + /// + public int MaxConnectionPoolSize { get; set; } + + /// + /// 대기 큐 타임아웃 시간 (초) + /// 예: 120초 + /// + public int WaitQueueTimeoutSecs { get; set; } +} + +/// +/// AWS 서비스 설정 +/// AWS 액세스 키, 리전, CloudWatch, S3 등의 설정 +/// +public class AwsConfig +{ + /// + /// AWS 서비스 사용 여부 + /// false인 경우 AWS 기능 비활성화 + /// + public bool Enable { get; set; } + + /// + /// AWS 액세스 키 ID + /// AWS API 호출을 위한 인증 키 + /// + public string AccessKey { get; set; } = string.Empty; + + /// + /// AWS 비밀 액세스 키 + /// AWS API 호출을 위한 비밀 키 + /// + public string SecretKey { get; set; } = string.Empty; + + /// + /// AWS 리전 + /// 예: "us-west-2" + /// + public string Region { get; set; } = string.Empty; + + /// + /// 마일스톤 이름 + /// 버전 관리를 위한 마일스톤 식별자 + /// 예: "MS5" + /// + public string MilestoneName { get; set; } = string.Empty; + + /// + /// 로컬 DynamoDB 사용 여부 + /// true인 경우 로컬 DynamoDB 서버 사용 + /// + public bool LocalDynamoDB { get; set; } + + /// + /// CloudWatch 로그 설정 + /// AWS CloudWatch 로그 서비스 설정 + /// + public CloudWatchLogConfig CloudWatchLog { get; set; } = new(); + + /// + /// S3 스토리지 설정 + /// AWS S3 버킷 및 폴더 설정 + /// + public S3Config S3 { get; set; } = new(); +} + +/// +/// CloudWatch 로그 설정 +/// AWS CloudWatch 로그 서비스 관련 설정 +/// +public class CloudWatchLogConfig +{ + /// + /// CloudWatch 로그 사용 여부 + /// + public bool Enable { get; set; } + + /// + /// CloudWatch 로그 그룹명 + /// 예: "MetaverseLog-Dev" + /// + public string LogGroup { get; set; } = string.Empty; + + /// + /// 로그 이름 패턴 + /// 예: "Developer" + /// + public string LogNamePattern { get; set; } = string.Empty; + + /// + /// 로그 레벨 + /// 예: "Debug", "Info", "Error" 등 + /// + public string LogLevel { get; set; } = string.Empty; + + /// + /// 로그 레이아웃 설정 + /// 로그 출력 형식을 정의하는 키-값 쌍 + /// + public Dictionary Layout { get; set; } = new(); +} + +/// +/// S3 스토리지 설정 +/// AWS S3 버킷 및 서비스 폴더 설정 +/// +public class S3Config +{ + /// + /// 서비스 폴더명 + /// 예: "Dev" + /// + public string ServiceFolderName { get; set; } = string.Empty; + + /// + /// 마이홈 UGC 정보 버킷명 + /// 사용자 생성 콘텐츠 저장용 S3 버킷 + /// + public string MyhomeUgcInfoBucketName { get; set; } = string.Empty; + + /// + /// 비콘 앱 프로필 버킷명 + /// 앱 프로필 정보 저장용 S3 버킷 + /// + public string BeaconAppProfileBucketName { get; set; } = string.Empty; +} + +/// +/// 프로그램 버전 설정 +/// 각 구성 요소별 버전 파일명 정의 +/// +public class ProgramVersionConfig +{ + /// + /// 메타 스키마 버전 파일명 + /// 예: "MetaSchemaVersion.json" + /// + public string MetaSchemaVersion { get; set; } = string.Empty; + + /// + /// 메타 데이터 버전 파일명 + /// 예: "MetaDataVersion.json" + /// + public string MetaDataVersion { get; set; } = string.Empty; + + /// + /// 패킷 버전 파일명 + /// 예: "PacketVersion.json" + /// + public string PacketVersion { get; set; } = string.Empty; + + /// + /// 리소스 버전 파일명 + /// 예: "ServerResourceVersion.json" + /// + public string ResourceVersion { get; set; } = string.Empty; + + /// + /// 설정 버전 파일명 + /// 예: "ServerConfigVersion.json" + /// + public string ConfigVersion { get; set; } = string.Empty; + + /// + /// 로직 버전 파일명 + /// 예: "ServerBinaryVersion.json" + /// + public string LogicVersion { get; set; } = string.Empty; + + /// + /// 데이터베이스 스키마 버전 파일명 + /// 예: "DbSchemaVersion.json" + /// + public string DbSchemaVersion { get; set; } = string.Empty; +} + +/// +/// 인증 규칙 설정 +/// 클라이언트 인증 방식 및 플랫폼 제한 규칙 +/// +public class AuthRuleConfig +{ + /// + /// 클라이언트 독립 실행 허용 여부 + /// true인 경우 런처 없이 클라이언트 직접 실행 허용 + /// + public bool ClientStandaloneAllow { get; set; } + + /// + /// 런처를 통한 SSO 계정 인증 허용 여부 + /// true인 경우 런처를 통한 SSO 인증 활성화 + /// + public bool ClientBySsoAccountAuthWithLauncherAllow { get; set; } + + /// + /// 허용할 플랫폼 타입들 + /// 빈 문자열인 경우 모든 플랫폼 허용 + /// + public string PlatformTypeAllows { get; set; } = string.Empty; +} + +/// +/// 로드 밸런싱 규칙 설정 +/// 서버 간 부하 분산 규칙 정의 +/// +public class LoadBalancingRuleConfig +{ + /// + /// 인증 서버에서 게임 서버로의 부하 분산 규칙 + /// + public AuthToGameRuleConfig AuthToGameRule { get; set; } = new(); +} + +/// +/// 인증-게임 서버 간 부하 분산 규칙 +/// 인증 서버에서 게임 서버로 사용자를 분배하는 규칙 +/// +public class AuthToGameRuleConfig +{ + /// + /// 부하 분산 알고리즘 + /// 예: "WeightedRoundRobin" + /// + public string Rule { get; set; } = string.Empty; + + /// + /// 최소 가중치 비율 (%) + /// 예: 0% + /// + public int MinRate { get; set; } + + /// + /// 최대 가중치 비율 (%) + /// 예: 100% + /// + public int MaxRate { get; set; } + + /// + /// 사용자 언어 기반 분배 여부 + /// true인 경우 사용자 언어에 따라 서버 분배 + /// + public bool UserLanguageBased { get; set; } +} + +/// +/// NFT 관련 규칙 설정 +/// NFT 데이터베이스 접근 및 API 설정 +/// +public class NftRuleConfig +{ + /// + /// NFT 데이터베이스 접근 허용 여부 + /// false인 경우 NFT 관련 기능 비활성화 + /// + public bool NftDBAccess { get; set; } + + /// + /// 소유자별 NFT 조회 API URL + /// CP NFT 정보를 가져오기 위한 외부 API 주소 + /// + public string CPNftForOwnerAllGetUrl { get; set; } = string.Empty; +} + +/// +/// 에코 시스템 설정 +/// 외부 에코 시스템 서비스 및 알림 설정 +/// +public class EchoSystemConfig +{ + /// + /// 에코 시스템 API 서버 주소 + /// 예: "https://eco-system-dev-rollup-admin-api.caliverse.io" + /// + public string BaseAddress { get; set; } = string.Empty; + + /// + /// Slack 웹훅 주소 + /// 알림 전송을 위한 Slack 웹훅 URL + /// + public string SlackAddress { get; set; } = string.Empty; +} + +/// +/// AI 채팅 서비스 설정 +/// AI 채팅 API 서버 연결 및 암호화 설정 +/// +public class AIChatConfig +{ + /// + /// AI 채팅 API 서버 주소 + /// 예: "https://ai-dev-api.caliverse.io" + /// + public string BaseAddress { get; set; } = string.Empty; + + /// + /// AI 채팅 서비스 전용 비밀 키 + /// API 호출 인증을 위한 암호화된 키 + /// + public string PrivateKey { get; set; } = string.Empty; +} + +/// +/// 빌링 서비스 설정 +/// 결제 및 구매 관련 API 서버 설정 +/// +public class BillingConfig +{ + /// + /// 빌링 API 서버 주소 + /// 예: "https://dev-api.caliverse.io" + /// + public string BaseAddress { get; set; } = string.Empty; +} + +/// +/// UGQ(User Generated Quest) 설정 +/// 사용자 생성 퀘스트 시스템 API 설정 +/// +public class UgqConfig +{ + /// + /// UGQ API 서버 주소 + /// 예: "https://dev-ugqapi.caliverse.io:11000" + /// + public string ApiServerAddress { get; set; } = string.Empty; + + /// + /// 인게임 URL 접두사 + /// 게임 내에서 사용할 API 경로 접두사 + /// 예: "/api/v1/InGame" + /// + public string UrlInGamePrefix { get; set; } = string.Empty; +} + +/// +/// RabbitMQ 메시지 큐 설정 +/// 메시지 큐 서버 연결 및 인증 정보 +/// +public class RabbitmqConfig +{ + /// + /// RabbitMQ 서버 호스트명 + /// 예: "localhost" + /// + public string HostName { get; set; } = string.Empty; + + /// + /// RabbitMQ 서버 포트 + /// 예: 5672 + /// + public int Port { get; set; } + + /// + /// RabbitMQ 사용자명 + /// 예: "admin" + /// + public string UserName { get; set; } = string.Empty; + + /// + /// RabbitMQ 비밀번호 + /// 예: "admin" + /// + public string Password { get; set; } = string.Empty; + + /// + /// SSL 연결 사용 여부 + /// true인 경우 SSL/TLS 보안 연결 사용 + /// + public bool SSL { get; set; } +} + +/// +/// 게임 설정 +/// 게임 플레이 관련 타이밍 및 캐시 설정 +/// +public class GameConfigSettings +{ + /// + /// 예약 대기 시간 (밀리초) + /// 게임 예약 시 최대 대기 시간 + /// 예: 1200000ms (20분) + /// + public int ReservationWaitTimeMSec { get; set; } + + /// + /// 로그인 캐시 만료 시간 (밀리초) + /// 로그인 정보 캐시 유지 시간 + /// 예: 3600000ms (1시간) + /// + public int LoginCacheExpiryTimeMSec { get; set; } + + /// + /// 서버 전환 캐시 만료 시간 (밀리초) + /// 서버 전환 정보 캐시 유지 시간 + /// 예: 3600000ms (1시간) + /// + public int ServerSwitchCacheExpiryTimeMSec { get; set; } +} + +/// +/// 메타버스 브로커 설정 +/// 메타버스 브로커 서비스 연결 및 인증 설정 +/// +public class MetaverseBrokerConfig +{ + /// + /// 메타버스 브로커 JWT 비밀 키 + /// JWT 토큰 생성 및 검증을 위한 키 + /// + public string JwtSecretKey { get; set; } = string.Empty; + + /// + /// JWT 토큰 만료 시간 (분) + /// 예: 1440분 (24시간) + /// + public int ExpireMinutes { get; set; } + + /// + /// 메타버스 브로커 데이터베이스 연결 문자열 + /// 브로커 전용 데이터베이스 연결 정보 + /// + public string MetaverseBrokerDb { get; set; } = string.Empty; +} diff --git a/MatchServer/Infra/Config/ServerConfigService.cs b/MatchServer/Infra/Config/ServerConfigService.cs new file mode 100644 index 0000000..7b4948c --- /dev/null +++ b/MatchServer/Infra/Config/ServerConfigService.cs @@ -0,0 +1,40 @@ +// ServerConfigService.cs + +using CommonInfra; +using Microsoft.Extensions.Options; + +public class ServerConfigService : IServerConfigService +{ + private readonly IOptionsSnapshot _serverConfig; + + public ServerConfigService(IOptionsSnapshot serverConfig) + { + _serverConfig = serverConfig; + } + + private ServerConfig Config => _serverConfig.Value; + + public string GetLogDir() => Config.LogDir; + public bool IsLocalServer() => Config.LocalServer; + public string GetServiceType() => Config.ServiceType; + public int GetDefaultMaxUser() => Config.DefaultMaxUser; + + public string GetSsoAccountDbConnectionString() => Config.SsoAccountDb; + public string GetAccountNftDbConnectionString() => Config.AccountNftDb; + public string GetRedisConnectionString() => Config.Redis; + public MongoDbConfig GetMongoDbConfig() => Config.MongoDb; + + public AwsConfig GetAwsConfig() => Config.AWS; + public bool IsAwsEnabled() => Config.AWS.Enable; + public string GetAwsRegion() => Config.AWS.Region; + + public string GetAiChatBaseAddress() => Config.AIChat.BaseAddress; + public string GetBillingBaseAddress() => Config.Billing.BaseAddress; + public UgqConfig GetUgqConfig() => Config.Ugq; + + public GameConfigSettings GetGameConfig() => Config.GameConfig; + public bool IsBattleSystemEnabled() => Config.BattleSystemEnable; + public bool IsCheatCommandAlwaysAllow() => Config.CheatCommandAlwaysAllow; + + public ServerConfig GetServerConfig() => Config; +} diff --git a/MatchServer/MatchServer.csproj b/MatchServer/MatchServer.csproj new file mode 100644 index 0000000..9583b4b --- /dev/null +++ b/MatchServer/MatchServer.csproj @@ -0,0 +1,42 @@ + + + Exe + net8.0 + enable + enable + true + true + Debug;Release;Shipping + 8600,8602,8603,8604 + true + + + full + $(DefineConstants);SEQUENCE + + + full + $(DefineConstants);SEQUENCE + + + full + $(DefineConstants);SEQUENCE + true + + + true + + + + + + + + + + + + + + + diff --git a/MatchServer/MatchServerExtensions.cs b/MatchServer/MatchServerExtensions.cs new file mode 100644 index 0000000..97b5a5b --- /dev/null +++ b/MatchServer/MatchServerExtensions.cs @@ -0,0 +1,89 @@ +using System.Reflection; +using ControlCenter.NamedPipeHost.Extensions; +using ControlCenter.NamedPipeHost.Manager; +using MatchServer; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using NLog.Extensions.Logging; +using ServerBase; +using ServerCommon; +using ServerCore; +using StackExchange.Redis; + +public static class MatchServerExtensions +{ + public static void AddNLog(this HostApplicationBuilder builder) + { + builder.Logging.ClearProviders(); + builder.Logging.AddNLog(Log.NLogFileName); + } + public static void AddMatchServerService(this IServiceCollection services, string[] args) + { + services.AddSingleton(); + services.AddSingleton(x => + { + var server = x.GetRequiredService(); + return server.MatchServerLogic.getServerConfig(); + }); + services.AddSingleton(x => + { + var server = x.GetRequiredService(); + return server.MatchServerLogic.getDynamoDbClient(); + }); + services.AddSingleton(x => + { + var server = x.GetRequiredService(); + return server.MatchServerLogic.getRedisConnector(); + }); + services.AddSingleton(x => + { + var server = x.GetRequiredService(); + return server.MatchServerLogic.getRedisConnector().getDatabase(); + }); + services.AddSingleton(x => + { + var server = x.GetRequiredService(); + return server.MatchServerLogic.getRabbitMqConnector() as RabbitMq4Match; + }); + services.AddTransient(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + // Factories + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + // 인던 서버 정보 + services.AddSingleton(x => x.GetRequiredService()); + // 게임 이벤트 관리 + services.AddSingleton(); + + // BackgroundService + services.AddHostedService(); + } + + public static void UseMatchServerService(this IHost app, string[] args) + { + var server = app.Services.GetRequiredService(); + _ = server.Run(args); + server.WaitOnStart().Wait(); + + var serviceProvider = app.Services; + // Mq의 핸들러에 강제 주입을 위해 정의함 + ServiceProviderHelper.SetServiceProvider(serviceProvider); + + // 호스트 시작 종료 콜백 처리 + serviceProvider.GetRequiredService().ApplicationStarted.Register(() => + { + var logger = Log.getLogger(); + logger.info($"Match Server Service Started"); + }); + serviceProvider.GetRequiredService().ApplicationStopped.Register(Log.shutdown); + } +} diff --git a/MatchServer/MatchServerLogic.cs b/MatchServer/MatchServerLogic.cs new file mode 100644 index 0000000..b244ce4 --- /dev/null +++ b/MatchServer/MatchServerLogic.cs @@ -0,0 +1,325 @@ +using Microsoft.Extensions.Configuration; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; +using MODULE_ID = System.UInt32; +using ServerMetricsHelper = ServerBase.ServerMetricsHelper; + +namespace MatchServer; + +public class MatchServerLogic : ServerLogicBase, IWithPacketNamespaceVerifier, IWithConfiguration, + IWithServerMetrics, IWithLogActor +{ + const string PacketNamespace = "MatchServer.PacketHandler"; + private readonly string _serverConfigPath = "./"; + public Action? OnServerStart { get; set; } + + public MatchServerLogic(ServerConfig config) : base(config) + { + } + + public MatchServerLogic(ServerConfig config, string serverConfigPath) : base(config) + { + _serverConfigPath = serverConfigPath; + } + + public Result mergeConfiguration(IConfiguration configuration) + { + var result = new Result(); + var key_options = configuration; + + var port = ushort.Parse(key_options["port"] ?? "0"); + var config_file = key_options["config"]; + + var server_config = getServerConfig(); + + server_config.setAppParamPort(port); + server_config.setConfigFilePath($"{_serverConfigPath + ServerConfig.ConfigDirectory + "/" + config_file}"); + + return result; + } + + + protected override string onGetDumpFilename() + { + return getServerName() + "_" + getServerConfig().getAppParamPort(); + } + + public override async Task onInit() + { + var result = new Result(); + var err_msg = string.Empty; + + var businesslog_refresh_date = MetaHelper.GameConfigMeta.BusinessLogRefreshTime; + DailyTimeEventManager.Instance.tryAddTask("MatchBusinessLogRefresh", businesslog_refresh_date, onBusinessLogRefresh); + + result = await base.onInit(); + if (result.isFail()) + { + err_msg = $"Failed to onInit() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + return result; + } + + return result; + } + + // 비즈니스 로그가 일일 아카이빙 되도록 강제로 매일 로그를 남긴다. + private async Task onBusinessLogRefresh() + { + await Task.CompletedTask; + + var log_invokers = new List(1); + var empty_business_refresh_with_log_actor = new EmptyBusinessWithLogActor(); + DailyRefreshBusinessLog log = new(); + log_invokers.Add(log); + BusinessLogger.collectLogs(new LogActionEx(LogActionType.TestBusinessLog), + empty_business_refresh_with_log_actor, log_invokers); + Log.getLogger().info("Match EmptyBusinessLog write"); + } + + protected override Result onCreateServerName() + { + var result = new Result(); + var err_msg = string.Empty; + + var server_type_name = getServerType(); + var server_type = server_type_name.toServerType(); + if (false == server_type.isValidServerType()) + { + err_msg = $"ServerType invalid !!! : {server_type.ToString()}"; + result.setFail(ServerErrorCode.ServerTypeInvalid, err_msg); + return result; + } + + var server_config = getServerConfig(); + var listen_port = server_config.toClientListenPort(); + // if (listen_port <= 0) + // { + // err_msg = $"Client listen port invalid !!! : appParamPort:{listen_port}, configListenPort:{server_config.ClientListenPort} - {server_type.ToString()}"; + // result.setFail(ServerErrorCode.ClientListenPortInvalid, err_msg); + // return result; + // } + // + base.setServerName(server_type.toServerName(server_config.toClientListenIP(), listen_port, 0, 0)); + + return result; + } + + protected override Result onLoadMetaDatas() + { + return ServerCommon.ServerLogicHelper.loadMetaDatas(this); + } + + protected override async Task onRegisterModuleAll() + { + var result = new Result(); + + var config = getServerConfig(); + var load_config_info = config.getLoadedConfig(); + NullReferenceCheckHelper.throwIfNull(load_config_info, + () => $"load_config_info is null !!! - {toBasicString()}"); + + { + result = await tryCreateAndRegisterModule(() => + { + var config_param = new DynamoDbClient.ConfigParam(); + result = config_param.tryReadFromJsonOrDefault(load_config_info); + if (result.isFail()) + { + return (null, result); + } + + var module_context = new ModuleContext((MODULE_ID)ModuleId.DynamoDbConnector + , 0, 0, config_param); + var created_module = new DynamoDbClient(module_context); + return (created_module, result); + }); + } + { + result = await tryCreateAndRegisterModule(() => + { + var config_param = new RedisConnector.ConfigParam(); + result = config_param.tryReadFromJsonOrDefault(load_config_info); + if (result.isFail()) + { + return (null, result); + } + + var module_context = new ModuleContext((MODULE_ID)ModuleId.RedisConnector + , 0, 0, config_param); + var created_module = new RedisConnector(module_context); + return (created_module, result); + }); + } + { + result = await tryCreateAndRegisterModule(() => + { + var found_redis_connector = getModule((MODULE_ID)ModuleId.RedisConnector); + if (null == found_redis_connector) + { + return ( + null + , new Result() { ErrorCode = ServerErrorCode.ModuleNotFound, ResultString = $"RedisConnector not found !!! : moduleId:{ModuleId.RedisConnector}" } + ); + } + + var config_param = new RedisWithLuaScriptExecutor.ConfigParam(); + result = config_param.tryReadFromJsonOrDefault(load_config_info); + if (result.isFail()) + { + return (null, result); + } + + var module_context = new ModuleContext((MODULE_ID)ModuleId.RedisWithLuaScriptExecutor + , 0, 0, config_param); + var created_module = new RedisWithLuaScriptExecutor(found_redis_connector, module_context); + + return (created_module, result); + }); + } + { + result = await tryCreateAndRegisterModule(() => + { + var config_param = new RabbitMqConnector.ConfigParam(); + result = config_param.tryReadFromJsonOrDefault(load_config_info); + if (result.isFail()) + { + return (null, result); + } + + config_param.ServiceName = getServerName(); + + var module_context = new ModuleContext((MODULE_ID)ModuleId.RabbitMqConnector + , 0, 0, config_param); + var created_module = new RabbitMq4Match(module_context); + config_param.fnServerMessageRecvFromConsumer = created_module.onRecvProtocol; + + return (created_module, result); + }); + } + return result; + } + + public override async Task onCreateTickerAll() + { + var result = new Result(); + + result = await this.createTimeEventForMinuteTicker(); + if (result.isFail()) + { + Log.getLogger() + .error( + $"Failed to createTimeEventForMinuteTicker() !!! : {result.toBasicString()} - {toBasicString()}"); + return result; + } + + var world_id = 0; + result = await onInitServerKeyToCache(world_id); + if (result.isFail()) + { + return result; + } + + return result; + } + + + public bool isValidPacketNamespace(string? toCheckNamespace, IPacketCommand packetCommand) + { + if (null != toCheckNamespace && true == toCheckNamespace.Contains(PacketNamespace)) + { + return true; + } + + Log.getLogger().error( + $"Invalid MatchServer PacketNamespace !!!, not included Namespace : {PacketNamespace} ⊆ {toCheckNamespace}, packetCommnad:{packetCommand.toBasicString()}"); + return false; + } + + public async Task setupServerMetrics() + { + var result = + await ServerMetricsHelper.setupDefaultServerMetrics(this, + (MODULE_ID)ModuleId.RedisConnector); + if (result.isFail()) + { + return result; + } + + return result; + } + + protected override async Task onStartServer() + { + var result = await base.onStartServer(); + OnServerStart?.Invoke(); + return result; + } + + public bool isSetupCompleted() + { + return ServerMetricsHelper.isSetupCompleted(this); + } + + public ServerMetricsCacheRequest getServerMetricsCacheRequest() + { + return ServerBase.ServerMetricsHelper.getServerMetricsCacheRequest(this); + } + + public override async Task onTaskServerInfoSyncTick() + { + var result = await syncServerInfoToCache(); + if (result.isFail()) + { + Log.getLogger() + .error($"Failed to syncServerInfoToCache() !!! : {result.toBasicString()} - {toBasicString()}"); + } + } + + public async Task syncServerInfoToCache() + { + var result = new Result(); + var server_name = getServerName(); + var server_config = getServerConfig(); + var listen_ip = server_config.toClientListenIP(); + var listen_port = server_config.toClientListenPort(); + // TODO: 카운트 정보 처리 + var user_count = 0; + var max_user_count = 0; + var reservation_count = 0; + var ugc_npc_count = 0; + var return_count = 0; + var aws_instance_id = getInstanceId(); + + result = await ServerBase.ServerMetricsHelper.syncServerInfoToCache(this + , server_name + , listen_ip, listen_port + , user_count, max_user_count + , reservation_count, return_count + , ugc_npc_count + , aws_instance_id); + if (result.isFail()) + { + return result; + } + + return result; + } + + public ILogActor toLogActor() + { + var log_info = new LogicActorLog(); + log_info.initLogInfo( + this.getServerConfig().getRegionId() + , this.getServerConfig().getWorldId() + , this.getServerType().toServerType() + , this.getServerName() + ); + return log_info; + } + + public int getWorldId() => 0; + public int getChannelNo() => 0; +} diff --git a/MatchServer/MatchServerService.cs b/MatchServer/MatchServerService.cs new file mode 100644 index 0000000..347b8cf --- /dev/null +++ b/MatchServer/MatchServerService.cs @@ -0,0 +1,124 @@ +using CommandLine; +using ControlCenter.NamedPipe.Model; +using MatchServer; +using Microsoft.Extensions.Configuration; +using ServerBase; +using ServerCore; + +public class ServiceProviderHelper +{ + public static IServiceProvider? Services { get; private set; } + + public static void SetServiceProvider(IServiceProvider serviceProvider) + { + Services = serviceProvider; + } +} + +public interface IServerInfoProvider +{ + Task> GetServerInfosByServerType(ServerType serverType); +} + +/// +/// 매칭 로직을 서비스로 실행한다. +/// +public class MatchServerService : IServerInfoProvider +{ + private readonly NLog.Logger _logger; + + public class CmdOptions + { + [Option("config", Default = "ServerConfig.json")] + public string Config { get; set; } = "ServerConfig.json"; + } + + private MatchServerLogic? _serverLogic; + private readonly CancellationTokenSource _onStartCts = new(); + public MatchServerLogic MatchServerLogic => _serverLogic!; + + public MatchServerService() + { + _logger = Log.getLogger(); + _logger.info($"{nameof(MatchServerService)} Start" ); + } + + public async Task> GetServerInfosByServerType(ServerType serverType) + { + var (result, serverInfos) = await MatchServerLogic.getServerInfosByServerType(serverType); + return result.isFail() ? [] : serverInfos; + } + + public async Task Run(string[] args) + { + var result = new Result(); + + _logger.info($"GameServer Run"); + + var name = nameof(MatchServerService); + + try + { + result = await Parser.Default.ParseArguments(args).MapResult( + async (CmdOptions opts) => await RunServer(opts), errors => Task.FromResult(result) + ); + } + catch (Exception e) + { + var err_msg = $"Exception !!!, {name}.runServer() : message:{e}"; + result.setFail(ServerErrorCode.TryCatchException, err_msg); + } + finally + { + if (result.isFail()) + { + Log.getLogger().error($"{name} finally error !!! - {result.toBasicString()}"); + } + } + + _logger.info($"{name} terminated"); + Log.shutdown(); + return 0; + } + + private async Task RunServer(CmdOptions argCmdOptions) + { + var result = new Result(); + + var key_options = new Dictionary { { "config", argCmdOptions.Config }, }; + + var configuration = new ConfigurationBuilder().AddInMemoryCollection(key_options!).Build(); + NullReferenceCheckHelper.throwIfNull(configuration, () => $"configuration is null !!!"); + var server_config = new ServerConfig(); + + _serverLogic = new MatchServerLogic(server_config); + _serverLogic.setServerType(nameof(ServerType.Match)); + _serverLogic.setConfiguration(configuration); + _serverLogic.OnServerStart = () => + { + _onStartCts.Cancel(); + }; + + var namedOptionBuilder = new NamedPipeClientOptionBuilder(); + namedOptionBuilder.setIP(NetworkHelper.getEthernetLocalIPv4()) + .setPort(0) + .setType(nameof(ServerType.Match)) + .setServiceCategory(nameof(ServiceCategory.Caliverse)); + + result = await _serverLogic.onRunServer(namedOptionBuilder); + if (result.isFail()) + { + return result; + } + + return result; + } + + public async Task WaitOnStart() + { + while (!_onStartCts.IsCancellationRequested) + { + await Task.Delay(10); + } + } +} diff --git a/MatchServer/NamedPipePacketHandler/ForceStopServerPacketHandler.cs b/MatchServer/NamedPipePacketHandler/ForceStopServerPacketHandler.cs new file mode 100644 index 0000000..fe714f8 --- /dev/null +++ b/MatchServer/NamedPipePacketHandler/ForceStopServerPacketHandler.cs @@ -0,0 +1,33 @@ +using ControlCenter.NamedPipe; +using ControlCenter.NamedPipeHost.Manager; +using ServerBase; +using ServerControlCenter; + +using ServerCore; + + +namespace MatchServer; + +public class ForceStopServerMessageReceiver : NamedPipeReceiver +{ + // private readonly ServerInfoManager m_info_manager; + + public ForceStopServerMessageReceiver() + { + // m_info_manager = infoManager; + } + + public override async Task Handle(A2S_REQ_FORCE_STOP_SERVER message, string message_id) + { + Log.getLogger().debug($"{nameof(ForceStopServerMessageReceiver)}: Receive - message_id[{message_id}] / message[{message}]"); + + await NamedPipeMonitor.ChangeServerStatus(ServerStatus.Stop); + // m_info_manager.setServerStatus(ServerStatus.Stop); + + // 정보 전달 대기 + await Task.Delay(1_000); + + // process 종료 + Environment.Exit(0); + } +} diff --git a/MatchServer/NamedPipePacketHandler/StopServerPacketHandler.cs b/MatchServer/NamedPipePacketHandler/StopServerPacketHandler.cs new file mode 100644 index 0000000..0b7243d --- /dev/null +++ b/MatchServer/NamedPipePacketHandler/StopServerPacketHandler.cs @@ -0,0 +1,27 @@ +using ControlCenter.NamedPipe; +using ControlCenter.NamedPipeHost.Manager; +using ServerBase; +using ServerControlCenter; +using ServerCore; + +namespace MatchServer; + +public class StopServerMessageReceiver : NamedPipeReceiver +{ + public StopServerMessageReceiver() + { + } + + public override async Task Handle(A2S_REQ_STOP_SERVER message, string message_id) + { + Log.getLogger().debug($"{nameof(StopServerMessageReceiver)}: Receive - message_id[{message_id}] / message[{message}]"); + + await NamedPipeMonitor.ChangeServerStatus(ServerStatus.Stop); + + // 정보 전달 대기 + await Task.Delay(1_000); + + // process 종료 + Environment.Exit(0); + } +} diff --git a/MatchServer/Program.cs b/MatchServer/Program.cs new file mode 100644 index 0000000..85fcf95 --- /dev/null +++ b/MatchServer/Program.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Hosting; + +var builder = new HostApplicationBuilder(); +builder.AddNLog(); +builder.Services.AddMatchServerService(args); + +IHost app = builder.Build(); +app.UseMatchServerService(args); +app.Run(); diff --git a/MatchServer/Services/BusinessLog/LogActor/MatchLogActor.cs b/MatchServer/Services/BusinessLog/LogActor/MatchLogActor.cs new file mode 100644 index 0000000..dfb16e8 --- /dev/null +++ b/MatchServer/Services/BusinessLog/LogActor/MatchLogActor.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; +using ServerBase; +using ServerCommon; + +namespace MatchServer; + +public class MatchLogActor : IWithLogActor +{ + private readonly ILogActor _logActor; + + public class LogActor : ILogActor + { + [JsonProperty] public string ServerName { get; init; } = nameof(ServerType.Match); + [JsonProperty] public int GameModeId { get; set; } + [JsonProperty] public int EventId { get; set; } + [JsonProperty] public required string Region { get; set; } + [JsonProperty] public required string MatchGroupId { get; set; } + } + + public MatchLogActor(MatchPoolInfo matchPoolInfo) + { + _logActor = new LogActor + { + GameModeId = matchPoolInfo.GameModeId, + EventId = matchPoolInfo.GameEventId, + Region = matchPoolInfo.Region, + MatchGroupId = matchPoolInfo.MatchGroupId + }; + } + + public ILogActor toLogActor() + { + return _logActor; + } +} diff --git a/MatchServer/Services/Factories/InstanceRoomRepo.cs b/MatchServer/Services/Factories/InstanceRoomRepo.cs new file mode 100644 index 0000000..d57eaeb --- /dev/null +++ b/MatchServer/Services/Factories/InstanceRoomRepo.cs @@ -0,0 +1,206 @@ +using System.Text.Json.Serialization; +using Google.Protobuf.WellKnownTypes; +using Newtonsoft.Json; +using ServerBase; +using ServerCommon; +using ServerCore; +using StackExchange.Redis; + +namespace MatchServer; + +public class InstanceRoomRepo +{ + private readonly IMatchMetaManager _matchMetaManager; + private readonly GameEventManager _gameEventManager; + private readonly IServerInfoProvider _serverInfoProvider; + private readonly GameInstanceRoomStorage _instanceRoomStorage; + private readonly IDatabase _redisDb; + private readonly NLog.Logger _logger; + + public InstanceRoomRepo( + IMatchMetaManager matchMetaManager, + GameEventManager gameEventManager, + IServerInfoProvider serverInfoProvider, + GameInstanceRoomStorage instanceRoomStorage, + IDatabase redisDb) + { + _matchMetaManager = matchMetaManager; + _gameEventManager = gameEventManager; + _serverInfoProvider = serverInfoProvider; + _redisDb = redisDb; + + _instanceRoomStorage = instanceRoomStorage; + _instanceRoomStorage.Init(_redisDb, ""); + _logger = Log.getLogger(); + } + + public async Task UpdateGameRoomInfo(MatchRoom matchRoom) + { + var gameEvent = GetGameEvent(matchRoom.MatchPoolInfo.GameEventId); + var gameRoomInfo = ConvertMatchRoom2GameRoomInfo(matchRoom, gameEvent); + return await _instanceRoomStorage.SetInstanceRoomGameInfoAsync(matchRoom.Id, gameRoomInfo); + } + + public async Task RemoveGameRoomInfo(string roomId) + { + return await _instanceRoomStorage.DeleteGameRoomInfoAsync(roomId); + } + + private GameEvent? GetGameEvent(int gameEventId) + { + GameEvent gameEvent = null; + if (gameEventId != 0) + { + gameEvent = _gameEventManager.GetGameEvent(gameEventId); + } + + return gameEvent; + } + + public async Task<(Result, InstanceRoomInfo?)> CreateInstanceGameRoom( + MatchRoom matchRoom) + { + var result = new Result(); + try + { + var storage = _instanceRoomStorage; + + var indunServers = await _serverInfoProvider.GetServerInfosByServerType(ServerType.Indun); + + var roomCapacity = matchRoom.MatchPolicy.MaxTotalMemberCount; + (result, var serverInfo) = GetBestInstanceServerForCreateInstance(indunServers.ToArray(), roomCapacity); + MatchGuard.ResultFailException(result); + + var eventId = matchRoom.MatchPoolInfo.GameEventId; + GameEvent gameEvent = null; + if (eventId != 0) + { + gameEvent = _gameEventManager.GetGameEvent(matchRoom.MatchPoolInfo.GameEventId); + if (gameEvent is null) + { + _logger.warn($"Game Event Not Found !!! : eventId:{eventId}"); + } + } + + var instanceRoomInfo = new InstanceRoomInfo + { + roomId = matchRoom.Id, + InstanceAddress = serverInfo.Address, + InstancePort = serverInfo.Port, + InstanceId = matchRoom.InstanceMetaId, + UgcNpcCount = 0, + InstancePlaceType = matchRoom.InstanceMetaType, + GameModeId = matchRoom.MatchPoolInfo.GameModeId, + GameEventId = matchRoom.MatchPoolInfo.GameEventId, + InstanceStartTime = DateTime.UtcNow.ToTimestamp() + }; + + result = await storage.CreateInstanceGameRoom(instanceRoomInfo); + MatchGuard.ResultFailException(result); + + var gameRoomInfo = ConvertMatchRoom2GameRoomInfo(matchRoom, gameEvent); + MatchGuard.NotNull(gameRoomInfo); + + result = await storage.SetInstanceRoomGameInfoAsync(matchRoom.Id, gameRoomInfo); + MatchGuard.ResultFailException(result); + + // 룸 생성 확인 + (result, var gameRoomInfoGet) = await storage.GetInstanceRoomGameInfoAsync(matchRoom.Id); + MatchGuard.ResultFailException(result); + MatchGuard.NotNull(gameRoomInfoGet); + + try + { + var gameInfoStr = JsonConvert.SerializeObject(gameRoomInfo, Formatting.Indented); + _logger.Info($"CreateInstanceGameRoom{Environment.NewLine} {gameInfoStr}"); + } + catch (Exception ex) + { + _logger.Info("Fail => JsonConvert.SerializeObject(gameRoomInfo)"); + } + + return (result, instanceRoomInfo); + } + catch (ResultException ex) + { + result = ex.Result; + } + catch (Exception e) + { + // ignored + if (result.isSuccess()) + { + result.setFail(ServerErrorCode.TryCatchException, $"Exception From CreateInstanceGameRoom => {e}"); + } + } + + return (result, null); + } + + private (Result, ServerInfo?) GetBestInstanceServerForCreateInstance( + ServerInfo[] indunServers, + int roomCapacity) + { + string errMsg; + var result = new Result(); + + if (indunServers.Length == 0) + { + errMsg = $"Not Exist Indun Server !!!"; + result.setFail(ServerErrorCode.ValidServerNotFound, errMsg); + Log.getLogger().Error(result.toBasicString()); + + return (result, null); + } + + int selectedServerIndex = + LoadBalanceServerHelper.getBestInstanceServerIndexForCreate(indunServers.ToList(), roomCapacity); + if (selectedServerIndex == -1) + { + foreach (var serverInfo in indunServers) + { + Log.getLogger().info( + $"IndunServer Capacity Info !!! : serverName:{serverInfo.Name}, serverCapacity:{serverInfo.Capacity}, serverRoomCapacity:{serverInfo.RoomCapacity}"); + } + + errMsg = $"Not Found Enough Capacity Indun Server !!! : tryCreateRoomCapacity:{roomCapacity}"; + result.setFail(ServerErrorCode.ValidServerNotFound, errMsg); + Log.getLogger().error(result.toBasicString()); + + return (result, null); + } + + return (result, indunServers[selectedServerIndex]); + } + + /// + /// MatchRoom 정보를 이용하여 GameRoomInfo를 생성함 + /// + /// 변환할 MatchRoom 객체 + /// 이벤트 매니저에 등록된 이벤트 정보 + /// 생성된 GameRoomInfo 객체 + public GameRoomInfo ConvertMatchRoom2GameRoomInfo(MatchRoom matchRoom, GameEvent? gameEvent) + { + return new GameRoomInfo + { + Id = matchRoom.Id, + PolicyVersion = matchRoom.MatchPolicy.Version ?? string.Empty, + GameModeId = matchRoom.MatchPolicy.GameModeId, + MatchGroupId = matchRoom.MatchGroupId, + InstanceMetaId = matchRoom.InstanceMetaId, + Teams = matchRoom.Teams.SelectMany(team => team.Members.Select(member => new GameRoomInfo.TeamInfo + { + Idx = team.Idx, UserGuid = member.UserInfo.UserGuid, NickName = member.UserInfo.Nickname + })).ToArray(), + TeamPolicies = matchRoom.MatchPolicy.TeamPolicies, + MinTeamCount = matchRoom.MatchPolicy.MinTeamCount, + MaxTeamCount = matchRoom.MatchPolicy.MaxTeamCount, + + // 이벤트 관련 + GameEventId = matchRoom.MatchPolicy.GameEventId, + HotTimeEvent = gameEvent?.FfaHotTime ?? 1, + EventStartTime = gameEvent?.StartTime ?? DateTime.MinValue, + EventEndTime = gameEvent?.EndTime ?? DateTime.MaxValue, + }; + } +} diff --git a/MatchServer/Services/Factories/MatchGroupProcessorFactory.cs b/MatchServer/Services/Factories/MatchGroupProcessorFactory.cs new file mode 100644 index 0000000..827b1a6 --- /dev/null +++ b/MatchServer/Services/Factories/MatchGroupProcessorFactory.cs @@ -0,0 +1,65 @@ +using MatchServer; +using NLog; +using ServerCommon; +using ServerCore; +using StackExchange.Redis; + +public class MatchGroupProcessorFactory : IMatchGroupProcessorFactory +{ + private readonly MatchUserRepository _matchUserRepository; + private readonly MatchRoomRepository _matchRoomRepository; + private readonly IMatchMetaManager _matchMetaManager; + private readonly GameEventManager _gameEventManager; + private readonly MatchServerService _matchServerService; + private readonly InstanceRoomRepo _instanceRoomRepo; + private readonly IMatchProcessorFactory _matchProcessorFactory; + private readonly IMatchMq _mq; + private readonly IDatabase _redisDb; + + public MatchGroupProcessorFactory( + MatchUserRepository matchUserRepository, + MatchRoomRepository matchRoomRepository, + IMatchMetaManager matchMetaManager, + GameEventManager gameEventManager, + MatchServerService matchServerService, + InstanceRoomRepo instanceRoomRepo, + IMatchProcessorFactory matchProcessorFactory, + IMatchMq mq, + IDatabase redisDb) + { + _matchUserRepository = matchUserRepository; + _matchRoomRepository = matchRoomRepository; + _matchMetaManager = matchMetaManager; + _gameEventManager = gameEventManager; + _matchServerService = matchServerService; + _instanceRoomRepo = instanceRoomRepo; + _matchProcessorFactory = matchProcessorFactory; + _mq = mq; + _redisDb = redisDb; + } + + public MatchGroupProcessor CreateMatchGroupProcessor(string matchGroupId) + { + return new MatchGroupProcessor( + matchGroupId, + _matchUserRepository, + _matchRoomRepository, + _matchMetaManager, + _matchProcessorFactory, + _instanceRoomRepo, + _mq); + } + + public MatchGroupProcessor CreateMatchGroupProcessor(MatchPoolInfo matchPoolInfo, MatchPolicy matchPolicy) + { + return new MatchGroupProcessor( + matchPoolInfo, + matchPolicy, + _matchUserRepository, + _matchRoomRepository, + _matchMetaManager, + _matchProcessorFactory, + _instanceRoomRepo, + _mq); + } +} diff --git a/MatchServer/Services/Factories/MatchProcessorFactory.cs b/MatchServer/Services/Factories/MatchProcessorFactory.cs new file mode 100644 index 0000000..0ed0fe1 --- /dev/null +++ b/MatchServer/Services/Factories/MatchProcessorFactory.cs @@ -0,0 +1,17 @@ +using MatchServer; +using ServerCommon; + +public class MatchProcessorFactory : IMatchProcessorFactory +{ + private readonly IMatchRoomFactory _roomFactory; + + public MatchProcessorFactory(IMatchRoomFactory roomFactory) + { + _roomFactory = roomFactory; + } + + public MatchProcessor Create(MatchPoolInfo poolInfo, MatchPolicy policy) + { + return new MatchProcessor(poolInfo, policy, _roomFactory); + } +} diff --git a/MatchServer/Services/Factories/MatchRoomFactory.cs b/MatchServer/Services/Factories/MatchRoomFactory.cs new file mode 100644 index 0000000..9aa5f22 --- /dev/null +++ b/MatchServer/Services/Factories/MatchRoomFactory.cs @@ -0,0 +1,23 @@ +using MatchServer; +using ServerCommon; + +public class MatchRoomFactory : IMatchRoomFactory +{ + private readonly IMatchMetaManager _matchMetaManager; + + public MatchRoomFactory(IMatchMetaManager matchMetaManager) + { + _matchMetaManager = matchMetaManager; + } + + public MatchRoom CreateRoom(MatchPoolInfo matchPoolInfo, MatchPolicy matchPolicy) + { + var instanceMetaData = _matchMetaManager.GetInstanceMetaByRandomWithGameMode(matchPoolInfo.GameModeId); + return new MatchRoom(createRoomId(), matchPoolInfo, matchPolicy, instanceMetaData.Id); + + string createRoomId() + { + return $"{matchPoolInfo.MatchGroupId}:{Ulid.NewUlid()}"; + } + } +} diff --git a/MatchServer/Services/GameEventCheckWorker.cs b/MatchServer/Services/GameEventCheckWorker.cs new file mode 100644 index 0000000..54d26c8 --- /dev/null +++ b/MatchServer/Services/GameEventCheckWorker.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.Hosting; +using ServerBase; +using ServerCommon; +using ServerCore; +using Constant = ServerCommon.Constant; + +namespace MatchServer; + +public class GameEventCheckWorker: BackgroundService +{ + private readonly GameEventManager _gameEventManager; + private readonly MatchServerService _matchServerService; + private readonly NLog.Logger _logger; + + public GameEventCheckWorker(GameEventManager gameEventManager, MatchServerService matchServerService) + { + _gameEventManager = gameEventManager; + _matchServerService = matchServerService; + _logger = Log.getLogger(); + } + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await _matchServerService.WaitOnStart(); + do + { + var result = await _gameEventManager.RefreshEvent(); + if (result.isFail()) + { + _logger.error($"GameEvent Refresh Fail => {result.ResultString}"); + } + await Task.Delay(Constant.GAME_EVENT_CHECK_INTERVAL, stoppingToken); + } + while(!stoppingToken.IsCancellationRequested); + } +} diff --git a/MatchServer/Services/Interfaces/IMatchExecutor.cs b/MatchServer/Services/Interfaces/IMatchExecutor.cs new file mode 100644 index 0000000..8ecadc6 --- /dev/null +++ b/MatchServer/Services/Interfaces/IMatchExecutor.cs @@ -0,0 +1,6 @@ +namespace MatchServer; + +public interface IMatchExecutor +{ + Task ExecuteMatchingAsync(string matchGroupId); +} diff --git a/MatchServer/Services/Interfaces/IMatchGroupProcessorFactory.cs b/MatchServer/Services/Interfaces/IMatchGroupProcessorFactory.cs new file mode 100644 index 0000000..69fc1bf --- /dev/null +++ b/MatchServer/Services/Interfaces/IMatchGroupProcessorFactory.cs @@ -0,0 +1,8 @@ +using MatchServer; +using ServerCommon; + +public interface IMatchGroupProcessorFactory +{ + MatchGroupProcessor CreateMatchGroupProcessor(string matchGroupId); + MatchGroupProcessor CreateMatchGroupProcessor(MatchPoolInfo matchPoolInfo, MatchPolicy matchPolicy); +} diff --git a/MatchServer/Services/Interfaces/IMatchMakingProcessor.cs b/MatchServer/Services/Interfaces/IMatchMakingProcessor.cs new file mode 100644 index 0000000..e39b896 --- /dev/null +++ b/MatchServer/Services/Interfaces/IMatchMakingProcessor.cs @@ -0,0 +1,9 @@ +// using ServerCommon; +// +// namespace MatchServer; +// +// public interface IMatchMakingProcessor +// { +// MatchUpResult MatchUp(IEnumerable waitingUsers, IEnumerable matchedRooms); +// MatchMakingOption Option { get; } +// } diff --git a/MatchServer/Services/Interfaces/IMatchMessageFactory.cs b/MatchServer/Services/Interfaces/IMatchMessageFactory.cs new file mode 100644 index 0000000..b02c264 --- /dev/null +++ b/MatchServer/Services/Interfaces/IMatchMessageFactory.cs @@ -0,0 +1,12 @@ +// using ServerCommon; +// +// namespace MatchServer; +// +// public interface IMatchMessageFactory +// { +// ServerMessage CreateReserveResponse(MatchUserInfo userInfo, string traceId, MatchReserveResult result); +// ServerMessage CreateReserveError(MatchUserInfo userInfo, string traceId); +// ServerMessage CreateCancelResponse(MatchUserInfo userInfo, string traceId, MatchCancelResult result); +// ServerMessage CreateMatchResultMessage(MatchReserveUser user); +// ServerMessage CreateMatchFailMessage(MatchReserveUser user); +// } diff --git a/MatchServer/Services/Interfaces/IMatchMessageSender.cs b/MatchServer/Services/Interfaces/IMatchMessageSender.cs new file mode 100644 index 0000000..ef0109f --- /dev/null +++ b/MatchServer/Services/Interfaces/IMatchMessageSender.cs @@ -0,0 +1,9 @@ +// namespace MatchServer; +// +// public interface IMatchMessageSender +// { +// Task SendReserveResponseAsync(MatchUserInfo userInfo, string traceId, MatchReserveResult result); +// Task SendReserveErrorAsync(MatchUserInfo userInfo, string traceId); +// Task SendCancelResponseAsync(MatchUserInfo userInfo, string traceId, MatchCancelResult result); +// Task SendMatchResultsAsync(MatchExecutionResult result); +// } diff --git a/MatchServer/Services/Interfaces/IMatchMetaManager.cs b/MatchServer/Services/Interfaces/IMatchMetaManager.cs new file mode 100644 index 0000000..a1c8e5c --- /dev/null +++ b/MatchServer/Services/Interfaces/IMatchMetaManager.cs @@ -0,0 +1,46 @@ +using MetaAssets; +using ServerCommon; + +namespace MatchServer; + +public interface IMatchMetaManager +{ + TimeSpan MatchInterval { get; } + + /// + /// 모든 게임모드 메타데이터 목록을 반환합니다. + /// + /// 게임모드 메타데이터 배열 + GameModeMetaData[] GetGameModeList(); + + /// + /// 지정된 게임모드 ID가 존재하는지 확인합니다. + /// + /// 확인할 게임모드 ID + /// 게임모드 존재 여부 + bool HasGameMode(int gameModeId); + + /// + /// 매치 메타데이터를 업데이트합니다. + /// + /// 업데이트할 버전 (빈 문자열인 경우 새 버전 생성) + /// 업데이트 결과 + Task OnUpdateMatchMetaAsync(string version = ""); + + // /// + // /// 게임모드 ID로 랜덤 인스턴스 메타데이터를 가져옵니다. + // /// + // /// 게임모드 ID + // /// 인스턴스 메타데이터 (없으면 null) + // int GetInstanceMetaIdByRandomWithGameMode(int gameModeId); + + InstanceMetaData? GetInstanceMetaByRandomWithGameMode(int gameModeId); + + /// + /// 게임모드 ID와 게임 이벤트 ID로 매치 정책을 생성합니다. + /// + /// 게임모드 ID + /// 게임 이벤트 ID + /// 매치 정책 + MatchPolicy CreateMatchPolicy(int gameModeId, int gameEventId); +} diff --git a/MatchServer/Services/Interfaces/IMatchProcessorFactory.cs b/MatchServer/Services/Interfaces/IMatchProcessorFactory.cs new file mode 100644 index 0000000..7492053 --- /dev/null +++ b/MatchServer/Services/Interfaces/IMatchProcessorFactory.cs @@ -0,0 +1,7 @@ +using MatchServer; +using ServerCommon; + +public interface IMatchProcessorFactory +{ + MatchProcessor Create(MatchPoolInfo poolInfo, MatchPolicy policy); +} diff --git a/MatchServer/Services/Interfaces/IMatchRoomFactory.cs b/MatchServer/Services/Interfaces/IMatchRoomFactory.cs new file mode 100644 index 0000000..0957c05 --- /dev/null +++ b/MatchServer/Services/Interfaces/IMatchRoomFactory.cs @@ -0,0 +1,6 @@ +using ServerCommon; + +public interface IMatchRoomFactory +{ + MatchRoom CreateRoom(MatchPoolInfo matchPoolInfo, MatchPolicy matchPolicy); +} diff --git a/MatchServer/Services/MatchGroupManager.cs b/MatchServer/Services/MatchGroupManager.cs new file mode 100644 index 0000000..8782101 --- /dev/null +++ b/MatchServer/Services/MatchGroupManager.cs @@ -0,0 +1,290 @@ +using ServerCommon; +using ServerCore; +using static ServerMessage.Types; + +namespace MatchServer; +/// +/// MatchGroupProcessor와 QueProcessor를 사용해서 +/// MatchGroup의 실행을 멀티스레드로 처리 +/// +public class MatchGroupManager +{ + private readonly MatchGroupProcessor _matchGroupProcessor; + private readonly InstanceRoomRepo _instanceRoomRepo; + private readonly SequenceWorkQue _que = new(); + private readonly SequenceWorker _sequenceWorker; + private readonly NLog.Logger _logger; + private readonly IMatchMq _mq; + + public MatchUserRepository UserRepository => _matchGroupProcessor?.UserRepository; + public MatchRoomRepository RoomRepository => _matchGroupProcessor?.RoomRepository; + + public MatchGroupManager(MatchGroupProcessor matchGroupProcessor, InstanceRoomRepo instanceRoomRepo, IMatchMq mq) + { + _matchGroupProcessor = matchGroupProcessor; + _instanceRoomRepo = instanceRoomRepo; + _sequenceWorker = new SequenceWorker(_que); + _logger = Log.getLogger(); + _mq = mq; + } + + public async Task OnReserveAsync(MatchUserInfo matchUserInfo, string traceId) + { + await _que.AddAsync(async () => + { + OnReserveHandlerAsync(matchUserInfo, traceId); + await Task.CompletedTask; + }); + } + + public async Task OnCancelAsync(string userGuid, string serverName, string traceId) + { + await _que.AddAsync(async () => + { + OnCancelHandlerAsync(userGuid, serverName, traceId); + await Task.CompletedTask; + }); + } + + public async Task OnGameUserQuitAsync(string roomId, string userGuid) + { + await _que.AddAsync(async () => + { + await OnGameUserQuitHandlerAsync(roomId, userGuid); + }); + } + + public async Task OnGameUserJoinAsync(string roomId, string userGuid) + { + await _que.AddAsync(async () => + { + OnGameUserJoinHandlerAsync(roomId, userGuid); + await Task.CompletedTask; + }); + } + + public async Task OnGameChangeStateAsync(string roomId, MatchGameStateType state) + { + await _que.AddAsync( async () => + { + await OnGameChangeStateHandlerAsync(roomId, state); + }); + } + + public async Task OnUpdateMatchMetaAsync() + { + await _que.AddAsync(() => + { + _matchGroupProcessor.OnUpdateMatchMeta(); + return Task.CompletedTask; + }); + } + + private async Task OnGameChangeStateHandlerAsync(string roomId, MatchGameStateType state) + { + try + { + await RoomRepository.ChangeGameStateAsync(roomId, state); + var matchRoom = RoomRepository.FindRoom(roomId); + if (matchRoom is null) + { + _logger.error($"Match room not found for room id {roomId}"); + } + + // 상태 변경에 대한 정보 업데이트 + if (state == MatchGameStateType.Destroy) + { + MatchReserveUser[] users = matchRoom.GetTotalMembers(); + UserRepository.RemoveUsers(users); + RoomRepository.RemoveRoom(roomId); + await _instanceRoomRepo.RemoveGameRoomInfo(roomId); + } + else + { + await _instanceRoomRepo.UpdateGameRoomInfo(matchRoom); + } + _matchGroupProcessor.BusinessLogMatchRoomUpdate(matchRoom); + } + catch (Exception ex) + { + _logger.error($"OnGameChangeSateAsync => {ex}"); + } + } + + private void OnGameUserJoinHandlerAsync(string roomId, string userGuid) + { + var matchUserRepository = _matchGroupProcessor.UserRepository; + var matchRoomRepository = _matchGroupProcessor.RoomRepository; + var user = matchUserRepository.FindUser(userGuid); + if (user is null) + { + return; + } + + var matchRoom = matchRoomRepository.FindRoom(roomId); + if (matchRoom is null) + { + return; + } + + user.UserInfo.Status = MatchStatusType.JoinRoomChecked; + _matchGroupProcessor.BusinessLogMatchRoomUserJoinChecked(user); + } + + private async Task OnGameUserQuitHandlerAsync(string roomId, string userGuid) + { + var matchUserRepository = _matchGroupProcessor.UserRepository; + var matchRoomRepository = _matchGroupProcessor.RoomRepository; + matchUserRepository.RemoveUser(userGuid); + var matchRoom = matchRoomRepository.GetRoom(roomId); + if (matchRoom is null) + { + // 타이밍 이슈로 없을 수도 있다. + return; + } + matchRoom.RemoveMember(userGuid); + + await _instanceRoomRepo.UpdateGameRoomInfo(matchRoom); + var user = matchUserRepository.FindUser(userGuid); + if (user is null) + { + return; + } + user.UserInfo.Status = MatchStatusType.QuitRoom; + _matchGroupProcessor.BusinessLogMatchRoomUserQuit(matchRoom, user); + } + + /// + /// 매칭 예약 요청을 처리하는 핸들러 + /// 사용자를 매칭 대기 큐에 추가하고 결과를 게임 서버로 응답 + /// + /// + private void OnReserveHandlerAsync(MatchUserInfo matchUserInfo, string traceId) + { + try + { + var matchUserRepository = _matchGroupProcessor.UserRepository; + var matchRoomRepository = _matchGroupProcessor.RoomRepository; + var userGuid = matchUserInfo.UserGuid; + // 사용자 저장소에 매칭 예약 시도 + var (matchReserveUser, oldMatchReserveUser) = matchUserRepository.ReserveUser(matchUserInfo); + + // 이전 게임 룸 관련 정보 삭제 - 예약 도중이나 게임 중에 이슈가 발생해서 메모리에 남아 있는 유저 정보를 삭제한다. + if (oldMatchReserveUser is not null) + { + MatchRoom? room = matchRoomRepository.GetRoom(oldMatchReserveUser.RoomId); + room?.RemoveMember(oldMatchReserveUser.UserInfo.UserGuid); + } + else + { + // room에서 삭제가 안되는 경우가 있다. + matchRoomRepository.RemoveMemberByUserGuid(userGuid); + } + + // 유저의 상태 정보를 변경한다. + matchReserveUser.UserInfo.Status = MatchStatusType.Reserved; + matchReserveUser.RematchElapsed = _matchGroupProcessor.MatchPolicy.MatchWaitTime; + + SendReserveResponseAsync(matchReserveUser, traceId); + } + catch (ResultException ex) + { + _logger.error($"Match reserve Exception userGuid: {matchUserInfo.UserGuid} => {ex}"); + } + catch (Exception ex) + { + // 오류 발생시 로그 기록 및 에러 응답 전송 (통합된 메서드 사용) + _logger.error($"Match reserve Exception {matchUserInfo.UserGuid}: {ex}"); + SendReserveFailResponseAsync(matchUserInfo, traceId, ServerErrorCode.MatchServerException); + } + } + + /// + /// 매칭 예약 응답 메시지 전송 + /// + private void SendReserveResponseAsync(MatchReserveUser reserveUser, string traceId, + ServerErrorCode errorCode = ServerErrorCode.Success) + { + var message = new ServerMessage + { + AckMatchReserve = new MS2GS_ACK_MATCH_RESERVE + { + TraceId = traceId, + ErrorCode = errorCode, + MatchPlayerInfo = reserveUser.UserInfo, + MatchStatusInfo = new MatchStatusInfo + { + Status = MatchStatusType.Reserved, + MatchStep = reserveUser.MatchStep, + WaitTimeSec = (int)reserveUser.WaitElapsed.TotalSeconds, + WaitTimeMaxSec = (int)_matchGroupProcessor.MatchPolicy.MatchWaitTime.TotalSeconds, + } + } + }; + _mq.SendMessage(reserveUser.UserInfo.ServerName, message); + } + + private void SendReserveFailResponseAsync(MatchUserInfo userInfo, string traceId, ServerErrorCode errorCode) + { + var message = new ServerMessage + { + AckMatchReserve = new MS2GS_ACK_MATCH_RESERVE + { + TraceId = traceId, + ErrorCode = errorCode, + MatchPlayerInfo = userInfo + } + }; + _mq.SendMessage(userInfo.ServerName, message); + } + + public void OnCancelHandlerAsync(string userGuid, string targetServerName, string traceId) + { + try + { + // TODO 매칭 프로세스가 진행되는 동안에는 매칭 취소 요청을 처리할 수 없다. + var matchReserveUser = UserRepository.FindUser(userGuid); + // 매칭이 안된 유저만 캔슬 가능 + if (matchReserveUser.UserInfo.Status is MatchStatusType.None or MatchStatusType.Reserved or MatchStatusType.Progress) + { + UserRepository.RemoveUser(userGuid); + } + matchReserveUser.UserInfo.Status = MatchStatusType.Cancel; + SendCancelResponse(targetServerName, userGuid, traceId); + _matchGroupProcessor.BusinessLogMatchCancel(matchReserveUser.UserInfo); + } + catch (ResultException ex) + { + _logger.error($"Match OnCancelAsync => {ex}"); + } + catch (Exception ex) + { + // 오류 발생시 로그 기록 (취소는 별도 에러 응답 없음) + _logger.error($"Match cancel Error for user {userGuid}: {ex}"); + SendCancelResponse(targetServerName, userGuid, traceId, ServerErrorCode.MatchServerException); + } + } + + /// + /// 매칭 취소 응답 메시지 전송 + /// + private void SendCancelResponse(string serverName, string userGuid, string traceId, ServerErrorCode errorCode = ServerErrorCode.Success) + { + var message = new ServerMessage + { + AckMatchCancel = new MS2GS_ACK_MATCH_CANCEL + { + MatchCancelType = MatchCancelType.Normal, + UserGuid = userGuid, + TraceId = traceId, + ErrorCode = errorCode + } + }; + _mq.SendMessage(serverName, message); + } + + internal async Task RunMatchUpAsync() + { + await _matchGroupProcessor.RunMatchingAsync(); + } +} diff --git a/MatchServer/Services/MatchGroupProcessor.Log.cs b/MatchServer/Services/MatchGroupProcessor.Log.cs new file mode 100644 index 0000000..f20c756 --- /dev/null +++ b/MatchServer/Services/MatchGroupProcessor.Log.cs @@ -0,0 +1,246 @@ +using System.Text; +using MatchServer; +using Newtonsoft.Json; +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace MatchServer; + +public partial class MatchGroupProcessor +{ + /// + /// 매칭 결과 로그를 생성하고 수집하는 메서드 + /// + /// 매칭 실행 결과 + private void BusinessLogMatchResult(MatchExecutionResult result) + { + try + { + var logAction = new LogActionEx(LogActionType.MatchResult); + var logInvokers = new List(); + { + var logs = result.NewMatchedRooms.Select(ILogInvoker (x) => + new BusinessLogInvoker(LogDomainType.MatchRoom, + logInvoker => new MatchRoomLog(logInvoker) + { + RoomId = x.Id, + LogState = MatchRoomLogType.Create, + GameState = x.GameState, + Users = x.Teams.SelectMany(x => x.Members.Select(y => new MatchServerUserLog(logInvoker) + { + TeamId = y.TeamId, + UserGuid = y.UserGuid, + Nickname = y.UserInfo.Nickname, + MatchStatus = y.UserInfo.Status + })) + })); + logInvokers.AddRange(logs); + } + + { + var logs = result.UpdatedRooms.Select(ILogInvoker (x) => + new BusinessLogInvoker(LogDomainType.MatchRoom, + logInvoker => new MatchRoomLog(logInvoker) + { + RoomId = x.Id, + LogState = MatchRoomLogType.Update, + GameState = x.GameState, + Users = x.Teams.SelectMany(x => x.Members.Select(y => new MatchServerUserLog(logInvoker) + { + TeamId = y.TeamId, + UserGuid = y.UserGuid, + Nickname = y.UserInfo.Nickname, + MatchStatus = y.UserInfo.Status + })) + })); + logInvokers.AddRange(logs); + } + + BusinessLogger.collectLogs(logAction, LogActor, logInvokers); + DebugLogInvokers(nameof(LogActionType.MatchResult), logInvokers.ToArray()); + } + catch (Exception e) + { + _logger.error($"BusinessLogMatchResult => {e}"); + } + } + + /// + /// 룸의 상태가 변경될 때 로그를 남김 + /// + /// + public void BusinessLogMatchRoomUpdate(MatchRoom matchRoom) + { + try + { + MatchPoolInfo matchPoolInfo = MatchPoolInfo.ConvertByMatchGroupId(matchRoom.MatchGroupId); + var logAction = new LogActionEx(LogActionType.MatchRoomUpdate); + var log = new BusinessLogInvoker(LogDomainType.MatchRoom, + logInvoker => new MatchRoomLog(logInvoker) + { + GameModeId = matchPoolInfo.GameModeId, + Region = matchPoolInfo.Region, + Event = matchPoolInfo.EventId, + RoomId = matchRoom.Id, + LogState = MatchRoomLogType.Update, + GameState = matchRoom.GameState, + Users = matchRoom.Teams.SelectMany(x => x.Members.Select(y => new MatchServerUserLog(logInvoker) + { + RoomId = y.RoomId, + TeamId = y.TeamId, + UserGuid = y.UserGuid, + Nickname = y.UserInfo.Nickname, + MatchStatus = y.UserInfo.Status + })) + }); + BusinessLogger.collectLogs(logAction, LogActor, [log]); + DebugLogInvokers(nameof(LogActionType.MatchRoomUpdate), log); + } + catch (Exception e) + { + _logger.error($"BusinessLogMatchRoomUpdate => {e}"); + } + } + + public void BusinessLogMatchReserve(MatchUserInfo matchUserInfo) + { + try + { + var matchPoolInfo = MatchPoolInfo.ConvertByMatchGroupId(matchUserInfo.MatchGroupId); + var logAction = new LogActionEx(LogActionType.MatchReserve); + var log = new BusinessLogInvoker(LogDomainType.MatchServerUser, + logInvoker => new MatchServerUserLog(logInvoker) + { + GameModeId = matchPoolInfo.GameModeId, + Region = matchPoolInfo.Region, + Event = matchPoolInfo.EventId, + UserGuid = matchUserInfo.UserGuid, + Nickname = matchUserInfo.Nickname, + MatchStatus = matchUserInfo.Status + }); + BusinessLogger.collectLogs(logAction, LogActor, [log]); + DebugLogInvokers(nameof(LogActionType.MatchReserve), log); + } + catch (Exception e) + { + _logger.error($"BusinessLogMatchReserve => {e}"); + } + } + + public void BusinessLogMatchCancel(MatchUserInfo matchUserInfo) + { + try + { + var matchPoolInfo = MatchPoolInfo.ConvertByMatchGroupId(matchUserInfo.MatchGroupId); + var logAction = new LogActionEx(LogActionType.MatchCancel); + var log = new BusinessLogInvoker(LogDomainType.MatchServerUser, + logInvoker => new MatchServerUserLog(logInvoker) + { + GameModeId = matchPoolInfo.GameModeId, + Region = matchPoolInfo.Region, + Event = matchPoolInfo.EventId, + UserGuid = matchUserInfo.UserGuid, + Nickname = matchUserInfo.Nickname, + MatchStatus = matchUserInfo.Status + }); + BusinessLogger.collectLogs(logAction, LogActor, [log]); + DebugLogInvokers(logAction.getLogActionType(), log); + } + catch (Exception e) + { + _logger.error($"BusinessLogMatchCancel => {e}"); + } + } + + public void BusinessLogMatchRoomUserQuit(MatchRoom matchRoom, MatchReserveUser matchReserveUser) + { + try + { + var matchPoolInfo = MatchPoolInfo.ConvertByMatchGroupId(matchReserveUser.UserInfo.MatchGroupId); + var logAction = new LogActionEx(LogActionType.MatchRoomUserQuit); + var invokers = new List(); + // Quit 유저 + var userLog = new BusinessLogInvoker(LogDomainType.MatchServerUser, + logInvoker => new MatchServerUserLog(logInvoker) + { + GameModeId = matchPoolInfo.GameModeId, + Region = matchPoolInfo.Region, + Event = matchPoolInfo.EventId, + RoomId = matchReserveUser.RoomId, + TeamId = matchReserveUser.TeamId, + UserGuid = matchReserveUser.UserGuid, + Nickname = matchReserveUser.UserInfo.Nickname, + MatchStatus = matchReserveUser.UserInfo.Status, + }); + invokers.Add(userLog); + // 변경된 룸 상태 + var roomLog = new BusinessLogInvoker(LogDomainType.MatchRoom, + logInvoker => new MatchRoomLog(logInvoker) + { + GameModeId = matchPoolInfo.GameModeId, + Region = matchPoolInfo.Region, + Event = matchPoolInfo.EventId, + RoomId = matchRoom.Id, + LogState = MatchRoomLogType.Update, + GameState = matchRoom.GameState, + Users = matchRoom.Teams.SelectMany(x => + x.Members.Select(y => new MatchServerUserLog(logInvoker) + { + RoomId = y.RoomId, + TeamId = y.TeamId, + UserGuid = y.UserGuid, + Nickname = y.UserInfo.Nickname, + MatchStatus = y.UserInfo.Status + })) + }); + invokers.Add(roomLog); + BusinessLogger.collectLogs(logAction, LogActor, invokers); + DebugLogInvokers(nameof(LogActionType.MatchRoomUserQuit), [userLog, roomLog]); + } + catch (Exception e) + { + _logger.error($"BusinessLogMatchRoomUserQuit => {e}"); + } + } + + /// + /// 유저가 실제로 매칭 룸에 입장했는 지 여부 체크 + /// + /// + public void BusinessLogMatchRoomUserJoinChecked(MatchReserveUser matchReserveUser) + { + try + { + // 입장 확인 - 이 유저는 이미 룸에 존재함 + var matchPoolInfo = MatchPoolInfo.ConvertByMatchGroupId(matchReserveUser.UserInfo.MatchGroupId); + var logAction = new LogActionEx(LogActionType.MatchRoomUserJoin); + var log = new BusinessLogInvoker(LogDomainType.MatchServerUser, + logInvoker => new MatchServerUserLog(logInvoker) + { + GameModeId = matchPoolInfo.GameModeId, + Region = matchPoolInfo.Region, + Event = matchPoolInfo.EventId, + RoomId = matchReserveUser.RoomId, + TeamId = matchReserveUser.TeamId, + UserGuid = matchReserveUser.UserGuid, + Nickname = matchReserveUser.UserInfo.Nickname, + MatchStatus = matchReserveUser.UserInfo.Status + }); + BusinessLogger.collectLogs(logAction, LogActor, [log]); + DebugLogInvokers(nameof(LogActionType.MatchRoomUserJoin), log); + } + catch (Exception e) + { + _logger.error($"BusinessLogMatchRoomUserJoin => {e}"); + } + } + + private void DebugLogInvokers(string label, params ILogInvoker[] logInvokers) + { + var logMessages = logInvokers.Select(x => x.ToString()); + var logContent = string.Join(Environment.NewLine, logMessages); + _logger.debug($"[{label}]{Environment.NewLine}{logContent}"); + } +} diff --git a/MatchServer/Services/MatchGroupProcessor.cs b/MatchServer/Services/MatchGroupProcessor.cs new file mode 100644 index 0000000..8274f86 --- /dev/null +++ b/MatchServer/Services/MatchGroupProcessor.cs @@ -0,0 +1,499 @@ +using ServerBase; +using ServerCommon; +using ServerCommon.BusinessLogDomain; +using ServerCore; + +namespace MatchServer; + +public partial class MatchGroupProcessor +{ + #region Private Fields + + private readonly string _matchGroupId; + + /// + /// 매칭 대기 중인 사용자 정보를 관리하는 저장소 + /// 사용자 추가/제거/상태 업데이트 담당 + /// + private readonly MatchUserRepository _userRepository; + + /// + /// 매칭 완료된 방(룸) 정보를 관리하는 저장소 + /// 새로운 방 생성 및 멤버 관리 담당 + /// + private readonly MatchRoomRepository _roomRepository; + + private readonly IMatchMetaManager _matchMetaManager; + private readonly MatchProcessor _matchProcessor; + private readonly IMatchProcessorFactory _matchProcessorFactory; + private readonly InstanceRoomRepo _instanceRoomRepo; + + // 조회 시 이슈가 있음 + private volatile MatchPolicy _matchPolicy; + + /// + /// 메시지 큐 인터페이스 - 게임 서버로 메시지 전송 담당 + /// + private readonly IMatchMq _mq; + + /// + /// 로깅을 위한 NLog 인스턴스 + /// 디버그 정보 및 오류 로그 기록 + /// + private readonly NLog.Logger _logger; + + private CancellationTokenSource _cts; + + private MatchPoolInfo MatchPoolInfo { get; init; } + public MatchPolicy MatchPolicy => _matchPolicy; + public MatchLogActor LogActor { get; } + + public MatchUserRepository UserRepository => _userRepository; + public MatchRoomRepository RoomRepository => _roomRepository; + + #endregion + + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public MatchGroupProcessor( + string matchGroupId, + MatchUserRepository userRepository, + MatchRoomRepository roomRepository, + IMatchMetaManager matchMetaManager, + IMatchProcessorFactory matchProcessorFactory, + InstanceRoomRepo instanceRoomRepo, + IMatchMq mq) + { + _matchGroupId = matchGroupId; + _userRepository = userRepository; + _roomRepository = roomRepository; + _matchMetaManager = matchMetaManager; + _matchProcessorFactory = matchProcessorFactory; + _instanceRoomRepo = instanceRoomRepo; + _mq = mq; + _logger = Log.getLogger(); + + MatchPoolInfo = MatchPoolInfo.ConvertByMatchGroupId(_matchGroupId); + _matchPolicy = _matchMetaManager.CreateMatchPolicy(MatchPoolInfo.GameModeId, MatchPoolInfo.GameEventId); + _matchProcessor = _matchProcessorFactory.Create(MatchPoolInfo, _matchPolicy); + + LogActor = new MatchLogActor(MatchPoolInfo); + } + + public MatchGroupProcessor( + MatchPoolInfo matchPoolInfo, + MatchPolicy matchPolicy, + MatchUserRepository userRepository, + MatchRoomRepository roomRepository, + IMatchMetaManager matchMetaManager, + IMatchProcessorFactory matchProcessorFactory, + InstanceRoomRepo instanceRoomRepo, + IMatchMq mq) + { + MatchPoolInfo = matchPoolInfo; + _matchPolicy = matchPolicy; + + _userRepository = userRepository; + _roomRepository = roomRepository; + _matchMetaManager = matchMetaManager; + _matchProcessorFactory = matchProcessorFactory; + _instanceRoomRepo = instanceRoomRepo; + _mq = mq; + _logger = Log.getLogger(); + + _matchProcessor = _matchProcessorFactory.Create(MatchPoolInfo, _matchPolicy); + _matchGroupId = matchPoolInfo.MatchGroupId; + + LogActor = new MatchLogActor(MatchPoolInfo); + } + + /// + /// 주기적으로 실행되는 매칭 프로세스 + /// 대기 중인 사용자들을 대상으로 매칭을 수행하고 결과를 처리 + /// OperationProcessor의 업데이트 핸들러로 등록되어 주기적 실행 + /// + public async Task RunMatchingAsync() + { + try + { + ProcessInvalidUserChangeStatus(); + ProcessInvalidUserCleanUp(); + await ProcessTimeoutUsersAsync(); + + MatchReserveUser[] waitingUsers = _userRepository.GetWaitingUsers(_matchGroupId).ToArray(); + if (waitingUsers.Length == 0) + { + return null; + } + + _logger.debug($"Start match process with {waitingUsers.Length} users"); + + IEnumerable existingRooms = _roomRepository.GetCanEnterRooms(_matchGroupId, _matchPolicy); + + // 매칭 실행 + MatchExecutionResult matchResult = _matchProcessor.MatchUp(waitingUsers, existingRooms); + + // 매칭 결과가 있는 경우에만 후속 처리 진행 + if (matchResult.HasMatches) + { + await ProcessMatchResultsAsync(matchResult); + } + + return matchResult; + } + catch (Exception ex) + { + // 매칭 실행 중 오류 발생시 로그 기록 + _logger.error($"Error in ExecuteMatchingAsync: {ex}"); + } + + return null; + } + + // 장기 매칭 체류자 데이터 정리 + // 매칭에 성공한 후 1분 내로 입장하지 않은 유저를 JoinRoomInvalid 상태로 변경 + // 언리얼 툴에서 로그인하면 1분 넘게 로딩이 걸리기 때문에, 인던 서버에 접속확인이 안될 수 있다. + // 그래서 바로 삭제하지 않고 상태만 변경함 + // 매칭 요청 허용 상태로 변겅함 + private void ProcessInvalidUserChangeStatus() + { + // TODO 이 값을 Config에 추가할 것 + double joinWaitMinutes = 1; + var matchRooms = _roomRepository.GetRooms(MatchPoolInfo.MatchGroupId); + foreach (var matchRoom in matchRooms) + { + var members = matchRoom.GetTotalMembers(); + foreach (var member in members) + { + if (member.UserInfo.Status == MatchStatusType.Success && + member.RoomJoinTime.AddMinutes(joinWaitMinutes) < DateTime.UtcNow) + { + member.UserInfo.Status = MatchStatusType.JoinRoomInvalid; + } + } + } + } + + + // 장기 매칭 체류자 완전 정리 + // 10분이 지나면 완전 삭제 처리함 + private void ProcessInvalidUserCleanUp() + { + // TODO 이 값을 Config에 추가할 것 + double joinWaitMaxMinutes = 10; + List removeMembers = []; + var matchRooms = _roomRepository.GetRooms(MatchPoolInfo.MatchGroupId); + foreach (var matchRoom in matchRooms) + { + var members = matchRoom.GetTotalMembers(); + foreach (var member in members) + { + if (member.UserInfo.Status == MatchStatusType.JoinRoomInvalid && + member.RoomJoinTime.AddMinutes(joinWaitMaxMinutes) < DateTime.UtcNow) + { + removeMembers.Add(member); + } + } + } + _userRepository.RemoveUsers(removeMembers); + foreach (var removeMember in removeMembers) + { + _roomRepository.RemoveMemberByUserGuid(removeMember.UserGuid); + } + // 모든 removeMembers의 guid를 로그로 출력 + if (removeMembers.Count == 0) return; + _logger.info($"[[ Invalid Match User Clean Up ]] {string.Join("\n", removeMembers.Select(x => x.UserInfo.UserGuid))}"); + } + + private async Task ProcessTimeoutUsersAsync() + { + // 이벤트 시간 적용 - 일단 유보한다. + // todo 이벤트 시간 관련 정책을 재고하자. - 매칭 예약에서 막자. + // if (MatchPoolInfo.GameEventId > 0 && !_gameEventManager.IsValidGameEventTime(MatchPoolInfo.GameEventId)) + // { + // // 대기 유저를 모두 강제로 타임아웃 처리한다. + // var waitingUsers = _userRepository.GetWaitingUsers(_matchGroupId); + // _userRepository.RemoveUsers(waitingUsers); + // await SendMatchTimeoutMessagesAsync(waitingUsers); + // _logger.info($"[[ Invalid Game Event Time ]] "); + // } + + var timeoutUsers = _userRepository.RemoveTimeoutUsers(_matchGroupId, _matchPolicy.MatchWaitTime); + await SendMatchTimeoutMessagesAsync(timeoutUsers); + } + + /// + /// 매칭 실행 결과를 처리하는 메서드 + /// - 매칭되지 않은 사용자 상태 업데이트 + /// - 새로 생성된 방 정보 저장 + /// - 매칭 결과를 게임 서버로 전송 + /// + /// 매칭 실행 결과 객체 + private async Task ProcessMatchResultsAsync(MatchExecutionResult result) + { + // 매칭되지 않은 사용자들의 상태 업데이트 (대기 시간 증가 등) + // await _userRepository.UpdateUsersAsync(result.NotMatchedUsers); + // 매칭된 유저의 정보 업데이트 + foreach (var user in result.MatchedUsers) + { + user.UserInfo.Status = MatchStatusType.Success; + user.RoomJoinTime = DateTime.UtcNow; + } + + foreach (var user in result.EnterExistingRoomUsers) + { + user.UserInfo.Status = MatchStatusType.Success; + user.RoomJoinTime = DateTime.UtcNow; + } + + foreach (var user in result.UnmatchedUsers) + { + user.UserInfo.Status = MatchStatusType.Reserved; + user.RoomId = string.Empty; + user.TeamId = string.Empty; + } + + // 인던 룸 생성 + var validMatchedRooms = new List(); + foreach (MatchRoom newMatchedRoom in result.NewMatchedRooms) + { + // 싱글 게임 유저이고 본인이 선택한 인스턴스 메타가 있을 경우, 이 메타로 설정함. + if (newMatchedRoom.MatchPolicy.IsSingleGame) + { + var user = newMatchedRoom.Teams.FirstOrDefault()?.Members.FirstOrDefault(); + if (user != null && user.UserInfo.InstanceId != 0) + { + newMatchedRoom.SetInstanceMetaId(user.UserInfo.InstanceId); + } + else + { + _logger.error("[Match] Not found a user in room"); + } + } + // 새로운 인던 생성 + (Result createInstanceResult, InstanceRoomInfo? instanceRoomInfo) = + await _instanceRoomRepo.CreateInstanceGameRoom( + newMatchedRoom); + if (createInstanceResult.isSuccess() && instanceRoomInfo != null) + { + validMatchedRooms.Add(newMatchedRoom); + string serverName = ServerType.Indun.toServerName(instanceRoomInfo.InstanceAddress, + (ushort)instanceRoomInfo.InstancePort); + newMatchedRoom.StartOnIndun(instanceRoomInfo, serverName); + } + } + + // 새로 매칭된 방들을 방 저장소에 추가 + _roomRepository.AddRooms(validMatchedRooms); + + foreach (MatchRoom updatedRoom in result.UpdatedRooms) + { + Result updateGameRoomInfoResult = await _instanceRoomRepo.UpdateGameRoomInfo(updatedRoom); + if (updateGameRoomInfoResult.isFail()) + { + _logger.error($"Failed _instanceRoomRepo.UpdateGameRoomInfo {updateGameRoomInfoResult}"); + } + } + + // 매칭 결과를 게임 서버로 전송 (성공/실패 알림) - 통합된 메서드 사용 + await SendMatchResultsAsync(result); + + BusinessLogMatchResult(result); + } + + #region Message Factory Methods (통합된 IMatchMessageFactory 기능) + + /// + /// 매칭 결과 메시지 생성 (MatchReserveUser 버전) + /// + private ServerMessage CreateMatchResultMessage(MatchReserveUser user) + { + return new ServerMessage + { + NtfMatchResult = new ServerMessage.Types.MS2GS_NTF_MATCH_RESULT + { + MatchStatus = user.UserInfo.Status, + MatchRoomKey = user.RoomId ?? string.Empty, + MatchUpTeamId = user.TeamId ?? string.Empty, + MatchUserInfo = user.UserInfo, + } + }; + } + + /// + /// 매칭 결과 메시지 생성 (MatchReserveUser 버전) + /// + private ServerMessage CreateMatchStatusMessage(MatchReserveUser user) + { + return new ServerMessage + { + NtfMatchStatus = new ServerMessage.Types.MS2GS_NTF_MATCH_STATUS + { + MatchStatusInfo = new MatchStatusInfo + { + Status = user.UserInfo.Status, + MatchStep = user.MatchStep, + WaitTimeSec = user.WaitElapsed.Seconds, + WaitTimeMaxSec = (int)MatchPolicy.MatchWaitTime.TotalSeconds, + }, + UserGuid = user.UserInfo.UserGuid, + } + }; + } + + /// + /// 매칭 실패 메시지 생성 + /// + private ServerMessage CreateMatchTimeoutMessage(MatchReserveUser user) + { + return new ServerMessage + { + NtfMatchResult = new ServerMessage.Types.MS2GS_NTF_MATCH_RESULT + { + MatchStatus = MatchStatusType.Timeout, + MatchRoomKey = string.Empty, + MatchUpTeamId = string.Empty, + MatchUserInfo = user.UserInfo, + } + }; + } + + #endregion + + + /// + /// 매칭 결과 메시지들을 일괄 전송 + /// + private async Task SendMatchResultsAsync(MatchExecutionResult result) + { + await SendSuccessMessagesAsync(result.MatchedUsers); + await SendUserGameJoinMessageAsync(result.EnterExistingRoomUsers); + await SendMatchStatusMessagesAsync(result.UnmatchedUsers); + } + + /// + /// 매칭 실패 메시지들을 서버별로 그룹화하여 전송 + /// + private async Task SendMatchTimeoutMessagesAsync(IEnumerable users) + { + var messageGroups = users + .Select(user => new { ServerName = user.UserInfo.ServerName, Message = CreateMatchTimeoutMessage(user) }) + .GroupBy(x => x.ServerName).ToList(); + + foreach (var group in messageGroups) + { + _mq.SendBatchMessages(group.Key, group.Select(x => x.Message)); + } + + await Task.CompletedTask; + } + + /// + /// 매칭 성공 메시지들을 서버별로 그룹화하여 전송 + /// + private async Task SendSuccessMessagesAsync(IEnumerable successfulMatches) + { + if (!successfulMatches.Any()) + { + return; + } + + var messageGroups = successfulMatches + .Select(result => + new { ServerName = result.UserInfo.ServerName, Message = CreateMatchResultMessage(result) }) + .GroupBy(x => x.ServerName); + + foreach (var group in messageGroups) + { + _mq.SendBatchMessages(group.Key, group.Select(x => x.Message)); + _logger.debug($"Sent {group.Count()} success messages to server {group.Key}"); + } + + await Task.CompletedTask; + } + + /// + /// 난입 유저 알림 - 난입 하려고 하는 인던 룸에 전송함 + /// + /// + private async Task SendUserGameJoinMessageAsync(IEnumerable users) + { + if (!users.Any()) + { + return; + } + + await SendSuccessMessagesAsync(users); + foreach (MatchReserveUser matchReserveUser in users) + { + var room = _roomRepository.FindRoom(matchReserveUser.RoomId); + if (room == null) + { + _logger.error($"RoomId: {matchReserveUser.RoomId} not found"); + continue; + } + + // 유저의 난입을 알린다. + var message = new ServerMessage + { + NtfMatchGameJoinReserve = new ServerMessage.Types.MS2GS_NTF_MATCH_GAME_JOIN_RESERVE + { + RoomId = matchReserveUser.RoomId, + UserGuid = matchReserveUser.UserInfo.UserGuid, + TeamId = matchReserveUser.TeamId + } + }; + _mq.SendMessage(room.ServerName, message); + } + } + + /// + /// 매칭 안될 때, 상태를 유저에게 전송 + /// + private async Task SendMatchStatusMessagesAsync(IEnumerable users) + { + if (!users.Any()) + { + return; + } + + var messageGroups = users + .Select(matchReserveUser => + new + { + ServerName = matchReserveUser.UserInfo.ServerName, + Message = CreateMatchStatusMessage(matchReserveUser) + }) + .GroupBy(x => x.ServerName); + + foreach (var group in messageGroups) + { + _mq.SendBatchMessages(group.Key, group.Select(x => x.Message)); + _logger.debug($"Sent {group.Count()} status messages to server {group.Key}"); + } + + await Task.CompletedTask; + } + + /// + /// 이 함수를 적용하면 기존 룸에 있는 MatchPolicy와의 연결이 끊긴다. + /// - 기존에 생성된 방은 이전의 MatchPolicy로 동작 + /// + public void OnUpdateMatchMeta() + { + // 메타 정보가 업데이트 되면 정책을 다시 작성한다. - 기존 룸에 있는 MatchPolicy와의 레퍼런스는 단절됨 + _matchPolicy = _matchMetaManager.CreateMatchPolicy(MatchPoolInfo.GameModeId, MatchPoolInfo.GameEventId); + } +} diff --git a/MatchServer/Services/MatchMessageHandler.cs b/MatchServer/Services/MatchMessageHandler.cs new file mode 100644 index 0000000..1efc23d --- /dev/null +++ b/MatchServer/Services/MatchMessageHandler.cs @@ -0,0 +1,71 @@ +using ServerCommon; +using static ServerMessage.Types; + +namespace MatchServer.Services; + +public class MatchMessageHandler +{ + private readonly IMatchMq _mq; + + internal MatchMessageHandler(IMatchMq mq) + { + _mq = mq; + } + + /// + /// 매칭 예약 응답 메시지 전송 + /// + internal void SendReserveResponseAsync(MatchReserveUser reserveUser, MatchPolicy matchPolicy, string traceId, + ServerErrorCode errorCode = ServerErrorCode.Success) + { + var message = new ServerMessage + { + AckMatchReserve = new MS2GS_ACK_MATCH_RESERVE + { + TraceId = traceId, + ErrorCode = errorCode, + MatchPlayerInfo = reserveUser.UserInfo, + MatchStatusInfo = new MatchStatusInfo + { + Status = MatchStatusType.Reserved, + MatchStep = reserveUser.MatchStep, + WaitTimeSec = (int)reserveUser.WaitElapsed.TotalSeconds, + WaitTimeMaxSec = (int)matchPolicy.MatchWaitTime.TotalSeconds, + } + } + }; + _mq.SendMessage(reserveUser.UserInfo.ServerName, message); + } + + internal void SendReserveFailResponseAsync(MatchUserInfo userInfo, string traceId, + ServerErrorCode errorCode) + { + var message = new ServerMessage + { + AckMatchReserve = new MS2GS_ACK_MATCH_RESERVE + { + TraceId = traceId, ErrorCode = errorCode, MatchPlayerInfo = userInfo + } + }; + _mq.SendMessage(userInfo.ServerName, message); + } + + /// + /// 매칭 취소 응답 메시지 전송 + /// + internal void SendCancelResponse(string serverName, string userGuid, string traceId, + ServerErrorCode errorCode = ServerErrorCode.Success) + { + var message = new ServerMessage + { + AckMatchCancel = new MS2GS_ACK_MATCH_CANCEL + { + MatchCancelType = MatchCancelType.Normal, + UserGuid = userGuid, + TraceId = traceId, + ErrorCode = errorCode + } + }; + _mq.SendMessage(serverName, message); + } +} diff --git a/MatchServer/Services/MatchMetaManager.cs b/MatchServer/Services/MatchMetaManager.cs new file mode 100644 index 0000000..19a515b --- /dev/null +++ b/MatchServer/Services/MatchMetaManager.cs @@ -0,0 +1,188 @@ +using MetaAssets; +using ServerCommon; + +namespace MatchServer; + +public class MatchMetaManager : IMatchMetaManager +{ + private string _version; + private readonly InstanceMetaTable _instanceMeta; + private readonly GameModeMetaTable _gameModeMeta; + private readonly GameModeCommonMetaTable _gameModeCommonMeta; + private readonly GameModeTeamMetaTable _gameModeTeamMeta; + + private readonly GameModeMatchMetaTable _gameModeMatchMeta; + + // 인스턴스 그룹 + private readonly GameModeInstanceMetaTable _gameModeInstanceMataTable; + + private TimeSpan _matchIntervalTime; + + public MatchMetaManager(GameEventManager gameEventManager) + { + var metaData = MetaData.Instance; + // 서버 재시작 시 마다 새로운 버전이 생성됨 + _version = CreateVersion(); + _instanceMeta = metaData.Meta.InstanceMetaTable; + _gameModeInstanceMataTable = metaData.Meta.GameModeInstanceMetaTable; + _gameModeMeta = metaData.Meta.GameModeMetaTable; + _gameModeMatchMeta = metaData.Meta.GameModeMatchMetaTable; + _gameModeCommonMeta = metaData.Meta.GameModeCommonMetaTable; + _gameModeTeamMeta = metaData.Meta.GameModeTeamMetaTable; + // _gameModeStartMeta = metaData.Meta.GameModeStartMetaTable; + // _gameModeTpsFfaMeta = metaData.Meta.GameModeTpsFfaMetaTable; + var firstMatchData = _gameModeMatchMeta.GameModeMatchDataList.FirstOrDefault(); + MatchGuard.NotNull(firstMatchData); + // 1회 대기 시간을 3으로 나눈 값으로 한다. + // TODO 옵션이나 테이블 등으로 처리할 것 + _matchIntervalTime = TimeSpan.FromSeconds(10); // TimeSpan.FromSeconds((float)firstMatchData.MatchWaitTimeSec / 3); + } + + public TimeSpan MatchInterval => _matchIntervalTime; + + private string CreateVersion() + { + return $"{Ulid.NewUlid():N}"; + } + + public GameModeMetaData[] GetGameModeList() => _gameModeMeta.GameModeMetaDataListbyId.Values.ToArray(); + + public bool HasGameMode(int gameModeId) + { + return _gameModeMeta.GameModeMetaDataListbyId.ContainsKey(gameModeId); + } + + private void ReloadGameModeMetaData(int gameModeId) + { + } + + public async Task OnUpdateMatchMetaAsync(string version = "") + { + // 버전 정보 갱신 + _version = string.IsNullOrEmpty(version) ? CreateVersion() : version; + + // TODO @heaon 운영툴에서 메타데이터를 다시 로딩 + + await Task.CompletedTask; + return new Result(); + } + + public int GetInstanceMetaIdByRandomWithGameMode(int gameModeId) + { + var meta = GetInstanceMetaByRandomWithGameMode(gameModeId); + return meta?.Id ?? 0; + } + + public InstanceMetaData? GetInstanceMetaByRandomWithGameMode(int gameModeId) + { + var (gameModeData, gameModeTeamData, gameModeCommonData, gameModeMatchData) = GetMetaData(gameModeId); + MatchGuard.InvalidMetaData(gameModeData, $"GameModeId:{gameModeId}"); + return GetInstanceMetaByRandom(gameModeData.InstanceGroupID); + } + + private InstanceMetaData? GetInstanceMetaByRandom(int instanceGroupId) + { + var gameModeInstanceMetaData = GetRandomInstanceByGroupId(instanceGroupId); + var instanceMetaId = gameModeInstanceMetaData?.InstanceID ?? 0; + if (instanceMetaId == 0) return null; + return _instanceMeta.InstanceMetaDataListbyId[instanceMetaId]; + } + + /// + /// 확률적으로 인스턴스 메타를 선택 + /// + /// + /// + private GameModeInstanceMetaData? GetRandomInstanceByGroupId(int groupId) + { + var instancesInGroup = _gameModeInstanceMataTable.GameModeInstanceMetaDataList + .Where(x => x.GroupID == groupId) + .ToList(); + + if (!instancesInGroup.Any()) return null; + + if (instancesInGroup.Count == 1) + { + return instancesInGroup.FirstOrDefault(); + } + + var totalProbability = instancesInGroup.Sum(x => x.Probability); + if (totalProbability <= 0) return instancesInGroup.FirstOrDefault(); + + var randomValue = new Random().NextDouble() * totalProbability; + var cumulativeProbability = 0.0; + + return instancesInGroup + .Select(instance => + new { Instance = instance, CumulativeProb = cumulativeProbability += instance.Probability }) + .FirstOrDefault(x => randomValue <= x.CumulativeProb)?.Instance; + } + + private (GameModeMetaData gameModeData, GameModeTeamMetaData gameModeTeamData, GameModeCommonData gameModeCommonData + , GameModeMatchMetaData gameModeMatchData) GetMetaData( + int gameModeId) + { + _gameModeMeta.GameModeMetaDataListbyId.TryGetValue(gameModeId, out var gameModeData); + MatchGuard.InvalidMetaData(gameModeData, $"GameModeId:{gameModeId}"); + + _gameModeTeamMeta.GameModeTeamDataListbyGameModeTeamID.TryGetValue(gameModeData.GameModeTeamID, + out GameModeTeamMetaData gameModeTeamData); + MatchGuard.InvalidMetaData(gameModeTeamData, $"GameModeTeamID:{gameModeData.GameModeTeamID}"); + + GameModeCommonData? gameModeCommonData = + _gameModeCommonMeta.GameModeCommonMetaDataListbyGameModeCommonID[gameModeData.GameModeCommonID]; + MatchGuard.InvalidMetaData(gameModeCommonData, $"GameModeCommonID:{gameModeData.GameModeCommonID}"); + + GameModeMatchMetaData? gameModeMatchData = + _gameModeMatchMeta.GameModeMatchDataListbyGameModeMatchID[gameModeData.GameModeMatchID]; + MatchGuard.InvalidMetaData(gameModeMatchData, $"GameModeMatchID:{gameModeData.GameModeMatchID}"); + + return (gameModeData, gameModeTeamData, gameModeCommonData, gameModeMatchData); + } + + public MatchPolicy CreateMatchPolicy(int gameModeId, int gameEventId) + { + var (gameModeData, gameModeTeamData, gameModeCommonData, gameModeMatchData) = GetMetaData(gameModeId); + + int maxTeamCount = gameModeTeamData.MaxTeam; + // 두 배열의 크기가 다를 경우 작은 쪽으로 선택 + int dataLength = Math.Min(gameModeTeamData.MinTeamPlayer.Count, gameModeTeamData.MaxTeamPlayer.Count); + var teamOptionList = new List(maxTeamCount); //[maxTeamCount]; + teamOptionList.AddRange(Enumerable.Range(0, maxTeamCount).Select(i => + { + int idx = i < dataLength ? i : dataLength - 1; + int minTeamMemberCount = + gameModeTeamData.MinTeamPlayer[idx] == 0 ? 1 : gameModeTeamData.MinTeamPlayer[idx]; + int maxTeamMemberCount = + gameModeTeamData.MaxTeamPlayer[idx] == 0 ? 1 : gameModeTeamData.MaxTeamPlayer[idx]; + ; + return new MatchTeamPolicy(minTeamMemberCount, maxTeamMemberCount); + })); + + var isSingleMode = maxTeamCount == 1 && gameModeTeamData.MaxTeamPlayer.Count == 1; + + var matchLoopIntervalMs = isSingleMode ? 500 : 10000; + + var instanceMetaData = GetInstanceMetaByRandom(gameModeData.InstanceGroupID); + MatchGuard.InvalidMetaData(instanceMetaData, $"InstanceGroupID:{gameModeData.InstanceGroupID}"); + + var matchPolicy = new MatchPolicy + { + Version = _version, + GameModeId = gameModeId, + GameEventId = gameEventId, + JoinInMaxTime = TimeSpan.FromSeconds(gameModeMatchData.JoinInMaxTimeSec), + MaxStartWaitTime = TimeSpan.FromSeconds(gameModeCommonData.MaxStartWaitTime), + MinTeamCount = gameModeTeamData.MinTeam, + MaxTeamCount = gameModeTeamData.MaxTeam, + TeamPolicies = teamOptionList.ToArray(), + MatchWaitTime = TimeSpan.FromSeconds(gameModeMatchData.MatchWaitTimeSec), + MatchRetryCount = gameModeMatchData.MatchRetryCount, + IsJoinAnytime = + gameModeMatchData.JoinInProgress is JoinInProgressType.JoinIn + or JoinInProgressType.JoinInSnapShot, + MatchLoopIntervalMs = matchLoopIntervalMs + }; + return matchPolicy; + } +} diff --git a/MatchServer/Services/MatchProcessor.cs b/MatchServer/Services/MatchProcessor.cs new file mode 100644 index 0000000..5460986 --- /dev/null +++ b/MatchServer/Services/MatchProcessor.cs @@ -0,0 +1,173 @@ +using ServerCommon; +using ServerCore; + +namespace MatchServer; + +public class MatchProcessor +{ + private readonly MatchPoolInfo _matchPoolInfo; + private readonly MatchPolicy _matchPolicy; + private readonly IMatchRoomFactory _matchRoomFactory; + private readonly NLog.Logger _logger; + + public MatchProcessor( + MatchPoolInfo matchMatchPoolInfo, + MatchPolicy matchPolicy, + IMatchRoomFactory matchRoomFactory) + { + _matchPoolInfo = matchMatchPoolInfo; + _matchPolicy = matchPolicy; + _matchRoomFactory = matchRoomFactory; + _logger = Log.getLogger(); + } + + /// + /// 대기중인 유저와 기존 방에 대해 매칭 프로세스를 실행합니다. + /// + /// 매칭을 기다리는 유저 목록 + /// 매칭 가능한 기존 방 목록 + /// 매칭된 방과 유저, 매칭되지 않은 유저를 포함하는 매칭 실행 결과 + public MatchExecutionResult MatchUp(IEnumerable waitingUsers, + IEnumerable matchableRooms) + { + try + { + // 유효한 유저가 없는 경우만 실행 + if (!waitingUsers.Any()) + { + return new MatchExecutionResult(); + } + + // 사용자 우선순위 정렬 (대기 시간 기준) + var orderedUsers = waitingUsers.OrderByDescending(x => x.WaitElapsed).ToList(); + + // 기존 룸에 난입 처리 + var enterRoomResult = ProcessEnterExistingRoom(orderedUsers, matchableRooms); + + // 새로운 룸 생성 및 매칭 + var newMatchRooms = CreateNewMatchRooms(enterRoomResult.RemainingUsers); + + // 매칭 결과 분류 및 반환 + return BuildMatchExecutionResult(enterRoomResult, newMatchRooms, orderedUsers); + } + catch (ResultException ex) + { + _logger.error($"매칭 과정 중 오류가 발생했습니다: {ex}\n{ex.StackTrace}"); + return new MatchExecutionResult(); + } + catch (Exception e) + { + _logger.error($"매칭 과정 중 오류가 발생했습니다: {e.Message}\n{e.StackTrace}"); + return new MatchExecutionResult(); + } + } + + /// + /// 난입 유저 처리 + /// + /// + /// + /// + private RoomEnterResult ProcessEnterExistingRoom(List users, IEnumerable matchedRooms) + { + var enterRoomUsers = new List(); + var updatedRooms = new HashSet(); + + var remainingUsers = new List(users); + var roomList = matchedRooms.ToList(); + + foreach (var user in users.ToList()) + { + if (TryEnterExistingRoom(user, roomList, out var targetRoom)) + { + user.UserInfo.Status = MatchStatusType.Success; + enterRoomUsers.Add(user); + updatedRooms.Add(targetRoom); + remainingUsers.Remove(user); + } + } + + return new RoomEnterResult(enterRoomUsers, updatedRooms, remainingUsers); + } + + private bool TryEnterExistingRoom(MatchReserveUser user, IEnumerable rooms, + out MatchRoom? targetRoom) + { + foreach (var room in rooms) + { + if (room.AddMemberOnTeamBalance(user)) + { + targetRoom = room; + return true; + } + } + + targetRoom = null; + return false; + } + + private List CreateNewMatchRooms(IEnumerable users) + { + var newMatchRooms = new List(); + var currentRoom = CreateNewRoom(); + newMatchRooms.Add(currentRoom); + + foreach (var user in users) + { + if (currentRoom.IsFull()) + { + currentRoom = CreateNewRoom(); + newMatchRooms.Add(currentRoom); + } + + if (!currentRoom.AddMemberOnTeamBalance(user)) + { + _logger.error($"Failed to add user to new room: {user.UserInfo.UserGuid}"); + } + } + + return newMatchRooms; + } + + private MatchRoom CreateNewRoom() + { + return _matchRoomFactory.CreateRoom(_matchPoolInfo, _matchPolicy); + } + + private MatchExecutionResult BuildMatchExecutionResult( + RoomEnterResult roomEnterResult, + List newMatchRooms, + IEnumerable waitingUsers) + { + // 시작 가능한 룸에 매칭된 유저만 매칭을 적용한다. + var newStartableRooms = newMatchRooms.Where(room => room.CanStart()); + var newMatchedUsers = newStartableRooms.SelectMany(room => room.GetTotalMembers()); + var enterExistingRoomUsers = roomEnterResult.IntrudeUsers; + var matchedUserSet = new HashSet(newMatchedUsers.Concat(enterExistingRoomUsers)); + var unmatchedUsers = waitingUsers + .Where(user => !matchedUserSet.Contains(user)) + .ToList(); + + + return new MatchExecutionResult + { + UpdatedRooms = roomEnterResult.UpdatedRooms, + NewMatchedRooms = newStartableRooms, + // 신규 매치 유저 + MatchedUsers = newMatchedUsers, + // 난입된 유저 + EnterExistingRoomUsers = enterExistingRoomUsers, + // 매칭 실패 유저 + UnmatchedUsers = unmatchedUsers, + }; + } + + //============================================================================== + // 헬퍼 레코드 타입들 + //============================================================================== + + private record RoomEnterResult( + List IntrudeUsers, + HashSet UpdatedRooms, + List RemainingUsers); +} diff --git a/MatchServer/Services/MatchService.Cmd.cs b/MatchServer/Services/MatchService.Cmd.cs new file mode 100644 index 0000000..2419fb0 --- /dev/null +++ b/MatchServer/Services/MatchService.Cmd.cs @@ -0,0 +1,79 @@ +using ServerCore; + +namespace MatchServer; + +public partial class MatchService +{ + private async Task OnMatchCheatCmd(IEnumerable cmdArgs) + { + if (_serverConfig.ServiceType == nameof(ServiceType.Live)) + { + _logger.error($"Not Support CheatCmd For Live Service"); + return; + } + + if (!cmdArgs.Any()) + { + _logger.error($"OnMatchCheatCmd No Cmd"); + return; + } + + string cmd = cmdArgs.FirstOrDefault() ?? string.Empty; + try + { + string[] args = cmdArgs.Skip(1).ToArray(); + + switch (cmd) + { + case "check_event": + _que.AddWorkFirstAsync(async () => + { + _gameEventManager.ApplyGameEvent = Convert.ToBoolean(args[0]); + _logger.info($"CheatCmd check_event => {_gameEventManager.ApplyGameEvent}"); + await Task.CompletedTask; + }); + break; + + case "reset": + _que.AddWorkFirstAsync( async () => + { + _matchUserRepository.Clear(); + _matchRoomRepository.Clear(); + await Task.CompletedTask; + }); + break; + + // case "cancel": + // { + // string userGuid = args[0]; + // _matchUserRepository.RemoveUser(userGuid); + // } + // break; + // // 매치에 실패하도록 옵션 설정 + // case "option_fail": + // { + // int gameModeId = int.Parse(args[0]); + // string region = args[1]; + // var matchPoolInfo = new MatchPoolInfo(gameModeId, region, 0); + // await OnUpdateMatchMeta(matchPoolInfo); + // } + // break; + // // 처음 옵션으로 되돌림 + // case "option_restore": + // { + // int gameModeId = int.Parse(args[0]); + // string region = args[1]; + // var matchPoolInfo = new MatchPoolInfo(gameModeId, region, 0); + // await OnUpdateMatchMeta(matchPoolInfo); + // } + // break; + } + } + catch (Exception ex) + { + _logger.error($"{cmd} => " + ex); + } + + await Task.CompletedTask; + } +} diff --git a/MatchServer/Services/MatchService.cs b/MatchServer/Services/MatchService.cs new file mode 100644 index 0000000..0dad801 --- /dev/null +++ b/MatchServer/Services/MatchService.cs @@ -0,0 +1,593 @@ +using System.Collections.Concurrent; +using Google.Protobuf; +using ServerBase; +using ServerCommon; +using ServerCore; +using static ServerMessage.Types; + +namespace MatchServer; + +/// +/// 여러 매칭 그룹을 관리하고 메시지 처리를 담당하는 통합 서비스 +/// +public partial class MatchService : IDisposable +{ + private const string MatchGroupNullOrEmptyMessage = "Match group cannot be null or empty"; + + private readonly IMatchMq _mq; + private readonly ServerConfig _serverConfig; + private readonly IMatchMetaManager _matchMetaManager; + private readonly GameEventManager _gameEventManager; + private readonly NLog.Logger _logger; + private readonly ConcurrentDictionary _matchGroupProcessors = new(); + + private readonly SequenceWorkQue _que = new(); + private readonly SequenceWorker _sequenceWorker; + + private readonly MatchUserRepository _matchUserRepository; + private readonly MatchRoomRepository _matchRoomRepository; + private readonly InstanceRoomRepo _instanceRoomRepo; + private readonly IMatchGroupProcessorFactory _matchGroupProcessorFactory; + private readonly CancellationTokenSource _cts; + + public MatchService( + MatchUserRepository userRepository, + MatchRoomRepository roomRepository, + InstanceRoomRepo instanceRoomRepo, + IMatchMetaManager matchMetaManager, + GameEventManager gameEventManager, + IMatchGroupProcessorFactory matchGroupProcessorFactory, + IMatchMq mq, + ServerConfig serverConfig) + { + _mq = mq; + _serverConfig = serverConfig; + _matchMetaManager = matchMetaManager; + _gameEventManager = gameEventManager; + _matchUserRepository = userRepository; + _matchRoomRepository = roomRepository; + _instanceRoomRepo = instanceRoomRepo; + _matchGroupProcessorFactory = matchGroupProcessorFactory; + _logger = Log.getLogger(); + + _cts = new CancellationTokenSource(); + + _sequenceWorker = new SequenceWorker(_que); + _ = _sequenceWorker.StartAsync(_cts.Token); + // RunMatchGroupProcessorAsync(); + } + + public async Task AddWorkAsync(TMessage recvMessage) where TMessage : IMessage + { + await (recvMessage switch + { + GS2MS_REQ_MATCH_RESERVE reserve => + _que.AddAsync(() => OnReserveAsync(reserve)), + + GS2MS_NTF_MATCH_CHEAT_CMD { Args: var args } => + _que.AddAsync(() => OnMatchCheatCmd(args)), + + GS2MS_REQ_MATCH_CANCEL cancel => + _que.AddAsync(() => OnCancelAsync(cancel)), + + GS2MS_NTF_MATCH_GAME_QUIT quit => + _que.AddAsync(() => OnGameUserQuitAsync(quit)), + + GS2MS_NTF_MATCH_GAME_JOIN join => + _que.AddAsync(() => OnGameUserJoinAsync(join)), + + GS2MS_NTF_MATCH_CHANGE_GAME_STATE changeState => + _que.AddAsync(() => OnGameChangeSateAsync(changeState)), + + _ => throw new NotSupportedException($"Message type {typeof(TMessage).Name} is not supported") + }); + } + + /// + /// 유저가 실제로 게임에 입장 했는 지 여부를 체크 + /// + /// + private async Task OnGameUserJoinAsync(GS2MS_NTF_MATCH_GAME_JOIN join) + { + // 유저가 실제로 게임에 입장 했는 지 여부를 체크 + // todo @heon 일정 시간동안 입장하지 않으면 유저 삭제 처리? + try + { + var user = _matchUserRepository.FindUser(join.UserGuid); + if (user is null) + { + return; + } + + _matchGroupProcessors.TryGetValue(user.UserInfo.MatchGroupId, out MatchGroupProcessor? matchGroup); + if (matchGroup is null) + { + return; + } + + user.UserInfo.Status = MatchStatusType.JoinRoomChecked; + + // 비즈니스 로그 + matchGroup.BusinessLogMatchRoomUserJoinChecked(user); + + await Task.CompletedTask; + } + catch (Exception ex) + { + _logger.error($"OnGameUserJoinAsync => {ex}"); + } + } + + private async Task OnGameChangeSateAsync(GS2MS_NTF_MATCH_CHANGE_GAME_STATE message) + { + try + { + await _matchRoomRepository.ChangeGameStateAsync(message.RoomId, message.State); + var matchRoom = _matchRoomRepository.FindRoom(message.RoomId); + if (matchRoom is null) + { + _logger.debug($"Match room not found for room id {message.RoomId}"); + return; + } + + _matchGroupProcessors.TryGetValue(matchRoom.MatchGroupId, out MatchGroupProcessor? matchGroup); + matchGroup.BusinessLogMatchRoomUpdate(matchRoom); + + // 상태 변경에 대한 정보 업데이트 + if (message.State == MatchGameStateType.Destroy) + { + MatchReserveUser[] users = matchRoom.GetTotalMembers(); + _matchUserRepository.RemoveUsers(users); + _matchRoomRepository.RemoveRoom(message.RoomId); + await _instanceRoomRepo.RemoveGameRoomInfo(message.RoomId); + } + else + { + await _instanceRoomRepo.UpdateGameRoomInfo(matchRoom); + } + + matchGroup.BusinessLogMatchRoomUpdate(matchRoom); + } + catch (Exception ex) + { + _logger.error($"OnGameChangeSateAsync => {ex}"); + } + } + + private async Task OnGameUserQuitAsync(GS2MS_NTF_MATCH_GAME_QUIT message) + { + try + { + var user = _matchUserRepository.FindUser(message.UserGuid); + MatchGuard.NotNull(user, () => $"[OnGameUserQuitAsync] Match user not found for user {message.UserGuid}, roomId:{message.RoomId}"); + _matchUserRepository.RemoveUser(message.UserGuid); + + var matchRoom = _matchRoomRepository.GetRoom(message.RoomId); + if (matchRoom is null) + { + _logger.debug($"Match room not found for room id {message.RoomId}"); + return; + } + + _matchRoomRepository.RemoveMember(message.RoomId, message.UserGuid); + // 룸의 레디스 정보 업데이트 + await _instanceRoomRepo.UpdateGameRoomInfo(matchRoom); + + user.UserInfo.Status = MatchStatusType.QuitRoom; + + _matchGroupProcessors.TryGetValue(user.UserInfo.MatchGroupId, out MatchGroupProcessor? matchGroup); + + // 비즈니스 로그 + matchGroup.BusinessLogMatchRoomUserQuit(matchRoom, user); + } + catch (Exception ex) + { + _logger.warn($"OnGameUserQuitAsync =>{ex}"); + } + } + + /// + /// 매칭 예약 요청을 처리하는 핸들러 + /// 사용자를 매칭 대기 큐에 추가하고 결과를 게임 서버로 응답 + /// + /// 매칭 예약 요청 메시지 (사용자 정보 포함) + private async Task OnReserveAsync(GS2MS_REQ_MATCH_RESERVE message) + { + try + { + var matchGroupId = message.MatchUserInfo.MatchGroupId; + MatchGuard.InvalidMatchGroupId(matchGroupId); + + var matchPoolInfo = MatchPoolInfo.ConvertByMatchGroupId(matchGroupId); + int gameModeId = matchPoolInfo.GameModeId; + + bool isValidGameModeId = _matchMetaManager.HasGameMode(gameModeId); + MatchGuard.InvalidGameModeId(isValidGameModeId, gameModeId); + + MatchGroupProcessor matchGroup = await GetOrCreateMatchGroupAsync(message.MatchUserInfo.MatchGroupId); + MatchGuard.NotNull(matchGroup, () => $"Match group not found for user {message.MatchUserInfo.UserGuid}"); + + var matchUserInfo = message.MatchUserInfo; + + // 이미 예약이나 게임 상태인 유저인지 판단 + var matchReserveUser = _matchUserRepository.FindUser(matchUserInfo.UserGuid); + + // 이미 예약 유저이고 게임이 진행 중이라면 예약 불가 + if (matchReserveUser is not null && !CaneMatchReserveStatus(matchReserveUser.UserInfo.Status)) + { + // 게임 서버로 예약 실패 전송 + SendReserveResponseAsync(matchReserveUser, message.TraceId, ServerErrorCode.MatchReserveFail); + _logger.info($"Match reserve failed => user: {message.MatchUserInfo.UserGuid}"); + return; + } + + // 사용자 저장소에 매칭 예약 시도 + (matchReserveUser, var oldMatchReserveUser) = _matchUserRepository.ReserveUser(message.MatchUserInfo); + + // 이전 게임 룸 관련 정보 삭제 - 예약 도중이나 게임 중에 이슈가 발생해서 메모리에 남아 있는 유저 정보를 삭제한다. + if (oldMatchReserveUser is not null) + { + MatchRoom? room = _matchRoomRepository.GetRoom(oldMatchReserveUser.RoomId); + room?.RemoveMember(oldMatchReserveUser.UserInfo.UserGuid); + } + else + { + // room에서 삭제가 안되는 경우가 있다. + _matchRoomRepository.RemoveMemberByUserGuid(message.MatchUserInfo.UserGuid); + } + + // 유저의 상태 정보를 변경한다. + matchReserveUser.UserInfo.Status = MatchStatusType.Reserved; + matchReserveUser.RematchElapsed = matchGroup.MatchPolicy.MatchWaitTime; + + // 게임 서버로 예약 결과 응답 전송 (통합된 메서드 사용) + SendReserveResponseAsync(matchReserveUser, message.TraceId); + + // 매칭 결과 비즈니스 로그 저장 + matchGroup.BusinessLogMatchReserve(matchReserveUser.UserInfo); + + // 처리 결과 로그 기록 + _logger.debug($"Match reserve ok for user {message.MatchUserInfo.UserGuid}"); + } + catch (ResultException ex) + { + _logger.error($"[Match Exception] Match Reserve Failed userGuid: {message.MatchUserInfo.UserGuid} => {ex}"); + } + catch (Exception ex) + { + // 오류 발생시 로그 기록 및 에러 응답 전송 (통합된 메서드 사용) + _logger.error($"Match reserve Exception {message.MatchUserInfo.UserGuid}: {ex}"); + SendReserveFailResponseAsync(message.MatchUserInfo, message.TraceId, ServerErrorCode.MatchServerException); + } + } + + /// + /// 매칭 예약 응답 메시지 전송 + /// + private void SendReserveResponseAsync(MatchReserveUser reserveUser, string traceId, + ServerErrorCode errorCode = ServerErrorCode.Success) + { + _matchGroupProcessors.TryGetValue(reserveUser.UserInfo.MatchGroupId, out MatchGroupProcessor? matchGroup); + if (matchGroup is null) + { + // TODO @heon 나중에 정의 + return; + } + + var message = new ServerMessage + { + AckMatchReserve = new MS2GS_ACK_MATCH_RESERVE + { + TraceId = traceId, + ErrorCode = errorCode, + MatchPlayerInfo = reserveUser.UserInfo, + MatchStatusInfo = new MatchStatusInfo + { + Status = MatchStatusType.Reserved, + MatchStep = reserveUser.MatchStep, + WaitTimeSec = (int)reserveUser.WaitElapsed.TotalSeconds, + WaitTimeMaxSec = (int)matchGroup.MatchPolicy.MatchWaitTime.TotalSeconds, + } + } + }; + _mq.SendMessage(reserveUser.UserInfo.ServerName, message); + } + + private void SendReserveFailResponseAsync(MatchUserInfo userInfo, string traceId, + ServerErrorCode errorCode) + { + var message = new ServerMessage + { + AckMatchReserve = new MS2GS_ACK_MATCH_RESERVE + { + TraceId = traceId, ErrorCode = errorCode, MatchPlayerInfo = userInfo + } + }; + _mq.SendMessage(userInfo.ServerName, message); + } + + /// + /// 매칭 취소 요청을 처리하는 핸들러 + /// 대기 중인 사용자를 매칭 큐에서 제거하고 결과를 게임 서버로 응답 + /// + /// 매칭 취소 요청 메시지 (플레이어 정보 포함) + private async Task OnCancelAsync(GS2MS_REQ_MATCH_CANCEL message) + { + string? userGuid = message.UserGuid; + string? targetServerName = message.ServerName; + try + { + // 사용자 저장소에서 매칭 취소 처리 - 존재하지 않더라도 취소는 성공으로 클라에 전송 + MatchReserveUser? matchReserveUser = _matchUserRepository.CancelUser(userGuid); + if (matchReserveUser is null) + { + SendCancelResponse(targetServerName, userGuid, message.TraceId, ServerErrorCode.Success); + return; + } + + // 유저가 요청한 매칭 그룹이 존재하지 않음 - 존재하지 않더라도 취소는 성공으로 클라에 전송 + _matchGroupProcessors.TryGetValue(matchReserveUser.UserInfo.MatchGroupId, + out MatchGroupProcessor? matchGroup); + if (matchGroup is null) + { + SendCancelResponse(targetServerName, userGuid, message.TraceId, ServerErrorCode.Success); + return; + } + + // 매칭 취소가 가능한 상태인 지 검사 + if (CaneMatchReserveStatus(matchReserveUser.UserInfo.Status)) + { + matchReserveUser.UserInfo.Status = MatchStatusType.Cancel; + // 게임 서버로 취소 결과 응답 전송 (통합된 메서드 사용) + SendCancelResponse(targetServerName, userGuid, message.TraceId); + // 정상 적인 매칭 취소 - 비즈니스 로그 + matchGroup.BusinessLogMatchCancel(matchReserveUser.UserInfo); + } + else + { + SendCancelResponse(targetServerName, userGuid, message.TraceId, ServerErrorCode.MatchCancelFail); + } + } + catch (ResultException ex) + { + _logger.error($"Match OnCancelAsync => {ex}"); + } + catch (Exception ex) + { + // 오류 발생시 로그 기록 (취소는 별도 에러 응답 없음) + _logger.error($"Match cancel Error for user {userGuid}: {ex}"); + SendCancelResponse(targetServerName, userGuid, message.TraceId, ServerErrorCode.MatchServerException); + } + + await Task.CompletedTask; + } + + /// + /// 매칭 취소 응답 메시지 전송 + /// + private void SendCancelResponse(string serverName, string userGuid, string traceId, + ServerErrorCode errorCode = ServerErrorCode.Success) + { + var message = new ServerMessage + { + AckMatchCancel = new MS2GS_ACK_MATCH_CANCEL + { + MatchCancelType = MatchCancelType.Normal, + UserGuid = userGuid, + TraceId = traceId, + ErrorCode = errorCode + } + }; + _mq.SendMessage(serverName, message); + } + + /// + /// 매치 그룹을 가져오거나 없으면 새로 생성 + /// + public Task GetOrCreateMatchGroupAsync(string matchGroupId) + { + ValidateMatchGroupParameter(matchGroupId); + + // 이미 존재하면 반환 + if (_matchGroupProcessors.TryGetValue(matchGroupId, out MatchGroupProcessor? existingOrchestrator)) + { + return Task.FromResult(existingOrchestrator); + } + + // 새로 생성하여 추가 + MatchGroupProcessor matchGroup = CreateMatchGroupManager(matchGroupId); + if (_matchGroupProcessors.TryAdd(matchGroupId, matchGroup)) + { + RunMatchGroupProcessor(matchGroup); + _logger.info("Created new match group: {0}", matchGroupId); + return Task.FromResult(matchGroup); + } + + // 동시성 문제로 추가 실패시 기존 것을 다시 조회해서 반환 + if (_matchGroupProcessors.TryGetValue(matchGroupId, out MatchGroupProcessor? concurrentOrchestrator)) + { + return Task.FromResult(concurrentOrchestrator); + } + + throw new InvalidOperationException($"Failed to create or retrieve match group: {matchGroupId}"); + } + + /// + /// 매칭 그룹 중지 및 제거 + /// + public Task StopMatchGroupAsync(string matchGroupId) + { + if (!IsValidMatchGroupId(matchGroupId)) + return Task.FromResult(false); + + if (_matchGroupProcessors.TryRemove(matchGroupId, out MatchGroupProcessor? matchMakingGroupManager)) + { + try + { + return Task.FromResult(true); + } + catch (Exception ex) + { + _logger.error($"Error stopping match group => {ex}"); + return Task.FromResult(false); + } + } + + return Task.FromResult(false); + } + + /// + /// 매치 그룹이 존재하는지 확인 + /// + public bool HasMatchGroup(string matchGroupId) + { + return IsValidMatchGroupId(matchGroupId) && _matchGroupProcessors.ContainsKey(matchGroupId); + } + + /// + /// 실행 중인 모든 매칭 그룹 목록 + /// + public IReadOnlyList GetRunningMatchGroups() + { + return _matchGroupProcessors.Keys.ToList().AsReadOnly(); + } + + private void ValidateMatchGroupParameter(string matchGroupId) + { + if (string.IsNullOrWhiteSpace(matchGroupId)) + throw new ArgumentException(MatchGroupNullOrEmptyMessage, nameof(matchGroupId)); + } + + private static bool IsValidMatchGroupId(string matchGroupId) + { + return !string.IsNullOrWhiteSpace(matchGroupId); + } + + private MatchGroupProcessor CreateMatchGroupManager(string matchGroupId) + { + return _matchGroupProcessorFactory.CreateMatchGroupProcessor(matchGroupId); + } + + /// + /// 매칭 정책을 업데이트 한다. + /// queProcessor에서 호출하여 스레드 안전하게 처리함 + /// + /// 매칭 풀 정보 + private async Task OnUpdateMatchMeta(MatchPoolInfo matchPoolInfo) + { + _que.AddWorkFirstAsync(async () => + { + await _matchMetaManager.OnUpdateMatchMetaAsync(); + MatchGroupProcessor matchGroup = await GetOrCreateMatchGroupAsync(matchPoolInfo.MatchGroupId); + matchGroup.OnUpdateMatchMeta(); + }); + await Task.CompletedTask; + } + + /// + /// QueProcessor에 등록해서 주기적으로 호출함 + /// Mq를 통해 수신된 메시지와의 동기화를 위해서 QueProcessor에 등록함 + /// + private void RunMatchGroupProcessor(MatchGroupProcessor matchGroupProcessor) + { + _ = Task.Run(async () => + { + while (!_cts.IsCancellationRequested) + { + // matchGroupProcessor 별로 매칭 시간을 조절하도록 수정. + await Task.Delay(matchGroupProcessor.MatchPolicy.MatchLoopIntervalMs, _cts.Token); + _que.AddWorkFirstAsync(async () => + { + await matchGroupProcessor.RunMatchingAsync(); + }); + } + }); + } + + // /// + // /// QueProcessor에 등록해서 주기적으로 호출함 + // /// Mq를 통해 수신된 메시지와의 동기화를 위해서 QueProcessor에 등록함 + // /// + // private void RunMatchGroupProcessorAsync() + // { + // // _ = Task.Run(async () => + // // { + // // while (!_cts.IsCancellationRequested) + // // { + // // // _matchGroup 별로 매칭 시간을 조절하도록 수정. + // // await Task.Delay(_matchMetaManager.MatchInterval, _cts.Token); + // // _que.AddWorkFirstAsync(async () => + // // { + // // IEnumerable tasks = _matchGroupProcessors.Values.ToList() + // // .Select(processor => processor.RunMatchingAsync()); + // // await Task.WhenAll(tasks); + // // }); + // // } + // // }); + // + // foreach (MatchGroupProcessor matchGroupProcessor in _matchGroupProcessors.Values) + // { + // _ = Task.Run(async () => + // { + // while (!_cts.IsCancellationRequested) + // { + // // matchGroupProcessor 별로 매칭 시간을 조절하도록 수정. + // await Task.Delay(matchGroupProcessor.MatchPolicy.MatchLoopIntervalMs, _cts.Token); + // _que.AddWorkFirstAsync(async () => + // { + // await matchGroupProcessor.RunMatchingAsync(); + // }); + // } + // }); + // } + // + // // _ = Task.Run(async () => + // // { + // // while (!_cts.IsCancellationRequested) + // // { + // // // _matchGroup 별로 매칭 시간을 조절하도록 수정. + // // await Task.Delay(_matchMetaManager.MatchInterval, _cts.Token); + // // _que.AddWorkFirstAsync(async () => + // // { + // // IEnumerable tasks = _matchGroupProcessors.Values.ToList() + // // .Select(processor => processor.RunMatchingAsync()); + // // await Task.WhenAll(tasks); + // // }); + // // } + // // }); + // } + + private bool CaneMatchReserveStatus(MatchStatusType matchStatusType) + { + return matchStatusType switch + { + // todo MatchStatusType.Progress 상태일 때는 예외 처리해야함 + MatchStatusType.Progress or MatchStatusType.JoinRoomChecked or MatchStatusType.Success + => + false, + _ => true + }; + } + + #region Dispose + + private async Task StopAllMatchGroupsAsync() + { + await _cts.CancelAsync(); + Task[] stopTasks = _matchGroupProcessors.Keys + .Select(StopMatchGroupAsync) + .ToArray(); + + await Task.WhenAll(stopTasks); + + _logger.info("All match groups stopped"); + } + + public void Dispose() + { + StopAllMatchGroupsAsync().Wait(); + _sequenceWorker.Dispose(); + } + + #endregion +} diff --git a/MatchServer/Services/MatchServiceNew.Cmd.cs b/MatchServer/Services/MatchServiceNew.Cmd.cs new file mode 100644 index 0000000..5fd9235 --- /dev/null +++ b/MatchServer/Services/MatchServiceNew.Cmd.cs @@ -0,0 +1,79 @@ +using ServerCore; + +namespace MatchServer; + +public partial class MatchServiceNew +{ + private async Task OnMatchCheatCmd(IEnumerable cmdArgs) + { + if (_serverConfig.ServiceType == nameof(ServiceType.Live)) + { + _logger.error($"Not Support CheatCmd For Live Service"); + return; + } + + if (!cmdArgs.Any()) + { + _logger.error($"OnMatchCheatCmd No Cmd"); + return; + } + + string cmd = cmdArgs.FirstOrDefault() ?? string.Empty; + try + { + string[] args = [.. cmdArgs.Skip(1)]; + + switch (cmd) + { + case "check_event": + _que.AddWorkFirstAsync(async () => + { + _gameEventManager.ApplyGameEvent = Convert.ToBoolean(args[0]); + _logger.info($"CheatCmd check_event => {_gameEventManager.ApplyGameEvent}"); + await Task.CompletedTask; + }); + break; + + // 매칭 중인 모든 것을 초기화 - 게임이 중단되지는 않음 + case "reset": + _que.AddWorkFirstAsync( async () => + { + _matchGroupManagers.Clear(); + await Task.CompletedTask; + }); + break; + + // case "cancel": + // { + // string userGuid = args[0]; + // _matchUserRepository.RemoveUser(userGuid); + // } + // break; + // // 매치에 실패하도록 옵션 설정 + // case "option_fail": + // { + // int gameModeId = int.Parse(args[0]); + // string region = args[1]; + // var matchPoolInfo = new MatchPoolInfo(gameModeId, region, 0); + // await OnUpdateMatchMeta(matchPoolInfo); + // } + // break; + // // 처음 옵션으로 되돌림 + // case "option_restore": + // { + // int gameModeId = int.Parse(args[0]); + // string region = args[1]; + // var matchPoolInfo = new MatchPoolInfo(gameModeId, region, 0); + // await OnUpdateMatchMeta(matchPoolInfo); + // } + // break; + } + } + catch (Exception ex) + { + _logger.error($"{cmd} => " + ex); + } + + await Task.CompletedTask; + } +} diff --git a/MatchServer/Services/MatchServiceNew.cs b/MatchServer/Services/MatchServiceNew.cs new file mode 100644 index 0000000..447b74d --- /dev/null +++ b/MatchServer/Services/MatchServiceNew.cs @@ -0,0 +1,368 @@ +using System.Collections.Concurrent; +using Google.Protobuf; +using ServerBase; +using ServerCommon; +using ServerCore; +using static ServerMessage.Types; + +namespace MatchServer; + +/// +/// 여러 매칭 그룹을 관리하고 메시지 처리를 담당하는 통합 서비스 +/// +public partial class MatchServiceNew : IDisposable +{ + private const string MatchGroupNullOrEmptyMessage = "Match group cannot be null or empty"; + + private readonly IMatchMq _mq; + private readonly ServerConfig _serverConfig; + private readonly IMatchMetaManager _matchMetaManager; + private readonly GameEventManager _gameEventManager; + private readonly NLog.Logger _logger; + private readonly ConcurrentDictionary _matchGroupManagers = new(); + + private readonly SequenceWorkQue _que = new(); + private readonly SequenceWorker _sequenceWorker; + private readonly InstanceRoomRepo _instanceRoomRepo; + private readonly IMatchGroupProcessorFactory _matchGroupProcessorFactory; + private readonly CancellationTokenSource _cts; + + public MatchServiceNew( + InstanceRoomRepo instanceRoomRepo, + IMatchMetaManager matchMetaManager, + GameEventManager gameEventManager, + IMatchGroupProcessorFactory matchGroupProcessorFactory, + IMatchMq mq, + ServerConfig serverConfig) + { + _mq = mq; + _serverConfig = serverConfig; + _matchMetaManager = matchMetaManager; + _gameEventManager = gameEventManager; + _instanceRoomRepo = instanceRoomRepo; + _matchGroupProcessorFactory = matchGroupProcessorFactory; + _logger = Log.getLogger(); + + _cts = new CancellationTokenSource(); + + _sequenceWorker = new SequenceWorker(_que); + _ = _sequenceWorker.StartAsync(_cts.Token); + RunMatchUpLoopAsync(); + } + + public async Task AddWorkAsync(TMessage recvMessage) where TMessage : IMessage + { + await (recvMessage switch + { + GS2MS_REQ_MATCH_RESERVE reserve => + _que.AddAsync(() => OnReserveAsync(reserve)), + + GS2MS_REQ_MATCH_CANCEL cancel => + _que.AddAsync(() => OnCancelAsync(cancel)), + + GS2MS_NTF_MATCH_GAME_JOIN join => + _que.AddAsync(() => OnGameUserJoinAsync(join)), + + GS2MS_NTF_MATCH_GAME_QUIT quit => + _que.AddAsync(() => OnGameUserQuitAsync(quit)), + + GS2MS_NTF_MATCH_CHANGE_GAME_STATE changeState => + _que.AddAsync(() => OnGameChangeSateAsync(changeState)), + + GS2MS_NTF_MATCH_CHEAT_CMD { Args: var args } => + _que.AddAsync(() => OnMatchCheatCmd(args)), + + _ => throw new NotSupportedException($"Message type {typeof(TMessage).Name} is not supported") + }); + } + + /// + /// 유저가 실제로 게임에 입장 했는 지 여부를 체크 + /// + /// + private async Task OnGameUserJoinAsync(GS2MS_NTF_MATCH_GAME_JOIN message) + { + // 유저가 실제로 게임에 입장 했는 지 여부를 체크 + // todo @heon 일정 시간동안 입장하지 않으면 유저 삭제 처리? + try + { + var matchGroupId = GetMatchGroupId(message.RoomId); + _matchGroupManagers.TryGetValue(matchGroupId, out MatchGroupManager? matchGroup); + if (matchGroup is null) + { + return; + } + await matchGroup.OnGameUserJoinAsync(message.RoomId, message.UserGuid); + await Task.CompletedTask; + } + catch (Exception ex) + { + _logger.error($"OnGameUserJoinAsync => {ex}"); + } + } + + private async Task OnGameChangeSateAsync(GS2MS_NTF_MATCH_CHANGE_GAME_STATE message) + { + try + { + var matchGroupId = GetMatchGroupId(message.RoomId); + _matchGroupManagers.TryGetValue(matchGroupId, out MatchGroupManager? matchGroupManager); + await matchGroupManager.OnGameChangeStateAsync(message.RoomId, message.State); + } + catch (Exception ex) + { + _logger.error($"OnGameChangeSateAsync => {ex}"); + } + } + + private async Task OnGameUserQuitAsync(GS2MS_NTF_MATCH_GAME_QUIT message) + { + try + { + var matchGroupId = GetMatchGroupId(message.RoomId); + _matchGroupManagers.TryGetValue(matchGroupId, out MatchGroupManager? matchGroupManager); + await matchGroupManager.OnGameUserQuitAsync(message.RoomId, message.UserGuid); + } + catch (Exception ex) + { + _logger.error($"OnGameQuitAsync =>{ex}"); + } + } + + /// + /// 매칭 예약 요청을 처리하는 핸들러 + /// 사용자를 매칭 대기 큐에 추가하고 결과를 게임 서버로 응답 + /// + /// 매칭 예약 요청 메시지 (사용자 정보 포함) + private async Task OnReserveAsync(GS2MS_REQ_MATCH_RESERVE message) + { + try + { + var matchGroupId = message.MatchUserInfo.MatchGroupId; + MatchGuard.InvalidMatchGroupId(matchGroupId); + + var matchPoolInfo = MatchPoolInfo.ConvertByMatchGroupId(matchGroupId); + int gameModeId = matchPoolInfo.GameModeId; + + bool isValidGameModeId = _matchMetaManager.HasGameMode(gameModeId); + MatchGuard.InvalidGameModeId(isValidGameModeId, gameModeId); + + var matchGroup = await GetOrCreateMatchGroupAsync(message.MatchUserInfo.MatchGroupId); + MatchGuard.NotNull(matchGroup, () => $"Match group not found for user {message.MatchUserInfo.UserGuid}"); + + await matchGroup.OnReserveAsync(message.MatchUserInfo, message.TraceId); + } + catch (ResultException ex) + { + _logger.error($"Match reserve Exception userGuid: {message.MatchUserInfo.UserGuid} => {ex}"); + } + catch (Exception ex) + { + // 오류 발생시 로그 기록 및 에러 응답 전송 (통합된 메서드 사용) + _logger.error($"Match reserve Exception {message.MatchUserInfo.UserGuid}: {ex}"); + } + } + + /// + /// 매칭 취소 요청을 처리하는 핸들러 + /// 대기 중인 사용자를 매칭 큐에서 제거하고 결과를 게임 서버로 응답 + /// + /// 매칭 취소 요청 메시지 (플레이어 정보 포함) + private async Task OnCancelAsync(GS2MS_REQ_MATCH_CANCEL message) + { + string? userGuid = message.UserGuid; + string? targetServerName = message.ServerName; + try + { + // 어느 매칭 그룹에 속해 있는 지 알 수 없으므로 모든 매칭 그룹에 취소 요청을 전달 + foreach (var matchGroupManager in _matchGroupManagers.Values) + { + await matchGroupManager.OnCancelAsync(message.UserGuid, targetServerName, message.TraceId); + } + + // 처리 결과 로그 기록 + _logger.debug( + $"Match cancel handled for user {userGuid}"); + } + catch (ResultException ex) + { + _logger.error($"Match OnCancelAsync => {ex}"); + } + catch (Exception ex) + { + // 오류 발생시 로그 기록 (취소는 별도 에러 응답 없음) + _logger.error($"Match cancel Error for user {userGuid}: {ex}"); + SendCancelResponse(targetServerName, userGuid, message.TraceId, ServerErrorCode.MatchServerException); + } + } + + /// + /// 매칭 취소 응답 메시지 전송 + /// + private void SendCancelResponse(string serverName, string userGuid, string traceId, + ServerErrorCode errorCode = ServerErrorCode.Success) + { + var message = new ServerMessage + { + AckMatchCancel = new MS2GS_ACK_MATCH_CANCEL + { + MatchCancelType = MatchCancelType.Normal, + UserGuid = userGuid, + TraceId = traceId, + ErrorCode = errorCode + } + }; + _mq.SendMessage(serverName, message); + } + + /// + /// 매치 그룹을 가져오거나 없으면 새로 생성 + /// + public Task GetOrCreateMatchGroupAsync(string matchGroupId) + { + ValidateMatchGroupParameter(matchGroupId); + + // 이미 존재하면 반환 + if (_matchGroupManagers.TryGetValue(matchGroupId, out MatchGroupManager? existingManager)) + { + return Task.FromResult(existingManager); + } + + // 새로 생성하여 추가 + var matchGroup = CreateMatchGroupManager(matchGroupId); + if (_matchGroupManagers.TryAdd(matchGroupId, matchGroup)) + { + _logger.info("Created new match group: {0}", matchGroupId); + return Task.FromResult(matchGroup); + } + + // 동시성 문제로 추가 실패시 기존 것을 다시 조회해서 반환 + if (_matchGroupManagers.TryGetValue(matchGroupId, out MatchGroupManager? concurrentOrchestrator)) + { + return Task.FromResult(concurrentOrchestrator); + } + + throw new InvalidOperationException($"Failed to create or retrieve match group: {matchGroupId}"); + } + + /// + /// 매칭 그룹 중지 및 제거 + /// + public Task StopMatchGroupAsync(string matchGroupId) + { + if (!IsValidMatchGroupId(matchGroupId)) + return Task.FromResult(false); + + if (_matchGroupManagers.TryRemove(matchGroupId, out var matchMakingGroupManager)) + { + try + { + return Task.FromResult(true); + } + catch (Exception ex) + { + _logger.error($"Error stopping match group => {ex}"); + return Task.FromResult(false); + } + } + + return Task.FromResult(false); + } + + /// + /// 매치 그룹이 존재하는지 확인 + /// + public bool HasMatchGroup(string matchGroupId) + { + return IsValidMatchGroupId(matchGroupId) && _matchGroupManagers.ContainsKey(matchGroupId); + } + + /// + /// 실행 중인 모든 매칭 그룹 목록 + /// + public IReadOnlyList GetRunningMatchGroups() + { + return _matchGroupManagers.Keys.ToList().AsReadOnly(); + } + + private void ValidateMatchGroupParameter(string matchGroupId) + { + if (string.IsNullOrWhiteSpace(matchGroupId)) + throw new ArgumentException(MatchGroupNullOrEmptyMessage, nameof(matchGroupId)); + } + + private static bool IsValidMatchGroupId(string matchGroupId) + { + return !string.IsNullOrWhiteSpace(matchGroupId); + } + + private MatchGroupManager CreateMatchGroupManager(string matchGroupId) + { + var matchGroupProcessor = _matchGroupProcessorFactory.CreateMatchGroupProcessor(matchGroupId); + return new MatchGroupManager(matchGroupProcessor, _instanceRoomRepo, _mq); + } + + /// + /// 매칭 정책을 업데이트 한다. + /// queProcessor에서 호출하여 스레드 안전하게 처리함 + /// + /// 매칭 풀 정보 + private async Task OnUpdateMatchMeta(MatchPoolInfo matchPoolInfo) + { + _que.AddWorkFirstAsync(async () => + { + await _matchMetaManager.OnUpdateMatchMetaAsync(); + var matchGroup = await GetOrCreateMatchGroupAsync(matchPoolInfo.MatchGroupId); + await matchGroup.OnUpdateMatchMetaAsync(); + }); + await Task.CompletedTask; + } + + /// + /// QueProcessor에 등록해서 주기적으로 호출함 + /// Mq를 통해 수신된 메시지와의 동기화를 위해서 QueProcessor에 등록함 + /// + private void RunMatchUpLoopAsync() + { + _ = Task.Run(async () => + { + while (!_cts.IsCancellationRequested) + { + await Task.Delay(_matchMetaManager.MatchInterval, _cts.Token); + _que.AddWorkFirstAsync(async () => + { + IEnumerable tasks = _matchGroupManagers.Values.ToList() + .Select(matchManager => matchManager.RunMatchUpAsync()); + await Task.WhenAll(tasks); + }); + } + }); + } + + #region Dispose + + private async Task StopAllMatchGroupsAsync() + { + await _cts.CancelAsync(); + Task[] stopTasks = _matchGroupManagers.Keys + .Select(StopMatchGroupAsync) + .ToArray(); + + await Task.WhenAll(stopTasks); + + _logger.info("All match groups stopped"); + } + + public void Dispose() + { + StopAllMatchGroupsAsync().Wait(); + _sequenceWorker.Dispose(); + } + + private string GetMatchGroupId(string roomId) + { + return roomId.Split(":").FirstOrDefault() ?? string.Empty; + } + + #endregion +} diff --git a/MatchServer/Services/Repositories/MatchRoomRepository.cs b/MatchServer/Services/Repositories/MatchRoomRepository.cs new file mode 100644 index 0000000..2154523 --- /dev/null +++ b/MatchServer/Services/Repositories/MatchRoomRepository.cs @@ -0,0 +1,136 @@ +using ServerCommon; +using ServerCore; + +namespace MatchServer; + +// todo redis와 연동해서 저장해야함 +// +public class MatchRoomRepository +{ + private readonly Dictionary _matchedRooms = []; + private readonly NLog.Logger _logger; + + public int RoomCount => _matchedRooms.Count; + private Func? _onRoomRemoved; + + public MatchRoomRepository() + { + _logger = Log.getLogger(); + } + + public int GetRoomCountAsync() + { + return _matchedRooms.Count; + } + + public void SetOnRoomRemovedEvent(Func onRoomRemoved) + { + this._onRoomRemoved = onRoomRemoved; + } + + public IEnumerable GetRooms(string matchGroupId) + { + return _matchedRooms.Values.Where(x => matchGroupId == x.MatchGroupId); + } + + public IEnumerable GetCanEnterRooms(string matchGroupId, MatchPolicy policy) + { + return _matchedRooms.Values + .Where(x => + matchGroupId == x.MatchGroupId && + x.CanEnter() && + x.MatchPolicy.Version == policy.Version); + } + + public MatchRoom? GetRoom(string roomId) + { + _matchedRooms.TryGetValue(roomId, out MatchRoom? room); + if (room == null) + { + _logger.error($"RoomId: {roomId} not found"); + return null; + } + + return room; + } + + public bool AddRooms(IEnumerable rooms) + { + int addedCount = rooms.Count(room => _matchedRooms.TryAdd(room.Id, room)); + _logger.debug($"Added {addedCount} new matched rooms. Total rooms: {_matchedRooms.Count}"); + return true; + } + + public bool RemoveMember(string roomId, string userGuid) + { + if (string.IsNullOrEmpty(roomId) || string.IsNullOrEmpty(userGuid)) + return false; + + MatchRoom? room = _matchedRooms.GetValueOrDefault(roomId); + if (room == null) + { + _logger.error($"RoomId: {roomId} not found"); + return false; + } + + var result = room.RemoveMember(userGuid); + _logger.debug($"Member {userGuid} removed from room {roomId}"); + return result; + } + + public void RemoveMemberByUserGuid(string userGuid) + { + // MatchRoom? modifiedRoom = null; + foreach (var room in _matchedRooms.Values) + { + if (room.RemoveMember(userGuid)) + { + // modifiedRoom = room; + break; + } + } + + // // 유저가 삭제된 룸이 비어 있으면 룸을 삭제 - Destory에서 처리하도록 수정함 + // if (modifiedRoom is not null) + // { + // _logger.debug($"Member {userGuid} removed from room {modifiedRoom.Id}"); + // if (modifiedRoom.IsEmpty()) + // { + // _matchedRooms.Remove(modifiedRoom.Id); + // } + // } + } + + public Task ChangeGameStateAsync(string roomId, MatchGameStateType state) + { + var room = _matchedRooms.GetValueOrDefault(roomId); + if (room == null) + { + _logger.error($"RoomId: {roomId} not found"); + return Task.FromResult(false); + } + room.GameState = state; + _logger.info($"Room {roomId} state changed to {state}"); + return Task.FromResult(true); + } + + public void Clear() + { + _matchedRooms.Clear(); + } + + public void RemoveRoom(string roomId) + { + if (_matchedRooms.Remove(roomId)) + { + _logger.debug($"Room {roomId} removed"); + _onRoomRemoved?.Invoke(roomId); + } + } + + public MatchRoom? FindRoom(string roomId) + { + _matchedRooms.TryGetValue(roomId, out MatchRoom room); + return room; + } +} diff --git a/MatchServer/Services/Repositories/MatchUserRepository.cs b/MatchServer/Services/Repositories/MatchUserRepository.cs new file mode 100644 index 0000000..c582dab --- /dev/null +++ b/MatchServer/Services/Repositories/MatchUserRepository.cs @@ -0,0 +1,92 @@ +using ServerCommon; +using ServerCore; + +namespace MatchServer; + +public class MatchUserRepository +{ + private readonly Dictionary _matchUsers = new(); + private readonly NLog.Logger _logger; + + public int TotalUserCount => _matchUsers.Count; + public void Clear() + { + _matchUsers.Clear(); + } + + public MatchUserRepository() + { + _logger = Log.getLogger(); + } + + // 중복 예약이 들어오면 나중에 온 요청을 덮어 쓴다. - 어짜피 중복 접속이 안되고, 중복 요청이면 해킹을 의심해야 한다. + public (MatchReserveUser? curUser, MatchReserveUser? oldUser) ReserveUser(MatchUserInfo userInfo) + { + // 중복이 있는 경우 기존 정보를 덮어 쓴다. + if (_matchUsers.TryGetValue(userInfo.UserGuid, out MatchReserveUser? oldUser)) + { + userInfo.Status = MatchStatusType.Reserved; + var newMatchUser = new MatchReserveUser + { + UserInfo = userInfo + }; + _matchUsers[userInfo.UserGuid] = newMatchUser; + return (newMatchUser, oldUser); + } + userInfo.Status = MatchStatusType.Reserved; + var reserveUser = new MatchReserveUser { UserInfo = userInfo }; + return _matchUsers.TryAdd(userInfo.UserGuid, reserveUser) ? (reserveUser, null) : (null, null); + } + + public MatchReserveUser? CancelUser(string userGuid) + { + return _matchUsers.Remove(userGuid, out MatchReserveUser? matchReserveUser) ? matchReserveUser : null; + } + + public IEnumerable GetWaitingUsers(string matchGroupId) + { + return _matchUsers.Values.Where(x => + x.UserInfo.MatchGroupId == matchGroupId && + // (x.UserInfo.StartTime + Duration.FromTimeSpan(x.MatchDelay)).ToDateTime() <= DateTime.UtcNow && + x.UserInfo.Status is MatchStatusType.Progress or MatchStatusType.Reserved); + } + + /// + /// _matchUsers에서 matchGroupId에 해당하는 하는 대상 중에 HasTimeout이 true인 유저를 삭제하고 해당 유저 목륵을 반환함 + /// + /// + /// + /// + public IEnumerable RemoveTimeoutUsers(string matchGroupId, TimeSpan maxWaitTime) + { + foreach (var kvp in _matchUsers.ToList()) // 딕셔너리 스냅샷만 생성 + { + if (kvp.Value.UserInfo.MatchGroupId == matchGroupId && + kvp.Value.UserInfo.Status == MatchStatusType.Reserved && + kvp.Value.HasTimeout(maxWaitTime)) + { + _matchUsers.Remove(kvp.Key); + yield return kvp.Value; // 지연 반환 + } + } + } + + public void RemoveUsers(IEnumerable users) + { + foreach (var user in users) + { + _matchUsers.Remove(user.UserInfo.UserGuid); + } + } + + public MatchReserveUser? FindUser(string userGuid) + { + _matchUsers.TryGetValue(userGuid, out MatchReserveUser? matchReserveUser); + return matchReserveUser; + } + + public bool RemoveUser(string userGuid) + { + return _matchUsers.Remove(userGuid); + } +} diff --git a/MatchServer/Services/ValueObjects/MatchExecutionResult.cs b/MatchServer/Services/ValueObjects/MatchExecutionResult.cs new file mode 100644 index 0000000..daee1d6 --- /dev/null +++ b/MatchServer/Services/ValueObjects/MatchExecutionResult.cs @@ -0,0 +1,13 @@ +using ServerCommon; + +namespace MatchServer; + +public class MatchExecutionResult +{ + public bool HasMatches => EnterExistingRoomUsers.Any() || MatchedUsers.Any(); + public IEnumerable UnmatchedUsers { get; init; } = []; + public IEnumerable UpdatedRooms { get; init; } = []; + public IEnumerable NewMatchedRooms { get; init; } = []; + public IEnumerable EnterExistingRoomUsers { get; init; } = []; + public IEnumerable MatchedUsers { get; init; } = []; +} diff --git a/MatchServer/Services/ValueObjects/ProcessedUsersResult.cs b/MatchServer/Services/ValueObjects/ProcessedUsersResult.cs new file mode 100644 index 0000000..8ae3fd7 --- /dev/null +++ b/MatchServer/Services/ValueObjects/ProcessedUsersResult.cs @@ -0,0 +1,180 @@ +using ServerCommon; + +namespace MatchServer; + +/// +/// 타임아웃 처리 결과를 나타내는 클래스 +/// 매칭되지 않은 유저들의 타임아웃 검사 후 분류된 결과를 포함 +/// +public class ProcessedUsersResult +{ + /// + /// 계속해서 매칭 대기를 할 유저들 + /// + public IReadOnlyList ContinuingUsers { get; init; } = []; + + /// + /// 타임아웃이나 오류로 인해 매칭에서 제외된 유저들 + /// + public IReadOnlyList FailedUsers { get; init; } = []; + + /// + /// 타임아웃된 유저들 (FailedUsers의 서브셋) + /// + public IReadOnlyList TimeoutUsers { get; init; } = []; + + /// + /// 오류로 인해 실패한 유저들 (FailedUsers의 서브셋) + /// + public IReadOnlyList ErrorUsers { get; init; } = []; + + /// + /// 전체 처리된 유저 수 + /// + public int TotalProcessedCount => ContinuingUsers.Count + FailedUsers.Count; + + /// + /// 타임아웃된 유저 수 + /// + public int TimeoutCount => TimeoutUsers.Count; + + /// + /// 오류로 실패한 유저 수 + /// + public int ErrorCount => ErrorUsers.Count; + + /// + /// 계속 대기할 유저 수 + /// + public int ContinuingCount => ContinuingUsers.Count; + + /// + /// 전체 실패한 유저 수 + /// + public int FailedCount => FailedUsers.Count; + + /// + /// 처리 결과가 있는지 여부 + /// + public bool HasResults => TotalProcessedCount > 0; + + /// + /// 실패한 유저가 있는지 여부 + /// + public bool HasFailures => FailedCount > 0; + + /// + /// 계속 대기할 유저가 있는지 여부 + /// + public bool HasContinuingUsers => ContinuingCount > 0; + + /// + /// 빈 결과 생성 + /// + public static ProcessedUsersResult Empty() => new(); + + /// + /// 성공적인 처리 결과 생성 + /// + public static ProcessedUsersResult Create( + IEnumerable continuingUsers, + IEnumerable timeoutUsers, + IEnumerable errorUsers) + { + var continuingList = continuingUsers?.ToList() ?? []; + var timeoutList = timeoutUsers?.ToList() ?? []; + var errorList = errorUsers?.ToList() ?? []; + var failedList = timeoutList.Concat(errorList).ToList(); + + return new ProcessedUsersResult + { + ContinuingUsers = continuingList.AsReadOnly(), + FailedUsers = failedList.AsReadOnly(), + TimeoutUsers = timeoutList.AsReadOnly(), + ErrorUsers = errorList.AsReadOnly() + }; + } + + /// + /// 모든 유저가 계속 대기하는 결과 생성 + /// + public static ProcessedUsersResult AllContinuing(IEnumerable users) + { + var userList = users?.ToList() ?? []; + return new ProcessedUsersResult + { + ContinuingUsers = userList.AsReadOnly(), + FailedUsers = [], + TimeoutUsers = [], + ErrorUsers = [] + }; + } + + /// + /// 모든 유저가 타임아웃된 결과 생성 + /// + public static ProcessedUsersResult AllTimeout(IEnumerable users) + { + var userList = users?.ToList() ?? []; + return new ProcessedUsersResult + { + ContinuingUsers = [], + FailedUsers = userList.AsReadOnly(), + TimeoutUsers = userList.AsReadOnly(), + ErrorUsers = [] + }; + } + + /// + /// 모든 유저가 오류로 실패한 결과 생성 + /// + public static ProcessedUsersResult AllError(IEnumerable users) + { + var userList = users?.ToList() ?? []; + return new ProcessedUsersResult + { + ContinuingUsers = [], + FailedUsers = userList.AsReadOnly(), + TimeoutUsers = [], + ErrorUsers = userList.AsReadOnly() + }; + } + + /// + /// 처리 결과를 요약한 문자열 반환 + /// + public string GetSummary() + { + return $"Processed: {TotalProcessedCount} users - " + + $"Continuing: {ContinuingCount}, " + + $"Timeout: {TimeoutCount}, " + + $"Error: {ErrorCount}"; + } + + /// + /// 실패 이유별 유저 목록을 딕셔너리로 반환 + /// + public Dictionary> GetFailuresByReason() + { + var result = new Dictionary>(); + + foreach (var user in FailedUsers) + { + if (!result.ContainsKey(user.UserInfo.Status)) + { + result[user.UserInfo.Status] = []; + } + result[user.UserInfo.Status].Add(user); + } + + return result; + } + + /// + /// 디버깅을 위한 상세 정보 반환 + /// + public override string ToString() + { + return GetSummary(); + } +} diff --git a/Protocol/Protocol.csproj b/Protocol/Protocol.csproj index 7b49d28..b54e0d1 100644 --- a/Protocol/Protocol.csproj +++ b/Protocol/Protocol.csproj @@ -1,80 +1,72 @@ - - - - net8.0 - enable - enable - 54783c90-d68f-44ae-9706-9bb7f7654d7d - Debug;Release;Shipping - true - 8600,8602,8603 - true - - - - full - $(DefineConstants); - - - - full - $(DefineConstants); - - - - full - $(DefineConstants); - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetClient.dll - - - ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetServer.dll - - - - - - - - - - - - - + + + net8.0 + enable + enable + 54783c90-d68f-44ae-9706-9bb7f7654d7d + Debug;Release;Shipping + true + 8600,8602,8603 + true + + + full + $(DefineConstants); + + + full + $(DefineConstants); + + + full + $(DefineConstants); + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetClient.dll + + + ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetServer.dll + + + + + + + + + + diff --git a/Protocol/client-proto/.svn/pristine/01/01089b1b43cf4488ddfd990500fad3e71f32d00d.svn-base b/Protocol/client-proto/.svn/pristine/01/01089b1b43cf4488ddfd990500fad3e71f32d00d.svn-base new file mode 100644 index 0000000..863da61 --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/01/01089b1b43cf4488ddfd990500fad3e71f32d00d.svn-base @@ -0,0 +1,72 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package protocol; + +option csharp_namespace = "Google.Protobuf.CES.Packet"; +//import "google/protobuf/timestamp.proto"; + + enum Protocol{ + + MOVE_NEXT = 0; + MOVE_COMBINE = 1; + VOICE = 30; + VOICE_STATE = 31; + MUSIC = 40; + MUSICBox = 41; + DANCE = 50; + DANCE_ReqRoomTime = 51; + DANCE_ResRoomTime = 52; + DANCE_StateUpdate = 53; + JUMP = 70; + GUIDE = 80; + PRESENTATION_PAGE = 81; + AIM = 100; + PARTY_MOVE_MEMBER = 110; + + BATTLE_MOVE = 120; // 이동,멈춤,점프... + BATTLE_AIM = 121; // 조준. + BATTLE_STATUS = 122; // 현재상태. + BATTLE_MANTLE = 123; // 기어오르기. + BATTLE_SHOOT = 130; // 총알발사. + BATTLE_DAMAGE = 131; // 피격. + BATTLE_RELOAD = 132; // 재장전 + BATTLE_DODGE = 133; // 구르기 + BATTLE_WEAPON_CHANGE = 134; // 무기교환 + BATTLE_WEAPON_ADD = 135; // 무기추가 + BATTLE_RESPAWN = 136; // 사망후 리스폰. + BATTLE_OBJECT_INTERACTION = 137; // 오브젝트 인터렉션 + BATTLE_FINISHED_LODING = 138; // 인게임 로딩 끝 + BATTLE_READY_TO_EXIT = 139; // 인던 나가기 전에 결과창 보는 중 + BATTLE_LAUNCH_CHARACTER = 140; // 범위 타격에 의해서 날아감 + BATTLE_PLAY_ANIMATION = 141; // 특정 상황에서 에니메이션 실행 할 때 + BATTLE_HIT_EFFECT = 142; // 캐릭터나 사물에 이펙트 표시 + + MOB_AI_ATTACK = 200; + MOB_AI_MOVETO = 201; + MOB_AI_FOCUS = 202; + MOB_AI_SYNCVAR = 203; + MOB_AI_SPAWN = 204; + MOB_AI_SHOOT = 205; + MOB_AI_RELOAD = 206; + + RUNNING_TRIGGER = 300; + RUNNING_ITEM = 301; + RUNNING_HURDLE_SPAWN = 302; + RUNNING_HURDLE_STATE = 303; + RUNNING_HURDLE_MOVE = 304; + RUNNING_HURDLE_TRAVAARSAL_STATE = 305; + + HOST_ROOMINFO = 401; // 호스트 테스트용 임시 패킷 + REQ_HOST_ROOMINFO = 402; // 호스트 테스트용 임시 패킷 + }; + + +// [END messages] diff --git a/Protocol/client-proto/.svn/pristine/16/168d7827b64c0f53f1b1d7a3fc6dfa13a3ea1b27.svn-base b/Protocol/client-proto/.svn/pristine/16/168d7827b64c0f53f1b1d7a3fc6dfa13a3ea1b27.svn-base new file mode 100644 index 0000000..aac98f8 --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/16/168d7827b64c0f53f1b1d7a3fc6dfa13a3ea1b27.svn-base @@ -0,0 +1,527 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package Packet; + +//import "google/protobuf/timestamp.proto"; +// [END declaration] + +// [START csharp_declaration] +option csharp_namespace = "Google.Protobuf.CES.Packet"; +// [END csharp_declaration] + +// [START messages] + +message SM_REQ_MOVE_NEXT +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float ang =4; // + optional float movevalue =5; // + optional int32 start =6; // ù ̵̸ true ̵θ false + optional int32 move_type = 7; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + optional int32 server_time=8; // Ŷ ð +} + +message SM_REQ_MOVE_COMBINE +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float lx =4; // + optional float ly =5; // + optional float lz =6; // + optional float ang =7; // + optional int32 move_speed =8; // + optional int32 move_type = 9; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + optional int32 start =10; // ù ̵̸ true ̵θ false + optional int32 server_time=11; // Ŷ ð +} + +message SM_REQ_TRACKING_TYPE +{ + optional int32 type =1; +} + +message SM_REQ_VOICE +{ + optional int32 sampleRate = 1; // + optional int32 numchannels = 2; // + optional int32 PCMSize = 3; // + repeated int32 data = 4; + optional int32 dataSize = 5; // +} + +message SM_REQ_MUSICBOX +{ + optional string propguid = 1; + optional int32 sampleRate = 2; // + optional int32 numchannels = 3; // + optional int32 PCMSize = 4; // + repeated int32 data = 5; + optional int32 dataSize = 6; // +} + +message SM_REQ_JUMP +{ + optional float sx =1; // + optional float sy =2; // + optional float sz =3; // + optional float lx =4; // + optional float ly =5; // + optional float lz =6; // + optional float ang =7; // + optional int32 start =8; // ù ̵̸ true ̵θ false + optional int32 server_time=9; // Ŷ ð +} + +message SM_REQ_GUIDE +{ + optional string guoup =1; // + optional int32 operatorid =2; // + optional int32 guidetype =3; // +} + +message SM_REQ_PRESENTATION_PAGE +{ + string pageurl =1; // + int32 pageid =2; // +} + + +message SM_REQ_AIM +{ + optional float pitch =1; // +} + +message SM_REQ_PARTY_MOVE_MEMBER +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // +} + +message SM_REQ_VOICE_STATE +{ + optional string nickname =1; // + optional int32 state =2; // +} + +message SM_REQ_DANCE_INFO +{ + optional int32 danceid = 1; // ̵ + optional int32 motionid = 2; // + optional int32 dancetype = 3; // Ÿ޺ + optional int64 timestamp = 4; // ð +} + +message SM_REQ_DANCE_ROOMTIME +{ + int64 sendmytime = 1; +} + +message SM_SEND_DANCE_ROOMTIME +{ + int64 sendroomtime = 1; + string guid = 2; +} + +message SM_SEND_DANCE_STATE +{ + optional int32 state = 1; + optional int64 sendroomtime = 2; + optional int32 state01 = 3; + optional int32 state02 = 4; + optional int32 state03 = 5; + optional string str01 = 6; + optional string str02 = 7; + optional string str03 = 8; +} + + +////////////////////////////////////// + +// Ѿ +message SM_DATA_BATTLE_BULLET +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float yaw =4; // + optional float pitch =5; // + optional int32 damage =6; // +} + +message SM_DATA_WEAPON_DATA +{ + int32 weapontype = 1; + int32 currentbullet = 2; // Ѿ. + int32 maxbullet = 3; // źâ ִź +} + +message SM_REQ_BATTLE_MOVE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + int32 move_speed =11; // ̵ӵ + int32 move_type = 12; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int32 type =13; // ̵ȭŸ + float aim_yaw =14; // + float aim_pitch =15; // + int32 cur_hp=16; // + int32 shoot_type = 17; // ѱ߻ + int32 cur_weapontype=18;// Ȱȭ + int32 server_time=19; // Ŷ ð + repeated SM_DATA_WEAPON_DATA weapontypes=20; //ִ +} + +// ij . +message SM_REQ_BATTLE_STATUS +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 shoot_type = 8; // ѱ߻ + int32 cur_weapontype=9; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=10; //ִ + int32 podcarry = 11; // ִ. + int32 podcount = 12; // ȹ . + int32 killcount = 13; + int32 deathcount = 14; + int32 assistcount = 15; + int32 damagecount = 16; +} + +// /Ǯ +message SM_REQ_BATTLE_AIM +{ + float ang =1; // ij + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 aim_type = 4; // Ÿ + int32 move_type =5; // ̵Ÿ -- ߰ +} + +// ߻ +message SM_REQ_BATTLE_SHOOT +{ + int32 weapontype =1; // ߻繫 + int32 remainbullet = 2; // ź. : currentbullet + float aim_yaw =3; // ī޶ + float aim_pitch =4; // ī޶ + int32 aim_type = 5; // Ÿ + int32 server_time=6; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 7; // ߻Ѿ. ϼִ. + +} + +message SM_CHARACTER_KEY +{ + int32 type = 1; + string guid = 2; +} + +// ǰ, ڽ hp . -> ȣƮ ¾Ҵٰ . +message SM_REQ_BATTLE_DAMAGE +{ + int32 damage =1; // + int32 cur_hp=2; // . 0̸ . + SM_CHARACTER_KEY attacker=3; // . + SM_CHARACTER_KEY victim=4; // ǰ. + int32 attack_weapon_type = 5; // ݹŸ. +} + +// +message SM_REQ_BATTLE_RELOAD +{ + int32 cur_hp=1; // + int32 cur_weapontype=2; //Ȱȭ + int32 currentbullet = 3; // źâ ź + int32 maxbullet=4; // ִź +} + +// +message SM_REQ_BATTLE_DODGE +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + int32 dir_id = 5; // Ÿ +} + +// +message SM_REQ_BATTLE_WEAPON_CHANGE +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ߰ +message SM_REQ_BATTLE_WEAPON_ADD +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ij . +message SM_REQ_BATTLE_RESPAWN +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 cur_weapontype=8; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=9; //ִ +} + +// Ʈ ͷ +message SM_REQ_BATTLE_OBJECT_INTERACTION +{ + int32 objecttableid=1; // ͷ Ʋ Ʈ Table ID + string objectguid=2; // ͷ Ʋ Ʈ GUID +} + +// . +message SM_REQ_BATTLE_MANTLE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float tx =11; // + float ty =12; // + float tz =13; // + int32 height = 14;// +} + +// ΰ ε +message SM_REQ_BATTLE_FINISHED_LODING +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // +} + +// غ : δ â +message SM_REQ_BATTLE_READY_TO_EXIT +{ +} + +// Ÿݿ ؼ ư +message SM_REQ_BATTLE_LAUNCH_CHARACTER +{ + SM_CHARACTER_KEY target=1; // ư + float vx =2; // LaunchVelocity + float vy =3; // + float vz =4; // + int32 xyoverride = 5;//bXYOverride + int32 zoverride = 6;//bZOverride +} + +// Ư Ȳ ϸ̼ +message SM_REQ_BATTLE_PLAY_ANIMATION +{ + SM_CHARACTER_KEY target =1; // + int32 type = 2; // + int32 value = 3; // +} + +// Ʈ +message SM_REQ_BATTLE_HIT_EFFECT +{ + SM_CHARACTER_KEY target =1; // + int32 weapontype = 2; // + int32 hiteffecttype = 3; // + float nx =4; // ImpactNormal + float ny =5; // + float nz =6; // + float px =7; // ImpactPoint + float py =8; // + float pz =9; // +} + +message SM_MOB_AI_VECTOR +{ + float px =1; // ImpactPoint + float py =2; // + float pz =3; // +} + +message SM_MOB_AI_AGGRO +{ + SM_CHARACTER_KEY target = 1; + int32 aggropoint = 2; +} + +message SM_MOB_AI_SYNCDATA +{ + string mobguid = 1; + int32 currentAiBehavior = 2; + + int32 taskflags = 3; + + SM_MOB_AI_VECTOR startLocation = 4; + SM_MOB_AI_VECTOR lastKnownLocation = 5; + SM_MOB_AI_VECTOR lastHearingLocation = 6; + + SM_CHARACTER_KEY targetCharacter = 7; + SM_CHARACTER_KEY lastHearingCharacter = 8; + + repeated SM_MOB_AI_AGGRO targetCharacterList = 9; // +} + +message SM_MOB_AI_ATTACK +{ + string mobguid = 1; + float attack_time = 2; + SM_CHARACTER_KEY targetCharacter = 3; + int32 attack_type = 4; + SM_MOB_AI_VECTOR location = 5; +} + +// Ѿ ߻. +message SM_MOB_AI_SHOOT +{ + string mobguid = 1; + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 server_time=4; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 5; // ߻Ѿ. ϼִ. +} + +// . +message SM_MOB_AI_RELOAD +{ + string mobguid = 1; +} + +message SM_MOB_AI_FOCUS +{ + string mobguid = 1; + int32 focus = 2; + SM_CHARACTER_KEY targetCharacter = 3; +} + +message SM_MOB_MOVETO +{ + string mobguid = 1; + int32 targetType = 2; + SM_MOB_AI_VECTOR tagetLocation = 3; + float radius = 4; + int32 stop = 5; + float moveSpeed = 6; +} + +message SM_MOB_SPAWN +{ + string mobguid = 1; + int32 mob_type = 2; + SM_MOB_AI_VECTOR location = 3; + float angle = 4; + string anchorguid = 5; +} + +message SM_RUNNING_TRIGGER +{ + string Anchor_GUID = 1; + string User_GUID = 2; + int32 server_Time = 3; + int32 hurdle_type = 4; + int32 is_triggered = 5; + + // Ʈ Ÿ ũ + int32 impact_type = 6; + float impact_x = 7; + float impact_y = 8; + float impact_z = 9; + int32 is_xy_override = 10; + int32 is_z_override = 11; + + // Ÿ ũ + int32 ragdoll_type = 12; + string ragdoll_bone_name = 13; + int32 is_partial = 14; + int32 is_moveable = 15; +} + +message SM_RUNNING_ITEM +{ + string Anchor_GUID = 1; + string str_item = 2; +} + +message SM_RUNNING_HURDLE_SPAWN +{ + string Hurdle_GUID = 1; + string class_path = 2; + + float loc_x = 3; + float loc_y = 4; + float loc_z = 5; + + float rot_pitch = 6; + float rot_yaw = 7; + float rot_roll = 8; +} + +message SM_RUNNING_HURDLE_MOVE +{ + string Hurdle_GUID = 1; + + float loc_x = 2; + float loc_y = 3; + float loc_z = 4; + + float rot_pitch = 5; + float rot_yaw = 6; + float rot_roll = 7; + + float vel_x = 8; + float vel_y = 9; + float vel_z = 10; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_HOST_ROOMINFO +{ + string host_guid = 1; + int64 room_starttime = 2; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_REQ_HOST_ROOMINFO +{ + +} + diff --git a/Protocol/client-proto/.svn/pristine/1e/1ef74f5f5fad84aa036940bc896af50a39927477.svn-base b/Protocol/client-proto/.svn/pristine/1e/1ef74f5f5fad84aa036940bc896af50a39927477.svn-base new file mode 100644 index 0000000..a0e4e51 --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/1e/1ef74f5f5fad84aa036940bc896af50a39927477.svn-base @@ -0,0 +1,73 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package protocol; + +option csharp_namespace = "Google.Protobuf.CES.Packet"; +//import "google/protobuf/timestamp.proto"; + + enum Protocol{ + + MOVE_NEXT = 0; + MOVE_COMBINE = 1; + VOICE = 30; + VOICE_STATE = 31; + MUSIC = 40; + MUSICBox = 41; + DANCE = 50; + DANCE_ReqRoomTime = 51; + DANCE_ResRoomTime = 52; + DANCE_StateUpdate = 53; + JUMP = 70; + //GUIDE = 80; + PRESENTATION_PAGE = 81; + AIM = 100; + PARTY_MOVE_MEMBER = 110; + + BATTLE_MOVE = 120; // 이동,멈춤,점프... + BATTLE_AIM = 121; // 조준. + BATTLE_STATUS = 122; // 현재상태. + BATTLE_MANTLE = 123; // 기어오르기. + BATTLE_SHOOT = 130; // 총알발사. + BATTLE_DAMAGE = 131; // 피격. + BATTLE_RELOAD = 132; // 재장전 + BATTLE_DODGE = 133; // 구르기 + BATTLE_WEAPON_CHANGE = 134; // 무기교환 + BATTLE_WEAPON_ADD = 135; // 무기추가 + BATTLE_RESPAWN = 136; // 사망후 리스폰. + BATTLE_OBJECT_INTERACTION = 137; // 오브젝트 인터렉션 + BATTLE_FINISHED_LODING = 138; // 인게임 로딩 끝 + BATTLE_READY_TO_EXIT = 139; // 인던 나가기 전에 결과창 보는 중 + BATTLE_LAUNCH_CHARACTER = 140; // 범위 타격에 의해서 날아감 + BATTLE_PLAY_ANIMATION = 141; // 특정 상황에서 에니메이션 실행 할 때 + BATTLE_HIT_EFFECT = 142; // 캐릭터나 사물에 이펙트 표시 + + MOB_AI_ATTACK = 200; + MOB_AI_MOVETO = 201; + MOB_AI_FOCUS = 202; + MOB_AI_SYNCVAR = 203; + MOB_AI_SPAWN = 204; + MOB_AI_SHOOT = 205; + MOB_AI_RELOAD = 206; + + RUNNING_TRIGGER = 300; + RUNNING_ITEM = 301; + RUNNING_HURDLE_SPAWN = 302; + RUNNING_HURDLE_STATE = 303; + RUNNING_HURDLE_MOVE = 304; + RUNNING_HURDLE_TRAVAARSAL_STATE = 305; + RUNNING_ATTACH = 306; + + HOST_ROOMINFO = 401; // 호스트 테스트용 임시 패킷 + REQ_HOST_ROOMINFO = 402; // 호스트 테스트용 임시 패킷 + }; + + +// [END messages] diff --git a/Protocol/client-proto/.svn/pristine/4f/4f5ec643ea9a553172ab1a4514c3b6f995651e51.svn-base b/Protocol/client-proto/.svn/pristine/4f/4f5ec643ea9a553172ab1a4514c3b6f995651e51.svn-base new file mode 100644 index 0000000..0be2e84 --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/4f/4f5ec643ea9a553172ab1a4514c3b6f995651e51.svn-base @@ -0,0 +1,76 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package protocol; + +option csharp_namespace = "Google.Protobuf.CES.Packet"; +//import "google/protobuf/timestamp.proto"; + + enum Protocol{ + + MOVE_NEXT = 0; + MOVE_COMBINE = 1; + VOICE = 30; + VOICE_STATE = 31; + MUSIC = 40; + MUSICBox = 41; + DANCE = 50; + DANCE_ReqRoomTime = 51; + DANCE_ResRoomTime = 52; + DANCE_StateUpdate = 53; + JUMP = 70; + //GUIDE = 80; + PRESENTATION_PAGE = 81; + AIM = 100; + PARTY_MOVE_MEMBER = 110; + + BATTLE_MOVE = 120; // 이동,멈춤,점프... + BATTLE_AIM = 121; // 조준. + BATTLE_STATUS = 122; // 현재상태. + BATTLE_MANTLE = 123; // 기어오르기. + BATTLE_SHOOT = 130; // 총알발사. + BATTLE_DAMAGE = 131; // 피격. + BATTLE_RELOAD = 132; // 재장전 + BATTLE_DODGE = 133; // 구르기 + BATTLE_WEAPON_CHANGE = 134; // 무기교환 + BATTLE_WEAPON_ADD = 135; // 무기추가 + BATTLE_RESPAWN = 136; // 사망후 리스폰. + BATTLE_OBJECT_INTERACTION = 137; // 오브젝트 인터렉션 + BATTLE_FINISHED_LODING = 138; // 인게임 로딩 끝 + BATTLE_READY_TO_EXIT = 139; // 인던 나가기 전에 결과창 보는 중 + BATTLE_LAUNCH_CHARACTER = 140; // 범위 타격에 의해서 날아감 + BATTLE_PLAY_ANIMATION = 141; // 특정 상황에서 에니메이션 실행 할 때 + BATTLE_HIT_EFFECT = 142; // 캐릭터나 사물에 이펙트 표시 + + MOB_AI_ATTACK = 200; + MOB_AI_MOVETO = 201; + MOB_AI_FOCUS = 202; + MOB_AI_SYNCVAR = 203; + MOB_AI_SPAWN = 204; + MOB_AI_SHOOT = 205; + MOB_AI_RELOAD = 206; + + RUNNING_TRIGGER = 300; + RUNNING_ITEM = 301; + RUNNING_HURDLE_SPAWN = 302; + RUNNING_HURDLE_STATE = 303; + RUNNING_HURDLE_MOVE = 304; + RUNNING_HURDLE_TRAVAARSAL_STATE = 305; + RUNNING_ATTACH = 306; + RUNNING_ANCHOR_SCRIPT = 307; + RUNNING_ANCHOR_INFO = 308; + RUNNING_GRAVITY = 309; + + HOST_ROOMINFO = 401; // 호스트 테스트용 임시 패킷 + REQ_HOST_ROOMINFO = 402; // 호스트 테스트용 임시 패킷 + }; + + +// [END messages] diff --git a/Protocol/client-proto/.svn/pristine/73/73a39a2b5fa598b020b6889e479359461fcaec40.svn-base b/Protocol/client-proto/.svn/pristine/73/73a39a2b5fa598b020b6889e479359461fcaec40.svn-base new file mode 100644 index 0000000..3fe978d --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/73/73a39a2b5fa598b020b6889e479359461fcaec40.svn-base @@ -0,0 +1,74 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package protocol; + +option csharp_namespace = "Google.Protobuf.CES.Packet"; +//import "google/protobuf/timestamp.proto"; + + enum Protocol{ + + MOVE_NEXT = 0; + MOVE_COMBINE = 1; + VOICE = 30; + VOICE_STATE = 31; + MUSIC = 40; + MUSICBox = 41; + DANCE = 50; + DANCE_ReqRoomTime = 51; + DANCE_ResRoomTime = 52; + DANCE_StateUpdate = 53; + JUMP = 70; + //GUIDE = 80; + PRESENTATION_PAGE = 81; + AIM = 100; + PARTY_MOVE_MEMBER = 110; + + BATTLE_MOVE = 120; // 이동,멈춤,점프... + BATTLE_AIM = 121; // 조준. + BATTLE_STATUS = 122; // 현재상태. + BATTLE_MANTLE = 123; // 기어오르기. + BATTLE_SHOOT = 130; // 총알발사. + BATTLE_DAMAGE = 131; // 피격. + BATTLE_RELOAD = 132; // 재장전 + BATTLE_DODGE = 133; // 구르기 + BATTLE_WEAPON_CHANGE = 134; // 무기교환 + BATTLE_WEAPON_ADD = 135; // 무기추가 + BATTLE_RESPAWN = 136; // 사망후 리스폰. + BATTLE_OBJECT_INTERACTION = 137; // 오브젝트 인터렉션 + BATTLE_FINISHED_LODING = 138; // 인게임 로딩 끝 + BATTLE_READY_TO_EXIT = 139; // 인던 나가기 전에 결과창 보는 중 + BATTLE_LAUNCH_CHARACTER = 140; // 범위 타격에 의해서 날아감 + BATTLE_PLAY_ANIMATION = 141; // 특정 상황에서 에니메이션 실행 할 때 + BATTLE_HIT_EFFECT = 142; // 캐릭터나 사물에 이펙트 표시 + + MOB_AI_ATTACK = 200; + MOB_AI_MOVETO = 201; + MOB_AI_FOCUS = 202; + MOB_AI_SYNCVAR = 203; + MOB_AI_SPAWN = 204; + MOB_AI_SHOOT = 205; + MOB_AI_RELOAD = 206; + + RUNNING_TRIGGER = 300; + RUNNING_ITEM = 301; + RUNNING_HURDLE_SPAWN = 302; + RUNNING_HURDLE_STATE = 303; + RUNNING_HURDLE_MOVE = 304; + RUNNING_HURDLE_TRAVAARSAL_STATE = 305; + RUNNING_ATTACH = 306; + RUNNING_ANCHOR_SCRIPT = 307; + + HOST_ROOMINFO = 401; // 호스트 테스트용 임시 패킷 + REQ_HOST_ROOMINFO = 402; // 호스트 테스트용 임시 패킷 + }; + + +// [END messages] diff --git a/Protocol/client-proto/.svn/pristine/7a/7a71755c25085873df744f902fc55d169f3c058d.svn-base b/Protocol/client-proto/.svn/pristine/7a/7a71755c25085873df744f902fc55d169f3c058d.svn-base new file mode 100644 index 0000000..478da02 --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/7a/7a71755c25085873df744f902fc55d169f3c058d.svn-base @@ -0,0 +1,470 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package Packet; + +//import "google/protobuf/timestamp.proto"; +// [END declaration] + +// [START csharp_declaration] +option csharp_namespace = "Google.Protobuf.CES.Packet"; +// [END csharp_declaration] + +// [START messages] + +message SM_REQ_MOVE_NEXT +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float ang =4; // + optional float movevalue =5; // + optional int32 start =6; // ù ̵̸ true ̵θ false +} + +message SM_REQ_MOVE_COMBINE +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float lx =4; // + optional float ly =5; // + optional float lz =6; // + optional float ang =7; // + optional int32 move_speed =8; // + optional int32 start =9; // ù ̵̸ true ̵θ false +} + +message SM_REQ_TRACKING_TYPE +{ + optional int32 type =1; +} + +message SM_REQ_VOICE +{ + optional int32 sampleRate = 1; // + optional int32 numchannels = 2; // + optional int32 PCMSize = 3; // + repeated int32 data = 4; + optional int32 dataSize = 5; // +} + +message SM_REQ_MUSICBOX +{ + optional string propguid = 1; + optional int32 sampleRate = 2; // + optional int32 numchannels = 3; // + optional int32 PCMSize = 4; // + repeated int32 data = 5; + optional int32 dataSize = 6; // +} + +message SM_REQ_JUMP +{ + optional float sx =1; // + optional float sy =2; // + optional float sz =3; // + optional float lx =4; // + optional float ly =5; // + optional float lz =6; // + optional float ang =7; // + optional int32 start =8; // ù ̵̸ true ̵θ false +} + +message SM_REQ_GUIDE +{ + optional string guoup =1; // + optional int32 operatorid =2; // + optional int32 guidetype =3; // +} + +message SM_REQ_PRESENTATION_PAGE +{ + string pageurl =1; // + int32 pageid =2; // +} + + +message SM_REQ_AIM +{ + optional float pitch =1; // +} + +message SM_REQ_PARTY_MOVE_MEMBER +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // +} + +message SM_REQ_VOICE_STATE +{ + optional string nickname =1; // + optional int32 state =2; // +} + +message SM_REQ_DANCE_INFO +{ + optional int32 danceid = 1; // ̵ + optional int32 motionid = 2; // + optional int32 dancetype = 3; // Ÿ޺ + optional int64 timestamp = 4; // ð +} + +message SM_REQ_DANCE_ROOMTIME +{ + int64 sendmytime = 1; +} + +message SM_SEND_DANCE_ROOMTIME +{ + int64 sendroomtime = 1; + string guid = 2; +} + +message SM_SEND_DANCE_STATE +{ + optional int32 state = 1; + optional int64 sendroomtime = 2; + optional int32 state01 = 3; + optional int32 state02 = 4; + optional int32 state03 = 5; + optional string str01 = 6; + optional string str02 = 7; + optional string str03 = 8; +} + + +////////////////////////////////////// + +// Ѿ +message SM_DATA_BATTLE_BULLET +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float yaw =4; // + optional float pitch =5; // + optional int32 damage =6; // +} + +message SM_DATA_WEAPON_DATA +{ + int32 weapontype = 1; + int32 currentbullet = 2; // Ѿ. + int32 maxbullet = 3; // źâ ִź +} + +message SM_REQ_BATTLE_MOVE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + int32 move_speed =11; // ̵Ÿ + int32 type =12; // ̵ȭŸ + float aim_yaw =13; // + float aim_pitch =14; // + int32 cur_hp=15; // + int32 shoot_type = 16; // ѱ߻ + int32 cur_weapontype=17;// Ȱȭ + int32 server_time=18; // Ŷ ð + repeated SM_DATA_WEAPON_DATA weapontypes=19; //ִ +} + +// ij . +message SM_REQ_BATTLE_STATUS +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 shoot_type = 8; // ѱ߻ + int32 cur_weapontype=9; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=10; //ִ + int32 podcarry = 11; // ִ. + int32 podcount = 12; // ȹ . + int32 killcount = 13; + int32 deathcount = 14; + int32 assistcount = 15; + int32 damagecount = 16; +} + +// /Ǯ +message SM_REQ_BATTLE_AIM +{ + float ang =1; // ij + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 aim_type = 4; // Ÿ + int32 move_type =5; // ̵Ÿ -- ߰ +} + +// ߻ +message SM_REQ_BATTLE_SHOOT +{ + int32 weapontype =1; // ߻繫 + int32 remainbullet = 2; // ź. : currentbullet + float aim_yaw =3; // ī޶ + float aim_pitch =4; // ī޶ + int32 aim_type = 5; // Ÿ + int32 server_time=6; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 7; // ߻Ѿ. ϼִ. + +} + +message SM_CHARACTER_KEY +{ + int32 type = 1; + string guid = 2; +} + +// ǰ, ڽ hp . -> ȣƮ ¾Ҵٰ . +message SM_REQ_BATTLE_DAMAGE +{ + int32 damage =1; // + int32 cur_hp=2; // . 0̸ . + SM_CHARACTER_KEY attacker=3; // . + SM_CHARACTER_KEY victim=4; // ǰ. + int32 attack_weapon_type = 5; // ݹŸ. +} + +// +message SM_REQ_BATTLE_RELOAD +{ + int32 cur_hp=1; // + int32 cur_weapontype=2; //Ȱȭ + int32 currentbullet = 3; // źâ ź + int32 maxbullet=4; // ִź +} + +// +message SM_REQ_BATTLE_DODGE +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + int32 dir_id = 5; // Ÿ +} + +// +message SM_REQ_BATTLE_WEAPON_CHANGE +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ߰ +message SM_REQ_BATTLE_WEAPON_ADD +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ij . +message SM_REQ_BATTLE_RESPAWN +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 cur_weapontype=8; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=9; //ִ +} + +// Ʈ ͷ +message SM_REQ_BATTLE_OBJECT_INTERACTION +{ + int32 objecttableid=1; // ͷ Ʋ Ʈ Table ID + string objectguid=2; // ͷ Ʋ Ʈ GUID +} + +// . +message SM_REQ_BATTLE_MANTLE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float tx =11; // + float ty =12; // + float tz =13; // + int32 height = 14;// +} + +// ΰ ε +message SM_REQ_BATTLE_FINISHED_LODING +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // +} + +// غ : δ â +message SM_REQ_BATTLE_READY_TO_EXIT +{ +} + +// Ÿݿ ؼ ư +message SM_REQ_BATTLE_LAUNCH_CHARACTER +{ + SM_CHARACTER_KEY target=1; // ư + float vx =2; // LaunchVelocity + float vy =3; // + float vz =4; // + int32 xyoverride = 5;//bXYOverride + int32 zoverride = 6;//bZOverride +} + +// Ư Ȳ ϸ̼ +message SM_REQ_BATTLE_PLAY_ANIMATION +{ + SM_CHARACTER_KEY target =1; // + int32 type = 2; // + int32 value = 3; // +} + +// Ʈ +message SM_REQ_BATTLE_HIT_EFFECT +{ + SM_CHARACTER_KEY target =1; // + int32 weapontype = 2; // + int32 hiteffecttype = 3; // + float nx =4; // ImpactNormal + float ny =5; // + float nz =6; // + float px =7; // ImpactPoint + float py =8; // + float pz =9; // +} + +message SM_MOB_AI_VECTOR +{ + float px =1; // ImpactPoint + float py =2; // + float pz =3; // +} + +message SM_MOB_AI_AGGRO +{ + SM_CHARACTER_KEY target = 1; + int32 aggropoint = 2; +} + +message SM_MOB_AI_SYNCDATA +{ + string mobguid = 1; + int32 currentAiBehavior = 2; + + int32 taskflags = 3; + + SM_MOB_AI_VECTOR startLocation = 4; + SM_MOB_AI_VECTOR lastKnownLocation = 5; + SM_MOB_AI_VECTOR lastHearingLocation = 6; + + SM_CHARACTER_KEY targetCharacter = 7; + SM_CHARACTER_KEY lastHearingCharacter = 8; + + repeated SM_MOB_AI_AGGRO targetCharacterList = 9; // +} + +message SM_MOB_AI_ATTACK +{ + string mobguid = 1; + float attack_time = 2; + SM_CHARACTER_KEY targetCharacter = 3; + int32 attack_type = 4; + SM_MOB_AI_VECTOR location = 5; +} + +// Ѿ ߻. +message SM_MOB_AI_SHOOT +{ + string mobguid = 1; + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 server_time=4; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 5; // ߻Ѿ. ϼִ. +} + +// . +message SM_MOB_AI_RELOAD +{ + string mobguid = 1; +} + +message SM_MOB_AI_FOCUS +{ + string mobguid = 1; + int32 focus = 2; + SM_CHARACTER_KEY targetCharacter = 3; +} + +message SM_MOB_MOVETO +{ + string mobguid = 1; + int32 targetType = 2; + SM_MOB_AI_VECTOR tagetLocation = 3; + float radius = 4; + int32 stop = 5; + float moveSpeed = 6; +} + +message SM_MOB_SPAWN +{ + string mobguid = 1; + int32 mob_type = 2; + SM_MOB_AI_VECTOR location = 3; + float angle = 4; + string anchorguid = 5; +} + +message SM_RUNNING_TRIGGER +{ + string Anchor_GUID = 1; + string User_GUID = 2; + int32 server_Time = 3; + int32 hurdle_type = 4; + int32 is_triggered = 5; + + // Ʈ Ÿ ũ + int32 impact_type = 6; + float impact_x = 7; + float impact_y = 8; + float impact_z = 9; + int32 is_xy_override = 10; + int32 is_z_override = 11; + + // Ÿ ũ + int32 ragdoll_type = 12; + string ragdoll_bone_name = 13; + int32 is_partial = 14; + int32 is_moveable = 15; +} \ No newline at end of file diff --git a/Protocol/client-proto/.svn/pristine/7d/7d09c1feeaa48562e6aa5d862154ce91ecb0c56e.svn-base b/Protocol/client-proto/.svn/pristine/7d/7d09c1feeaa48562e6aa5d862154ce91ecb0c56e.svn-base new file mode 100644 index 0000000..ee87147 --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/7d/7d09c1feeaa48562e6aa5d862154ce91ecb0c56e.svn-base @@ -0,0 +1,555 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package Packet; + +//import "google/protobuf/timestamp.proto"; +// [END declaration] + +// [START csharp_declaration] +option csharp_namespace = "Google.Protobuf.CES.Packet"; +// [END csharp_declaration] + +// [START messages] + +message SM_REQ_MOVE_NEXT +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float movevalue =5; // + int32 start =6; // ù ̵̸ true ̵θ false + int32 move_type = 7; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int64 server_time=8; // Ŷ ð +} + +message SM_REQ_MOVE_COMBINE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float ang =7; // + int32 move_speed =8; // + int32 move_type = 9; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int32 start =10; // ù ̵̸ true ̵θ false + int64 server_time=11; // Ŷ ð +} + +message SM_REQ_VOICE +{ + int32 sampleRate = 1; // + int32 numchannels = 2; // + int32 PCMSize = 3; // + repeated int32 data = 4; + int32 dataSize = 5; // +} + +message SM_REQ_MUSICBOX +{ + string propguid = 1; + int32 sampleRate = 2; // + int32 numchannels = 3; // + int32 PCMSize = 4; // + repeated int32 data = 5; + int32 dataSize = 6; // +} + +message SM_REQ_JUMP +{ + float sx =1; // + float sy =2; // + float sz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float ang =7; // + int32 start =8; // ù ̵̸ true ̵θ false + int64 server_time=9; // Ŷ ð +} + +message SM_REQ_PRESENTATION_PAGE +{ + string pageurl =1; // + int32 pageid =2; // +} + + +message SM_REQ_AIM +{ + float pitch =1; // +} + +message SM_REQ_PARTY_MOVE_MEMBER +{ + float px =1; // + float py =2; // + float pz =3; // +} + +message SM_REQ_VOICE_STATE +{ + string nickname =1; // + int32 state =2; // +} + +message SM_REQ_DANCE_INFO +{ + int32 danceid = 1; // ̵ + int32 motionid = 2; // + int32 dancetype = 3; // Ÿ޺ + int64 timestamp = 4; // ð +} + +message SM_REQ_DANCE_ROOMTIME +{ + int64 sendmytime = 1; +} + +message SM_SEND_DANCE_ROOMTIME +{ + int64 sendroomtime = 1; + string guid = 2; +} + +message SM_SEND_DANCE_STATE +{ + int32 state = 1; + int64 sendroomtime = 2; + int32 state01 = 3; + int32 state02 = 4; + int32 state03 = 5; + string str01 = 6; + string str02 = 7; + string str03 = 8; +} + + +////////////////////////////////////// + +// Ѿ +message SM_DATA_BATTLE_BULLET +{ + float px =1; // + float py =2; // + float pz =3; // + float yaw =4; // + float pitch =5; // + int32 damage =6; // +} + +message SM_DATA_WEAPON_DATA +{ + int32 weapontype = 1; // + int32 weaponid = 2; // ̺ID + int32 currentbullet = 3; // Ѿ. + int32 maxbullet = 4; // źâ ִź +} + +message SM_REQ_BATTLE_MOVE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + optional float vx =7; // + optional float vy =8; // + optional float vz =9; // + float ang =10; // + float movescale =11; + int32 move_speed =12; // ̵ӵ + int32 move_type = 13; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int32 type =14; // ̵ȭŸ + optional float aim_yaw =15; // + optional float aim_pitch =16; // + optional int32 shoot_type = 17; // ѱ߻ + optional int32 cur_weapontype=18;// Ȱȭ + int64 server_time=19; // Ŷ ð + repeated SM_DATA_WEAPON_DATA weapontypes=20; //ִ +} + +// ij . +message SM_REQ_BATTLE_STATUS +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 shoot_type = 8; // ѱ߻ + int32 cur_weapontype=9; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=10; //ִ + int32 podcarry = 11; // ִ. + int32 podcount = 12; // ȹ . + int32 killcount = 13; + int32 deathcount = 14; + int32 assistcount = 15; + int32 damagecount = 16; +} + +// /Ǯ +message SM_REQ_BATTLE_AIM +{ + float ang =1; // ij + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 aim_type = 4; // Ÿ + int32 move_type =5; // ̵Ÿ -- ߰ +} + +// ߻ +message SM_REQ_BATTLE_SHOOT +{ + int32 weapontype =1; // ߻繫 + int32 remainbullet = 2; // ź. : currentbullet + float aim_yaw =3; // ī޶ + float aim_pitch =4; // ī޶ + int32 aim_type = 5; // Ÿ + int64 server_time=6; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 7; // ߻Ѿ. ϼִ. + +} + +message SM_CHARACTER_KEY +{ + int32 type = 1; + string guid = 2; +} + +// ǰ, ڽ hp . -> ȣƮ ¾Ҵٰ . +message SM_REQ_BATTLE_DAMAGE +{ + int32 damage =1; // + int32 cur_hp=2; // . 0̸ . + SM_CHARACTER_KEY attacker=3; // . + SM_CHARACTER_KEY victim=4; // ǰ. + int32 attack_weapon_type = 5; // ݹŸ. +} + +// +message SM_REQ_BATTLE_RELOAD +{ + int32 cur_hp=1; // + int32 cur_weapontype=2; //Ȱȭ + int32 currentbullet = 3; // źâ ź + int32 maxbullet=4; // ִź +} + +// +message SM_REQ_BATTLE_DODGE +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + int32 dir_id = 5; // Ÿ +} + +// +message SM_REQ_BATTLE_WEAPON_CHANGE +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ߰ +message SM_REQ_BATTLE_WEAPON_ADD +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ij . +message SM_REQ_BATTLE_RESPAWN +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 cur_weapontype=8; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=9; //ִ +} + +// Ʈ ͷ +message SM_REQ_BATTLE_OBJECT_INTERACTION +{ + int32 objecttableid=1; // ͷ Ʋ Ʈ Table ID + string objectguid=2; // ͷ Ʋ Ʈ GUID +} + +// . +message SM_REQ_BATTLE_MANTLE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float tx =11; // + float ty =12; // + float tz =13; // + int32 height = 14;// +} + +// ΰ ε +message SM_REQ_BATTLE_FINISHED_LODING +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // +} + +// غ : δ â +message SM_REQ_BATTLE_READY_TO_EXIT +{ +} + +// Ÿݿ ؼ ư +message SM_REQ_BATTLE_LAUNCH_CHARACTER +{ + SM_CHARACTER_KEY target=1; // ư + float vx =2; // LaunchVelocity + float vy =3; // + float vz =4; // + int32 xyoverride = 5;//bXYOverride + int32 zoverride = 6;//bZOverride +} + +// Ư Ȳ ϸ̼ +message SM_REQ_BATTLE_PLAY_ANIMATION +{ + SM_CHARACTER_KEY target =1; // + int32 type = 2; // + int32 value = 3; // +} + +// Ʈ +message SM_REQ_BATTLE_HIT_EFFECT +{ + SM_CHARACTER_KEY target =1; // + int32 weaponid = 2; // + int32 hiteffecttype = 3; // + float nx =4; // ImpactNormal + float ny =5; // + float nz =6; // + float px =7; // ImpactPoint + float py =8; // + float pz =9; // +} + +message SM_MOB_AI_VECTOR +{ + float px =1; // ImpactPoint + float py =2; // + float pz =3; // +} + +message SM_MOB_AI_AGGRO +{ + SM_CHARACTER_KEY target = 1; + int32 aggropoint = 2; +} + +message SM_MOB_AI_SYNCDATA +{ + string mobguid = 1; + int32 currentAiBehavior = 2; + + int32 taskflags = 3; + + SM_MOB_AI_VECTOR startLocation = 4; + SM_MOB_AI_VECTOR lastKnownLocation = 5; + SM_MOB_AI_VECTOR lastHearingLocation = 6; + + SM_CHARACTER_KEY targetCharacter = 7; + SM_CHARACTER_KEY lastHearingCharacter = 8; + + repeated SM_MOB_AI_AGGRO targetCharacterList = 9; // +} + +message SM_MOB_AI_ATTACK +{ + string mobguid = 1; + float attack_time = 2; + SM_CHARACTER_KEY targetCharacter = 3; + int32 attack_type = 4; + SM_MOB_AI_VECTOR location = 5; +} + +// Ѿ ߻. +message SM_MOB_AI_SHOOT +{ + string mobguid = 1; + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int64 server_time=4; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 5; // ߻Ѿ. ϼִ. +} + +// . +message SM_MOB_AI_RELOAD +{ + string mobguid = 1; +} + +message SM_MOB_AI_FOCUS +{ + string mobguid = 1; + int32 focus = 2; + SM_CHARACTER_KEY targetCharacter = 3; +} + +message SM_MOB_MOVETO +{ + string mobguid = 1; + int32 targetType = 2; + SM_MOB_AI_VECTOR tagetLocation = 3; + float radius = 4; + int32 stop = 5; + float moveSpeed = 6; +} + +message SM_MOB_SPAWN +{ + string mobguid = 1; + int32 mob_type = 2; + SM_MOB_AI_VECTOR location = 3; + float angle = 4; + string anchorguid = 5; +} + +message SM_RUNNING_TRIGGER +{ + string Anchor_GUID = 1; + string User_GUID = 2; + int32 hurdle_type = 3; + int32 is_triggered = 4; + + // Ʈ Ÿ ũ + optional int32 impact_type = 5; + optional SM_MOB_AI_VECTOR impact_force = 6; + optional int32 is_xy_override = 7; + optional int32 is_z_override = 8; + + // Ÿ ũ + optional int32 ragdoll_type = 9; + optional string ragdoll_bone_name = 10; + optional int32 is_partial = 11; + optional int32 is_moveable = 12; +} + +message SM_RUNNING_ATTACH +{ + string Anchor_GUID = 1; + int32 is_triggered = 5; +} + +message SM_HURDLE_TRAVERSAL +{ + string anchor_guid = 1; + int32 use_hurdle = 2; + int32 use_hurdle_mode = 3; + + int32 hurdle_type = 4; + int32 traversal_state = 5; + int32 use_anim_dir = 6; + + int32 montage_action = 7; + optional float loc_x = 8; + optional float loc_y = 9; + optional float loc_z = 10; + optional float rot_pitch = 11; + optional float rot_yaw = 12; + optional float rot_roll = 13; +} + +message SM_RUNNING_ITEM +{ + string Anchor_GUID = 1; + string str_item = 2; +} + +message SM_RUNNING_HURDLE_SPAWN +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + string class_path = 3; + int64 server_time = 4; + + float loc_x = 5; + float loc_y = 6; + float loc_z = 7; + + float rot_pitch = 8; + float rot_yaw = 9; + float rot_roll = 10; + + int32 hurdle_type = 11; + + float power = 12; +} + +message SM_RUNNING_HURDLE_STATE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + int32 state_type = 4; +} + +message SM_RUNNING_HURDLE_MOVE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + float loc_x = 4; + float loc_y = 5; + float loc_z = 6; + + float rot_pitch = 7; + float rot_yaw = 8; + float rot_roll = 9; + + float vel_x = 10; + float vel_y = 11; + float vel_z = 12; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_HOST_ROOMINFO +{ + string host_guid = 1; + int64 room_starttime = 2; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_REQ_HOST_ROOMINFO +{ + +} + diff --git a/Protocol/client-proto/.svn/pristine/86/86272cd6b74b5fb74e0defd00ecb0c5f09d92372.svn-base b/Protocol/client-proto/.svn/pristine/86/86272cd6b74b5fb74e0defd00ecb0c5f09d92372.svn-base new file mode 100644 index 0000000..c76d61c --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/86/86272cd6b74b5fb74e0defd00ecb0c5f09d92372.svn-base @@ -0,0 +1,565 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package Packet; + +//import "google/protobuf/timestamp.proto"; +// [END declaration] + +// [START csharp_declaration] +option csharp_namespace = "Google.Protobuf.CES.Packet"; +// [END csharp_declaration] + +// [START messages] + +message SM_REQ_MOVE_NEXT +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float ang =4; // + optional float movevalue =5; // + optional int32 start =6; // ù ̵̸ true ̵θ false + optional int32 move_type = 7; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + optional int32 server_time=8; // Ŷ ð +} + +message SM_REQ_MOVE_COMBINE +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float lx =4; // + optional float ly =5; // + optional float lz =6; // + optional float ang =7; // + optional int32 move_speed =8; // + optional int32 move_type = 9; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + optional int32 start =10; // ù ̵̸ true ̵θ false + optional int32 server_time=11; // Ŷ ð +} + +message SM_REQ_TRACKING_TYPE +{ + optional int32 type =1; +} + +message SM_REQ_VOICE +{ + optional int32 sampleRate = 1; // + optional int32 numchannels = 2; // + optional int32 PCMSize = 3; // + repeated int32 data = 4; + optional int32 dataSize = 5; // +} + +message SM_REQ_MUSICBOX +{ + optional string propguid = 1; + optional int32 sampleRate = 2; // + optional int32 numchannels = 3; // + optional int32 PCMSize = 4; // + repeated int32 data = 5; + optional int32 dataSize = 6; // +} + +message SM_REQ_JUMP +{ + optional float sx =1; // + optional float sy =2; // + optional float sz =3; // + optional float lx =4; // + optional float ly =5; // + optional float lz =6; // + optional float ang =7; // + optional int32 start =8; // ù ̵̸ true ̵θ false + optional int32 server_time=9; // Ŷ ð +} + +message SM_REQ_GUIDE +{ + optional string guoup =1; // + optional int32 operatorid =2; // + optional int32 guidetype =3; // +} + +message SM_REQ_PRESENTATION_PAGE +{ + string pageurl =1; // + int32 pageid =2; // +} + + +message SM_REQ_AIM +{ + optional float pitch =1; // +} + +message SM_REQ_PARTY_MOVE_MEMBER +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // +} + +message SM_REQ_VOICE_STATE +{ + optional string nickname =1; // + optional int32 state =2; // +} + +message SM_REQ_DANCE_INFO +{ + optional int32 danceid = 1; // ̵ + optional int32 motionid = 2; // + optional int32 dancetype = 3; // Ÿ޺ + optional int64 timestamp = 4; // ð +} + +message SM_REQ_DANCE_ROOMTIME +{ + int64 sendmytime = 1; +} + +message SM_SEND_DANCE_ROOMTIME +{ + int64 sendroomtime = 1; + string guid = 2; +} + +message SM_SEND_DANCE_STATE +{ + optional int32 state = 1; + optional int64 sendroomtime = 2; + optional int32 state01 = 3; + optional int32 state02 = 4; + optional int32 state03 = 5; + optional string str01 = 6; + optional string str02 = 7; + optional string str03 = 8; +} + + +////////////////////////////////////// + +// Ѿ +message SM_DATA_BATTLE_BULLET +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float yaw =4; // + optional float pitch =5; // + optional int32 damage =6; // +} + +message SM_DATA_WEAPON_DATA +{ + int32 weapontype = 1; + int32 currentbullet = 2; // Ѿ. + int32 maxbullet = 3; // źâ ִź +} + +message SM_REQ_BATTLE_MOVE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float movescale =11; + int32 move_speed =12; // ̵ӵ + int32 move_type = 13; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int32 type =14; // ̵ȭŸ + float aim_yaw =15; // + float aim_pitch =16; // + int32 cur_hp=17; // + int32 shoot_type = 18; // ѱ߻ + int32 cur_weapontype=19;// Ȱȭ + int32 server_time=20; // Ŷ ð + repeated SM_DATA_WEAPON_DATA weapontypes=21; //ִ +} + +// ij . +message SM_REQ_BATTLE_STATUS +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 shoot_type = 8; // ѱ߻ + int32 cur_weapontype=9; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=10; //ִ + int32 podcarry = 11; // ִ. + int32 podcount = 12; // ȹ . + int32 killcount = 13; + int32 deathcount = 14; + int32 assistcount = 15; + int32 damagecount = 16; +} + +// /Ǯ +message SM_REQ_BATTLE_AIM +{ + float ang =1; // ij + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 aim_type = 4; // Ÿ + int32 move_type =5; // ̵Ÿ -- ߰ +} + +// ߻ +message SM_REQ_BATTLE_SHOOT +{ + int32 weapontype =1; // ߻繫 + int32 remainbullet = 2; // ź. : currentbullet + float aim_yaw =3; // ī޶ + float aim_pitch =4; // ī޶ + int32 aim_type = 5; // Ÿ + int32 server_time=6; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 7; // ߻Ѿ. ϼִ. + +} + +message SM_CHARACTER_KEY +{ + int32 type = 1; + string guid = 2; +} + +// ǰ, ڽ hp . -> ȣƮ ¾Ҵٰ . +message SM_REQ_BATTLE_DAMAGE +{ + int32 damage =1; // + int32 cur_hp=2; // . 0̸ . + SM_CHARACTER_KEY attacker=3; // . + SM_CHARACTER_KEY victim=4; // ǰ. + int32 attack_weapon_type = 5; // ݹŸ. +} + +// +message SM_REQ_BATTLE_RELOAD +{ + int32 cur_hp=1; // + int32 cur_weapontype=2; //Ȱȭ + int32 currentbullet = 3; // źâ ź + int32 maxbullet=4; // ִź +} + +// +message SM_REQ_BATTLE_DODGE +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + int32 dir_id = 5; // Ÿ +} + +// +message SM_REQ_BATTLE_WEAPON_CHANGE +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ߰ +message SM_REQ_BATTLE_WEAPON_ADD +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ij . +message SM_REQ_BATTLE_RESPAWN +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 cur_weapontype=8; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=9; //ִ +} + +// Ʈ ͷ +message SM_REQ_BATTLE_OBJECT_INTERACTION +{ + int32 objecttableid=1; // ͷ Ʋ Ʈ Table ID + string objectguid=2; // ͷ Ʋ Ʈ GUID +} + +// . +message SM_REQ_BATTLE_MANTLE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float tx =11; // + float ty =12; // + float tz =13; // + int32 height = 14;// +} + +// ΰ ε +message SM_REQ_BATTLE_FINISHED_LODING +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // +} + +// غ : δ â +message SM_REQ_BATTLE_READY_TO_EXIT +{ +} + +// Ÿݿ ؼ ư +message SM_REQ_BATTLE_LAUNCH_CHARACTER +{ + SM_CHARACTER_KEY target=1; // ư + float vx =2; // LaunchVelocity + float vy =3; // + float vz =4; // + int32 xyoverride = 5;//bXYOverride + int32 zoverride = 6;//bZOverride +} + +// Ư Ȳ ϸ̼ +message SM_REQ_BATTLE_PLAY_ANIMATION +{ + SM_CHARACTER_KEY target =1; // + int32 type = 2; // + int32 value = 3; // +} + +// Ʈ +message SM_REQ_BATTLE_HIT_EFFECT +{ + SM_CHARACTER_KEY target =1; // + int32 weapontype = 2; // + int32 hiteffecttype = 3; // + float nx =4; // ImpactNormal + float ny =5; // + float nz =6; // + float px =7; // ImpactPoint + float py =8; // + float pz =9; // +} + +message SM_MOB_AI_VECTOR +{ + float px =1; // ImpactPoint + float py =2; // + float pz =3; // +} + +message SM_MOB_AI_AGGRO +{ + SM_CHARACTER_KEY target = 1; + int32 aggropoint = 2; +} + +message SM_MOB_AI_SYNCDATA +{ + string mobguid = 1; + int32 currentAiBehavior = 2; + + int32 taskflags = 3; + + SM_MOB_AI_VECTOR startLocation = 4; + SM_MOB_AI_VECTOR lastKnownLocation = 5; + SM_MOB_AI_VECTOR lastHearingLocation = 6; + + SM_CHARACTER_KEY targetCharacter = 7; + SM_CHARACTER_KEY lastHearingCharacter = 8; + + repeated SM_MOB_AI_AGGRO targetCharacterList = 9; // +} + +message SM_MOB_AI_ATTACK +{ + string mobguid = 1; + float attack_time = 2; + SM_CHARACTER_KEY targetCharacter = 3; + int32 attack_type = 4; + SM_MOB_AI_VECTOR location = 5; +} + +// Ѿ ߻. +message SM_MOB_AI_SHOOT +{ + string mobguid = 1; + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 server_time=4; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 5; // ߻Ѿ. ϼִ. +} + +// . +message SM_MOB_AI_RELOAD +{ + string mobguid = 1; +} + +message SM_MOB_AI_FOCUS +{ + string mobguid = 1; + int32 focus = 2; + SM_CHARACTER_KEY targetCharacter = 3; +} + +message SM_MOB_MOVETO +{ + string mobguid = 1; + int32 targetType = 2; + SM_MOB_AI_VECTOR tagetLocation = 3; + float radius = 4; + int32 stop = 5; + float moveSpeed = 6; +} + +message SM_MOB_SPAWN +{ + string mobguid = 1; + int32 mob_type = 2; + SM_MOB_AI_VECTOR location = 3; + float angle = 4; + string anchorguid = 5; +} + +message SM_RUNNING_TRIGGER +{ + string Anchor_GUID = 1; + string User_GUID = 2; + int64 server_Time = 3; + int32 hurdle_type = 4; + int32 is_triggered = 5; + + // Ʈ Ÿ ũ + int32 impact_type = 6; + float impact_x = 7; + float impact_y = 8; + float impact_z = 9; + int32 is_xy_override = 10; + int32 is_z_override = 11; + + // Ÿ ũ + int32 ragdoll_type = 12; + string ragdoll_bone_name = 13; + int32 is_partial = 14; + int32 is_moveable = 15; +} + +message SM_HURDLE_TRAVERSAL +{ + string anchor_guid = 1; + int64 server_time = 2; + int32 use_hurdle = 3; + int32 use_hurdle_mode = 4; + + int32 hurdle_type = 5; + int32 traversal_state = 6; + int32 use_anim_dir = 7; + + int32 montage_action = 8; + float loc_x = 9; + float loc_y = 10; + float loc_z = 11; + float rot_pitch = 12; + float rot_yaw = 13; + float rot_roll = 14; +} + +message SM_RUNNING_ITEM +{ + string Anchor_GUID = 1; + string str_item = 2; +} + +message SM_RUNNING_HURDLE_SPAWN +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + string class_path = 3; + int64 server_time = 4; + + float loc_x = 5; + float loc_y = 6; + float loc_z = 7; + + float rot_pitch = 8; + float rot_yaw = 9; + float rot_roll = 10; + + int32 hurdle_type = 11; + + float power = 12; +} + +message SM_RUNNING_HURDLE_STATE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + int32 state_type = 4; +} + +message SM_RUNNING_HURDLE_MOVE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + float loc_x = 4; + float loc_y = 5; + float loc_z = 6; + + float rot_pitch = 7; + float rot_yaw = 8; + float rot_roll = 9; + + float vel_x = 10; + float vel_y = 11; + float vel_z = 12; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_HOST_ROOMINFO +{ + string host_guid = 1; + int64 room_starttime = 2; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_REQ_HOST_ROOMINFO +{ + +} + diff --git a/Protocol/client-proto/.svn/pristine/8e/8e9a15cc522f11fb55c98f64915423fecbd4854b.svn-base b/Protocol/client-proto/.svn/pristine/8e/8e9a15cc522f11fb55c98f64915423fecbd4854b.svn-base new file mode 100644 index 0000000..00c43d5 --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/8e/8e9a15cc522f11fb55c98f64915423fecbd4854b.svn-base @@ -0,0 +1,564 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package Packet; + +//import "google/protobuf/timestamp.proto"; +// [END declaration] + +// [START csharp_declaration] +option csharp_namespace = "Google.Protobuf.CES.Packet"; +// [END csharp_declaration] + +// [START messages] + +message SM_REQ_MOVE_NEXT +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float ang =4; // + optional float movevalue =5; // + optional int32 start =6; // ù ̵̸ true ̵θ false + optional int32 move_type = 7; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + optional int32 server_time=8; // Ŷ ð +} + +message SM_REQ_MOVE_COMBINE +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float lx =4; // + optional float ly =5; // + optional float lz =6; // + optional float ang =7; // + optional int32 move_speed =8; // + optional int32 move_type = 9; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + optional int32 start =10; // ù ̵̸ true ̵θ false + optional int32 server_time=11; // Ŷ ð +} + +message SM_REQ_TRACKING_TYPE +{ + optional int32 type =1; +} + +message SM_REQ_VOICE +{ + optional int32 sampleRate = 1; // + optional int32 numchannels = 2; // + optional int32 PCMSize = 3; // + repeated int32 data = 4; + optional int32 dataSize = 5; // +} + +message SM_REQ_MUSICBOX +{ + optional string propguid = 1; + optional int32 sampleRate = 2; // + optional int32 numchannels = 3; // + optional int32 PCMSize = 4; // + repeated int32 data = 5; + optional int32 dataSize = 6; // +} + +message SM_REQ_JUMP +{ + optional float sx =1; // + optional float sy =2; // + optional float sz =3; // + optional float lx =4; // + optional float ly =5; // + optional float lz =6; // + optional float ang =7; // + optional int32 start =8; // ù ̵̸ true ̵θ false + optional int32 server_time=9; // Ŷ ð +} + +message SM_REQ_GUIDE +{ + optional string guoup =1; // + optional int32 operatorid =2; // + optional int32 guidetype =3; // +} + +message SM_REQ_PRESENTATION_PAGE +{ + string pageurl =1; // + int32 pageid =2; // +} + + +message SM_REQ_AIM +{ + optional float pitch =1; // +} + +message SM_REQ_PARTY_MOVE_MEMBER +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // +} + +message SM_REQ_VOICE_STATE +{ + optional string nickname =1; // + optional int32 state =2; // +} + +message SM_REQ_DANCE_INFO +{ + optional int32 danceid = 1; // ̵ + optional int32 motionid = 2; // + optional int32 dancetype = 3; // Ÿ޺ + optional int64 timestamp = 4; // ð +} + +message SM_REQ_DANCE_ROOMTIME +{ + int64 sendmytime = 1; +} + +message SM_SEND_DANCE_ROOMTIME +{ + int64 sendroomtime = 1; + string guid = 2; +} + +message SM_SEND_DANCE_STATE +{ + optional int32 state = 1; + optional int64 sendroomtime = 2; + optional int32 state01 = 3; + optional int32 state02 = 4; + optional int32 state03 = 5; + optional string str01 = 6; + optional string str02 = 7; + optional string str03 = 8; +} + + +////////////////////////////////////// + +// Ѿ +message SM_DATA_BATTLE_BULLET +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float yaw =4; // + optional float pitch =5; // + optional int32 damage =6; // +} + +message SM_DATA_WEAPON_DATA +{ + int32 weapontype = 1; + int32 currentbullet = 2; // Ѿ. + int32 maxbullet = 3; // źâ ִź +} + +message SM_REQ_BATTLE_MOVE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float movescale =11; + int32 move_speed =12; // ̵ӵ + int32 move_type = 13; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int32 type =14; // ̵ȭŸ + float aim_yaw =15; // + float aim_pitch =16; // + int32 cur_hp=17; // + int32 shoot_type = 18; // ѱ߻ + int32 cur_weapontype=19;// Ȱȭ + int32 server_time=20; // Ŷ ð + repeated SM_DATA_WEAPON_DATA weapontypes=21; //ִ +} + +// ij . +message SM_REQ_BATTLE_STATUS +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 shoot_type = 8; // ѱ߻ + int32 cur_weapontype=9; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=10; //ִ + int32 podcarry = 11; // ִ. + int32 podcount = 12; // ȹ . + int32 killcount = 13; + int32 deathcount = 14; + int32 assistcount = 15; + int32 damagecount = 16; +} + +// /Ǯ +message SM_REQ_BATTLE_AIM +{ + float ang =1; // ij + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 aim_type = 4; // Ÿ + int32 move_type =5; // ̵Ÿ -- ߰ +} + +// ߻ +message SM_REQ_BATTLE_SHOOT +{ + int32 weapontype =1; // ߻繫 + int32 remainbullet = 2; // ź. : currentbullet + float aim_yaw =3; // ī޶ + float aim_pitch =4; // ī޶ + int32 aim_type = 5; // Ÿ + int32 server_time=6; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 7; // ߻Ѿ. ϼִ. + +} + +message SM_CHARACTER_KEY +{ + int32 type = 1; + string guid = 2; +} + +// ǰ, ڽ hp . -> ȣƮ ¾Ҵٰ . +message SM_REQ_BATTLE_DAMAGE +{ + int32 damage =1; // + int32 cur_hp=2; // . 0̸ . + SM_CHARACTER_KEY attacker=3; // . + SM_CHARACTER_KEY victim=4; // ǰ. + int32 attack_weapon_type = 5; // ݹŸ. +} + +// +message SM_REQ_BATTLE_RELOAD +{ + int32 cur_hp=1; // + int32 cur_weapontype=2; //Ȱȭ + int32 currentbullet = 3; // źâ ź + int32 maxbullet=4; // ִź +} + +// +message SM_REQ_BATTLE_DODGE +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + int32 dir_id = 5; // Ÿ +} + +// +message SM_REQ_BATTLE_WEAPON_CHANGE +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ߰ +message SM_REQ_BATTLE_WEAPON_ADD +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ij . +message SM_REQ_BATTLE_RESPAWN +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 cur_weapontype=8; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=9; //ִ +} + +// Ʈ ͷ +message SM_REQ_BATTLE_OBJECT_INTERACTION +{ + int32 objecttableid=1; // ͷ Ʋ Ʈ Table ID + string objectguid=2; // ͷ Ʋ Ʈ GUID +} + +// . +message SM_REQ_BATTLE_MANTLE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float tx =11; // + float ty =12; // + float tz =13; // + int32 height = 14;// +} + +// ΰ ε +message SM_REQ_BATTLE_FINISHED_LODING +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // +} + +// غ : δ â +message SM_REQ_BATTLE_READY_TO_EXIT +{ +} + +// Ÿݿ ؼ ư +message SM_REQ_BATTLE_LAUNCH_CHARACTER +{ + SM_CHARACTER_KEY target=1; // ư + float vx =2; // LaunchVelocity + float vy =3; // + float vz =4; // + int32 xyoverride = 5;//bXYOverride + int32 zoverride = 6;//bZOverride +} + +// Ư Ȳ ϸ̼ +message SM_REQ_BATTLE_PLAY_ANIMATION +{ + SM_CHARACTER_KEY target =1; // + int32 type = 2; // + int32 value = 3; // +} + +// Ʈ +message SM_REQ_BATTLE_HIT_EFFECT +{ + SM_CHARACTER_KEY target =1; // + int32 weapontype = 2; // + int32 hiteffecttype = 3; // + float nx =4; // ImpactNormal + float ny =5; // + float nz =6; // + float px =7; // ImpactPoint + float py =8; // + float pz =9; // +} + +message SM_MOB_AI_VECTOR +{ + float px =1; // ImpactPoint + float py =2; // + float pz =3; // +} + +message SM_MOB_AI_AGGRO +{ + SM_CHARACTER_KEY target = 1; + int32 aggropoint = 2; +} + +message SM_MOB_AI_SYNCDATA +{ + string mobguid = 1; + int32 currentAiBehavior = 2; + + int32 taskflags = 3; + + SM_MOB_AI_VECTOR startLocation = 4; + SM_MOB_AI_VECTOR lastKnownLocation = 5; + SM_MOB_AI_VECTOR lastHearingLocation = 6; + + SM_CHARACTER_KEY targetCharacter = 7; + SM_CHARACTER_KEY lastHearingCharacter = 8; + + repeated SM_MOB_AI_AGGRO targetCharacterList = 9; // +} + +message SM_MOB_AI_ATTACK +{ + string mobguid = 1; + float attack_time = 2; + SM_CHARACTER_KEY targetCharacter = 3; + int32 attack_type = 4; + SM_MOB_AI_VECTOR location = 5; +} + +// Ѿ ߻. +message SM_MOB_AI_SHOOT +{ + string mobguid = 1; + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 server_time=4; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 5; // ߻Ѿ. ϼִ. +} + +// . +message SM_MOB_AI_RELOAD +{ + string mobguid = 1; +} + +message SM_MOB_AI_FOCUS +{ + string mobguid = 1; + int32 focus = 2; + SM_CHARACTER_KEY targetCharacter = 3; +} + +message SM_MOB_MOVETO +{ + string mobguid = 1; + int32 targetType = 2; + SM_MOB_AI_VECTOR tagetLocation = 3; + float radius = 4; + int32 stop = 5; + float moveSpeed = 6; +} + +message SM_MOB_SPAWN +{ + string mobguid = 1; + int32 mob_type = 2; + SM_MOB_AI_VECTOR location = 3; + float angle = 4; + string anchorguid = 5; +} + +message SM_RUNNING_TRIGGER +{ + string Anchor_GUID = 1; + string User_GUID = 2; + int64 server_Time = 3; + int32 hurdle_type = 4; + int32 is_triggered = 5; + + // Ʈ Ÿ ũ + int32 impact_type = 6; + float impact_x = 7; + float impact_y = 8; + float impact_z = 9; + int32 is_xy_override = 10; + int32 is_z_override = 11; + + // Ÿ ũ + int32 ragdoll_type = 12; + string ragdoll_bone_name = 13; + int32 is_partial = 14; + int32 is_moveable = 15; +} + +message SM_HURDLE_TRAVERSAL +{ + string anchor_guid = 1; + int64 server_time = 3; + int32 hurdle_type = 4; + int32 traversal_state = 5; + int32 montage_action = 6; + + float loc_x = 7; + float loc_y = 8; + float loc_z = 9; + + float rot_pitch = 10; + float rot_yaw = 11; + float rot_roll = 12; + + int32 use_hurdle = 13; +} + +message SM_RUNNING_ITEM +{ + string Anchor_GUID = 1; + string str_item = 2; +} + +message SM_RUNNING_HURDLE_SPAWN +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + string class_path = 3; + int64 server_time = 4; + + float loc_x = 5; + float loc_y = 6; + float loc_z = 7; + + float rot_pitch = 8; + float rot_yaw = 9; + float rot_roll = 10; + + int32 hurdle_type = 11; + + float power = 12; +} + +message SM_RUNNING_HURDLE_STATE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + int32 state_type = 4; +} + +message SM_RUNNING_HURDLE_MOVE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + float loc_x = 4; + float loc_y = 5; + float loc_z = 6; + + float rot_pitch = 7; + float rot_yaw = 8; + float rot_roll = 9; + + float vel_x = 10; + float vel_y = 11; + float vel_z = 12; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_HOST_ROOMINFO +{ + string host_guid = 1; + int64 room_starttime = 2; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_REQ_HOST_ROOMINFO +{ + +} + diff --git a/Protocol/client-proto/.svn/pristine/a4/a452b6ce4f470cbc2510f47f620cef6acfb9c314.svn-base b/Protocol/client-proto/.svn/pristine/a4/a452b6ce4f470cbc2510f47f620cef6acfb9c314.svn-base new file mode 100644 index 0000000..787f71b --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/a4/a452b6ce4f470cbc2510f47f620cef6acfb9c314.svn-base @@ -0,0 +1,591 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package Packet; + +//import "google/protobuf/timestamp.proto"; +// [END declaration] + +// [START csharp_declaration] +option csharp_namespace = "Google.Protobuf.CES.Packet"; +// [END csharp_declaration] + +// [START messages] + +message SM_REQ_MOVE_NEXT +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float movevalue =5; // + int32 start =6; // ù ̵̸ true ̵θ false + int32 move_type = 7; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int64 server_time=8; // Ŷ ð +} + +message SM_REQ_MOVE_COMBINE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + optional float vx =7; // + optional float vy =8; // + optional float vz =9; // + float ang =10; // + int32 move_speed =11; // + int32 move_type = 12; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int32 start =13; // ù ̵̸ true ̵θ false + int64 server_time=14; // Ŷ ð +} + +message SM_REQ_VOICE +{ + int32 sampleRate = 1; // + int32 numchannels = 2; // + int32 PCMSize = 3; // + repeated int32 data = 4; + int32 dataSize = 5; // +} + +message SM_REQ_MUSICBOX +{ + string propguid = 1; + int32 sampleRate = 2; // + int32 numchannels = 3; // + int32 PCMSize = 4; // + repeated int32 data = 5; + int32 dataSize = 6; // +} + +message SM_REQ_JUMP +{ + float sx =1; // + float sy =2; // + float sz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + int32 start =11; // ù ̵̸ true ̵θ false + int64 server_time=12; // Ŷ ð +} + +message SM_REQ_PRESENTATION_PAGE +{ + string pageurl =1; // + int32 pageid =2; // +} + + +message SM_REQ_AIM +{ + float pitch =1; // +} + +message SM_REQ_PARTY_MOVE_MEMBER +{ + float px =1; // + float py =2; // + float pz =3; // +} + +message SM_REQ_VOICE_STATE +{ + string nickname =1; // + int32 state =2; // +} + +message SM_REQ_DANCE_INFO +{ + int32 danceid = 1; // ̵ + int32 motionid = 2; // + int32 dancetype = 3; // Ÿ޺ + int64 timestamp = 4; // ð +} + +message SM_REQ_DANCE_ROOMTIME +{ + int64 sendmytime = 1; +} + +message SM_SEND_DANCE_ROOMTIME +{ + int64 sendroomtime = 1; + string guid = 2; +} + +message SM_SEND_DANCE_STATE +{ + int32 state = 1; + int64 sendroomtime = 2; + int32 state01 = 3; + int32 state02 = 4; + int32 state03 = 5; + string str01 = 6; + string str02 = 7; + string str03 = 8; +} + + +////////////////////////////////////// + +// Ѿ +message SM_DATA_BATTLE_BULLET +{ + float px =1; // + float py =2; // + float pz =3; // + float yaw =4; // + float pitch =5; // + int32 damage =6; // +} + +message SM_DATA_WEAPON_DATA +{ + int32 weapontype = 1; // + int32 weaponid = 2; // ̺ID + int32 currentbullet = 3; // Ѿ. + int32 maxbullet = 4; // źâ ִź +} + +message SM_REQ_BATTLE_MOVE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + optional float vx =7; // + optional float vy =8; // + optional float vz =9; // + float ang =10; // + float movescale =11; + int32 move_speed =12; // ̵ӵ + int32 move_type = 13; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int32 type =14; // ̵ȭŸ + optional float aim_yaw =15; // + optional float aim_pitch =16; // + optional int32 shoot_type = 17; // ѱ߻ + optional int32 cur_weapontype=18;// Ȱȭ + int64 server_time=19; // Ŷ ð + repeated SM_DATA_WEAPON_DATA weapontypes=20; //ִ +} + +// ij . +message SM_REQ_BATTLE_STATUS +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 shoot_type = 8; // ѱ߻ + int32 cur_weapontype=9; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=10; //ִ + int32 podcarry = 11; // ִ. + int32 podcount = 12; // ȹ . + int32 killcount = 13; + int32 deathcount = 14; + int32 assistcount = 15; + int32 damagecount = 16; +} + +// /Ǯ +message SM_REQ_BATTLE_AIM +{ + float ang =1; // ij + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 aim_type = 4; // Ÿ + int32 move_type =5; // ̵Ÿ -- ߰ +} + +// ߻ +message SM_REQ_BATTLE_SHOOT +{ + int32 weapontype =1; // ߻繫 + int32 remainbullet = 2; // ź. : currentbullet + float aim_yaw =3; // ī޶ + float aim_pitch =4; // ī޶ + int32 aim_type = 5; // Ÿ + int64 server_time=6; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 7; // ߻Ѿ. ϼִ. + +} + +message SM_CHARACTER_KEY +{ + int32 type = 1; + string guid = 2; +} + +// ǰ, ڽ hp . -> ȣƮ ¾Ҵٰ . +message SM_REQ_BATTLE_DAMAGE +{ + int32 damage =1; // + int32 cur_hp=2; // . 0̸ . + SM_CHARACTER_KEY attacker=3; // . + SM_CHARACTER_KEY victim=4; // ǰ. + int32 attack_weapon_type = 5; // ݹŸ. +} + +// +message SM_REQ_BATTLE_RELOAD +{ + int32 cur_hp=1; // + int32 cur_weapontype=2; //Ȱȭ + int32 currentbullet = 3; // źâ ź + int32 maxbullet=4; // ִź +} + +// +message SM_REQ_BATTLE_DODGE +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + int32 dir_id = 5; // Ÿ +} + +// +message SM_REQ_BATTLE_WEAPON_CHANGE +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ߰ +message SM_REQ_BATTLE_WEAPON_ADD +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ij . +message SM_REQ_BATTLE_RESPAWN +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 cur_weapontype=8; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=9; //ִ +} + +// Ʈ ͷ +message SM_REQ_BATTLE_OBJECT_INTERACTION +{ + int32 objecttableid=1; // ͷ Ʋ Ʈ Table ID + string objectguid=2; // ͷ Ʋ Ʈ GUID +} + +// . +message SM_REQ_BATTLE_MANTLE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float tx =11; // + float ty =12; // + float tz =13; // + int32 height = 14;// +} + +// ΰ ε +message SM_REQ_BATTLE_FINISHED_LODING +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // +} + +// غ : δ â +message SM_REQ_BATTLE_READY_TO_EXIT +{ +} + +// Ÿݿ ؼ ư +message SM_REQ_BATTLE_LAUNCH_CHARACTER +{ + SM_CHARACTER_KEY target=1; // ư + float vx =2; // LaunchVelocity + float vy =3; // + float vz =4; // + int32 xyoverride = 5;//bXYOverride + int32 zoverride = 6;//bZOverride +} + +// Ư Ȳ ϸ̼ +message SM_REQ_BATTLE_PLAY_ANIMATION +{ + SM_CHARACTER_KEY target =1; // + int32 type = 2; // + int32 value = 3; // +} + +// Ʈ +message SM_REQ_BATTLE_HIT_EFFECT +{ + SM_CHARACTER_KEY target =1; // + int32 weaponid = 2; // + int32 hiteffecttype = 3; // + float nx =4; // ImpactNormal + float ny =5; // + float nz =6; // + float px =7; // ImpactPoint + float py =8; // + float pz =9; // +} + +message SM_MOB_AI_VECTOR +{ + float px =1; // ImpactPoint + float py =2; // + float pz =3; // +} + +message SM_MOB_AI_AGGRO +{ + SM_CHARACTER_KEY target = 1; + int32 aggropoint = 2; +} + +message SM_MOB_AI_SYNCDATA +{ + string mobguid = 1; + int32 currentAiBehavior = 2; + + int32 taskflags = 3; + + SM_MOB_AI_VECTOR startLocation = 4; + SM_MOB_AI_VECTOR lastKnownLocation = 5; + SM_MOB_AI_VECTOR lastHearingLocation = 6; + + SM_CHARACTER_KEY targetCharacter = 7; + SM_CHARACTER_KEY lastHearingCharacter = 8; + + repeated SM_MOB_AI_AGGRO targetCharacterList = 9; // +} + +message SM_MOB_AI_ATTACK +{ + string mobguid = 1; + float attack_time = 2; + SM_CHARACTER_KEY targetCharacter = 3; + int32 attack_type = 4; + SM_MOB_AI_VECTOR location = 5; +} + +// Ѿ ߻. +message SM_MOB_AI_SHOOT +{ + string mobguid = 1; + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int64 server_time=4; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 5; // ߻Ѿ. ϼִ. +} + +// . +message SM_MOB_AI_RELOAD +{ + string mobguid = 1; +} + +message SM_MOB_AI_FOCUS +{ + string mobguid = 1; + int32 focus = 2; + SM_CHARACTER_KEY targetCharacter = 3; +} + +message SM_MOB_MOVETO +{ + string mobguid = 1; + int32 targetType = 2; + SM_MOB_AI_VECTOR tagetLocation = 3; + float radius = 4; + int32 stop = 5; + float moveSpeed = 6; +} + +message SM_MOB_SPAWN +{ + string mobguid = 1; + int32 mob_type = 2; + SM_MOB_AI_VECTOR location = 3; + float angle = 4; + string anchorguid = 5; +} + +message SM_RUNNING_TRIGGER +{ + string Anchor_GUID = 1; + string User_GUID = 2; + int32 hurdle_type = 3; + int32 is_triggered = 4; + + // Ʈ Ÿ ũ + optional int32 impact_type = 5; + optional SM_MOB_AI_VECTOR impact_force = 6; + optional int32 is_xy_override = 7; + optional int32 is_z_override = 8; + + // Ÿ ũ + optional int32 ragdoll_type = 9; + optional string ragdoll_bone_name = 10; + optional int32 is_partial = 11; + optional int32 is_moveable = 12; +} + +message SM_RUNNING_ATTACH +{ + string Anchor_GUID = 1; + int32 is_triggered = 5; +} + +message SM_HURDLE_TRAVERSAL +{ + string anchor_guid = 1; + int32 use_hurdle = 2; + int32 use_hurdle_mode = 3; + + int32 hurdle_type = 4; + int32 traversal_state = 5; + int32 use_anim_dir = 6; + + int32 montage_action = 7; + optional float loc_x = 8; + optional float loc_y = 9; + optional float loc_z = 10; + optional float rot_pitch = 11; + optional float rot_yaw = 12; + optional float rot_roll = 13; +} + +message SM_RUNNING_ITEM +{ + string Anchor_GUID = 1; + string str_item = 2; +} + +message SM_RUNNING_HURDLE_SPAWN +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + string class_path = 3; + int64 server_time = 4; + + float loc_x = 5; + float loc_y = 6; + float loc_z = 7; + + float rot_pitch = 8; + float rot_yaw = 9; + float rot_roll = 10; + + int32 hurdle_type = 11; + + float power = 12; +} + +message SM_RUNNING_HURDLE_STATE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + int32 state_type = 4; +} + +message SM_RUNNING_HURDLE_MOVE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + float loc_x = 4; + float loc_y = 5; + float loc_z = 6; + + float rot_pitch = 7; + float rot_yaw = 8; + float rot_roll = 9; + + float vel_x = 10; + float vel_y = 11; + float vel_z = 12; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_HOST_ROOMINFO +{ + string host_guid = 1; + int64 room_starttime = 2; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_REQ_HOST_ROOMINFO +{ + +} + +message SM_RUNNING_SCRIPT +{ + string Hurdle_GUID = 1; + int32 State = 2; +} + +message SM_RUNNING_ANCHOR_INFO +{ + string Hurdle_GUID = 1; + int32 Hurdle_Type = 2; + repeated int32 IntValue = 3; + repeated float FloatValue = 4; + optional string StrValue = 5; +} + +// Ŀ ߷ Ŷ +message SM_RUNNING_GRAVITY +{ + int32 gravity_mode = 1; + float gravity_scale = 2; + SM_MOB_AI_VECTOR gravity_direction = 3; + + float air_control = 4; + float air_control_boost_multiplier = 5; + float air_control_boost_velocity_threshold = 6; + + float asend_power = 7; + float desend_power = 8; +} + diff --git a/Protocol/client-proto/.svn/pristine/b3/b3475cc5ba2f4797a1c3ae30a8e1e0560b411b03.svn-base b/Protocol/client-proto/.svn/pristine/b3/b3475cc5ba2f4797a1c3ae30a8e1e0560b411b03.svn-base new file mode 100644 index 0000000..3e4d5c8 --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/b3/b3475cc5ba2f4797a1c3ae30a8e1e0560b411b03.svn-base @@ -0,0 +1,567 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package Packet; + +//import "google/protobuf/timestamp.proto"; +// [END declaration] + +// [START csharp_declaration] +option csharp_namespace = "Google.Protobuf.CES.Packet"; +// [END csharp_declaration] + +// [START messages] + +message SM_REQ_MOVE_NEXT +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float ang =4; // + optional float movevalue =5; // + optional int32 start =6; // ù ̵̸ true ̵θ false + optional int32 move_type = 7; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + optional int32 server_time=8; // Ŷ ð +} + +message SM_REQ_MOVE_COMBINE +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float lx =4; // + optional float ly =5; // + optional float lz =6; // + optional float ang =7; // + optional int32 move_speed =8; // + optional int32 move_type = 9; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + optional int32 start =10; // ù ̵̸ true ̵θ false + optional int32 server_time=11; // Ŷ ð +} + +message SM_REQ_TRACKING_TYPE +{ + optional int32 type =1; +} + +message SM_REQ_VOICE +{ + optional int32 sampleRate = 1; // + optional int32 numchannels = 2; // + optional int32 PCMSize = 3; // + repeated int32 data = 4; + optional int32 dataSize = 5; // +} + +message SM_REQ_MUSICBOX +{ + optional string propguid = 1; + optional int32 sampleRate = 2; // + optional int32 numchannels = 3; // + optional int32 PCMSize = 4; // + repeated int32 data = 5; + optional int32 dataSize = 6; // +} + +message SM_REQ_JUMP +{ + optional float sx =1; // + optional float sy =2; // + optional float sz =3; // + optional float lx =4; // + optional float ly =5; // + optional float lz =6; // + optional float ang =7; // + optional int32 start =8; // ù ̵̸ true ̵θ false + optional int32 server_time=9; // Ŷ ð +} + +message SM_REQ_GUIDE +{ + optional string guoup =1; // + optional int32 operatorid =2; // + optional int32 guidetype =3; // +} + +message SM_REQ_PRESENTATION_PAGE +{ + string pageurl =1; // + int32 pageid =2; // +} + + +message SM_REQ_AIM +{ + optional float pitch =1; // +} + +message SM_REQ_PARTY_MOVE_MEMBER +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // +} + +message SM_REQ_VOICE_STATE +{ + optional string nickname =1; // + optional int32 state =2; // +} + +message SM_REQ_DANCE_INFO +{ + optional int32 danceid = 1; // ̵ + optional int32 motionid = 2; // + optional int32 dancetype = 3; // Ÿ޺ + optional int64 timestamp = 4; // ð +} + +message SM_REQ_DANCE_ROOMTIME +{ + int64 sendmytime = 1; +} + +message SM_SEND_DANCE_ROOMTIME +{ + int64 sendroomtime = 1; + string guid = 2; +} + +message SM_SEND_DANCE_STATE +{ + optional int32 state = 1; + optional int64 sendroomtime = 2; + optional int32 state01 = 3; + optional int32 state02 = 4; + optional int32 state03 = 5; + optional string str01 = 6; + optional string str02 = 7; + optional string str03 = 8; +} + + +////////////////////////////////////// + +// Ѿ +message SM_DATA_BATTLE_BULLET +{ + optional float px =1; // + optional float py =2; // + optional float pz =3; // + optional float yaw =4; // + optional float pitch =5; // + optional int32 damage =6; // +} + +message SM_DATA_WEAPON_DATA +{ + int32 weapontype = 1; // + int32 weaponid = 2; // ̺ID + int32 currentbullet = 3; // Ѿ. + int32 maxbullet = 4; // źâ ִź +} + +message SM_REQ_BATTLE_MOVE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float movescale =11; + int32 move_speed =12; // ̵ӵ + int32 move_type = 13; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int32 type =14; // ̵ȭŸ + float aim_yaw =15; // + float aim_pitch =16; // + int32 cur_hp=17; // + int32 shoot_type = 18; // ѱ߻ + int32 cur_weapontype=19;// Ȱȭ + int32 server_time=20; // Ŷ ð + repeated SM_DATA_WEAPON_DATA weapontypes=21; //ִ +} + +// ij . +message SM_REQ_BATTLE_STATUS +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 shoot_type = 8; // ѱ߻ + int32 cur_weapontype=9; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=10; //ִ + int32 podcarry = 11; // ִ. + int32 podcount = 12; // ȹ . + int32 killcount = 13; + int32 deathcount = 14; + int32 assistcount = 15; + int32 damagecount = 16; +} + +// /Ǯ +message SM_REQ_BATTLE_AIM +{ + float ang =1; // ij + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 aim_type = 4; // Ÿ + int32 move_type =5; // ̵Ÿ -- ߰ +} + +// ߻ +message SM_REQ_BATTLE_SHOOT +{ + int32 weapontype =1; // ߻繫 + int32 remainbullet = 2; // ź. : currentbullet + float aim_yaw =3; // ī޶ + float aim_pitch =4; // ī޶ + int32 aim_type = 5; // Ÿ + int32 server_time=6; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 7; // ߻Ѿ. ϼִ. + +} + +message SM_CHARACTER_KEY +{ + int32 type = 1; + string guid = 2; +} + +// ǰ, ڽ hp . -> ȣƮ ¾Ҵٰ . +message SM_REQ_BATTLE_DAMAGE +{ + int32 damage =1; // + int32 cur_hp=2; // . 0̸ . + SM_CHARACTER_KEY attacker=3; // . + SM_CHARACTER_KEY victim=4; // ǰ. + int32 attack_weapon_type = 5; // ݹŸ. +} + +// +message SM_REQ_BATTLE_RELOAD +{ + int32 cur_hp=1; // + int32 cur_weapontype=2; //Ȱȭ + int32 currentbullet = 3; // źâ ź + int32 maxbullet=4; // ִź +} + +// +message SM_REQ_BATTLE_DODGE +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + int32 dir_id = 5; // Ÿ +} + +// +message SM_REQ_BATTLE_WEAPON_CHANGE +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ߰ +message SM_REQ_BATTLE_WEAPON_ADD +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ij . +message SM_REQ_BATTLE_RESPAWN +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 cur_weapontype=8; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=9; //ִ +} + +// Ʈ ͷ +message SM_REQ_BATTLE_OBJECT_INTERACTION +{ + int32 objecttableid=1; // ͷ Ʋ Ʈ Table ID + string objectguid=2; // ͷ Ʋ Ʈ GUID +} + +// . +message SM_REQ_BATTLE_MANTLE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float tx =11; // + float ty =12; // + float tz =13; // + int32 height = 14;// +} + +// ΰ ε +message SM_REQ_BATTLE_FINISHED_LODING +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // +} + +// غ : δ â +message SM_REQ_BATTLE_READY_TO_EXIT +{ +} + +// Ÿݿ ؼ ư +message SM_REQ_BATTLE_LAUNCH_CHARACTER +{ + SM_CHARACTER_KEY target=1; // ư + float vx =2; // LaunchVelocity + float vy =3; // + float vz =4; // + int32 xyoverride = 5;//bXYOverride + int32 zoverride = 6;//bZOverride +} + +// Ư Ȳ ϸ̼ +message SM_REQ_BATTLE_PLAY_ANIMATION +{ + SM_CHARACTER_KEY target =1; // + int32 type = 2; // + int32 value = 3; // +} + +// Ʈ +message SM_REQ_BATTLE_HIT_EFFECT +{ + SM_CHARACTER_KEY target =1; // + int32 weaponid = 2; // + int32 hiteffecttype = 3; // + float nx =4; // ImpactNormal + float ny =5; // + float nz =6; // + float px =7; // ImpactPoint + float py =8; // + float pz =9; // +} + +message SM_MOB_AI_VECTOR +{ + float px =1; // ImpactPoint + float py =2; // + float pz =3; // +} + +message SM_MOB_AI_AGGRO +{ + SM_CHARACTER_KEY target = 1; + int32 aggropoint = 2; +} + +message SM_MOB_AI_SYNCDATA +{ + string mobguid = 1; + int32 currentAiBehavior = 2; + + int32 taskflags = 3; + + SM_MOB_AI_VECTOR startLocation = 4; + SM_MOB_AI_VECTOR lastKnownLocation = 5; + SM_MOB_AI_VECTOR lastHearingLocation = 6; + + SM_CHARACTER_KEY targetCharacter = 7; + SM_CHARACTER_KEY lastHearingCharacter = 8; + + repeated SM_MOB_AI_AGGRO targetCharacterList = 9; // +} + +message SM_MOB_AI_ATTACK +{ + string mobguid = 1; + float attack_time = 2; + SM_CHARACTER_KEY targetCharacter = 3; + int32 attack_type = 4; + SM_MOB_AI_VECTOR location = 5; +} + +// Ѿ ߻. +message SM_MOB_AI_SHOOT +{ + string mobguid = 1; + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 server_time=4; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 5; // ߻Ѿ. ϼִ. +} + +// . +message SM_MOB_AI_RELOAD +{ + string mobguid = 1; +} + +message SM_MOB_AI_FOCUS +{ + string mobguid = 1; + int32 focus = 2; + SM_CHARACTER_KEY targetCharacter = 3; +} + +message SM_MOB_MOVETO +{ + string mobguid = 1; + int32 targetType = 2; + SM_MOB_AI_VECTOR tagetLocation = 3; + float radius = 4; + int32 stop = 5; + float moveSpeed = 6; +} + +message SM_MOB_SPAWN +{ + string mobguid = 1; + int32 mob_type = 2; + SM_MOB_AI_VECTOR location = 3; + float angle = 4; + string anchorguid = 5; +} + +message SM_RUNNING_TRIGGER +{ + string Anchor_GUID = 1; + string User_GUID = 2; + int64 server_Time = 3; + int32 hurdle_type = 4; + int32 is_triggered = 5; + + SM_MOB_AI_VECTOR user_location = 6; + float user_angle = 7; + + // Ʈ Ÿ ũ + int32 impact_type = 8; + SM_MOB_AI_VECTOR impact_force = 9; + int32 is_xy_override = 10; + int32 is_z_override = 11; + + // Ÿ ũ + int32 ragdoll_type = 12; + string ragdoll_bone_name = 13; + int32 is_partial = 14; + int32 is_moveable = 15; +} + +message SM_HURDLE_TRAVERSAL +{ + string anchor_guid = 1; + int64 server_time = 2; + int32 use_hurdle = 3; + int32 use_hurdle_mode = 4; + + int32 hurdle_type = 5; + int32 traversal_state = 6; + int32 use_anim_dir = 7; + + int32 montage_action = 8; + float loc_x = 9; + float loc_y = 10; + float loc_z = 11; + float rot_pitch = 12; + float rot_yaw = 13; + float rot_roll = 14; +} + +message SM_RUNNING_ITEM +{ + string Anchor_GUID = 1; + string str_item = 2; +} + +message SM_RUNNING_HURDLE_SPAWN +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + string class_path = 3; + int64 server_time = 4; + + float loc_x = 5; + float loc_y = 6; + float loc_z = 7; + + float rot_pitch = 8; + float rot_yaw = 9; + float rot_roll = 10; + + int32 hurdle_type = 11; + + float power = 12; +} + +message SM_RUNNING_HURDLE_STATE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + int32 state_type = 4; +} + +message SM_RUNNING_HURDLE_MOVE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + float loc_x = 4; + float loc_y = 5; + float loc_z = 6; + + float rot_pitch = 7; + float rot_yaw = 8; + float rot_roll = 9; + + float vel_x = 10; + float vel_y = 11; + float vel_z = 12; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_HOST_ROOMINFO +{ + string host_guid = 1; + int64 room_starttime = 2; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_REQ_HOST_ROOMINFO +{ + +} + diff --git a/Protocol/client-proto/.svn/pristine/d4/d4f06695c8e6689660c94d86ba0ade4ef23568b1.svn-base b/Protocol/client-proto/.svn/pristine/d4/d4f06695c8e6689660c94d86ba0ade4ef23568b1.svn-base new file mode 100644 index 0000000..7d1bf71 --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/d4/d4f06695c8e6689660c94d86ba0ade4ef23568b1.svn-base @@ -0,0 +1,560 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package Packet; + +//import "google/protobuf/timestamp.proto"; +// [END declaration] + +// [START csharp_declaration] +option csharp_namespace = "Google.Protobuf.CES.Packet"; +// [END csharp_declaration] + +// [START messages] + +message SM_REQ_MOVE_NEXT +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float movevalue =5; // + int32 start =6; // ù ̵̸ true ̵θ false + int32 move_type = 7; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int64 server_time=8; // Ŷ ð +} + +message SM_REQ_MOVE_COMBINE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float ang =7; // + int32 move_speed =8; // + int32 move_type = 9; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int32 start =10; // ù ̵̸ true ̵θ false + int64 server_time=11; // Ŷ ð +} + +message SM_REQ_VOICE +{ + int32 sampleRate = 1; // + int32 numchannels = 2; // + int32 PCMSize = 3; // + repeated int32 data = 4; + int32 dataSize = 5; // +} + +message SM_REQ_MUSICBOX +{ + string propguid = 1; + int32 sampleRate = 2; // + int32 numchannels = 3; // + int32 PCMSize = 4; // + repeated int32 data = 5; + int32 dataSize = 6; // +} + +message SM_REQ_JUMP +{ + float sx =1; // + float sy =2; // + float sz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float ang =7; // + int32 start =8; // ù ̵̸ true ̵θ false + int64 server_time=9; // Ŷ ð +} + +message SM_REQ_PRESENTATION_PAGE +{ + string pageurl =1; // + int32 pageid =2; // +} + + +message SM_REQ_AIM +{ + float pitch =1; // +} + +message SM_REQ_PARTY_MOVE_MEMBER +{ + float px =1; // + float py =2; // + float pz =3; // +} + +message SM_REQ_VOICE_STATE +{ + string nickname =1; // + int32 state =2; // +} + +message SM_REQ_DANCE_INFO +{ + int32 danceid = 1; // ̵ + int32 motionid = 2; // + int32 dancetype = 3; // Ÿ޺ + int64 timestamp = 4; // ð +} + +message SM_REQ_DANCE_ROOMTIME +{ + int64 sendmytime = 1; +} + +message SM_SEND_DANCE_ROOMTIME +{ + int64 sendroomtime = 1; + string guid = 2; +} + +message SM_SEND_DANCE_STATE +{ + int32 state = 1; + int64 sendroomtime = 2; + int32 state01 = 3; + int32 state02 = 4; + int32 state03 = 5; + string str01 = 6; + string str02 = 7; + string str03 = 8; +} + + +////////////////////////////////////// + +// Ѿ +message SM_DATA_BATTLE_BULLET +{ + float px =1; // + float py =2; // + float pz =3; // + float yaw =4; // + float pitch =5; // + int32 damage =6; // +} + +message SM_DATA_WEAPON_DATA +{ + int32 weapontype = 1; // + int32 weaponid = 2; // ̺ID + int32 currentbullet = 3; // Ѿ. + int32 maxbullet = 4; // źâ ִź +} + +message SM_REQ_BATTLE_MOVE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + optional float vx =7; // + optional float vy =8; // + optional float vz =9; // + float ang =10; // + float movescale =11; + int32 move_speed =12; // ̵ӵ + int32 move_type = 13; // ̵Ÿ (ȱ, ٱ, Ʈ, ɾƼ̵) + int32 type =14; // ̵ȭŸ + optional float aim_yaw =15; // + optional float aim_pitch =16; // + optional int32 shoot_type = 17; // ѱ߻ + optional int32 cur_weapontype=18;// Ȱȭ + int64 server_time=19; // Ŷ ð + repeated SM_DATA_WEAPON_DATA weapontypes=20; //ִ +} + +// ij . +message SM_REQ_BATTLE_STATUS +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 shoot_type = 8; // ѱ߻ + int32 cur_weapontype=9; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=10; //ִ + int32 podcarry = 11; // ִ. + int32 podcount = 12; // ȹ . + int32 killcount = 13; + int32 deathcount = 14; + int32 assistcount = 15; + int32 damagecount = 16; +} + +// /Ǯ +message SM_REQ_BATTLE_AIM +{ + float ang =1; // ij + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int32 aim_type = 4; // Ÿ + int32 move_type =5; // ̵Ÿ -- ߰ +} + +// ߻ +message SM_REQ_BATTLE_SHOOT +{ + int32 weapontype =1; // ߻繫 + int32 remainbullet = 2; // ź. : currentbullet + float aim_yaw =3; // ī޶ + float aim_pitch =4; // ī޶ + int32 aim_type = 5; // Ÿ + int64 server_time=6; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 7; // ߻Ѿ. ϼִ. + +} + +message SM_CHARACTER_KEY +{ + int32 type = 1; + string guid = 2; +} + +// ǰ, ڽ hp . -> ȣƮ ¾Ҵٰ . +message SM_REQ_BATTLE_DAMAGE +{ + int32 damage =1; // + int32 cur_hp=2; // . 0̸ . + SM_CHARACTER_KEY attacker=3; // . + SM_CHARACTER_KEY victim=4; // ǰ. + int32 attack_weapon_type = 5; // ݹŸ. +} + +// +message SM_REQ_BATTLE_RELOAD +{ + int32 cur_hp=1; // + int32 cur_weapontype=2; //Ȱȭ + int32 currentbullet = 3; // źâ ź + int32 maxbullet=4; // ִź +} + +// +message SM_REQ_BATTLE_DODGE +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + int32 dir_id = 5; // Ÿ +} + +// +message SM_REQ_BATTLE_WEAPON_CHANGE +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ߰ +message SM_REQ_BATTLE_WEAPON_ADD +{ + int32 cur_weapontype=1; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=2; //ִ +} + +// ij . +message SM_REQ_BATTLE_RESPAWN +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // + float aim_yaw =5; // + float aim_pitch =6; // ī޶ + int32 cur_hp=7; // + int32 cur_weapontype=8; //Ȱȭ + repeated SM_DATA_WEAPON_DATA weapontypes=9; //ִ +} + +// Ʈ ͷ +message SM_REQ_BATTLE_OBJECT_INTERACTION +{ + int32 objecttableid=1; // ͷ Ʋ Ʈ Table ID + string objectguid=2; // ͷ Ʋ Ʈ GUID +} + +// . +message SM_REQ_BATTLE_MANTLE +{ + float px =1; // + float py =2; // + float pz =3; // + float lx =4; // + float ly =5; // + float lz =6; // + float vx =7; // + float vy =8; // + float vz =9; // + float ang =10; // + float tx =11; // + float ty =12; // + float tz =13; // + int32 height = 14;// +} + +// ΰ ε +message SM_REQ_BATTLE_FINISHED_LODING +{ + float px =1; // + float py =2; // + float pz =3; // + float ang =4; // +} + +// غ : δ â +message SM_REQ_BATTLE_READY_TO_EXIT +{ +} + +// Ÿݿ ؼ ư +message SM_REQ_BATTLE_LAUNCH_CHARACTER +{ + SM_CHARACTER_KEY target=1; // ư + float vx =2; // LaunchVelocity + float vy =3; // + float vz =4; // + int32 xyoverride = 5;//bXYOverride + int32 zoverride = 6;//bZOverride +} + +// Ư Ȳ ϸ̼ +message SM_REQ_BATTLE_PLAY_ANIMATION +{ + SM_CHARACTER_KEY target =1; // + int32 type = 2; // + int32 value = 3; // +} + +// Ʈ +message SM_REQ_BATTLE_HIT_EFFECT +{ + SM_CHARACTER_KEY target =1; // + int32 weaponid = 2; // + int32 hiteffecttype = 3; // + float nx =4; // ImpactNormal + float ny =5; // + float nz =6; // + float px =7; // ImpactPoint + float py =8; // + float pz =9; // +} + +message SM_MOB_AI_VECTOR +{ + float px =1; // ImpactPoint + float py =2; // + float pz =3; // +} + +message SM_MOB_AI_AGGRO +{ + SM_CHARACTER_KEY target = 1; + int32 aggropoint = 2; +} + +message SM_MOB_AI_SYNCDATA +{ + string mobguid = 1; + int32 currentAiBehavior = 2; + + int32 taskflags = 3; + + SM_MOB_AI_VECTOR startLocation = 4; + SM_MOB_AI_VECTOR lastKnownLocation = 5; + SM_MOB_AI_VECTOR lastHearingLocation = 6; + + SM_CHARACTER_KEY targetCharacter = 7; + SM_CHARACTER_KEY lastHearingCharacter = 8; + + repeated SM_MOB_AI_AGGRO targetCharacterList = 9; // +} + +message SM_MOB_AI_ATTACK +{ + string mobguid = 1; + float attack_time = 2; + SM_CHARACTER_KEY targetCharacter = 3; + int32 attack_type = 4; + SM_MOB_AI_VECTOR location = 5; +} + +// Ѿ ߻. +message SM_MOB_AI_SHOOT +{ + string mobguid = 1; + float aim_yaw =2; // ī޶ + float aim_pitch =3; // ī޶ + int64 server_time=4; // Ŷ ð + repeated SM_DATA_BATTLE_BULLET bullets = 5; // ߻Ѿ. ϼִ. +} + +// . +message SM_MOB_AI_RELOAD +{ + string mobguid = 1; +} + +message SM_MOB_AI_FOCUS +{ + string mobguid = 1; + int32 focus = 2; + SM_CHARACTER_KEY targetCharacter = 3; +} + +message SM_MOB_MOVETO +{ + string mobguid = 1; + int32 targetType = 2; + SM_MOB_AI_VECTOR tagetLocation = 3; + float radius = 4; + int32 stop = 5; + float moveSpeed = 6; +} + +message SM_MOB_SPAWN +{ + string mobguid = 1; + int32 mob_type = 2; + SM_MOB_AI_VECTOR location = 3; + float angle = 4; + string anchorguid = 5; +} + +message SM_RUNNING_TRIGGER +{ + string Anchor_GUID = 1; + string User_GUID = 2; + int32 hurdle_type = 3; + int32 is_triggered = 4; + + // Ʈ Ÿ ũ + optional int32 impact_type = 5; + optional SM_MOB_AI_VECTOR impact_force = 6; + optional int32 is_xy_override = 7; + optional int32 is_z_override = 8; + + // Ÿ ũ + optional int32 ragdoll_type = 9; + optional string ragdoll_bone_name = 10; + optional int32 is_partial = 11; + optional int32 is_moveable = 12; +} + +message SM_RUNNING_ATTACH +{ + string Anchor_GUID = 1; + int32 is_triggered = 5; +} + +message SM_HURDLE_TRAVERSAL +{ + string anchor_guid = 1; + int32 use_hurdle = 2; + int32 use_hurdle_mode = 3; + + int32 hurdle_type = 4; + int32 traversal_state = 5; + int32 use_anim_dir = 6; + + int32 montage_action = 7; + optional float loc_x = 8; + optional float loc_y = 9; + optional float loc_z = 10; + optional float rot_pitch = 11; + optional float rot_yaw = 12; + optional float rot_roll = 13; +} + +message SM_RUNNING_ITEM +{ + string Anchor_GUID = 1; + string str_item = 2; +} + +message SM_RUNNING_HURDLE_SPAWN +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + string class_path = 3; + int64 server_time = 4; + + float loc_x = 5; + float loc_y = 6; + float loc_z = 7; + + float rot_pitch = 8; + float rot_yaw = 9; + float rot_roll = 10; + + int32 hurdle_type = 11; + + float power = 12; +} + +message SM_RUNNING_HURDLE_STATE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + int32 state_type = 4; +} + +message SM_RUNNING_HURDLE_MOVE +{ + string Hurdle_GUID = 1; + string Owner_Anchor_GUID = 2; + int64 server_time = 3; + + float loc_x = 4; + float loc_y = 5; + float loc_z = 6; + + float rot_pitch = 7; + float rot_yaw = 8; + float rot_roll = 9; + + float vel_x = 10; + float vel_y = 11; + float vel_z = 12; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_HOST_ROOMINFO +{ + string host_guid = 1; + int64 room_starttime = 2; +} + +// ȣƮ ׽Ʈ ӽ Ŷ +message SM_REQ_HOST_ROOMINFO +{ + +} + +message SM_RUNNING_SCRIPT +{ + string Hurdle_GUID = 1; + int32 State = 2; +} \ No newline at end of file diff --git a/Protocol/client-proto/.svn/pristine/db/dbb908ee11e33594095c5428d39963250e646fe0.svn-base b/Protocol/client-proto/.svn/pristine/db/dbb908ee11e33594095c5428d39963250e646fe0.svn-base new file mode 100644 index 0000000..04d742e --- /dev/null +++ b/Protocol/client-proto/.svn/pristine/db/dbb908ee11e33594095c5428d39963250e646fe0.svn-base @@ -0,0 +1,70 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package protocol; + +option csharp_namespace = "Google.Protobuf.CES.Packet"; +//import "google/protobuf/timestamp.proto"; + + enum Protocol{ + + MOVE_NEXT = 0; + MOVE_COMBINE = 1; + VOICE = 30; + VOICE_STATE = 31; + MUSIC = 40; + MUSICBox = 41; + DANCE = 50; + DANCE_ReqRoomTime = 51; + DANCE_ResRoomTime = 52; + DANCE_StateUpdate = 53; + JUMP = 70; + GUIDE = 80; + PRESENTATION_PAGE = 81; + AIM = 100; + PARTY_MOVE_MEMBER = 110; + + BATTLE_MOVE = 120; // 이동,멈춤,점프... + BATTLE_AIM = 121; // 조준. + BATTLE_STATUS = 122; // 현재상태. + BATTLE_MANTLE = 123; // 기어오르기. + BATTLE_SHOOT = 130; // 총알발사. + BATTLE_DAMAGE = 131; // 피격. + BATTLE_RELOAD = 132; // 재장전 + BATTLE_DODGE = 133; // 구르기 + BATTLE_WEAPON_CHANGE = 134; // 무기교환 + BATTLE_WEAPON_ADD = 135; // 무기추가 + BATTLE_RESPAWN = 136; // 사망후 리스폰. + BATTLE_OBJECT_INTERACTION = 137; // 오브젝트 인터렉션 + BATTLE_FINISHED_LODING = 138; // 인게임 로딩 끝 + BATTLE_READY_TO_EXIT = 139; // 인던 나가기 전에 결과창 보는 중 + BATTLE_LAUNCH_CHARACTER = 140; // 범위 타격에 의해서 날아감 + BATTLE_PLAY_ANIMATION = 141; // 특정 상황에서 에니메이션 실행 할 때 + BATTLE_HIT_EFFECT = 142; // 캐릭터나 사물에 이펙트 표시 + + MOB_AI_ATTACK = 200; + MOB_AI_MOVETO = 201; + MOB_AI_FOCUS = 202; + MOB_AI_SYNCVAR = 203; + MOB_AI_SPAWN = 204; + MOB_AI_SHOOT = 205; + MOB_AI_RELOAD = 206; + + RUNNING_TRIGGER = 300; + RUNNING_ITEM = 301; + RUNNING_HURDLE_SPAWN = 302; + RUNNING_HURDLE_MOVE = 303; + + HOST_ROOMINFO = 401; // 호스트 테스트용 임시 패킷 + REQ_HOST_ROOMINFO = 402; // 호스트 테스트용 임시 패킷 + }; + + +// [END messages] diff --git a/Protocol/client-proto/.svn/wc.db b/Protocol/client-proto/.svn/wc.db index 0889264e35f18e19f6862c686258bdd5a74f5591..e60a74d5cf90c9f87f55c8d480833a5b639e72c0 100644 GIT binary patch delta 3243 zcmbVOU5H%e89ryX+05BFKbxO{7DZQ@)J-Il@Bc?C5h|q!F)LPNb-?|8n=#35NVb$( z@gx_5Hw7i&3N|95ZG#}K3#L~^yzxe&2K6F%C&@)_G&kNzY2WE=awaDG(=sq~n0cS) zd%yR6-shcDA8bAK!Pb}VNH4#9=P%OBf1iKhK-yWka^SAnZm0EdQoUI{R9-1xEcX^4 z7YoHy{$Bp;_IK@9+xJg?HTnGHboOE9vx(MUTMw^noBDC9?sX>m$yheG>*QgBcd^KW z6O0C<4d)a~Vk|a{W9Fz8QEM(Z4jgCGyN-E2z01P0ph8>1HPK8e%Cwdq0|8=MklxNw zb|4~FxWFQl$XQ1?t`R&(L68ek7#FQ}6f^YED!srwP-IXf%4&~@Hj!wA3}wa&6nZ-b z^sUw8B{ftVVtF)ND~kxSLMa`X)Rb!`aC9a(hFV__*<&QUMm(5coby`vNV(LWQz?Xo zPt)5zO7B@j@B?=$2%)qP$Z6)4um%|)Scrsjp)Bss4Cygp4lCy+<=B%LjdYP(3-L|_ z8f0%9u-7-T$0156KfH`r%oqe^Wym@>$wUrU)|yBrW$VKsz2Y{KfQ^NV5*v`To+D^L zB-nAqRc~rEdFhdMIEG*hQ(TZBjNy=cO)zIJXdIkHj*GDG(vY4ixR(%IwI{gp_1(XW;J;tJu@2gz(9+f5v@g3 z2DBSlu#kLloxL8Y9$}=k#g1A?O8k3P(!dFyDG>NRK>ZZMg)s8V`y= zplARF$+ZjAnqGdLe!#ARHh54RLL$b|a_+Q>f@!EcscD2ec<2eU`~NYd7l<(;ji5wG zNSitkf+7Kggn|S?_1dHK-dc@R2p9)CkU0W{0GKMWhwXp~z{(7JF%EbTBUlLy3dImdJf$L;7HeWjqKD zcAyPX3_(!PRv6(xI;fY8vdbro#1bk3d4gso^)Li1YDA*r++(35b%-lJ5*;9a z2ZJJ{3{LuvQSFVA*iyw1GwZg=Iav-4x!PWxg~JX4P6uU8kU z*V-3zRf_zxLKbJsPx39*SjEeJ@uU1m`KRL3>M;1@8||8m_tXE)8#@i;HimawzuVbB zeqdttnl84Z!EDxU!K~#WKeUjk2C`XSs2a%4vtF+~>}7-5>;M-HWZkK3 zhO)MKgW2q;wHwH0$7iyE$kCCQUS}q<0cIfdmDk?wr`=BeY*Kz+T+F|(PF0T=f2rOn z9xcz6Gu4;Hzlxs~&y~;TZ|BdJ|E~6ykL90&x__0|d&lR})xp|elXpf!H`vNMo6y0E zU1!7AW=9jd(Z1Z?gl@F^wl$&a?y(vc*;EsIV=Jw(l&U6_Zm?m>CUl)`QU7;c6iw_# z)}J?_H~aXlHr{SR*Ysq%YRZ#M>_!Hgt(^Pk-0A#kGG4HxVC9+WAV{f~-(4L{4`;hu zMRs!hCrQ$oO8QB0H94OZtt6Wpzh`;wP%3thJ#eYN;1-V^KN^;T@1L02(_e`Fh5pj- z$K>0|#Dg=>6fevz|LssZJARsq<@*n&Ps{^mVe!NGv2JTj?gZdaPqg^ z^ieYL&}@7E?=B=sa&GzB!Su;T#vb_1t*31E@9>yB2Fd?4`}U7};gr)-F2C|f z`qb06&HVr4T+KY29%lZB_fI3eJbNfTaL8x=_!qV_7@&0`JMRlL;-$jQ0u^d`H_)dMjXtkc`PWNreJ5C>l-1OGYx*?i~unfOilGWq6g zHdwHTd$WSTMyAb~>t-{GaPX~U;Jd=#z#q!Lnr|ilHNOA+T0ny*@=dPV!?k(yKL19Z zw+zg@<_x^%e3SWofl`XR+>8aBnjE~G<&DO?)6IPu!}%FMNaf`h>>>>>> .r131478 "b2RlTm90U2V0EP///////////wESFgoRVHJ5Q2F0Y2hFeGNlcHRpb24QkU4S", "FAoPRG90TmV0RXhjZXB0aW9uEJJOEhYKEVByb3VkTmV0RXhjZXB0aW9uEJNO", "EhYKEVJhYmJpdE1xRXhjZXB0aW9uEJROEhYKEUR5bmFtb0RiRXhjZXB0aW9u", "EJVOEh4KGUR5bmFtb0RiVHJhbnNhY3RFeGNlcHRpb24Qlk4SEwoOUmVkaXNF", "eGNlcHRpb24Ql04SFgoRTWV0YUluZm9FeGNlcHRpb24QmE4SFQoQTXlTcWxE", +<<<<<<< .mine "YkV4Y2VwdGlvbhCZThIlCiBOTG9nV2l0aEF3c0Nsb3VkV2F0Y2hTZXR1cEZh", "aWxlZBCvThIUCg9Mb2dBY3Rpb25Jc051bGwQw04SFgoRTG9nQXBwZW5kZXJJ", "c051bGwQxE4SFwoSTG9nRm9ybWF0dGVySXNOdWxsEMVOEhkKFExvZ0FjdGlv", @@ -728,6 +735,1448 @@ public static partial class DefineResultReflection { "b3RGb3VuZFByb2R1Y3RJZBCXLhIdChhSYW5kb21Cb3hJdGVtRGF0YUludmFs", "aWQQ1C8SFgoRTm90RXhpc3RHYWNoYURhdGEQ1S9CLworY29tLmNhbGl2ZXJz", "ZS5hZG1pbi5kb21haW4uUmFiYml0TXEubWVzc2FnZVABYgZwcm90bzM=")); +||||||| .r128445 + "YkV4Y2VwdGlvbhCZThIVChBNb25nb0RiRXhjZXB0aW9uEJpOEiUKIE5sb2dX", + "aXRoQXdzQ2xvdWRXYXRjaFNldHVwRmFpbGVkEK9OEhcKEk5sb2dOb3RJbml0", + "aWFsaXplZBCwThIUCg9Mb2dBY3Rpb25Jc051bGwQw04SFgoRTG9nQXBwZW5k", + "ZXJJc051bGwQxE4SFwoSTG9nRm9ybWF0dGVySXNOdWxsEMVOEhkKFExvZ0Fj", + "dGlvblR5cGVJbnZhbGlkEMZOEhIKDVJtaUhvc3RJc051bGwQ9U4SHQoYUm1p", + "SG9zdEhhbmRsZXJCaW5kRmFpbGVkEPZOEhkKFFN1YkhhbmRsZXJCaW5kRmFp", + "bGVkEPdOEhYKEVByb3h5QXR0YWNoRmFpbGVkEPhOEh4KGVNldE1lc3NhZ2VN", + "YXhMZW5ndGhGYWlsZWQQ+U4SJAofUGFja2V0UmVjdkhhbmRsZXJSZWdpc3Rl", + "ckZhaWxlZBCnTxIkCh9QYWNrZXRTZW5kSGFuZGxlclJlZ2lzdGVyRmFpbGVk", + "EKhPEhYKEVBhY2tldFJlY3ZJbnZhbGlkEKlPEh4KGVJhY2tldFJlY3ZIYW5k", + "bGVyTm90Rm91bmQQqk8SHgoZTGFyZ2VQYWNrZXROb3RBbGxSZWNlaXZlZBCr", + "TxIcChdMYXJnZVBhY2tldFJlY3ZUaW1lT3ZlchCsTxIXChJEYlF1ZXJ5VHlw", + "ZUludmFsaWQQ2U8SKQokRHluYW1vRGJUcmFuc2FjdGlvbkNhbmNlbGVkRXhj", + "ZXB0aW9uEONPEiQKH0R5bmFtb0RiQW1hem9uRHluYW1vRGJFeGNlcHRpb24Q", + "5E8SIwoeRHluYW1vRGJBbWF6b25TZXJ2aWNlRXhjZXB0aW9uEOVPEh0KGER5", + "bmFtb0RiQ29uZmlnTG9hZEZhaWxlZBDmTxIaChVEeW5hbW9EYkNvbm5lY3RG", + "YWlsZWQQ508SHgoZRHluYW1vRGJUYWJsZUNyZWF0ZUZhaWxlZBDoTxIeChlE", + "eW5hbW9EYlRhYmxlTm90Q29ubmVjdGVkEOlPEhgKE0R5bmFtb0RiUXVlcnlG", + "YWlsZWQQ6k8SHQoYRHluYW1vRGJJdGVtU2l6ZUV4Y2VlZGVkEOtPEiIKHUR5", + "bmFtb0RiUXVlcnlOb01hdGNoQXR0cmlidXRlEOxPEikKJER5bmFtb0RiVHJh", + "bnNhY3Rpb25Db25mbGljdEV4Y2VwdGlvbhDtTxIcChdEeW5hbW9EYkV4cHJl", + "c3Npb25FcnJvchDuTxIfChpEeW5hbW9EYlByaW1hcnlLZXlOb3RGb3VuZBDv", + "TxIdChhEeW5hbW9EYlRhYmxlTmFtZUludmFsaWQQ8E8SHAoXRHluYW1vRGJD", + "b25uZWN0b3JJc051bGwQ8U8SIAobRHluYW1vRGJUYWJsZU5hbWVEdXBsaWNh", + "dGVkEPJPEhsKFkR5bmFtb0RiUXVlcnlFeGNlcHRpb24Q908SHQoYRHluYW1v", + "RGJRdWVyeU5vUmVxdWVzdGVkEPhPEicKIkR5bmFtb0RiUXVlcnlOb3RGb3Vu", + "ZERvY3VtZW50UXVlcnkQ+U8SKwomRHluYW1vRGJRdWVyeUV4Y2VwdGlvbk5v", + "dGlmaWVyTm90Rm91bmQQ+k8SKQokRHluYW1vRGJEb2N1bWVudElzTnVsbElu", + "UXVlcnlDb250ZXh0EIFQEh4KGUR5bmFtb0RiRG9jdW1lbnRJc0ludmFsaWQQ", + "glASLAonRHluYW1vRGJEb2N1bWVudFF1ZXJ5Q29udGV4dFR5cGVJbnZhbGlk", + "EINQEiQKH0R5bmFtb0RiRG9jdW1lbnRDb3B5RmFpbGVkVG9Eb2MQhFASIQoc", + "RHluYW1vRGJEb2N1bWVudFVwc2VydEZhaWxlZBCFUBIhChxEeW5hbW9EYkl0", + "ZW1SZXF1ZXN0SXNJbnZhbGlkEItQEi8KKkR5bmFtb0RiSXRlbVJlcXVlc3RR", + "dWVyeUNvbnRleHRUeXBlSW52YWxpZBCMUBItCihEeW5hbW9EYkl0ZW1SZXF1", + "ZXN0VXBkYXRlRXhwcmVzc2lvbkVtcHR5EI1QEhkKFER5bmFtb0RiRG9jUGtJ", + "bnZhbGlkEJVQEhkKFER5bmFtb0RiRG9jU2tJbnZhbGlkEJZQEiQKH0R5bmFt", + "b0RiRG9jQXR0cmliVHlwZUR1cGxpY2F0ZWQQl1ASJAofRHluYW1vRGJEb2ND", + "b3B5RmFpbGVkVG9Eb2N1bWVudBCYUBImCiFEeW5hbW9EYkRvY0NvcHlGYWls", + "ZWRGcm9tRG9jdW1lbnQQmVASHAoXRHluYW1vRGJEb2NUeXBlTm90TWF0Y2gQ", + "mlASGwoWRHluYW1vRGJSZXF1ZXN0SW52YWxpZBCbUBIkCh9EeW5hbW9EYkRv", + "Y0F0dHJpYnV0ZVN0YXRlTm90U2V0EJxQEicKIkR5bmFtb0RiRG9jQXR0cmli", + "V3JhcHBlckNvcHlGYWlsZWQQnVASJgohRHluYW1vRGJEb2NBdHRyaWJ1dGVH", + "ZXR0aW5nRmFpbGVkEJ5QEh8KGkR5bmFtb0RiRG9jTGlua1BrU2tJbnZhbGlk", + "EJ9QEiMKHkR5bmFtb0RiRG9jQXR0cmliV3JhcHBlcklzTnVsbBCgUBIgChtN", + "eVNxbENvbm5lY3Rpb25DcmVhdGVGYWlsZWQQqVASHgoZTXlTcWxDb25uZWN0", + "aW9uT3BlbkZhaWxlZBCqUBIaChVNeVNxbERiUXVlcnlFeGNlcHRpb24Qq1AS", + "IQocTW9uZ29EYkluaXRBbmRWZWZpZnlEYkZhaWxlZBCzUBIdChhSZWRpc1Nl", + "cnZlckNvbm5lY3RGYWlsZWQQvVASHAoXUmVkaXNTdHJpbmdzV3JpdGVGYWls", + "ZWQQvlASGwoWUmVkaXNTdHJpbmdzUmVhZEZhaWxlZBC/UBIZChRSZWRpc1Nl", + "dHNXcml0ZUZhaWxlZBDAUBIYChNSZWRpc1NldHNSZWFkRmFpbGVkEMFQEh8K", + "GlJlZGlzU29ydGVkU2V0c1dyaXRlRmFpbGVkEMJQEh4KGVJlZGlzU29ydGVk", + "U2V0c1JlYWRGYWlsZWQQw1ASGwoWUmVkaXNIYXNoZXNXcml0ZUZhaWxlZBDE", + "UBIaChVSZWRpc0hhc2hlc1JlYWRGYWlsZWQQxVASGgoVUmVkaXNMaXN0c1dy", + "aXRlRmFpbGVkEMZQEhkKFFJlZGlzTGlzdHNSZWFkRmFpbGVkEMdQEhsKFlJl", + "ZGlzUmVxdWVzdEtleUlzRW1wdHkQyFASHQoYUmVkaXNMb2dpbkNhY2hlR2V0", + "RmFpbGVkEMlQEh0KGFJlZGlzTG9naW5DYWNoZVNldEZhaWxlZBDKUBIgChtS", + "ZWRpc1ByaXZhdGVDYWNoZUR1cGxpY2F0ZWQQy1ASJQogUmVkaXNHbG9iYWxT", + "aGFyZWRDYWNoZUR1cGxpY2F0ZWQQzFASKQokUmVkaXNMb2dpbkNhY2hlT3du", + "ZXJVc2VyR3VpZE5vdE1hdGNoEM1QEiMKHlJlZGlzR2xvYmFsUGFydHlDYWNo", + "ZUdldEZhaWxlZBDOUBIlCiBSZWRpc0dsb2JhbFBhcnR5Q2FjaGVXcml0ZUZh", + "aWxlZBDPUBIrCiZSZWRpc0dsb2JhbFBhcnR5TWVtYmVyQ2FjaGVXcml0ZUZh", + "aWxlZBDQUBIrCiZSZWRpc0dsb2JhbFBhcnR5U2VydmVyQ2FjaGVXcml0ZUZh", + "aWxlZBDRUBI0Ci9SZWRpc0dsb2JhbFBhcnR5SW52aXRlUGFydHlTZW5kQ2Fj", + "aGVXcml0ZUZhaWxlZBDSUBIoCiNSZWRpc0luc3RhbmNlUm9vbUluZm9DYWNo", + "ZUdldEZhaWxlZBDTUBIpCiRSZWRpc1VnY05wY1RvdGFsUmFua0NhY2hlV3Jp", + "dGVGYWlsZWQQ1FASIAobUmVkaXNSZXF1ZXN0SGFuZGxlck5vdEZvdW5kENVQ", + "EiAKG1JhYmJpdE1xQ29uc3VtZXJTdGFydEZhaWxlZBChURIaChVSYWJiaXRN", + "cUNvbm5lY3RGYWlsZWQQolESGQoUUmFiYml0TWVzc2FnZVRpbWVPbGQQo1ES", + "IAobUmFiYml0TXFDaGFubmVsQ3JlYXRlRmFpbGVkEKRREhkKFFMzQ2xpZW50", + "Q3JlYXRlRmFpbGVkEIVSEhkKFFMzQnVja2V0Q3JlYXRlRmFpbGVkEIZSEhcK", + "ElMzRmlsZVVwbG9hZEZhaWxlZBCHUhIXChJTM0ZpbGVEZWxldGVGYWlsZWQQ", + "iFISFAoPUzNGaWxlR2V0RmFpbGVkEIlSEhcKEk1ldGFEYXRhTG9hZEZhaWxl", + "ZBC3UhIUCg9JbnZhbGlkTWV0YURhdGEQuFISEgoNTWV0YUlkSW52YWxpZBC5", + "UhIUCg9Kc29uVHlwZUludmFsaWQQ81ISIQocSnNvbkNvbnZlcnREZXNlcmlh", + "bGl6ZUZhaWxlZBD0UhIdChhTZXJ2ZXJDb25maWdGaWxlTm90Rm91bmQQzVMS", + "FgoRU2VydmVyVHlwZUludmFsaWQQzlMSJwoiQWxyZWFkeVJ1bm5pbmdTZXJ2", + "ZXJXaXRoTGlzdGVuUG9ydBDPUxIZChROb3RGb3VuZENhY2hlU3RvcmFnZRDQ", + "UxIWChFGdW5jdGlvblBhcmFtTnVsbBDRUxIZChRGdW5jdGlvbkludmFsaWRQ", + "YXJhbRDSUxIcChdDbGllbnRMaXN0ZW5Qb3J0SW52YWxpZBDTUxIZChROb3RP", + "dmVycmlkZUludGVyZmFjZRDUUxIaChVTZXJ2ZXJPblJ1bm5pbmdGYWlsZWQQ", + "1VMSFwoSU2VydmljZVR5cGVJbnZhbGlkENZTEhsKFkZ1bmN0aW9uTm90SW1w", + "bGVtZW50ZWQQ11MSLgopQ2xhc3NEb2VzTm90SW1wbGVtZW50SW50ZXJmYWNl", + "SW5oZXJpdGFuY2UQ2FMSFwoSUnVsZVR5cGVEdXBsaWNhdGVkENlTEhgKE0Ns", + "YXNzVHlwZUNhc3RJc051bGwQ2lMSIgodUGVyaW9kaWNUYXNrQWxyZWFkeVJl", + "Z2lzdGVyZWQQ21MSIgodRW50aXR5VGlja2VyQWxyZWFkeVJlZ2lzdGVyZWQQ", + "3FMSGQoURW50aXR5VGlja2VyTm90Rm91bmQQ3VMSFwoSRW50aXR5QmFzZU5v", + "dEZvdW5kEN5TEhgKE1ZhbGlkU2VydmVyTm90Rm91bmQQ31MSIAobVGFyZ2V0", + "U2VydmVyVXNlckNvdW50RXhjZWVkEOBTEhcKElRhcmdldFVzZXJOb3RGb3Vu", + "ZBDhUxIXChJUYXJnZXRVc2VyTm90TG9nSW4Q4lMSEAoLTm90RXhpc3RNYXAQ", + "41MSIgodRmFpbGVkVG9SZXNlcnZlRW50ZXJDb25kaXRpb24Q5FMSHQoYRmFp", + "bGVkVG9SZXNlcnZhdGlvbkVudGVyEOVTEhsKFk93bmVyRW50aXR5VHlwZUlu", + "dmFsaWQQ5lMSHAoXT3duZXJFbnRpdHlDYW5ub3RGaWxsdXAQ51MSFQoQT3du", + "ZXJHdWlkSW52YWxpZBDoUxIhChxEYWlseVRpbWVFdmVudEFkZGl0aW9uRmFp", + "bGVkEOlTEiQKH1Byb2dyYW1WZXJzaW9uUGF0aFRva2VuTm90Rm91bmQQ6lMS", + "HQoYQ3VycmVudGx5UHJvY2Vzc2luZ1N0YXRlEOtTEhkKFFNlcnZlclVybFR5", + "cGVJbnZhbGlkEOxTEiMKHlNlcnZlclVybFR5cGVBbHJlYWR5UmVnaXN0ZXJl", + "ZBDtUxIcChdTZXJ2ZXJPZmZsaW5lTW9kZUVuYWJsZRDuUxIRCgxPd25lcklu", + "dmFsaWQQ71MSGwoWTW9kdWxlQWxyZWFkeVJlZ2lzdHJlZBDwUxIlCiBNb2R1", + "bGVBbHJlYWR5QWRkSW5pdGlhbGl6ZU1vZHVsZRDxUxITCg5Nb2R1bGVOb3RG", + "b3VuZBDyUxIdChhQcm9ncmFtVmVyc2lvbkxvYWRGYWlsZWQQ81MSGQoUU2Vy", + "dmVyQWxyZWFkeVJ1bm5pbmcQ9FMSJAofTWV0YURhdGFDb3B5VG9EeW5hbW9E", + "YkRvY0ZhaWxlZBDiVBIoCiNNZXRhRGF0YUNvcHlUb0VudGl0eUF0dHJpYnV0", + "ZUZhaWxlZBDjVBIhChxEeW5hbW9EYkRvY0NvcHlUb0NhY2hlRmFpbGVkEORU", + "EisKJkR5bmFtb0RiRG9jQ29weVRvRW50aXR5QXR0cmlidXRlRmFpbGVkEOVU", + "EiUKIENhY2hlQ29weVRvRW50aXR5QXR0cmlidXRlRmFpbGVkEOZUEiEKHENh", + "Y2hlQ29weVRvRHluYW1vRGJEb2NGYWlsZWQQ51QSJQogRW50aXR5QXR0cmli", + "dXRlQ29weVRvQ2FjaGVGYWlsZWQQ6FQSKwomRW50aXR5QXR0cmlidXRlQ29w", + "eVRvRHluYW1vRGJEb2NGYWlsZWQQ6VQSOQo0RW50aXR5QXR0cmlidXRlQ29w", + "eVRvRW50aXR5QXR0cmlidXRlVHJhbnNhY3RvckZhaWxlZBDqVBI5CjRFbnRp", + "dHlBdHRyaWJ1dGVUcmFuc2FjdG9yQ29weVRvRW50aXR5QXR0cmlidXRlRmFp", + "bGVkEOtUEjUKMEVudGl0eUF0dHJpYnV0ZVRyYW5zYWN0b3JDb3B5VG9EeW5h", + "bW9EYkRvY0ZhaWxlZBDsVBITCg5BdHRyaWJOb3RGb3VuZBDtVBIZChRBdHRy", + "aWJQYXRoTWFrZUZhaWxlZBDuVBIeChlFbnRpdHlBdHRyaWJ1dGVDYXN0RmFp", + "bGVkEO9UEh4KGVN0cmluZ0NvbnZlcnRUb0VudW1GYWlsZWQQ8FQSHgoZTWV0", + "YVNjaGVtYVZlcnNpb25Ob3RNYXRjaBCVVRIcChdNZXRhRGF0YVZlcnNpb25O", + "b3RNYXRjaBCWVRIaChVQYWNrZXRWZXJzaW9uTm90TWF0Y2gQl1USHwoaQ2xp", + "ZW50TG9naWNWZXJzaW9uTm90TWF0Y2gQmFUSHAoXUmVzb3VyY2VWZXJzaW9u", + "Tm90TWF0Y2gQmVUSHwoaQ2xpZW50UHJvZ3JhbVZlcnNpb25Jc051bGwQmlUS", + "EwoOVGVzdElkTm90QWxsb3cQ+VUSEQoMQm90ZE5vdEFsbG93EPpVEhkKFEFj", + "Y291bnRJZExlbmd0aFNob3J0EPtVEiQKH0FjY291bnRJZE5vdEZvdW5kSW5T", + "c29BY2NvdW50RGIQ/FUSIQocTWV0YURhdGFOb3RGb3VuZEJ5VGVzdFVzZXJJ", + "ZBD9VRIcChdBY2NvdW50UGFzc3dvcmROb3RNYXRjaBD+VRInCiJVc2VyRGF0", + "YUNvbnZlcnRUb0FjY291bnRBdHRyRmFpbGVkEP9VEiQKH0FjY291bnRCYXNl", + "QXR0cmliSW5zZXJ0RGJGYWlsZWQQgFYSGAoTTm9TZXJ2ZXJDb25uZWN0YWJs", + "ZRCBVhITCg5CbG9ja2VkQWNjb3VudBCCVhIsCidTc29BY2NvdW50QXV0aFdp", + "dGhMYXVuY2hlckxvZ2luTm90QWxsb3cQg1YSIgodQ2xpZW50U3RhbmRhbG9u", + "ZUxvZ2luTm90QWxsb3cQhFYSGQoUUGxhdGZvcm1UeXBlTm90QWxsb3cQhVYS", + "JgohQWNjb3VudENhbk5vdFJlYWRGcm9tU3NvQWNjb3VudERiEIZWEiEKHFNz", + "b0FjY291bnRBdXRoSnd0Q2hlY2tGYWlsZWQQh1YSKQokVXNlcklkS2V5Tm90", + "Rm91bmRJblNzb0FjY291bnRBdXRoSnd0EIhWEigKI1VzZXJJZFZhbHVlRW1w", + "dHlJblNzb0FjY291bnRBdXRoSnd0EIlWEi4KKUFjY291bnRUeXBlS2V5Tm90", + "Rm91bmRJblNzb0FjY291bnRBdXRoSnd0EIpWEjAKK0FjY291bnRUeXBlVmFs", + "dWVOb3RBbGxvd0luU3NvQWNjb3VudEF1dGhKd3QQi1YSKAojQWNjb3VudEJh", + "c2VEb2NOb3RGb3VuZEluTWV0YXZlcnNlRGIQjFYSLgopQWNjZXNzVG9rZW5L", + "ZXlOb3RBbGxvd0luU3NvQWNjb3VudEF1dGhKd3QQjVYSJgohQWNjZXNzVG9r", + "ZW5Ob3RNYXRjaEluU3NvQWNjb3VudERiEI5WEhgKE0FjY291bnRUeXBlTm90", + "QWxsb3cQj1YSGgoVQWNjb3VudEJhc2VEb2NOb3RMb2FkEJBWEhcKEk5vdEVu", + "b3VnaEF1dGhvcml0eRCuVhIZChRBY2NvdW50QmFzZURvY0lzTnVsbBCvVhIV", + "ChBBY2NvdW50SWRJbnZhbGlkELBWEhsKFkFjY291bnRXaXRob3V0VXNlckd1", + "aWQQsVYSIgodU3NvQWNjb3VudEF1dGhKd3RUb2tlbkV4cGlyZWQQslYSHwoa", + "U3NvQWNjb3VudEF1dGhKd3RFeGNlcHRpb24Qs1YSJAofVHJhbnNhY3Rpb25S", + "dW5uZXJBbHJlYWR5UnVubmluZxDBVxIeChlUcmFuc2FjdGlvblJ1bm5lck5v", + "dEZvdW5kEMJXEhYKEUVudGl0eUd1aWRJbnZhbGlkEKVYEhsKFkVudGl0eUF0", + "dHJpYkR1cGxpY2F0ZWQQplgSGgoVRW50aXR5QXR0cmlidXRlSXNOdWxsEKdY", + "EhwKF0VudGl0eUF0dHJpYnV0ZU5vdEZvdW5kEKhYEiAKG0VudGl0eUF0dHJp", + "YnV0ZVN0YXRlSW52YWxpZBCpWBIWChFFbnRpdHlUeXBlSW52YWxpZBCqWBIW", + "ChFFbnRpdHlMaW5rZWRUb01hcBCrWBIZChRFbnRpdHlOb3RMaW5rZWRUb01h", + "cBCsWBIaChVFbnRpdHlTdGF0ZU5vdERhbmNpbmcQrVgSGwoWRW50aXR5QWN0", + "aW9uRHVwbGljYXRlZBCJWRIZChRFbnRpdHlBY3Rpb25Ob3RGb3VuZBCKWRId", + "ChhFbnRpdHlCYXNlSGZzbUluaXRGYWlsZWQQ7VkSIAobUmVkaXNHbG9iYWxF", + "bnRpdHlEdXBsaWNhdGVkENFaEhoKFVVzZXJJc1N3aXRjaGluZ1NlcnZlchC1", + "WxIdChhVc2VySXNOb3RTd2l0Y2hpbmdTZXJ2ZXIQtlsSHwoaU2VydmVyU3dp", + "dGNoaW5nT3RwTm90TWF0Y2gQt1sSIwoeQ29ubmVjdGVkU2VydmVySXNOb3RE", + "ZXN0U2VydmVyELhbEhsKFkludmFsaWRSZXNlcnZhdGlvblVzZXIQuVsSFgoR", + "SW52YWxpZFJldHVyblVzZXIQulsSKQokVXNlck5pY2tuYW1lTm90QWxsb3dX", + "aXRoU3BlY2lhbENoYXJzEOFdEiwKJ1VzZXJOaWNrbmFtZUFsbG93ZWRNaW4y", + "VG9NYXg4V2l0aEtvcmVhbhDiXRIuCilVc2VyTmlja25hbWVBbGxvd2VkTWlu", + "NFRvTWF4MTZXaXRoRW5nbGlzaBDjXRItCihVc2VyTmlja25hbWVOb3RBbGxv", + "d2VkTnVtYmVyQXRGaXJzdENoYXJzEORdEh4KGVVzZXJOaWNrbmFtZU5vdEFs", + "bG93Q2hhcnMQ5V0SLQooVXNlck5pY2tuYW1lTm90QWxsb3dXaXRoSW5pdGlh", + "bGlzbUtvcmVhbhDmXRIUCg9Vc2VyTmlja25hbWVCYW4Q510SGAoTVXNlckR1", + "cGxpY2F0ZWRMb2dpbhDoXRIRCgxVc2VyTm90TG9naW4Q6V0SKQokVXNlckNy", + "ZWF0aW9uRm9yRHluYW1vRGJEb2NEdXBsaWNhdGVkEOpdEikKJFVzZXJHdWlk", + "QXBwbHlUb1JlZkF0dHJpYnV0ZUFsbEZhaWxlZBDrXRImCiFUZXN0VXNlclBy", + "ZXBhcmVDcmVhdGVOb3RDb21wbGV0ZWQQ7F0SKQokRGVmYXVsdFVzZXJQcmVw", + "YXJlQ3JlYXRlTm90Q29tcGxldGVkEO1dEiAKG1VzZXJQcmVwYXJlTG9hZE5v", + "dENvbXBsZXRlZBDuXRIbChZVc2VyTmlja25hbWVOb3RDcmVhdGVkEO9dEh8K", + "GlVzZXJOaWNrbmFtZUFscmVhZHlDcmVhdGVkEPBdEh8KGlVzZXJDcmVhdGVT", + "dGVwTm90Q29tcGxldGVkEPFdEhgKE1VzZXJDcmVhdGVDb21wbGV0ZWQQ8l0S", + "GwoWVXNlclN1YktleUJpbmRUb0ZhaWxlZBDzXRIcChdVc2VyU3ViS2V5UmVw", + "bGFjZUZhaWxlZBD0XRIUCg9Vc2VyR3VpZEludmFsaWQQ9V0SGgoVVXNlck5p", + "Y2tuYW1lRG9jSXNOdWxsEPZdEhIKDVVzZXJEb2NJc051bGwQ910SGQoUVXNl", + "ckd1aWRBbHJlYWR5QWRkZWQQ+F0SGwoWVXNlck5pY2tuYW1lRHVwbGljYXRl", + "ZBD5XRIjCh5Vc2VyTmlja25hbWVBbGxvd2VkTWluMlRvTWF4MTIQ+l0SIAob", + "VXNlck5pY2tuYW1lU2VhcmNoUGFnZVdyb25nEPtdEhYKEVVzZXJOaWNrbmFt", + "ZUVtcHR5EPxdEiEKHFVzZXJDb250ZW50c1NldHRpbmdEb2NJc051bGwQ/V0S", + "FgoRVXNlck1vbmV5RG9jRW1wdHkQ/l0SIQocVXNlclJlcG9ydEludmFsaWRU", + "aXRsZUxlbmd0aBDFXhIjCh5Vc2VyUmVwb3J0SW52YWxpZENvbnRlbnRMZW5n", + "dGgQxl4SGwoWVXNlclJlcG9ydERvY0V4Y2VwdGlvbhDHXhIrCiZUZXN0Q2hh", + "cmFjdGVyUHJlcGFyZUNyZWF0ZU5vdENvbXBsZXRlZBDJZRIuCilEZWZhdWx0", + "Q2hhcmFjdGVyUHJlcGFyZUNyZWF0ZU5vdENvbXBsZXRlZBDUZRIlCiBDaGFy", + "YWN0ZXJQcmVwYXJlTG9hZE5vdENvbXBsZXRlZBDVZRIsCidDaGFyYWN0ZXJC", + "YXNlRG9jTG9hZER1cGxpY2F0ZWRDaGFyYWN0ZXIQ1mUSGQoUQ2hhcmFjdGVy", + "Tm90U2VsZWN0ZWQQ12USFgoRQ2hhcmFjdGVyTm90Rm91bmQQ2GUSGwoWQ2hh", + "cmFjdGVyQmFzZURvY05vUmVhZBDZZRIlCiBDaGFyYWN0ZXJDdXN0b21pemlu", + "Z05vdENvbXBsZXRlZBDaZRIkCh9DaGFyYWN0ZXJDcmVhdGVTdGVwTm90Q29t", + "cGxldGVkENtlEikKJENoYXJhY3RlckN1c3RvbWl6aW5nQWxyZWFkeUNvbXBs", + "ZXRlZBDcZRIfChpDaGFyYWN0ZXJQcmVwYXJlTm90Q3JlYXRlZBDdZRIdChhD", + "aGFyYWN0ZXJDcmVhdGVDb21wbGV0ZWQQ3mUSGwoWQ2hhcmFjdGVyQmFzZURv", + "Y0lzTnVsbBDfZRIVChBBYmlsaXR5Tm90RW5vdWdoEPVnEhkKFEl0ZW1NZXRh", + "RGF0YU5vdEZvdW5kELFtEhQKD0l0ZW1HdWlkSW52YWxpZBCybRISCg1JdGVt", + "RG9jSXNOdWxsELNtEicKIkl0ZW1EZWZhdWx0QXR0cmlidXRlTm90Rm91bmRJ", + "bk1ldGEQtG0SIwoeSXRlbUxldmVsRW5jaGFudE5vdEZvdW5kSW5NZXRhELVt", + "Eh4KGUl0ZW1FbmNoYW50Tm90Rm91bmRJbk1ldGEQtm0SKwomSXRlbUF0dHJp", + "YnV0ZVJhbmRvbUdyb3VwTm90Rm91bmRJbk1ldGEQt20SLwoqSXRlbUF0dHJp", + "YnV0ZVJhbmRvbUdyb3VwVG90YWxXZWlnaHRJbnZhbGlkELhtEhEKDEl0ZW1O", + "b3RGb3VuZBC5bRIeChlJdGVtQ2xvdGhJbnZhbGlkTGFyZ2VUeXBlELptEh4K", + "GUl0ZW1DbG90aEludmFsaWRTbWFsbFR5cGUQu20SGgoVSXRlbVN0YWNrQ291", + "bnRJbnZhbGlkELxtEhcKEkl0ZW1NYXhDb3VudEV4Y2VlZBC9bRIeChlJdGVt", + "RG9jTG9hZER1cGxpY2F0ZWRJdGVtEL5tEhkKFENsb3RoU2xvdFR5cGVJbnZh", + "bGlkEL9tEhwKF0l0ZW1TdGFja0NvdW50Tm90RW5vdWdoEMBtEhcKEkl0ZW1D", + "b3VudE5vdEVub3VnaBDBbRIYChNJdGVtSW52YWxpZEl0ZW1UeXBlEMJtEh0K", + "GEl0ZW1Ub29sTWV0YURhdGFOb3RGb3VuZBDDbRIVChBJdGVtVG9vbE5vdEZv", + "dW5kEMRtEh0KGEl0ZW1Ub29sTm90QWN0aXZhdGVTdGF0ZRDFbRIYChNUb29s", + "QWN0aW9uRG9jSXNOdWxsEMZtEiUKIFRvb2xBY3Rpb25BbHJlYWR5VW5hY3Rp", + "dmF0ZVN0YXRlEMdtEiMKHlRvb2xBY3Rpb25BbHJlYWR5QWN0aXZhdGVTdGF0", + "ZRDIbRIXChJJdGVtVGF0dG9vTm90Rm91bmQQyW0SJQogSXRlbUF0dHJpYnV0", + "ZUVuY2hhbnRNZXRhTm90Rm91bmQQym0SIwoeSXRlbUF0dHJpYnV0ZUNoYW5n", + "ZU5vdFNlbGVjdGVkEMttEiQKH0l0ZW1QYXJzaW5nRnJvbVN0cmluZ1RvSW50", + "RXJvcnIQzG0SKQokSXRlbVZhbHVlUGFyc2luZ0Zyb21TdHJpbmdUb0ludEVy", + "b3JyEM1tEiYKIUl0ZW1GaXJzdFB1cmNoYXNlSGlzdG9yeURvY0lzTnVsbBDO", + "bRIyCi1JdGVtRmlyc3RQdXJjaGFzZUhpc3RvcnlEb2NMb2FkRHVwbGljYXRl", + "ZEl0ZW0Qz20SKQokSXRlbUZpcnN0UHVyY2hhc2VIaXN0b3J5QWxyZWFkeUV4", + "aXN0ENBtEiwKJ0l0ZW1GaXJzdFB1cmNoYXNlRGlzY291bnRJdGVtQ291bnRX", + "cm9uZxDRbRIfChpJdGVtQXR0cmlidXRlSWRUeXBlSW52YWxpZBDSbRIUCg9J", + "dGVtQWxsb2NGYWlsZWQQ020SFwoSSXRlbUd1aWREdXBsaWNhdGVkENRtEhgK", + "E0l0ZW1MZXZlbEN1cnJlbnRNYXgQ1W0SFwoSSXRlbUNhbk5vdEJlU3RvcmVk", + "ENZtEhwKF0l0ZW1Vc2VGdW5jdGlvbk5vdEZvdW5kEJVuEh0KGEl0ZW1Vc2VR", + "dWVzdE1haWxDb3VudE1heBCWbhIjCh5JdGVtVXNlTm90RXhpc3RBc3NpZ25h", + "YmxlUXVlc3QQl24SGwoWSXRlbVVzZUFscmVhZHlIYXNRdWVzdBCYbhIfChpJ", + "dGVtVXNlQWxyZWFkeUhhc1F1ZXN0TWFpbBCZbhIjCh5CYWdSdWxlSXRlbUxh", + "cmdlVHlwZUR1cGxpY2F0ZWQQmXUSKQokVG9vbEVxdWlwUnVsZUl0ZW1MYXJn", + "ZVR5cGVEdXBsaWNhdGVkEJp1EioKJUNsb3RoRXF1aXBSdWxlSXRlbUxhcmdl", + "VHlwZUR1cGxpY2F0ZWQQm3USKwomVGF0dG9vRXF1aXBSdWxlSXRlbUxhcmdl", + "VHlwZUR1cGxpY2F0ZWQQnHUSGgoVSW52ZW50b3J5UnVsZU5vdEZvdW5kEJ11", + "EhcKEkVxdWlwSW52ZW5Ob3RGb3VuZBCedRIYChNTbG90c0FscmVhZHlFcXVp", + "cGVkEJ91EhoKFVNsb3RzQWxyZWFkeVVuZXF1aXBlZBCgdRISCg1CYWdJc0l0", + "ZW1GdWxsEKF1EhMKDkJhZ0lzSXRlbUVtcHR5EKJ1EhoKFUJhZ0RlbHRhSXRl", + "bUR1cGxpYXRlZBCjdRIUCg9CYWdJdGVtTm90Rm91bmQQpHUSKAojQ2xvdGhF", + "cXVpcFJ1bGVDbG90aFNsb3RUeXBlTm90Rm91bmQQpXUSJgohVG9vbEVxdWlw", + "UnVsZVRvb2xTbG90VHlwZU5vdEZvdW5kEKZ1EioKJVRhdHRvb0VxdWlwUnVs", + "ZVRhdHRvb1Nsb3RUeXBlTm90Rm91bmQQp3USGAoTQmFnVGFiVHlwZUFkZEZh", + "aWxlZBCodRIWChFCYWdUYWJUeXBlSW52YWxpZBCpdRIXChJCYWdUYWJUeXBl", + "Tm90Rm91bmQQqnUSGwoWQmFnVGFiQ291bnRNZXJnZUZhaWxlZBCrdRIfChpJ", + "bnZlbnRvcnlFbnRpdHlUeXBlSW52YWxpZBCsdRIaChVJbnZlbkVxdWlwVHlw", + "ZUludmFsaWQQrXUSGgoVQmFnSXNSZXNlcnZlZEl0ZW1GdWxsEK51EhsKFkJh", + "Z0lzUmVzZXJ2ZWRJdGVtRW1wdHkQr3USFgoRRXF1aXBTbG90Tm90TWF0Y2gQ", + "sHUSGAoTRXF1aXBTbG90T3V0T2ZSYW5nZRCxdRIeChlTbG90c0FscmVhZHlS", + "ZXNlcnZlZEVxdWlwELJ1EiAKG1Nsb3RzQWxyZWFkeVJlc2VydmVkVW5lcXVp", + "cBCzdRIVChBTbG90VHlwZU5vdEZvdW5kELR1Eh8KGlVnY05wY01ldGFHdWlk", + "QWxyZWFkeUFkZGVkEP11EhQKD1VnY05wY0RvY0lzTnVsbBD+dRIZChRVZ2NO", + "cGNNYXhDb3VudEV4Y2VlZBD/dRIdChhVZ2NOcGNDbG90aEl0ZW1Ob3RFbm91", + "Z2gQgHYSIgodVWdjTnBjRG9jTG9hZER1cGxpY2F0ZWRVZ2NOcGMQgXYSIgod", + "VWdjTnBjRGVzY3JpcHRpb25MZW5ndGhFeGNlZWQQgnYSIwoeVWdjTnBjV29y", + "ZFNjZW5hcmlvTGVuZ3RoRXhjZWVkEIN2Eh8KGlVnY05wY0dyZWV0aW5nTGVu", + "Z3RoRXhjZWVkEIR2Eh4KGVVnY05wY1RhdHRvb0l0ZW1Ob3RFbm91Z2gQhXYS", + "JwoiVWdjTnBjSGFiaXRTb2NpYWxBY3Rpb25Db3VudEV4Y2VlZBCGdhIqCiVV", + "Z2NOcGNEaWFsb2d1ZVNvY2lhbEFjdGlvbkNvdW50RXhjZWVkEId2Eh0KGFVn", + "Y05wY05pY2tuYW1lRHVwbGljYXRlZBCIdhITCg5VZ2NOcGNOb3RGb3VuZBCJ", + "dhIjCh5VZ2NOcGNJbnRyb2R1Y3Rpb25MZW5ndGhFeGNlZWQQinYSFwoSVWdj", + "TnBjTWF4VGFnRXhjZWVkEIt2EhgKE1VnY05wY05pY2tuYW1lRW1wdHkQjHYS", + "JgohVWdjTnBjQWxyZWFkeVJlZ2lzdGVyZWRJbkdhbWVab25lEI12EiIKHVVn", + "Y05wY05vdFJlZ2lzdGVyZWRJbkdhbWVab25lEI52EiQKH1VnY05wY0xpa2VT", + "ZWxlY3RlZUNvdW50Tm90Rm91bmQQj3YSIwoeVWdjTnBjTGlrZVNlbGVjdGVk", + "RmxhZ05vdEZvdW5kEJB2Eh8KGlVnY05wY0R1cGxpY2F0ZUluTXlob21lVWdj", + "EJF2EhcKElVnY05wY0xvY2F0ZWRTdGF0ZRCSdhIbChZVZ2NOcGNNZXRhRGF0", + "YU5vdEZvdW5kEJN2EiMKHkJlYWNvbkFwcFByb2ZpbGVVcGxvYWRDb29sVGlt", + "ZRCUdhIaChVCZWFjb25Cb2R5SXRlbUludmFsaWQQlXYSHAoXVWdjTnBjSGFz", + "QmVhY29uU2hvcEl0ZW0QlnYSHwoaVWdjTnBjUmFua0VudGl0eUlzTm90Rm91", + "bmQQ4XYSGQoUVWdjTnBjUmFua091dE9mUmFuZ2UQ4nYSIwoeRmFybWluZ0Vm", + "ZmVjdERvY0xpbmtQa1NrTm90U2V0EMV3Ei0KKEZhcm1pbmdFZmZlY3RBbHJl", + "YWR5UmVnaXN0ZXJlZEluR2FtZVpvbmUQxncSEwoORmFybWluZ0FscmVhZHkQ", + "x3cSEAoLRmFybWluZ0J5TWUQyHcSIAobRmFybWluZ1Byb3BNZXRhRGF0YU5v", + "dEZvdW5kEMl3EhsKFkZhcm1pbmdUcnlDb3VudEludmFsaWQQyncSFAoPRmFy", + "bWluZ05vdFN0YXRlEMt3EhkKFEZhcm1pbmdPd25lck5vdE1hdGNoEMx3EiUK", + "IEZhcm1pbmdTdW1tb25lZEVudGl0eVR5cGVJbnZhbGlkEM13EiQKH0Zhcm1p", + "bmdFZmZlY3ROb3RFeGlzdEluR2FtZVpvbmUQzncSEAoLRmFyaW1nU3RhdGUQ", + "z3cSGwoWRmFybWluZ1N0YW5kQnlOb3RTdGF0ZRDQdxIaChVGYXJtaW5nQW5j", + "aG9yTm90Rm91bmQQ0XcSJgohRmFybWluZ0JlYWNvbkRiSW5mb0ludGVncml0", + "eUVycm9yENJ3EhoKFUdhY2hhTWV0YURhdGFOb3RGb3VuZBCpeBIVChBHYWNo", + "YVJld2FyZEVtcHR5EKp4EhMKDk1hc3Rlck5vdEZvdW5kELl7EhUKEE1hc3Rl", + "ck5vdFJlbGF0ZWQQunsSFAoPR2FtZVpvbmVOb3RKb2luEJ18Eg4KCU1hcElz", + "TnVsbBCefBIcChdMb2NhdGlvblVuaXF1ZUlkSW52YWxpZBCffBITCg5Qcm9w", + "SXNPY2N1cGllZBCgfBIWChFQcm9wSXNOb3RPY2N1cGllZBChfBIUCg9Gcmll", + "bmREb2NJc051bGwQgX0SGgoVRnJpZW5kRm9sZGVyRG9jSXNOdWxsEIJ9EiIK", + "HFNvY2lhbEFjdGlvbk1ldGFEYXRhTm90Rm91bmQQ6YQBEhoKFFNvY2lhbEFj", + "dGlvbk5vdEZvdW5kEOqEARIvCilTb2NpYWxBY3Rpb25Eb2NMb2FkRHVwbGlj", + "YXRlZFNvY2lhbEFjdGlvbhDrhAESHgoYU29jaWFsQWN0aW9uQWxyZWFkeUV4", + "aXN0EOyEARIgChpTb2NpYWxBY3Rpb25TbG90T3V0T2ZSYW5nZRDthAESGwoV", + "U29jaWFsQWN0aW9uTm90T25TbG90EO6EARIbChVTb2NpYWxBY3Rpb25Eb2NJ", + "c051bGwQ74QBEhwKFkNoYW5uZWxNb3ZlU2FtZUNoYW5uZWwQ0YwBEiAKGkNo", + "YW5uZWxJbnZhbGlkTW92ZUNvb2xUaW1lENKMARIWChBOb3RDaGFubmVsU2Vy", + "dmVyENOMARIYChJPd25lZFJvb21Eb2NJc051bGwQuZQBEhMKDVJvb21Eb2NJ", + "c051bGwQupQBEhUKD1Jvb21Jc05vdE15SG9tZRC7lAESGgoUTWFwUmFuZ2VP", + "dXRPZkNlbGxQb3MQvJQBEhwKFk1hcEdyaWRCb3VuZE91dE9mUmFuZ2UQvZQB", + "EhkKE01hcEdyaWROb3RGb3VuZEdyaWQQvpQBEhkKE01hcEdyaWROb3RGb3Vu", + "ZENlbGwQv5QBEh8KGU1hcEdyaWRDZWxsTm90Rm91bmRQbGF5ZXIQwJQBEh8K", + "GU1hcEdyaWRDZWxsTm90Rm91bmRVZ2NOcGMQwZQBEhIKDE1haWxOb3RGb3Vu", + "ZBChnAESFgoQTWFpbEFscmVhZHlUYWtlbhCinAESGQoTTWFpbEludmFsaWRN", + "YWlsVHlwZRCjnAESHAoWTWFpbE1heFNlbmRDb3VudEV4Y2VlZBCknAESEwoN", + "TWFpbERvY0lzTnVsbBClnAESHQoXTWFpbEJsb2NrVXNlckNhbm5vdFNlbmQQ", + "ppwBEiYKIE1haWxNYXhUYXJnZXRSZWNlaXZlZENvdW50RXhjZWVkEKecARIW", + "ChBNYWlsQ2FudFNlbmRTZWxmEKicARIgChpNYWlsQ2FudERlbGV0ZUlmSXRl", + "bUV4aXN0cxCpnAESFgoQTWFpbERvY0V4Y2VwdGlvbhCqnAESGgoUTWFpbFBy", + "b2ZpbGVEb2NJc051bGwQzZ4BEh0KF01haWxQcm9maWxlRG9jRXhjZXB0aW9u", + "EM6eARIZChNTeXN0ZW1NYWlsRG9jSXNOdWxsEJWgARIYChJQYXJ0eUNhbm5v", + "dFNldEd1aWQQiaQBEiAKGlBhcnR5RmFpbGVkTWFrZVBhcnR5TWVtYmVyEIqk", + "ARIRCgtQYXJ0eUlzRnVsbBCLpAESGAoSQWxyZWFkeUludml0ZVBhcnR5EIyk", + "ARIZChNOb3RGb3VuZFBhcnR5SW52aXRlEI2kARITCg1Ob3RGb3VuZFBhcnR5", + "EI6kARIOCghOb3RQYXJ0eRCPpAESFAoOTm90UGFydHlMZWFkZXIQkKQBEhIK", + "DEpvaW5pbmdQYXJ0eRCRpAESFAoOTm90UGFydHlNZW1iZXIQkqQBEhMKDUFs", + "cmVhZHlTdW1tb24Qk6QBEh0KF1BhcnR5TGVhZGVyU2VydmVySXNGdWxsEJSk", + "ARIbChVJbnZpdGVNZW1iZXJJc0NvbmNlcnQQlaQBEhwKFkZhaWxUb1NlbmRJ", + "bnZpdGVNZW1iZXIQlqQBEhgKEkFscmVhZHlQYXJ0eU1lbWJlchCXpAESHQoX", + "SW52YWxpZFN1bW1vblNlcnZlclR5cGUQmKQBEh0KF1N1bW1vblVzZXJMaW1p", + "dERpc3RhbmNlEJmkARIZChNJbnZhbGlkU3VtbW9uTWVtYmVyEJqkARIaChRQ", + "YXJ0eUxlYWRlckxvZ2dlZE91dBCbpAESGwoVQWxyZWFkeVN0YXJ0UGFydHlW", + "b3RlEJykARIWChBOb1N0YXJ0UGFydHlWb3RlEJ2kARIeChhBbHJlYWR5UGFz", + "c1BhcnR5Vm90ZVRpbWUQnqQBEhoKFEludmFsaWRQYXJ0eVZvdGVUaW1lEJ+k", + "ARIbChVBbHJlYWR5UmVwbHlQYXJ0eVZvdGUQoKQBEhoKFEVtcHR5UGFydHlJ", + "bnN0YW5jZUlkEKGkARIYChJJbnZhbGlkSW52aXRlUGxhY2UQoqQBEh0KF0lu", + "dml0ZVBhcnR5SW52YWxpZFVzZXJzEKOkARIbChVTdW1tb25QYXJ0eU1lbWJl", + "ckZhaWwQpKQBEh4KGEludmFsaWRQYXJ0eVN0cmluZ0xlbmd0aBClpAESIQob", + "SW5jbHVkZUJhbldvcmRGcm9tUGFydHlOYW1lEKakARIiChxKb2luaW5nUGFy", + "dHlNZW1iZXJJbmZvSXNOdWxsEKekARIeChhJbnZhbGlkU3VtbW9uV29ybGRT", + "ZXJ2ZXIQqKQBEhsKFU5vdEV4aXN0UGFydHlJbnN0YW5jZRDvqwESGgoUQnVm", + "Zk1ldGFEYXRhTm90Rm91bmQQ8asBEh0KF0J1ZmZOb3RSZWdpc3RyeUNhdGVn", + "b3J5EPKrARISCgxCdWZmTm90Rm91bmQQ86sBEiEKG0J1ZmZJbnZhbGlkQnVm", + "ZkNhdGVnb3J5VHlwZRD0qwESIQobQnVmZkNhY2hlTG9hZER1cGxpY2F0ZWRC", + "dWZmEPWrARIeChhCdWZmSW52YWxpZEF0dHJpYnV0ZVR5cGUQ9qsBEh0KF1F1", + "ZXN0QXNzaW5nRGF0YU5vdEV4aXN0ENizARIXChFRdWVzdE1haWxOb3RFeGlz", + "dBDZswESFwoRUXVlc3RBbHJlYWR5RW5kZWQQ2rMBEhMKDVF1ZXN0Q291bnRN", + "YXgQ27MBEh0KF1F1ZXN0VHlwZUFzc2lnbkNvdW50TWF4ENyzARIWChBRdWVz", + "dEludmFsaWRUeXBlEN2zARIXChFRdWVzdEludmFsaWRWYWx1ZRDeswESFQoP", + "UXVlc3RJZE5vdEZvdW5kEN+zARIZChNRdWVzdEludmFsaWRUYXNrTnVtEOCz", + "ARIbChVRdWVzdFJlZnVzZU9ubHlOb3JtYWwQ4bMBEh4KGFF1ZXN0QWJhZG9u", + "Tm90RXhpc3RRdWVzdBDiswESGgoUUXVlc3RBbHJlYWR5Q29tcGxldGUQ47MB", + "EhYKEFF1ZXN0Tm90Q29tcGxldGUQ5LMBEhwKFlF1ZXN0QWJhbmRvbk9ubHlO", + "b3JtYWwQ5bMBEhgKElF1ZXN0TWFpbERvY0lzTnVsbBDmswESFAoOUXVlc3RE", + "b2NJc051bGwQ57MBEhcKEUVuZFF1ZXN0RG9jSXNOdWxsEOizARIfChlRdWVz", + "dE1ldGFCYXNlTm90SW1wbGVtZW50EOmzARIgChpRdWVzdE5vdGlmeVJlZGlz", + "UmVnaXN0RmFpbBDqswESFwoRUXVlc3RBbHJlYWR5RXhpc3QQ67MBEhsKFVdv", + "cmxkTWV0YURhdGFOb3RGb3VuZBDBuwESGgoUTGFja09mV29ybGRFbnRlckl0", + "ZW0QwrsBEh4KGFdvcmxkTWFwVHJlZURhdGFOb3RGb3VuZBDDuwESIwodV29y", + "bGRNYXBUcmVlQ2hpbGRMYW5kTm90Rm91bmQQxLsBEhkKE1Jvb21NYXBEYXRh", + "Tm90Rm91bmQQxbsBEhoKFExhbmRNZXRhRGF0YU5vdEZvdW5kELW/ARISCgxM", + "YW5kTm90Rm91bmQQtr8BEh8KGUxhbmREb2NMb2FkRHVwbGljYXRlZExhbmQQ", + "t78BEhMKDUxhbmREb2NJc051bGwQuL8BEhcKEU93bmVkTGFuZE5vdEZvdW5k", + "ELm/ARIpCiNPd25lZExhbmREb2NMb2FkRHVwbGljYXRlZE93bmVkTGFuZBC6", + "vwESGAoST3duZWRMYW5kRG9jSXNOdWxsELu/ARIdChdMYW5kTWFwVHJlZURh", + "dGFOb3RGb3VuZBC8vwESJgogTGFuZE1hcFRyZWVDaGlsZEJ1aWxkaW5nTm90", + "Rm91bmQQvb8BEhwKFkxhbmRCdWlsZGluZ0lzTm90RW1wdHkQvr8BEhkKE0xh", + "bmRNYXBEYXRhTm90Rm91bmQQv78BEhQKDkxhbmRFeGlzdE93bmVyEMC/ARIZ", + "ChNMYW5kRWRpdG9ySXNOb3RVc2VyEMG/ARIZChNMYW5kT3duZXJJc05vdE1h", + "dGNoEMK/ARIeChhCdWlsZGluZ01ldGFEYXRhTm90Rm91bmQQqcMBEhYKEEJ1", + "aWxkaW5nTm90Rm91bmQQqsMBEicKIUJ1aWxkaW5nRG9jTG9hZER1cGxpY2F0", + "ZWRCdWlsZGluZxCrwwESFwoRQnVpbGRpbmdEb2NJc051bGwQrMMBEhsKFU93", + "bmVkQnVpbGRpbmdOb3RGb3VuZBCtwwESMQorT3duZWRCdWlsZGluZ0RvY0xv", + "YWREdXBsaWNhdGVkT3duZWRCdWlsZGluZxCuwwESHAoWT3duZWRCdWlsZGlu", + "Z0RvY0lzTnVsbBCvwwESIQobQnVpbGRpbmdNYXBUcmVlRGF0YU5vdEZvdW5k", + "ELDDARImCiBCdWlsZGluZ01hcFRyZWVDaGlsZFJvb21Ob3RGb3VuZBCxwwES", + "HQoXQnVpbGRpbmdGbG9vcklzTm90RW1wdHkQssMBEh0KF0J1aWxkaW5nTWFw", + "RGF0YU5vdEZvdW5kELPDARIYChJCdWlsZGluZ0V4aXN0T3duZXIQtMMBEicK", + "IUJ1aWxkaW5nTWFwVHJlZVBhcmVudExhbmROb3RGb3VuZBChoAESHQoXQnVp", + "bGRpbmdPd25lcklzTm90TWF0Y2gQtsMBEhwKFk15SG9tZU1ldGFEYXRhTm90", + "Rm91bmQQnccBEhQKDk15SG9tZU5vdEZvdW5kEJ7HARIjCh1NeUhvbWVEb2NM", + "b2FkRHVwbGljYXRlZE15SG9tZRCfxwESGAoSTXlIb21lQWxyZWFkeUV4aXN0", + "EKDHARIVCg9NeUhvbWVEb2NJc051bGwQoccBEhUKD015SG9tZUlzTm90TWlu", + "ZRCixwESJAoeTXlIb21lQ2FudEV4Y2hhbmdlV2hlbkNyYWZ0aW5nEKPHARIi", + "ChxFZGl0YWJsZVJvb21NZXRhRGF0YU5vdEZvdW5kEKTHARInCiFFZGl0YWJs", + "ZUZyYW1ld29ya01ldGFEYXRhTm90Rm91bmQQpccBEhkKE0ludGVyaW9yUG9p", + "bnRFeGNlZWQQpscBEhkKE015aG9tZU5vdEVub3VnaFNsb3QQp8cBEhYKEE15", + "aG9tZUlzU2VsZWN0ZWQQqMcBEhsKFU15aG9tZU5hbWVMZW5ndGhTaG9ydBCp", + "xwESGgoUTXlob21lTmFtZUxlbmd0aExvbmcQqscBEhoKFE15aG9tZU5hbWVE", + "dXBsaWNhdGVkEKvHARIeChhNeWhvbWVJbnRlcnBob25lTm90RXhpc3QQrMcB", + "Eh4KGE15aG9tZVN0YXJ0UG9pbnROb3RFeGlzdBCtxwESHAoWTXlob21lSW50", + "ZXJwaG9uZUV4Y2VlZBCuxwESHAoWTXlob21lU3RhcnRQb2ludEV4Y2VlZBCv", + "xwESGwoVQ3JhZnRpbmdDbG90aGVzRXhjZWVkELDHARIbChVDcmFmdGluZ0Nv", + "b2tpbmdFeGNlZWQQsccBEh0KF0NyYWZ0aW5nRnVybml0dXJlRXhjZWVkELLH", + "ARIZChNBbmNob3JJc05vdEluTXlob21lELPHARImCiBEb05vdFJlbW92ZVBy", + "b2Nlc3NDcmFmdGluZ0FuY2hvchC0xwESGQoTQW5jaG9yR3VpZER1cGxpY2F0", + "ZRC1xwESFgoQTXlob21lSXNFZGl0dGluZxC2xwESFgoQTXlob21lSXNPblJl", + "bnRhbBC3xwESHwoZTXlob21lVWdjSW5mb0ZpbGVOb3RGb3VuZRC4xwESIQob", + "RWRpdGFibGVSb29tU2l6ZVR5cGVJbnZhbGlkELnHARIYChJDcmFmdGVyQ291", + "bnRFeGNlZWQQuscBEhsKFU1pbmltYXBNYXJrZXJOb3RGb3VuZBCRywESMQor", + "TWluaW1hcE1hcmtlckRvY0xvYWREdXBsaWNhdGVkTWluaW1hcE1hcmtlchCS", + "ywESHAoWTWluaW1hcE1hcmtlckRvY0lzTnVsbBCTywESGgoUQ2FydE1ldGFE", + "YXRhTm90Rm91bmQQhc8BEhgKEkNhcnRNYXhDb3VudEV4Y2VlZBCGzwESGwoV", + "Q2FydFN0YWNrQ291bnRJbnZhbGlkEIfPARIdChdDYXJ0U3RhY2tDb3VudE5v", + "dEVub3VnaBCIzwESFgoQQ2FydEl0ZW1Ob3RGb3VuZBCJzwESIQobQ2FydE5v", + "dFJlZ2lzdHJ5Q3VycmVuY3lUeXBlEIrPARIdChdDYXJ0SW52YWxpZEN1cnJl", + "bmN5VHlwZRCLzwESEwoNQ2FydERvY0lzTnVsbBCMzwESFgoQQ2FydERvY0V4", + "Y2VwdGlvbhCNzwESGAoSQ2hhdFNlbmRTZWxmRmFpbGVkEPnSARIZChNDaGF0", + "SW52YWxpZENoYXRUeXBlEPrSARIgChpDaGF0QmxvY2tVc2VyQ2Fubm90V2hp", + "c3BlchD70gESHgoYQ2hhdEludmFsaWRNZXNzYWdlTGVuZ3RoEPzSARIYChJD", + "aGF0SW5jbHVkZUJhbldvcmQQ/dIBEhkKE05vdGljZUNoYXREb2NJc051bGwQ", + "7dYBEiQKHkVzY2FwZVBvc2l0aW9uTm90QXZhaWxhYmxlVGltZRDh2gESHQoX", + "RXNjYXBlUG9zaXRpb25Eb2NJc051bGwQ4toBEhgKEkJsb2NrVXNlckRvY0lz", + "TnVsbBDV3gESHwoZQ2hhcmFjdGVyUHJvZmlsZURvY0lzTnVsbBDJ4gESIAoa", + "Q3VzdG9tRGVmaW5lZERhdGFEb2NJc051bGwQ9eQBEh4KGEN1c3RvbURlZmlu", + "ZWRVaURvY0lzTnVsbBC95gESGQoTR2FtZU9wdGlvbkRvY0lzTnVsbBCx6gES", + "HAoWR2FtZU9wdGlvbkRvY0V4Y2VwdGlvbhCy6gESFAoOTGV2ZWxEb2NJc051", + "bGwQpe4BEhcKEUxvY2F0aW9uRG9jSXNOdWxsEJnyARIUCg5Ob3RVc2FibGVQ", + "bGFjZRCa8gESIQobUmVkaXNMb2NhdGlvbkNhY2hlU2V0RmFpbGVkEJvyARIU", + "Cg5Nb25leURvY0lzTnVsbBCB+gESHwoZTW9uZXlDb250cm9sTm90SW5pdGlh", + "bGl6ZRCC+gESFAoOTW9uZXlOb3RFbm91Z2gQg/oBEhsKFU1vbmV5TWF4Q291", + "bnRFeGNlZWRlZBCE+gESHgoYQ3VycmVuY3lNZXRhRGF0YU5vdEZvdW5kEIX6", + "ARImCiBTaG9wUHJvZHVjdFRyYWRpbmdNZXRlckRvY0lzTnVsbBDpgQISFgoQ", + "U2hvcElzTXlIb21lSXRlbRDqgQISGAoSSW52YWxpZFNob3BCdXlUeXBlEOuB", + "AhIdChdTaG9wUHJvZHVjdENhbm5vdFJlbndhbBDsgQISHgoYU2hvcFByb2R1", + "Y3ROb3RSZW53YWxUaW1lEO2BAhInCiFTaG9wUHJvZHVjdFJlbmV3YWxDb3Vu", + "dEFscmVhZHlNYXgQ7oECEhgKElNob3BJdGVtQ2Fubm90U2VsbBDvgQISGAoS", + "UmV3YXJkSW5mb05vdEV4aXN0ENCJAhIXChFSZXdhcmRJbnZhbGlkVHlwZRDR", + "iQISHAoWUmV3YXJkSW52YWxpZFR5cGVWYWx1ZRDSiQISHgoYTm90UmVxdWly", + "ZUF0dHJpYnV0ZVZhbHVlENOJAhIuCihSZXdhcmRHcm91cElkUGFyc2luZ0Zy", + "b21TdHJpbmdUb0ludEVyb3JyENSJAhIWChBDbGFpbUludmFsaWRUeXBlELiR", + "AhIdChdDbGFpbU1lbWJlcnNoaXBOb3RFeGlzdBC5kQISFwoRQ2xhaW1JbmZv", + "Tm90RXhpc3QQupECEh4KGENsYWltUmV3YXJkTm90RW5vdWdoVGltZRC7kQIS", + "GQoTQ2xhaW1SZXdhcmRFdmVudEVuZBC8kQISFAoOQ2xhaW1Eb2NJc051bGwQ", + "vZECEhoKFENyYWZ0UmVjaXBlRG9jSXNOdWxsEKGZAhIYChJDcmFmdEhlbHBE", + "b2NJc051bGwQopkCEhQKDkNyYWZ0RG9jSXNOdWxsEKOZAhIeChhDcmFmdGlu", + "Z01ldGFEYXRhTm90Rm91bmQQpJkCEhcKEUNyYWZ0aW5nTm90RmluaXNoEKWZ", + "AhIfChlDcmFmdGluZ05vdENyYWZ0aW5nQW5jaG9yEKaZAhIdChdDcmFmdGlu", + "Z0FscmVhZHlDcmFmdGluZxCnmQISHwoZQ3JhZnRpbmdBbmNob3JJc05vdFBs", + "YWNlZBComQISIQobQ3JhZnRpbmdSZWNpcGVJc05vdFJlZ2lzdGVyEKmZAhIo", + "CiJDcmFmdGluZ0FuY2hvcklzTm90TWF0Y2hXaXRoUmVjaXBlEKqZAhIbChVD", + "cmFmdGluZ0hlbHBDb3VudE92ZXIQq5kCEiMKHUNyYWZ0aW5nSGVscFNhbWVV", + "c2VyQ291bnRPdmVyEKyZAhIjCh1DcmFmdGluZ0hlbHBSZWNlaXZlZENvdW50", + "T3ZlchCtmQISGQoTQ3JhZnRIZWxwRG9jSXNFbXB0eRCumQISFQoPQ3JhZnRE", + "b2NJc0VtcHR5EK+ZAhIbChVDcmFmdGluZ0FscmVhZHlGaW5pc2gQsJkCEiUK", + "H0NyYWZ0aW5nUmVjaXBlSXNBbHJlYWR5UmVnaXN0ZXIQsZkCEhcKEUNyYWZ0", + "RG9jRXhjZXB0aW9uELKZAhIbChVDcmFmdEhlbHBEb2NFeGNlcHRpb24Qs5kC", + "Eh0KF0NyYWZ0UmVjaXBlRG9jRXhjZXB0aW9uELSZAhIeChhDcmFmdEludmFs", + "aWRSZXF1ZXN0Q291bnQQtZkCEh8KGVVncUFwaVNlcnZlclJlcXVlc3RGYWls", + "ZWQQiaECEicKIVVncUFwaVNlcnZlckNvbnZlcnRUb09iamVjdEZhaWxlZBCK", + "oQISKwolVWdxQXBpU2VydmVySW52YWlsZFNlYXJjaENhdGVnb3J5VHlwZRCL", + "oQISIAoaVWdxUmVwb3J0SW52YWxpZFRleHRMZW5ndGgQjKECEhoKFFVncVF1", + "ZXN0TWV0YU5vdEV4aXN0EI2hAhIeChhVZ3FCZWdpbkNyZWF0b3JQb2ludEZh", + "aWwQjqECEhgKElVncVF1ZXN0U2h1dGRvd25lZBCPoQISIgocVWdxVGVzdFF1", + "ZXN0QWxyZWFkeUNvbXBsZXRlZBCQoQISNAouVWdxUmVhc3NpZ25Vc2luZ0l0", + "ZW1FcnJvckNhdXNlUXVlc3ROb3RDb21wbGV0ZRCRoQISNQovVWdxUmVhc3Np", + "Z25Vc2luZ0l0ZW1FcnJvckNhdXNlUXVlc3RBbHJlYWR5RXhpc3QQkqECEjYK", + "MFVncVJlYXNzaWduVXNpbmdJdGVtRXJyb3JDYXVzZU5ld1JldmlzaW9uVXBk", + "YXRlZBCToQISIQobVWdxUXVlc3REYXRhSW52YWxpZFJldmlzaW9uEJShAhIl", + "Ch9VZ3FBYm9ydENhbm5vdENhdXNlSW52YWxpZFN0YXRlEJWhAhIeChhVZ3FN", + "ZXRhR2VuZXJhdG9yTm90RXhpc3QQlqECEiEKG1VncVF1ZXN0RGF0YVJldmlz", + "aW9uVXBkYXRlZBCXoQISMwotVWdxUmV2aXNpb25DYW5ub3RTbWFsbGVyVGhh", + "blJlcXVlc3RlZFJldmlzaW9uEJihAhIdChdVZ3FSZXZpc2lvblN0YXRlTm90", + "TGl2ZRCZoQISJgogVWdxUmV2aXNpb25TdGF0ZU9ubHlMaXZlbkFuZFRlc3QQ", + "mqECEiYKIFVncUFwaVNlcnZlckh0dHBSZXF1ZXN0RXhjZXB0aW9uEJuhAhId", + "ChdVZ3FRdWVzdFJldmlzaW9uQ2hhbmdlZBCcoQISHwoZVWdxQXNzaWduQ2Fu", + "bm90T3duZWRRdWVzdBCdoQISJQofVWdxQWxyZWFkeU93bmVkT2xkUmV2aXNp", + "b25RdWVzdBCeoQISGQoTU2Vhc29uUGFzc0RvY0lzTnVsbBDxqAISIAoaU2Vh", + "c29uUGFzc01ldGFEYXRhTm90Rm91bmQQ8qgCEiYKIFNlYXNvblBhc3NSZXdh", + "cmRNZXRhRGF0YU5vdEZvdW5kEPOoAhIYChJTZWFzb25QYXNzTWF4R3JhZGUQ", + "9KgCEh0KF1NlYXNvblBhc3NOb3RBYmxlUGVyaW9kEPWoAhIhChtTZWFzb25Q", + "YXNzQWxyZWFkeUJ1eUNoYXJnZWQQ9qgCEiIKHFNlYXNvblBhc3NBbHJlYWR5", + "VGFrZW5SZXdhcmQQ96gCEh4KGFNlYXNvblBhc3NOb3RFbm91Z2hHcmFkZRD4", + "qAISHwoZU2Vhc29uUGFzc05lZWRDaGFyZ2VkUGFzcxD5qAISGgoUU2Vhc29u", + "UGFzc0ludmFsaWRFeHAQ+qgCEhwKFlNlYXNvblBhc3NEb2NFeGNlcHRpb24Q", + "+6gCEiQKHkxhbmRBdWN0aW9uUmVkaXNDYWNoZVNldEZhaWxlZBDZsAISGQoT", + "TGFuZEF1Y3Rpb25Ob3RGb3VuZBDasAISJQofTGFuZEF1Y3Rpb25JbnZhbGlk", + "QXVjdGlvbk51bWJlchDbsAISJwohTGFuZEF1Y3Rpb25CaWRDdXJyZW5jeVR5", + "cGVJbnZhbGlkELTqARIiChxMYW5kQXVjdGlvbkJpZFByaWNlTm90RW5vdWdo", + "EN2wAhIiChxMYW5kQXVjdGlvbkVtcHR5Q2FjaGVJblJlZGlzEN6wAhIkCh5M", + "YW5kQXVjdGlvblJlZ2lzdHJ5SW5mb0ludmFsaWQQ37ACEhsKFUxhbmRBdWN0", + "aW9uTm90U3RhcnRlZBDgsAISKgokTGFuZEF1Y3Rpb25NaXNtYXRjaEJldHdl", + "ZW5DYWNoZUFuZERiEOGwAhIfChlMYW5kQXVjdGlvbkFscmVhZHlTdGFydGVk", + "EOKwAhIfChlMYW5kQXVjdGlvbkxhbmRJdGVtTm90U2V0EOOwAhIiChxMYW5k", + "QXVjdGlvbkVkaXRvclR5cGVJbnZhbGlkEOSwAhIdChdMYW5kQXVjdGlvbkFs", + "cmVhZHlFbmRlZBDlsAISKgokTGFuZEF1Y3Rpb25IaWdoZXN0QmlkVXNlckF0", + "dHJpYkVycm9yEOawAhIqCiRMYW5kQXVjdGlvblJlZnVuZEJpZFByaWNlQXR0", + "cmliRXJyb3IQ57ACEjAKKkxhbmRBdWN0aW9uQmlkZGVyUmVmdW5kQmlkUHJp", + "Y2VBdHRyaWJFcnJvchDosAISIAoaR2FtZUNvbmZpZ01ldGFEYXRhTm90Rm91", + "bmQQwbgCEioKJEF0dHJpYnV0ZURlZmluZWl0aW9uTWV0YURhdGFOb3RGb3Vu", + "ZBDCuAISIQobUmVxdWlyZW1lbnRNZXRhRGF0YU5vdEZvdW5kEMO4AhIgChpT", + "eXN0ZW1NYWlsTWV0YURhdGFOb3RGb3VuZBDEuAISHgoYSW50ZXJpb3JNZXRh", + "RGF0YU5vdEZvdW5kEMW4AhIfChlQcm9wR3JvdXBNZXRhRGF0YU5vdEZvdW5k", + "EMa4AhIXChFBaUNoYXRTZXJ2ZXJTdGFydBCpwAISGwoVQWlDaGF0U2VydmVy", + "UmVxRmFpbGVkEKrAAhIcChZBaUNoYXRTZXJ2ZXJCYWRyZXF1ZXN0EKvAAhIb", + "ChVBaUNoYXRTZXJ2ZXJGb3JiaWRkZW4QrMACEh4KGEFpQ2hhdFNlcnZlclVu", + "YXV0aG9yaXplZBCtwAISHgoYQWlDaGF0U2VydmVyVXNlck5vdEZvdW5kEK7A", + "AhIjCh1BaUNoYXRTZXJ2ZXJDaGFyYWN0ZXJOb3RGb3VuZBCvwAISIgocQWlD", + "aGF0U2VydmVyUmVhY3Rpb25Ob3RGb3VuZBCwwAISKAoiQWlDaGF0U2VydmVy", + "RXhhbXBsZURpYWxvbmdOb3RGb3VuZBCxwAISIQobQWlDaGF0U2VydmVyU2Vz", + "c2lvbk5vdEZvdW5kELLAAhIfChlBaUNoYXRTZXJ2ZXJNb2RlbE5vdEZvdW5k", + "ELPAAhIgChpBaUNoYXRTZXJ2ZXJOb3RFbm91Z2hQb2ludBC0wAISIwodQWlD", + "aGF0U2VydmVySW52YWxpZFBhcmFtZXRlcnMQtcACEiYKIEFpQ2hhdFNlcnZl", + "clNlc3Npb25MaW1pdEV4Y2VlZGVkELbAAhIiChxBaUNoYXRTZXJ2ZXJJbnZh", + "bGlkU2lnbmF0dXJlELfAAhIjCh1BaUNoYXRTZXJ2ZXJVc2VyQWxyZWFkeUV4", + "aXN0cxC4wAISHgoYQWlDaGF0U2VydmVyUmVtb3ZlRmFpbGVkELnAAhIfChlB", + "aUNoYXRTZXJ2ZXJEdXBsaWNhdGVHdWlkELrAAhIdChdBaUNoYXRTZXJ2ZXJE", + "dXBsaWNhdGVJZBC7wAISHgoYQWlDaGF0U2VydmVyQ2hhdE5vdEZvdW5kELzA", + "AhIeChhBaUNoYXRTZXJ2ZXJUb2tlbkV4cGlyZWQQvcACEiAKGkFpQ2hhdFNl", + "cnZlckludGVybmFsU2VydmVyEL7AAhIgChpBaUNoYXRTZXJ2ZXJJbnZhbGlk", + "TWVzc2FnZRC/wAISIQobQWlDaGF0U2VydmVyVXNlck9ubHlGZWF0dXJlEMDA", + "AhIgChpBaUNoYXRTZXJ2ZXJPd25lcnNoaXBFcnJvchDBwAISKgokQWlDaGF0", + "U2VydmVyQ2hhcmdlT3JkZXJOb3RGb3VuZEVycm9yEMLAAhIVCg9BaUNoYXRT", + "ZXJ2ZXJFbmQQjMECEiIKHEFpQ2hhdFNlcnZlclJldHJ5Q2hhcmdlUG9pbnQQ", + "jcECEhoKFEFpQ2hhdFNlcnZlckluYWN0aXZlEI7BAhIYChJBaUNoYXREb2NF", + "eGNlcHRpb24Qj8ECEg8KCU5wY0lzQnVzeRCRyAISFwoRTGFja09mRGFpbHlD", + "YWxpdW0Q+c8CEhcKEUxhY2tPZlRvdGFsQ2FsaXVtEPrPAhIeChhMYWNrT2ZD", + "b21taXNzaW9uQ3VycmVuY3kQ+88CEh8KGUxhY2tPZkNvbW1pc3Npb25NYXRl", + "cmlhbHMQ/M8CEhsKFUludmFsaWRNYXRlcmlhbFNsb3RJZBD9zwISFgoQRmFp", + "bFRvTG9hZENhbGl1bRD+zwISHAoWRmFpbFRvU2F2ZUNhbGl1bUR5bmFtbxD/", + "zwISGQoTTGFja09mQ29udmVydENhbGl1bRCr0AISHwoZR2V0RmFpbEVjaG9T", + "eXN0ZW1SZXNwb25zZRDc0AISIgocRmFpbFRvR2V0RWNob1N5c3RlbUh0dHBF", + "cnJvchDd0AISJAoeRmFpbFRvR2V0RWNob1N5c3RlbU1lc3NhZ2VOdWxsEN7Q", + "AhIiChxGYWlsVG9HZXRFY2hvU3lzdGVtRXhjZXB0aW9uEN/QAhIaChRGYWls", + "VG9TZW5kRWNob1N5c3RlbRDg0AISHwoZRmFpbFRvR2V0RWNob1N5c3RlbVJv", + "bGxVcBDh0AISHQoXQWNjb3VudExvZ2luQmxvY2tFbmFibGUQ0YYDEhsKFUlu", + "c3RhbmNlUm9vbUV4Y2VwdGlvbhC5jgMSJgogSW5zdGFuY2VSb29tQ2Fubm90", + "V3JpdGVFeHRyYUluZm8Quo4DEiYKIEluc3RhbmNlUm9vbU5vdENoYXJnZWRT", + "ZWFzb25QYXNzELuOAxIeChhJbnN0YW5jZU1ldGFEYXRhTm90Rm91bmQQvI4D", + "EiQKHkluc3RhbmNlTWV0YURhdGFPdmVyTGltaXRXcm9uZxC9jgMSHwoZSW5z", + "dGFuY2VBY2Nlc3NUeXBlSW52YWxpZBC+jgMSIQobSW5zdGFuY2VBY2Nlc3NJ", + "dGVtTm90RW5vdWdoEL+OAxIoCiJJbnN0YW5jZUFjY2Vzc1NlYXNvblBhc3NO", + "b3RDaGFyZ2VkEMCOAxIYChJJbnN0YW5jZVJvb21Jc0Z1bGwQwY4DEh4KGElu", + "c3RhbmNlUm9vbUlkRHVwbGljYXRlZBDCjgMSIQobSW5zdGFuY2VSb29tTm90", + "RXhpc3RBdFJlZGlzEMOOAxIeChhUYXNrUmVzZXJ2YXRpb25Eb2NJc051bGwQ", + "oZYDEiIKHEJpbGxpbmdHZXRQdXJjaGFzZUluZm9GYWlsZWQQiZ4DEh4KGEJp", + "bGxpbmdVcGRhdGVTdGF0ZUZhaWxlZBCKngMSHQoXQmlsbGluZ0ludmFsaWRT", + "dGF0ZVR5cGUQi54DEikKI0JpbGxpbmdGYWlsZWRQYXJzZVByb2R1Y3RNZXRh", + "SWRUeXBlEIyeAxIdChdCaWxsaW5nU3RhdGVUeXBlSW52YWxpZBCNngMSIwod", + "QmlsbGluZ1N0YXRlVHlwZUNhbnRCZUNoYW5nZWQQjp4DEhwKFkJpbGxpbmdT", + "dGF0ZVR5cGVSZWZ1bmQQj54DEiQKHkJpbGxpbmdTdGF0ZVR5cGVSZWZ1bmRD", + "b21wbGV0ZRCQngMSGgoUVGF4aU1ldGFEYXRhTm90Rm91bmQQ8aUDEhUKD1Rh", + "eGlUeXBlSW52YWxpZBDypQMSGgoUV2FycE1ldGFEYXRhTm90Rm91bmQQ86UD", + "EhUKD1dhcnBUeXBlSW52YWxpZBD0pQMSFAoOUmVudGFsTm90Rm91bmQQ2a0D", + "EiMKHVJlbnRhbERvY0xvYWREdXBsaWNhdGVkUmVudGFsENqtAxIVCg9SZW50", + "YWxEb2NJc051bGwQ260DEhwKFlJlbnRhbE5vdEF2YWlsYWJsZUxhbmQQ3K0D", + "EhoKFFJlbnRhbEFkZHJlc3NJbnZhbGlkEN2tAxIdChdSZW50YWxBZGRyZXNz", + "SXNOb3RFbXB0eRDerQMSHwoZUmVudGFsZmVlTWV0YURhdGFOb3RGb3VuZBDf", + "rQMSHgoYUmVudGFsQ29udHJhY3RJbmZvVXBkYXRlEOCtAxIiChxSZW50YWxD", + "dXJyZW5jeUFtb3VudElzVG9vTG93EMm1AxIfChlSZW50YWxDdXJyZW5jeVR5", + "cGVJc1dyb25nEMq1AxIoCiJQYWNrYWdlTGFzdE9yZGVyUmVjb2RlRG9jRXhj", + "ZXB0aW9uEMG1AxIfChlQYWNrYWdlUmVwZWF0RG9jRXhjZXB0aW9uEMK1AxIZ", + "ChNCZWFjb25TaG9wRXhjZXB0aW9uEKm9AxIfChlCZWFjb25TaG9wSW52YWxp", + "ZEFyZ3VtZW50EKq9AxInCiFCZWFjb25TaG9wQmVhY29uSXNOb3RJblJlbnRh", + "bEhvbWUQq70DEhwKFkJlYWNvblNob3BOb3RGb3VuZEl0ZW0QrL0DEh0KF0Jl", + "YWNvblNob3BOb3RFbm91Z2hJdGVtEK29AxIiChxCZWFjb25TaG9wSW52YWxp", + "ZEl0ZW1Gb3JTZWxsEK69AxIgChpCZWFjb25TaG9wTm90Rm91bmRNZXRhRGF0", + "YRCvvQMSHwoZQmVhY29uU2hvcExvd1NlbGxpbmdQcmljZRCwvQMSJAoeQmVh", + "Y29uU2hvcE5vdEVub3VnaFJlZ2lzdGVyRmVlELG9AxIaChRCZWFjb25TaG9w", + "U2xvdElzRnVsbBCyvQMSJwohQmVhY29uU2hvcE92ZXJPbmVEYXlSZWdpc3Rl", + "ckxpbWl0ELO9AxIeChhCZWFjb25TaG9wRmFpbGVkVG9DcmVhdGUQtL0DEiMK", + "HUJlYWNvblNob3BGYWlsZWRSZWdpc3RlckJvYXJkELW9AxIhChtCZWFjb25T", + "aG9wRmFpbGVkRGVsZXRlQm9hcmQQtr0DEiUKH0JlYWNvblNob3BOb3RGb3Vu", + "ZEl0ZW1Gcm9tQm9hcmQQt70DEiAKGkJlYWNvblNob3BQcm9maWxlRXhjZXB0", + "aW9uELi9AxIlCh9CZWFjb25TaG9wTm90RW5vdWdoUmVnaXN0ZXJHb2xkELm9", + "AxIgChpCZWFjb25TaG9wTGFja09mSXRlbUFtb3VudBC6vQMSIgocQmVhY29u", + "U2hvcEZhaWxlZEdldEJvYXJkSXRlbRC7vQMSIwodQmVhY29uU2hvcFNvbGRS", + "ZWNvcmRFeGNlcHRpb24QvL0DEisKJUJlYWNvblNob3BGYWlsZWRSZWxvYWRC", + "ZWFjb25TaG9wSW52ZW4Qvb0DEh0KF0JlYWNvblNob3BVcGRhdGVOZXdEYXRh", + "EL69AxImCiBCZWFjb25TaG9wRmFpbGVkVXBkYXRlRGF0YUZyb21EYhC/vQMS", + "IwodQmVhY29uU2hvcE5vdEZvdW5kU29sZFJlY29yZHMQwL0DEiEKG0JlYWNv", + "blNob3BQcm9maWxlRG9jSXNFbXB0eRDBvQMSIgocQmVhY29uU2hvcFNvbGRQ", + "cmljZUV4Y2VwdGlvbhDCvQMSIQobQmVhY29uU2hvcE5vdEZvdW5kU29sZFBy", + "aWNlEMO9AxIkCh5CZWFjb25TaG9wRmFpbGVkVG9GaW5kT3JVcGRhdGUQxL0D", + "EhsKFUJlYWNvblNob3BEYkV4Y2VwdGlvbhDFvQMSIAoaQmVhY29uU2hvcE92", + "ZXJTZWxsaW5nUHJpY2UQxr0DEiIKHEJlYWNvblNob3BPdmVyUmVudGFsU2Fm", + "ZVRpbWUQx70DEiMKHUJlYWNvblNob3BEZWFjdGl2ZUl0ZW1Gb3JTZWxsEMi9", + "AxIaChRVZ3FJbnZhbGlkVGFza0FjdGlvbhDh1AMSGwoVVWdxVGFza0FjdGlv", + "bkRpc2FibGVkEOLUAxIaChRVZ3FJbnZhbGlkRGlhbG9nVHlwZRDj1AMSHwoZ", + "VWdxSW52YWxpZERpYWxvZ0NvbmRpdGlvbhDk1AMSGAoSVWdxVmFsaWRhdGlv", + "bkVycm9yEOXUAxITCg1VZ3FOdWxsRW50aXR5EObUAxIZChNVZ3FTdGF0ZUNo", + "YW5nZUVycm9yEOfUAxIbChVVZ3FOb3RFbm91Z2hRdWVzdFNsb3QQ6NQDEhUK", + "D1VncU5vdEFsbG93RWRpdBDp1AMSGgoUVWdxRGlhbG9nSXNOb3RJblRhc2sQ", + "6tQDEhUKD1VncVJlcXVpcmVJbWFnZRDr1AMSFgoQVWdxUmVxdWlyZUJlYWNv", + "bhDs1AMSGQoTVWdxQmVhY29uSW5wdXRFcnJvchDt1AMSGgoUVWdxR2FtZURC", + "QWNjZXNzRXJyb3IQ7tQDEhUKD1VncU5vdE93blVnY05wYxDv1AMSHQoXVWdx", + "QWxyZWFkeUV4aXN0c0FjY291bnQQ8NQDEhgKElVncVNlcnZlckV4Y2VwdGlv", + "bhDx1AMSHgoYVWdxSW52YWxpZFdlYlBvcnRhbFRva2VuEPLUAxIVCg9VZ3FJ", + "bnZhbGlkVG9rZW4Q89QDEhcKEVVncVJlcXVpcmVBY2NvdW50EPTUAxIeChhV", + "Z3FOb3RFbm91Z2hDcmVhdG9yUG9pbnQQ9dQDEhIKDFVncVNsb3RMaW1pdBD2", + "1AMSHwoZVWdxRXhjZWVkVHJhbnNhY3Rpb25SZXRyeRD31AMSGAoSVWdxTWV0", + "YXZlcnNlT25saW5lEPjUAxIUCg5VZ3FBdXRoUmVtb3ZlZBD51AMSGwoVVWdx", + "SW52YWxpZFByZXNldEltYWdlEPrUAxIaChRVZ3FOb3RFbm91Z2hDdXJyZW5j", + "eRD71AMSFgoQVWdxQ3VycmVuY3lFcnJvchD81AMSIgocVWdxQWxyZWFkeUV4", + "aXN0c1Jlc2VydmVHcmFkZRD91AMSGwoVVWdxSW52YWxpZFJlc2VydmVUaW1l", + "EP7UAxIZChNVZ3FTYW1lR3JhZGVSZXNlcnZlEP/UAxIaChRVZ3FBbHJlYWR5", + "Qm9va21hcmtlZBDJ3AMSFgoQVWdxTm90Qm9va21hcmtlZBDK3AMSFQoPVWdx", + "QWxyZWFkeUxpa2VkEMvcAxIRCgtVZ3FOb3RMaWtlZBDM3AMSGAoSVWdxQWxy", + "ZWFkeVJlcG9ydGVkEM3cAxIUCg5VZ3FOb3RPd25RdWVzdBDO3AMSFQoPVWdx", + "SW52YWxpZFN0YXRlEM/cAxIdChdOZnRGb3JPd25lckFsbEdldEZhaWxlZBCx", + "5AMSGQoTSW50ZXJuYWxTZXJ2ZXJFcnJvchDxogQSDgoIUmRiRXJyb3IQ8qIE", + "EhEKC0R5bmFtb0Vycm9yEPOiBBIUCg5JbnZhbGlkUmVxdWVzdBD7ogQSFgoQ", + "UGxhbmV0SWROb3RGb3VuZBDZqgQSIwodUGxhbmV0U2VjcmV0S2V5RG9lc05v", + "dE1hdGNoZWQQ2qoEEhYKEEludmFsaWRQbGFuZXRKd3QQ26oEEhYKEEV4cGly", + "ZWRQbGFuZXRKd3QQ3KoEEiAKGk1ldGF2ZXJzZUNsaWVudE9uQ29ubmVjdGVk", + "EKGsBBIUCg5JbnZhbGlkVXNlckp3dBCirAQSFAoORXhwaXJlZFVzZXJKd3QQ", + "o6wEEhUKD0FjY291bnROb3RGb3VuZBCFrQQSEgoMVXNlck5vdEZvdW5kEIat", + "BBIdChdFeGNoYW5nZU9yZGVySWROb3RGb3VuZBDBsgQSKgokRXhjaGFuZ2VU", + "b3RhbE9yZGVyRGFpbHlMaW1pdEV4Y2VlZGVkEMKyBBIpCiNFeGNoYW5nZVVz", + "ZXJPcmRlckRhaWx5TGltaXRFeGNlZWRlZBDDsgQSFQoPQm90UGxheWVySXNO", + "dWxsENHbOhIcChZDbGllbnRUb0xvZ2luUmVzSXNOdWxsENLbOhIgChpDbGll", + "bnRUb0xvZ2luTWVzc2FnZUlzTnVsbBDT2zoSGwoVQ2xpZW50VG9HYW1lUmVz", + "SXNOdWxsENTbOhIfChlDbGllbnRUb0dhbWVNZXNzYWdlSXNOdWxsENXbOhIb", + "ChVDb25uZWN0ZWRUb1NlcnZlckZhaWwQ1ts6EhoKFFNjZW5hcmlvblBhcmFt", + "SXNOdWxsENfbOhIhChpCYXR0bGVSb29tQ29udGVudHNUeXBlT25seRDBsZ8F", + "Eh4KF0JhdHRsZUluc3RhbmNlVHlwZUVycm9yEMKxnwUSJQoeQmF0dGxlSW5z", + "dGFuY2VJbmZvQWxyZWFkeUV4aXN0EMOxnwUSIQoaQmF0dGxlSW5zdGFuY2VJ", + "bmZvTm90RXhpc3QQxLGfBRIkCh1CYXR0bGVJbnN0YW5jZUpvaW5QbGF5ZXJF", + "cnJvchDFsZ8FEi0KJkJhdHRsZUluc3RhbmNlVXNhYmxlU3Bhd25Qb2ludE5v", + "dEV4aXN0EMaxnwUSHQoWQmF0dGxlSW5zdGFuY2VJbkFjdGl2ZRDHsZ8FEiQK", + "HUJhdHRsZUluc3RhbmNlTm90RXhpc3RBbmNob3JzEMixnwUSJQoeQmF0dGxl", + "SW5zdGFuY2VBZGRQb2RDb21iYXRGYWlsEMmxnwUSJwogQmF0dGxlSW5zdGFu", + "Y2VPYmplY3RNZXRhTm90RXhpc3QQyrGfBRIwCilCYXR0bGVJbnN0YW5jZU9i", + "amVjdEludGVyYWN0aW9uTm90eWV0VGltZRDLsZ8FEiMKHEJhdHRsZUluc3Rh", + "bmNlT2JqZWN0Tm90RXhpc3QQzLGfBRIrCiRCYXR0bGVJbnN0YW5jZVBvZENv", + "bWJhdEFscmVhZHlPY2N1cHkQzbGfBRIvCihCYXR0bGVJbnN0YW5jZU9iamVj", + "dEludGVyYWN0aW9uTm90QWN0aXZlEM+xnwUSKwokQmF0dGxlSW5zdGFuY2VN", + "ZXRhQ29uZmlnTm90RXhpc3REYXRhENCxnwUSKwokQmF0dGxlSW5zdGFuY2VN", + "ZXRhUmV3YXJkTm90RXhpc3REYXRhENGxnwUSMwosQmF0dGxlSW5zdGFuY2VQ", + "aWNrdXBQb2RHZW5lcmF0ZWRUaW1lTm90RXhpc3QQ0rGfBRIqCiNCYXR0bGVJ", + "bnN0YW5jZVBpY2t1cFBvZE5vdEV4aXN0RGF0YRDTsZ8FEicKIEJhdHRsZUlu", + "c3RhbmNlTm90RXhpc3RQbGF5ZXJJbmZvENSxnwUSJAodQmF0dGxlSW5zdGFu", + "Y2VJbnRlcmFjdGlvbkZhaWwQ1bGfBRIxCipCYXR0bGVJbnN0YW5jZVBpY2t1", + "cFBvZFJld2FyZEFsbG9jYXRlRXJyb3IQ1rGfBRImCh9CYXR0bGVJbnN0YW5j", + "ZU5vdEV4aXN0RXZlbnRJbmZvENexnwUSIAoZQmF0dGxlSW5zdGFuY2VDbG9z", + "aW5nVGltZRDYsZ8FEiIKG0JhdHRsZUluc3RhbmNlU2VxUGFyc2VFcnJvchDZ", + "sZ8FEiIKG0dhbWVNb2RlSm9pbkhhbmRsZXJOb3RFeGlzdBCksp8FEiIKG0dh", + "bWVNb2RlSW5pdEhhbmRsZXJOb3RFeGlzdBClsp8FEikKIkdhbWVNb2RlSm9p", + "blN1Y2Nlc3NIYW5kbGVyTm90RXhpc3QQprKfBRIZChJHYW1lTW9kZUNyZWF0", + "ZUZhaWwQp7KfBRIaChNHYW1lTW9kZUNsYXNzSXNOdWxsEKiynwUSGwoUR2Ft", + "ZU1vZGVBbHJlYWR5RXhpc3QQqbKfBRIgChlHYW1lTW9kZUludmFsaWRBbmNo", + "b3JHdWlkEKqynwUSKgojU2VydmVyTWV0cmljc1RyaWdnZXJIYW5kbGVyTm90", + "Rm91bmQQ/brXLxIMCghEdXBMb2dpbhABEgoKBk1vdmluZxACEgsKB0RiRXJy", + "b3IQAxIMCghLaWNrRmFpbBAEEhYKEk5vdENvcnJlY3RQYXNzd29yZBAFEhAK", + "DE5vdEZvdW5kVXNlchAGEhAKDE5vR2FtZVNlcnZlchAHEhAKDExvZ2luUGVu", + "ZGluZxAIEhIKDk5vdEltcGxlbWVudGVkEAkSHQoZTm90RXhpc3RTZWxlY3Rl", + "ZENoYXJhY3RlchAKEhUKEU5vdEV4aXN0Q2hhcmFjdGVyEAsSFAoQU2VydmVy", + "TG9naWNFcnJvchAMEhEKDU5vUGVybWlzc2lvbnMQDhINCglSZWRpc0ZhaWwQ", + "DxIOCglMb2dpbkZhaWwQ6AcSEwoORHVwbGljYXRlZFVzZXIQ6QcSEQoMSW52", + "YWxpZFRva2VuEOoHEhUKEE5vdENvcnJlY3RTZXJ2ZXIQ6wcSDwoKSW5zcGVj", + "dGlvbhDsBxIOCglCbGFja0xpc3QQ7QcSDwoKU2VydmVyRnVsbBDuBxITCg5O", + "b3RGb3VuZFNlcnZlchDvBxISCg1Ob3RGb3VuZFRhYmxlEPAHEg8KClRhYmxl", + "RXJyb3IQ8QcSFQoQSW52YWxpZENvbmRpdGlvbhDMCBITCg5DaGFyQ3JlYXRl", + "RmFpbBDQDxITCg5DaGFyU2VsZWN0RmFpbBC0EBITCg5DcmVhdGVSb29tRmFp", + "bBC4FxIRCgxKb2luUm9vbUZhaWwQnBgSFQoQSm9pbkluc3RhbmNlRmFpbBCA", + "GRIbChZOb3RFeGlzdEluc3RhbmNlVGlja2V0EIEZEhYKEUxlYXZlSW5zdGFu", + "Y2VGYWlsEOQZEhkKFE5vdEV4aXN0SW5zdGFuY2VSb29tEMgaEhsKFk5vdENv", + "cnJlY3RJbnN0YW5jZVJvb20QyRoSDwoKUG9zSXNFbXB0eRDKGhIUCg9FbnRl", + "ck15SG9tZUZhaWwQ2B0SFAoPTGVhdmVNeUhvbWVGYWlsELweEhcKEkV4Y2hh", + "bmdlTXlIb21lRmFpbBCgHxIXChJOb3RGb3VuZE15SG9tZURhdGEQoR8SEwoO", + "Tm90TXlIb21lT3duZXIQoh8SFgoRQWxyZWFkeUhhdmVNeUhvbWUQox8SGwoW", + "RXhjaGFuZ2VNeUhvbWVQcm9wRmFpbBCEIBIZChRFeGNoYW5nZUJ1aWxkaW5n", + "RmFpbBDMIRIfChpFeGNoYW5nZUJ1aWxkaW5nTEZQcm9wRmFpbBCwIhIZChRF", + "eGNoYW5nZUluc3RhbmNlRmFpbBCUIxIhChxFeGNoYW5nZVNvY2lhbEFjdGlv", + "blNsb3RGYWlsEPgjEh0KGE5vdEZvdW5kU29jaWFsQWN0aW9uRGF0YRD6IxIa", + "ChVOb3RJblNvY2lhbEFjdGlvblNsb3QQ+yMSHAoXQWxyZWFkeUhhdmVTb2Np", + "YWxBY3Rpb24Q/CMSGQoUTm90Rm91bmRCdWlsZGluZ0RhdGEQrCQSFgoRTm90", + "Rm91bmRGbG9vckluZm8QrSQSFgoRTm90Rm91bmRJbmR1bkRhdGEQryQSGQoU", + "RW50ZXJGaXR0aW5nUm9vbUZhaWwQ3CQSGgoVTm90RW50ZXRlZEZpdHRpbmdS", + "b29tEN0kEhAKC01ha2VGYWlsT3RwEN4kEh0KGE5vdEV4aXN0Um9vbUluZm9G", + "b3JFbnRlchDfJBIfChpOb3RFeGlzdEdhbWVTZXJ2ZXJGb3JFbnRlchDgJBIV", + "ChBQb3NpdGlvblNhdmVGYWlsEOEkEhwKF0V4Y2hhbmdlRW1vdGlvblNsb3RG", + "YWlsEMAlEhoKFUVtb3Rpb25TbG90T3V0T2ZSYW5nZRDBJRIcChdOb3RGb3Vu", + "ZEFuY2hvckd1aWRJbk1hcBCjJhIXChJOb3RGb3VuZEFuY2hvckd1aWQQpCYS", + "DwoKUHJvcElzVXNlZBCmJhIUCg9Qcm9wVHlwZWlzV3JvbmcQpyYSGAoTTm90", + "Rm91bmRFbW90aW9uRGF0YRC4JhIVChBOb3RJbkVtb3Rpb25TbG90ELkmEhMK", + "DkNyZWF0ZUl0ZW1GYWlsEIQnEhAKC0FkZEl0ZW1GYWlsEIUnEhMKDkRlbGV0", + "ZUl0ZW1GYWlsEIYnEhIKDU5vTW9yZUFkZEl0ZW0QhycSEQoMTm90Rm91bmRJ", + "dGVtEIgnEhIKDU5vdEVub3VnaEl0ZW0QiScSFQoQSW52YWxpZFNsb3RJbmRl", + "eBCKJxIXChJEdXBsaWNhdGVkSXRlbUd1aWQQiycSGAoTTm90Rm91bmRJdGVt", + "VGFibGVJZBCMJxIUCg9Ob3RTZWxlY3RlZENoYXIQjScSEAoLTm90Rm91bmRN", + "YXAQjicSEQoMTm90RW1wdHlTbG90EI8nEg4KCUVtcHR5U2xvdBCQJxIYChNO", + "b3RGb3VuZEJ1ZmZUYWJsZUlkEJEnEhEKDE5vdEZvdW5kQnVmZhCSJxIcChdO", + "b3RFeGlzdEZvcmVjZWRNb3ZlSW5mbxCTJxIXChJEdXBsaWNhdGVkTmlja05h", + "bWUQlScSFwoSQWxyZWFkeVNldE5pY2tOYW1lEJYnEhkKFERpc2FsbG93ZWRD", + "aGFyYWN0ZXJzEJcnEhMKDkRiVXBkYXRlRmFpbGVkEJgnEhQKD0ludmFsaWRB", + "cmd1bWVudBCZJxIUCg9NYWlsU3lzdGVtRXJyb3IQpicSEgoNSW52YWxpZFRh", + "cmdldBCnJxIRCgxOb3RGb3VuZE1haWwQqCcSFAoPRW1wdHlJdGVtSW5NYWls", + "EKknEhYKEU1haWxTZW5kQ291bnRPdmVyEKonEhQKD0NoYW5nZWROaWNrTmFt", + "ZRCrJxIWChFTdGF0ZUNoYW5nZUZhaWxlZBCwJxITCg5Ob3RGb3VuZFRhcmdl", + "dBC6JxIWChFCbG9ja2VkRnJvbVRhcmdldBC7JxIRCgxMb2dPZmZUYXJnZXQQ", + "vCcSEwoOQ2FudFNlbmRUb1NlbGYQvScSFAoPQnVmZlR5cGVJc1dyb25nEM4n", + "EhkKFFJlZ2lzdGVyVG9vbFNsb3RGYWlsENgnEhsKFkRlcmVnaXN0ZXJUb29s", + "U2xvdEZhaWwQ2ScSFwoSVG9vbFNsb3RPdXRPZlJhbmdlENonEhUKEE5vdEZv", + "dW5kVG9vbFNsb3QQ2ycSEgoNRW1wdHlUb29sU2xvdBDcJxIdChhGb2xkZXJO", + "YW1lRXhjZWVkZWRMZW5ndGgQ4icSGwoWRm9sZGVyTmFtZUFscmVhZHlFeGlz", + "dBDjJxIXChJGb2xkZXJOYW1lTm90RXhpc3QQ5CcSIgodRm9sZGVyTmFtZUFs", + "cmVhZHlNYXhIb2xkQ291bnQQ5ScSFgoRRm9sZGVyQ291bnRFeGNlZWQQ5icS", + "FQoQRm9sZGVyQ3JlYXRlRmFpbBDnJxIeChlGb2xkZXJSZU5hbWVDYW5ub3RE", + "ZWZhdWx0EOgnEhoKFUZvbGRlck9kZXJ0eXBlSW52YWxpZBDpJxIeChlGcmll", + "bmRSZXF1ZXN0Tm90RXhpc3RJbmZvEOwnEh0KGEZyaWVuZFJlcXVlc3RBbHJl", + "YWR5U2VuZBDtJxIgChtGcmllbmRSZXF1ZXN0QWxyZWFkeVJlY2VpdmUQ7icS", + "IAobRnJpZW5kUmVxdWVzdENhbnRTZW5kVG9TZWxmEO8nEiYKIUZyaWVuZFJl", + "cXVlc3ROb3RFeGlzdFJlY2VpdmVkSW5mbxDwJxIfChpGcmllbmRSZXF1ZXN0", + "QWxyZWFkeUZyaWVuZBDxJxIQCgtJbnZhbGlkVHlwZRDyJxIZChRJbnZhbGlk", + "QXR0cmlidXRlU2xvdBDzJxIlCiBGcmllbmRSZXF1ZXN0Q2Fubm90U2VuZEJs", + "b2NrVXNlchD/JxIeChlBZGRGcmllbmRBbHJlYWR5QmxvY2tVc2VyEIAoEh8K", + "GkFkZEZyaWVuZE5vdEV4aXN0Q2hhcmFjdGVyEIEoEhsKFkFkZEZyaWVuZEFs", + "cmVhZHlGcmllbmQQgigSIgodQWRkRnJpZW5kQWxyZWFkeUNhbmNlbFJlcXVl", + "c3QQgygSHAoXQWRkRnJpZW5kQWxyZWFkeUV4cGlyZWQQhCgSFwoSRnJpZW5k", + "SW5mb05vdEV4aXN0EIooEiUKIEZyaWVuZEluZm9NeUNvdW50QWxyZWFkeU1h", + "eENvdW50EIsoEikKJEZyaWVuZEluZm9PdGhlcnNDb3VudEFscmVhZHlNYXhD", + "b3VudBCMKBIWChFGcmllbmRJbmZvT2ZmbGluZRCNKBIbChZGcmllbmRJbmZv", + "QWxyZWFkeUV4aXN0EI4oEiEKHEZyaWVuZEludml0ZU15UG9zSXNOb3RNeUhv", + "bWUQlCgSIQocRnJpZW5kSW52aXRlRG9udERpc3R1cmJTdGF0ZRCVKBIhChxG", + "cmllbmRJbnZpdGVFeHBpcmVUaW1lUmVtYWluEJYoEiMKHkZyaWVuZEludml0", + "ZVdhaXRpbmdPdGhlckludml0ZRCXKBIeChlGcmllbmRJbnZpdGVBbHJlYWR5", + "RXhwaXJlEJgoEh8KGkZyaWVuZEtpY2tNeVBvc0lzTm90TXlIb21lEJkoEh0K", + "GEZyaWVuZEtpY2tNZW1iZXJOb3RFeGlzdBCaKBIcChdGcmllbmRJc0luQW5v", + "dGhlck15aG9tZRCbKBIWChFCbG9ja1VzZXJNYXhDb3VudBCeKBIaChVCbG9j", + "a1VzZXJBbHJlYWR5QmxvY2sQnygSHgoZQmxvY2tVc2VyQ2Fubm90U2VuZE1h", + "aWxUbxCgKBITCg5CbG9ja2VkQnlPdGhlchChKBIdChhCbG9ja1VzZXJDYW5u", + "b3RXaGlzcGVyVG8QoigSEwoOQmxvY2tJbmZvRW1wdHkQoygSHwoaQmxvY2tV", + "c2VyQ2Fubm90SW52aXRlUGFydHkQpCgSIwoeQ2FydFNlbGxUeXBlTWlzc01h", + "dGNoV2l0aFRhYmxlENAoEhgKE0NhcnRGdWxsU3RhY2tvZkl0ZW0Q0SgSDwoK", + "Q2FydGlzRnVsbBDSKBIQCgtCYW5OaWNrTmFtZRDTKBIZChRDdXJyZW5jeU5v", + "dEZvdW5kRGF0YRCYKhIWChFDdXJyZW5jeU5vdEVub3VnaBCZKhIZChRDdXJy", + "ZW5jeUludmFsaWRWYWx1ZRCaKhIUCg9TdGFydEJ1ZmZGYWlsZWQQ/CoSEwoO", + "U3RvcEJ1ZmZGYWlsZWQQ/SoSFQoQTm90Rm91bmROaWNrTmFtZRDgKxIgChtO", + "b3RGb3VuZFRhdHRvb0F0dHJpYnV0ZURhdGEQqC0SEwoOTm90Rm91bmRTaG9w", + "SWQQjC4SGAoTVGltZU92ZXJGb3JQdXJjaGFzZRCNLhIXChJOb3RFbm91Z2hB", + "dHRyaWJ1dGUQji4SEgoNSW52YWxpZEdlbmRlchCPLhIQCgtJc0VxdWlwSXRl", + "bRCQLhIQCgtJbnZhbGlkSXRlbRCRLhIWChFBbHJlYWR5UmVnaXN0ZXJlZBCS", + "LhIVChBOb3RGb3VuZFNob3BJdGVtEJMuEhYKEU5vdEVub3VnaFNob3BJdGVt", + "EJQuEhUKEEludmFsaWRJdGVtQ291bnQQlS4SEgoNSW52ZW50b3J5RnVsbBCW", + "LhIWChFOb3RGb3VuZFByb2R1Y3RJZBCXLhIdChhSYW5kb21Cb3hJdGVtRGF0", + "YUludmFsaWQQ1C8SFgoRTm90RXhpc3RHYWNoYURhdGEQ1S9CLworY29tLmNh", + "bGl2ZXJzZS5hZG1pbi5kb21haW4uUmFiYml0TXEubWVzc2FnZVABYgZwcm90", + "bzM=")); +======= + "YkV4Y2VwdGlvbhCZThIVChBNb25nb0RiRXhjZXB0aW9uEJpOEiUKIE5sb2dX", + "aXRoQXdzQ2xvdWRXYXRjaFNldHVwRmFpbGVkEK9OEhcKEk5sb2dOb3RJbml0", + "aWFsaXplZBCwThIUCg9Mb2dBY3Rpb25Jc051bGwQw04SFgoRTG9nQXBwZW5k", + "ZXJJc051bGwQxE4SFwoSTG9nRm9ybWF0dGVySXNOdWxsEMVOEhkKFExvZ0Fj", + "dGlvblR5cGVJbnZhbGlkEMZOEhIKDVJtaUhvc3RJc051bGwQ9U4SHQoYUm1p", + "SG9zdEhhbmRsZXJCaW5kRmFpbGVkEPZOEhkKFFN1YkhhbmRsZXJCaW5kRmFp", + "bGVkEPdOEhYKEVByb3h5QXR0YWNoRmFpbGVkEPhOEh4KGVNldE1lc3NhZ2VN", + "YXhMZW5ndGhGYWlsZWQQ+U4SJAofUGFja2V0UmVjdkhhbmRsZXJSZWdpc3Rl", + "ckZhaWxlZBCnTxIkCh9QYWNrZXRTZW5kSGFuZGxlclJlZ2lzdGVyRmFpbGVk", + "EKhPEhYKEVBhY2tldFJlY3ZJbnZhbGlkEKlPEh4KGVJhY2tldFJlY3ZIYW5k", + "bGVyTm90Rm91bmQQqk8SHgoZTGFyZ2VQYWNrZXROb3RBbGxSZWNlaXZlZBCr", + "TxIcChdMYXJnZVBhY2tldFJlY3ZUaW1lT3ZlchCsTxIiCh1MYXJnZVBhY2tl", + "dFByb2Nlc3NUeXBlSW52YWxpZBCtTxIaChVMYXJnZVBhY2tldERhdGFJc051", + "bGwQrk8SGQoUTGFyZ2VQYWNrZXRFeGNlcHRpb24Qr08SFwoSRGJRdWVyeVR5", + "cGVJbnZhbGlkENlPEikKJER5bmFtb0RiVHJhbnNhY3Rpb25DYW5jZWxlZEV4", + "Y2VwdGlvbhDjTxIkCh9EeW5hbW9EYkFtYXpvbkR5bmFtb0RiRXhjZXB0aW9u", + "EORPEiMKHkR5bmFtb0RiQW1hem9uU2VydmljZUV4Y2VwdGlvbhDlTxIdChhE", + "eW5hbW9EYkNvbmZpZ0xvYWRGYWlsZWQQ5k8SGgoVRHluYW1vRGJDb25uZWN0", + "RmFpbGVkEOdPEh4KGUR5bmFtb0RiVGFibGVDcmVhdGVGYWlsZWQQ6E8SHgoZ", + "RHluYW1vRGJUYWJsZU5vdENvbm5lY3RlZBDpTxIYChNEeW5hbW9EYlF1ZXJ5", + "RmFpbGVkEOpPEh0KGER5bmFtb0RiSXRlbVNpemVFeGNlZWRlZBDrTxIiCh1E", + "eW5hbW9EYlF1ZXJ5Tm9NYXRjaEF0dHJpYnV0ZRDsTxIpCiREeW5hbW9EYlRy", + "YW5zYWN0aW9uQ29uZmxpY3RFeGNlcHRpb24Q7U8SHAoXRHluYW1vRGJFeHBy", + "ZXNzaW9uRXJyb3IQ7k8SHwoaRHluYW1vRGJQcmltYXJ5S2V5Tm90Rm91bmQQ", + "708SHQoYRHluYW1vRGJUYWJsZU5hbWVJbnZhbGlkEPBPEhwKF0R5bmFtb0Ri", + "Q29ubmVjdG9ySXNOdWxsEPFPEiAKG0R5bmFtb0RiVGFibGVOYW1lRHVwbGlj", + "YXRlZBDyTxIbChZEeW5hbW9EYlF1ZXJ5RXhjZXB0aW9uEPdPEh0KGER5bmFt", + "b0RiUXVlcnlOb1JlcXVlc3RlZBD4TxInCiJEeW5hbW9EYlF1ZXJ5Tm90Rm91", + "bmREb2N1bWVudFF1ZXJ5EPlPEisKJkR5bmFtb0RiUXVlcnlFeGNlcHRpb25O", + "b3RpZmllck5vdEZvdW5kEPpPEikKJER5bmFtb0RiRG9jdW1lbnRJc051bGxJ", + "blF1ZXJ5Q29udGV4dBCBUBIeChlEeW5hbW9EYkRvY3VtZW50SXNJbnZhbGlk", + "EIJQEiwKJ0R5bmFtb0RiRG9jdW1lbnRRdWVyeUNvbnRleHRUeXBlSW52YWxp", + "ZBCDUBIkCh9EeW5hbW9EYkRvY3VtZW50Q29weUZhaWxlZFRvRG9jEIRQEiEK", + "HER5bmFtb0RiRG9jdW1lbnRVcHNlcnRGYWlsZWQQhVASIQocRHluYW1vRGJJ", + "dGVtUmVxdWVzdElzSW52YWxpZBCLUBIvCipEeW5hbW9EYkl0ZW1SZXF1ZXN0", + "UXVlcnlDb250ZXh0VHlwZUludmFsaWQQjFASLQooRHluYW1vRGJJdGVtUmVx", + "dWVzdFVwZGF0ZUV4cHJlc3Npb25FbXB0eRCNUBIZChREeW5hbW9EYkRvY1Br", + "SW52YWxpZBCVUBIZChREeW5hbW9EYkRvY1NrSW52YWxpZBCWUBIkCh9EeW5h", + "bW9EYkRvY0F0dHJpYlR5cGVEdXBsaWNhdGVkEJdQEiQKH0R5bmFtb0RiRG9j", + "Q29weUZhaWxlZFRvRG9jdW1lbnQQmFASJgohRHluYW1vRGJEb2NDb3B5RmFp", + "bGVkRnJvbURvY3VtZW50EJlQEhwKF0R5bmFtb0RiRG9jVHlwZU5vdE1hdGNo", + "EJpQEhsKFkR5bmFtb0RiUmVxdWVzdEludmFsaWQQm1ASJAofRHluYW1vRGJE", + "b2NBdHRyaWJ1dGVTdGF0ZU5vdFNldBCcUBInCiJEeW5hbW9EYkRvY0F0dHJp", + "YldyYXBwZXJDb3B5RmFpbGVkEJ1QEiYKIUR5bmFtb0RiRG9jQXR0cmlidXRl", + "R2V0dGluZ0ZhaWxlZBCeUBIfChpEeW5hbW9EYkRvY0xpbmtQa1NrSW52YWxp", + "ZBCfUBIjCh5EeW5hbW9EYkRvY0F0dHJpYldyYXBwZXJJc051bGwQoFASIAob", + "TXlTcWxDb25uZWN0aW9uQ3JlYXRlRmFpbGVkEKlQEh4KGU15U3FsQ29ubmVj", + "dGlvbk9wZW5GYWlsZWQQqlASGgoVTXlTcWxEYlF1ZXJ5RXhjZXB0aW9uEKtQ", + "EiEKHE1vbmdvRGJJbml0QW5kVmVmaWZ5RGJGYWlsZWQQs1ASHQoYUmVkaXNT", + "ZXJ2ZXJDb25uZWN0RmFpbGVkEL1QEhwKF1JlZGlzU3RyaW5nc1dyaXRlRmFp", + "bGVkEL5QEhsKFlJlZGlzU3RyaW5nc1JlYWRGYWlsZWQQv1ASGQoUUmVkaXNT", + "ZXRzV3JpdGVGYWlsZWQQwFASGAoTUmVkaXNTZXRzUmVhZEZhaWxlZBDBUBIf", + "ChpSZWRpc1NvcnRlZFNldHNXcml0ZUZhaWxlZBDCUBIeChlSZWRpc1NvcnRl", + "ZFNldHNSZWFkRmFpbGVkEMNQEhsKFlJlZGlzSGFzaGVzV3JpdGVGYWlsZWQQ", + "xFASGgoVUmVkaXNIYXNoZXNSZWFkRmFpbGVkEMVQEhoKFVJlZGlzTGlzdHNX", + "cml0ZUZhaWxlZBDGUBIZChRSZWRpc0xpc3RzUmVhZEZhaWxlZBDHUBIbChZS", + "ZWRpc1JlcXVlc3RLZXlJc0VtcHR5EMhQEh0KGFJlZGlzTG9naW5DYWNoZUdl", + "dEZhaWxlZBDJUBIdChhSZWRpc0xvZ2luQ2FjaGVTZXRGYWlsZWQQylASIAob", + "UmVkaXNQcml2YXRlQ2FjaGVEdXBsaWNhdGVkEMtQEiUKIFJlZGlzR2xvYmFs", + "U2hhcmVkQ2FjaGVEdXBsaWNhdGVkEMxQEikKJFJlZGlzTG9naW5DYWNoZU93", + "bmVyVXNlckd1aWROb3RNYXRjaBDNUBIjCh5SZWRpc0dsb2JhbFBhcnR5Q2Fj", + "aGVHZXRGYWlsZWQQzlASJQogUmVkaXNHbG9iYWxQYXJ0eUNhY2hlV3JpdGVG", + "YWlsZWQQz1ASKwomUmVkaXNHbG9iYWxQYXJ0eU1lbWJlckNhY2hlV3JpdGVG", + "YWlsZWQQ0FASKwomUmVkaXNHbG9iYWxQYXJ0eVNlcnZlckNhY2hlV3JpdGVG", + "YWlsZWQQ0VASNAovUmVkaXNHbG9iYWxQYXJ0eUludml0ZVBhcnR5U2VuZENh", + "Y2hlV3JpdGVGYWlsZWQQ0lASKAojUmVkaXNJbnN0YW5jZVJvb21JbmZvQ2Fj", + "aGVHZXRGYWlsZWQQ01ASKQokUmVkaXNVZ2NOcGNUb3RhbFJhbmtDYWNoZVdy", + "aXRlRmFpbGVkENRQEiAKG1JlZGlzUmVxdWVzdEhhbmRsZXJOb3RGb3VuZBDV", + "UBIgChtSYWJiaXRNcUNvbnN1bWVyU3RhcnRGYWlsZWQQoVESGgoVUmFiYml0", + "TXFDb25uZWN0RmFpbGVkEKJREhkKFFJhYmJpdE1lc3NhZ2VUaW1lT2xkEKNR", + "EiAKG1JhYmJpdE1xQ2hhbm5lbENyZWF0ZUZhaWxlZBCkURIZChRTM0NsaWVu", + "dENyZWF0ZUZhaWxlZBCFUhIZChRTM0J1Y2tldENyZWF0ZUZhaWxlZBCGUhIX", + "ChJTM0ZpbGVVcGxvYWRGYWlsZWQQh1ISFwoSUzNGaWxlRGVsZXRlRmFpbGVk", + "EIhSEhQKD1MzRmlsZUdldEZhaWxlZBCJUhIXChJNZXRhRGF0YUxvYWRGYWls", + "ZWQQt1ISFAoPSW52YWxpZE1ldGFEYXRhELhSEhIKDU1ldGFJZEludmFsaWQQ", + "uVISFAoPSnNvblR5cGVJbnZhbGlkEPNSEiEKHEpzb25Db252ZXJ0RGVzZXJp", + "YWxpemVGYWlsZWQQ9FISHQoYU2VydmVyQ29uZmlnRmlsZU5vdEZvdW5kEM1T", + "EhYKEVNlcnZlclR5cGVJbnZhbGlkEM5TEicKIkFscmVhZHlSdW5uaW5nU2Vy", + "dmVyV2l0aExpc3RlblBvcnQQz1MSGQoUTm90Rm91bmRDYWNoZVN0b3JhZ2UQ", + "0FMSFgoRRnVuY3Rpb25QYXJhbU51bGwQ0VMSGQoURnVuY3Rpb25JbnZhbGlk", + "UGFyYW0Q0lMSHAoXQ2xpZW50TGlzdGVuUG9ydEludmFsaWQQ01MSGQoUTm90", + "T3ZlcnJpZGVJbnRlcmZhY2UQ1FMSGgoVU2VydmVyT25SdW5uaW5nRmFpbGVk", + "ENVTEhcKElNlcnZpY2VUeXBlSW52YWxpZBDWUxIbChZGdW5jdGlvbk5vdElt", + "cGxlbWVudGVkENdTEi4KKUNsYXNzRG9lc05vdEltcGxlbWVudEludGVyZmFj", + "ZUluaGVyaXRhbmNlENhTEhcKElJ1bGVUeXBlRHVwbGljYXRlZBDZUxIYChND", + "bGFzc1R5cGVDYXN0SXNOdWxsENpTEiIKHVBlcmlvZGljVGFza0FscmVhZHlS", + "ZWdpc3RlcmVkENtTEiIKHUVudGl0eVRpY2tlckFscmVhZHlSZWdpc3RlcmVk", + "ENxTEhkKFEVudGl0eVRpY2tlck5vdEZvdW5kEN1TEhcKEkVudGl0eUJhc2VO", + "b3RGb3VuZBDeUxIYChNWYWxpZFNlcnZlck5vdEZvdW5kEN9TEiAKG1Rhcmdl", + "dFNlcnZlclVzZXJDb3VudEV4Y2VlZBDgUxIXChJUYXJnZXRVc2VyTm90Rm91", + "bmQQ4VMSFwoSVGFyZ2V0VXNlck5vdExvZ0luEOJTEhAKC05vdEV4aXN0TWFw", + "EONTEiIKHUZhaWxlZFRvUmVzZXJ2ZUVudGVyQ29uZGl0aW9uEORTEh0KGEZh", + "aWxlZFRvUmVzZXJ2YXRpb25FbnRlchDlUxIbChZPd25lckVudGl0eVR5cGVJ", + "bnZhbGlkEOZTEhwKF093bmVyRW50aXR5Q2Fubm90RmlsbHVwEOdTEhUKEE93", + "bmVyR3VpZEludmFsaWQQ6FMSIQocRGFpbHlUaW1lRXZlbnRBZGRpdGlvbkZh", + "aWxlZBDpUxIkCh9Qcm9ncmFtVmVyc2lvblBhdGhUb2tlbk5vdEZvdW5kEOpT", + "Eh0KGEN1cnJlbnRseVByb2Nlc3NpbmdTdGF0ZRDrUxIZChRTZXJ2ZXJVcmxU", + "eXBlSW52YWxpZBDsUxIjCh5TZXJ2ZXJVcmxUeXBlQWxyZWFkeVJlZ2lzdGVy", + "ZWQQ7VMSHAoXU2VydmVyT2ZmbGluZU1vZGVFbmFibGUQ7lMSEQoMT3duZXJJ", + "bnZhbGlkEO9TEhsKFk1vZHVsZUFscmVhZHlSZWdpc3RyZWQQ8FMSJQogTW9k", + "dWxlQWxyZWFkeUFkZEluaXRpYWxpemVNb2R1bGUQ8VMSEwoOTW9kdWxlTm90", + "Rm91bmQQ8lMSHQoYUHJvZ3JhbVZlcnNpb25Mb2FkRmFpbGVkEPNTEhkKFFNl", + "cnZlckFscmVhZHlSdW5uaW5nEPRTEiQKH01ldGFEYXRhQ29weVRvRHluYW1v", + "RGJEb2NGYWlsZWQQ4lQSKAojTWV0YURhdGFDb3B5VG9FbnRpdHlBdHRyaWJ1", + "dGVGYWlsZWQQ41QSIQocRHluYW1vRGJEb2NDb3B5VG9DYWNoZUZhaWxlZBDk", + "VBIrCiZEeW5hbW9EYkRvY0NvcHlUb0VudGl0eUF0dHJpYnV0ZUZhaWxlZBDl", + "VBIlCiBDYWNoZUNvcHlUb0VudGl0eUF0dHJpYnV0ZUZhaWxlZBDmVBIhChxD", + "YWNoZUNvcHlUb0R5bmFtb0RiRG9jRmFpbGVkEOdUEiUKIEVudGl0eUF0dHJp", + "YnV0ZUNvcHlUb0NhY2hlRmFpbGVkEOhUEisKJkVudGl0eUF0dHJpYnV0ZUNv", + "cHlUb0R5bmFtb0RiRG9jRmFpbGVkEOlUEjkKNEVudGl0eUF0dHJpYnV0ZUNv", + "cHlUb0VudGl0eUF0dHJpYnV0ZVRyYW5zYWN0b3JGYWlsZWQQ6lQSOQo0RW50", + "aXR5QXR0cmlidXRlVHJhbnNhY3RvckNvcHlUb0VudGl0eUF0dHJpYnV0ZUZh", + "aWxlZBDrVBI1CjBFbnRpdHlBdHRyaWJ1dGVUcmFuc2FjdG9yQ29weVRvRHlu", + "YW1vRGJEb2NGYWlsZWQQ7FQSEwoOQXR0cmliTm90Rm91bmQQ7VQSGQoUQXR0", + "cmliUGF0aE1ha2VGYWlsZWQQ7lQSHgoZRW50aXR5QXR0cmlidXRlQ2FzdEZh", + "aWxlZBDvVBIeChlTdHJpbmdDb252ZXJ0VG9FbnVtRmFpbGVkEPBUEh4KGU1l", + "dGFTY2hlbWFWZXJzaW9uTm90TWF0Y2gQlVUSHAoXTWV0YURhdGFWZXJzaW9u", + "Tm90TWF0Y2gQllUSGgoVUGFja2V0VmVyc2lvbk5vdE1hdGNoEJdVEh8KGkNs", + "aWVudExvZ2ljVmVyc2lvbk5vdE1hdGNoEJhVEhwKF1Jlc291cmNlVmVyc2lv", + "bk5vdE1hdGNoEJlVEh8KGkNsaWVudFByb2dyYW1WZXJzaW9uSXNOdWxsEJpV", + "EhMKDlRlc3RJZE5vdEFsbG93EPlVEhEKDEJvdGROb3RBbGxvdxD6VRIZChRB", + "Y2NvdW50SWRMZW5ndGhTaG9ydBD7VRIkCh9BY2NvdW50SWROb3RGb3VuZElu", + "U3NvQWNjb3VudERiEPxVEiEKHE1ldGFEYXRhTm90Rm91bmRCeVRlc3RVc2Vy", + "SWQQ/VUSHAoXQWNjb3VudFBhc3N3b3JkTm90TWF0Y2gQ/lUSJwoiVXNlckRh", + "dGFDb252ZXJ0VG9BY2NvdW50QXR0ckZhaWxlZBD/VRIkCh9BY2NvdW50QmFz", + "ZUF0dHJpYkluc2VydERiRmFpbGVkEIBWEhgKE05vU2VydmVyQ29ubmVjdGFi", + "bGUQgVYSEwoOQmxvY2tlZEFjY291bnQQglYSLAonU3NvQWNjb3VudEF1dGhX", + "aXRoTGF1bmNoZXJMb2dpbk5vdEFsbG93EINWEiIKHUNsaWVudFN0YW5kYWxv", + "bmVMb2dpbk5vdEFsbG93EIRWEhkKFFBsYXRmb3JtVHlwZU5vdEFsbG93EIVW", + "EiYKIUFjY291bnRDYW5Ob3RSZWFkRnJvbVNzb0FjY291bnREYhCGVhIhChxT", + "c29BY2NvdW50QXV0aEp3dENoZWNrRmFpbGVkEIdWEikKJFVzZXJJZEtleU5v", + "dEZvdW5kSW5Tc29BY2NvdW50QXV0aEp3dBCIVhIoCiNVc2VySWRWYWx1ZUVt", + "cHR5SW5Tc29BY2NvdW50QXV0aEp3dBCJVhIuCilBY2NvdW50VHlwZUtleU5v", + "dEZvdW5kSW5Tc29BY2NvdW50QXV0aEp3dBCKVhIwCitBY2NvdW50VHlwZVZh", + "bHVlTm90QWxsb3dJblNzb0FjY291bnRBdXRoSnd0EItWEigKI0FjY291bnRC", + "YXNlRG9jTm90Rm91bmRJbk1ldGF2ZXJzZURiEIxWEi4KKUFjY2Vzc1Rva2Vu", + "S2V5Tm90QWxsb3dJblNzb0FjY291bnRBdXRoSnd0EI1WEiYKIUFjY2Vzc1Rv", + "a2VuTm90TWF0Y2hJblNzb0FjY291bnREYhCOVhIYChNBY2NvdW50VHlwZU5v", + "dEFsbG93EI9WEhoKFUFjY291bnRCYXNlRG9jTm90TG9hZBCQVhIXChJOb3RF", + "bm91Z2hBdXRob3JpdHkQrlYSGQoUQWNjb3VudEJhc2VEb2NJc051bGwQr1YS", + "FQoQQWNjb3VudElkSW52YWxpZBCwVhIbChZBY2NvdW50V2l0aG91dFVzZXJH", + "dWlkELFWEiIKHVNzb0FjY291bnRBdXRoSnd0VG9rZW5FeHBpcmVkELJWEh8K", + "GlNzb0FjY291bnRBdXRoSnd0RXhjZXB0aW9uELNWEiQKH1RyYW5zYWN0aW9u", + "UnVubmVyQWxyZWFkeVJ1bm5pbmcQwVcSHgoZVHJhbnNhY3Rpb25SdW5uZXJO", + "b3RGb3VuZBDCVxIWChFFbnRpdHlHdWlkSW52YWxpZBClWBIbChZFbnRpdHlB", + "dHRyaWJEdXBsaWNhdGVkEKZYEhoKFUVudGl0eUF0dHJpYnV0ZUlzTnVsbBCn", + "WBIcChdFbnRpdHlBdHRyaWJ1dGVOb3RGb3VuZBCoWBIgChtFbnRpdHlBdHRy", + "aWJ1dGVTdGF0ZUludmFsaWQQqVgSFgoRRW50aXR5VHlwZUludmFsaWQQqlgS", + "FgoRRW50aXR5TGlua2VkVG9NYXAQq1gSGQoURW50aXR5Tm90TGlua2VkVG9N", + "YXAQrFgSGgoVRW50aXR5U3RhdGVOb3REYW5jaW5nEK1YEhsKFkVudGl0eUFj", + "dGlvbkR1cGxpY2F0ZWQQiVkSGQoURW50aXR5QWN0aW9uTm90Rm91bmQQilkS", + "HQoYRW50aXR5QmFzZUhmc21Jbml0RmFpbGVkEO1ZEiAKG1JlZGlzR2xvYmFs", + "RW50aXR5RHVwbGljYXRlZBDRWhIaChVVc2VySXNTd2l0Y2hpbmdTZXJ2ZXIQ", + "tVsSHQoYVXNlcklzTm90U3dpdGNoaW5nU2VydmVyELZbEh8KGlNlcnZlclN3", + "aXRjaGluZ090cE5vdE1hdGNoELdbEiMKHkNvbm5lY3RlZFNlcnZlcklzTm90", + "RGVzdFNlcnZlchC4WxIbChZJbnZhbGlkUmVzZXJ2YXRpb25Vc2VyELlbEhYK", + "EUludmFsaWRSZXR1cm5Vc2VyELpbEikKJFVzZXJOaWNrbmFtZU5vdEFsbG93", + "V2l0aFNwZWNpYWxDaGFycxDhXRIsCidVc2VyTmlja25hbWVBbGxvd2VkTWlu", + "MlRvTWF4OFdpdGhLb3JlYW4Q4l0SLgopVXNlck5pY2tuYW1lQWxsb3dlZE1p", + "bjRUb01heDE2V2l0aEVuZ2xpc2gQ410SLQooVXNlck5pY2tuYW1lTm90QWxs", + "b3dlZE51bWJlckF0Rmlyc3RDaGFycxDkXRIeChlVc2VyTmlja25hbWVOb3RB", + "bGxvd0NoYXJzEOVdEi0KKFVzZXJOaWNrbmFtZU5vdEFsbG93V2l0aEluaXRp", + "YWxpc21Lb3JlYW4Q5l0SFAoPVXNlck5pY2tuYW1lQmFuEOddEhgKE1VzZXJE", + "dXBsaWNhdGVkTG9naW4Q6F0SEQoMVXNlck5vdExvZ2luEOldEikKJFVzZXJD", + "cmVhdGlvbkZvckR5bmFtb0RiRG9jRHVwbGljYXRlZBDqXRIpCiRVc2VyR3Vp", + "ZEFwcGx5VG9SZWZBdHRyaWJ1dGVBbGxGYWlsZWQQ610SJgohVGVzdFVzZXJQ", + "cmVwYXJlQ3JlYXRlTm90Q29tcGxldGVkEOxdEikKJERlZmF1bHRVc2VyUHJl", + "cGFyZUNyZWF0ZU5vdENvbXBsZXRlZBDtXRIgChtVc2VyUHJlcGFyZUxvYWRO", + "b3RDb21wbGV0ZWQQ7l0SGwoWVXNlck5pY2tuYW1lTm90Q3JlYXRlZBDvXRIf", + "ChpVc2VyTmlja25hbWVBbHJlYWR5Q3JlYXRlZBDwXRIfChpVc2VyQ3JlYXRl", + "U3RlcE5vdENvbXBsZXRlZBDxXRIYChNVc2VyQ3JlYXRlQ29tcGxldGVkEPJd", + "EhsKFlVzZXJTdWJLZXlCaW5kVG9GYWlsZWQQ810SHAoXVXNlclN1YktleVJl", + "cGxhY2VGYWlsZWQQ9F0SFAoPVXNlckd1aWRJbnZhbGlkEPVdEhoKFVVzZXJO", + "aWNrbmFtZURvY0lzTnVsbBD2XRISCg1Vc2VyRG9jSXNOdWxsEPddEhkKFFVz", + "ZXJHdWlkQWxyZWFkeUFkZGVkEPhdEhsKFlVzZXJOaWNrbmFtZUR1cGxpY2F0", + "ZWQQ+V0SIwoeVXNlck5pY2tuYW1lQWxsb3dlZE1pbjJUb01heDEyEPpdEiAK", + "G1VzZXJOaWNrbmFtZVNlYXJjaFBhZ2VXcm9uZxD7XRIWChFVc2VyTmlja25h", + "bWVFbXB0eRD8XRIhChxVc2VyQ29udGVudHNTZXR0aW5nRG9jSXNOdWxsEP1d", + "EhYKEVVzZXJNb25leURvY0VtcHR5EP5dEiEKHFVzZXJSZXBvcnRJbnZhbGlk", + "VGl0bGVMZW5ndGgQxV4SIwoeVXNlclJlcG9ydEludmFsaWRDb250ZW50TGVu", + "Z3RoEMZeEhsKFlVzZXJSZXBvcnREb2NFeGNlcHRpb24Qx14SKwomVGVzdENo", + "YXJhY3RlclByZXBhcmVDcmVhdGVOb3RDb21wbGV0ZWQQyWUSLgopRGVmYXVs", + "dENoYXJhY3RlclByZXBhcmVDcmVhdGVOb3RDb21wbGV0ZWQQ1GUSJQogQ2hh", + "cmFjdGVyUHJlcGFyZUxvYWROb3RDb21wbGV0ZWQQ1WUSLAonQ2hhcmFjdGVy", + "QmFzZURvY0xvYWREdXBsaWNhdGVkQ2hhcmFjdGVyENZlEhkKFENoYXJhY3Rl", + "ck5vdFNlbGVjdGVkENdlEhYKEUNoYXJhY3Rlck5vdEZvdW5kENhlEhsKFkNo", + "YXJhY3RlckJhc2VEb2NOb1JlYWQQ2WUSJQogQ2hhcmFjdGVyQ3VzdG9taXpp", + "bmdOb3RDb21wbGV0ZWQQ2mUSJAofQ2hhcmFjdGVyQ3JlYXRlU3RlcE5vdENv", + "bXBsZXRlZBDbZRIpCiRDaGFyYWN0ZXJDdXN0b21pemluZ0FscmVhZHlDb21w", + "bGV0ZWQQ3GUSHwoaQ2hhcmFjdGVyUHJlcGFyZU5vdENyZWF0ZWQQ3WUSHQoY", + "Q2hhcmFjdGVyQ3JlYXRlQ29tcGxldGVkEN5lEhsKFkNoYXJhY3RlckJhc2VE", + "b2NJc051bGwQ32USFQoQQWJpbGl0eU5vdEVub3VnaBD1ZxIZChRJdGVtTWV0", + "YURhdGFOb3RGb3VuZBCxbRIUCg9JdGVtR3VpZEludmFsaWQQsm0SEgoNSXRl", + "bURvY0lzTnVsbBCzbRInCiJJdGVtRGVmYXVsdEF0dHJpYnV0ZU5vdEZvdW5k", + "SW5NZXRhELRtEiMKHkl0ZW1MZXZlbEVuY2hhbnROb3RGb3VuZEluTWV0YRC1", + "bRIeChlJdGVtRW5jaGFudE5vdEZvdW5kSW5NZXRhELZtEisKJkl0ZW1BdHRy", + "aWJ1dGVSYW5kb21Hcm91cE5vdEZvdW5kSW5NZXRhELdtEi8KKkl0ZW1BdHRy", + "aWJ1dGVSYW5kb21Hcm91cFRvdGFsV2VpZ2h0SW52YWxpZBC4bRIRCgxJdGVt", + "Tm90Rm91bmQQuW0SHgoZSXRlbUNsb3RoSW52YWxpZExhcmdlVHlwZRC6bRIe", + "ChlJdGVtQ2xvdGhJbnZhbGlkU21hbGxUeXBlELttEhoKFUl0ZW1TdGFja0Nv", + "dW50SW52YWxpZBC8bRIXChJJdGVtTWF4Q291bnRFeGNlZWQQvW0SHgoZSXRl", + "bURvY0xvYWREdXBsaWNhdGVkSXRlbRC+bRIZChRDbG90aFNsb3RUeXBlSW52", + "YWxpZBC/bRIcChdJdGVtU3RhY2tDb3VudE5vdEVub3VnaBDAbRIXChJJdGVt", + "Q291bnROb3RFbm91Z2gQwW0SGAoTSXRlbUludmFsaWRJdGVtVHlwZRDCbRId", + "ChhJdGVtVG9vbE1ldGFEYXRhTm90Rm91bmQQw20SFQoQSXRlbVRvb2xOb3RG", + "b3VuZBDEbRIdChhJdGVtVG9vbE5vdEFjdGl2YXRlU3RhdGUQxW0SGAoTVG9v", + "bEFjdGlvbkRvY0lzTnVsbBDGbRIlCiBUb29sQWN0aW9uQWxyZWFkeVVuYWN0", + "aXZhdGVTdGF0ZRDHbRIjCh5Ub29sQWN0aW9uQWxyZWFkeUFjdGl2YXRlU3Rh", + "dGUQyG0SFwoSSXRlbVRhdHRvb05vdEZvdW5kEMltEiUKIEl0ZW1BdHRyaWJ1", + "dGVFbmNoYW50TWV0YU5vdEZvdW5kEMptEiMKHkl0ZW1BdHRyaWJ1dGVDaGFu", + "Z2VOb3RTZWxlY3RlZBDLbRIkCh9JdGVtUGFyc2luZ0Zyb21TdHJpbmdUb0lu", + "dEVyb3JyEMxtEikKJEl0ZW1WYWx1ZVBhcnNpbmdGcm9tU3RyaW5nVG9JbnRF", + "cm9ychDNbRImCiFJdGVtRmlyc3RQdXJjaGFzZUhpc3RvcnlEb2NJc051bGwQ", + "zm0SMgotSXRlbUZpcnN0UHVyY2hhc2VIaXN0b3J5RG9jTG9hZER1cGxpY2F0", + "ZWRJdGVtEM9tEikKJEl0ZW1GaXJzdFB1cmNoYXNlSGlzdG9yeUFscmVhZHlF", + "eGlzdBDQbRIsCidJdGVtRmlyc3RQdXJjaGFzZURpc2NvdW50SXRlbUNvdW50", + "V3JvbmcQ0W0SHwoaSXRlbUF0dHJpYnV0ZUlkVHlwZUludmFsaWQQ0m0SFAoP", + "SXRlbUFsbG9jRmFpbGVkENNtEhcKEkl0ZW1HdWlkRHVwbGljYXRlZBDUbRIY", + "ChNJdGVtTGV2ZWxDdXJyZW50TWF4ENVtEhcKEkl0ZW1DYW5Ob3RCZVN0b3Jl", + "ZBDWbRIcChdJdGVtVXNlRnVuY3Rpb25Ob3RGb3VuZBCVbhIdChhJdGVtVXNl", + "UXVlc3RNYWlsQ291bnRNYXgQlm4SIwoeSXRlbVVzZU5vdEV4aXN0QXNzaWdu", + "YWJsZVF1ZXN0EJduEhsKFkl0ZW1Vc2VBbHJlYWR5SGFzUXVlc3QQmG4SHwoa", + "SXRlbVVzZUFscmVhZHlIYXNRdWVzdE1haWwQmW4SIwoeQmFnUnVsZUl0ZW1M", + "YXJnZVR5cGVEdXBsaWNhdGVkEJl1EikKJFRvb2xFcXVpcFJ1bGVJdGVtTGFy", + "Z2VUeXBlRHVwbGljYXRlZBCadRIqCiVDbG90aEVxdWlwUnVsZUl0ZW1MYXJn", + "ZVR5cGVEdXBsaWNhdGVkEJt1EisKJlRhdHRvb0VxdWlwUnVsZUl0ZW1MYXJn", + "ZVR5cGVEdXBsaWNhdGVkEJx1EhoKFUludmVudG9yeVJ1bGVOb3RGb3VuZBCd", + "dRIXChJFcXVpcEludmVuTm90Rm91bmQQnnUSGAoTU2xvdHNBbHJlYWR5RXF1", + "aXBlZBCfdRIaChVTbG90c0FscmVhZHlVbmVxdWlwZWQQoHUSEgoNQmFnSXNJ", + "dGVtRnVsbBChdRITCg5CYWdJc0l0ZW1FbXB0eRCidRIaChVCYWdEZWx0YUl0", + "ZW1EdXBsaWF0ZWQQo3USFAoPQmFnSXRlbU5vdEZvdW5kEKR1EigKI0Nsb3Ro", + "RXF1aXBSdWxlQ2xvdGhTbG90VHlwZU5vdEZvdW5kEKV1EiYKIVRvb2xFcXVp", + "cFJ1bGVUb29sU2xvdFR5cGVOb3RGb3VuZBCmdRIqCiVUYXR0b29FcXVpcFJ1", + "bGVUYXR0b29TbG90VHlwZU5vdEZvdW5kEKd1EhgKE0JhZ1RhYlR5cGVBZGRG", + "YWlsZWQQqHUSFgoRQmFnVGFiVHlwZUludmFsaWQQqXUSFwoSQmFnVGFiVHlw", + "ZU5vdEZvdW5kEKp1EhsKFkJhZ1RhYkNvdW50TWVyZ2VGYWlsZWQQq3USHwoa", + "SW52ZW50b3J5RW50aXR5VHlwZUludmFsaWQQrHUSGgoVSW52ZW5FcXVpcFR5", + "cGVJbnZhbGlkEK11EhoKFUJhZ0lzUmVzZXJ2ZWRJdGVtRnVsbBCudRIbChZC", + "YWdJc1Jlc2VydmVkSXRlbUVtcHR5EK91EhYKEUVxdWlwU2xvdE5vdE1hdGNo", + "ELB1EhgKE0VxdWlwU2xvdE91dE9mUmFuZ2UQsXUSHgoZU2xvdHNBbHJlYWR5", + "UmVzZXJ2ZWRFcXVpcBCydRIgChtTbG90c0FscmVhZHlSZXNlcnZlZFVuZXF1", + "aXAQs3USFQoQU2xvdFR5cGVOb3RGb3VuZBC0dRIfChpVZ2NOcGNNZXRhR3Vp", + "ZEFscmVhZHlBZGRlZBD9dRIUCg9VZ2NOcGNEb2NJc051bGwQ/nUSGQoUVWdj", + "TnBjTWF4Q291bnRFeGNlZWQQ/3USHQoYVWdjTnBjQ2xvdGhJdGVtTm90RW5v", + "dWdoEIB2EiIKHVVnY05wY0RvY0xvYWREdXBsaWNhdGVkVWdjTnBjEIF2EiIK", + "HVVnY05wY0Rlc2NyaXB0aW9uTGVuZ3RoRXhjZWVkEIJ2EiMKHlVnY05wY1dv", + "cmRTY2VuYXJpb0xlbmd0aEV4Y2VlZBCDdhIfChpVZ2NOcGNHcmVldGluZ0xl", + "bmd0aEV4Y2VlZBCEdhIeChlVZ2NOcGNUYXR0b29JdGVtTm90RW5vdWdoEIV2", + "EicKIlVnY05wY0hhYml0U29jaWFsQWN0aW9uQ291bnRFeGNlZWQQhnYSKgol", + "VWdjTnBjRGlhbG9ndWVTb2NpYWxBY3Rpb25Db3VudEV4Y2VlZBCHdhIdChhV", + "Z2NOcGNOaWNrbmFtZUR1cGxpY2F0ZWQQiHYSEwoOVWdjTnBjTm90Rm91bmQQ", + "iXYSIwoeVWdjTnBjSW50cm9kdWN0aW9uTGVuZ3RoRXhjZWVkEIp2EhcKElVn", + "Y05wY01heFRhZ0V4Y2VlZBCLdhIYChNVZ2NOcGNOaWNrbmFtZUVtcHR5EIx2", + "EiYKIVVnY05wY0FscmVhZHlSZWdpc3RlcmVkSW5HYW1lWm9uZRCNdhIiCh1V", + "Z2NOcGNOb3RSZWdpc3RlcmVkSW5HYW1lWm9uZRCOdhIkCh9VZ2NOcGNMaWtl", + "U2VsZWN0ZWVDb3VudE5vdEZvdW5kEI92EiMKHlVnY05wY0xpa2VTZWxlY3Rl", + "ZEZsYWdOb3RGb3VuZBCQdhIfChpVZ2NOcGNEdXBsaWNhdGVJbk15aG9tZVVn", + "YxCRdhIXChJVZ2NOcGNMb2NhdGVkU3RhdGUQknYSGwoWVWdjTnBjTWV0YURh", + "dGFOb3RGb3VuZBCTdhIjCh5CZWFjb25BcHBQcm9maWxlVXBsb2FkQ29vbFRp", + "bWUQlHYSGgoVQmVhY29uQm9keUl0ZW1JbnZhbGlkEJV2EhwKF1VnY05wY0hh", + "c0JlYWNvblNob3BJdGVtEJZ2Eh8KGlVnY05wY1JhbmtFbnRpdHlJc05vdEZv", + "dW5kEOF2EhkKFFVnY05wY1JhbmtPdXRPZlJhbmdlEOJ2EiMKHkZhcm1pbmdF", + "ZmZlY3REb2NMaW5rUGtTa05vdFNldBDFdxItCihGYXJtaW5nRWZmZWN0QWxy", + "ZWFkeVJlZ2lzdGVyZWRJbkdhbWVab25lEMZ3EhMKDkZhcm1pbmdBbHJlYWR5", + "EMd3EhAKC0Zhcm1pbmdCeU1lEMh3EiAKG0Zhcm1pbmdQcm9wTWV0YURhdGFO", + "b3RGb3VuZBDJdxIbChZGYXJtaW5nVHJ5Q291bnRJbnZhbGlkEMp3EhQKD0Zh", + "cm1pbmdOb3RTdGF0ZRDLdxIZChRGYXJtaW5nT3duZXJOb3RNYXRjaBDMdxIl", + "CiBGYXJtaW5nU3VtbW9uZWRFbnRpdHlUeXBlSW52YWxpZBDNdxIkCh9GYXJt", + "aW5nRWZmZWN0Tm90RXhpc3RJbkdhbWVab25lEM53EhAKC0ZhcmltZ1N0YXRl", + "EM93EhsKFkZhcm1pbmdTdGFuZEJ5Tm90U3RhdGUQ0HcSGgoVRmFybWluZ0Fu", + "Y2hvck5vdEZvdW5kENF3EiYKIUZhcm1pbmdCZWFjb25EYkluZm9JbnRlZ3Jp", + "dHlFcnJvchDSdxIaChVHYWNoYU1ldGFEYXRhTm90Rm91bmQQqXgSFQoQR2Fj", + "aGFSZXdhcmRFbXB0eRCqeBITCg5NYXN0ZXJOb3RGb3VuZBC5exIVChBNYXN0", + "ZXJOb3RSZWxhdGVkELp7EhQKD0dhbWVab25lTm90Sm9pbhCdfBIOCglNYXBJ", + "c051bGwQnnwSHAoXTG9jYXRpb25VbmlxdWVJZEludmFsaWQQn3wSEwoOUHJv", + "cElzT2NjdXBpZWQQoHwSFgoRUHJvcElzTm90T2NjdXBpZWQQoXwSFAoPRnJp", + "ZW5kRG9jSXNOdWxsEIF9EhoKFUZyaWVuZEZvbGRlckRvY0lzTnVsbBCCfRIi", + "ChxTb2NpYWxBY3Rpb25NZXRhRGF0YU5vdEZvdW5kEOmEARIaChRTb2NpYWxB", + "Y3Rpb25Ob3RGb3VuZBDqhAESLwopU29jaWFsQWN0aW9uRG9jTG9hZER1cGxp", + "Y2F0ZWRTb2NpYWxBY3Rpb24Q64QBEh4KGFNvY2lhbEFjdGlvbkFscmVhZHlF", + "eGlzdBDshAESIAoaU29jaWFsQWN0aW9uU2xvdE91dE9mUmFuZ2UQ7YQBEhsK", + "FVNvY2lhbEFjdGlvbk5vdE9uU2xvdBDuhAESGwoVU29jaWFsQWN0aW9uRG9j", + "SXNOdWxsEO+EARIcChZDaGFubmVsTW92ZVNhbWVDaGFubmVsENGMARIgChpD", + "aGFubmVsSW52YWxpZE1vdmVDb29sVGltZRDSjAESFgoQTm90Q2hhbm5lbFNl", + "cnZlchDTjAESGAoST3duZWRSb29tRG9jSXNOdWxsELmUARITCg1Sb29tRG9j", + "SXNOdWxsELqUARIVCg9Sb29tSXNOb3RNeUhvbWUQu5QBEhoKFE1hcFJhbmdl", + "T3V0T2ZDZWxsUG9zELyUARIcChZNYXBHcmlkQm91bmRPdXRPZlJhbmdlEL2U", + "ARIZChNNYXBHcmlkTm90Rm91bmRHcmlkEL6UARIZChNNYXBHcmlkTm90Rm91", + "bmRDZWxsEL+UARIfChlNYXBHcmlkQ2VsbE5vdEZvdW5kUGxheWVyEMCUARIf", + "ChlNYXBHcmlkQ2VsbE5vdEZvdW5kVWdjTnBjEMGUARISCgxNYWlsTm90Rm91", + "bmQQoZwBEhYKEE1haWxBbHJlYWR5VGFrZW4QopwBEhkKE01haWxJbnZhbGlk", + "TWFpbFR5cGUQo5wBEhwKFk1haWxNYXhTZW5kQ291bnRFeGNlZWQQpJwBEhMK", + "DU1haWxEb2NJc051bGwQpZwBEh0KF01haWxCbG9ja1VzZXJDYW5ub3RTZW5k", + "EKacARImCiBNYWlsTWF4VGFyZ2V0UmVjZWl2ZWRDb3VudEV4Y2VlZBCnnAES", + "FgoQTWFpbENhbnRTZW5kU2VsZhConAESIAoaTWFpbENhbnREZWxldGVJZkl0", + "ZW1FeGlzdHMQqZwBEhYKEE1haWxEb2NFeGNlcHRpb24QqpwBEhoKFE1haWxQ", + "cm9maWxlRG9jSXNOdWxsEM2eARIdChdNYWlsUHJvZmlsZURvY0V4Y2VwdGlv", + "bhDOngESGQoTU3lzdGVtTWFpbERvY0lzTnVsbBCVoAESGAoSUGFydHlDYW5u", + "b3RTZXRHdWlkEImkARIgChpQYXJ0eUZhaWxlZE1ha2VQYXJ0eU1lbWJlchCK", + "pAESEQoLUGFydHlJc0Z1bGwQi6QBEhgKEkFscmVhZHlJbnZpdGVQYXJ0eRCM", + "pAESGQoTTm90Rm91bmRQYXJ0eUludml0ZRCNpAESEwoNTm90Rm91bmRQYXJ0", + "eRCOpAESDgoITm90UGFydHkQj6QBEhQKDk5vdFBhcnR5TGVhZGVyEJCkARIS", + "CgxKb2luaW5nUGFydHkQkaQBEhQKDk5vdFBhcnR5TWVtYmVyEJKkARITCg1B", + "bHJlYWR5U3VtbW9uEJOkARIdChdQYXJ0eUxlYWRlclNlcnZlcklzRnVsbBCU", + "pAESGwoVSW52aXRlTWVtYmVySXNDb25jZXJ0EJWkARIcChZGYWlsVG9TZW5k", + "SW52aXRlTWVtYmVyEJakARIYChJBbHJlYWR5UGFydHlNZW1iZXIQl6QBEh0K", + "F0ludmFsaWRTdW1tb25TZXJ2ZXJUeXBlEJikARIdChdTdW1tb25Vc2VyTGlt", + "aXREaXN0YW5jZRCZpAESGQoTSW52YWxpZFN1bW1vbk1lbWJlchCapAESGgoU", + "UGFydHlMZWFkZXJMb2dnZWRPdXQQm6QBEhsKFUFscmVhZHlTdGFydFBhcnR5", + "Vm90ZRCcpAESFgoQTm9TdGFydFBhcnR5Vm90ZRCdpAESHgoYQWxyZWFkeVBh", + "c3NQYXJ0eVZvdGVUaW1lEJ6kARIaChRJbnZhbGlkUGFydHlWb3RlVGltZRCf", + "pAESGwoVQWxyZWFkeVJlcGx5UGFydHlWb3RlEKCkARIaChRFbXB0eVBhcnR5", + "SW5zdGFuY2VJZBChpAESGAoSSW52YWxpZEludml0ZVBsYWNlEKKkARIdChdJ", + "bnZpdGVQYXJ0eUludmFsaWRVc2VycxCjpAESGwoVU3VtbW9uUGFydHlNZW1i", + "ZXJGYWlsEKSkARIeChhJbnZhbGlkUGFydHlTdHJpbmdMZW5ndGgQpaQBEiEK", + "G0luY2x1ZGVCYW5Xb3JkRnJvbVBhcnR5TmFtZRCmpAESIgocSm9pbmluZ1Bh", + "cnR5TWVtYmVySW5mb0lzTnVsbBCnpAESHgoYSW52YWxpZFN1bW1vbldvcmxk", + "U2VydmVyEKikARIbChVOb3RFeGlzdFBhcnR5SW5zdGFuY2UQ76sBEhoKFEJ1", + "ZmZNZXRhRGF0YU5vdEZvdW5kEPGrARIdChdCdWZmTm90UmVnaXN0cnlDYXRl", + "Z29yeRDyqwESEgoMQnVmZk5vdEZvdW5kEPOrARIhChtCdWZmSW52YWxpZEJ1", + "ZmZDYXRlZ29yeVR5cGUQ9KsBEiEKG0J1ZmZDYWNoZUxvYWREdXBsaWNhdGVk", + "QnVmZhD1qwESHgoYQnVmZkludmFsaWRBdHRyaWJ1dGVUeXBlEParARIdChdR", + "dWVzdEFzc2luZ0RhdGFOb3RFeGlzdBDYswESFwoRUXVlc3RNYWlsTm90RXhp", + "c3QQ2bMBEhcKEVF1ZXN0QWxyZWFkeUVuZGVkENqzARITCg1RdWVzdENvdW50", + "TWF4ENuzARIdChdRdWVzdFR5cGVBc3NpZ25Db3VudE1heBDcswESFgoQUXVl", + "c3RJbnZhbGlkVHlwZRDdswESFwoRUXVlc3RJbnZhbGlkVmFsdWUQ3rMBEhUK", + "D1F1ZXN0SWROb3RGb3VuZBDfswESGQoTUXVlc3RJbnZhbGlkVGFza051bRDg", + "swESGwoVUXVlc3RSZWZ1c2VPbmx5Tm9ybWFsEOGzARIeChhRdWVzdEFiYWRv", + "bk5vdEV4aXN0UXVlc3QQ4rMBEhoKFFF1ZXN0QWxyZWFkeUNvbXBsZXRlEOOz", + "ARIWChBRdWVzdE5vdENvbXBsZXRlEOSzARIcChZRdWVzdEFiYW5kb25Pbmx5", + "Tm9ybWFsEOWzARIYChJRdWVzdE1haWxEb2NJc051bGwQ5rMBEhQKDlF1ZXN0", + "RG9jSXNOdWxsEOezARIXChFFbmRRdWVzdERvY0lzTnVsbBDoswESHwoZUXVl", + "c3RNZXRhQmFzZU5vdEltcGxlbWVudBDpswESIAoaUXVlc3ROb3RpZnlSZWRp", + "c1JlZ2lzdEZhaWwQ6rMBEhcKEVF1ZXN0QWxyZWFkeUV4aXN0EOuzARIbChVX", + "b3JsZE1ldGFEYXRhTm90Rm91bmQQwbsBEhoKFExhY2tPZldvcmxkRW50ZXJJ", + "dGVtEMK7ARIeChhXb3JsZE1hcFRyZWVEYXRhTm90Rm91bmQQw7sBEiMKHVdv", + "cmxkTWFwVHJlZUNoaWxkTGFuZE5vdEZvdW5kEMS7ARIZChNSb29tTWFwRGF0", + "YU5vdEZvdW5kEMW7ARIaChRMYW5kTWV0YURhdGFOb3RGb3VuZBC1vwESEgoM", + "TGFuZE5vdEZvdW5kELa/ARIfChlMYW5kRG9jTG9hZER1cGxpY2F0ZWRMYW5k", + "ELe/ARITCg1MYW5kRG9jSXNOdWxsELi/ARIXChFPd25lZExhbmROb3RGb3Vu", + "ZBC5vwESKQojT3duZWRMYW5kRG9jTG9hZER1cGxpY2F0ZWRPd25lZExhbmQQ", + "ur8BEhgKEk93bmVkTGFuZERvY0lzTnVsbBC7vwESHQoXTGFuZE1hcFRyZWVE", + "YXRhTm90Rm91bmQQvL8BEiYKIExhbmRNYXBUcmVlQ2hpbGRCdWlsZGluZ05v", + "dEZvdW5kEL2/ARIcChZMYW5kQnVpbGRpbmdJc05vdEVtcHR5EL6/ARIZChNM", + "YW5kTWFwRGF0YU5vdEZvdW5kEL+/ARIUCg5MYW5kRXhpc3RPd25lchDAvwES", + "GQoTTGFuZEVkaXRvcklzTm90VXNlchDBvwESGQoTTGFuZE93bmVySXNOb3RN", + "YXRjaBDCvwESHgoYQnVpbGRpbmdNZXRhRGF0YU5vdEZvdW5kEKnDARIWChBC", + "dWlsZGluZ05vdEZvdW5kEKrDARInCiFCdWlsZGluZ0RvY0xvYWREdXBsaWNh", + "dGVkQnVpbGRpbmcQq8MBEhcKEUJ1aWxkaW5nRG9jSXNOdWxsEKzDARIbChVP", + "d25lZEJ1aWxkaW5nTm90Rm91bmQQrcMBEjEKK093bmVkQnVpbGRpbmdEb2NM", + "b2FkRHVwbGljYXRlZE93bmVkQnVpbGRpbmcQrsMBEhwKFk93bmVkQnVpbGRp", + "bmdEb2NJc051bGwQr8MBEiEKG0J1aWxkaW5nTWFwVHJlZURhdGFOb3RGb3Vu", + "ZBCwwwESJgogQnVpbGRpbmdNYXBUcmVlQ2hpbGRSb29tTm90Rm91bmQQscMB", + "Eh0KF0J1aWxkaW5nRmxvb3JJc05vdEVtcHR5ELLDARIdChdCdWlsZGluZ01h", + "cERhdGFOb3RGb3VuZBCzwwESGAoSQnVpbGRpbmdFeGlzdE93bmVyELTDARIn", + "CiFCdWlsZGluZ01hcFRyZWVQYXJlbnRMYW5kTm90Rm91bmQQoaABEh0KF0J1", + "aWxkaW5nT3duZXJJc05vdE1hdGNoELbDARIcChZNeUhvbWVNZXRhRGF0YU5v", + "dEZvdW5kEJ3HARIUCg5NeUhvbWVOb3RGb3VuZBCexwESIwodTXlIb21lRG9j", + "TG9hZER1cGxpY2F0ZWRNeUhvbWUQn8cBEhgKEk15SG9tZUFscmVhZHlFeGlz", + "dBCgxwESFQoPTXlIb21lRG9jSXNOdWxsEKHHARIVCg9NeUhvbWVJc05vdE1p", + "bmUQoscBEiQKHk15SG9tZUNhbnRFeGNoYW5nZVdoZW5DcmFmdGluZxCjxwES", + "IgocRWRpdGFibGVSb29tTWV0YURhdGFOb3RGb3VuZBCkxwESJwohRWRpdGFi", + "bGVGcmFtZXdvcmtNZXRhRGF0YU5vdEZvdW5kEKXHARIZChNJbnRlcmlvclBv", + "aW50RXhjZWVkEKbHARIZChNNeWhvbWVOb3RFbm91Z2hTbG90EKfHARIWChBN", + "eWhvbWVJc1NlbGVjdGVkEKjHARIbChVNeWhvbWVOYW1lTGVuZ3RoU2hvcnQQ", + "qccBEhoKFE15aG9tZU5hbWVMZW5ndGhMb25nEKrHARIaChRNeWhvbWVOYW1l", + "RHVwbGljYXRlZBCrxwESHgoYTXlob21lSW50ZXJwaG9uZU5vdEV4aXN0EKzH", + "ARIeChhNeWhvbWVTdGFydFBvaW50Tm90RXhpc3QQrccBEhwKFk15aG9tZUlu", + "dGVycGhvbmVFeGNlZWQQrscBEhwKFk15aG9tZVN0YXJ0UG9pbnRFeGNlZWQQ", + "r8cBEhsKFUNyYWZ0aW5nQ2xvdGhlc0V4Y2VlZBCwxwESGwoVQ3JhZnRpbmdD", + "b29raW5nRXhjZWVkELHHARIdChdDcmFmdGluZ0Z1cm5pdHVyZUV4Y2VlZBCy", + "xwESGQoTQW5jaG9ySXNOb3RJbk15aG9tZRCzxwESJgogRG9Ob3RSZW1vdmVQ", + "cm9jZXNzQ3JhZnRpbmdBbmNob3IQtMcBEhkKE0FuY2hvckd1aWREdXBsaWNh", + "dGUQtccBEhYKEE15aG9tZUlzRWRpdHRpbmcQtscBEhYKEE15aG9tZUlzT25S", + "ZW50YWwQt8cBEh8KGU15aG9tZVVnY0luZm9GaWxlTm90Rm91bmUQuMcBEiEK", + "G0VkaXRhYmxlUm9vbVNpemVUeXBlSW52YWxpZBC5xwESGAoSQ3JhZnRlckNv", + "dW50RXhjZWVkELrHARIbChVNaW5pbWFwTWFya2VyTm90Rm91bmQQkcsBEjEK", + "K01pbmltYXBNYXJrZXJEb2NMb2FkRHVwbGljYXRlZE1pbmltYXBNYXJrZXIQ", + "kssBEhwKFk1pbmltYXBNYXJrZXJEb2NJc051bGwQk8sBEhoKFENhcnRNZXRh", + "RGF0YU5vdEZvdW5kEIXPARIYChJDYXJ0TWF4Q291bnRFeGNlZWQQhs8BEhsK", + "FUNhcnRTdGFja0NvdW50SW52YWxpZBCHzwESHQoXQ2FydFN0YWNrQ291bnRO", + "b3RFbm91Z2gQiM8BEhYKEENhcnRJdGVtTm90Rm91bmQQic8BEiEKG0NhcnRO", + "b3RSZWdpc3RyeUN1cnJlbmN5VHlwZRCKzwESHQoXQ2FydEludmFsaWRDdXJy", + "ZW5jeVR5cGUQi88BEhMKDUNhcnREb2NJc051bGwQjM8BEhYKEENhcnREb2NF", + "eGNlcHRpb24Qjc8BEhgKEkNoYXRTZW5kU2VsZkZhaWxlZBD50gESGQoTQ2hh", + "dEludmFsaWRDaGF0VHlwZRD60gESIAoaQ2hhdEJsb2NrVXNlckNhbm5vdFdo", + "aXNwZXIQ+9IBEh4KGENoYXRJbnZhbGlkTWVzc2FnZUxlbmd0aBD80gESGAoS", + "Q2hhdEluY2x1ZGVCYW5Xb3JkEP3SARIZChNOb3RpY2VDaGF0RG9jSXNOdWxs", + "EO3WARIkCh5Fc2NhcGVQb3NpdGlvbk5vdEF2YWlsYWJsZVRpbWUQ4doBEh0K", + "F0VzY2FwZVBvc2l0aW9uRG9jSXNOdWxsEOLaARIYChJCbG9ja1VzZXJEb2NJ", + "c051bGwQ1d4BEh8KGUNoYXJhY3RlclByb2ZpbGVEb2NJc051bGwQyeIBEiAK", + "GkN1c3RvbURlZmluZWREYXRhRG9jSXNOdWxsEPXkARIeChhDdXN0b21EZWZp", + "bmVkVWlEb2NJc051bGwQveYBEhkKE0dhbWVPcHRpb25Eb2NJc051bGwQseoB", + "EhwKFkdhbWVPcHRpb25Eb2NFeGNlcHRpb24QsuoBEhQKDkxldmVsRG9jSXNO", + "dWxsEKXuARIXChFMb2NhdGlvbkRvY0lzTnVsbBCZ8gESFAoOTm90VXNhYmxl", + "UGxhY2UQmvIBEiEKG1JlZGlzTG9jYXRpb25DYWNoZVNldEZhaWxlZBCb8gES", + "FAoOTW9uZXlEb2NJc051bGwQgfoBEh8KGU1vbmV5Q29udHJvbE5vdEluaXRp", + "YWxpemUQgvoBEhQKDk1vbmV5Tm90RW5vdWdoEIP6ARIbChVNb25leU1heENv", + "dW50RXhjZWVkZWQQhPoBEh4KGEN1cnJlbmN5TWV0YURhdGFOb3RGb3VuZBCF", + "+gESJgogU2hvcFByb2R1Y3RUcmFkaW5nTWV0ZXJEb2NJc051bGwQ6YECEhYK", + "EFNob3BJc015SG9tZUl0ZW0Q6oECEhgKEkludmFsaWRTaG9wQnV5VHlwZRDr", + "gQISHQoXU2hvcFByb2R1Y3RDYW5ub3RSZW53YWwQ7IECEh4KGFNob3BQcm9k", + "dWN0Tm90UmVud2FsVGltZRDtgQISJwohU2hvcFByb2R1Y3RSZW5ld2FsQ291", + "bnRBbHJlYWR5TWF4EO6BAhIYChJTaG9wSXRlbUNhbm5vdFNlbGwQ74ECEhgK", + "ElJld2FyZEluZm9Ob3RFeGlzdBDQiQISFwoRUmV3YXJkSW52YWxpZFR5cGUQ", + "0YkCEhwKFlJld2FyZEludmFsaWRUeXBlVmFsdWUQ0okCEh4KGE5vdFJlcXVp", + "cmVBdHRyaWJ1dGVWYWx1ZRDTiQISLgooUmV3YXJkR3JvdXBJZFBhcnNpbmdG", + "cm9tU3RyaW5nVG9JbnRFcm9ychDUiQISFgoQQ2xhaW1JbnZhbGlkVHlwZRC4", + "kQISHQoXQ2xhaW1NZW1iZXJzaGlwTm90RXhpc3QQuZECEhcKEUNsYWltSW5m", + "b05vdEV4aXN0ELqRAhIeChhDbGFpbVJld2FyZE5vdEVub3VnaFRpbWUQu5EC", + "EhkKE0NsYWltUmV3YXJkRXZlbnRFbmQQvJECEhQKDkNsYWltRG9jSXNOdWxs", + "EL2RAhIaChRDcmFmdFJlY2lwZURvY0lzTnVsbBChmQISGAoSQ3JhZnRIZWxw", + "RG9jSXNOdWxsEKKZAhIUCg5DcmFmdERvY0lzTnVsbBCjmQISHgoYQ3JhZnRp", + "bmdNZXRhRGF0YU5vdEZvdW5kEKSZAhIXChFDcmFmdGluZ05vdEZpbmlzaBCl", + "mQISHwoZQ3JhZnRpbmdOb3RDcmFmdGluZ0FuY2hvchCmmQISHQoXQ3JhZnRp", + "bmdBbHJlYWR5Q3JhZnRpbmcQp5kCEh8KGUNyYWZ0aW5nQW5jaG9ySXNOb3RQ", + "bGFjZWQQqJkCEiEKG0NyYWZ0aW5nUmVjaXBlSXNOb3RSZWdpc3RlchCpmQIS", + "KAoiQ3JhZnRpbmdBbmNob3JJc05vdE1hdGNoV2l0aFJlY2lwZRCqmQISGwoV", + "Q3JhZnRpbmdIZWxwQ291bnRPdmVyEKuZAhIjCh1DcmFmdGluZ0hlbHBTYW1l", + "VXNlckNvdW50T3ZlchCsmQISIwodQ3JhZnRpbmdIZWxwUmVjZWl2ZWRDb3Vu", + "dE92ZXIQrZkCEhkKE0NyYWZ0SGVscERvY0lzRW1wdHkQrpkCEhUKD0NyYWZ0", + "RG9jSXNFbXB0eRCvmQISGwoVQ3JhZnRpbmdBbHJlYWR5RmluaXNoELCZAhIl", + "Ch9DcmFmdGluZ1JlY2lwZUlzQWxyZWFkeVJlZ2lzdGVyELGZAhIXChFDcmFm", + "dERvY0V4Y2VwdGlvbhCymQISGwoVQ3JhZnRIZWxwRG9jRXhjZXB0aW9uELOZ", + "AhIdChdDcmFmdFJlY2lwZURvY0V4Y2VwdGlvbhC0mQISHgoYQ3JhZnRJbnZh", + "bGlkUmVxdWVzdENvdW50ELWZAhIfChlVZ3FBcGlTZXJ2ZXJSZXF1ZXN0RmFp", + "bGVkEImhAhInCiFVZ3FBcGlTZXJ2ZXJDb252ZXJ0VG9PYmplY3RGYWlsZWQQ", + "iqECEisKJVVncUFwaVNlcnZlckludmFpbGRTZWFyY2hDYXRlZ29yeVR5cGUQ", + "i6ECEiAKGlVncVJlcG9ydEludmFsaWRUZXh0TGVuZ3RoEIyhAhIaChRVZ3FR", + "dWVzdE1ldGFOb3RFeGlzdBCNoQISHgoYVWdxQmVnaW5DcmVhdG9yUG9pbnRG", + "YWlsEI6hAhIYChJVZ3FRdWVzdFNodXRkb3duZWQQj6ECEiIKHFVncVRlc3RR", + "dWVzdEFscmVhZHlDb21wbGV0ZWQQkKECEjQKLlVncVJlYXNzaWduVXNpbmdJ", + "dGVtRXJyb3JDYXVzZVF1ZXN0Tm90Q29tcGxldGUQkaECEjUKL1VncVJlYXNz", + "aWduVXNpbmdJdGVtRXJyb3JDYXVzZVF1ZXN0QWxyZWFkeUV4aXN0EJKhAhI2", + "CjBVZ3FSZWFzc2lnblVzaW5nSXRlbUVycm9yQ2F1c2VOZXdSZXZpc2lvblVw", + "ZGF0ZWQQk6ECEiEKG1VncVF1ZXN0RGF0YUludmFsaWRSZXZpc2lvbhCUoQIS", + "JQofVWdxQWJvcnRDYW5ub3RDYXVzZUludmFsaWRTdGF0ZRCVoQISHgoYVWdx", + "TWV0YUdlbmVyYXRvck5vdEV4aXN0EJahAhIhChtVZ3FRdWVzdERhdGFSZXZp", + "c2lvblVwZGF0ZWQQl6ECEjMKLVVncVJldmlzaW9uQ2Fubm90U21hbGxlclRo", + "YW5SZXF1ZXN0ZWRSZXZpc2lvbhCYoQISHQoXVWdxUmV2aXNpb25TdGF0ZU5v", + "dExpdmUQmaECEiYKIFVncVJldmlzaW9uU3RhdGVPbmx5TGl2ZW5BbmRUZXN0", + "EJqhAhImCiBVZ3FBcGlTZXJ2ZXJIdHRwUmVxdWVzdEV4Y2VwdGlvbhCboQIS", + "HQoXVWdxUXVlc3RSZXZpc2lvbkNoYW5nZWQQnKECEh8KGVVncUFzc2lnbkNh", + "bm5vdE93bmVkUXVlc3QQnaECEiUKH1VncUFscmVhZHlPd25lZE9sZFJldmlz", + "aW9uUXVlc3QQnqECEhkKE1NlYXNvblBhc3NEb2NJc051bGwQ8agCEiAKGlNl", + "YXNvblBhc3NNZXRhRGF0YU5vdEZvdW5kEPKoAhImCiBTZWFzb25QYXNzUmV3", + "YXJkTWV0YURhdGFOb3RGb3VuZBDzqAISGAoSU2Vhc29uUGFzc01heEdyYWRl", + "EPSoAhIdChdTZWFzb25QYXNzTm90QWJsZVBlcmlvZBD1qAISIQobU2Vhc29u", + "UGFzc0FscmVhZHlCdXlDaGFyZ2VkEPaoAhIiChxTZWFzb25QYXNzQWxyZWFk", + "eVRha2VuUmV3YXJkEPeoAhIeChhTZWFzb25QYXNzTm90RW5vdWdoR3JhZGUQ", + "+KgCEh8KGVNlYXNvblBhc3NOZWVkQ2hhcmdlZFBhc3MQ+agCEhoKFFNlYXNv", + "blBhc3NJbnZhbGlkRXhwEPqoAhIcChZTZWFzb25QYXNzRG9jRXhjZXB0aW9u", + "EPuoAhIkCh5MYW5kQXVjdGlvblJlZGlzQ2FjaGVTZXRGYWlsZWQQ2bACEhkK", + "E0xhbmRBdWN0aW9uTm90Rm91bmQQ2rACEiUKH0xhbmRBdWN0aW9uSW52YWxp", + "ZEF1Y3Rpb25OdW1iZXIQ27ACEicKIUxhbmRBdWN0aW9uQmlkQ3VycmVuY3lU", + "eXBlSW52YWxpZBC06gESIgocTGFuZEF1Y3Rpb25CaWRQcmljZU5vdEVub3Vn", + "aBDdsAISIgocTGFuZEF1Y3Rpb25FbXB0eUNhY2hlSW5SZWRpcxDesAISJAoe", + "TGFuZEF1Y3Rpb25SZWdpc3RyeUluZm9JbnZhbGlkEN+wAhIbChVMYW5kQXVj", + "dGlvbk5vdFN0YXJ0ZWQQ4LACEioKJExhbmRBdWN0aW9uTWlzbWF0Y2hCZXR3", + "ZWVuQ2FjaGVBbmREYhDhsAISHwoZTGFuZEF1Y3Rpb25BbHJlYWR5U3RhcnRl", + "ZBDisAISHwoZTGFuZEF1Y3Rpb25MYW5kSXRlbU5vdFNldBDjsAISIgocTGFu", + "ZEF1Y3Rpb25FZGl0b3JUeXBlSW52YWxpZBDksAISHQoXTGFuZEF1Y3Rpb25B", + "bHJlYWR5RW5kZWQQ5bACEioKJExhbmRBdWN0aW9uSGlnaGVzdEJpZFVzZXJB", + "dHRyaWJFcnJvchDmsAISKgokTGFuZEF1Y3Rpb25SZWZ1bmRCaWRQcmljZUF0", + "dHJpYkVycm9yEOewAhIwCipMYW5kQXVjdGlvbkJpZGRlclJlZnVuZEJpZFBy", + "aWNlQXR0cmliRXJyb3IQ6LACEiAKGkdhbWVDb25maWdNZXRhRGF0YU5vdEZv", + "dW5kEMG4AhIqCiRBdHRyaWJ1dGVEZWZpbmVpdGlvbk1ldGFEYXRhTm90Rm91", + "bmQQwrgCEiEKG1JlcXVpcmVtZW50TWV0YURhdGFOb3RGb3VuZBDDuAISIAoa", + "U3lzdGVtTWFpbE1ldGFEYXRhTm90Rm91bmQQxLgCEh4KGEludGVyaW9yTWV0", + "YURhdGFOb3RGb3VuZBDFuAISHwoZUHJvcEdyb3VwTWV0YURhdGFOb3RGb3Vu", + "ZBDGuAISFwoRQWlDaGF0U2VydmVyU3RhcnQQqcACEhsKFUFpQ2hhdFNlcnZl", + "clJlcUZhaWxlZBCqwAISHAoWQWlDaGF0U2VydmVyQmFkcmVxdWVzdBCrwAIS", + "GwoVQWlDaGF0U2VydmVyRm9yYmlkZGVuEKzAAhIeChhBaUNoYXRTZXJ2ZXJV", + "bmF1dGhvcml6ZWQQrcACEh4KGEFpQ2hhdFNlcnZlclVzZXJOb3RGb3VuZBCu", + "wAISIwodQWlDaGF0U2VydmVyQ2hhcmFjdGVyTm90Rm91bmQQr8ACEiIKHEFp", + "Q2hhdFNlcnZlclJlYWN0aW9uTm90Rm91bmQQsMACEigKIkFpQ2hhdFNlcnZl", + "ckV4YW1wbGVEaWFsb25nTm90Rm91bmQQscACEiEKG0FpQ2hhdFNlcnZlclNl", + "c3Npb25Ob3RGb3VuZBCywAISHwoZQWlDaGF0U2VydmVyTW9kZWxOb3RGb3Vu", + "ZBCzwAISIAoaQWlDaGF0U2VydmVyTm90RW5vdWdoUG9pbnQQtMACEiMKHUFp", + "Q2hhdFNlcnZlckludmFsaWRQYXJhbWV0ZXJzELXAAhImCiBBaUNoYXRTZXJ2", + "ZXJTZXNzaW9uTGltaXRFeGNlZWRlZBC2wAISIgocQWlDaGF0U2VydmVySW52", + "YWxpZFNpZ25hdHVyZRC3wAISIwodQWlDaGF0U2VydmVyVXNlckFscmVhZHlF", + "eGlzdHMQuMACEh4KGEFpQ2hhdFNlcnZlclJlbW92ZUZhaWxlZBC5wAISHwoZ", + "QWlDaGF0U2VydmVyRHVwbGljYXRlR3VpZBC6wAISHQoXQWlDaGF0U2VydmVy", + "RHVwbGljYXRlSWQQu8ACEh4KGEFpQ2hhdFNlcnZlckNoYXROb3RGb3VuZBC8", + "wAISHgoYQWlDaGF0U2VydmVyVG9rZW5FeHBpcmVkEL3AAhIgChpBaUNoYXRT", + "ZXJ2ZXJJbnRlcm5hbFNlcnZlchC+wAISIAoaQWlDaGF0U2VydmVySW52YWxp", + "ZE1lc3NhZ2UQv8ACEiEKG0FpQ2hhdFNlcnZlclVzZXJPbmx5RmVhdHVyZRDA", + "wAISIAoaQWlDaGF0U2VydmVyT3duZXJzaGlwRXJyb3IQwcACEioKJEFpQ2hh", + "dFNlcnZlckNoYXJnZU9yZGVyTm90Rm91bmRFcnJvchDCwAISFQoPQWlDaGF0", + "U2VydmVyRW5kEIzBAhIiChxBaUNoYXRTZXJ2ZXJSZXRyeUNoYXJnZVBvaW50", + "EI3BAhIaChRBaUNoYXRTZXJ2ZXJJbmFjdGl2ZRCOwQISGAoSQWlDaGF0RG9j", + "RXhjZXB0aW9uEI/BAhIPCglOcGNJc0J1c3kQkcgCEhcKEUxhY2tPZkRhaWx5", + "Q2FsaXVtEPnPAhIXChFMYWNrT2ZUb3RhbENhbGl1bRD6zwISHgoYTGFja09m", + "Q29tbWlzc2lvbkN1cnJlbmN5EPvPAhIfChlMYWNrT2ZDb21taXNzaW9uTWF0", + "ZXJpYWxzEPzPAhIbChVJbnZhbGlkTWF0ZXJpYWxTbG90SWQQ/c8CEhYKEEZh", + "aWxUb0xvYWRDYWxpdW0Q/s8CEhwKFkZhaWxUb1NhdmVDYWxpdW1EeW5hbW8Q", + "/88CEhkKE0xhY2tPZkNvbnZlcnRDYWxpdW0Qq9ACEh8KGUdldEZhaWxFY2hv", + "U3lzdGVtUmVzcG9uc2UQ3NACEiIKHEZhaWxUb0dldEVjaG9TeXN0ZW1IdHRw", + "RXJyb3IQ3dACEiQKHkZhaWxUb0dldEVjaG9TeXN0ZW1NZXNzYWdlTnVsbBDe", + "0AISIgocRmFpbFRvR2V0RWNob1N5c3RlbUV4Y2VwdGlvbhDf0AISGgoURmFp", + "bFRvU2VuZEVjaG9TeXN0ZW0Q4NACEh8KGUZhaWxUb0dldEVjaG9TeXN0ZW1S", + "b2xsVXAQ4dACEh0KF0FjY291bnRMb2dpbkJsb2NrRW5hYmxlENGGAxIbChVJ", + "bnN0YW5jZVJvb21FeGNlcHRpb24QuY4DEiYKIEluc3RhbmNlUm9vbUNhbm5v", + "dFdyaXRlRXh0cmFJbmZvELqOAxImCiBJbnN0YW5jZVJvb21Ob3RDaGFyZ2Vk", + "U2Vhc29uUGFzcxC7jgMSHgoYSW5zdGFuY2VNZXRhRGF0YU5vdEZvdW5kELyO", + "AxIkCh5JbnN0YW5jZU1ldGFEYXRhT3ZlckxpbWl0V3JvbmcQvY4DEh8KGUlu", + "c3RhbmNlQWNjZXNzVHlwZUludmFsaWQQvo4DEiEKG0luc3RhbmNlQWNjZXNz", + "SXRlbU5vdEVub3VnaBC/jgMSKAoiSW5zdGFuY2VBY2Nlc3NTZWFzb25QYXNz", + "Tm90Q2hhcmdlZBDAjgMSGAoSSW5zdGFuY2VSb29tSXNGdWxsEMGOAxIeChhJ", + "bnN0YW5jZVJvb21JZER1cGxpY2F0ZWQQwo4DEiEKG0luc3RhbmNlUm9vbU5v", + "dEV4aXN0QXRSZWRpcxDDjgMSHgoYVGFza1Jlc2VydmF0aW9uRG9jSXNOdWxs", + "EKGWAxIiChxCaWxsaW5nR2V0UHVyY2hhc2VJbmZvRmFpbGVkEImeAxIeChhC", + "aWxsaW5nVXBkYXRlU3RhdGVGYWlsZWQQip4DEh0KF0JpbGxpbmdJbnZhbGlk", + "U3RhdGVUeXBlEIueAxIpCiNCaWxsaW5nRmFpbGVkUGFyc2VQcm9kdWN0TWV0", + "YUlkVHlwZRCMngMSHQoXQmlsbGluZ1N0YXRlVHlwZUludmFsaWQQjZ4DEiMK", + "HUJpbGxpbmdTdGF0ZVR5cGVDYW50QmVDaGFuZ2VkEI6eAxIcChZCaWxsaW5n", + "U3RhdGVUeXBlUmVmdW5kEI+eAxIkCh5CaWxsaW5nU3RhdGVUeXBlUmVmdW5k", + "Q29tcGxldGUQkJ4DEhoKFFRheGlNZXRhRGF0YU5vdEZvdW5kEPGlAxIVCg9U", + "YXhpVHlwZUludmFsaWQQ8qUDEhoKFFdhcnBNZXRhRGF0YU5vdEZvdW5kEPOl", + "AxIVCg9XYXJwVHlwZUludmFsaWQQ9KUDEhQKDlJlbnRhbE5vdEZvdW5kENmt", + "AxIjCh1SZW50YWxEb2NMb2FkRHVwbGljYXRlZFJlbnRhbBDarQMSFQoPUmVu", + "dGFsRG9jSXNOdWxsENutAxIcChZSZW50YWxOb3RBdmFpbGFibGVMYW5kENyt", + "AxIaChRSZW50YWxBZGRyZXNzSW52YWxpZBDdrQMSHQoXUmVudGFsQWRkcmVz", + "c0lzTm90RW1wdHkQ3q0DEh8KGVJlbnRhbGZlZU1ldGFEYXRhTm90Rm91bmQQ", + "360DEh4KGFJlbnRhbENvbnRyYWN0SW5mb1VwZGF0ZRDgrQMSIgocUmVudGFs", + "Q3VycmVuY3lBbW91bnRJc1Rvb0xvdxDJtQMSHwoZUmVudGFsQ3VycmVuY3lU", + "eXBlSXNXcm9uZxDKtQMSKAoiUGFja2FnZUxhc3RPcmRlclJlY29kZURvY0V4", + "Y2VwdGlvbhDBtQMSHwoZUGFja2FnZVJlcGVhdERvY0V4Y2VwdGlvbhDCtQMS", + "GQoTQmVhY29uU2hvcEV4Y2VwdGlvbhCpvQMSHwoZQmVhY29uU2hvcEludmFs", + "aWRBcmd1bWVudBCqvQMSJwohQmVhY29uU2hvcEJlYWNvbklzTm90SW5SZW50", + "YWxIb21lEKu9AxIcChZCZWFjb25TaG9wTm90Rm91bmRJdGVtEKy9AxIdChdC", + "ZWFjb25TaG9wTm90RW5vdWdoSXRlbRCtvQMSIgocQmVhY29uU2hvcEludmFs", + "aWRJdGVtRm9yU2VsbBCuvQMSIAoaQmVhY29uU2hvcE5vdEZvdW5kTWV0YURh", + "dGEQr70DEh8KGUJlYWNvblNob3BMb3dTZWxsaW5nUHJpY2UQsL0DEiQKHkJl", + "YWNvblNob3BOb3RFbm91Z2hSZWdpc3RlckZlZRCxvQMSGgoUQmVhY29uU2hv", + "cFNsb3RJc0Z1bGwQsr0DEicKIUJlYWNvblNob3BPdmVyT25lRGF5UmVnaXN0", + "ZXJMaW1pdBCzvQMSHgoYQmVhY29uU2hvcEZhaWxlZFRvQ3JlYXRlELS9AxIj", + "Ch1CZWFjb25TaG9wRmFpbGVkUmVnaXN0ZXJCb2FyZBC1vQMSIQobQmVhY29u", + "U2hvcEZhaWxlZERlbGV0ZUJvYXJkELa9AxIlCh9CZWFjb25TaG9wTm90Rm91", + "bmRJdGVtRnJvbUJvYXJkELe9AxIgChpCZWFjb25TaG9wUHJvZmlsZUV4Y2Vw", + "dGlvbhC4vQMSJQofQmVhY29uU2hvcE5vdEVub3VnaFJlZ2lzdGVyR29sZBC5", + "vQMSIAoaQmVhY29uU2hvcExhY2tPZkl0ZW1BbW91bnQQur0DEiIKHEJlYWNv", + "blNob3BGYWlsZWRHZXRCb2FyZEl0ZW0Qu70DEiMKHUJlYWNvblNob3BTb2xk", + "UmVjb3JkRXhjZXB0aW9uELy9AxIrCiVCZWFjb25TaG9wRmFpbGVkUmVsb2Fk", + "QmVhY29uU2hvcEludmVuEL29AxIdChdCZWFjb25TaG9wVXBkYXRlTmV3RGF0", + "YRC+vQMSJgogQmVhY29uU2hvcEZhaWxlZFVwZGF0ZURhdGFGcm9tRGIQv70D", + "EiMKHUJlYWNvblNob3BOb3RGb3VuZFNvbGRSZWNvcmRzEMC9AxIhChtCZWFj", + "b25TaG9wUHJvZmlsZURvY0lzRW1wdHkQwb0DEiIKHEJlYWNvblNob3BTb2xk", + "UHJpY2VFeGNlcHRpb24Qwr0DEiEKG0JlYWNvblNob3BOb3RGb3VuZFNvbGRQ", + "cmljZRDDvQMSJAoeQmVhY29uU2hvcEZhaWxlZFRvRmluZE9yVXBkYXRlEMS9", + "AxIbChVCZWFjb25TaG9wRGJFeGNlcHRpb24Qxb0DEiAKGkJlYWNvblNob3BP", + "dmVyU2VsbGluZ1ByaWNlEMa9AxIiChxCZWFjb25TaG9wT3ZlclJlbnRhbFNh", + "ZmVUaW1lEMe9AxIjCh1CZWFjb25TaG9wRGVhY3RpdmVJdGVtRm9yU2VsbBDI", + "vQMSGgoUVWdxSW52YWxpZFRhc2tBY3Rpb24Q4dQDEhsKFVVncVRhc2tBY3Rp", + "b25EaXNhYmxlZBDi1AMSGgoUVWdxSW52YWxpZERpYWxvZ1R5cGUQ49QDEh8K", + "GVVncUludmFsaWREaWFsb2dDb25kaXRpb24Q5NQDEhgKElVncVZhbGlkYXRp", + "b25FcnJvchDl1AMSEwoNVWdxTnVsbEVudGl0eRDm1AMSGQoTVWdxU3RhdGVD", + "aGFuZ2VFcnJvchDn1AMSGwoVVWdxTm90RW5vdWdoUXVlc3RTbG90EOjUAxIV", + "Cg9VZ3FOb3RBbGxvd0VkaXQQ6dQDEhoKFFVncURpYWxvZ0lzTm90SW5UYXNr", + "EOrUAxIVCg9VZ3FSZXF1aXJlSW1hZ2UQ69QDEhYKEFVncVJlcXVpcmVCZWFj", + "b24Q7NQDEhkKE1VncUJlYWNvbklucHV0RXJyb3IQ7dQDEhoKFFVncUdhbWVE", + "QkFjY2Vzc0Vycm9yEO7UAxIVCg9VZ3FOb3RPd25VZ2NOcGMQ79QDEh0KF1Vn", + "cUFscmVhZHlFeGlzdHNBY2NvdW50EPDUAxIYChJVZ3FTZXJ2ZXJFeGNlcHRp", + "b24Q8dQDEh4KGFVncUludmFsaWRXZWJQb3J0YWxUb2tlbhDy1AMSFQoPVWdx", + "SW52YWxpZFRva2VuEPPUAxIXChFVZ3FSZXF1aXJlQWNjb3VudBD01AMSHgoY", + "VWdxTm90RW5vdWdoQ3JlYXRvclBvaW50EPXUAxISCgxVZ3FTbG90TGltaXQQ", + "9tQDEh8KGVVncUV4Y2VlZFRyYW5zYWN0aW9uUmV0cnkQ99QDEhgKElVncU1l", + "dGF2ZXJzZU9ubGluZRD41AMSFAoOVWdxQXV0aFJlbW92ZWQQ+dQDEhsKFVVn", + "cUludmFsaWRQcmVzZXRJbWFnZRD61AMSGgoUVWdxTm90RW5vdWdoQ3VycmVu", + "Y3kQ+9QDEhYKEFVncUN1cnJlbmN5RXJyb3IQ/NQDEiIKHFVncUFscmVhZHlF", + "eGlzdHNSZXNlcnZlR3JhZGUQ/dQDEhsKFVVncUludmFsaWRSZXNlcnZlVGlt", + "ZRD+1AMSGQoTVWdxU2FtZUdyYWRlUmVzZXJ2ZRD/1AMSGgoUVWdxQWxyZWFk", + "eUJvb2ttYXJrZWQQydwDEhYKEFVncU5vdEJvb2ttYXJrZWQQytwDEhUKD1Vn", + "cUFscmVhZHlMaWtlZBDL3AMSEQoLVWdxTm90TGlrZWQQzNwDEhgKElVncUFs", + "cmVhZHlSZXBvcnRlZBDN3AMSFAoOVWdxTm90T3duUXVlc3QQztwDEhUKD1Vn", + "cUludmFsaWRTdGF0ZRDP3AMSHQoXTmZ0Rm9yT3duZXJBbGxHZXRGYWlsZWQQ", + "seQDEhcKEU1xUmVzcG9uc2VUaW1lb3V0EJnsAxIZChNJbnRlcm5hbFNlcnZl", + "ckVycm9yEPGiBBIOCghSZGJFcnJvchDyogQSEQoLRHluYW1vRXJyb3IQ86IE", + "EhQKDkludmFsaWRSZXF1ZXN0EPuiBBIWChBQbGFuZXRJZE5vdEZvdW5kENmq", + "BBIjCh1QbGFuZXRTZWNyZXRLZXlEb2VzTm90TWF0Y2hlZBDaqgQSFgoQSW52", + "YWxpZFBsYW5ldEp3dBDbqgQSFgoQRXhwaXJlZFBsYW5ldEp3dBDcqgQSIAoa", + "TWV0YXZlcnNlQ2xpZW50T25Db25uZWN0ZWQQoawEEhQKDkludmFsaWRVc2Vy", + "Snd0EKKsBBIUCg5FeHBpcmVkVXNlckp3dBCjrAQSFQoPQWNjb3VudE5vdEZv", + "dW5kEIWtBBISCgxVc2VyTm90Rm91bmQQhq0EEh0KF0V4Y2hhbmdlT3JkZXJJ", + "ZE5vdEZvdW5kEMGyBBIqCiRFeGNoYW5nZVRvdGFsT3JkZXJEYWlseUxpbWl0", + "RXhjZWVkZWQQwrIEEikKI0V4Y2hhbmdlVXNlck9yZGVyRGFpbHlMaW1pdEV4", + "Y2VlZGVkEMOyBBIVCg9Cb3RQbGF5ZXJJc051bGwQ0ds6EhwKFkNsaWVudFRv", + "TG9naW5SZXNJc051bGwQ0ts6EiAKGkNsaWVudFRvTG9naW5NZXNzYWdlSXNO", + "dWxsENPbOhIbChVDbGllbnRUb0dhbWVSZXNJc051bGwQ1Ns6Eh8KGUNsaWVu", + "dFRvR2FtZU1lc3NhZ2VJc051bGwQ1ds6EhsKFUNvbm5lY3RlZFRvU2VydmVy", + "RmFpbBDW2zoSGgoUU2NlbmFyaW9uUGFyYW1Jc051bGwQ19s6EiEKGkJhdHRs", + "ZVJvb21Db250ZW50c1R5cGVPbmx5EMGxnwUSHgoXQmF0dGxlSW5zdGFuY2VU", + "eXBlRXJyb3IQwrGfBRIlCh5CYXR0bGVJbnN0YW5jZUluZm9BbHJlYWR5RXhp", + "c3QQw7GfBRIhChpCYXR0bGVJbnN0YW5jZUluZm9Ob3RFeGlzdBDEsZ8FEiQK", + "HUJhdHRsZUluc3RhbmNlSm9pblBsYXllckVycm9yEMWxnwUSLQomQmF0dGxl", + "SW5zdGFuY2VVc2FibGVTcGF3blBvaW50Tm90RXhpc3QQxrGfBRIdChZCYXR0", + "bGVJbnN0YW5jZUluQWN0aXZlEMexnwUSJAodQmF0dGxlSW5zdGFuY2VOb3RF", + "eGlzdEFuY2hvcnMQyLGfBRIlCh5CYXR0bGVJbnN0YW5jZUFkZFBvZENvbWJh", + "dEZhaWwQybGfBRInCiBCYXR0bGVJbnN0YW5jZU9iamVjdE1ldGFOb3RFeGlz", + "dBDKsZ8FEjAKKUJhdHRsZUluc3RhbmNlT2JqZWN0SW50ZXJhY3Rpb25Ob3R5", + "ZXRUaW1lEMuxnwUSIwocQmF0dGxlSW5zdGFuY2VPYmplY3ROb3RFeGlzdBDM", + "sZ8FEisKJEJhdHRsZUluc3RhbmNlUG9kQ29tYmF0QWxyZWFkeU9jY3VweRDN", + "sZ8FEi8KKEJhdHRsZUluc3RhbmNlT2JqZWN0SW50ZXJhY3Rpb25Ob3RBY3Rp", + "dmUQz7GfBRIrCiRCYXR0bGVJbnN0YW5jZU1ldGFDb25maWdOb3RFeGlzdERh", + "dGEQ0LGfBRIrCiRCYXR0bGVJbnN0YW5jZU1ldGFSZXdhcmROb3RFeGlzdERh", + "dGEQ0bGfBRIzCixCYXR0bGVJbnN0YW5jZVBpY2t1cFBvZEdlbmVyYXRlZFRp", + "bWVOb3RFeGlzdBDSsZ8FEioKI0JhdHRsZUluc3RhbmNlUGlja3VwUG9kTm90", + "RXhpc3REYXRhENOxnwUSJwogQmF0dGxlSW5zdGFuY2VOb3RFeGlzdFBsYXll", + "ckluZm8Q1LGfBRIkCh1CYXR0bGVJbnN0YW5jZUludGVyYWN0aW9uRmFpbBDV", + "sZ8FEjEKKkJhdHRsZUluc3RhbmNlUGlja3VwUG9kUmV3YXJkQWxsb2NhdGVF", + "cnJvchDWsZ8FEiYKH0JhdHRsZUluc3RhbmNlTm90RXhpc3RFdmVudEluZm8Q", + "17GfBRIgChlCYXR0bGVJbnN0YW5jZUNsb3NpbmdUaW1lENixnwUSIgobQmF0", + "dGxlSW5zdGFuY2VTZXFQYXJzZUVycm9yENmxnwUSIwocQmF0dGxlSW5zdGFu", + "Y2VFdmVudElkSW52YWxpZBDasZ8FEiIKG0dhbWVNb2RlSm9pbkhhbmRsZXJO", + "b3RFeGlzdBCksp8FEiIKG0dhbWVNb2RlSW5pdEhhbmRsZXJOb3RFeGlzdBCl", + "sp8FEikKIkdhbWVNb2RlSm9pblN1Y2Nlc3NIYW5kbGVyTm90RXhpc3QQprKf", + "BRIZChJHYW1lTW9kZUNyZWF0ZUZhaWwQp7KfBRIaChNHYW1lTW9kZUNsYXNz", + "SXNOdWxsEKiynwUSGwoUR2FtZU1vZGVBbHJlYWR5RXhpc3QQqbKfBRIgChlH", + "YW1lTW9kZUludmFsaWRBbmNob3JHdWlkEKqynwUSHAoVR2FtZU1vZGVJbnZh", + "bGlkQWN0aW9uEKuynwUSMwosR2FtZU1vZGVQcmVwYXJhdGlvbkZvckxlYXZp", + "bmdIYW5kbGVyTm90RXhpc3QQrLKfBRIjChxHYW1lTW9kZUxlYXZlSGFuZGxl", + "ck5vdEV4aXN0EK2ynwUSHwoYR2FtZU1vZGVSdW5SYW5rVXNlckV4aXN0EK6y", + "nwUSFwoQTWF0Y2hDb21tb25FcnJvchCBttwFEhgKEU1hdGNoVXNlck5vdEZv", + "dW5kEIucnDkSHwoYTWF0Y2hVc2VyUmVzZXJ2ZWRBbHJlYWR5EIycnDkSGwoU", + "TWF0Y2hVc2VyTm90UmVzZXJ2ZWQQjZycORIdChZNYXRjaFVzZXJHcm91cE5v", + "dEZvdW5kEI6cnDkSGQoSTWF0Y2hUYXNrRXhjZXB0aW9uEJWcnDkSHAoVTWF0", + "Y2hTZXJ2ZXJOb1Jlc3BvbnNlEOa93AUSEQoKTWF0Y2hFcnJvchDnvdwFEioK", + "I1NlcnZlck1ldHJpY3NUcmlnZ2VySGFuZGxlck5vdEZvdW5kEP261y8SDAoI", + "RHVwTG9naW4QARIKCgZNb3ZpbmcQAhILCgdEYkVycm9yEAMSDAoIS2lja0Zh", + "aWwQBBIWChJOb3RDb3JyZWN0UGFzc3dvcmQQBRIQCgxOb3RGb3VuZFVzZXIQ", + "BhIQCgxOb0dhbWVTZXJ2ZXIQBxIQCgxMb2dpblBlbmRpbmcQCBISCg5Ob3RJ", + "bXBsZW1lbnRlZBAJEh0KGU5vdEV4aXN0U2VsZWN0ZWRDaGFyYWN0ZXIQChIV", + "ChFOb3RFeGlzdENoYXJhY3RlchALEhQKEFNlcnZlckxvZ2ljRXJyb3IQDBIR", + "Cg1Ob1Blcm1pc3Npb25zEA4SDQoJUmVkaXNGYWlsEA8SDgoJTG9naW5GYWls", + "EOgHEhMKDkR1cGxpY2F0ZWRVc2VyEOkHEhEKDEludmFsaWRUb2tlbhDqBxIV", + "ChBOb3RDb3JyZWN0U2VydmVyEOsHEg8KCkluc3BlY3Rpb24Q7AcSDgoJQmxh", + "Y2tMaXN0EO0HEg8KClNlcnZlckZ1bGwQ7gcSEwoOTm90Rm91bmRTZXJ2ZXIQ", + "7wcSEgoNTm90Rm91bmRUYWJsZRDwBxIPCgpUYWJsZUVycm9yEPEHEhUKEElu", + "dmFsaWRDb25kaXRpb24QzAgSEwoOQ2hhckNyZWF0ZUZhaWwQ0A8SEwoOQ2hh", + "clNlbGVjdEZhaWwQtBASEwoOQ3JlYXRlUm9vbUZhaWwQuBcSEQoMSm9pblJv", + "b21GYWlsEJwYEhUKEEpvaW5JbnN0YW5jZUZhaWwQgBkSGwoWTm90RXhpc3RJ", + "bnN0YW5jZVRpY2tldBCBGRIWChFMZWF2ZUluc3RhbmNlRmFpbBDkGRIZChRO", + "b3RFeGlzdEluc3RhbmNlUm9vbRDIGhIbChZOb3RDb3JyZWN0SW5zdGFuY2VS", + "b29tEMkaEg8KClBvc0lzRW1wdHkQyhoSFAoPRW50ZXJNeUhvbWVGYWlsENgd", + "EhQKD0xlYXZlTXlIb21lRmFpbBC8HhIXChJFeGNoYW5nZU15SG9tZUZhaWwQ", + "oB8SFwoSTm90Rm91bmRNeUhvbWVEYXRhEKEfEhMKDk5vdE15SG9tZU93bmVy", + "EKIfEhYKEUFscmVhZHlIYXZlTXlIb21lEKMfEhsKFkV4Y2hhbmdlTXlIb21l", + "UHJvcEZhaWwQhCASGQoURXhjaGFuZ2VCdWlsZGluZ0ZhaWwQzCESHwoaRXhj", + "aGFuZ2VCdWlsZGluZ0xGUHJvcEZhaWwQsCISGQoURXhjaGFuZ2VJbnN0YW5j", + "ZUZhaWwQlCMSIQocRXhjaGFuZ2VTb2NpYWxBY3Rpb25TbG90RmFpbBD4IxId", + "ChhOb3RGb3VuZFNvY2lhbEFjdGlvbkRhdGEQ+iMSGgoVTm90SW5Tb2NpYWxB", + "Y3Rpb25TbG90EPsjEhwKF0FscmVhZHlIYXZlU29jaWFsQWN0aW9uEPwjEhkK", + "FE5vdEZvdW5kQnVpbGRpbmdEYXRhEKwkEhYKEU5vdEZvdW5kRmxvb3JJbmZv", + "EK0kEhYKEU5vdEZvdW5kSW5kdW5EYXRhEK8kEhkKFEVudGVyRml0dGluZ1Jv", + "b21GYWlsENwkEhoKFU5vdEVudGV0ZWRGaXR0aW5nUm9vbRDdJBIQCgtNYWtl", + "RmFpbE90cBDeJBIdChhOb3RFeGlzdFJvb21JbmZvRm9yRW50ZXIQ3yQSHwoa", + "Tm90RXhpc3RHYW1lU2VydmVyRm9yRW50ZXIQ4CQSFQoQUG9zaXRpb25TYXZl", + "RmFpbBDhJBIcChdFeGNoYW5nZUVtb3Rpb25TbG90RmFpbBDAJRIaChVFbW90", + "aW9uU2xvdE91dE9mUmFuZ2UQwSUSHAoXTm90Rm91bmRBbmNob3JHdWlkSW5N", + "YXAQoyYSFwoSTm90Rm91bmRBbmNob3JHdWlkEKQmEg8KClByb3BJc1VzZWQQ", + "piYSFAoPUHJvcFR5cGVpc1dyb25nEKcmEhgKE05vdEZvdW5kRW1vdGlvbkRh", + "dGEQuCYSFQoQTm90SW5FbW90aW9uU2xvdBC5JhITCg5DcmVhdGVJdGVtRmFp", + "bBCEJxIQCgtBZGRJdGVtRmFpbBCFJxITCg5EZWxldGVJdGVtRmFpbBCGJxIS", + "Cg1Ob01vcmVBZGRJdGVtEIcnEhEKDE5vdEZvdW5kSXRlbRCIJxISCg1Ob3RF", + "bm91Z2hJdGVtEIknEhUKEEludmFsaWRTbG90SW5kZXgQiicSFwoSRHVwbGlj", + "YXRlZEl0ZW1HdWlkEIsnEhgKE05vdEZvdW5kSXRlbVRhYmxlSWQQjCcSFAoP", + "Tm90U2VsZWN0ZWRDaGFyEI0nEhAKC05vdEZvdW5kTWFwEI4nEhEKDE5vdEVt", + "cHR5U2xvdBCPJxIOCglFbXB0eVNsb3QQkCcSGAoTTm90Rm91bmRCdWZmVGFi", + "bGVJZBCRJxIRCgxOb3RGb3VuZEJ1ZmYQkicSHAoXTm90RXhpc3RGb3JlY2Vk", + "TW92ZUluZm8QkycSFwoSRHVwbGljYXRlZE5pY2tOYW1lEJUnEhcKEkFscmVh", + "ZHlTZXROaWNrTmFtZRCWJxIZChREaXNhbGxvd2VkQ2hhcmFjdGVycxCXJxIT", + "Cg5EYlVwZGF0ZUZhaWxlZBCYJxIUCg9JbnZhbGlkQXJndW1lbnQQmScSFAoP", + "TWFpbFN5c3RlbUVycm9yEKYnEhIKDUludmFsaWRUYXJnZXQQpycSEQoMTm90", + "Rm91bmRNYWlsEKgnEhQKD0VtcHR5SXRlbUluTWFpbBCpJxIWChFNYWlsU2Vu", + "ZENvdW50T3ZlchCqJxIUCg9DaGFuZ2VkTmlja05hbWUQqycSFgoRU3RhdGVD", + "aGFuZ2VGYWlsZWQQsCcSEwoOTm90Rm91bmRUYXJnZXQQuicSFgoRQmxvY2tl", + "ZEZyb21UYXJnZXQQuycSEQoMTG9nT2ZmVGFyZ2V0ELwnEhMKDkNhbnRTZW5k", + "VG9TZWxmEL0nEhQKD0J1ZmZUeXBlSXNXcm9uZxDOJxIZChRSZWdpc3RlclRv", + "b2xTbG90RmFpbBDYJxIbChZEZXJlZ2lzdGVyVG9vbFNsb3RGYWlsENknEhcK", + "ElRvb2xTbG90T3V0T2ZSYW5nZRDaJxIVChBOb3RGb3VuZFRvb2xTbG90ENsn", + "EhIKDUVtcHR5VG9vbFNsb3QQ3CcSHQoYRm9sZGVyTmFtZUV4Y2VlZGVkTGVu", + "Z3RoEOInEhsKFkZvbGRlck5hbWVBbHJlYWR5RXhpc3QQ4ycSFwoSRm9sZGVy", + "TmFtZU5vdEV4aXN0EOQnEiIKHUZvbGRlck5hbWVBbHJlYWR5TWF4SG9sZENv", + "dW50EOUnEhYKEUZvbGRlckNvdW50RXhjZWVkEOYnEhUKEEZvbGRlckNyZWF0", + "ZUZhaWwQ5ycSHgoZRm9sZGVyUmVOYW1lQ2Fubm90RGVmYXVsdBDoJxIaChVG", + "b2xkZXJPZGVydHlwZUludmFsaWQQ6ScSHgoZRnJpZW5kUmVxdWVzdE5vdEV4", + "aXN0SW5mbxDsJxIdChhGcmllbmRSZXF1ZXN0QWxyZWFkeVNlbmQQ7ScSIAob", + "RnJpZW5kUmVxdWVzdEFscmVhZHlSZWNlaXZlEO4nEiAKG0ZyaWVuZFJlcXVl", + "c3RDYW50U2VuZFRvU2VsZhDvJxImCiFGcmllbmRSZXF1ZXN0Tm90RXhpc3RS", + "ZWNlaXZlZEluZm8Q8CcSHwoaRnJpZW5kUmVxdWVzdEFscmVhZHlGcmllbmQQ", + "8ScSEAoLSW52YWxpZFR5cGUQ8icSGQoUSW52YWxpZEF0dHJpYnV0ZVNsb3QQ", + "8ycSJQogRnJpZW5kUmVxdWVzdENhbm5vdFNlbmRCbG9ja1VzZXIQ/ycSHgoZ", + "QWRkRnJpZW5kQWxyZWFkeUJsb2NrVXNlchCAKBIfChpBZGRGcmllbmROb3RF", + "eGlzdENoYXJhY3RlchCBKBIbChZBZGRGcmllbmRBbHJlYWR5RnJpZW5kEIIo", + "EiIKHUFkZEZyaWVuZEFscmVhZHlDYW5jZWxSZXF1ZXN0EIMoEhwKF0FkZEZy", + "aWVuZEFscmVhZHlFeHBpcmVkEIQoEhcKEkZyaWVuZEluZm9Ob3RFeGlzdBCK", + "KBIlCiBGcmllbmRJbmZvTXlDb3VudEFscmVhZHlNYXhDb3VudBCLKBIpCiRG", + "cmllbmRJbmZvT3RoZXJzQ291bnRBbHJlYWR5TWF4Q291bnQQjCgSFgoRRnJp", + "ZW5kSW5mb09mZmxpbmUQjSgSGwoWRnJpZW5kSW5mb0FscmVhZHlFeGlzdBCO", + "KBIhChxGcmllbmRJbnZpdGVNeVBvc0lzTm90TXlIb21lEJQoEiEKHEZyaWVu", + "ZEludml0ZURvbnREaXN0dXJiU3RhdGUQlSgSIQocRnJpZW5kSW52aXRlRXhw", + "aXJlVGltZVJlbWFpbhCWKBIjCh5GcmllbmRJbnZpdGVXYWl0aW5nT3RoZXJJ", + "bnZpdGUQlygSHgoZRnJpZW5kSW52aXRlQWxyZWFkeUV4cGlyZRCYKBIfChpG", + "cmllbmRLaWNrTXlQb3NJc05vdE15SG9tZRCZKBIdChhGcmllbmRLaWNrTWVt", + "YmVyTm90RXhpc3QQmigSHAoXRnJpZW5kSXNJbkFub3RoZXJNeWhvbWUQmygS", + "FgoRQmxvY2tVc2VyTWF4Q291bnQQnigSGgoVQmxvY2tVc2VyQWxyZWFkeUJs", + "b2NrEJ8oEh4KGUJsb2NrVXNlckNhbm5vdFNlbmRNYWlsVG8QoCgSEwoOQmxv", + "Y2tlZEJ5T3RoZXIQoSgSHQoYQmxvY2tVc2VyQ2Fubm90V2hpc3BlclRvEKIo", + "EhMKDkJsb2NrSW5mb0VtcHR5EKMoEh8KGkJsb2NrVXNlckNhbm5vdEludml0", + "ZVBhcnR5EKQoEiMKHkNhcnRTZWxsVHlwZU1pc3NNYXRjaFdpdGhUYWJsZRDQ", + "KBIYChNDYXJ0RnVsbFN0YWNrb2ZJdGVtENEoEg8KCkNhcnRpc0Z1bGwQ0igS", + "EAoLQmFuTmlja05hbWUQ0ygSGQoUQ3VycmVuY3lOb3RGb3VuZERhdGEQmCoS", + "FgoRQ3VycmVuY3lOb3RFbm91Z2gQmSoSGQoUQ3VycmVuY3lJbnZhbGlkVmFs", + "dWUQmioSFAoPU3RhcnRCdWZmRmFpbGVkEPwqEhMKDlN0b3BCdWZmRmFpbGVk", + "EP0qEhUKEE5vdEZvdW5kTmlja05hbWUQ4CsSIAobTm90Rm91bmRUYXR0b29B", + "dHRyaWJ1dGVEYXRhEKgtEhMKDk5vdEZvdW5kU2hvcElkEIwuEhgKE1RpbWVP", + "dmVyRm9yUHVyY2hhc2UQjS4SFwoSTm90RW5vdWdoQXR0cmlidXRlEI4uEhIK", + "DUludmFsaWRHZW5kZXIQjy4SEAoLSXNFcXVpcEl0ZW0QkC4SEAoLSW52YWxp", + "ZEl0ZW0QkS4SFgoRQWxyZWFkeVJlZ2lzdGVyZWQQki4SFQoQTm90Rm91bmRT", + "aG9wSXRlbRCTLhIWChFOb3RFbm91Z2hTaG9wSXRlbRCULhIVChBJbnZhbGlk", + "SXRlbUNvdW50EJUuEhIKDUludmVudG9yeUZ1bGwQli4SFgoRTm90Rm91bmRQ", + "cm9kdWN0SWQQly4SHQoYUmFuZG9tQm94SXRlbURhdGFJbnZhbGlkENQvEhYK", + "EU5vdEV4aXN0R2FjaGFEYXRhENUvQi8KK2NvbS5jYWxpdmVyc2UuYWRtaW4u", + "ZG9tYWluLlJhYmJpdE1xLm1lc3NhZ2VQAWIGcHJvdG8z")); +>>>>>>> .r131478 descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ServerErrorCode), }, null, new pbr::GeneratedClrTypeInfo[] { @@ -789,7 +2238,7 @@ public enum ServerErrorCode { [pbr::OriginalName("MySqlDbException")] MySqlDbException = 10009, /// ///============================================================================================= - /// NLog 관련 오류 : 10030 ~ + /// NLog 관련 오류 : 10030 ~ ///============================================================================================= /// [pbr::OriginalName("NLogWithAwsCloudWatchSetupFailed")] NlogWithAwsCloudWatchSetupFailed = 10031, @@ -859,6 +2308,18 @@ public enum ServerErrorCode { /// [pbr::OriginalName("LargePacketRecvTimeOver")] LargePacketRecvTimeOver = 10156, /// + /// 대용량 패킷이 수신 타입이 유효하지 않다. + /// + [pbr::OriginalName("LargePacketProcessTypeInvalid")] LargePacketProcessTypeInvalid = 10157, + /// + /// 대용량 패킷이 데이터가 존재하지 않는다. + /// + [pbr::OriginalName("LargePacketDataIsNull")] LargePacketDataIsNull = 10158, + /// + /// 대용량 패킷 Exception + /// + [pbr::OriginalName("LargePacketException")] LargePacketException = 10159, + /// ///============================================================================================= /// DB 오류 : 10200 ~ ///============================================================================================= @@ -878,7 +2339,7 @@ public enum ServerErrorCode { /// [pbr::OriginalName("DynamoDbAmazonServiceException")] DynamoDbAmazonServiceException = 10213, /// - /// DynamoDB Config 파일 로딩을 실패 했습니다. + /// DynamoDB Config 파일 로딩을 실패 했습니다. /// [pbr::OriginalName("DynamoDbConfigLoadFailed")] DynamoDbConfigLoadFailed = 10214, /// @@ -1014,7 +2475,7 @@ public enum ServerErrorCode { /// [pbr::OriginalName("DynamoDbDocAttribWrapperCopyFailed")] DynamoDbDocAttribWrapperCopyFailed = 10269, /// - /// DynamoDbDoc Attribute 가져오기를 실패 했습니다. + /// DynamoDbDoc Attribute 가져오기를 실패 했습니다. /// [pbr::OriginalName("DynamoDbDocAttributeGettingFailed")] DynamoDbDocAttributeGettingFailed = 10270, /// @@ -1159,7 +2620,7 @@ public enum ServerErrorCode { /// [pbr::OriginalName("S3ClientCreateFailed")] S3ClientCreateFailed = 10501, /// - /// S3 Bucket 생성을 실패 했습니다. + /// S3 Bucket 생성을 실패 했습니다. /// [pbr::OriginalName("S3BucketCreateFailed")] S3BucketCreateFailed = 10502, /// @@ -1402,7 +2863,7 @@ public enum ServerErrorCode { /// [pbr::OriginalName("EntityAttributeCastFailed")] EntityAttributeCastFailed = 10563, /// - /// 문자열을 Enum으로 변환하는 것을 실패 했습니다. + /// 문자열을 Enum으로 변환하는 것을 실패 했습니다. /// [pbr::OriginalName("StringConvertToEnumFailed")] StringConvertToEnumFailed = 10564, /// @@ -1988,7 +3449,7 @@ public enum ServerErrorCode { /// [pbr::OriginalName("ItemAllocFailed")] ItemAllocFailed = 14035, /// - /// 아이템 Guid 중복 오류 입니다. + /// 아이템 Guid 중복 오류 입니다. /// [pbr::OriginalName("ItemGuidDuplicated")] ItemGuidDuplicated = 14036, /// @@ -2238,6 +3699,18 @@ public enum ServerErrorCode { /// [pbr::OriginalName("BeaconBodyItemInvalid")] BeaconBodyItemInvalid = 15125, /// +<<<<<<< .mine +||||||| .r128445 + /// UgcNpc가 BeaconShopItem을 가지고 있습니다. + /// + [pbr::OriginalName("UgcNpcHasBeaconShopItem")] UgcNpcHasBeaconShopItem = 15126, + /// +======= + /// UgcNpc가 BeaconShopItem을 가지고 있습니다. + /// + [pbr::OriginalName("UgcNpcHasBeaconShopItem")] UgcNpcHasBeaconShopItem = 15126, + /// +>>>>>>> .r131478 ///============================================================================================= /// UgcNpc Ranking 관련 오류 : 15201 ~ ///============================================================================================= @@ -2929,11 +4402,11 @@ public enum ServerErrorCode { /// [pbr::OriginalName("MyhomeNameDuplicated")] MyhomeNameDuplicated = 25515, /// - /// 마이홈 인터폰이 없습니다. + /// 마이홈 인터폰이 없습니다. /// [pbr::OriginalName("MyhomeInterphoneNotExist")] MyhomeInterphoneNotExist = 25516, /// - /// 마이홈 시작 지점이 없습니다. + /// 마이홈 시작 지점이 없습니다. /// [pbr::OriginalName("MyhomeStartPointNotExist")] MyhomeStartPointNotExist = 25517, /// @@ -2945,7 +4418,7 @@ public enum ServerErrorCode { /// [pbr::OriginalName("MyhomeStartPointExceed")] MyhomeStartPointExceed = 25519, /// - /// 의상 제작대 허용 갯수를 초과 하였습니다. + /// 의상 제작대 허용 갯수를 초과 하였습니다. /// [pbr::OriginalName("CraftingClothesExceed")] CraftingClothesExceed = 25520, /// @@ -3197,7 +4670,6 @@ public enum ServerErrorCode { /// [pbr::OriginalName("RewardInvalidTypeValue")] RewardInvalidTypeValue = 34002, /// - /// /// [pbr::OriginalName("NotRequireAttributeValue")] NotRequireAttributeValue = 34003, /// @@ -3371,7 +4843,7 @@ public enum ServerErrorCode { /// [pbr::OriginalName("UgqAbortCannotCauseInvalidState")] UgqAbortCannotCauseInvalidState = 37013, /// - /// UGQ Meta 생성 클래스가 존재 하지 않습니다. + /// UGQ Meta 생성 클래스가 존재 하지 않습니다. /// [pbr::OriginalName("UgqMetaGeneratorNotExist")] UgqMetaGeneratorNotExist = 37014, /// @@ -3559,7 +5031,6 @@ public enum ServerErrorCode { /// [pbr::OriginalName("AiChatServerBadrequest")] AiChatServerBadrequest = 41003, /// - /// /// [pbr::OriginalName("AiChatServerForbidden")] AiChatServerForbidden = 41004, /// @@ -4162,11 +5633,17 @@ public enum ServerErrorCode { [pbr::OriginalName("UgqInvalidState")] UgqInvalidState = 61007, /// ///============================================================================================= - /// NFT 관련 오류 : 62000 ~ + /// NFT 관련 오류 : 62000 ~ ///============================================================================================= /// [pbr::OriginalName("NftForOwnerAllGetFailed")] NftForOwnerAllGetFailed = 62001, /// + ///=========================================== + /// RabbitMqRpc + ///=========================================== + /// + [pbr::OriginalName("MqResponseTimeout")] MqResponseTimeout = 63001, + /// ///============================================================================================= /// Broker Api Server 오류: 70000 ~ ///============================================================================================= @@ -4282,7 +5759,7 @@ public enum ServerErrorCode { /// [pbr::OriginalName("BattleInstanceInfoNotExist")] BattleInstanceInfoNotExist = 11000004, /// - /// 플에이어 객체 + /// 플에이어 객체 /// [pbr::OriginalName("BattleInstanceJoinPlayerError")] BattleInstanceJoinPlayerError = 11000005, /// @@ -4290,7 +5767,7 @@ public enum ServerErrorCode { /// [pbr::OriginalName("BattleInstanceUsableSpawnPointNotExist")] BattleInstanceUsableSpawnPointNotExist = 11000006, /// - /// 배틀 인스턴스 비활성화 상태 입니다. + /// 배틀 인스턴스 비활성화 상태 입니다. /// [pbr::OriginalName("BattleInstanceInActive")] BattleInstanceInActive = 11000007, /// @@ -4354,14 +5831,18 @@ public enum ServerErrorCode { /// [pbr::OriginalName("BattleInstanceNotExistEventInfo")] BattleInstanceNotExistEventInfo = 11000023, /// - /// 배틀 이벤트, 라운드가 거의다 끝나가는 시간입니다. + /// 배틀 이벤트, 라운드가 거의다 끝나가는 시간입니다. /// [pbr::OriginalName("BattleInstanceClosingTime")] BattleInstanceClosingTime = 11000024, /// - /// 배틀 인스턴스 시퀀스 파싱 에러 + /// 배틀 인스턴스 시퀀스 파싱 에러 /// [pbr::OriginalName("BattleInstanceSeqParseError")] BattleInstanceSeqParseError = 11000025, /// + /// 전투 이벤트 아이디가 유효하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceEventIdInvalid")] BattleInstanceEventIdInvalid = 11000026, + /// /// 게임모드 조인 핸들러가 없습니다. /// [pbr::OriginalName("GameModeJoinHandlerNotExist")] GameModeJoinHandlerNotExist = 11000100, @@ -4373,6 +5854,78 @@ public enum ServerErrorCode { /// 게임모드 조인 성공 핸들러가 없습니다. /// [pbr::OriginalName("GameModeJoinSuccessHandlerNotExist")] GameModeJoinSuccessHandlerNotExist = 11000102, +<<<<<<< .mine +||||||| .r128445 + /// + /// 게임모드 생성 실패 + /// + [pbr::OriginalName("GameModeCreateFail")] GameModeCreateFail = 11000103, + /// + /// 게임모드가 null + /// + [pbr::OriginalName("GameModeClassIsNull")] GameModeClassIsNull = 11000104, + /// + /// 게임모드가 존재 + /// + [pbr::OriginalName("GameModeAlreadyExist")] GameModeAlreadyExist = 11000105, + /// + /// 유효하지 않은 AnchorGuid 입니다. + /// + [pbr::OriginalName("GameModeInvalidAnchorGuid")] GameModeInvalidAnchorGuid = 11000106, + /// + ///============================================================================================= + /// Server Metrics 오류: 99999100 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ServerMetricsTriggerHandlerNotFound")] ServerMetricsTriggerHandlerNotFound = 99999101, +======= + /// + /// 게임모드 생성 실패 + /// + [pbr::OriginalName("GameModeCreateFail")] GameModeCreateFail = 11000103, + /// + /// 게임모드가 null + /// + [pbr::OriginalName("GameModeClassIsNull")] GameModeClassIsNull = 11000104, + /// + /// 게임모드가 존재 + /// + [pbr::OriginalName("GameModeAlreadyExist")] GameModeAlreadyExist = 11000105, + /// + /// 유효하지 않은 AnchorGuid 입니다. + /// + [pbr::OriginalName("GameModeInvalidAnchorGuid")] GameModeInvalidAnchorGuid = 11000106, + /// + /// 유효하지 않은 Action 입니다. + /// + [pbr::OriginalName("GameModeInvalidAction")] GameModeInvalidAction = 11000107, + /// + /// 게임모드 나가기준비 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModePreparationForLeavingHandlerNotExist")] GameModePreparationForLeavingHandlerNotExist = 11000108, + /// + /// 게임모드 나가기 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeLeaveHandlerNotExist")] GameModeLeaveHandlerNotExist = 11000109, + /// + /// 게임모드 레이스 랭크가 존재 + /// + [pbr::OriginalName("GameModeRunRankUserExist")] GameModeRunRankUserExist = 11000110, + [pbr::OriginalName("MatchCommonError")] MatchCommonError = 12000001, + [pbr::OriginalName("MatchUserNotFound")] MatchUserNotFound = 120000011, + [pbr::OriginalName("MatchUserReservedAlready")] MatchUserReservedAlready = 120000012, + [pbr::OriginalName("MatchUserNotReserved")] MatchUserNotReserved = 120000013, + [pbr::OriginalName("MatchUserGroupNotFound")] MatchUserGroupNotFound = 120000014, + [pbr::OriginalName("MatchTaskException")] MatchTaskException = 120000021, + [pbr::OriginalName("MatchServerNoResponse")] MatchServerNoResponse = 12000998, + [pbr::OriginalName("MatchError")] MatchError = 12000999, + /// + ///============================================================================================= + /// Server Metrics 오류: 99999100 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ServerMetricsTriggerHandlerNotFound")] ServerMetricsTriggerHandlerNotFound = 99999101, +>>>>>>> .r131478 [pbr::OriginalName("DupLogin")] DupLogin = 1, [pbr::OriginalName("Moving")] Moving = 2, [pbr::OriginalName("DbError")] DbError = 3, diff --git a/Protocol/out-Proto/DefineResult.cs.mine b/Protocol/out-Proto/DefineResult.cs.mine new file mode 100644 index 0000000..43309e0 --- /dev/null +++ b/Protocol/out-Proto/DefineResult.cs.mine @@ -0,0 +1,4786 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Define_Result.proto +// +#pragma warning disable 1591, 0612, 3021, 8981 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +/// Holder for reflection information generated from Define_Result.proto +public static partial class DefineResultReflection { + + #region Descriptor + /// File descriptor for Define_Result.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static DefineResultReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChNEZWZpbmVfUmVzdWx0LnByb3RvIkMKBlJlc3VsdBIjCgllcnJvckNvZGUY", + "ASABKA4yEC5TZXJ2ZXJFcnJvckNvZGUSFAoMcmVzdWx0U3RyaW5nGAIgASgJ", + "Kv/2AQoPU2VydmVyRXJyb3JDb2RlEgsKB1N1Y2Nlc3MQABIdChBSZXN1bHRD", + "b2RlTm90U2V0EP///////////wESFgoRVHJ5Q2F0Y2hFeGNlcHRpb24QkU4S", + "FAoPRG90TmV0RXhjZXB0aW9uEJJOEhYKEVByb3VkTmV0RXhjZXB0aW9uEJNO", + "EhYKEVJhYmJpdE1xRXhjZXB0aW9uEJROEhYKEUR5bmFtb0RiRXhjZXB0aW9u", + "EJVOEh4KGUR5bmFtb0RiVHJhbnNhY3RFeGNlcHRpb24Qlk4SEwoOUmVkaXNF", + "eGNlcHRpb24Ql04SFgoRTWV0YUluZm9FeGNlcHRpb24QmE4SFQoQTXlTcWxE", + "YkV4Y2VwdGlvbhCZThIlCiBOTG9nV2l0aEF3c0Nsb3VkV2F0Y2hTZXR1cEZh", + "aWxlZBCvThIUCg9Mb2dBY3Rpb25Jc051bGwQw04SFgoRTG9nQXBwZW5kZXJJ", + "c051bGwQxE4SFwoSTG9nRm9ybWF0dGVySXNOdWxsEMVOEhkKFExvZ0FjdGlv", + "blR5cGVJbnZhbGlkEMZOEhIKDVJtaUhvc3RJc051bGwQ9U4SHQoYUm1pSG9z", + "dEhhbmRsZXJCaW5kRmFpbGVkEPZOEhkKFFN1YkhhbmRsZXJCaW5kRmFpbGVk", + "EPdOEh0KGFN1dGJBbmRQcm94eUF0dGFjaEZhaWxlZBD4ThIeChlTZXRNZXNz", + "YWdlTWF4TGVuZ3RoRmFpbGVkEPlOEiQKH1BhY2tldFJlY3ZIYW5kbGVyUmVn", + "aXN0ZXJGYWlsZWQQp08SJAofUGFja2V0U2VuZEhhbmRsZXJSZWdpc3RlckZh", + "aWxlZBCoTxIWChFQYWNrZXRSZWN2SW52YWxpZBCpTxIeChlSYWNrZXRSZWN2", + "SGFuZGxlck5vdEZvdW5kEKpPEh4KGUxhcmdlUGFja2V0Tm90QWxsUmVjZWl2", + "ZWQQq08SHAoXTGFyZ2VQYWNrZXRSZWN2VGltZU92ZXIQrE8SFwoSRGJRdWVy", + "eVR5cGVJbnZhbGlkENlPEikKJER5bmFtb0RiVHJhbnNhY3Rpb25DYW5jZWxl", + "ZEV4Y2VwdGlvbhDjTxIkCh9EeW5hbW9EYkFtYXpvbkR5bmFtb0RiRXhjZXB0", + "aW9uEORPEiMKHkR5bmFtb0RiQW1hem9uU2VydmljZUV4Y2VwdGlvbhDlTxId", + "ChhEeW5hbW9EYkNvbmZpZ0xvYWRGYWlsZWQQ5k8SGgoVRHluYW1vRGJDb25u", + "ZWN0RmFpbGVkEOdPEh4KGUR5bmFtb0RiVGFibGVDcmVhdGVGYWlsZWQQ6E8S", + "HgoZRHluYW1vRGJUYWJsZU5vdENvbm5lY3RlZBDpTxIYChNEeW5hbW9EYlF1", + "ZXJ5RmFpbGVkEOpPEh0KGER5bmFtb0RiSXRlbVNpemVFeGNlZWRlZBDrTxIi", + "Ch1EeW5hbW9EYlF1ZXJ5Tm9NYXRjaEF0dHJpYnV0ZRDsTxIpCiREeW5hbW9E", + "YlRyYW5zYWN0aW9uQ29uZmxpY3RFeGNlcHRpb24Q7U8SHAoXRHluYW1vRGJF", + "eHByZXNzaW9uRXJyb3IQ7k8SHwoaRHluYW1vRGJQcmltYXJ5S2V5Tm90Rm91", + "bmQQ708SHQoYRHluYW1vRGJUYWJsZU5hbWVJbnZhbGlkEPBPEhwKF0R5bmFt", + "b0RiQ29ubmVjdG9ySXNOdWxsEPFPEiAKG0R5bmFtb0RiVGFibGVOYW1lRHVw", + "bGljYXRlZBDyTxIbChZEeW5hbW9EYlF1ZXJ5RXhjZXB0aW9uEPdPEh0KGER5", + "bmFtb0RiUXVlcnlOb1JlcXVlc3RlZBD4TxInCiJEeW5hbW9EYlF1ZXJ5Tm90", + "Rm91bmREb2N1bWVudFF1ZXJ5EPlPEisKJkR5bmFtb0RiUXVlcnlFeGNlcHRp", + "b25Ob3RpZmllck5vdEZvdW5kEPpPEikKJER5bmFtb0RiRG9jdW1lbnRJc051", + "bGxJblF1ZXJ5Q29udGV4dBCBUBIeChlEeW5hbW9EYkRvY3VtZW50SXNJbnZh", + "bGlkEIJQEiwKJ0R5bmFtb0RiRG9jdW1lbnRRdWVyeUNvbnRleHRUeXBlSW52", + "YWxpZBCDUBIkCh9EeW5hbW9EYkRvY3VtZW50Q29weUZhaWxlZFRvRG9jEIRQ", + "EiEKHER5bmFtb0RiRG9jdW1lbnRVcHNlcnRGYWlsZWQQhVASIQocRHluYW1v", + "RGJJdGVtUmVxdWVzdElzSW52YWxpZBCLUBIvCipEeW5hbW9EYkl0ZW1SZXF1", + "ZXN0UXVlcnlDb250ZXh0VHlwZUludmFsaWQQjFASLQooRHluYW1vRGJJdGVt", + "UmVxdWVzdFVwZGF0ZUV4cHJlc3Npb25FbXB0eRCNUBIZChREeW5hbW9EYkRv", + "Y1BrSW52YWxpZBCVUBIZChREeW5hbW9EYkRvY1NrSW52YWxpZBCWUBIkCh9E", + "eW5hbW9EYkRvY0F0dHJpYlR5cGVEdXBsaWNhdGVkEJdQEiQKH0R5bmFtb0Ri", + "RG9jQ29weUZhaWxlZFRvRG9jdW1lbnQQmFASJgohRHluYW1vRGJEb2NDb3B5", + "RmFpbGVkRnJvbURvY3VtZW50EJlQEhwKF0R5bmFtb0RiRG9jVHlwZU5vdE1h", + "dGNoEJpQEhsKFkR5bmFtb0RiUmVxdWVzdEludmFsaWQQm1ASJAofRHluYW1v", + "RGJEb2NBdHRyaWJ1dGVTdGF0ZU5vdFNldBCcUBInCiJEeW5hbW9EYkRvY0F0", + "dHJpYldyYXBwZXJDb3B5RmFpbGVkEJ1QEiYKIUR5bmFtb0RiRG9jQXR0cmli", + "dXRlR2V0dGluZ0ZhaWxlZBCeUBIfChpEeW5hbW9EYkRvY0xpbmtQa1NrSW52", + "YWxpZBCfUBIjCh5EeW5hbW9EYkRvY0F0dHJpYldyYXBwZXJJc051bGwQoFAS", + "IAobTXlTcWxDb25uZWN0aW9uQ3JlYXRlRmFpbGVkEKlQEh4KGU15U3FsQ29u", + "bmVjdGlvbk9wZW5GYWlsZWQQqlASGgoVTXlTcWxEYlF1ZXJ5RXhjZXB0aW9u", + "EKtQEh0KGFJlZGlzU2VydmVyQ29ubmVjdEZhaWxlZBC9UBIcChdSZWRpc1N0", + "cmluZ3NXcml0ZUZhaWxlZBC+UBIbChZSZWRpc1N0cmluZ3NSZWFkRmFpbGVk", + "EL9QEhkKFFJlZGlzU2V0c1dyaXRlRmFpbGVkEMBQEhgKE1JlZGlzU2V0c1Jl", + "YWRGYWlsZWQQwVASHwoaUmVkaXNTb3J0ZWRTZXRzV3JpdGVGYWlsZWQQwlAS", + "HgoZUmVkaXNTb3J0ZWRTZXRzUmVhZEZhaWxlZBDDUBIbChZSZWRpc0hhc2hl", + "c1dyaXRlRmFpbGVkEMRQEhoKFVJlZGlzSGFzaGVzUmVhZEZhaWxlZBDFUBIa", + "ChVSZWRpc0xpc3RzV3JpdGVGYWlsZWQQxlASGQoUUmVkaXNMaXN0c1JlYWRG", + "YWlsZWQQx1ASGwoWUmVkaXNSZXF1ZXN0S2V5SXNFbXB0eRDIUBIdChhSZWRp", + "c0xvZ2luQ2FjaGVHZXRGYWlsZWQQyVASHQoYUmVkaXNMb2dpbkNhY2hlU2V0", + "RmFpbGVkEMpQEiAKG1JlZGlzUHJpdmF0ZUNhY2hlRHVwbGljYXRlZBDLUBIl", + "CiBSZWRpc0dsb2JhbFNoYXJlZENhY2hlRHVwbGljYXRlZBDMUBIpCiRSZWRp", + "c0xvZ2luQ2FjaGVPd25lclVzZXJHdWlkTm90TWF0Y2gQzVASIwoeUmVkaXNH", + "bG9iYWxQYXJ0eUNhY2hlR2V0RmFpbGVkEM5QEiUKIFJlZGlzR2xvYmFsUGFy", + "dHlDYWNoZVdyaXRlRmFpbGVkEM9QEisKJlJlZGlzR2xvYmFsUGFydHlNZW1i", + "ZXJDYWNoZVdyaXRlRmFpbGVkENBQEisKJlJlZGlzR2xvYmFsUGFydHlTZXJ2", + "ZXJDYWNoZVdyaXRlRmFpbGVkENFQEjQKL1JlZGlzR2xvYmFsUGFydHlJbnZp", + "dGVQYXJ0eVNlbmRDYWNoZVdyaXRlRmFpbGVkENJQEigKI1JlZGlzSW5zdGFu", + "Y2VSb29tSW5mb0NhY2hlR2V0RmFpbGVkENNQEikKJFJlZGlzVWdjTnBjVG90", + "YWxSYW5rQ2FjaGVXcml0ZUZhaWxlZBDUUBIgChtSYWJiaXRNcUNvbnN1bWVy", + "U3RhcnRGYWlsZWQQoVESGgoVUmFiYml0TXFDb25uZWN0RmFpbGVkEKJREhkK", + "FFJhYmJpdE1lc3NhZ2VUaW1lT2xkEKNREhkKFFMzQ2xpZW50Q3JlYXRlRmFp", + "bGVkEIVSEhkKFFMzQnVja2V0Q3JlYXRlRmFpbGVkEIZSEhcKElMzRmlsZVVw", + "bG9hZEZhaWxlZBCHUhIXChJTM0ZpbGVEZWxldGVGYWlsZWQQiFISFAoPUzNG", + "aWxlR2V0RmFpbGVkEIlSEhcKEk1ldGFEYXRhTG9hZEZhaWxlZBC3UhIUCg9J", + "bnZhbGlkTWV0YURhdGEQuFISEgoNTWV0YUlkSW52YWxpZBC5UhIUCg9Kc29u", + "VHlwZUludmFsaWQQ81ISIQocSnNvbkNvbnZlcnREZXNlcmlhbGl6ZUZhaWxl", + "ZBD0UhIdChhTZXJ2ZXJDb25maWdGaWxlTm90Rm91bmQQzVMSFgoRU2VydmVy", + "VHlwZUludmFsaWQQzlMSJwoiQWxyZWFkeVJ1bm5pbmdTZXJ2ZXJXaXRoTGlz", + "dGVuUG9ydBDPUxIZChROb3RGb3VuZENhY2hlU3RvcmFnZRDQUxIWChFGdW5j", + "dGlvblBhcmFtTnVsbBDRUxIZChRGdW5jdGlvbkludmFsaWRQYXJhbRDSUxIc", + "ChdDbGllbnRMaXN0ZW5Qb3J0SW52YWxpZBDTUxIZChROb3RPdmVycmlkZUlu", + "dGVyZmFjZRDUUxIaChVTZXJ2ZXJPblJ1bm5pbmdGYWlsZWQQ1VMSFwoSU2Vy", + "dmljZVR5cGVJbnZhbGlkENZTEhsKFkZ1bmN0aW9uTm90SW1wbGVtZW50ZWQQ", + "11MSLgopQ2xhc3NEb2VzTm90SW1wbGVtZW50SW50ZXJmYWNlSW5oZXJpdGFu", + "Y2UQ2FMSFwoSUnVsZVR5cGVEdXBsaWNhdGVkENlTEhgKE0NsYXNzVHlwZUNh", + "c3RJc051bGwQ2lMSIgodUGVyaW9kaWNUYXNrQWxyZWFkeVJlZ2lzdGVyZWQQ", + "21MSIgodRW50aXR5VGlja2VyQWxyZWFkeVJlZ2lzdGVyZWQQ3FMSGQoURW50", + "aXR5VGlja2VyTm90Rm91bmQQ3VMSFwoSRW50aXR5QmFzZU5vdEZvdW5kEN5T", + "EhgKE1ZhbGlkU2VydmVyTm90Rm91bmQQ31MSIAobVGFyZ2V0U2VydmVyVXNl", + "ckNvdW50RXhjZWVkEOBTEhcKElRhcmdldFVzZXJOb3RGb3VuZBDhUxIXChJU", + "YXJnZXRVc2VyTm90TG9nSW4Q4lMSEAoLTm90RXhpc3RNYXAQ41MSIgodRmFp", + "bGVkVG9SZXNlcnZlRW50ZXJDb25kaXRpb24Q5FMSHQoYRmFpbGVkVG9SZXNl", + "cnZhdGlvbkVudGVyEOVTEhsKFk93bmVyRW50aXR5VHlwZUludmFsaWQQ5lMS", + "HAoXT3duZXJFbnRpdHlDYW5ub3RGaWxsdXAQ51MSFQoQT3duZXJHdWlkSW52", + "YWxpZBDoUxIhChxEYWlseVRpbWVFdmVudEFkZGl0aW9uRmFpbGVkEOlTEiQK", + "H1Byb2dyYW1WZXJzaW9uUGF0aFRva2VuTm90Rm91bmQQ6lMSHQoYQ3VycmVu", + "dGx5UHJvY2Vzc2luZ1N0YXRlEOtTEhkKFFNlcnZlclVybFR5cGVJbnZhbGlk", + "EOxTEiMKHlNlcnZlclVybFR5cGVBbHJlYWR5UmVnaXN0ZXJlZBDtUxIcChdT", + "ZXJ2ZXJPZmZsaW5lTW9kZUVuYWJsZRDuUxIRCgxPd25lckludmFsaWQQ71MS", + "JAofTWV0YURhdGFDb3B5VG9EeW5hbW9EYkRvY0ZhaWxlZBDiVBIoCiNNZXRh", + "RGF0YUNvcHlUb0VudGl0eUF0dHJpYnV0ZUZhaWxlZBDjVBIhChxEeW5hbW9E", + "YkRvY0NvcHlUb0NhY2hlRmFpbGVkEORUEisKJkR5bmFtb0RiRG9jQ29weVRv", + "RW50aXR5QXR0cmlidXRlRmFpbGVkEOVUEiUKIENhY2hlQ29weVRvRW50aXR5", + "QXR0cmlidXRlRmFpbGVkEOZUEiEKHENhY2hlQ29weVRvRHluYW1vRGJEb2NG", + "YWlsZWQQu1ISJQogRW50aXR5QXR0cmlidXRlQ29weVRvQ2FjaGVGYWlsZWQQ", + "vFISKwomRW50aXR5QXR0cmlidXRlQ29weVRvRHluYW1vRGJEb2NGYWlsZWQQ", + "vVISOQo0RW50aXR5QXR0cmlidXRlQ29weVRvRW50aXR5QXR0cmlidXRlVHJh", + "bnNhY3RvckZhaWxlZBC+UhI5CjRFbnRpdHlBdHRyaWJ1dGVUcmFuc2FjdG9y", + "Q29weVRvRW50aXR5QXR0cmlidXRlRmFpbGVkEL9SEjUKMEVudGl0eUF0dHJp", + "YnV0ZVRyYW5zYWN0b3JDb3B5VG9EeW5hbW9EYkRvY0ZhaWxlZBDAUhITCg5B", + "dHRyaWJOb3RGb3VuZBDBUhIZChRBdHRyaWJQYXRoTWFrZUZhaWxlZBDCUhIe", + "ChlFbnRpdHlBdHRyaWJ1dGVDYXN0RmFpbGVkEMNSEh4KGVN0cmluZ0NvbnZl", + "cnRUb0VudW1GYWlsZWQQxFISHgoZTWV0YVNjaGVtYVZlcnNpb25Ob3RNYXRj", + "aBCVVRIcChdNZXRhRGF0YVZlcnNpb25Ob3RNYXRjaBCWVRIaChVQYWNrZXRW", + "ZXJzaW9uTm90TWF0Y2gQl1USHwoaQ2xpZW50TG9naWNWZXJzaW9uTm90TWF0", + "Y2gQmFUSHAoXUmVzb3VyY2VWZXJzaW9uTm90TWF0Y2gQmVUSHwoaQ2xpZW50", + "UHJvZ3JhbVZlcnNpb25Jc051bGwQmlUSEwoOVGVzdElkTm90QWxsb3cQ+VUS", + "EQoMQm90ZE5vdEFsbG93EPpVEhkKFEFjY291bnRJZExlbmd0aFNob3J0EPtV", + "EiQKH0FjY291bnRJZE5vdEZvdW5kSW5Tc29BY2NvdW50RGIQ/FUSIQocTWV0", + "YURhdGFOb3RGb3VuZEJ5VGVzdFVzZXJJZBD9VRIcChdBY2NvdW50UGFzc3dv", + "cmROb3RNYXRjaBD+VRInCiJVc2VyRGF0YUNvbnZlcnRUb0FjY291bnRBdHRy", + "RmFpbGVkEP9VEiQKH0FjY291bnRCYXNlQXR0cmliSW5zZXJ0RGJGYWlsZWQQ", + "gFYSGAoTTm9TZXJ2ZXJDb25uZWN0YWJsZRCBVhITCg5CbG9ja2VkQWNjb3Vu", + "dBCCVhIsCidTc29BY2NvdW50QXV0aFdpdGhMYXVuY2hlckxvZ2luTm90QWxs", + "b3cQg1YSIgodQ2xpZW50U3RhbmRhbG9uZUxvZ2luTm90QWxsb3cQhFYSGQoU", + "UGxhdGZvcm1UeXBlTm90QWxsb3cQhVYSJgohQWNjb3VudENhbk5vdFJlYWRG", + "cm9tU3NvQWNjb3VudERiEIZWEiEKHFNzb0FjY291bnRBdXRoSnd0Q2hlY2tG", + "YWlsZWQQh1YSKQokVXNlcklkS2V5Tm90Rm91bmRJblNzb0FjY291bnRBdXRo", + "Snd0EIhWEigKI1VzZXJJZFZhbHVlRW1wdHlJblNzb0FjY291bnRBdXRoSnd0", + "EIlWEi4KKUFjY291bnRUeXBlS2V5Tm90Rm91bmRJblNzb0FjY291bnRBdXRo", + "Snd0EIpWEjAKK0FjY291bnRUeXBlVmFsdWVOb3RBbGxvd0luU3NvQWNjb3Vu", + "dEF1dGhKd3QQi1YSKAojQWNjb3VudEJhc2VEb2NOb3RGb3VuZEluTWV0YXZl", + "cnNlRGIQjFYSLgopQWNjZXNzVG9rZW5LZXlOb3RBbGxvd0luU3NvQWNjb3Vu", + "dEF1dGhKd3QQjVYSJgohQWNjZXNzVG9rZW5Ob3RNYXRjaEluU3NvQWNjb3Vu", + "dERiEI5WEhgKE0FjY291bnRUeXBlTm90QWxsb3cQj1YSGgoVQWNjb3VudEJh", + "c2VEb2NOb3RMb2FkEJBWEhcKEk5vdEVub3VnaEF1dGhvcml0eRCuVhIZChRB", + "Y2NvdW50QmFzZURvY0lzTnVsbBCvVhIVChBBY2NvdW50SWRJbnZhbGlkELBW", + "EhsKFkFjY291bnRXaXRob3V0VXNlckd1aWQQsVYSIgodU3NvQWNjb3VudEF1", + "dGhKd3RUb2tlbkV4cGlyZWQQslYSHwoaU3NvQWNjb3VudEF1dGhKd3RFeGNl", + "cHRpb24Qs1YSJAofVHJhbnNhY3Rpb25SdW5uZXJBbHJlYWR5UnVubmluZxDB", + "VxIeChlUcmFuc2FjdGlvblJ1bm5lck5vdEZvdW5kEMJXEhYKEUVudGl0eUd1", + "aWRJbnZhbGlkEKVYEhsKFkVudGl0eUF0dHJpYkR1cGxpY2F0ZWQQplgSGgoV", + "RW50aXR5QXR0cmlidXRlSXNOdWxsEKdYEhwKF0VudGl0eUF0dHJpYnV0ZU5v", + "dEZvdW5kEKhYEiAKG0VudGl0eUF0dHJpYnV0ZVN0YXRlSW52YWxpZBCpWBIW", + "ChFFbnRpdHlUeXBlSW52YWxpZBCqWBIWChFFbnRpdHlMaW5rZWRUb01hcBCr", + "WBIZChRFbnRpdHlOb3RMaW5rZWRUb01hcBCsWBIaChVFbnRpdHlTdGF0ZU5v", + "dERhbmNpbmcQrVgSGwoWRW50aXR5QWN0aW9uRHVwbGljYXRlZBCJWRIZChRF", + "bnRpdHlBY3Rpb25Ob3RGb3VuZBCKWRIdChhFbnRpdHlCYXNlSGZzbUluaXRG", + "YWlsZWQQ7VkSIAobUmVkaXNHbG9iYWxFbnRpdHlEdXBsaWNhdGVkENFaEhoK", + "FVVzZXJJc1N3aXRjaGluZ1NlcnZlchC1WxIdChhVc2VySXNOb3RTd2l0Y2hp", + "bmdTZXJ2ZXIQtlsSHwoaU2VydmVyU3dpdGNoaW5nT3RwTm90TWF0Y2gQt1sS", + "IwoeQ29ubmVjdGVkU2VydmVySXNOb3REZXN0U2VydmVyELhbEhsKFkludmFs", + "aWRSZXNlcnZhdGlvblVzZXIQuVsSFgoRSW52YWxpZFJldHVyblVzZXIQulsS", + "KQokVXNlck5pY2tuYW1lTm90QWxsb3dXaXRoU3BlY2lhbENoYXJzEOFdEiwK", + "J1VzZXJOaWNrbmFtZUFsbG93ZWRNaW4yVG9NYXg4V2l0aEtvcmVhbhDiXRIu", + "CilVc2VyTmlja25hbWVBbGxvd2VkTWluNFRvTWF4MTZXaXRoRW5nbGlzaBDj", + "XRItCihVc2VyTmlja25hbWVOb3RBbGxvd2VkTnVtYmVyQXRGaXJzdENoYXJz", + "EORdEh4KGVVzZXJOaWNrbmFtZU5vdEFsbG93Q2hhcnMQ5V0SLQooVXNlck5p", + "Y2tuYW1lTm90QWxsb3dXaXRoSW5pdGlhbGlzbUtvcmVhbhDmXRIUCg9Vc2Vy", + "Tmlja25hbWVCYW4Q510SGAoTVXNlckR1cGxpY2F0ZWRMb2dpbhDoXRIRCgxV", + "c2VyTm90TG9naW4Q6V0SKQokVXNlckNyZWF0aW9uRm9yRHluYW1vRGJEb2NE", + "dXBsaWNhdGVkEOpdEikKJFVzZXJHdWlkQXBwbHlUb1JlZkF0dHJpYnV0ZUFs", + "bEZhaWxlZBDrXRImCiFUZXN0VXNlclByZXBhcmVDcmVhdGVOb3RDb21wbGV0", + "ZWQQ7F0SKQokRGVmYXVsdFVzZXJQcmVwYXJlQ3JlYXRlTm90Q29tcGxldGVk", + "EO1dEiAKG1VzZXJQcmVwYXJlTG9hZE5vdENvbXBsZXRlZBDuXRIbChZVc2Vy", + "Tmlja25hbWVOb3RDcmVhdGVkEO9dEh8KGlVzZXJOaWNrbmFtZUFscmVhZHlD", + "cmVhdGVkEPBdEh8KGlVzZXJDcmVhdGVTdGVwTm90Q29tcGxldGVkEPFdEhgK", + "E1VzZXJDcmVhdGVDb21wbGV0ZWQQ8l0SGwoWVXNlclN1YktleUJpbmRUb0Zh", + "aWxlZBDzXRIcChdVc2VyU3ViS2V5UmVwbGFjZUZhaWxlZBD0XRIUCg9Vc2Vy", + "R3VpZEludmFsaWQQ9V0SGgoVVXNlck5pY2tuYW1lRG9jSXNOdWxsEPZdEhIK", + "DVVzZXJEb2NJc051bGwQ910SGQoUVXNlckd1aWRBbHJlYWR5QWRkZWQQ+F0S", + "GwoWVXNlck5pY2tuYW1lRHVwbGljYXRlZBD5XRIjCh5Vc2VyTmlja25hbWVB", + "bGxvd2VkTWluMlRvTWF4MTIQ+l0SIAobVXNlck5pY2tuYW1lU2VhcmNoUGFn", + "ZVdyb25nEPtdEhYKEVVzZXJOaWNrbmFtZUVtcHR5EPxdEiEKHFVzZXJDb250", + "ZW50c1NldHRpbmdEb2NJc051bGwQ/V0SFgoRVXNlck1vbmV5RG9jRW1wdHkQ", + "/l0SIQocVXNlclJlcG9ydEludmFsaWRUaXRsZUxlbmd0aBDFXhIjCh5Vc2Vy", + "UmVwb3J0SW52YWxpZENvbnRlbnRMZW5ndGgQxl4SGwoWVXNlclJlcG9ydERv", + "Y0V4Y2VwdGlvbhDHXhIrCiZUZXN0Q2hhcmFjdGVyUHJlcGFyZUNyZWF0ZU5v", + "dENvbXBsZXRlZBDJZRIuCilEZWZhdWx0Q2hhcmFjdGVyUHJlcGFyZUNyZWF0", + "ZU5vdENvbXBsZXRlZBDUZRIlCiBDaGFyYWN0ZXJQcmVwYXJlTG9hZE5vdENv", + "bXBsZXRlZBDVZRIsCidDaGFyYWN0ZXJCYXNlRG9jTG9hZER1cGxpY2F0ZWRD", + "aGFyYWN0ZXIQ1mUSGQoUQ2hhcmFjdGVyTm90U2VsZWN0ZWQQ12USFgoRQ2hh", + "cmFjdGVyTm90Rm91bmQQ2GUSGwoWQ2hhcmFjdGVyQmFzZURvY05vUmVhZBDZ", + "ZRIlCiBDaGFyYWN0ZXJDdXN0b21pemluZ05vdENvbXBsZXRlZBDaZRIkCh9D", + "aGFyYWN0ZXJDcmVhdGVTdGVwTm90Q29tcGxldGVkENtlEikKJENoYXJhY3Rl", + "ckN1c3RvbWl6aW5nQWxyZWFkeUNvbXBsZXRlZBDcZRIfChpDaGFyYWN0ZXJQ", + "cmVwYXJlTm90Q3JlYXRlZBDdZRIdChhDaGFyYWN0ZXJDcmVhdGVDb21wbGV0", + "ZWQQ3mUSGwoWQ2hhcmFjdGVyQmFzZURvY0lzTnVsbBDfZRIVChBBYmlsaXR5", + "Tm90RW5vdWdoEPVnEhkKFEl0ZW1NZXRhRGF0YU5vdEZvdW5kELFtEhQKD0l0", + "ZW1HdWlkSW52YWxpZBCybRISCg1JdGVtRG9jSXNOdWxsELNtEicKIkl0ZW1E", + "ZWZhdWx0QXR0cmlidXRlTm90Rm91bmRJbk1ldGEQtG0SIwoeSXRlbUxldmVs", + "RW5jaGFudE5vdEZvdW5kSW5NZXRhELVtEh4KGUl0ZW1FbmNoYW50Tm90Rm91", + "bmRJbk1ldGEQtm0SKwomSXRlbUF0dHJpYnV0ZVJhbmRvbUdyb3VwTm90Rm91", + "bmRJbk1ldGEQt20SLwoqSXRlbUF0dHJpYnV0ZVJhbmRvbUdyb3VwVG90YWxX", + "ZWlnaHRJbnZhbGlkELhtEhEKDEl0ZW1Ob3RGb3VuZBC5bRIeChlJdGVtQ2xv", + "dGhJbnZhbGlkTGFyZ2VUeXBlELptEh4KGUl0ZW1DbG90aEludmFsaWRTbWFs", + "bFR5cGUQu20SGgoVSXRlbVN0YWNrQ291bnRJbnZhbGlkELxtEhcKEkl0ZW1N", + "YXhDb3VudEV4Y2VlZBC9bRIeChlJdGVtRG9jTG9hZER1cGxpY2F0ZWRJdGVt", + "EL5tEhkKFENsb3RoU2xvdFR5cGVJbnZhbGlkEL9tEhwKF0l0ZW1TdGFja0Nv", + "dW50Tm90RW5vdWdoEMBtEhcKEkl0ZW1Db3VudE5vdEVub3VnaBDBbRIYChNJ", + "dGVtSW52YWxpZEl0ZW1UeXBlEMJtEh0KGEl0ZW1Ub29sTWV0YURhdGFOb3RG", + "b3VuZBDDbRIVChBJdGVtVG9vbE5vdEZvdW5kEMRtEh0KGEl0ZW1Ub29sTm90", + "QWN0aXZhdGVTdGF0ZRDFbRIYChNUb29sQWN0aW9uRG9jSXNOdWxsEMZtEiUK", + "IFRvb2xBY3Rpb25BbHJlYWR5VW5hY3RpdmF0ZVN0YXRlEMdtEiMKHlRvb2xB", + "Y3Rpb25BbHJlYWR5QWN0aXZhdGVTdGF0ZRDIbRIXChJJdGVtVGF0dG9vTm90", + "Rm91bmQQyW0SJQogSXRlbUF0dHJpYnV0ZUVuY2hhbnRNZXRhTm90Rm91bmQQ", + "ym0SIwoeSXRlbUF0dHJpYnV0ZUNoYW5nZU5vdFNlbGVjdGVkEMttEiQKH0l0", + "ZW1QYXJzaW5nRnJvbVN0cmluZ1RvSW50RXJvcnIQzG0SKQokSXRlbVZhbHVl", + "UGFyc2luZ0Zyb21TdHJpbmdUb0ludEVyb3JyEM1tEiYKIUl0ZW1GaXJzdFB1", + "cmNoYXNlSGlzdG9yeURvY0lzTnVsbBDObRIyCi1JdGVtRmlyc3RQdXJjaGFz", + "ZUhpc3RvcnlEb2NMb2FkRHVwbGljYXRlZEl0ZW0Qz20SKQokSXRlbUZpcnN0", + "UHVyY2hhc2VIaXN0b3J5QWxyZWFkeUV4aXN0ENBtEiwKJ0l0ZW1GaXJzdFB1", + "cmNoYXNlRGlzY291bnRJdGVtQ291bnRXcm9uZxDRbRIfChpJdGVtQXR0cmli", + "dXRlSWRUeXBlSW52YWxpZBDSbRIUCg9JdGVtQWxsb2NGYWlsZWQQ020SFwoS", + "SXRlbUd1aWREdXBsaWNhdGVkENRtEhgKE0l0ZW1MZXZlbEN1cnJlbnRNYXgQ", + "1W0SFwoSSXRlbUNhbk5vdEJlU3RvcmVkENZtEhwKF0l0ZW1Vc2VGdW5jdGlv", + "bk5vdEZvdW5kEJVuEh0KGEl0ZW1Vc2VRdWVzdE1haWxDb3VudE1heBCWbhIj", + "Ch5JdGVtVXNlTm90RXhpc3RBc3NpZ25hYmxlUXVlc3QQl24SGwoWSXRlbVVz", + "ZUFscmVhZHlIYXNRdWVzdBCYbhIfChpJdGVtVXNlQWxyZWFkeUhhc1F1ZXN0", + "TWFpbBCZbhIjCh5CYWdSdWxlSXRlbUxhcmdlVHlwZUR1cGxpY2F0ZWQQmXUS", + "KQokVG9vbEVxdWlwUnVsZUl0ZW1MYXJnZVR5cGVEdXBsaWNhdGVkEJp1EioK", + "JUNsb3RoRXF1aXBSdWxlSXRlbUxhcmdlVHlwZUR1cGxpY2F0ZWQQm3USKwom", + "VGF0dG9vRXF1aXBSdWxlSXRlbUxhcmdlVHlwZUR1cGxpY2F0ZWQQnHUSGgoV", + "SW52ZW50b3J5UnVsZU5vdEZvdW5kEJ11EhcKEkVxdWlwSW52ZW5Ob3RGb3Vu", + "ZBCedRIYChNTbG90c0FscmVhZHlFcXVpcGVkEJ91EhoKFVNsb3RzQWxyZWFk", + "eVVuZXF1aXBlZBCgdRISCg1CYWdJc0l0ZW1GdWxsEKF1EhMKDkJhZ0lzSXRl", + "bUVtcHR5EKJ1EhoKFUJhZ0RlbHRhSXRlbUR1cGxpYXRlZBCjdRIUCg9CYWdJ", + "dGVtTm90Rm91bmQQpHUSKAojQ2xvdGhFcXVpcFJ1bGVDbG90aFNsb3RUeXBl", + "Tm90Rm91bmQQpXUSJgohVG9vbEVxdWlwUnVsZVRvb2xTbG90VHlwZU5vdEZv", + "dW5kEKZ1EioKJVRhdHRvb0VxdWlwUnVsZVRhdHRvb1Nsb3RUeXBlTm90Rm91", + "bmQQp3USGAoTQmFnVGFiVHlwZUFkZEZhaWxlZBCodRIWChFCYWdUYWJUeXBl", + "SW52YWxpZBCpdRIXChJCYWdUYWJUeXBlTm90Rm91bmQQqnUSGwoWQmFnVGFi", + "Q291bnRNZXJnZUZhaWxlZBCrdRIfChpJbnZlbnRvcnlFbnRpdHlUeXBlSW52", + "YWxpZBCsdRIaChVJbnZlbkVxdWlwVHlwZUludmFsaWQQrXUSGgoVQmFnSXNS", + "ZXNlcnZlZEl0ZW1GdWxsEK51EhsKFkJhZ0lzUmVzZXJ2ZWRJdGVtRW1wdHkQ", + "r3USFgoRRXF1aXBTbG90Tm90TWF0Y2gQsHUSGAoTRXF1aXBTbG90T3V0T2ZS", + "YW5nZRCxdRIeChlTbG90c0FscmVhZHlSZXNlcnZlZEVxdWlwELJ1EiAKG1Ns", + "b3RzQWxyZWFkeVJlc2VydmVkVW5lcXVpcBCzdRIVChBTbG90VHlwZU5vdEZv", + "dW5kELR1Eh8KGlVnY05wY01ldGFHdWlkQWxyZWFkeUFkZGVkEP11EhQKD1Vn", + "Y05wY0RvY0lzTnVsbBD+dRIZChRVZ2NOcGNNYXhDb3VudEV4Y2VlZBD/dRId", + "ChhVZ2NOcGNDbG90aEl0ZW1Ob3RFbm91Z2gQgHYSIgodVWdjTnBjRG9jTG9h", + "ZER1cGxpY2F0ZWRVZ2NOcGMQgXYSIgodVWdjTnBjRGVzY3JpcHRpb25MZW5n", + "dGhFeGNlZWQQgnYSIwoeVWdjTnBjV29yZFNjZW5hcmlvTGVuZ3RoRXhjZWVk", + "EIN2Eh8KGlVnY05wY0dyZWV0aW5nTGVuZ3RoRXhjZWVkEIR2Eh4KGVVnY05w", + "Y1RhdHRvb0l0ZW1Ob3RFbm91Z2gQhXYSJwoiVWdjTnBjSGFiaXRTb2NpYWxB", + "Y3Rpb25Db3VudEV4Y2VlZBCGdhIqCiVVZ2NOcGNEaWFsb2d1ZVNvY2lhbEFj", + "dGlvbkNvdW50RXhjZWVkEId2Eh0KGFVnY05wY05pY2tuYW1lRHVwbGljYXRl", + "ZBCIdhITCg5VZ2NOcGNOb3RGb3VuZBCJdhIjCh5VZ2NOcGNJbnRyb2R1Y3Rp", + "b25MZW5ndGhFeGNlZWQQinYSFwoSVWdjTnBjTWF4VGFnRXhjZWVkEIt2EhgK", + "E1VnY05wY05pY2tuYW1lRW1wdHkQjHYSJgohVWdjTnBjQWxyZWFkeVJlZ2lz", + "dGVyZWRJbkdhbWVab25lEI12EiIKHVVnY05wY05vdFJlZ2lzdGVyZWRJbkdh", + "bWVab25lEI52EiQKH1VnY05wY0xpa2VTZWxlY3RlZUNvdW50Tm90Rm91bmQQ", + "j3YSIwoeVWdjTnBjTGlrZVNlbGVjdGVkRmxhZ05vdEZvdW5kEJB2Eh8KGlVn", + "Y05wY0R1cGxpY2F0ZUluTXlob21lVWdjEJF2EhcKElVnY05wY0xvY2F0ZWRT", + "dGF0ZRCSdhIbChZVZ2NOcGNNZXRhRGF0YU5vdEZvdW5kEJN2EiMKHkJlYWNv", + "bkFwcFByb2ZpbGVVcGxvYWRDb29sVGltZRCUdhIaChVCZWFjb25Cb2R5SXRl", + "bUludmFsaWQQlXYSHwoaVWdjTnBjUmFua0VudGl0eUlzTm90Rm91bmQQ4XYS", + "GQoUVWdjTnBjUmFua091dE9mUmFuZ2UQ4nYSIwoeRmFybWluZ0VmZmVjdERv", + "Y0xpbmtQa1NrTm90U2V0EMV3Ei0KKEZhcm1pbmdFZmZlY3RBbHJlYWR5UmVn", + "aXN0ZXJlZEluR2FtZVpvbmUQxncSEwoORmFybWluZ0FscmVhZHkQx3cSEAoL", + "RmFybWluZ0J5TWUQyHcSIAobRmFybWluZ1Byb3BNZXRhRGF0YU5vdEZvdW5k", + "EMl3EhsKFkZhcm1pbmdUcnlDb3VudEludmFsaWQQyncSFAoPRmFybWluZ05v", + "dFN0YXRlEMt3EhkKFEZhcm1pbmdPd25lck5vdE1hdGNoEMx3EiUKIEZhcm1p", + "bmdTdW1tb25lZEVudGl0eVR5cGVJbnZhbGlkEM13EiQKH0Zhcm1pbmdFZmZl", + "Y3ROb3RFeGlzdEluR2FtZVpvbmUQzncSEAoLRmFyaW1nU3RhdGUQz3cSGwoW", + "RmFybWluZ1N0YW5kQnlOb3RTdGF0ZRDQdxIaChVGYXJtaW5nQW5jaG9yTm90", + "Rm91bmQQ0XcSJgohRmFybWluZ0JlYWNvbkRiSW5mb0ludGVncml0eUVycm9y", + "ENJ3EhoKFUdhY2hhTWV0YURhdGFOb3RGb3VuZBCpeBIVChBHYWNoYVJld2Fy", + "ZEVtcHR5EKp4EhMKDk1hc3Rlck5vdEZvdW5kELl7EhUKEE1hc3Rlck5vdFJl", + "bGF0ZWQQunsSFAoPR2FtZVpvbmVOb3RKb2luEJ18Eg4KCU1hcElzTnVsbBCe", + "fBIcChdMb2NhdGlvblVuaXF1ZUlkSW52YWxpZBCffBIUCg9GcmllbmREb2NJ", + "c051bGwQgX0SGgoVRnJpZW5kRm9sZGVyRG9jSXNOdWxsEIJ9EiIKHFNvY2lh", + "bEFjdGlvbk1ldGFEYXRhTm90Rm91bmQQ6YQBEhoKFFNvY2lhbEFjdGlvbk5v", + "dEZvdW5kEOqEARIvCilTb2NpYWxBY3Rpb25Eb2NMb2FkRHVwbGljYXRlZFNv", + "Y2lhbEFjdGlvbhDrhAESHgoYU29jaWFsQWN0aW9uQWxyZWFkeUV4aXN0EOyE", + "ARIgChpTb2NpYWxBY3Rpb25TbG90T3V0T2ZSYW5nZRDthAESGwoVU29jaWFs", + "QWN0aW9uTm90T25TbG90EO6EARIbChVTb2NpYWxBY3Rpb25Eb2NJc051bGwQ", + "74QBEhwKFkNoYW5uZWxNb3ZlU2FtZUNoYW5uZWwQ0YwBEiAKGkNoYW5uZWxJ", + "bnZhbGlkTW92ZUNvb2xUaW1lENKMARIWChBOb3RDaGFubmVsU2VydmVyENOM", + "ARIYChJPd25lZFJvb21Eb2NJc051bGwQuZQBEhMKDVJvb21Eb2NJc051bGwQ", + "upQBEhUKD1Jvb21Jc05vdE15SG9tZRC7lAESGgoUTWFwUmFuZ2VPdXRPZkNl", + "bGxQb3MQvJQBEhwKFk1hcEdyaWRCb3VuZE91dE9mUmFuZ2UQvZQBEhkKE01h", + "cEdyaWROb3RGb3VuZEdyaWQQvpQBEhkKE01hcEdyaWROb3RGb3VuZENlbGwQ", + "v5QBEh8KGU1hcEdyaWRDZWxsTm90Rm91bmRQbGF5ZXIQwJQBEh8KGU1hcEdy", + "aWRDZWxsTm90Rm91bmRVZ2NOcGMQwZQBEhIKDE1haWxOb3RGb3VuZBChnAES", + "FgoQTWFpbEFscmVhZHlUYWtlbhCinAESGQoTTWFpbEludmFsaWRNYWlsVHlw", + "ZRCjnAESHAoWTWFpbE1heFNlbmRDb3VudEV4Y2VlZBCknAESEwoNTWFpbERv", + "Y0lzTnVsbBClnAESHQoXTWFpbEJsb2NrVXNlckNhbm5vdFNlbmQQppwBEiYK", + "IE1haWxNYXhUYXJnZXRSZWNlaXZlZENvdW50RXhjZWVkEKecARIWChBNYWls", + "Q2FudFNlbmRTZWxmEKicARIgChpNYWlsQ2FudERlbGV0ZUlmSXRlbUV4aXN0", + "cxCpnAESFgoQTWFpbERvY0V4Y2VwdGlvbhCqnAESGgoUTWFpbFByb2ZpbGVE", + "b2NJc051bGwQzZ4BEh0KF01haWxQcm9maWxlRG9jRXhjZXB0aW9uEM6eARIZ", + "ChNTeXN0ZW1NYWlsRG9jSXNOdWxsEJWgARIYChJQYXJ0eUNhbm5vdFNldEd1", + "aWQQiaQBEiAKGlBhcnR5RmFpbGVkTWFrZVBhcnR5TWVtYmVyEIqkARIRCgtQ", + "YXJ0eUlzRnVsbBCLpAESGAoSQWxyZWFkeUludml0ZVBhcnR5EIykARIZChNO", + "b3RGb3VuZFBhcnR5SW52aXRlEI2kARITCg1Ob3RGb3VuZFBhcnR5EI6kARIO", + "CghOb3RQYXJ0eRCPpAESFAoOTm90UGFydHlMZWFkZXIQkKQBEhIKDEpvaW5p", + "bmdQYXJ0eRCRpAESFAoOTm90UGFydHlNZW1iZXIQkqQBEhMKDUFscmVhZHlT", + "dW1tb24Qk6QBEh0KF1BhcnR5TGVhZGVyU2VydmVySXNGdWxsEJSkARIbChVJ", + "bnZpdGVNZW1iZXJJc0NvbmNlcnQQlaQBEhwKFkZhaWxUb1NlbmRJbnZpdGVN", + "ZW1iZXIQlqQBEhgKEkFscmVhZHlQYXJ0eU1lbWJlchCXpAESHQoXSW52YWxp", + "ZFN1bW1vblNlcnZlclR5cGUQmKQBEh0KF1N1bW1vblVzZXJMaW1pdERpc3Rh", + "bmNlEJmkARIZChNJbnZhbGlkU3VtbW9uTWVtYmVyEJqkARIaChRQYXJ0eUxl", + "YWRlckxvZ2dlZE91dBCbpAESGwoVQWxyZWFkeVN0YXJ0UGFydHlWb3RlEJyk", + "ARIWChBOb1N0YXJ0UGFydHlWb3RlEJ2kARIeChhBbHJlYWR5UGFzc1BhcnR5", + "Vm90ZVRpbWUQnqQBEhoKFEludmFsaWRQYXJ0eVZvdGVUaW1lEJ+kARIbChVB", + "bHJlYWR5UmVwbHlQYXJ0eVZvdGUQoKQBEhoKFEVtcHR5UGFydHlJbnN0YW5j", + "ZUlkEKGkARIYChJJbnZhbGlkSW52aXRlUGxhY2UQoqQBEh0KF0ludml0ZVBh", + "cnR5SW52YWxpZFVzZXJzEKOkARIbChVTdW1tb25QYXJ0eU1lbWJlckZhaWwQ", + "pKQBEh4KGEludmFsaWRQYXJ0eVN0cmluZ0xlbmd0aBClpAESIQobSW5jbHVk", + "ZUJhbldvcmRGcm9tUGFydHlOYW1lEKakARIiChxKb2luaW5nUGFydHlNZW1i", + "ZXJJbmZvSXNOdWxsEKekARIeChhJbnZhbGlkU3VtbW9uV29ybGRTZXJ2ZXIQ", + "qKQBEhsKFU5vdEV4aXN0UGFydHlJbnN0YW5jZRDvqwESGgoUQnVmZk1ldGFE", + "YXRhTm90Rm91bmQQ8asBEh0KF0J1ZmZOb3RSZWdpc3RyeUNhdGVnb3J5EPKr", + "ARISCgxCdWZmTm90Rm91bmQQ86sBEiEKG0J1ZmZJbnZhbGlkQnVmZkNhdGVn", + "b3J5VHlwZRD0qwESIQobQnVmZkNhY2hlTG9hZER1cGxpY2F0ZWRCdWZmEPWr", + "ARIeChhCdWZmSW52YWxpZEF0dHJpYnV0ZVR5cGUQ9qsBEh0KF1F1ZXN0QXNz", + "aW5nRGF0YU5vdEV4aXN0ENizARIXChFRdWVzdE1haWxOb3RFeGlzdBDZswES", + "FwoRUXVlc3RBbHJlYWR5RW5kZWQQ2rMBEhMKDVF1ZXN0Q291bnRNYXgQ27MB", + "Eh0KF1F1ZXN0VHlwZUFzc2lnbkNvdW50TWF4ENyzARIWChBRdWVzdEludmFs", + "aWRUeXBlEN2zARIXChFRdWVzdEludmFsaWRWYWx1ZRDeswESFQoPUXVlc3RJ", + "ZE5vdEZvdW5kEN+zARIZChNRdWVzdEludmFsaWRUYXNrTnVtEOCzARIbChVR", + "dWVzdFJlZnVzZU9ubHlOb3JtYWwQ4bMBEh4KGFF1ZXN0QWJhZG9uTm90RXhp", + "c3RRdWVzdBDiswESGgoUUXVlc3RBbHJlYWR5Q29tcGxldGUQ47MBEhYKEFF1", + "ZXN0Tm90Q29tcGxldGUQ5LMBEhwKFlF1ZXN0QWJhbmRvbk9ubHlOb3JtYWwQ", + "5bMBEhgKElF1ZXN0TWFpbERvY0lzTnVsbBDmswESFAoOUXVlc3REb2NJc051", + "bGwQ57MBEhcKEUVuZFF1ZXN0RG9jSXNOdWxsEOizARIfChlRdWVzdE1ldGFC", + "YXNlTm90SW1wbGVtZW50EOmzARIgChpRdWVzdE5vdGlmeVJlZGlzUmVnaXN0", + "RmFpbBDqswESFwoRUXVlc3RBbHJlYWR5RXhpc3QQ67MBEhsKFVdvcmxkTWV0", + "YURhdGFOb3RGb3VuZBDBuwESGgoUTGFja09mV29ybGRFbnRlckl0ZW0QwrsB", + "Eh4KGFdvcmxkTWFwVHJlZURhdGFOb3RGb3VuZBDDuwESIwodV29ybGRNYXBU", + "cmVlQ2hpbGRMYW5kTm90Rm91bmQQxLsBEhkKE1Jvb21NYXBEYXRhTm90Rm91", + "bmQQxbsBEhoKFExhbmRNZXRhRGF0YU5vdEZvdW5kELW/ARISCgxMYW5kTm90", + "Rm91bmQQtr8BEh8KGUxhbmREb2NMb2FkRHVwbGljYXRlZExhbmQQt78BEhMK", + "DUxhbmREb2NJc051bGwQuL8BEhcKEU93bmVkTGFuZE5vdEZvdW5kELm/ARIp", + "CiNPd25lZExhbmREb2NMb2FkRHVwbGljYXRlZE93bmVkTGFuZBC6vwESGAoS", + "T3duZWRMYW5kRG9jSXNOdWxsELu/ARIdChdMYW5kTWFwVHJlZURhdGFOb3RG", + "b3VuZBC8vwESJgogTGFuZE1hcFRyZWVDaGlsZEJ1aWxkaW5nTm90Rm91bmQQ", + "vb8BEhwKFkxhbmRCdWlsZGluZ0lzTm90RW1wdHkQvr8BEhkKE0xhbmRNYXBE", + "YXRhTm90Rm91bmQQv78BEhQKDkxhbmRFeGlzdE93bmVyEMC/ARIZChNMYW5k", + "RWRpdG9ySXNOb3RVc2VyEMG/ARIZChNMYW5kT3duZXJJc05vdE1hdGNoEMK/", + "ARIeChhCdWlsZGluZ01ldGFEYXRhTm90Rm91bmQQqcMBEhYKEEJ1aWxkaW5n", + "Tm90Rm91bmQQqsMBEicKIUJ1aWxkaW5nRG9jTG9hZER1cGxpY2F0ZWRCdWls", + "ZGluZxCrwwESFwoRQnVpbGRpbmdEb2NJc051bGwQrMMBEhsKFU93bmVkQnVp", + "bGRpbmdOb3RGb3VuZBCtwwESMQorT3duZWRCdWlsZGluZ0RvY0xvYWREdXBs", + "aWNhdGVkT3duZWRCdWlsZGluZxCuwwESHAoWT3duZWRCdWlsZGluZ0RvY0lz", + "TnVsbBCvwwESIQobQnVpbGRpbmdNYXBUcmVlRGF0YU5vdEZvdW5kELDDARIm", + "CiBCdWlsZGluZ01hcFRyZWVDaGlsZFJvb21Ob3RGb3VuZBCxwwESHQoXQnVp", + "bGRpbmdGbG9vcklzTm90RW1wdHkQssMBEh0KF0J1aWxkaW5nTWFwRGF0YU5v", + "dEZvdW5kELPDARIYChJCdWlsZGluZ0V4aXN0T3duZXIQtMMBEicKIUJ1aWxk", + "aW5nTWFwVHJlZVBhcmVudExhbmROb3RGb3VuZBChoAESHQoXQnVpbGRpbmdP", + "d25lcklzTm90TWF0Y2gQtsMBEhwKFk15SG9tZU1ldGFEYXRhTm90Rm91bmQQ", + "nccBEhQKDk15SG9tZU5vdEZvdW5kEJ7HARIjCh1NeUhvbWVEb2NMb2FkRHVw", + "bGljYXRlZE15SG9tZRCfxwESGAoSTXlIb21lQWxyZWFkeUV4aXN0EKDHARIV", + "Cg9NeUhvbWVEb2NJc051bGwQoccBEhUKD015SG9tZUlzTm90TWluZRCixwES", + "JAoeTXlIb21lQ2FudEV4Y2hhbmdlV2hlbkNyYWZ0aW5nEKPHARIiChxFZGl0", + "YWJsZVJvb21NZXRhRGF0YU5vdEZvdW5kEKTHARInCiFFZGl0YWJsZUZyYW1l", + "d29ya01ldGFEYXRhTm90Rm91bmQQpccBEhkKE0ludGVyaW9yUG9pbnRFeGNl", + "ZWQQpscBEhkKE015aG9tZU5vdEVub3VnaFNsb3QQp8cBEhYKEE15aG9tZUlz", + "U2VsZWN0ZWQQqMcBEhsKFU15aG9tZU5hbWVMZW5ndGhTaG9ydBCpxwESGgoU", + "TXlob21lTmFtZUxlbmd0aExvbmcQqscBEhoKFE15aG9tZU5hbWVEdXBsaWNh", + "dGVkEKvHARIeChhNeWhvbWVJbnRlcnBob25lTm90RXhpc3QQrMcBEh4KGE15", + "aG9tZVN0YXJ0UG9pbnROb3RFeGlzdBCtxwESHAoWTXlob21lSW50ZXJwaG9u", + "ZUV4Y2VlZBCuxwESHAoWTXlob21lU3RhcnRQb2ludEV4Y2VlZBCvxwESGwoV", + "Q3JhZnRpbmdDbG90aGVzRXhjZWVkELDHARIbChVDcmFmdGluZ0Nvb2tpbmdF", + "eGNlZWQQsccBEh0KF0NyYWZ0aW5nRnVybml0dXJlRXhjZWVkELLHARIZChNB", + "bmNob3JJc05vdEluTXlob21lELPHARImCiBEb05vdFJlbW92ZVByb2Nlc3ND", + "cmFmdGluZ0FuY2hvchC0xwESGQoTQW5jaG9yR3VpZER1cGxpY2F0ZRC1xwES", + "FgoQTXlob21lSXNFZGl0dGluZxC2xwESFgoQTXlob21lSXNPblJlbnRhbBC3", + "xwESHwoZTXlob21lVWdjSW5mb0ZpbGVOb3RGb3VuZRC4xwESIQobRWRpdGFi", + "bGVSb29tU2l6ZVR5cGVJbnZhbGlkELnHARIYChJDcmFmdGVyQ291bnRFeGNl", + "ZWQQuscBEhsKFU1pbmltYXBNYXJrZXJOb3RGb3VuZBCRywESMQorTWluaW1h", + "cE1hcmtlckRvY0xvYWREdXBsaWNhdGVkTWluaW1hcE1hcmtlchCSywESHAoW", + "TWluaW1hcE1hcmtlckRvY0lzTnVsbBCTywESGgoUQ2FydE1ldGFEYXRhTm90", + "Rm91bmQQhc8BEhgKEkNhcnRNYXhDb3VudEV4Y2VlZBCGzwESGwoVQ2FydFN0", + "YWNrQ291bnRJbnZhbGlkEIfPARIdChdDYXJ0U3RhY2tDb3VudE5vdEVub3Vn", + "aBCIzwESFgoQQ2FydEl0ZW1Ob3RGb3VuZBCJzwESIQobQ2FydE5vdFJlZ2lz", + "dHJ5Q3VycmVuY3lUeXBlEIrPARIdChdDYXJ0SW52YWxpZEN1cnJlbmN5VHlw", + "ZRCLzwESEwoNQ2FydERvY0lzTnVsbBCMzwESFgoQQ2FydERvY0V4Y2VwdGlv", + "bhCNzwESGAoSQ2hhdFNlbmRTZWxmRmFpbGVkEPnSARIZChNDaGF0SW52YWxp", + "ZENoYXRUeXBlEPrSARIgChpDaGF0QmxvY2tVc2VyQ2Fubm90V2hpc3BlchD7", + "0gESHgoYQ2hhdEludmFsaWRNZXNzYWdlTGVuZ3RoEPzSARIYChJDaGF0SW5j", + "bHVkZUJhbldvcmQQ/dIBEhkKE05vdGljZUNoYXREb2NJc051bGwQ7dYBEiQK", + "HkVzY2FwZVBvc2l0aW9uTm90QXZhaWxhYmxlVGltZRDh2gESHQoXRXNjYXBl", + "UG9zaXRpb25Eb2NJc051bGwQ4toBEhgKEkJsb2NrVXNlckRvY0lzTnVsbBDV", + "3gESHwoZQ2hhcmFjdGVyUHJvZmlsZURvY0lzTnVsbBDJ4gESIAoaQ3VzdG9t", + "RGVmaW5lZERhdGFEb2NJc051bGwQ9eQBEh4KGEN1c3RvbURlZmluZWRVaURv", + "Y0lzTnVsbBC95gESGQoTR2FtZU9wdGlvbkRvY0lzTnVsbBCx6gESHAoWR2Ft", + "ZU9wdGlvbkRvY0V4Y2VwdGlvbhCy6gESFAoOTGV2ZWxEb2NJc051bGwQpe4B", + "EhcKEUxvY2F0aW9uRG9jSXNOdWxsEJnyARIUCg5Ob3RVc2FibGVQbGFjZRCa", + "8gESIQobUmVkaXNMb2NhdGlvbkNhY2hlU2V0RmFpbGVkEJvyARIUCg5Nb25l", + "eURvY0lzTnVsbBCB+gESHwoZTW9uZXlDb250cm9sTm90SW5pdGlhbGl6ZRCC", + "+gESFAoOTW9uZXlOb3RFbm91Z2gQg/oBEhsKFU1vbmV5TWF4Q291bnRFeGNl", + "ZWRlZBCE+gESHgoYQ3VycmVuY3lNZXRhRGF0YU5vdEZvdW5kEIX6ARImCiBT", + "aG9wUHJvZHVjdFRyYWRpbmdNZXRlckRvY0lzTnVsbBDpgQISFgoQU2hvcElz", + "TXlIb21lSXRlbRDqgQISGAoSSW52YWxpZFNob3BCdXlUeXBlEOuBAhIdChdT", + "aG9wUHJvZHVjdENhbm5vdFJlbndhbBDsgQISHgoYU2hvcFByb2R1Y3ROb3RS", + "ZW53YWxUaW1lEO2BAhInCiFTaG9wUHJvZHVjdFJlbmV3YWxDb3VudEFscmVh", + "ZHlNYXgQ7oECEhgKElNob3BJdGVtQ2Fubm90U2VsbBDvgQISGAoSUmV3YXJk", + "SW5mb05vdEV4aXN0ENCJAhIXChFSZXdhcmRJbnZhbGlkVHlwZRDRiQISHAoW", + "UmV3YXJkSW52YWxpZFR5cGVWYWx1ZRDSiQISHgoYTm90UmVxdWlyZUF0dHJp", + "YnV0ZVZhbHVlENOJAhIuCihSZXdhcmRHcm91cElkUGFyc2luZ0Zyb21TdHJp", + "bmdUb0ludEVyb3JyENSJAhIWChBDbGFpbUludmFsaWRUeXBlELiRAhIdChdD", + "bGFpbU1lbWJlcnNoaXBOb3RFeGlzdBC5kQISFwoRQ2xhaW1JbmZvTm90RXhp", + "c3QQupECEh4KGENsYWltUmV3YXJkTm90RW5vdWdoVGltZRC7kQISGQoTQ2xh", + "aW1SZXdhcmRFdmVudEVuZBC8kQISFAoOQ2xhaW1Eb2NJc051bGwQvZECEhoK", + "FENyYWZ0UmVjaXBlRG9jSXNOdWxsEKGZAhIYChJDcmFmdEhlbHBEb2NJc051", + "bGwQopkCEhQKDkNyYWZ0RG9jSXNOdWxsEKOZAhIeChhDcmFmdGluZ01ldGFE", + "YXRhTm90Rm91bmQQpJkCEhcKEUNyYWZ0aW5nTm90RmluaXNoEKWZAhIfChlD", + "cmFmdGluZ05vdENyYWZ0aW5nQW5jaG9yEKaZAhIdChdDcmFmdGluZ0FscmVh", + "ZHlDcmFmdGluZxCnmQISHwoZQ3JhZnRpbmdBbmNob3JJc05vdFBsYWNlZBCo", + "mQISIQobQ3JhZnRpbmdSZWNpcGVJc05vdFJlZ2lzdGVyEKmZAhIoCiJDcmFm", + "dGluZ0FuY2hvcklzTm90TWF0Y2hXaXRoUmVjaXBlEKqZAhIbChVDcmFmdGlu", + "Z0hlbHBDb3VudE92ZXIQq5kCEiMKHUNyYWZ0aW5nSGVscFNhbWVVc2VyQ291", + "bnRPdmVyEKyZAhIjCh1DcmFmdGluZ0hlbHBSZWNlaXZlZENvdW50T3ZlchCt", + "mQISGQoTQ3JhZnRIZWxwRG9jSXNFbXB0eRCumQISFQoPQ3JhZnREb2NJc0Vt", + "cHR5EK+ZAhIbChVDcmFmdGluZ0FscmVhZHlGaW5pc2gQsJkCEiUKH0NyYWZ0", + "aW5nUmVjaXBlSXNBbHJlYWR5UmVnaXN0ZXIQsZkCEhcKEUNyYWZ0RG9jRXhj", + "ZXB0aW9uELKZAhIbChVDcmFmdEhlbHBEb2NFeGNlcHRpb24Qs5kCEh0KF0Ny", + "YWZ0UmVjaXBlRG9jRXhjZXB0aW9uELSZAhIeChhDcmFmdEludmFsaWRSZXF1", + "ZXN0Q291bnQQtZkCEh8KGVVncUFwaVNlcnZlclJlcXVlc3RGYWlsZWQQiaEC", + "EicKIVVncUFwaVNlcnZlckNvbnZlcnRUb09iamVjdEZhaWxlZBCKoQISKwol", + "VWdxQXBpU2VydmVySW52YWlsZFNlYXJjaENhdGVnb3J5VHlwZRCLoQISIAoa", + "VWdxUmVwb3J0SW52YWxpZFRleHRMZW5ndGgQjKECEhoKFFVncVF1ZXN0TWV0", + "YU5vdEV4aXN0EI2hAhIeChhVZ3FCZWdpbkNyZWF0b3JQb2ludEZhaWwQjqEC", + "EhgKElVncVF1ZXN0U2h1dGRvd25lZBCPoQISIgocVWdxVGVzdFF1ZXN0QWxy", + "ZWFkeUNvbXBsZXRlZBCQoQISNAouVWdxUmVhc3NpZ25Vc2luZ0l0ZW1FcnJv", + "ckNhdXNlUXVlc3ROb3RDb21wbGV0ZRCRoQISNQovVWdxUmVhc3NpZ25Vc2lu", + "Z0l0ZW1FcnJvckNhdXNlUXVlc3RBbHJlYWR5RXhpc3QQkqECEjYKMFVncVJl", + "YXNzaWduVXNpbmdJdGVtRXJyb3JDYXVzZU5ld1JldmlzaW9uVXBkYXRlZBCT", + "oQISIQobVWdxUXVlc3REYXRhSW52YWxpZFJldmlzaW9uEJShAhIlCh9VZ3FB", + "Ym9ydENhbm5vdENhdXNlSW52YWxpZFN0YXRlEJWhAhIeChhVZ3FNZXRhR2Vu", + "ZXJhdG9yTm90RXhpc3QQlqECEiEKG1VncVF1ZXN0RGF0YVJldmlzaW9uVXBk", + "YXRlZBCXoQISMwotVWdxUmV2aXNpb25DYW5ub3RTbWFsbGVyVGhhblJlcXVl", + "c3RlZFJldmlzaW9uEJihAhIdChdVZ3FSZXZpc2lvblN0YXRlTm90TGl2ZRCZ", + "oQISJgogVWdxUmV2aXNpb25TdGF0ZU9ubHlMaXZlbkFuZFRlc3QQmqECEiYK", + "IFVncUFwaVNlcnZlckh0dHBSZXF1ZXN0RXhjZXB0aW9uEJuhAhIdChdVZ3FR", + "dWVzdFJldmlzaW9uQ2hhbmdlZBCcoQISHwoZVWdxQXNzaWduQ2Fubm90T3du", + "ZWRRdWVzdBCdoQISJQofVWdxQWxyZWFkeU93bmVkT2xkUmV2aXNpb25RdWVz", + "dBCeoQISGQoTU2Vhc29uUGFzc0RvY0lzTnVsbBDxqAISIAoaU2Vhc29uUGFz", + "c01ldGFEYXRhTm90Rm91bmQQ8qgCEiYKIFNlYXNvblBhc3NSZXdhcmRNZXRh", + "RGF0YU5vdEZvdW5kEPOoAhIYChJTZWFzb25QYXNzTWF4R3JhZGUQ9KgCEh0K", + "F1NlYXNvblBhc3NOb3RBYmxlUGVyaW9kEPWoAhIhChtTZWFzb25QYXNzQWxy", + "ZWFkeUJ1eUNoYXJnZWQQ9qgCEiIKHFNlYXNvblBhc3NBbHJlYWR5VGFrZW5S", + "ZXdhcmQQ96gCEh4KGFNlYXNvblBhc3NOb3RFbm91Z2hHcmFkZRD4qAISHwoZ", + "U2Vhc29uUGFzc05lZWRDaGFyZ2VkUGFzcxD5qAISGgoUU2Vhc29uUGFzc0lu", + "dmFsaWRFeHAQ+qgCEhwKFlNlYXNvblBhc3NEb2NFeGNlcHRpb24Q+6gCEiQK", + "HkxhbmRBdWN0aW9uUmVkaXNDYWNoZVNldEZhaWxlZBDZsAISGQoTTGFuZEF1", + "Y3Rpb25Ob3RGb3VuZBDasAISJQofTGFuZEF1Y3Rpb25JbnZhbGlkQXVjdGlv", + "bk51bWJlchDbsAISJwohTGFuZEF1Y3Rpb25CaWRDdXJyZW5jeVR5cGVJbnZh", + "bGlkELTqARIiChxMYW5kQXVjdGlvbkJpZFByaWNlTm90RW5vdWdoEN2wAhIi", + "ChxMYW5kQXVjdGlvbkVtcHR5Q2FjaGVJblJlZGlzEN6wAhIkCh5MYW5kQXVj", + "dGlvblJlZ2lzdHJ5SW5mb0ludmFsaWQQ37ACEhsKFUxhbmRBdWN0aW9uTm90", + "U3RhcnRlZBDgsAISKgokTGFuZEF1Y3Rpb25NaXNtYXRjaEJldHdlZW5DYWNo", + "ZUFuZERiEOGwAhIfChlMYW5kQXVjdGlvbkFscmVhZHlTdGFydGVkEOKwAhIf", + "ChlMYW5kQXVjdGlvbkxhbmRJdGVtTm90U2V0EOOwAhIiChxMYW5kQXVjdGlv", + "bkVkaXRvclR5cGVJbnZhbGlkEOSwAhIdChdMYW5kQXVjdGlvbkFscmVhZHlF", + "bmRlZBDlsAISKgokTGFuZEF1Y3Rpb25IaWdoZXN0QmlkVXNlckF0dHJpYkVy", + "cm9yEOawAhIqCiRMYW5kQXVjdGlvblJlZnVuZEJpZFByaWNlQXR0cmliRXJy", + "b3IQ57ACEjAKKkxhbmRBdWN0aW9uQmlkZGVyUmVmdW5kQmlkUHJpY2VBdHRy", + "aWJFcnJvchDosAISIAoaR2FtZUNvbmZpZ01ldGFEYXRhTm90Rm91bmQQwbgC", + "EioKJEF0dHJpYnV0ZURlZmluZWl0aW9uTWV0YURhdGFOb3RGb3VuZBDCuAIS", + "IQobUmVxdWlyZW1lbnRNZXRhRGF0YU5vdEZvdW5kEMO4AhIgChpTeXN0ZW1N", + "YWlsTWV0YURhdGFOb3RGb3VuZBDEuAISHgoYSW50ZXJpb3JNZXRhRGF0YU5v", + "dEZvdW5kEMW4AhIfChlQcm9wR3JvdXBNZXRhRGF0YU5vdEZvdW5kEMa4AhIX", + "ChFBaUNoYXRTZXJ2ZXJTdGFydBCpwAISGwoVQWlDaGF0U2VydmVyUmVxRmFp", + "bGVkEKrAAhIcChZBaUNoYXRTZXJ2ZXJCYWRyZXF1ZXN0EKvAAhIbChVBaUNo", + "YXRTZXJ2ZXJGb3JiaWRkZW4QrMACEh4KGEFpQ2hhdFNlcnZlclVuYXV0aG9y", + "aXplZBCtwAISHgoYQWlDaGF0U2VydmVyVXNlck5vdEZvdW5kEK7AAhIjCh1B", + "aUNoYXRTZXJ2ZXJDaGFyYWN0ZXJOb3RGb3VuZBCvwAISIgocQWlDaGF0U2Vy", + "dmVyUmVhY3Rpb25Ob3RGb3VuZBCwwAISKAoiQWlDaGF0U2VydmVyRXhhbXBs", + "ZURpYWxvbmdOb3RGb3VuZBCxwAISIQobQWlDaGF0U2VydmVyU2Vzc2lvbk5v", + "dEZvdW5kELLAAhIfChlBaUNoYXRTZXJ2ZXJNb2RlbE5vdEZvdW5kELPAAhIg", + "ChpBaUNoYXRTZXJ2ZXJOb3RFbm91Z2hQb2ludBC0wAISIwodQWlDaGF0U2Vy", + "dmVySW52YWxpZFBhcmFtZXRlcnMQtcACEiYKIEFpQ2hhdFNlcnZlclNlc3Np", + "b25MaW1pdEV4Y2VlZGVkELbAAhIiChxBaUNoYXRTZXJ2ZXJJbnZhbGlkU2ln", + "bmF0dXJlELfAAhIjCh1BaUNoYXRTZXJ2ZXJVc2VyQWxyZWFkeUV4aXN0cxC4", + "wAISHgoYQWlDaGF0U2VydmVyUmVtb3ZlRmFpbGVkELnAAhIfChlBaUNoYXRT", + "ZXJ2ZXJEdXBsaWNhdGVHdWlkELrAAhIdChdBaUNoYXRTZXJ2ZXJEdXBsaWNh", + "dGVJZBC7wAISHgoYQWlDaGF0U2VydmVyQ2hhdE5vdEZvdW5kELzAAhIeChhB", + "aUNoYXRTZXJ2ZXJUb2tlbkV4cGlyZWQQvcACEiAKGkFpQ2hhdFNlcnZlcklu", + "dGVybmFsU2VydmVyEL7AAhIgChpBaUNoYXRTZXJ2ZXJJbnZhbGlkTWVzc2Fn", + "ZRC/wAISIQobQWlDaGF0U2VydmVyVXNlck9ubHlGZWF0dXJlEMDAAhIgChpB", + "aUNoYXRTZXJ2ZXJPd25lcnNoaXBFcnJvchDBwAISKgokQWlDaGF0U2VydmVy", + "Q2hhcmdlT3JkZXJOb3RGb3VuZEVycm9yEMLAAhIVCg9BaUNoYXRTZXJ2ZXJF", + "bmQQjMECEiIKHEFpQ2hhdFNlcnZlclJldHJ5Q2hhcmdlUG9pbnQQjcECEhoK", + "FEFpQ2hhdFNlcnZlckluYWN0aXZlEI7BAhIYChJBaUNoYXREb2NFeGNlcHRp", + "b24Qj8ECEg8KCU5wY0lzQnVzeRCRyAISFwoRTGFja09mRGFpbHlDYWxpdW0Q", + "+c8CEhcKEUxhY2tPZlRvdGFsQ2FsaXVtEPrPAhIeChhMYWNrT2ZDb21taXNz", + "aW9uQ3VycmVuY3kQ+88CEh8KGUxhY2tPZkNvbW1pc3Npb25NYXRlcmlhbHMQ", + "/M8CEhsKFUludmFsaWRNYXRlcmlhbFNsb3RJZBD9zwISFgoQRmFpbFRvTG9h", + "ZENhbGl1bRD+zwISHAoWRmFpbFRvU2F2ZUNhbGl1bUR5bmFtbxD/zwISGQoT", + "TGFja09mQ29udmVydENhbGl1bRCr0AISHwoZR2V0RmFpbEVjaG9TeXN0ZW1S", + "ZXNwb25zZRDc0AISIgocRmFpbFRvR2V0RWNob1N5c3RlbUh0dHBFcnJvchDd", + "0AISJAoeRmFpbFRvR2V0RWNob1N5c3RlbU1lc3NhZ2VOdWxsEN7QAhIiChxG", + "YWlsVG9HZXRFY2hvU3lzdGVtRXhjZXB0aW9uEN/QAhIaChRGYWlsVG9TZW5k", + "RWNob1N5c3RlbRDg0AISHwoZRmFpbFRvR2V0RWNob1N5c3RlbVJvbGxVcBDh", + "0AISHQoXQWNjb3VudExvZ2luQmxvY2tFbmFibGUQ0YYDEhsKFUluc3RhbmNl", + "Um9vbUV4Y2VwdGlvbhC5jgMSJgogSW5zdGFuY2VSb29tQ2Fubm90V3JpdGVF", + "eHRyYUluZm8Quo4DEiYKIEluc3RhbmNlUm9vbU5vdENoYXJnZWRTZWFzb25Q", + "YXNzELuOAxIeChhJbnN0YW5jZU1ldGFEYXRhTm90Rm91bmQQvI4DEiQKHklu", + "c3RhbmNlTWV0YURhdGFPdmVyTGltaXRXcm9uZxC9jgMSHwoZSW5zdGFuY2VB", + "Y2Nlc3NUeXBlSW52YWxpZBC+jgMSIQobSW5zdGFuY2VBY2Nlc3NJdGVtTm90", + "RW5vdWdoEL+OAxIoCiJJbnN0YW5jZUFjY2Vzc1NlYXNvblBhc3NOb3RDaGFy", + "Z2VkEMCOAxIYChJJbnN0YW5jZVJvb21Jc0Z1bGwQwY4DEh4KGEluc3RhbmNl", + "Um9vbUlkRHVwbGljYXRlZBDCjgMSIQobSW5zdGFuY2VSb29tTm90RXhpc3RB", + "dFJlZGlzEMOOAxIeChhUYXNrUmVzZXJ2YXRpb25Eb2NJc051bGwQoZYDEiIK", + "HEJpbGxpbmdHZXRQdXJjaGFzZUluZm9GYWlsZWQQiZ4DEh4KGEJpbGxpbmdV", + "cGRhdGVTdGF0ZUZhaWxlZBCKngMSHQoXQmlsbGluZ0ludmFsaWRTdGF0ZVR5", + "cGUQi54DEikKI0JpbGxpbmdGYWlsZWRQYXJzZVByb2R1Y3RNZXRhSWRUeXBl", + "EIyeAxIdChdCaWxsaW5nU3RhdGVUeXBlSW52YWxpZBCNngMSIwodQmlsbGlu", + "Z1N0YXRlVHlwZUNhbnRCZUNoYW5nZWQQjp4DEhwKFkJpbGxpbmdTdGF0ZVR5", + "cGVSZWZ1bmQQj54DEiQKHkJpbGxpbmdTdGF0ZVR5cGVSZWZ1bmRDb21wbGV0", + "ZRCQngMSGgoUVGF4aU1ldGFEYXRhTm90Rm91bmQQ8aUDEhUKD1RheGlUeXBl", + "SW52YWxpZBDypQMSGgoUV2FycE1ldGFEYXRhTm90Rm91bmQQ86UDEhUKD1dh", + "cnBUeXBlSW52YWxpZBD0pQMSFAoOUmVudGFsTm90Rm91bmQQ2a0DEiMKHVJl", + "bnRhbERvY0xvYWREdXBsaWNhdGVkUmVudGFsENqtAxIVCg9SZW50YWxEb2NJ", + "c051bGwQ260DEhwKFlJlbnRhbE5vdEF2YWlsYWJsZUxhbmQQ3K0DEhoKFFJl", + "bnRhbEFkZHJlc3NJbnZhbGlkEN2tAxIdChdSZW50YWxBZGRyZXNzSXNOb3RF", + "bXB0eRDerQMSHwoZUmVudGFsZmVlTWV0YURhdGFOb3RGb3VuZBDfrQMSHgoY", + "UmVudGFsQ29udHJhY3RJbmZvVXBkYXRlEOCtAxIiChxSZW50YWxDdXJyZW5j", + "eUFtb3VudElzVG9vTG93EMm1AxIfChlSZW50YWxDdXJyZW5jeVR5cGVJc1dy", + "b25nEMq1AxIoCiJQYWNrYWdlTGFzdE9yZGVyUmVjb2RlRG9jRXhjZXB0aW9u", + "EMG1AxIfChlQYWNrYWdlUmVwZWF0RG9jRXhjZXB0aW9uEMK1AxIZChNCZWFj", + "b25TaG9wRXhjZXB0aW9uEKm9AxIfChlCZWFjb25TaG9wSW52YWxpZEFyZ3Vt", + "ZW50EKq9AxInCiFCZWFjb25TaG9wQmVhY29uSXNOb3RJblJlbnRhbEhvbWUQ", + "q70DEhwKFkJlYWNvblNob3BOb3RGb3VuZEl0ZW0QrL0DEh0KF0JlYWNvblNo", + "b3BOb3RFbm91Z2hJdGVtEK29AxIiChxCZWFjb25TaG9wSW52YWxpZEl0ZW1G", + "b3JTZWxsEK69AxIgChpCZWFjb25TaG9wTm90Rm91bmRNZXRhRGF0YRCvvQMS", + "HwoZQmVhY29uU2hvcExvd1NlbGxpbmdQcmljZRCwvQMSJAoeQmVhY29uU2hv", + "cE5vdEVub3VnaFJlZ2lzdGVyRmVlELG9AxIaChRCZWFjb25TaG9wU2xvdElz", + "RnVsbBCyvQMSJwohQmVhY29uU2hvcE92ZXJPbmVEYXlSZWdpc3RlckxpbWl0", + "ELO9AxIeChhCZWFjb25TaG9wRmFpbGVkVG9DcmVhdGUQtL0DEiMKHUJlYWNv", + "blNob3BGYWlsZWRSZWdpc3RlckJvYXJkELW9AxIhChtCZWFjb25TaG9wRmFp", + "bGVkRGVsZXRlQm9hcmQQtr0DEiUKH0JlYWNvblNob3BOb3RGb3VuZEl0ZW1G", + "cm9tQm9hcmQQt70DEiAKGkJlYWNvblNob3BQcm9maWxlRXhjZXB0aW9uELi9", + "AxIlCh9CZWFjb25TaG9wTm90RW5vdWdoUmVnaXN0ZXJHb2xkELm9AxIgChpC", + "ZWFjb25TaG9wTGFja09mSXRlbUFtb3VudBC6vQMSIgocQmVhY29uU2hvcEZh", + "aWxlZEdldEJvYXJkSXRlbRC7vQMSIwodQmVhY29uU2hvcFNvbGRSZWNvcmRF", + "eGNlcHRpb24QvL0DEisKJUJlYWNvblNob3BGYWlsZWRSZWxvYWRCZWFjb25T", + "aG9wSW52ZW4Qvb0DEh0KF0JlYWNvblNob3BVcGRhdGVOZXdEYXRhEL69AxIm", + "CiBCZWFjb25TaG9wRmFpbGVkVXBkYXRlRGF0YUZyb21EYhC/vQMSIwodQmVh", + "Y29uU2hvcE5vdEZvdW5kU29sZFJlY29yZHMQwL0DEiEKG0JlYWNvblNob3BQ", + "cm9maWxlRG9jSXNFbXB0eRDBvQMSIgocQmVhY29uU2hvcFNvbGRQcmljZUV4", + "Y2VwdGlvbhDCvQMSIQobQmVhY29uU2hvcE5vdEZvdW5kU29sZFByaWNlEMO9", + "AxIaChRVZ3FJbnZhbGlkVGFza0FjdGlvbhDh1AMSGwoVVWdxVGFza0FjdGlv", + "bkRpc2FibGVkEOLUAxIaChRVZ3FJbnZhbGlkRGlhbG9nVHlwZRDj1AMSHwoZ", + "VWdxSW52YWxpZERpYWxvZ0NvbmRpdGlvbhDk1AMSGAoSVWdxVmFsaWRhdGlv", + "bkVycm9yEOXUAxITCg1VZ3FOdWxsRW50aXR5EObUAxIZChNVZ3FTdGF0ZUNo", + "YW5nZUVycm9yEOfUAxIbChVVZ3FOb3RFbm91Z2hRdWVzdFNsb3QQ6NQDEhUK", + "D1VncU5vdEFsbG93RWRpdBDp1AMSGgoUVWdxRGlhbG9nSXNOb3RJblRhc2sQ", + "6tQDEhUKD1VncVJlcXVpcmVJbWFnZRDr1AMSFgoQVWdxUmVxdWlyZUJlYWNv", + "bhDs1AMSGQoTVWdxQmVhY29uSW5wdXRFcnJvchDt1AMSGgoUVWdxR2FtZURC", + "QWNjZXNzRXJyb3IQ7tQDEhUKD1VncU5vdE93blVnY05wYxDv1AMSHQoXVWdx", + "QWxyZWFkeUV4aXN0c0FjY291bnQQ8NQDEhgKElVncVNlcnZlckV4Y2VwdGlv", + "bhDx1AMSHgoYVWdxSW52YWxpZFdlYlBvcnRhbFRva2VuEPLUAxIVCg9VZ3FJ", + "bnZhbGlkVG9rZW4Q89QDEhcKEVVncVJlcXVpcmVBY2NvdW50EPTUAxIeChhV", + "Z3FOb3RFbm91Z2hDcmVhdG9yUG9pbnQQ9dQDEhIKDFVncVNsb3RMaW1pdBD2", + "1AMSHwoZVWdxRXhjZWVkVHJhbnNhY3Rpb25SZXRyeRD31AMSGAoSVWdxTWV0", + "YXZlcnNlT25saW5lEPjUAxIUCg5VZ3FBdXRoUmVtb3ZlZBD51AMSGwoVVWdx", + "SW52YWxpZFByZXNldEltYWdlEPrUAxIaChRVZ3FOb3RFbm91Z2hDdXJyZW5j", + "eRD71AMSFgoQVWdxQ3VycmVuY3lFcnJvchD81AMSIgocVWdxQWxyZWFkeUV4", + "aXN0c1Jlc2VydmVHcmFkZRD91AMSGwoVVWdxSW52YWxpZFJlc2VydmVUaW1l", + "EP7UAxIZChNVZ3FTYW1lR3JhZGVSZXNlcnZlEP/UAxIaChRVZ3FBbHJlYWR5", + "Qm9va21hcmtlZBDJ3AMSFgoQVWdxTm90Qm9va21hcmtlZBDK3AMSFQoPVWdx", + "QWxyZWFkeUxpa2VkEMvcAxIRCgtVZ3FOb3RMaWtlZBDM3AMSGAoSVWdxQWxy", + "ZWFkeVJlcG9ydGVkEM3cAxIUCg5VZ3FOb3RPd25RdWVzdBDO3AMSFQoPVWdx", + "SW52YWxpZFN0YXRlEM/cAxIdChdOZnRGb3JPd25lckFsbEdldEZhaWxlZBCx", + "5AMSGQoTSW50ZXJuYWxTZXJ2ZXJFcnJvchDxogQSDgoIUmRiRXJyb3IQ8qIE", + "EhEKC0R5bmFtb0Vycm9yEPOiBBIUCg5JbnZhbGlkUmVxdWVzdBD7ogQSFgoQ", + "UGxhbmV0SWROb3RGb3VuZBDZqgQSIwodUGxhbmV0U2VjcmV0S2V5RG9lc05v", + "dE1hdGNoZWQQ2qoEEhYKEEludmFsaWRQbGFuZXRKd3QQ26oEEhYKEEV4cGly", + "ZWRQbGFuZXRKd3QQ3KoEEiAKGk1ldGF2ZXJzZUNsaWVudE9uQ29ubmVjdGVk", + "EKGsBBIUCg5JbnZhbGlkVXNlckp3dBCirAQSFAoORXhwaXJlZFVzZXJKd3QQ", + "o6wEEhUKD0FjY291bnROb3RGb3VuZBCFrQQSEgoMVXNlck5vdEZvdW5kEIat", + "BBIdChdFeGNoYW5nZU9yZGVySWROb3RGb3VuZBDBsgQSKgokRXhjaGFuZ2VU", + "b3RhbE9yZGVyRGFpbHlMaW1pdEV4Y2VlZGVkEMKyBBIpCiNFeGNoYW5nZVVz", + "ZXJPcmRlckRhaWx5TGltaXRFeGNlZWRlZBDDsgQSFQoPQm90UGxheWVySXNO", + "dWxsENHbOhIcChZDbGllbnRUb0xvZ2luUmVzSXNOdWxsENLbOhIgChpDbGll", + "bnRUb0xvZ2luTWVzc2FnZUlzTnVsbBDT2zoSGwoVQ2xpZW50VG9HYW1lUmVz", + "SXNOdWxsENTbOhIfChlDbGllbnRUb0dhbWVNZXNzYWdlSXNOdWxsENXbOhIb", + "ChVDb25uZWN0ZWRUb1NlcnZlckZhaWwQ1ts6EhoKFFNjZW5hcmlvblBhcmFt", + "SXNOdWxsENfbOhIhChpCYXR0bGVSb29tQ29udGVudHNUeXBlT25seRDBsZ8F", + "Eh4KF0JhdHRsZUluc3RhbmNlVHlwZUVycm9yEMKxnwUSJQoeQmF0dGxlSW5z", + "dGFuY2VJbmZvQWxyZWFkeUV4aXN0EMOxnwUSIQoaQmF0dGxlSW5zdGFuY2VJ", + "bmZvTm90RXhpc3QQxLGfBRIkCh1CYXR0bGVJbnN0YW5jZUpvaW5QbGF5ZXJF", + "cnJvchDFsZ8FEi0KJkJhdHRsZUluc3RhbmNlVXNhYmxlU3Bhd25Qb2ludE5v", + "dEV4aXN0EMaxnwUSHQoWQmF0dGxlSW5zdGFuY2VJbkFjdGl2ZRDHsZ8FEiQK", + "HUJhdHRsZUluc3RhbmNlTm90RXhpc3RBbmNob3JzEMixnwUSJQoeQmF0dGxl", + "SW5zdGFuY2VBZGRQb2RDb21iYXRGYWlsEMmxnwUSJwogQmF0dGxlSW5zdGFu", + "Y2VPYmplY3RNZXRhTm90RXhpc3QQyrGfBRIwCilCYXR0bGVJbnN0YW5jZU9i", + "amVjdEludGVyYWN0aW9uTm90eWV0VGltZRDLsZ8FEiMKHEJhdHRsZUluc3Rh", + "bmNlT2JqZWN0Tm90RXhpc3QQzLGfBRIrCiRCYXR0bGVJbnN0YW5jZVBvZENv", + "bWJhdEFscmVhZHlPY2N1cHkQzbGfBRIvCihCYXR0bGVJbnN0YW5jZU9iamVj", + "dEludGVyYWN0aW9uTm90QWN0aXZlEM+xnwUSKwokQmF0dGxlSW5zdGFuY2VN", + "ZXRhQ29uZmlnTm90RXhpc3REYXRhENCxnwUSKwokQmF0dGxlSW5zdGFuY2VN", + "ZXRhUmV3YXJkTm90RXhpc3REYXRhENGxnwUSMwosQmF0dGxlSW5zdGFuY2VQ", + "aWNrdXBQb2RHZW5lcmF0ZWRUaW1lTm90RXhpc3QQ0rGfBRIqCiNCYXR0bGVJ", + "bnN0YW5jZVBpY2t1cFBvZE5vdEV4aXN0RGF0YRDTsZ8FEicKIEJhdHRsZUlu", + "c3RhbmNlTm90RXhpc3RQbGF5ZXJJbmZvENSxnwUSJAodQmF0dGxlSW5zdGFu", + "Y2VJbnRlcmFjdGlvbkZhaWwQ1bGfBRIxCipCYXR0bGVJbnN0YW5jZVBpY2t1", + "cFBvZFJld2FyZEFsbG9jYXRlRXJyb3IQ1rGfBRImCh9CYXR0bGVJbnN0YW5j", + "ZU5vdEV4aXN0RXZlbnRJbmZvENexnwUSIAoZQmF0dGxlSW5zdGFuY2VDbG9z", + "aW5nVGltZRDYsZ8FEiIKG0JhdHRsZUluc3RhbmNlU2VxUGFyc2VFcnJvchDZ", + "sZ8FEiIKG0dhbWVNb2RlSm9pbkhhbmRsZXJOb3RFeGlzdBCksp8FEiIKG0dh", + "bWVNb2RlSW5pdEhhbmRsZXJOb3RFeGlzdBClsp8FEikKIkdhbWVNb2RlSm9p", + "blN1Y2Nlc3NIYW5kbGVyTm90RXhpc3QQprKfBRIMCghEdXBMb2dpbhABEgoK", + "Bk1vdmluZxACEgsKB0RiRXJyb3IQAxIMCghLaWNrRmFpbBAEEhYKEk5vdENv", + "cnJlY3RQYXNzd29yZBAFEhAKDE5vdEZvdW5kVXNlchAGEhAKDE5vR2FtZVNl", + "cnZlchAHEhAKDExvZ2luUGVuZGluZxAIEhIKDk5vdEltcGxlbWVudGVkEAkS", + "HQoZTm90RXhpc3RTZWxlY3RlZENoYXJhY3RlchAKEhUKEU5vdEV4aXN0Q2hh", + "cmFjdGVyEAsSFAoQU2VydmVyTG9naWNFcnJvchAMEhEKDU5vUGVybWlzc2lv", + "bnMQDhINCglSZWRpc0ZhaWwQDxIOCglMb2dpbkZhaWwQ6AcSEwoORHVwbGlj", + "YXRlZFVzZXIQ6QcSEQoMSW52YWxpZFRva2VuEOoHEhUKEE5vdENvcnJlY3RT", + "ZXJ2ZXIQ6wcSDwoKSW5zcGVjdGlvbhDsBxIOCglCbGFja0xpc3QQ7QcSDwoK", + "U2VydmVyRnVsbBDuBxITCg5Ob3RGb3VuZFNlcnZlchDvBxISCg1Ob3RGb3Vu", + "ZFRhYmxlEPAHEg8KClRhYmxlRXJyb3IQ8QcSFQoQSW52YWxpZENvbmRpdGlv", + "bhDMCBITCg5DaGFyQ3JlYXRlRmFpbBDQDxITCg5DaGFyU2VsZWN0RmFpbBC0", + "EBITCg5DcmVhdGVSb29tRmFpbBC4FxIRCgxKb2luUm9vbUZhaWwQnBgSFQoQ", + "Sm9pbkluc3RhbmNlRmFpbBCAGRIbChZOb3RFeGlzdEluc3RhbmNlVGlja2V0", + "EIEZEhYKEUxlYXZlSW5zdGFuY2VGYWlsEOQZEhkKFE5vdEV4aXN0SW5zdGFu", + "Y2VSb29tEMgaEhsKFk5vdENvcnJlY3RJbnN0YW5jZVJvb20QyRoSDwoKUG9z", + "SXNFbXB0eRDKGhIUCg9FbnRlck15SG9tZUZhaWwQ2B0SFAoPTGVhdmVNeUhv", + "bWVGYWlsELweEhcKEkV4Y2hhbmdlTXlIb21lRmFpbBCgHxIXChJOb3RGb3Vu", + "ZE15SG9tZURhdGEQoR8SEwoOTm90TXlIb21lT3duZXIQoh8SFgoRQWxyZWFk", + "eUhhdmVNeUhvbWUQox8SGwoWRXhjaGFuZ2VNeUhvbWVQcm9wRmFpbBCEIBIZ", + "ChRFeGNoYW5nZUJ1aWxkaW5nRmFpbBDMIRIfChpFeGNoYW5nZUJ1aWxkaW5n", + "TEZQcm9wRmFpbBCwIhIZChRFeGNoYW5nZUluc3RhbmNlRmFpbBCUIxIhChxF", + "eGNoYW5nZVNvY2lhbEFjdGlvblNsb3RGYWlsEPgjEh0KGE5vdEZvdW5kU29j", + "aWFsQWN0aW9uRGF0YRD6IxIaChVOb3RJblNvY2lhbEFjdGlvblNsb3QQ+yMS", + "HAoXQWxyZWFkeUhhdmVTb2NpYWxBY3Rpb24Q/CMSGQoUTm90Rm91bmRCdWls", + "ZGluZ0RhdGEQrCQSFgoRTm90Rm91bmRGbG9vckluZm8QrSQSFgoRTm90Rm91", + "bmRJbmR1bkRhdGEQryQSGQoURW50ZXJGaXR0aW5nUm9vbUZhaWwQ3CQSGgoV", + "Tm90RW50ZXRlZEZpdHRpbmdSb29tEN0kEhAKC01ha2VGYWlsT3RwEN4kEh0K", + "GE5vdEV4aXN0Um9vbUluZm9Gb3JFbnRlchDfJBIfChpOb3RFeGlzdEdhbWVT", + "ZXJ2ZXJGb3JFbnRlchDgJBIVChBQb3NpdGlvblNhdmVGYWlsEOEkEhwKF0V4", + "Y2hhbmdlRW1vdGlvblNsb3RGYWlsEMAlEhoKFUVtb3Rpb25TbG90T3V0T2ZS", + "YW5nZRDBJRIcChdOb3RGb3VuZEFuY2hvckd1aWRJbk1hcBCjJhIXChJOb3RG", + "b3VuZEFuY2hvckd1aWQQpCYSEwoOUHJvcElzT2NjdXBpZWQQpSYSDwoKUHJv", + "cElzVXNlZBCmJhIUCg9Qcm9wVHlwZWlzV3JvbmcQpyYSGAoTTm90Rm91bmRF", + "bW90aW9uRGF0YRC4JhIVChBOb3RJbkVtb3Rpb25TbG90ELkmEhMKDkNyZWF0", + "ZUl0ZW1GYWlsEIQnEhAKC0FkZEl0ZW1GYWlsEIUnEhMKDkRlbGV0ZUl0ZW1G", + "YWlsEIYnEhIKDU5vTW9yZUFkZEl0ZW0QhycSEQoMTm90Rm91bmRJdGVtEIgn", + "EhIKDU5vdEVub3VnaEl0ZW0QiScSFQoQSW52YWxpZFNsb3RJbmRleBCKJxIX", + "ChJEdXBsaWNhdGVkSXRlbUd1aWQQiycSGAoTTm90Rm91bmRJdGVtVGFibGVJ", + "ZBCMJxIUCg9Ob3RTZWxlY3RlZENoYXIQjScSEAoLTm90Rm91bmRNYXAQjicS", + "EQoMTm90RW1wdHlTbG90EI8nEg4KCUVtcHR5U2xvdBCQJxIYChNOb3RGb3Vu", + "ZEJ1ZmZUYWJsZUlkEJEnEhEKDE5vdEZvdW5kQnVmZhCSJxIcChdOb3RFeGlz", + "dEZvcmVjZWRNb3ZlSW5mbxCTJxIXChJEdXBsaWNhdGVkTmlja05hbWUQlScS", + "FwoSQWxyZWFkeVNldE5pY2tOYW1lEJYnEhkKFERpc2FsbG93ZWRDaGFyYWN0", + "ZXJzEJcnEhMKDkRiVXBkYXRlRmFpbGVkEJgnEhQKD0ludmFsaWRBcmd1bWVu", + "dBCZJxIUCg9NYWlsU3lzdGVtRXJyb3IQpicSEgoNSW52YWxpZFRhcmdldBCn", + "JxIRCgxOb3RGb3VuZE1haWwQqCcSFAoPRW1wdHlJdGVtSW5NYWlsEKknEhYK", + "EU1haWxTZW5kQ291bnRPdmVyEKonEhQKD0NoYW5nZWROaWNrTmFtZRCrJxIW", + "ChFTdGF0ZUNoYW5nZUZhaWxlZBCwJxITCg5Ob3RGb3VuZFRhcmdldBC6JxIW", + "ChFCbG9ja2VkRnJvbVRhcmdldBC7JxIRCgxMb2dPZmZUYXJnZXQQvCcSEwoO", + "Q2FudFNlbmRUb1NlbGYQvScSFAoPQnVmZlR5cGVJc1dyb25nEM4nEhkKFFJl", + "Z2lzdGVyVG9vbFNsb3RGYWlsENgnEhsKFkRlcmVnaXN0ZXJUb29sU2xvdEZh", + "aWwQ2ScSFwoSVG9vbFNsb3RPdXRPZlJhbmdlENonEhUKEE5vdEZvdW5kVG9v", + "bFNsb3QQ2ycSEgoNRW1wdHlUb29sU2xvdBDcJxIdChhGb2xkZXJOYW1lRXhj", + "ZWVkZWRMZW5ndGgQ4icSGwoWRm9sZGVyTmFtZUFscmVhZHlFeGlzdBDjJxIX", + "ChJGb2xkZXJOYW1lTm90RXhpc3QQ5CcSIgodRm9sZGVyTmFtZUFscmVhZHlN", + "YXhIb2xkQ291bnQQ5ScSFgoRRm9sZGVyQ291bnRFeGNlZWQQ5icSFQoQRm9s", + "ZGVyQ3JlYXRlRmFpbBDnJxIeChlGb2xkZXJSZU5hbWVDYW5ub3REZWZhdWx0", + "EOgnEhoKFUZvbGRlck9kZXJ0eXBlSW52YWxpZBDpJxIeChlGcmllbmRSZXF1", + "ZXN0Tm90RXhpc3RJbmZvEOwnEh0KGEZyaWVuZFJlcXVlc3RBbHJlYWR5U2Vu", + "ZBDtJxIgChtGcmllbmRSZXF1ZXN0QWxyZWFkeVJlY2VpdmUQ7icSIAobRnJp", + "ZW5kUmVxdWVzdENhbnRTZW5kVG9TZWxmEO8nEiYKIUZyaWVuZFJlcXVlc3RO", + "b3RFeGlzdFJlY2VpdmVkSW5mbxDwJxIfChpGcmllbmRSZXF1ZXN0QWxyZWFk", + "eUZyaWVuZBDxJxIQCgtJbnZhbGlkVHlwZRDyJxIZChRJbnZhbGlkQXR0cmli", + "dXRlU2xvdBDzJxIlCiBGcmllbmRSZXF1ZXN0Q2Fubm90U2VuZEJsb2NrVXNl", + "chD/JxIeChlBZGRGcmllbmRBbHJlYWR5QmxvY2tVc2VyEIAoEh8KGkFkZEZy", + "aWVuZE5vdEV4aXN0Q2hhcmFjdGVyEIEoEhsKFkFkZEZyaWVuZEFscmVhZHlG", + "cmllbmQQgigSIgodQWRkRnJpZW5kQWxyZWFkeUNhbmNlbFJlcXVlc3QQgygS", + "HAoXQWRkRnJpZW5kQWxyZWFkeUV4cGlyZWQQhCgSFwoSRnJpZW5kSW5mb05v", + "dEV4aXN0EIooEiUKIEZyaWVuZEluZm9NeUNvdW50QWxyZWFkeU1heENvdW50", + "EIsoEikKJEZyaWVuZEluZm9PdGhlcnNDb3VudEFscmVhZHlNYXhDb3VudBCM", + "KBIWChFGcmllbmRJbmZvT2ZmbGluZRCNKBIbChZGcmllbmRJbmZvQWxyZWFk", + "eUV4aXN0EI4oEiEKHEZyaWVuZEludml0ZU15UG9zSXNOb3RNeUhvbWUQlCgS", + "IQocRnJpZW5kSW52aXRlRG9udERpc3R1cmJTdGF0ZRCVKBIhChxGcmllbmRJ", + "bnZpdGVFeHBpcmVUaW1lUmVtYWluEJYoEiMKHkZyaWVuZEludml0ZVdhaXRp", + "bmdPdGhlckludml0ZRCXKBIeChlGcmllbmRJbnZpdGVBbHJlYWR5RXhwaXJl", + "EJgoEh8KGkZyaWVuZEtpY2tNeVBvc0lzTm90TXlIb21lEJkoEh0KGEZyaWVu", + "ZEtpY2tNZW1iZXJOb3RFeGlzdBCaKBIcChdGcmllbmRJc0luQW5vdGhlck15", + "aG9tZRCbKBIWChFCbG9ja1VzZXJNYXhDb3VudBCeKBIaChVCbG9ja1VzZXJB", + "bHJlYWR5QmxvY2sQnygSHgoZQmxvY2tVc2VyQ2Fubm90U2VuZE1haWxUbxCg", + "KBITCg5CbG9ja2VkQnlPdGhlchChKBIdChhCbG9ja1VzZXJDYW5ub3RXaGlz", + "cGVyVG8QoigSEwoOQmxvY2tJbmZvRW1wdHkQoygSHwoaQmxvY2tVc2VyQ2Fu", + "bm90SW52aXRlUGFydHkQpCgSIwoeQ2FydFNlbGxUeXBlTWlzc01hdGNoV2l0", + "aFRhYmxlENAoEhgKE0NhcnRGdWxsU3RhY2tvZkl0ZW0Q0SgSDwoKQ2FydGlz", + "RnVsbBDSKBIQCgtCYW5OaWNrTmFtZRDTKBIZChRDdXJyZW5jeU5vdEZvdW5k", + "RGF0YRCYKhIWChFDdXJyZW5jeU5vdEVub3VnaBCZKhIZChRDdXJyZW5jeUlu", + "dmFsaWRWYWx1ZRCaKhIUCg9TdGFydEJ1ZmZGYWlsZWQQ/CoSEwoOU3RvcEJ1", + "ZmZGYWlsZWQQ/SoSFQoQTm90Rm91bmROaWNrTmFtZRDgKxIgChtOb3RGb3Vu", + "ZFRhdHRvb0F0dHJpYnV0ZURhdGEQqC0SEwoOTm90Rm91bmRTaG9wSWQQjC4S", + "GAoTVGltZU92ZXJGb3JQdXJjaGFzZRCNLhIXChJOb3RFbm91Z2hBdHRyaWJ1", + "dGUQji4SEgoNSW52YWxpZEdlbmRlchCPLhIQCgtJc0VxdWlwSXRlbRCQLhIQ", + "CgtJbnZhbGlkSXRlbRCRLhIWChFBbHJlYWR5UmVnaXN0ZXJlZBCSLhIVChBO", + "b3RGb3VuZFNob3BJdGVtEJMuEhYKEU5vdEVub3VnaFNob3BJdGVtEJQuEhUK", + "EEludmFsaWRJdGVtQ291bnQQlS4SEgoNSW52ZW50b3J5RnVsbBCWLhIWChFO", + "b3RGb3VuZFByb2R1Y3RJZBCXLhIdChhSYW5kb21Cb3hJdGVtRGF0YUludmFs", + "aWQQ1C8SFgoRTm90RXhpc3RHYWNoYURhdGEQ1S9CLworY29tLmNhbGl2ZXJz", + "ZS5hZG1pbi5kb21haW4uUmFiYml0TXEubWVzc2FnZVABYgZwcm90bzM=")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ServerErrorCode), }, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Result), global::Result.Parser, new[]{ "ErrorCode", "ResultString" }, null, null, null, null) + })); + } + #endregion + +} +#region Enums +/// +/// 나중에 Error 카테고리별 범위 구성을 설정 해야 한다. - kangms +/// +public enum ServerErrorCode { + [pbr::OriginalName("Success")] Success = 0, + /// + ///============================================================================================= + /// 결과 코드 관련 오류 + ///============================================================================================= + /// + [pbr::OriginalName("ResultCodeNotSet")] ResultCodeNotSet = -1, + /// + ///============================================================================================= + /// 시스템 오류 : 10000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TryCatchException")] TryCatchException = 10001, + /// + /// DotNet 예외가 발생 했습니다. + /// + [pbr::OriginalName("DotNetException")] DotNetException = 10002, + /// + /// ProudNet 예외가 발생 했습니다. + /// + [pbr::OriginalName("ProudNetException")] ProudNetException = 10003, + /// + /// RabbitMQ 예외가 발생 했습니다. + /// + [pbr::OriginalName("RabbitMqException")] RabbitMqException = 10004, + /// + /// DynamoDB 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbException")] DynamoDbException = 10005, + /// + /// DynamoDB Transact 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbTransactException")] DynamoDbTransactException = 10006, + /// + /// Redis 예외가 발생 했습니다. + /// + [pbr::OriginalName("RedisException")] RedisException = 10007, + /// + /// Meta 스키마 및 데이터 예외가 발생 했습니다. + /// + [pbr::OriginalName("MetaInfoException")] MetaInfoException = 10008, + /// + /// MySqlDB 예외가 발생 했습니다. + /// + [pbr::OriginalName("MySqlDbException")] MySqlDbException = 10009, + /// + ///============================================================================================= + /// NLog 관련 오류 : 10030 ~ + ///============================================================================================= + /// + [pbr::OriginalName("NLogWithAwsCloudWatchSetupFailed")] NlogWithAwsCloudWatchSetupFailed = 10031, + /// + ///============================================================================================= + /// 비즈니스 로그 관련 오류 : 10050 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LogActionIsNull")] LogActionIsNull = 10051, + /// + /// LogAppender 객체가 Null 입니다. + /// + [pbr::OriginalName("LogAppenderIsNull")] LogAppenderIsNull = 10052, + /// + /// LogFormatter 객체가 Null 입니다. + /// + [pbr::OriginalName("LogFormatterIsNull")] LogFormatterIsNull = 10053, + /// + /// LogActionType 오류 입니다. + /// + [pbr::OriginalName("LogActionTypeInvalid")] LogActionTypeInvalid = 10054, + /// + ///============================================================================================= + /// 네트워크 오류 : 10100 ~ + ///============================================================================================= + /// ProudNet 오류 + /// + [pbr::OriginalName("RmiHostIsNull")] RmiHostIsNull = 10101, + /// + /// Rmi Host 핸들러에 바인딩을 실패 했습니다. + /// + [pbr::OriginalName("RmiHostHandlerBindFailed")] RmiHostHandlerBindFailed = 10102, + /// + /// Stub 핸들러에 바인딩을 실패 했습니다. + /// + [pbr::OriginalName("SubHandlerBindFailed")] SubHandlerBindFailed = 10103, + /// + /// Stub 과 Proxy 연결을 실패 했습니다. + /// + [pbr::OriginalName("SutbAndProxyAttachFailed")] SutbAndProxyAttachFailed = 10104, + /// + /// 패킷 최대 사이즈 설정 실패. + /// + [pbr::OriginalName("SetMessageMaxLengthFailed")] SetMessageMaxLengthFailed = 10105, + /// + /// 네트워크 모듈 오류 + /// + [pbr::OriginalName("PacketRecvHandlerRegisterFailed")] PacketRecvHandlerRegisterFailed = 10151, + /// + /// 패킷 송신 핸들러에 등록을 실패 했습니다. + /// + [pbr::OriginalName("PacketSendHandlerRegisterFailed")] PacketSendHandlerRegisterFailed = 10152, + /// + /// 패킷 오류 + /// + [pbr::OriginalName("PacketRecvInvalid")] PacketRecvInvalid = 10153, + /// + /// 수신 패킷 핸들러를 찾지 못했습니다. + /// + [pbr::OriginalName("RacketRecvHandlerNotFound")] RacketRecvHandlerNotFound = 10154, + /// + /// 대용량 패킷이 모두 수신되지 않았습니다. + /// + [pbr::OriginalName("LargePacketNotAllReceived")] LargePacketNotAllReceived = 10155, + /// + /// 대용량 패킷이 수신 대기 시간이 오래 지났다. + /// + [pbr::OriginalName("LargePacketRecvTimeOver")] LargePacketRecvTimeOver = 10156, + /// + ///============================================================================================= + /// DB 오류 : 10200 ~ + ///============================================================================================= + /// DB 공통 오류 + /// + [pbr::OriginalName("DbQueryTypeInvalid")] DbQueryTypeInvalid = 10201, + /// + /// DynamoDB 오류 + /// + [pbr::OriginalName("DynamoDbTransactionCanceledException")] DynamoDbTransactionCanceledException = 10211, + /// + /// DynamoDB AmazonDynamoDBException 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbAmazonDynamoDbException")] DynamoDbAmazonDynamoDbException = 10212, + /// + /// DynamoDB AmazonServiceException 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbAmazonServiceException")] DynamoDbAmazonServiceException = 10213, + /// + /// DynamoDB Config 파일 로딩을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbConfigLoadFailed")] DynamoDbConfigLoadFailed = 10214, + /// + /// DynamoDB 연결을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbConnectFailed")] DynamoDbConnectFailed = 10215, + /// + /// DynamoDB Table 생성을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbTableCreateFailed")] DynamoDbTableCreateFailed = 10216, + /// + /// DynamoDB Table과 연결이 되어있지 않습니다. + /// + [pbr::OriginalName("DynamoDbTableNotConnected")] DynamoDbTableNotConnected = 10217, + /// + /// DynamoDB Query를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbQueryFailed")] DynamoDbQueryFailed = 10218, + /// + /// DynamoDB Item 저장 크기를 초과 했습니다. + /// + [pbr::OriginalName("DynamoDbItemSizeExceeded")] DynamoDbItemSizeExceeded = 10219, + /// + /// DynamoDB Query와 일치하는 Attribute가 아닙니다. + /// + [pbr::OriginalName("DynamoDbQueryNoMatchAttribute")] DynamoDbQueryNoMatchAttribute = 10220, + /// + /// DynamoDB TransactionConflictException 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbTransactionConflictException")] DynamoDbTransactionConflictException = 10221, + /// + /// DynamoDB Expression 오류 입니다. + /// + [pbr::OriginalName("DynamoDbExpressionError")] DynamoDbExpressionError = 10222, + /// + /// DynamoDB PrimaryKey 를 찾을 수 없습니다. + /// + [pbr::OriginalName("DynamoDbPrimaryKeyNotFound")] DynamoDbPrimaryKeyNotFound = 10223, + /// + /// DynamoDB Table Name 오류 입니다. + /// + [pbr::OriginalName("DynamoDbTableNameInvalid")] DynamoDbTableNameInvalid = 10224, + /// + /// DynamoDB 연결 객체가 Null 입니다. + /// + [pbr::OriginalName("DynamoDbConnectorIsNull")] DynamoDbConnectorIsNull = 10225, + /// + /// DynamoDB Table 이름이 중복 되었습니다. + /// + [pbr::OriginalName("DynamoDbTableNameDuplicated")] DynamoDbTableNameDuplicated = 10226, + /// + /// DynamoDB 쿼리 오류 + /// + [pbr::OriginalName("DynamoDbQueryException")] DynamoDbQueryException = 10231, + /// + /// DynamoDbQuery Request를 가지고 있지 않습니다. + /// + [pbr::OriginalName("DynamoDbQueryNoRequested")] DynamoDbQueryNoRequested = 10232, + /// + /// DynamoDbQuery Document를 찾을 수 없습니다. + /// + [pbr::OriginalName("DynamoDbQueryNotFoundDocumentQuery")] DynamoDbQueryNotFoundDocumentQuery = 10233, + /// + /// DynamoDbQuery ExceptionNotifier를 찾을 수 없습니다. + /// + [pbr::OriginalName("DynamoDbQueryExceptionNotifierNotFound")] DynamoDbQueryExceptionNotifierNotFound = 10234, + /// + /// DynamoDB Document 오류 + /// + [pbr::OriginalName("DynamoDbDocumentIsNullInQueryContext")] DynamoDbDocumentIsNullInQueryContext = 10241, + /// + /// DynamoDbDocument내의 정보에 오류가 있습니다. + /// + [pbr::OriginalName("DynamoDbDocumentIsInvalid")] DynamoDbDocumentIsInvalid = 10242, + /// + /// DynamoDbDocumentQueryContext내의 Type 정보 오류 입니다. + /// + [pbr::OriginalName("DynamoDbDocumentQueryContextTypeInvalid")] DynamoDbDocumentQueryContextTypeInvalid = 10243, + /// + /// DynamoDbDocument를 Doc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocumentCopyFailedToDoc")] DynamoDbDocumentCopyFailedToDoc = 10244, + /// + /// DynamoDbDocument Upsert를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocumentUpsertFailed")] DynamoDbDocumentUpsertFailed = 10245, + /// + /// DynamoDB ItemRequest 오류 + /// + [pbr::OriginalName("DynamoDbItemRequestIsInvalid")] DynamoDbItemRequestIsInvalid = 10251, + /// + /// DynamoDbItemRequestQueryContext내의 Type 정보 오류 입니다. + /// + [pbr::OriginalName("DynamoDbItemRequestQueryContextTypeInvalid")] DynamoDbItemRequestQueryContextTypeInvalid = 10252, + /// + /// DynamoDbItemRequest내에 UpdateExpression 정보가 비어 있다. + /// + [pbr::OriginalName("DynamoDbItemRequestUpdateExpressionEmpty")] DynamoDbItemRequestUpdateExpressionEmpty = 10253, + /// + /// DynamoDB Custom Doc 오류 + /// + [pbr::OriginalName("DynamoDbDocPkInvalid")] DynamoDbDocPkInvalid = 10261, + /// + /// DynamoDbDoc의 SK 값이 오류 입니다. + /// + [pbr::OriginalName("DynamoDbDocSkInvalid")] DynamoDbDocSkInvalid = 10262, + /// + /// DynamoDbDoc의 AttribType이 중복 되었습니다. + /// + [pbr::OriginalName("DynamoDbDocAttribTypeDuplicated")] DynamoDbDocAttribTypeDuplicated = 10263, + /// + /// Doc를 DynamoDbDocument에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyFailedToDocument")] DynamoDbDocCopyFailedToDocument = 10264, + /// + /// DynamoDbDocument를 Doc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyFailedFromDocument")] DynamoDbDocCopyFailedFromDocument = 10265, + /// + /// DynamoDbDocType이 일치하지 않습니다. + /// + [pbr::OriginalName("DynamoDbDocTypeNotMatch")] DynamoDbDocTypeNotMatch = 10266, + /// + /// DynamoDbRequest 오류 입니다. + /// + [pbr::OriginalName("DynamoDbRequestInvalid")] DynamoDbRequestInvalid = 10267, + /// + /// DynamoDbDoc AttributeState 플래그를 선택하지 않았습니다. + /// + [pbr::OriginalName("DynamoDbDocAttributeStateNotSet")] DynamoDbDocAttributeStateNotSet = 10268, + /// + /// DynamoDbDoc AttribWrapper 복사를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocAttribWrapperCopyFailed")] DynamoDbDocAttribWrapperCopyFailed = 10269, + /// + /// DynamoDbDoc Attribute 가져오기를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocAttributeGettingFailed")] DynamoDbDocAttributeGettingFailed = 10270, + /// + /// DynamoDbDoc LinkPKSK 값 오류 입니다. + /// + [pbr::OriginalName("DynamoDbDocLinkPkSkInvalid")] DynamoDbDocLinkPkSkInvalid = 10271, + /// + /// DynamoDbDoc AttribWrapper가 Null 입니다. + /// + [pbr::OriginalName("DynamoDbDocAttribWrapperIsNull")] DynamoDbDocAttribWrapperIsNull = 10272, + /// + /// MySql 오류 + /// + [pbr::OriginalName("MySqlConnectionCreateFailed")] MySqlConnectionCreateFailed = 10281, + /// + /// MySqlConnection Open을 실패 했습니다. + /// + [pbr::OriginalName("MySqlConnectionOpenFailed")] MySqlConnectionOpenFailed = 10282, + /// + /// MySql 쿼리 오류 + /// + [pbr::OriginalName("MySqlDbQueryException")] MySqlDbQueryException = 10283, + /// + ///============================================================================================= + /// Cache 오류 : 10300 ~ + ///============================================================================================= + /// Redis 오류 + /// + [pbr::OriginalName("RedisServerConnectFailed")] RedisServerConnectFailed = 10301, + /// + /// Redis Strings 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisStringsWriteFailed")] RedisStringsWriteFailed = 10302, + /// + /// Redis Strings 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisStringsReadFailed")] RedisStringsReadFailed = 10303, + /// + /// Redis Sets 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSetsWriteFailed")] RedisSetsWriteFailed = 10304, + /// + /// Redis Sets 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSetsReadFailed")] RedisSetsReadFailed = 10305, + /// + /// Redis SortedSets 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSortedSetsWriteFailed")] RedisSortedSetsWriteFailed = 10306, + /// + /// Redis SortedSets 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSortedSetsReadFailed")] RedisSortedSetsReadFailed = 10307, + /// + /// Redis Hashes 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisHashesWriteFailed")] RedisHashesWriteFailed = 10308, + /// + /// Redis Hashes 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisHashesReadFailed")] RedisHashesReadFailed = 10309, + /// + /// Redis Lists 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisListsWriteFailed")] RedisListsWriteFailed = 10310, + /// + /// Redis Lists 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisListsReadFailed")] RedisListsReadFailed = 10311, + /// + /// Redis Request의 Key 값이 없습니다. + /// + [pbr::OriginalName("RedisRequestKeyIsEmpty")] RedisRequestKeyIsEmpty = 10312, + /// + /// Redis에서 LoginCache 정보 조회를 실패했습니다. + /// + [pbr::OriginalName("RedisLoginCacheGetFailed")] RedisLoginCacheGetFailed = 10313, + /// + /// Redis에 LoginCache 정보를 저장을 실패했습니다. + /// + [pbr::OriginalName("RedisLoginCacheSetFailed")] RedisLoginCacheSetFailed = 10314, + /// + /// RedisPrivateCache가 중복 등록 되었습니다. + /// + [pbr::OriginalName("RedisPrivateCacheDuplicated")] RedisPrivateCacheDuplicated = 10315, + /// + /// RedisGlobalSharedCache가 중복 등록 되었습니다. + /// + [pbr::OriginalName("RedisGlobalSharedCacheDuplicated")] RedisGlobalSharedCacheDuplicated = 10316, + /// + /// RedisLoginCache Owner UserGuid가 일치하지 않습니다. + /// + [pbr::OriginalName("RedisLoginCacheOwnerUserGuidNotMatch")] RedisLoginCacheOwnerUserGuidNotMatch = 10317, + /// + /// Redis PartyCache 읽기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyCacheGetFailed")] RedisGlobalPartyCacheGetFailed = 10318, + /// + /// Redis PartyCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyCacheWriteFailed")] RedisGlobalPartyCacheWriteFailed = 10319, + /// + /// Redis PartyMemberCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyMemberCacheWriteFailed")] RedisGlobalPartyMemberCacheWriteFailed = 10320, + /// + /// Redis PartyServerCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyServerCacheWriteFailed")] RedisGlobalPartyServerCacheWriteFailed = 10321, + /// + /// Redis PartyInvitePartySendCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyInvitePartySendCacheWriteFailed")] RedisGlobalPartyInvitePartySendCacheWriteFailed = 10322, + /// + /// Redis에서 InstanceRoomInfoCache 정보 조회를 실패했습니다. + /// + [pbr::OriginalName("RedisInstanceRoomInfoCacheGetFailed")] RedisInstanceRoomInfoCacheGetFailed = 10323, + /// + /// Redis 에서 누적 랭킹 데이터 저정에 실패했습니다. + /// + [pbr::OriginalName("RedisUgcNpcTotalRankCacheWriteFailed")] RedisUgcNpcTotalRankCacheWriteFailed = 10324, + /// + ///============================================================================================= + /// Message Queue 오류 : 10400 ~ + ///============================================================================================= + /// RabbitMQ 오류 + /// + [pbr::OriginalName("RabbitMqConsumerStartFailed")] RabbitMqConsumerStartFailed = 10401, + /// + /// RabbitMQ 연결을 실패 했습니다. + /// + [pbr::OriginalName("RabbitMqConnectFailed")] RabbitMqConnectFailed = 10402, + /// + /// RabbitMQ 메시지 시간이 오래 됐습니다. + /// + [pbr::OriginalName("RabbitMessageTimeOld")] RabbitMessageTimeOld = 10403, + /// + ///============================================================================================= + /// Message Queue 오류 : 10500 ~ + ///============================================================================================= + /// S3 오류 + /// + [pbr::OriginalName("S3ClientCreateFailed")] S3ClientCreateFailed = 10501, + /// + /// S3 Bucket 생성을 실패 했습니다. + /// + [pbr::OriginalName("S3BucketCreateFailed")] S3BucketCreateFailed = 10502, + /// + /// S3 File Upload를 실패 했습니다. + /// + [pbr::OriginalName("S3FileUploadFailed")] S3FileUploadFailed = 10503, + /// + /// S3 File Delete에 실패 했습니다. + /// + [pbr::OriginalName("S3FileDeleteFailed")] S3FileDeleteFailed = 10504, + /// + /// S3 File Get에 실패 했습니다. + /// + [pbr::OriginalName("S3FileGetFailed")] S3FileGetFailed = 10505, + /// + ///============================================================================================= + /// Meta 스키마 및 데이터 기반 오류 : 10550 ~ + ///============================================================================================= + /// 스키마 오류 + /// 데이터 오류 + /// + [pbr::OriginalName("MetaDataLoadFailed")] MetaDataLoadFailed = 10551, + /// + /// Meta 데이터 오류 입니다. + /// + [pbr::OriginalName("InvalidMetaData")] InvalidMetaData = 10552, + /// + /// Meta Id 오류 입니다. + /// + [pbr::OriginalName("MetaIdInvalid")] MetaIdInvalid = 10553, + /// + ///============================================================================================= + /// 기타 라이브러리 오류 : 10610 ~ + ///============================================================================================= + /// Json 오류 + /// + [pbr::OriginalName("JsonTypeInvalid")] JsonTypeInvalid = 10611, + /// + /// JsonConvert Deserialize 오류 입니다. + /// + [pbr::OriginalName("JsonConvertDeserializeFailed")] JsonConvertDeserializeFailed = 10612, + /// + ///============================================================================================= + /// 서버 공통 오류 : 10700 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ServerConfigFileNotFound")] ServerConfigFileNotFound = 10701, + /// + /// 서버 타입 오류 입니다. + /// + [pbr::OriginalName("ServerTypeInvalid")] ServerTypeInvalid = 10702, + /// + /// 해당 리슨포트로 이미 실행중인 프로세스가 있습니다. + /// + [pbr::OriginalName("AlreadyRunningServerWithListenPort")] AlreadyRunningServerWithListenPort = 10703, + /// + /// 캐시 스토리지를 찾을 수 없습니다. + /// + [pbr::OriginalName("NotFoundCacheStorage")] NotFoundCacheStorage = 10704, + /// + /// 함수 파라메터중에 Null 값이 있어서 오류 입니다. + /// + [pbr::OriginalName("FunctionParamNull")] FunctionParamNull = 10705, + /// + /// 함수 파라메터 오류 입니다. + /// + [pbr::OriginalName("FunctionInvalidParam")] FunctionInvalidParam = 10706, + /// + /// 클라이언트 리슨 포트 오류 입니다. + /// + [pbr::OriginalName("ClientListenPortInvalid")] ClientListenPortInvalid = 10707, + /// + /// Interface 를 Override 하지 않았습니다. + /// + [pbr::OriginalName("NotOverrideInterface")] NotOverrideInterface = 10708, + /// + /// 서버 실행후 대기를 실패 했습니다. + /// + [pbr::OriginalName("ServerOnRunningFailed")] ServerOnRunningFailed = 10709, + /// + /// 서비스 타입 오류 입니다. + /// + [pbr::OriginalName("ServiceTypeInvalid")] ServiceTypeInvalid = 10710, + /// + /// 함수를 구현하지 않았습니다. + /// + [pbr::OriginalName("FunctionNotImplemented")] FunctionNotImplemented = 10711, + /// + /// Interface를 상속받아 구현하지 않았습니다. + /// + [pbr::OriginalName("ClassDoesNotImplementInterfaceInheritance")] ClassDoesNotImplementInterfaceInheritance = 10712, + /// + /// 정책 타입이 중복 되었습니다. + /// + [pbr::OriginalName("RuleTypeDuplicated")] RuleTypeDuplicated = 10713, + /// + /// ClassType 형변환은 null 입니다. + /// + [pbr::OriginalName("ClassTypeCastIsNull")] ClassTypeCastIsNull = 10714, + /// + /// 이미 등록된 PeriodicTask 입니다. + /// + [pbr::OriginalName("PeriodicTaskAlreadyRegistered")] PeriodicTaskAlreadyRegistered = 10715, + /// + /// 이미 등록된 EntityTicker 입니다. + /// + [pbr::OriginalName("EntityTickerAlreadyRegistered")] EntityTickerAlreadyRegistered = 10716, + /// + /// EntityTicker를 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityTickerNotFound")] EntityTickerNotFound = 10717, + /// + /// EntityBase를 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityBaseNotFound")] EntityBaseNotFound = 10718, + /// + /// 부합하는 서버가 없습니다. + /// + [pbr::OriginalName("ValidServerNotFound")] ValidServerNotFound = 10719, + /// + /// 해당 서버에 유저가 가득찼습니다. + /// + [pbr::OriginalName("TargetServerUserCountExceed")] TargetServerUserCountExceed = 10720, + /// + /// 해당 유저를 찾을 수 없습니다. + /// + [pbr::OriginalName("TargetUserNotFound")] TargetUserNotFound = 10721, + /// + /// 대상이 접속중이지 않습니다. + /// + [pbr::OriginalName("TargetUserNotLogIn")] TargetUserNotLogIn = 10722, + /// + /// 맵에서 찾을 수 없습니다. + /// + [pbr::OriginalName("NotExistMap")] NotExistMap = 10723, + /// + /// 조건에 맞지 않아 입장 예약을 실패했습니다. + /// + [pbr::OriginalName("FailedToReserveEnterCondition")] FailedToReserveEnterCondition = 10724, + /// + /// 입장 예약에 실패했습니다. + /// + [pbr::OriginalName("FailedToReservationEnter")] FailedToReservationEnter = 10725, + /// + /// OwnerEntityType 오류 입니다. + /// + [pbr::OriginalName("OwnerEntityTypeInvalid")] OwnerEntityTypeInvalid = 10726, + /// + /// OwnerEntity 정보를 채울 수 없습니다. + /// + [pbr::OriginalName("OwnerEntityCannotFillup")] OwnerEntityCannotFillup = 10727, + /// + /// Owner Guid 오류 입니다. + /// + [pbr::OriginalName("OwnerGuidInvalid")] OwnerGuidInvalid = 10728, + /// + /// DailyTimeEvent 등록 오류입니다. + /// + [pbr::OriginalName("DailyTimeEventAdditionFailed")] DailyTimeEventAdditionFailed = 10729, + /// + /// 프로그램 VersionPath 토큰을 찾을 수 없습니다. + /// + [pbr::OriginalName("ProgramVersionPathTokenNotFound")] ProgramVersionPathTokenNotFound = 10730, + /// + /// 현재 처리중 입니다. + /// + [pbr::OriginalName("CurrentlyProcessingState")] CurrentlyProcessingState = 10731, + /// + /// ServerUrlType 오류 입니다. + /// + [pbr::OriginalName("ServerUrlTypeInvalid")] ServerUrlTypeInvalid = 10732, + /// + /// ServerUrlType 이미 등록되어 있습니다. + /// + [pbr::OriginalName("ServerUrlTypeAlreadyRegistered")] ServerUrlTypeAlreadyRegistered = 10733, + /// + /// 서버 Offline 모드가 활성화 상태 입니다. + /// + [pbr::OriginalName("ServerOfflineModeEnable")] ServerOfflineModeEnable = 10734, + /// + /// Owner 정보 오류 입니다. + /// + [pbr::OriginalName("OwnerInvalid")] OwnerInvalid = 10735, + /// + ///============================================================================================= + /// 데이터 변환 및 복사 오류 : 10850 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MetaDataCopyToDynamoDbDocFailed")] MetaDataCopyToDynamoDbDocFailed = 10850, + /// + /// Meta 데이터를 EntityAttribute에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("MetaDataCopyToEntityAttributeFailed")] MetaDataCopyToEntityAttributeFailed = 10851, + /// + /// DynamoDbDoc를 Cache에 복사하는 것을 실패 헀습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyToCacheFailed")] DynamoDbDocCopyToCacheFailed = 10852, + /// + /// DynamoDbDoc를 EntityAttribute에 복사하는 것을 실패 헀습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyToEntityAttributeFailed")] DynamoDbDocCopyToEntityAttributeFailed = 10853, + /// + /// Cache를 EntityAttrib에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("CacheCopyToEntityAttributeFailed")] CacheCopyToEntityAttributeFailed = 10854, + /// + /// Cache를 DynamoDbDoc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("CacheCopyToDynamoDbDocFailed")] CacheCopyToDynamoDbDocFailed = 10555, + /// + /// EntityAttribute를 Cache에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCopyToCacheFailed")] EntityAttributeCopyToCacheFailed = 10556, + /// + /// EntityAttribute를 DynamoDbDoc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCopyToDynamoDbDocFailed")] EntityAttributeCopyToDynamoDbDocFailed = 10557, + /// + /// EntityAttribute를 EntityAttributeTransactor에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCopyToEntityAttributeTransactorFailed")] EntityAttributeCopyToEntityAttributeTransactorFailed = 10558, + /// + /// EntityAttributeTransactor를 EntityAttribute에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeTransactorCopyToEntityAttributeFailed")] EntityAttributeTransactorCopyToEntityAttributeFailed = 10559, + /// + /// EntityAttributeTransactor를 DynamoDbDoc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeTransactorCopyToDynamoDbDocFailed")] EntityAttributeTransactorCopyToDynamoDbDocFailed = 10560, + /// + /// Attrib를 찾을 수 없습니다. + /// + [pbr::OriginalName("AttribNotFound")] AttribNotFound = 10561, + /// + /// Attrib Path 구성을 실패 했습니다. + /// + [pbr::OriginalName("AttribPathMakeFailed")] AttribPathMakeFailed = 10562, + /// + /// EntityAttribute Casting을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCastFailed")] EntityAttributeCastFailed = 10563, + /// + /// 문자열을 Enum으로 변환하는 것을 실패 했습니다. + /// + [pbr::OriginalName("StringConvertToEnumFailed")] StringConvertToEnumFailed = 10564, + /// + ///============================================================================================= + /// 프로그램 버전 오류 : 10900 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MetaSchemaVersionNotMatch")] MetaSchemaVersionNotMatch = 10901, + /// + /// 메타 데이터 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("MetaDataVersionNotMatch")] MetaDataVersionNotMatch = 10902, + /// + /// 패킷 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("PacketVersionNotMatch")] PacketVersionNotMatch = 10903, + /// + /// 클라이언트 로직 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("ClientLogicVersionNotMatch")] ClientLogicVersionNotMatch = 10904, + /// + /// 리소스 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("ResourceVersionNotMatch")] ResourceVersionNotMatch = 10905, + /// + /// ClientProgramVersion 정보 null 입니다. + /// + [pbr::OriginalName("ClientProgramVersionIsNull")] ClientProgramVersionIsNull = 10906, + /// + ///============================================================================================= + /// 계정 인증 오류 : 11000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TestIdNotAllow")] TestIdNotAllow = 11001, + /// + /// Bot 계정은 허용되지 않습니다. + /// + [pbr::OriginalName("BotdNotAllow")] BotdNotAllow = 11002, + /// + /// 계정 id 길이가 짧습니다. + /// + [pbr::OriginalName("AccountIdLengthShort")] AccountIdLengthShort = 11003, + /// + /// 통합인증Db에서 Id를 찾을 수 없습니다. + /// + [pbr::OriginalName("AccountIdNotFoundInSsoAccountDb")] AccountIdNotFoundInSsoAccountDb = 11004, + /// + /// 테스트 계정으로 Meta 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("MetaDataNotFoundByTestUserId")] MetaDataNotFoundByTestUserId = 11005, + /// + /// 계정 비밀번호가 일치하지 않습니다. + /// + [pbr::OriginalName("AccountPasswordNotMatch")] AccountPasswordNotMatch = 11006, + /// + /// UserData 정보를 AccountAttr 정보로 변환을 실패 했습니다. + /// + [pbr::OriginalName("UserDataConvertToAccountAttrFailed")] UserDataConvertToAccountAttrFailed = 11007, + /// + /// AccountBaseAttrib 정보를 DB에 추가를 실패 했습니다. + /// + [pbr::OriginalName("AccountBaseAttribInsertDbFailed")] AccountBaseAttribInsertDbFailed = 11008, + /// + /// 접속 가능한 서버가 없습니다. + /// + [pbr::OriginalName("NoServerConnectable")] NoServerConnectable = 11009, + /// + /// 접속 제재 처리된 계정입니다. + /// + [pbr::OriginalName("BlockedAccount")] BlockedAccount = 11010, + /// + /// 통합계정인증과 런처 로그인을 허용하지 않습니다. + /// + [pbr::OriginalName("SsoAccountAuthWithLauncherLoginNotAllow")] SsoAccountAuthWithLauncherLoginNotAllow = 11011, + /// + /// 클라이언트 단독 로그인을 허용하지 않습니다. + /// + [pbr::OriginalName("ClientStandaloneLoginNotAllow")] ClientStandaloneLoginNotAllow = 11012, + /// + /// 접속 허용이 되지 않는 PlatformType 입니다. + /// + [pbr::OriginalName("PlatformTypeNotAllow")] PlatformTypeNotAllow = 11013, + /// + /// 통합계정DB에서 계정 정보를 읽지 못했습니다. + /// + [pbr::OriginalName("AccountCanNotReadFromSsoAccountDb")] AccountCanNotReadFromSsoAccountDb = 11014, + /// + /// 통합계정인증 JWT 체크를 실패 했습니다. + /// + [pbr::OriginalName("SsoAccountAuthJwtCheckFailed")] SsoAccountAuthJwtCheckFailed = 11015, + /// + /// 통합계정인증 JWT 안에 UserId Key 정보가 없습니다. + /// + [pbr::OriginalName("UserIdKeyNotFoundInSsoAccountAuthJwt")] UserIdKeyNotFoundInSsoAccountAuthJwt = 11016, + /// + /// 통합계정인증 JWT 안에 UserId Value 정보가 없습니다. + /// + [pbr::OriginalName("UserIdValueEmptyInSsoAccountAuthJwt")] UserIdValueEmptyInSsoAccountAuthJwt = 11017, + /// + /// 통합계정인증 JWT 안에 AccountType Key 정보가 없습니다. + /// + [pbr::OriginalName("AccountTypeKeyNotFoundInSsoAccountAuthJwt")] AccountTypeKeyNotFoundInSsoAccountAuthJwt = 11018, + /// + /// 통합계정인증 JWT 안에 AccountType Value 정보가 허용된 AccountType이 아닙니다. + /// + [pbr::OriginalName("AccountTypeValueNotAllowInSsoAccountAuthJwt")] AccountTypeValueNotAllowInSsoAccountAuthJwt = 11019, + /// + /// 메타버스Db에서 AccountBaseDoc를 찾을 수 없습니다. + /// + [pbr::OriginalName("AccountBaseDocNotFoundInMetaverseDb")] AccountBaseDocNotFoundInMetaverseDb = 11020, + /// + /// 통합계정인증 JWT 안에 AssessToken Key 정보가 없습니다. + /// + [pbr::OriginalName("AccessTokenKeyNotAllowInSsoAccountAuthJwt")] AccessTokenKeyNotAllowInSsoAccountAuthJwt = 11021, + /// + /// 통합인증Db의 AccessToken과 일치하지 않습니다. + /// + [pbr::OriginalName("AccessTokenNotMatchInSsoAccountDb")] AccessTokenNotMatchInSsoAccountDb = 11022, + /// + /// 현재의 AccountType은 허용되지 않습니다. + /// + [pbr::OriginalName("AccountTypeNotAllow")] AccountTypeNotAllow = 11023, + /// + /// AccountBaseDoc가 로드되지 않았습니다. + /// + [pbr::OriginalName("AccountBaseDocNotLoad")] AccountBaseDocNotLoad = 11024, + /// + /// 권한이 부족합니다. + /// + [pbr::OriginalName("NotEnoughAuthority")] NotEnoughAuthority = 11054, + /// + /// AccountBaseDoc이 null 입니다. + /// + [pbr::OriginalName("AccountBaseDocIsNull")] AccountBaseDocIsNull = 11055, + /// + /// Account Id 오류 입니다. + /// + [pbr::OriginalName("AccountIdInvalid")] AccountIdInvalid = 11056, + /// + /// Account에 UserGuid가 없습니다. + /// + [pbr::OriginalName("AccountWithoutUserGuid")] AccountWithoutUserGuid = 11057, + /// + /// 통합계정인증 JWT 이 기간만료 입니다. + /// + [pbr::OriginalName("SsoAccountAuthJwtTokenExpired")] SsoAccountAuthJwtTokenExpired = 11058, + /// + /// 통합계정인증 JWT 예외가 발생 했습니다. + /// + [pbr::OriginalName("SsoAccountAuthJwtException")] SsoAccountAuthJwtException = 11059, + /// + ///============================================================================================= + /// 엔티티 트랜잭션 관련 오류 : 11200 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TransactionRunnerAlreadyRunning")] TransactionRunnerAlreadyRunning = 11201, + /// + /// TransactionRunner를 찾을 수 없습니다. + /// + [pbr::OriginalName("TransactionRunnerNotFound")] TransactionRunnerNotFound = 11202, + /// + ///============================================================================================= + /// 엔티티 속성 관련 오류 : 11300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EntityGuidInvalid")] EntityGuidInvalid = 11301, + /// + /// EntityAttrib 중복 등록 되었습니다. + /// + [pbr::OriginalName("EntityAttribDuplicated")] EntityAttribDuplicated = 11302, + /// + /// EntityAttribute가 null 입니다. + /// + [pbr::OriginalName("EntityAttributeIsNull")] EntityAttributeIsNull = 11303, + /// + /// EntityAttribute 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityAttributeNotFound")] EntityAttributeNotFound = 11304, + /// + /// EntityAttribute 상태 오류 입니다. + /// + [pbr::OriginalName("EntityAttributeStateInvalid")] EntityAttributeStateInvalid = 11305, + /// + /// EntityType 오류 입니다. + /// + [pbr::OriginalName("EntityTypeInvalid")] EntityTypeInvalid = 11306, + /// + /// Entity가 Map에 연결되어 있습니다. + /// + [pbr::OriginalName("EntityLinkedToMap")] EntityLinkedToMap = 11307, + /// + /// Entity가 Map에 연결되어 있지 않습니다. + /// + [pbr::OriginalName("EntityNotLinkedToMap")] EntityNotLinkedToMap = 11308, + /// + ///현재 dence 상태가 아닙니다. dance end 패킷이 날라올때 처리 + /// + [pbr::OriginalName("EntityStateNotDancing")] EntityStateNotDancing = 11309, + /// + ///============================================================================================= + /// 엔티티 얙션 관련 오류 : 11400 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EntityActionDuplicated")] EntityActionDuplicated = 11401, + /// + /// EntityAction을 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityActionNotFound")] EntityActionNotFound = 11402, + /// + ///============================================================================================= + /// 엔티티 상태 관련 오류 : 11500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EntityBaseHfsmInitFailed")] EntityBaseHfsmInitFailed = 11501, + /// + ///============================================================================================= + /// 글로벌 엔티티 오류 : 11600 ~ + ///============================================================================================= + /// + [pbr::OriginalName("RedisGlobalEntityDuplicated")] RedisGlobalEntityDuplicated = 11601, + /// + ///============================================================================================= + /// 접속 서버 변경 관련 오류 : 11700 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UserIsSwitchingServer")] UserIsSwitchingServer = 11701, + /// + /// 유저가 다른 서버로 접속을 변경하고 있지 않습니다. + /// + [pbr::OriginalName("UserIsNotSwitchingServer")] UserIsNotSwitchingServer = 11702, + /// + /// 접속 서버 변경 Otp 값이 일치하지 않습니다. + /// + [pbr::OriginalName("ServerSwitchingOtpNotMatch")] ServerSwitchingOtpNotMatch = 11703, + /// + /// 접속된 서버는 목적지 서버가 아닙니다. + /// + [pbr::OriginalName("ConnectedServerIsNotDestServer")] ConnectedServerIsNotDestServer = 11704, + /// + /// 예약된 유저가 아닙니다. + /// + [pbr::OriginalName("InvalidReservationUser")] InvalidReservationUser = 11705, + /// + /// 복귀 유저가 아닙니다. + /// + [pbr::OriginalName("InvalidReturnUser")] InvalidReturnUser = 11706, + /// + ///============================================================================================= + /// 유저 관련 오류 : 12000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UserNicknameNotAllowWithSpecialChars")] UserNicknameNotAllowWithSpecialChars = 12001, + /// + /// 유저 닉네임은 한글로 최소2 에서 최대8 글자까지 허용 합니다. + /// + [pbr::OriginalName("UserNicknameAllowedMin2ToMax8WithKorean")] UserNicknameAllowedMin2ToMax8WithKorean = 12002, + /// + /// 유저 닉네임은 영어로 최소4 에서 최대16 글자까지 허용 합니다. + /// + [pbr::OriginalName("UserNicknameAllowedMin4ToMax16WithEnglish")] UserNicknameAllowedMin4ToMax16WithEnglish = 12003, + /// + /// 유저 닉네임은 첫번째 글자에 숫자를 허용하지 않습니다. + /// + [pbr::OriginalName("UserNicknameNotAllowedNumberAtFirstChars")] UserNicknameNotAllowedNumberAtFirstChars = 12004, + /// + /// 유저 닉네임으로 허용되지 않는 문자 입니다. + /// + [pbr::OriginalName("UserNicknameNotAllowChars")] UserNicknameNotAllowChars = 12005, + /// + /// 유저 닉네임으로 한글 초성체를 허용하지 않습니다. + /// + [pbr::OriginalName("UserNicknameNotAllowWithInitialismKorean")] UserNicknameNotAllowWithInitialismKorean = 12006, + /// + /// 유저 닉네임이 금지어에 해당 합니다. + /// + [pbr::OriginalName("UserNicknameBan")] UserNicknameBan = 12007, + /// + /// 유저 중복 로그인 입니다. + /// + [pbr::OriginalName("UserDuplicatedLogin")] UserDuplicatedLogin = 12008, + /// + /// 유저가 로그인되어 있지 않습니다. + /// + [pbr::OriginalName("UserNotLogin")] UserNotLogin = 12009, + /// + /// 유저 생성을 위한 DynamoDbDoc가 중복 등록 되었습니다. + /// + [pbr::OriginalName("UserCreationForDynamoDbDocDuplicated")] UserCreationForDynamoDbDocDuplicated = 12010, + /// + /// UserGuid를 참조하는 모든 Attribute에 Guid를 적용하는 것을 실패했습니다. + /// + [pbr::OriginalName("UserGuidApplyToRefAttributeAllFailed")] UserGuidApplyToRefAttributeAllFailed = 12011, + /// + /// TestUserPrepareCreate 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("TestUserPrepareCreateNotCompleted")] TestUserPrepareCreateNotCompleted = 12012, + /// + /// DefaultUserPrepareCreate 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("DefaultUserPrepareCreateNotCompleted")] DefaultUserPrepareCreateNotCompleted = 12013, + /// + /// UserPrepareLoad 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("UserPrepareLoadNotCompleted")] UserPrepareLoadNotCompleted = 12014, + /// + /// 유저 닉네임이 생성되어 있지 않습니다. + /// + [pbr::OriginalName("UserNicknameNotCreated")] UserNicknameNotCreated = 12015, + /// + /// 유저 닉네임을 이미 생성 했습니다. + /// + [pbr::OriginalName("UserNicknameAlreadyCreated")] UserNicknameAlreadyCreated = 12016, + /// + /// 유저 생성 절차가 완료되지 않았습니다. + /// + [pbr::OriginalName("UserCreateStepNotCompleted")] UserCreateStepNotCompleted = 12017, + /// + /// 유저 생성이 완료 되었습니다. + /// + [pbr::OriginalName("UserCreateCompleted")] UserCreateCompleted = 12018, + /// + /// 유저 서브키 바인딩을 실패 했습니다. + /// + [pbr::OriginalName("UserSubKeyBindToFailed")] UserSubKeyBindToFailed = 12019, + /// + /// 유저 서브키 변경을 실패 했습니다. + /// + [pbr::OriginalName("UserSubKeyReplaceFailed")] UserSubKeyReplaceFailed = 12020, + /// + /// 유저 Guid 오류 입니다. + /// + [pbr::OriginalName("UserGuidInvalid")] UserGuidInvalid = 12021, + /// + /// UserNicknameDoc이 null 입니다. + /// + [pbr::OriginalName("UserNicknameDocIsNull")] UserNicknameDocIsNull = 12022, + /// + /// UserDoc이 null 입니다. + /// + [pbr::OriginalName("UserDocIsNull")] UserDocIsNull = 12023, + /// + /// UserGuid가 이미 등록되어 있습니다. + /// + [pbr::OriginalName("UserGuidAlreadyAdded")] UserGuidAlreadyAdded = 12024, + /// + /// User 닉네임이 중복 되었습니다. + /// + [pbr::OriginalName("UserNicknameDuplicated")] UserNicknameDuplicated = 12025, + /// + /// 유저 닉네임은 최소2 에서 최대12 글자까지 허용 합니다. + /// + [pbr::OriginalName("UserNicknameAllowedMin2ToMax12")] UserNicknameAllowedMin2ToMax12 = 12026, + /// + /// UserNickname 검색 페이지가 잘못되었습니다. + /// + [pbr::OriginalName("UserNicknameSearchPageWrong")] UserNicknameSearchPageWrong = 12027, + /// + /// 유저 닉네임이 없습니다. + /// + [pbr::OriginalName("UserNicknameEmpty")] UserNicknameEmpty = 12028, + /// + /// UserContentsSettingDoc이 null 입니다. + /// + [pbr::OriginalName("UserContentsSettingDocIsNull")] UserContentsSettingDocIsNull = 12029, + /// + /// 유저 MoneyDoc이 없습니다. + /// + [pbr::OriginalName("UserMoneyDocEmpty")] UserMoneyDocEmpty = 12030, + /// + ///============================================================================================= + /// 유저 신고하기 관련 오류 : 12100 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UserReportInvalidTitleLength")] UserReportInvalidTitleLength = 12101, + /// + /// 유저 신고하기 의 내용 길이 오류입니다. + /// + [pbr::OriginalName("UserReportInvalidContentLength")] UserReportInvalidContentLength = 12102, + /// + /// UserReport에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("UserReportDocException")] UserReportDocException = 12103, + /// + ///============================================================================================= + /// 캐릭터 관련 오류 : 13000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TestCharacterPrepareCreateNotCompleted")] TestCharacterPrepareCreateNotCompleted = 13001, + /// + /// DefaultCharacterPrepareCreate 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("DefaultCharacterPrepareCreateNotCompleted")] DefaultCharacterPrepareCreateNotCompleted = 13012, + /// + /// CharacterPrepareLoad 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("CharacterPrepareLoadNotCompleted")] CharacterPrepareLoadNotCompleted = 13013, + /// + /// CharacterBaseDoc 로딩중에 중복된 캐릭터가 발견되었습니다. + /// + [pbr::OriginalName("CharacterBaseDocLoadDuplicatedCharacter")] CharacterBaseDocLoadDuplicatedCharacter = 13014, + /// + /// 선택된 캐릭터가 없습니다. + /// + [pbr::OriginalName("CharacterNotSelected")] CharacterNotSelected = 13015, + /// + /// 캐릭터를 찾지 못했습니다. + /// + [pbr::OriginalName("CharacterNotFound")] CharacterNotFound = 13016, + /// + /// CharacterBaseDoc 읽지 않았습니다. + /// + [pbr::OriginalName("CharacterBaseDocNoRead")] CharacterBaseDocNoRead = 13017, + /// + /// 캐릭터 커스터마이징이 완료가 되지 않았습니다. + /// + [pbr::OriginalName("CharacterCustomizingNotCompleted")] CharacterCustomizingNotCompleted = 13018, + /// + /// 캐릭터 생성 절차가 완료되지 않았습니다. + /// + [pbr::OriginalName("CharacterCreateStepNotCompleted")] CharacterCreateStepNotCompleted = 13019, + /// + /// 캐릭터 커스터마이징이 이미 완료 되었습니다. + /// + [pbr::OriginalName("CharacterCustomizingAlreadyCompleted")] CharacterCustomizingAlreadyCompleted = 13020, + /// + /// 캐릭터 준비 단계에서 캐릭터 생성이 되지 않습니다. + /// + [pbr::OriginalName("CharacterPrepareNotCreated")] CharacterPrepareNotCreated = 13021, + /// + /// 캐릭터 생성이 완료 되었습니다. + /// + [pbr::OriginalName("CharacterCreateCompleted")] CharacterCreateCompleted = 13022, + /// + /// CharacterBaseDoc이 null 입니다. + /// + [pbr::OriginalName("CharacterBaseDocIsNull")] CharacterBaseDocIsNull = 13023, + /// + ///============================================================================================= + /// 능력치 관련 오류 : 13300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("AbilityNotEnough")] AbilityNotEnough = 13301, + /// + ///============================================================================================= + /// 아이템 관련 오류 : 14000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ItemMetaDataNotFound")] ItemMetaDataNotFound = 14001, + /// + /// 아이템 Guid 값 오류 입니다. + /// + [pbr::OriginalName("ItemGuidInvalid")] ItemGuidInvalid = 14002, + /// + /// ItemDoc 객체가 null 입니다. + /// + [pbr::OriginalName("ItemDocIsNull")] ItemDocIsNull = 14003, + /// + /// 아이템 DefaultAttribute를 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemDefaultAttributeNotFoundInMeta")] ItemDefaultAttributeNotFoundInMeta = 14004, + /// + /// 아이템 Level Enchant를 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemLevelEnchantNotFoundInMeta")] ItemLevelEnchantNotFoundInMeta = 14005, + /// + /// 아이템 Enchant를 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemEnchantNotFoundInMeta")] ItemEnchantNotFoundInMeta = 14006, + /// + /// 아이템 AttributeRandomGroup을 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemAttributeRandomGroupNotFoundInMeta")] ItemAttributeRandomGroupNotFoundInMeta = 14007, + /// + /// 아이템 AttributeRandomGroup의 TotalWeight 오류 입니다. + /// + [pbr::OriginalName("ItemAttributeRandomGroupTotalWeightInvalid")] ItemAttributeRandomGroupTotalWeightInvalid = 14008, + /// + /// 아이템을 찾지 못했습니다. + /// + [pbr::OriginalName("ItemNotFound")] ItemNotFound = 14009, + /// + /// 의상 아이템 LargeType 오류 입니다. + /// + [pbr::OriginalName("ItemClothInvalidLargeType")] ItemClothInvalidLargeType = 14010, + /// + /// 의상 아이템 SmallType 오류 입니다. + /// + [pbr::OriginalName("ItemClothInvalidSmallType")] ItemClothInvalidSmallType = 14011, + /// + /// 아이템 스택 개수 오류 입니다. + /// + [pbr::OriginalName("ItemStackCountInvalid")] ItemStackCountInvalid = 14012, + /// + /// 아이템 최대 보유 갯수를 초과 했습니다. + /// + [pbr::OriginalName("ItemMaxCountExceed")] ItemMaxCountExceed = 14013, + /// + /// ItemDoc 로딩중에 중복된 아이템이 발견되었습니다. + /// + [pbr::OriginalName("ItemDocLoadDuplicatedItem")] ItemDocLoadDuplicatedItem = 14014, + /// + /// ClothSlotType 오류 입니다. + /// + [pbr::OriginalName("ClothSlotTypeInvalid")] ClothSlotTypeInvalid = 14015, + /// + /// 아이템 스택 개수가 부족 합니다. + /// + [pbr::OriginalName("ItemStackCountNotEnough")] ItemStackCountNotEnough = 14016, + /// + /// 아이템 보유 개수가 부족 합니다. + /// + [pbr::OriginalName("ItemCountNotEnough")] ItemCountNotEnough = 14017, + /// + /// ItemType(LargeType, SmallType) 오류입니다. + /// + [pbr::OriginalName("ItemInvalidItemType")] ItemInvalidItemType = 14018, + /// + /// 아이템 Tool 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("ItemToolMetaDataNotFound")] ItemToolMetaDataNotFound = 14019, + /// + /// 아이템 Tool을 찾지 못했습니다. + /// + [pbr::OriginalName("ItemToolNotFound")] ItemToolNotFound = 14020, + /// + /// 아이템 Tool이 활성화 상태가 아닙니다. + /// + [pbr::OriginalName("ItemToolNotActivateState")] ItemToolNotActivateState = 14021, + /// + /// ToolActionDoc이 null 입니다. + /// + [pbr::OriginalName("ToolActionDocIsNull")] ToolActionDocIsNull = 14022, + /// + /// ToolAction이 이미 비활성화 상태 입니다. + /// + [pbr::OriginalName("ToolActionAlreadyUnactivateState")] ToolActionAlreadyUnactivateState = 14023, + /// + /// ToolAction이 이미 활성화 상태 입니다. + /// + [pbr::OriginalName("ToolActionAlreadyActivateState")] ToolActionAlreadyActivateState = 14024, + /// + /// 아이템 Tattoo가 없습니다. + /// + [pbr::OriginalName("ItemTattooNotFound")] ItemTattooNotFound = 14025, + /// + /// 아이템 Attribute Enchant 메타가 없습니다. + /// + [pbr::OriginalName("ItemAttributeEnchantMetaNotFound")] ItemAttributeEnchantMetaNotFound = 14026, + /// + /// 아이템 Attribute Change가 선택되지 않았습니다. + /// + [pbr::OriginalName("ItemAttributeChangeNotSelected")] ItemAttributeChangeNotSelected = 14027, + /// + /// 아이템 문자열을 정수로 변환하는데 오류가 발생했습니다. + /// + [pbr::OriginalName("ItemParsingFromStringToIntErorr")] ItemParsingFromStringToIntErorr = 14028, + /// + /// 아이템 개수 문자열을 정수로 변환하는데 오류가 발생했습니다. + /// + [pbr::OriginalName("ItemValueParsingFromStringToIntErorr")] ItemValueParsingFromStringToIntErorr = 14029, + /// + /// ItemFirstPurchaseHistoryDoc이 null 입니다. + /// + [pbr::OriginalName("ItemFirstPurchaseHistoryDocIsNull")] ItemFirstPurchaseHistoryDocIsNull = 14030, + /// + /// ItemFirstPurchaseHistoryDoc 로딩중에 중복된 아이템이 발견되었습니다. + /// + [pbr::OriginalName("ItemFirstPurchaseHistoryDocLoadDuplicatedItem")] ItemFirstPurchaseHistoryDocLoadDuplicatedItem = 14031, + /// + /// ItemFirstPurchaseHistory가 이미 존재합니다. + /// + [pbr::OriginalName("ItemFirstPurchaseHistoryAlreadyExist")] ItemFirstPurchaseHistoryAlreadyExist = 14032, + /// + /// 아이템 첫 구매 할인 아이템 개수가 잘못되었습니다. + /// + [pbr::OriginalName("ItemFirstPurchaseDiscountItemCountWrong")] ItemFirstPurchaseDiscountItemCountWrong = 14033, + /// + /// 아이템 AttributeIdType 오류 입니다. + /// + [pbr::OriginalName("ItemAttributeIdTypeInvalid")] ItemAttributeIdTypeInvalid = 14034, + /// + /// 아이템 할당 오류 입니다. + /// + [pbr::OriginalName("ItemAllocFailed")] ItemAllocFailed = 14035, + /// + /// 아이템 Guid 중복 오류 입니다. + /// + [pbr::OriginalName("ItemGuidDuplicated")] ItemGuidDuplicated = 14036, + /// + /// 아이템 레벨이 현재 최대 입니다. + /// + [pbr::OriginalName("ItemLevelCurrentMax")] ItemLevelCurrentMax = 14037, + /// + /// 보관할 수 없는 아이템 입니다. + /// + [pbr::OriginalName("ItemCanNotBeStored")] ItemCanNotBeStored = 14038, + /// + ///============================================================================================= + /// 아이템 액션 관련 오류 : 14101 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ItemUseFunctionNotFound")] ItemUseFunctionNotFound = 14101, + /// + /// 퀘스트 쿨타임 초기화 아이템 사용시 : 퀘스트 메일이 가득차서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseQuestMailCountMax")] ItemUseQuestMailCountMax = 14102, + /// + /// 퀘스트 쿨타임 초기화 아이템 사용시 : 이미 수행중이거나, 퀘스트메일이 존재해서 할당가능한 퀘스트가 없어서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseNotExistAssignableQuest")] ItemUseNotExistAssignableQuest = 14103, + /// + /// 퀘스트 할당 아이템 사용시 : 해당 퀘스트는 이미 진행중이어서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseAlreadyHasQuest")] ItemUseAlreadyHasQuest = 14104, + /// + /// 퀘스트 할당 아이템 사용시 : 해당 퀘스트메일은 이미 존재해서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseAlreadyHasQuestMail")] ItemUseAlreadyHasQuestMail = 14105, + /// + ///============================================================================================= + /// 인벤토리 관련 오류 : 15000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BagRuleItemLargeTypeDuplicated")] BagRuleItemLargeTypeDuplicated = 15001, + /// + /// ToolEquipRule에 ItemLargeType이 중복 되었습니다. + /// + [pbr::OriginalName("ToolEquipRuleItemLargeTypeDuplicated")] ToolEquipRuleItemLargeTypeDuplicated = 15002, + /// + /// ClosthEquipRule에 ItemLargeType이 중복 되었습니다. + /// + [pbr::OriginalName("ClothEquipRuleItemLargeTypeDuplicated")] ClothEquipRuleItemLargeTypeDuplicated = 15003, + /// + /// TattooEquipRule에 ItemLargeType이 중복 되었습니다. + /// + [pbr::OriginalName("TattooEquipRuleItemLargeTypeDuplicated")] TattooEquipRuleItemLargeTypeDuplicated = 15004, + /// + /// InventoryRule을 찾을 수 없습니다. + /// + [pbr::OriginalName("InventoryRuleNotFound")] InventoryRuleNotFound = 15005, + /// + /// EquipInven을 찾을 수 없습니다. + /// + [pbr::OriginalName("EquipInvenNotFound")] EquipInvenNotFound = 15006, + /// + /// 이미 장착된 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyEquiped")] SlotsAlreadyEquiped = 15007, + /// + /// 이미 장착 해제된 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyUnequiped")] SlotsAlreadyUnequiped = 15008, + /// + /// 가방에 아이템이 가득 찼습니다. + /// + [pbr::OriginalName("BagIsItemFull")] BagIsItemFull = 15009, + /// + /// 가방에 아이템이 비어 있습니다. + /// + [pbr::OriginalName("BagIsItemEmpty")] BagIsItemEmpty = 15010, + /// + /// 가방에 DeltaItem이 중복 되었습니다. + /// + [pbr::OriginalName("BagDeltaItemDupliated")] BagDeltaItemDupliated = 15011, + /// + /// 가방에서 아이템을 찾을 수 없습니다. + /// + [pbr::OriginalName("BagItemNotFound")] BagItemNotFound = 15012, + /// + /// ClothEquipRule에서 ClothSlotType을 찾을 수 없습니다. + /// + [pbr::OriginalName("ClothEquipRuleClothSlotTypeNotFound")] ClothEquipRuleClothSlotTypeNotFound = 15013, + /// + /// ToolEquipRule에서 ToolSlotType를 찾을 수 없습니다. + /// + [pbr::OriginalName("ToolEquipRuleToolSlotTypeNotFound")] ToolEquipRuleToolSlotTypeNotFound = 15014, + /// + /// TattooEquipRule에서 TattooSlotType을 찾을 수 없습니다. + /// + [pbr::OriginalName("TattooEquipRuleTattooSlotTypeNotFound")] TattooEquipRuleTattooSlotTypeNotFound = 15015, + /// + /// BagTabType 추가를 실패 했습니다. + /// + [pbr::OriginalName("BagTabTypeAddFailed")] BagTabTypeAddFailed = 15016, + /// + /// BagTabType 오류 입니다. + /// + [pbr::OriginalName("BagTabTypeInvalid")] BagTabTypeInvalid = 15017, + /// + /// BagTabType을 찾을 수 없습니다. + /// + [pbr::OriginalName("BagTabTypeNotFound")] BagTabTypeNotFound = 15018, + /// + /// BagTabCount Merge를 실패 했습니다. + /// + [pbr::OriginalName("BagTabCountMergeFailed")] BagTabCountMergeFailed = 15019, + /// + /// Inventory EntityType 오류 입니다. + /// + [pbr::OriginalName("InventoryEntityTypeInvalid")] InventoryEntityTypeInvalid = 15020, + /// + /// InvenEquipType 오류 입니다. + /// + [pbr::OriginalName("InvenEquipTypeInvalid")] InvenEquipTypeInvalid = 15021, + /// + /// 가방에 예약된 아이템이 가득 찼습니다. + /// + [pbr::OriginalName("BagIsReservedItemFull")] BagIsReservedItemFull = 15022, + /// + /// 가방에 예약된 아이템이 없습니다. + /// + [pbr::OriginalName("BagIsReservedItemEmpty")] BagIsReservedItemEmpty = 15023, + /// + /// 장착 슬롯이 일치하지 않습니다. + /// + [pbr::OriginalName("EquipSlotNotMatch")] EquipSlotNotMatch = 15024, + /// + /// 장착 슬롯 범위를 벗어났습니다. + /// + [pbr::OriginalName("EquipSlotOutOfRange")] EquipSlotOutOfRange = 15025, + /// + /// 이미 예약된 장착 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyReservedEquip")] SlotsAlreadyReservedEquip = 15026, + /// + /// 이미 예약된 장착 해제 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyReservedUnequip")] SlotsAlreadyReservedUnequip = 15027, + /// + /// SlotType을 찾을 수 없습니다. + /// + [pbr::OriginalName("SlotTypeNotFound")] SlotTypeNotFound = 15028, + /// + ///============================================================================================= + /// UgcNpc 관련 오류 : 15101 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UgcNpcMetaGuidAlreadyAdded")] UgcNpcMetaGuidAlreadyAdded = 15101, + /// + /// UgcNpcDoc이 null 입니다. + /// + [pbr::OriginalName("UgcNpcDocIsNull")] UgcNpcDocIsNull = 15102, + /// + /// UgcNpc 생성 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcMaxCountExceed")] UgcNpcMaxCountExceed = 15103, + /// + /// UgcNpc 의상 아이템이 부족 합니다. + /// + [pbr::OriginalName("UgcNpcClothItemNotEnough")] UgcNpcClothItemNotEnough = 15104, + /// + /// UgcNpcDoc 중복 로딩 되었습니다. + /// + [pbr::OriginalName("UgcNpcDocLoadDuplicatedUgcNpc")] UgcNpcDocLoadDuplicatedUgcNpc = 15105, + /// + /// UgcNpc 캐릭터 설명 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcDescriptionLengthExceed")] UgcNpcDescriptionLengthExceed = 15106, + /// + /// UgcNpc 세계관 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcWordScenarioLengthExceed")] UgcNpcWordScenarioLengthExceed = 15107, + /// + /// UgcNpc 인사말 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcGreetingLengthExceed")] UgcNpcGreetingLengthExceed = 15108, + /// + /// UgcNpc 타투 아이템이 부족 합니다. + /// + [pbr::OriginalName("UgcNpcTattooItemNotEnough")] UgcNpcTattooItemNotEnough = 15109, + /// + /// UgcNpc 자주 사용하는 소셜 액션 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcHabitSocialActionCountExceed")] UgcNpcHabitSocialActionCountExceed = 15110, + /// + /// UgcNpc 대화중 기본 소셜 액션 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcDialogueSocialActionCountExceed")] UgcNpcDialogueSocialActionCountExceed = 15111, + /// + /// UgcNpc 닉네임이 중복 되었습니다. + /// + [pbr::OriginalName("UgcNpcNicknameDuplicated")] UgcNpcNicknameDuplicated = 15112, + /// + /// UgcNpc를 찾을 수 없습니다. + /// + [pbr::OriginalName("UgcNpcNotFound")] UgcNpcNotFound = 15113, + /// + /// UgcNpc 자기소개 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcIntroductionLengthExceed")] UgcNpcIntroductionLengthExceed = 15114, + /// + /// UgcNpc Tag 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcMaxTagExceed")] UgcNpcMaxTagExceed = 15115, + /// + /// UgcNpc 닉네임이 없습니다. + /// + [pbr::OriginalName("UgcNpcNicknameEmpty")] UgcNpcNicknameEmpty = 15116, + /// + /// UgcNpc가 이미 게임존에 등록되어 있습니다. + /// + [pbr::OriginalName("UgcNpcAlreadyRegisteredInGameZone")] UgcNpcAlreadyRegisteredInGameZone = 15117, + /// + /// UgcNpc가 게임존에 등록되어 있지 않습니다. + /// + [pbr::OriginalName("UgcNpcNotRegisteredInGameZone")] UgcNpcNotRegisteredInGameZone = 15118, + /// + /// UgcNpcLikeSelecteeCount를 찾을 수 없습니다. + /// + [pbr::OriginalName("UgcNpcLikeSelecteeCountNotFound")] UgcNpcLikeSelecteeCountNotFound = 15119, + /// + /// UgcNpcLikeSelectedFlag를 찾을 수 없습니다. + /// + [pbr::OriginalName("UgcNpcLikeSelectedFlagNotFound")] UgcNpcLikeSelectedFlagNotFound = 15120, + /// + /// UgcNpc가 마이홈 Ugc에 중복 배치 되었습니다. + /// + [pbr::OriginalName("UgcNpcDuplicateInMyhomeUgc")] UgcNpcDuplicateInMyhomeUgc = 15121, + /// + /// UgcNpc가 배치된 상태 입니다. + /// + [pbr::OriginalName("UgcNpcLocatedState")] UgcNpcLocatedState = 15122, + /// + /// UgcNpc 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("UgcNpcMetaDataNotFound")] UgcNpcMetaDataNotFound = 15123, + /// + /// Beacon 앱 프로필 업로드 쿨타임 입니다. + /// + [pbr::OriginalName("BeaconAppProfileUploadCoolTime")] BeaconAppProfileUploadCoolTime = 15124, + /// + /// Beacon Body 아이템 오류 입니다. + /// + [pbr::OriginalName("BeaconBodyItemInvalid")] BeaconBodyItemInvalid = 15125, + /// + ///============================================================================================= + /// UgcNpc Ranking 관련 오류 : 15201 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UgcNpcRankEntityIsNotFound")] UgcNpcRankEntityIsNotFound = 15201, + /// + /// ugc npc ranking 조회 범위를 초과했습니다. + /// + [pbr::OriginalName("UgcNpcRankOutOfRange")] UgcNpcRankOutOfRange = 15202, + /// + ///============================================================================================= + /// Farming Effect 관련 오류 : 15301 ~ + ///============================================================================================= + /// + [pbr::OriginalName("FarmingEffectDocLinkPkSkNotSet")] FarmingEffectDocLinkPkSkNotSet = 15301, + /// + /// FarmingEffect가 이미 게임존에 등록되어 있습니다. + /// + [pbr::OriginalName("FarmingEffectAlreadyRegisteredInGameZone")] FarmingEffectAlreadyRegisteredInGameZone = 15302, + /// + /// 이미 진행중인 Farming 입니다. + /// + [pbr::OriginalName("FarmingAlready")] FarmingAlready = 15303, + /// + /// 나는 Farming 중입니다. + /// + [pbr::OriginalName("FarmingByMe")] FarmingByMe = 15304, + /// + /// FarmingPropMeta 데이터를 찾을 수 없습니다. + /// + [pbr::OriginalName("FarmingPropMetaDataNotFound")] FarmingPropMetaDataNotFound = 15305, + /// + /// Farming 시도 횟수 오류 입니다. + /// + [pbr::OriginalName("FarmingTryCountInvalid")] FarmingTryCountInvalid = 15306, + /// + /// Farming 상태가 아닙니다. + /// + [pbr::OriginalName("FarmingNotState")] FarmingNotState = 15307, + /// + /// Farming 소유자 일치 하지 않습니다. + /// + [pbr::OriginalName("FarmingOwnerNotMatch")] FarmingOwnerNotMatch = 15308, + /// + /// FarmingSummonedEntityType 오류 입니다. + /// + [pbr::OriginalName("FarmingSummonedEntityTypeInvalid")] FarmingSummonedEntityTypeInvalid = 15309, + /// + /// FarmingEffect가 게임존에 존재하지 않습니다. + /// + [pbr::OriginalName("FarmingEffectNotExistInGameZone")] FarmingEffectNotExistInGameZone = 15310, + /// + /// Farming 상태 입니다. + /// + [pbr::OriginalName("FarimgState")] FarimgState = 15311, + /// + /// Farming StandBy 상태가 아닙니다. + /// + [pbr::OriginalName("FarmingStandByNotState")] FarmingStandByNotState = 15312, + /// + /// Farming Anchor를 찾을 수 없습니다. + /// + [pbr::OriginalName("FarmingAnchorNotFound")] FarmingAnchorNotFound = 15313, + /// + /// Farming 관련 Beacon Db 정보 무결성 오류 입니다. + /// + [pbr::OriginalName("FarmingBeaconDbInfoIntegrityError")] FarmingBeaconDbInfoIntegrityError = 15314, + /// + ///============================================================================================= + /// Gacha 관련 오류 : 15401 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GachaMetaDataNotFound")] GachaMetaDataNotFound = 15401, + /// + /// Gacha 보상 정보가 없습니다. + /// + [pbr::OriginalName("GachaRewardEmpty")] GachaRewardEmpty = 15402, + /// + ///============================================================================================= + /// Master 관련 오류 : 15800 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MasterNotFound")] MasterNotFound = 15801, + /// + /// Master 관계 설정이 없다. + /// + [pbr::OriginalName("MasterNotRelated")] MasterNotRelated = 15802, + /// + ///============================================================================================= + /// 게임존 관련 오류 : 15900 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GameZoneNotJoin")] GameZoneNotJoin = 15901, + /// + /// Map 객체가 null 입니다. + /// + [pbr::OriginalName("MapIsNull")] MapIsNull = 15902, + /// + /// LOCATION_UNIQUE_ID 값 오류 입니다. + /// + [pbr::OriginalName("LocationUniqueIdInvalid")] LocationUniqueIdInvalid = 15903, + /// + ///============================================================================================= + /// 친구 관련 오류 : 16000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("FriendDocIsNull")] FriendDocIsNull = 16001, + /// + /// FriendFolderDoc이 null 입니다. + /// + [pbr::OriginalName("FriendFolderDocIsNull")] FriendFolderDocIsNull = 16002, + /// + ///============================================================================================= + /// 소셜 액션 관련 오류 : 17000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("SocialActionMetaDataNotFound")] SocialActionMetaDataNotFound = 17001, + /// + /// 소셜 액션을 찾지 못했습니다. + /// + [pbr::OriginalName("SocialActionNotFound")] SocialActionNotFound = 17002, + /// + /// SocialActionDoc 로딩중에 중복된 소셜 액션이 발견되었습니다. + /// + [pbr::OriginalName("SocialActionDocLoadDuplicatedSocialAction")] SocialActionDocLoadDuplicatedSocialAction = 17003, + /// + /// 소셜 액션이 이미 존재합니다. + /// + [pbr::OriginalName("SocialActionAlreadyExist")] SocialActionAlreadyExist = 17004, + /// + /// 소셜 액션 슬롯의 범위를 벗어났습니다. + /// + [pbr::OriginalName("SocialActionSlotOutOfRange")] SocialActionSlotOutOfRange = 17005, + /// + /// 소셜 액션이 슬롯에 존재하지 않습니다. + /// + [pbr::OriginalName("SocialActionNotOnSlot")] SocialActionNotOnSlot = 17006, + /// + /// SocialActionDoc이 null 입니다. + /// + [pbr::OriginalName("SocialActionDocIsNull")] SocialActionDocIsNull = 17007, + /// + ///============================================================================================= + /// 채널 관련 오류 : 18000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ChannelMoveSameChannel")] ChannelMoveSameChannel = 18001, + /// + /// 채널 이동 쿨타임이 지나지 않았습니다. + /// + [pbr::OriginalName("ChannelInvalidMoveCoolTime")] ChannelInvalidMoveCoolTime = 18002, + /// + /// 채널서버가 아닙니다. ( Indun 서버에서 이동요청시 발생 ) + /// + [pbr::OriginalName("NotChannelServer")] NotChannelServer = 18003, + /// + ///============================================================================================= + /// 던전 관련 오류 : 19000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("OwnedRoomDocIsNull")] OwnedRoomDocIsNull = 19001, + /// + /// RoomDoc이 null 입니다. + /// + [pbr::OriginalName("RoomDocIsNull")] RoomDocIsNull = 19002, + /// + /// Room이 마이홈이 아닙니다. + /// + [pbr::OriginalName("RoomIsNotMyHome")] RoomIsNotMyHome = 19003, + /// + /// MapRange의 범위를 벗어난 CellPos 입니다. + /// + [pbr::OriginalName("MapRangeOutOfCellPos")] MapRangeOutOfCellPos = 19004, + /// + /// MapGrid의 범위를 벗어 났습니다. + /// + [pbr::OriginalName("MapGridBoundOutOfRange")] MapGridBoundOutOfRange = 19005, + /// + /// Map내에서 Grid를 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridNotFoundGrid")] MapGridNotFoundGrid = 19006, + /// + /// MapGrid내에서 Cell을 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridNotFoundCell")] MapGridNotFoundCell = 19007, + /// + /// MapGrid내의 Cell에서 플레이어를 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridCellNotFoundPlayer")] MapGridCellNotFoundPlayer = 19008, + /// + /// MapGrid내의 Cell에서 UgcNpc를 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridCellNotFoundUgcNpc")] MapGridCellNotFoundUgcNpc = 19009, + /// + ///============================================================================================= + /// 메일 관련 오류 : 20000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MailNotFound")] MailNotFound = 20001, + /// + /// 이미 아이템을 받았습니다. + /// + [pbr::OriginalName("MailAlreadyTaken")] MailAlreadyTaken = 20002, + /// + /// 메일 MailType 오류 입니다. + /// + [pbr::OriginalName("MailInvalidMailType")] MailInvalidMailType = 20003, + /// + /// 보낼 수 있는 메일의 갯수를 초과 했습니다. + /// + [pbr::OriginalName("MailMaxSendCountExceed")] MailMaxSendCountExceed = 20004, + /// + /// 메일 Doc이 null 입니다. + /// + [pbr::OriginalName("MailDocIsNull")] MailDocIsNull = 20005, + /// + /// 메일 블락한 유저에게 보낼 수 없습니다. + /// + [pbr::OriginalName("MailBlockUserCannotSend")] MailBlockUserCannotSend = 20006, + /// + /// 타겟의 받을 수 있는 메일의 갯수를 초과 했습니다. + /// + [pbr::OriginalName("MailMaxTargetReceivedCountExceed")] MailMaxTargetReceivedCountExceed = 20007, + /// + /// 나에게 메일을 보낼 수 없습니다. + /// + [pbr::OriginalName("MailCantSendSelf")] MailCantSendSelf = 20008, + /// + /// 아이템이 있는 상태의 메일은 삭제할 수 없습니다. + /// + [pbr::OriginalName("MailCantDeleteIfItemExists")] MailCantDeleteIfItemExists = 20009, + /// + /// MailDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("MailDocException")] MailDocException = 20010, + /// + ///============================================================================================= + /// 메일 프로필 관련 오류 : 20300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MailProfileDocIsNull")] MailProfileDocIsNull = 20301, + /// + /// MailProfileDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("MailProfileDocException")] MailProfileDocException = 20302, + /// + ///============================================================================================= + /// 시스템 메일 관련 오류 : 20500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("SystemMailDocIsNull")] SystemMailDocIsNull = 20501, + /// + ///============================================================================================= + /// 파티 관련 오류 : 21000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("PartyCannotSetGuid")] PartyCannotSetGuid = 21001, + /// + /// Party Member 정보 생성 실패입니다. + /// + [pbr::OriginalName("PartyFailedMakePartyMember")] PartyFailedMakePartyMember = 21002, + /// + /// 파티 인원이 초과되었습니다. + /// + [pbr::OriginalName("PartyIsFull")] PartyIsFull = 21003, + /// + /// 이미 초대를 보냈습니다. + /// + [pbr::OriginalName("AlreadyInviteParty")] AlreadyInviteParty = 21004, + /// + /// 초대 정보를 확인할 수 없습니다. + /// + [pbr::OriginalName("NotFoundPartyInvite")] NotFoundPartyInvite = 21005, + /// + /// 파티 정보를 확인할 수 없습니다. + /// + [pbr::OriginalName("NotFoundParty")] NotFoundParty = 21006, + /// + /// 파티 상태가 아닙니다. + /// + [pbr::OriginalName("NotParty")] NotParty = 21007, + /// + /// 파티 리더가 아닙니다. + /// + [pbr::OriginalName("NotPartyLeader")] NotPartyLeader = 21008, + /// + /// 파티에 입장 중입니다. + /// + [pbr::OriginalName("JoiningParty")] JoiningParty = 21009, + /// + /// 파티원이 아닙니다. + /// + [pbr::OriginalName("NotPartyMember")] NotPartyMember = 21010, + /// + /// 이미 소환된 상태입니다. + /// + [pbr::OriginalName("AlreadySummon")] AlreadySummon = 21011, + /// + /// 파티 리더의 서버 허용인원이 초과되었습니다. + /// + [pbr::OriginalName("PartyLeaderServerIsFull")] PartyLeaderServerIsFull = 21012, + /// + /// 초대할 유저가 콘서트에 있습니다. + /// + [pbr::OriginalName("InviteMemberIsConcert")] InviteMemberIsConcert = 21013, + /// + /// 파티 초대 발송에 실패했습니다. + /// + [pbr::OriginalName("FailToSendInviteMember")] FailToSendInviteMember = 21014, + /// + /// 이미 파티에 소속되어 있습니다. + /// + [pbr::OriginalName("AlreadyPartyMember")] AlreadyPartyMember = 21015, + /// + /// 소환할 수 없는 서버에 있습니다. + /// + [pbr::OriginalName("InvalidSummonServerType")] InvalidSummonServerType = 21016, + /// + /// 소환 대상의 거리가 짧습니다. + /// + [pbr::OriginalName("SummonUserLimitDistance")] SummonUserLimitDistance = 21017, + /// + /// 소환 대상자가 아닙니다. + /// + [pbr::OriginalName("InvalidSummonMember")] InvalidSummonMember = 21018, + /// + /// 파티 리더가 logout 상태입니다. + /// + [pbr::OriginalName("PartyLeaderLoggedOut")] PartyLeaderLoggedOut = 21019, + /// + /// 진행 중인 Vote 가 있습니다. + /// + [pbr::OriginalName("AlreadyStartPartyVote")] AlreadyStartPartyVote = 21020, + /// + /// 진행 중인 Vote 가 없습니다. + /// + [pbr::OriginalName("NoStartPartyVote")] NoStartPartyVote = 21021, + /// + /// 투표 가능 시간이 지났습니다. + /// + [pbr::OriginalName("AlreadyPassPartyVoteTime")] AlreadyPassPartyVoteTime = 21022, + /// + /// 투표 시작 가능 시간이 지나지 않았습니다. + /// + [pbr::OriginalName("InvalidPartyVoteTime")] InvalidPartyVoteTime = 21023, + /// + /// 이미 투표를 하였습니다. + /// + [pbr::OriginalName("AlreadyReplyPartyVote")] AlreadyReplyPartyVote = 21024, + /// + /// 파티 인스턴스 가 없습니다. + /// + [pbr::OriginalName("EmptyPartyInstanceId")] EmptyPartyInstanceId = 21025, + /// + /// 초대할 수 없는 장소입니다. + /// + [pbr::OriginalName("InvalidInvitePlace")] InvalidInvitePlace = 21026, + /// + /// 초대 유저의 정보가 잘못되었습니다. + /// + [pbr::OriginalName("InvitePartyInvalidUsers")] InvitePartyInvalidUsers = 21027, + /// + /// 파티원 소환에 실패했습니다. + /// + [pbr::OriginalName("SummonPartyMemberFail")] SummonPartyMemberFail = 21028, + /// + /// 입력 글자수 최대 길이가 잘못되었습니다. + /// + [pbr::OriginalName("InvalidPartyStringLength")] InvalidPartyStringLength = 21029, + /// + /// 파티명에 금칙어가 있습니다. + /// + [pbr::OriginalName("IncludeBanWordFromPartyName")] IncludeBanWordFromPartyName = 21030, + /// + /// 파티원 정보가 없습니다. + /// + [pbr::OriginalName("JoiningPartyMemberInfoIsNull")] JoiningPartyMemberInfoIsNull = 21031, + /// + /// 서로 다른 월드에 있습니다. + /// + [pbr::OriginalName("InvalidSummonWorldServer")] InvalidSummonWorldServer = 21032, + [pbr::OriginalName("NotExistPartyInstance")] NotExistPartyInstance = 21999, + /// + ///============================================================================================= + /// 버프 관련 오류 : 22000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BuffMetaDataNotFound")] BuffMetaDataNotFound = 22001, + /// + /// 등록되지 않은 버프 카테고리 입니다. + /// + [pbr::OriginalName("BuffNotRegistryCategory")] BuffNotRegistryCategory = 22002, + /// + /// 버프를 찾지 못했습니다. + /// + [pbr::OriginalName("BuffNotFound")] BuffNotFound = 22003, + /// + /// 버프 BuffCategoryType 오류 입니다. + /// + [pbr::OriginalName("BuffInvalidBuffCategoryType")] BuffInvalidBuffCategoryType = 22004, + /// + /// BuffCache 로딩중에 중복된 버프가 발견되었습니다. + /// + [pbr::OriginalName("BuffCacheLoadDuplicatedBuff")] BuffCacheLoadDuplicatedBuff = 22005, + /// + /// 버프 AttributeType 오류 입니다. + /// + [pbr::OriginalName("BuffInvalidAttributeType")] BuffInvalidAttributeType = 22006, + /// + ///============================================================================================= + /// 퀘스트 관련 오류 : 23000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("QuestAssingDataNotExist")] QuestAssingDataNotExist = 23000, + /// + /// 퀘스트 메일이 없습니다. + /// + [pbr::OriginalName("QuestMailNotExist")] QuestMailNotExist = 23001, + /// + /// 이미 완료된 퀘스트 + /// + [pbr::OriginalName("QuestAlreadyEnded")] QuestAlreadyEnded = 23002, + /// + /// 할당 가능한 퀘스트 수 맥스 + /// + [pbr::OriginalName("QuestCountMax")] QuestCountMax = 23003, + /// + /// 타입별로 할당 가능한 퀘스트 수 맥스 + /// + [pbr::OriginalName("QuestTypeAssignCountMax")] QuestTypeAssignCountMax = 23004, + /// + /// 퀘스트 타입 오류 + /// + [pbr::OriginalName("QuestInvalidType")] QuestInvalidType = 23005, + /// + /// 퀘스트 값 오류 + /// + [pbr::OriginalName("QuestInvalidValue")] QuestInvalidValue = 23006, + /// + /// 퀘스트 아이디 없음 + /// + [pbr::OriginalName("QuestIdNotFound")] QuestIdNotFound = 23007, + /// + /// 잘못된 태스트 진행 번호 + /// + [pbr::OriginalName("QuestInvalidTaskNum")] QuestInvalidTaskNum = 23008, + /// + /// 퀘스트 거절은 일반 퀘스트만 가능 + /// + [pbr::OriginalName("QuestRefuseOnlyNormal")] QuestRefuseOnlyNormal = 23009, + /// + /// 포기하려는 퀘스트 정보 존재하지 않습니다. + /// + [pbr::OriginalName("QuestAbadonNotExistQuest")] QuestAbadonNotExistQuest = 23010, + /// + /// 이미 달성한 퀘스트 + /// + [pbr::OriginalName("QuestAlreadyComplete")] QuestAlreadyComplete = 23011, + /// + /// 퀘스트 미달성 + /// + [pbr::OriginalName("QuestNotComplete")] QuestNotComplete = 23012, + /// + /// 퀘스트 포기는 일반 퀘스트만 가능 + /// + [pbr::OriginalName("QuestAbandonOnlyNormal")] QuestAbandonOnlyNormal = 23013, + /// + /// QuestMailDoc이 null 입니다. + /// + [pbr::OriginalName("QuestMailDocIsNull")] QuestMailDocIsNull = 23014, + /// + /// QuestDoc이 null 입니다. + /// + [pbr::OriginalName("QuestDocIsNull")] QuestDocIsNull = 23015, + /// + /// EndQuestDoc이 null 입니다. + /// + [pbr::OriginalName("EndQuestDocIsNull")] EndQuestDocIsNull = 23016, + /// + /// QuestMetaBase가 구현되지 않았습니다. + /// + [pbr::OriginalName("QuestMetaBaseNotImplement")] QuestMetaBaseNotImplement = 23017, + /// + /// Quest notify 레디스에 등록 실패 + /// + [pbr::OriginalName("QuestNotifyRedisRegistFail")] QuestNotifyRedisRegistFail = 23018, + /// + /// Quest를 이미 보유중입니다. + /// + [pbr::OriginalName("QuestAlreadyExist")] QuestAlreadyExist = 23019, + /// + ///============================================================================================= + /// 월드 관련 오류 : 24000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("WorldMetaDataNotFound")] WorldMetaDataNotFound = 24001, + /// + /// 월드 입장 아이템 수량이 부족합니다. + /// + [pbr::OriginalName("LackOfWorldEnterItem")] LackOfWorldEnterItem = 24002, + /// + /// 월드 맵트리 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("WorldMapTreeDataNotFound")] WorldMapTreeDataNotFound = 24003, + /// + /// 월드 맵트리 하위 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("WorldMapTreeChildLandNotFound")] WorldMapTreeChildLandNotFound = 24004, + /// + /// 룸 맵 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("RoomMapDataNotFound")] RoomMapDataNotFound = 24005, + /// + ///============================================================================================= + /// 랜드 관련 오류 : 24500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LandMetaDataNotFound")] LandMetaDataNotFound = 24501, + /// + /// 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("LandNotFound")] LandNotFound = 24502, + /// + /// LandDoc 로딩중에 중복된 랜드가 발견되었습니다. + /// + [pbr::OriginalName("LandDocLoadDuplicatedLand")] LandDocLoadDuplicatedLand = 24503, + /// + /// LandDoc이 null 입니다. + /// + [pbr::OriginalName("LandDocIsNull")] LandDocIsNull = 24504, + /// + /// 소유 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("OwnedLandNotFound")] OwnedLandNotFound = 24505, + /// + /// OwnedLandDoc 로딩중에 중복된 랜드가 발견되었습니다. + /// + [pbr::OriginalName("OwnedLandDocLoadDuplicatedOwnedLand")] OwnedLandDocLoadDuplicatedOwnedLand = 24506, + /// + /// OwnedLandDoc이 null 입니다. + /// + [pbr::OriginalName("OwnedLandDocIsNull")] OwnedLandDocIsNull = 24507, + /// + /// 랜드 맵트리 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("LandMapTreeDataNotFound")] LandMapTreeDataNotFound = 24508, + /// + /// 랜드 맵트리 하위 빌딩을 찾지 못했습니다. + /// + [pbr::OriginalName("LandMapTreeChildBuildingNotFound")] LandMapTreeChildBuildingNotFound = 24509, + /// + /// 랜드 빌딩이 비어 있지 않습니다. + /// + [pbr::OriginalName("LandBuildingIsNotEmpty")] LandBuildingIsNotEmpty = 24510, + /// + /// 랜드 맵 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("LandMapDataNotFound")] LandMapDataNotFound = 24511, + /// + /// 랜드 오너가 존재합니다. + /// + [pbr::OriginalName("LandExistOwner")] LandExistOwner = 24512, + /// + /// 랜드 EditorType 이 USER 가 아닙니다. + /// + [pbr::OriginalName("LandEditorIsNotUser")] LandEditorIsNotUser = 24513, + /// + /// 랜드 오너가 아닙니다. + /// + [pbr::OriginalName("LandOwnerIsNotMatch")] LandOwnerIsNotMatch = 24514, + /// + ///============================================================================================= + /// 빌딩 관련 오류 : 25000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BuildingMetaDataNotFound")] BuildingMetaDataNotFound = 25001, + /// + /// 빌딩을 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingNotFound")] BuildingNotFound = 25002, + /// + /// BuildingDoc 로딩중에 중복된 빌딩이 발견되었습니다. + /// + [pbr::OriginalName("BuildingDocLoadDuplicatedBuilding")] BuildingDocLoadDuplicatedBuilding = 25003, + /// + /// BuildingDoc이 null 입니다. + /// + [pbr::OriginalName("BuildingDocIsNull")] BuildingDocIsNull = 25004, + /// + /// 소유 빌딩을 찾지 못했습니다. + /// + [pbr::OriginalName("OwnedBuildingNotFound")] OwnedBuildingNotFound = 25005, + /// + /// OwnedBuildingDoc 로딩중에 중복된 빌딩이 발견되었습니다. + /// + [pbr::OriginalName("OwnedBuildingDocLoadDuplicatedOwnedBuilding")] OwnedBuildingDocLoadDuplicatedOwnedBuilding = 25006, + /// + /// OwnedBuildingDoc이 null 입니다. + /// + [pbr::OriginalName("OwnedBuildingDocIsNull")] OwnedBuildingDocIsNull = 25007, + /// + /// 빌딩 맵트리 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapTreeDataNotFound")] BuildingMapTreeDataNotFound = 25008, + /// + /// 빌딩 맵트리 하위 룸을 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapTreeChildRoomNotFound")] BuildingMapTreeChildRoomNotFound = 25009, + /// + /// 빌딩 층이 비어 있지 않습니다. + /// + [pbr::OriginalName("BuildingFloorIsNotEmpty")] BuildingFloorIsNotEmpty = 25010, + /// + /// 빌딩 맵 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapDataNotFound")] BuildingMapDataNotFound = 25011, + /// + /// 빌딩 오너가 존재합니다. + /// + [pbr::OriginalName("BuildingExistOwner")] BuildingExistOwner = 25012, + /// + /// 빌딩 맵트리 상위 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapTreeParentLandNotFound")] BuildingMapTreeParentLandNotFound = 20513, + /// + /// 빌딩 오너가 아닙니다. + /// + [pbr::OriginalName("BuildingOwnerIsNotMatch")] BuildingOwnerIsNotMatch = 25014, + /// + ///============================================================================================= + /// 마이홈 관련 오류 : 25500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MyHomeMetaDataNotFound")] MyHomeMetaDataNotFound = 25501, + /// + /// 마이홈을 찾지 못했습니다. + /// + [pbr::OriginalName("MyHomeNotFound")] MyHomeNotFound = 25502, + /// + /// MyHomeDoc 로딩중에 중복된 마이홈이 발견되었습니다. + /// + [pbr::OriginalName("MyHomeDocLoadDuplicatedMyHome")] MyHomeDocLoadDuplicatedMyHome = 25503, + /// + /// 마이홈이 이미 존재합니다. + /// + [pbr::OriginalName("MyHomeAlreadyExist")] MyHomeAlreadyExist = 25504, + /// + /// MyHomeDoc이 null 입니다. + /// + [pbr::OriginalName("MyHomeDocIsNull")] MyHomeDocIsNull = 25505, + /// + /// 마이홈이 내 것이 아닙니다. + /// + [pbr::OriginalName("MyHomeIsNotMine")] MyHomeIsNotMine = 25506, + /// + /// 제작중일때는 마이홈을 변경할수 없습니다. + /// + [pbr::OriginalName("MyHomeCantExchangeWhenCrafting")] MyHomeCantExchangeWhenCrafting = 25507, + /// + /// EditableRoom 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("EditableRoomMetaDataNotFound")] EditableRoomMetaDataNotFound = 25508, + /// + /// EditableFramework 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("EditableFrameworkMetaDataNotFound")] EditableFrameworkMetaDataNotFound = 25509, + /// + /// 사용 가능한 InteriorPoint를 초과 하였습니다. + /// + [pbr::OriginalName("InteriorPointExceed")] InteriorPointExceed = 25510, + /// + /// 마이홈 슬롯이 부족 합니다. + /// + [pbr::OriginalName("MyhomeNotEnoughSlot")] MyhomeNotEnoughSlot = 25511, + /// + /// 마이홈이 선택되어 있습니다. + /// + [pbr::OriginalName("MyhomeIsSelected")] MyhomeIsSelected = 25512, + /// + /// 마이홈 이름 문자열 길이가 짧습니다 + /// + [pbr::OriginalName("MyhomeNameLengthShort")] MyhomeNameLengthShort = 25513, + /// + /// 마이홈 이름 문자열 길이가 깁니다. + /// + [pbr::OriginalName("MyhomeNameLengthLong")] MyhomeNameLengthLong = 25514, + /// + /// 마이홈 이름이 중복 되었습니다. + /// + [pbr::OriginalName("MyhomeNameDuplicated")] MyhomeNameDuplicated = 25515, + /// + /// 마이홈 인터폰이 없습니다. + /// + [pbr::OriginalName("MyhomeInterphoneNotExist")] MyhomeInterphoneNotExist = 25516, + /// + /// 마이홈 시작 지점이 없습니다. + /// + [pbr::OriginalName("MyhomeStartPointNotExist")] MyhomeStartPointNotExist = 25517, + /// + /// 마이홈 인터폰이 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("MyhomeInterphoneExceed")] MyhomeInterphoneExceed = 25518, + /// + /// 마이홈 시작 지점이 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("MyhomeStartPointExceed")] MyhomeStartPointExceed = 25519, + /// + /// 의상 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CraftingClothesExceed")] CraftingClothesExceed = 25520, + /// + /// 요리 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CraftingCookingExceed")] CraftingCookingExceed = 25521, + /// + /// 가구 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CraftingFurnitureExceed")] CraftingFurnitureExceed = 25522, + /// + /// 앵커가 마이홈에 배치되어 있지 않습니다. + /// + [pbr::OriginalName("AnchorIsNotInMyhome")] AnchorIsNotInMyhome = 25523, + /// + /// 제작 진행중인 제작대를 제거 할 수 없습니다. + /// + [pbr::OriginalName("DoNotRemoveProcessCraftingAnchor")] DoNotRemoveProcessCraftingAnchor = 25524, + /// + /// 앵커 Guid가 중복 되었습니다. + /// + [pbr::OriginalName("AnchorGuidDuplicate")] AnchorGuidDuplicate = 25525, + /// + /// 마이홈이 편집 중 입니다. + /// + [pbr::OriginalName("MyhomeIsEditting")] MyhomeIsEditting = 25526, + /// + /// 마이홈이 렌탈 중 입니다. + /// + [pbr::OriginalName("MyhomeIsOnRental")] MyhomeIsOnRental = 25527, + /// + /// 마이홈 Ugc Info 파일을 찾지 못했습니다. + /// + [pbr::OriginalName("MyhomeUgcInfoFileNotFoune")] MyhomeUgcInfoFileNotFoune = 25528, + /// + /// EditableRoom SizeType 오류 입니다. + /// + [pbr::OriginalName("EditableRoomSizeTypeInvalid")] EditableRoomSizeTypeInvalid = 25529, + /// + /// 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CrafterCountExceed")] CrafterCountExceed = 25530, + /// + ///============================================================================================= + /// 미니맵 마커 관련 오류 : 26000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MinimapMarkerNotFound")] MinimapMarkerNotFound = 26001, + /// + /// MinimapMarkerDoc 로딩중에 중복된 미니맵 마커가 발견되었습니다. + /// + [pbr::OriginalName("MinimapMarkerDocLoadDuplicatedMinimapMarker")] MinimapMarkerDocLoadDuplicatedMinimapMarker = 26002, + /// + /// MinimapMarkerDoc이 null 입니다. + /// + [pbr::OriginalName("MinimapMarkerDocIsNull")] MinimapMarkerDocIsNull = 26003, + /// + ///============================================================================================= + /// 카트 관련 오류 : 26500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CartMetaDataNotFound")] CartMetaDataNotFound = 26501, + /// + /// 카트 용량이 가득 찼습니다. + /// + [pbr::OriginalName("CartMaxCountExceed")] CartMaxCountExceed = 26502, + /// + /// 카트에 아이템 스텍이 가득 찼습니다. + /// + [pbr::OriginalName("CartStackCountInvalid")] CartStackCountInvalid = 26503, + /// + /// 카트의 아이템 갯수가 요청값보다 낮습니다. + /// + [pbr::OriginalName("CartStackCountNotEnough")] CartStackCountNotEnough = 26504, + /// + /// 카트에서 아이템을 찾지 못했습니다. + /// + [pbr::OriginalName("CartItemNotFound")] CartItemNotFound = 26505, + /// + /// 등록되지 않은 재화타입 입니다. + /// + [pbr::OriginalName("CartNotRegistryCurrencyType")] CartNotRegistryCurrencyType = 26506, + /// + /// 카트 CurrencyType 오류 입니다. + /// + [pbr::OriginalName("CartInvalidCurrencyType")] CartInvalidCurrencyType = 26507, + /// + /// CartDoc이 null 입니다. + /// + [pbr::OriginalName("CartDocIsNull")] CartDocIsNull = 26508, + /// + /// CartDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CartDocException")] CartDocException = 26509, + /// + ///============================================================================================= + /// 채팅 관련 오류 : 27000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ChatSendSelfFailed")] ChatSendSelfFailed = 27001, + /// + /// 채팅 ChatType 오류 입니다. + /// + [pbr::OriginalName("ChatInvalidChatType")] ChatInvalidChatType = 27002, + /// + /// 귓속말을 블락한 유저에게 보낼 수 없습니다. + /// + [pbr::OriginalName("ChatBlockUserCannotWhisper")] ChatBlockUserCannotWhisper = 27003, + /// + /// 채팅 메시지 길이를 초과했습니다. + /// + [pbr::OriginalName("ChatInvalidMessageLength")] ChatInvalidMessageLength = 27004, + /// + /// 금칙어가 포함되어 있습니다. + /// + [pbr::OriginalName("ChatIncludeBanWord")] ChatIncludeBanWord = 27005, + /// + /// NoticeChatDoc이 null 입니다. + /// + [pbr::OriginalName("NoticeChatDocIsNull")] NoticeChatDocIsNull = 27501, + /// + ///============================================================================================= + /// 탈출 관련 오류 : 28000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EscapePositionNotAvailableTime")] EscapePositionNotAvailableTime = 28001, + /// + /// EscapePositionDoc이 null 입니다. + /// + [pbr::OriginalName("EscapePositionDocIsNull")] EscapePositionDocIsNull = 28002, + /// + ///============================================================================================= + /// 블록 유저 관련 오류 : 28500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BlockUserDocIsNull")] BlockUserDocIsNull = 28501, + /// + ///============================================================================================= + /// 캐릭터 프로필 관련 오류 : 29000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CharacterProfileDocIsNull")] CharacterProfileDocIsNull = 29001, + /// + ///============================================================================================= + /// 커스텀 디파인 Data 관련 오류 : 29300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CustomDefinedDataDocIsNull")] CustomDefinedDataDocIsNull = 29301, + /// + ///============================================================================================= + /// 커스텀 디파인 UI 관련 오류 : 29500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CustomDefinedUiDocIsNull")] CustomDefinedUiDocIsNull = 29501, + /// + ///============================================================================================= + /// 게임 옵션 관련 오류 : 30000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GameOptionDocIsNull")] GameOptionDocIsNull = 30001, + /// + /// GameOptionDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("GameOptionDocException")] GameOptionDocException = 30002, + /// + ///============================================================================================= + /// 레벨 관련 오류 : 30500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LevelDocIsNull")] LevelDocIsNull = 30501, + /// + ///============================================================================================= + /// 위치 관련 오류 : 31000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LocationDocIsNull")] LocationDocIsNull = 31001, + /// + /// 사용 할 수 없는 장소 입니다. + /// + [pbr::OriginalName("NotUsablePlace")] NotUsablePlace = 31002, + /// + /// Redis에 LocationCache 정보 저장을 실패했습니다. + /// + [pbr::OriginalName("RedisLocationCacheSetFailed")] RedisLocationCacheSetFailed = 31003, + /// + ///============================================================================================= + /// 재화 관련 오류 : 32000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MoneyDocIsNull")] MoneyDocIsNull = 32001, + /// + /// CurrencyControl이 초기화되지 않았습니다. + /// + [pbr::OriginalName("MoneyControlNotInitialize")] MoneyControlNotInitialize = 32002, + /// + /// 금전이 부족합니다. + /// + [pbr::OriginalName("MoneyNotEnough")] MoneyNotEnough = 32003, + /// + /// 금전 최대 보유량을 초과 했습니다. + /// + [pbr::OriginalName("MoneyMaxCountExceeded")] MoneyMaxCountExceeded = 32004, + /// + /// 재화 메타 데이터를 찾을 수 없습니다. + /// + [pbr::OriginalName("CurrencyMetaDataNotFound")] CurrencyMetaDataNotFound = 32005, + /// + ///============================================================================================= + /// 상점 관련 오류 : 33000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ShopProductTradingMeterDocIsNull")] ShopProductTradingMeterDocIsNull = 33001, + /// + /// MyHome 에 설치된 Item 입니다. + /// + [pbr::OriginalName("ShopIsMyHomeItem")] ShopIsMyHomeItem = 33002, + /// + /// ShopProduct 에 Shop Buy Type 이 잘못되었습니다. + /// + [pbr::OriginalName("InvalidShopBuyType")] InvalidShopBuyType = 33003, + /// + /// 리뉴얼 할 수 없는 상점입니다. + /// + [pbr::OriginalName("ShopProductCannotRenwal")] ShopProductCannotRenwal = 33004, + /// + /// 자동 갱신시간 전 후 1분 동안은 갱신 할 수 없다. + /// + [pbr::OriginalName("ShopProductNotRenwalTime")] ShopProductNotRenwalTime = 33005, + /// + /// 갱신 가능한 횟수를 이미 다 사용했습니다. + /// + [pbr::OriginalName("ShopProductRenewalCountAlreadyMax")] ShopProductRenewalCountAlreadyMax = 33006, + /// + /// 재판매할 수 없는 상품입니다. + /// + [pbr::OriginalName("ShopItemCannotSell")] ShopItemCannotSell = 33007, + /// + ///============================================================================================= + /// 보상 관련 오류 : 34000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("RewardInfoNotExist")] RewardInfoNotExist = 34000, + /// + /// 보상 타입 오류 + /// + [pbr::OriginalName("RewardInvalidType")] RewardInvalidType = 34001, + /// + /// 보상 타입 값 오류 + /// + [pbr::OriginalName("RewardInvalidTypeValue")] RewardInvalidTypeValue = 34002, + /// + /// + /// + [pbr::OriginalName("NotRequireAttributeValue")] NotRequireAttributeValue = 34003, + /// + /// 보상 그룹 아이디 문자열을 정수로 변환하는데 오류가 발생했습니다. + /// + [pbr::OriginalName("RewardGroupIdParsingFromStringToIntErorr")] RewardGroupIdParsingFromStringToIntErorr = 34004, + /// + ///============================================================================================= + /// Claime 관련 오류 : 35000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ClaimInvalidType")] ClaimInvalidType = 35000, + /// + ///클레임 멤버십 없음 + /// + [pbr::OriginalName("ClaimMembershipNotExist")] ClaimMembershipNotExist = 35001, + /// + ///클레임 정보 없음 + /// + [pbr::OriginalName("ClaimInfoNotExist")] ClaimInfoNotExist = 35002, + /// + ///클레임 보상시간이 안됐다 + /// + [pbr::OriginalName("ClaimRewardNotEnoughTime")] ClaimRewardNotEnoughTime = 35003, + /// + ///클레임 보상 이벤트 종료 + /// + [pbr::OriginalName("ClaimRewardEventEnd")] ClaimRewardEventEnd = 35004, + /// + ///ClaimDoc 존재하지 않음 + /// + [pbr::OriginalName("ClaimDocIsNull")] ClaimDocIsNull = 35005, + /// + ///============================================================================================= + /// 제작 관련 오류 : 36000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CraftRecipeDocIsNull")] CraftRecipeDocIsNull = 36001, + /// + /// CraftHelpDoc이 null 입니다. + /// + [pbr::OriginalName("CraftHelpDocIsNull")] CraftHelpDocIsNull = 36002, + /// + /// CraftDoc이 null 입니다. + /// + [pbr::OriginalName("CraftDocIsNull")] CraftDocIsNull = 36003, + /// + /// Crafting 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("CraftingMetaDataNotFound")] CraftingMetaDataNotFound = 36004, + /// + /// 제작이 완료되지 않았습니다. + /// + [pbr::OriginalName("CraftingNotFinish")] CraftingNotFinish = 36005, + /// + /// 제작중이지 않은 제작대 입니다. + /// + [pbr::OriginalName("CraftingNotCraftingAnchor")] CraftingNotCraftingAnchor = 36006, + /// + /// 제작대가 이미 사용중입니다. + /// + [pbr::OriginalName("CraftingAlreadyCrafting")] CraftingAlreadyCrafting = 36007, + /// + /// 제작대 프랍이 배치되어 있지 않습니다. + /// + [pbr::OriginalName("CraftingAnchorIsNotPlaced")] CraftingAnchorIsNotPlaced = 36008, + /// + /// 제작대 레시피가 등록되어 있지 않습니다. + /// + [pbr::OriginalName("CraftingRecipeIsNotRegister")] CraftingRecipeIsNotRegister = 36009, + /// + /// 레시피와 해당하지 않는 제작대 입니다. + /// + [pbr::OriginalName("CraftingAnchorIsNotMatchWithRecipe")] CraftingAnchorIsNotMatchWithRecipe = 36010, + /// + /// 도움 줄수 있는 횟수가 초과했습니다. + /// + [pbr::OriginalName("CraftingHelpCountOver")] CraftingHelpCountOver = 36011, + /// + /// 같은 유저에게 도움줄 횟수가 초과했습니다. + /// + [pbr::OriginalName("CraftingHelpSameUserCountOver")] CraftingHelpSameUserCountOver = 36012, + /// + /// 도움 받을수 있는 횟수가 초과했습니다. + /// + [pbr::OriginalName("CraftingHelpReceivedCountOver")] CraftingHelpReceivedCountOver = 36013, + /// + /// CraftHelpDoc이 비어있습니다. + /// + [pbr::OriginalName("CraftHelpDocIsEmpty")] CraftHelpDocIsEmpty = 36014, + /// + /// CraftDoc이 비어있습니다. + /// + [pbr::OriginalName("CraftDocIsEmpty")] CraftDocIsEmpty = 36015, + /// + /// 이미 제작이 완료되어 도움을 줄수 없습니다. + /// + [pbr::OriginalName("CraftingAlreadyFinish")] CraftingAlreadyFinish = 36016, + /// + /// 제작대 레시피가 이미 등록되어 있습니다. + /// + [pbr::OriginalName("CraftingRecipeIsAlreadyRegister")] CraftingRecipeIsAlreadyRegister = 36017, + /// + /// CraftDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CraftDocException")] CraftDocException = 36018, + /// + /// CraftHelpDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CraftHelpDocException")] CraftHelpDocException = 36019, + /// + /// CraftRecipeDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CraftRecipeDocException")] CraftRecipeDocException = 36020, + /// + /// 잘못된 제작 갯수의 요청입니다. + /// + [pbr::OriginalName("CraftInvalidRequestCount")] CraftInvalidRequestCount = 36021, + /// + ///============================================================================================= + /// UGQ 관련 오류 : 37000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UgqApiServerRequestFailed")] UgqApiServerRequestFailed = 37001, + /// + /// API Server와의 통신 후 객체 변환 중 오류발생 + /// + [pbr::OriginalName("UgqApiServerConvertToObjectFailed")] UgqApiServerConvertToObjectFailed = 37002, + /// + /// API Server검색 카테고리가 유효하지 않습니다. + /// + [pbr::OriginalName("UgqApiServerInvaildSearchCategoryType")] UgqApiServerInvaildSearchCategoryType = 37003, + /// + /// ugc 신고하기의 내용 길이가 맞지 않습니다. + /// + [pbr::OriginalName("UgqReportInvalidTextLength")] UgqReportInvalidTextLength = 37004, + /// + /// UGQ 메타 정보가 없습니다. + /// + [pbr::OriginalName("UgqQuestMetaNotExist")] UgqQuestMetaNotExist = 37005, + /// + /// UGQ 재화를 차감하기 위한 request 요청이 실패 + /// + [pbr::OriginalName("UgqBeginCreatorPointFail")] UgqBeginCreatorPointFail = 37006, + /// + /// shutdown 된 Ugq Revision 입니다. + /// + [pbr::OriginalName("UgqQuestShutdowned")] UgqQuestShutdowned = 37007, + /// + /// 이미 Test Complete 한 단계입니다. + /// + [pbr::OriginalName("UgqTestQuestAlreadyCompleted")] UgqTestQuestAlreadyCompleted = 37008, + /// + /// 아이템 사용해서 UGQ를 재 할당 받으려고 했으나, 해당 퀘스트에 대해서 완료한 기록이 없어서 에러 + /// + [pbr::OriginalName("UgqReassignUsingItemErrorCauseQuestNotComplete")] UgqReassignUsingItemErrorCauseQuestNotComplete = 37009, + /// + /// 아이템 사용해서 UGQ를 재 할당 받으려고 했으나, 이미 진행중인 퀘스트 존재 + /// + [pbr::OriginalName("UgqReassignUsingItemErrorCauseQuestAlreadyExist")] UgqReassignUsingItemErrorCauseQuestAlreadyExist = 37010, + /// + /// 아이템 사용해서 UGQ를 재 할당 받으려고 했으나, 새로운 리비전이 업데이트 되었음 + /// + [pbr::OriginalName("UgqReassignUsingItemErrorCauseNewRevisionUpdated")] UgqReassignUsingItemErrorCauseNewRevisionUpdated = 37011, + /// + /// UGQ데이터의 리비전 정보가 유효하지 않습니다. + /// + [pbr::OriginalName("UgqQuestDataInvalidRevision")] UgqQuestDataInvalidRevision = 37012, + /// + /// UGQ state가 유효하지 않아 포기할수 없다. + /// + [pbr::OriginalName("UgqAbortCannotCauseInvalidState")] UgqAbortCannotCauseInvalidState = 37013, + /// + /// UGQ Meta 생성 클래스가 존재 하지 않습니다. + /// + [pbr::OriginalName("UgqMetaGeneratorNotExist")] UgqMetaGeneratorNotExist = 37014, + /// + /// UGQ Revision이 업데이트가 되었습니다. + /// + [pbr::OriginalName("UgqQuestDataRevisionUpdated")] UgqQuestDataRevisionUpdated = 37015, + /// + /// UGQ 최신 리비전은 요청한 리비전보다 작을 수 없습니다. + /// + [pbr::OriginalName("UgqRevisionCannotSmallerThanRequestedRevision")] UgqRevisionCannotSmallerThanRequestedRevision = 37016, + /// + /// UGQ 리비전의 상태가 Live가 아닙니다. + /// + [pbr::OriginalName("UgqRevisionStateNotLive")] UgqRevisionStateNotLive = 37017, + /// + /// UGQ 리비전의 상태가 Live 혹은 Test 상태인경우만 ugq 데이터 호출이 가능 + /// + [pbr::OriginalName("UgqRevisionStateOnlyLivenAndTest")] UgqRevisionStateOnlyLivenAndTest = 37018, + /// + /// api 서버를 못찾을 경우 + /// + [pbr::OriginalName("UgqApiServerHttpRequestException")] UgqApiServerHttpRequestException = 37019, + /// + /// 리비전이 변경됐습니다. + /// + [pbr::OriginalName("UgqQuestRevisionChanged")] UgqQuestRevisionChanged = 37020, + /// + /// 본인 소유의 ugq는 본인이 받을수 없습니다. + /// + [pbr::OriginalName("UgqAssignCannotOwnedQuest")] UgqAssignCannotOwnedQuest = 37021, + /// + /// 이미 이전 리비전의 퀘스트를 소유중입니다. + /// + [pbr::OriginalName("UgqAlreadyOwnedOldRevisionQuest")] UgqAlreadyOwnedOldRevisionQuest = 37022, + /// + ///============================================================================================= + /// 시즌 패스 관련 오류 : 38000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("SeasonPassDocIsNull")] SeasonPassDocIsNull = 38001, + /// + /// SeasonPass 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("SeasonPassMetaDataNotFound")] SeasonPassMetaDataNotFound = 38002, + /// + /// SeasonPassReward 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("SeasonPassRewardMetaDataNotFound")] SeasonPassRewardMetaDataNotFound = 38003, + /// + /// 최대 등급에 도달해 더이상 경험치 획득할수 없습니다. + /// + [pbr::OriginalName("SeasonPassMaxGrade")] SeasonPassMaxGrade = 38004, + /// + /// 시즌 패스 기간이 아닙니다. + /// + [pbr::OriginalName("SeasonPassNotAblePeriod")] SeasonPassNotAblePeriod = 38005, + /// + /// 유료 시즌 패스를 이미 구입 했습니다. + /// + [pbr::OriginalName("SeasonPassAlreadyBuyCharged")] SeasonPassAlreadyBuyCharged = 38006, + /// + /// 이미 수령한 보상입니다. + /// + [pbr::OriginalName("SeasonPassAlreadyTakenReward")] SeasonPassAlreadyTakenReward = 38007, + /// + /// 해당 등급에 도달하지 못한 보상입니다. + /// + [pbr::OriginalName("SeasonPassNotEnoughGrade")] SeasonPassNotEnoughGrade = 38008, + /// + /// 해당 보상은 유료 시즌 패스가 필요합니다. + /// + [pbr::OriginalName("SeasonPassNeedChargedPass")] SeasonPassNeedChargedPass = 38009, + /// + /// 경험치 획득이 양수가 아닙니다. + /// + [pbr::OriginalName("SeasonPassInvalidExp")] SeasonPassInvalidExp = 38010, + /// + /// SeasonPassDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("SeasonPassDocException")] SeasonPassDocException = 38011, + /// + ///============================================================================================= + /// 랜드 경매 관련 오류 : 39000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LandAuctionRedisCacheSetFailed")] LandAuctionRedisCacheSetFailed = 39001, + /// + /// LandAuction 객체를 찾을 수 없습니다. + /// + [pbr::OriginalName("LandAuctionNotFound")] LandAuctionNotFound = 39002, + /// + /// LandAuction의 Auction Number 오류 입니다. + /// + [pbr::OriginalName("LandAuctionInvalidAuctionNumber")] LandAuctionInvalidAuctionNumber = 39003, + /// + /// LandAuction 입찰 금전 타입 오류 입니다. + /// + [pbr::OriginalName("LandAuctionBidCurrencyTypeInvalid")] LandAuctionBidCurrencyTypeInvalid = 30004, + /// + /// LandAuction 입찰금이 부족 합니다. + /// + [pbr::OriginalName("LandAuctionBidPriceNotEnough")] LandAuctionBidPriceNotEnough = 39005, + /// + /// LandAuction 캐시 정보가 Redis에 없습니다. + /// + [pbr::OriginalName("LandAuctionEmptyCacheInRedis")] LandAuctionEmptyCacheInRedis = 39006, + /// + /// LandAuctionRegistryDoc에 입력되어 있는 정보 오류 입니다. + /// + [pbr::OriginalName("LandAuctionRegistryInfoInvalid")] LandAuctionRegistryInfoInvalid = 39007, + /// + /// LandAuction가 시작 상태가 아닙니다. + /// + [pbr::OriginalName("LandAuctionNotStarted")] LandAuctionNotStarted = 39008, + /// + /// LandAuction 관련 Cache 와 DB 정보가 일치하지 않습니다. + /// + [pbr::OriginalName("LandAuctionMismatchBetweenCacheAndDb")] LandAuctionMismatchBetweenCacheAndDb = 39009, + /// + /// LandAuction가 이미 시작되었습니다. + /// + [pbr::OriginalName("LandAuctionAlreadyStarted")] LandAuctionAlreadyStarted = 39010, + /// + /// LandAuction 관련 Land 메타 정보에 LandItem 설정이 안되어 있습니다. + /// + [pbr::OriginalName("LandAuctionLandItemNotSet")] LandAuctionLandItemNotSet = 39011, + /// + /// LandAuction 관련 Land 메타 정보에 EditorType 오류 입니다. + /// + [pbr::OriginalName("LandAuctionEditorTypeInvalid")] LandAuctionEditorTypeInvalid = 39012, + /// + /// LandAuction 관련 이미 종료된 랜드 경매 입니다. + /// + [pbr::OriginalName("LandAuctionAlreadyEnded")] LandAuctionAlreadyEnded = 39013, + /// + /// LandAuction 관련 HighestBidUserAttrib 오류 입니다. + /// + [pbr::OriginalName("LandAuctionHighestBidUserAttribError")] LandAuctionHighestBidUserAttribError = 39014, + /// + /// LandAuction 관련 RefundBidPriceAttribError 오류 입니다. + /// + [pbr::OriginalName("LandAuctionRefundBidPriceAttribError")] LandAuctionRefundBidPriceAttribError = 39015, + /// + /// LandAuction 관련 현재 입찰자 RefundBidPriceAttribError 오류 입니다. + /// + [pbr::OriginalName("LandAuctionBidderRefundBidPriceAttribError")] LandAuctionBidderRefundBidPriceAttribError = 39016, + /// + ///============================================================================================= + /// Meta 데이터 오류 : 40000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GameConfigMetaDataNotFound")] GameConfigMetaDataNotFound = 40001, + /// + /// AttributeDefineition 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("AttributeDefineitionMetaDataNotFound")] AttributeDefineitionMetaDataNotFound = 40002, + /// + /// Requirement 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("RequirementMetaDataNotFound")] RequirementMetaDataNotFound = 40003, + /// + /// SystemMail 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("SystemMailMetaDataNotFound")] SystemMailMetaDataNotFound = 40004, + /// + /// Interior 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("InteriorMetaDataNotFound")] InteriorMetaDataNotFound = 40005, + /// + /// PropGroup 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("PropGroupMetaDataNotFound")] PropGroupMetaDataNotFound = 40006, + /// + ///============================================================================================= + /// Ai Chat 서버 오류 : 41000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("AiChatServerStart")] AiChatServerStart = 41001, + /// + /// AI Chat 서버 요청이 실패했습니다. + /// + [pbr::OriginalName("AiChatServerReqFailed")] AiChatServerReqFailed = 41002, + /// + /// Request가 잘못되었습니다. + /// + [pbr::OriginalName("AiChatServerBadrequest")] AiChatServerBadrequest = 41003, + /// + /// + /// + [pbr::OriginalName("AiChatServerForbidden")] AiChatServerForbidden = 41004, + /// + /// 인증되지 않은 사용자입니다. + /// + [pbr::OriginalName("AiChatServerUnauthorized")] AiChatServerUnauthorized = 41005, + /// + /// 사용자를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerUserNotFound")] AiChatServerUserNotFound = 41006, + /// + /// 캐릭터를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerCharacterNotFound")] AiChatServerCharacterNotFound = 41007, + /// + /// 리액션를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerReactionNotFound")] AiChatServerReactionNotFound = 41008, + /// + /// 예시문구를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerExampleDialongNotFound")] AiChatServerExampleDialongNotFound = 41009, + /// + /// 세션을 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerSessionNotFound")] AiChatServerSessionNotFound = 41010, + /// + /// 모델을 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerModelNotFound")] AiChatServerModelNotFound = 41011, + /// + /// 포인트가 충분하지 않습니다. + /// + [pbr::OriginalName("AiChatServerNotEnoughPoint")] AiChatServerNotEnoughPoint = 41012, + /// + /// 인자가 잘못되었습니다. + /// + [pbr::OriginalName("AiChatServerInvalidParameters")] AiChatServerInvalidParameters = 41013, + /// + /// 세션 리미트를 초과하였습니다. + /// + [pbr::OriginalName("AiChatServerSessionLimitExceeded")] AiChatServerSessionLimitExceeded = 41014, + /// + /// 서명이 잘못되었습니다. + /// + [pbr::OriginalName("AiChatServerInvalidSignature")] AiChatServerInvalidSignature = 41015, + /// + /// 유저가 이미 존재합니다. + /// + [pbr::OriginalName("AiChatServerUserAlreadyExists")] AiChatServerUserAlreadyExists = 41016, + /// + /// 삭제에 실패했습니다. + /// + [pbr::OriginalName("AiChatServerRemoveFailed")] AiChatServerRemoveFailed = 41017, + /// + /// 중복된 Guid입니다. + /// + [pbr::OriginalName("AiChatServerDuplicateGuid")] AiChatServerDuplicateGuid = 41018, + /// + /// 중복된 Id입니다. + /// + [pbr::OriginalName("AiChatServerDuplicateId")] AiChatServerDuplicateId = 41019, + /// + /// 채팅을 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerChatNotFound")] AiChatServerChatNotFound = 41020, + /// + /// JWT 사용 기간이 만료 되었습니다. + /// + [pbr::OriginalName("AiChatServerTokenExpired")] AiChatServerTokenExpired = 41021, + /// + /// 서버 내부 오류입니다. + /// + [pbr::OriginalName("AiChatServerInternalServer")] AiChatServerInternalServer = 41022, + /// + /// 잘못된 메시지입니다. + /// + [pbr::OriginalName("AiChatServerInvalidMessage")] AiChatServerInvalidMessage = 41023, + /// + /// 유저만 사용 가능한 API 입니다. 현재 JWT 의 role 이 user가 아닙니다. + /// + [pbr::OriginalName("AiChatServerUserOnlyFeature")] AiChatServerUserOnlyFeature = 41024, + /// + /// 해당 API에 접근 할 권한이 없습니다. 해당 리소스의 소유자 또는 운영자만 접근 가능합니다. + /// + [pbr::OriginalName("AiChatServerOwnershipError")] AiChatServerOwnershipError = 41025, + /// + /// 오더를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerChargeOrderNotFoundError")] AiChatServerChargeOrderNotFoundError = 41026, + /// + /// 클라이언트용 Ai Chat Error Code EndPoint + /// + [pbr::OriginalName("AiChatServerEnd")] AiChatServerEnd = 41100, + /// + /// 포인트 충전 재시도 + /// + [pbr::OriginalName("AiChatServerRetryChargePoint")] AiChatServerRetryChargePoint = 41101, + /// + /// Aichat 비활성화 상태입니다. + /// + [pbr::OriginalName("AiChatServerInactive")] AiChatServerInactive = 41102, + /// + /// AiChatDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("AiChatDocException")] AiChatDocException = 41103, + /// + ///============================================================================================= + /// Npc State 관련 오류 + ///============================================================================================= + /// + [pbr::OriginalName("NpcIsBusy")] NpcIsBusy = 42001, + /// + ///============================================================================================= + /// 칼리움 컨버터 관련 오류 : 43000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LackOfDailyCalium")] LackOfDailyCalium = 43001, + /// + /// 전체 변환 제공 Calium 이 부족합니다. + /// + [pbr::OriginalName("LackOfTotalCalium")] LackOfTotalCalium = 43002, + /// + /// Calium 변환에 필요한 재화가 부족합니다. + /// + [pbr::OriginalName("LackOfCommissionCurrency")] LackOfCommissionCurrency = 43003, + /// + /// 인벤토리에 요청한 변환 Material 수량이 부족합니다. + /// + [pbr::OriginalName("LackOfCommissionMaterials")] LackOfCommissionMaterials = 43004, + /// + /// 입력한 SlotId 가 잘못되었습니다. + /// + [pbr::OriginalName("InvalidMaterialSlotId")] InvalidMaterialSlotId = 43005, + /// + /// Calium Data 로딩에 실패했습니다. + /// + [pbr::OriginalName("FailToLoadCalium")] FailToLoadCalium = 43006, + /// + /// Calium Data 저장에 실패했습니다. + /// + [pbr::OriginalName("FailToSaveCaliumDynamo")] FailToSaveCaliumDynamo = 43007, + /// + ///============================================================================================= + /// 칼리움 교환소 관련 오류 : 43050 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LackOfConvertCalium")] LackOfConvertCalium = 43051, + /// + ///============================================================================================= + /// Web3 관련 오류 : 43100 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GetFailEchoSystemResponse")] GetFailEchoSystemResponse = 43100, + /// + /// EchoSystem 데이터 응답 오류 ( HttpError ) + /// + [pbr::OriginalName("FailToGetEchoSystemHttpError")] FailToGetEchoSystemHttpError = 43101, + /// + /// EchoSystem 데이터 응답 오류 ( 응답값 : null ) + /// + [pbr::OriginalName("FailToGetEchoSystemMessageNull")] FailToGetEchoSystemMessageNull = 43102, + /// + /// EchoSystem 데이터 응답 오류 ( Http 예외 발생 ) + /// + [pbr::OriginalName("FailToGetEchoSystemException")] FailToGetEchoSystemException = 43103, + /// + /// EchoSystem 데이터 전송 오류 + /// + [pbr::OriginalName("FailToSendEchoSystem")] FailToSendEchoSystem = 43104, + /// + /// EchoSystem Rollup Data 획득 실패 + /// + [pbr::OriginalName("FailToGetEchoSystemRollUp")] FailToGetEchoSystemRollUp = 43105, + /// + ///============================================================================================= + /// 서비스 관련 오류 + ///============================================================================================= + /// + [pbr::OriginalName("AccountLoginBlockEnable")] AccountLoginBlockEnable = 50001, + /// + ///============================================================================================= + /// 인스턴스룸 관련 오류 : 51000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("InstanceRoomException")] InstanceRoomException = 51001, + /// + /// 인스턴스 룸 추가 데이터 저장 오류입니다. + /// + [pbr::OriginalName("InstanceRoomCannotWriteExtraInfo")] InstanceRoomCannotWriteExtraInfo = 51002, + /// + /// 인스턴스 룸을 위한 시즌패스 구매를 하지 않았습니다. + /// + [pbr::OriginalName("InstanceRoomNotChargedSeasonPass")] InstanceRoomNotChargedSeasonPass = 51003, + /// + /// 인스턴스 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("InstanceMetaDataNotFound")] InstanceMetaDataNotFound = 51004, + /// + /// 인스턴스 메타 데이타 OverLimit 가 잘못되었습니다. + /// + [pbr::OriginalName("InstanceMetaDataOverLimitWrong")] InstanceMetaDataOverLimitWrong = 51005, + /// + /// AccessType 오류 입니다. + /// + [pbr::OriginalName("InstanceAccessTypeInvalid")] InstanceAccessTypeInvalid = 51006, + /// + /// 인스턴스 입장에 필요한 아이템이 충분하지 않습니다. + /// + [pbr::OriginalName("InstanceAccessItemNotEnough")] InstanceAccessItemNotEnough = 51007, + /// + /// 인스턴스 입장에 필요한 시즌패스를 구매 하지 않았습니다. + /// + [pbr::OriginalName("InstanceAccessSeasonPassNotCharged")] InstanceAccessSeasonPassNotCharged = 51008, + /// + /// 인스턴스 룸이 가득 찼습니다. + /// + [pbr::OriginalName("InstanceRoomIsFull")] InstanceRoomIsFull = 51009, + /// + /// 인스턴스 룸 Id 가 중복 되었습니다. + /// + [pbr::OriginalName("InstanceRoomIdDuplicated")] InstanceRoomIdDuplicated = 51010, + /// + /// 인스턴스 룸이 레디스에 존재하지 않습니다. + /// + [pbr::OriginalName("InstanceRoomNotExistAtRedis")] InstanceRoomNotExistAtRedis = 51011, + /// + ///============================================================================================= + /// Task Reservation 관련 오류 : 52000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TaskReservationDocIsNull")] TaskReservationDocIsNull = 52001, + /// + ///============================================================================================= + /// Billing 관련 오류. Web api : 53000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BillingGetPurchaseInfoFailed")] BillingGetPurchaseInfoFailed = 53001, + /// + /// billing 상태 업데이트 실패 입니다. + /// + [pbr::OriginalName("BillingUpdateStateFailed")] BillingUpdateStateFailed = 53002, + /// + /// billing 확인되지 않은 상태 입니다. + /// + [pbr::OriginalName("BillingInvalidStateType")] BillingInvalidStateType = 53003, + /// + /// billing productMetaId의 타입 변환에 실패했습니다. + /// + [pbr::OriginalName("BillingFailedParseProductMetaIdType")] BillingFailedParseProductMetaIdType = 53004, + /// + /// billing 정의되어 있지 않은 stateType입니다. + /// + [pbr::OriginalName("BillingStateTypeInvalid")] BillingStateTypeInvalid = 53005, + /// + /// billing 변환 할수 없는 stateType입니다. + /// + [pbr::OriginalName("BillingStateTypeCantBeChanged")] BillingStateTypeCantBeChanged = 53006, + /// + /// billing 환불 처리중 입니다. + /// + [pbr::OriginalName("BillingStateTypeRefund")] BillingStateTypeRefund = 53007, + /// + /// billing 환불 처리가 완료된 상품입니다. + /// + [pbr::OriginalName("BillingStateTypeRefundComplete")] BillingStateTypeRefundComplete = 53008, + /// + ///============================================================================================= + /// 이동 관련 오류 : 54000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TaxiMetaDataNotFound")] TaxiMetaDataNotFound = 54001, + /// + /// TaxiType 오류 입니다. + /// + [pbr::OriginalName("TaxiTypeInvalid")] TaxiTypeInvalid = 54002, + /// + /// 워프 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("WarpMetaDataNotFound")] WarpMetaDataNotFound = 54003, + /// + /// WarpTyoe 오류 입니다. + /// + [pbr::OriginalName("WarpTypeInvalid")] WarpTypeInvalid = 54004, + /// + ///============================================================================================= + /// 렌탈 관련 오류 : 55000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("RentalNotFound")] RentalNotFound = 55001, + /// + /// RentalDoc 로딩중에 중복된 미니맵 마커가 발견되었습니다. + /// + [pbr::OriginalName("RentalDocLoadDuplicatedRental")] RentalDocLoadDuplicatedRental = 55002, + /// + /// RentalDoc이 null 입니다. + /// + [pbr::OriginalName("RentalDocIsNull")] RentalDocIsNull = 55003, + /// + /// 렌탈이 불가능한 랜드 입니다. + /// + [pbr::OriginalName("RentalNotAvailableLand")] RentalNotAvailableLand = 55004, + /// + /// 렌탈 주소가 유효하지 않습니다. + /// + [pbr::OriginalName("RentalAddressInvalid")] RentalAddressInvalid = 55005, + /// + /// 렌탈 주소가 비어 있지 않습니다. + /// + [pbr::OriginalName("RentalAddressIsNotEmpty")] RentalAddressIsNotEmpty = 55006, + /// + /// 렌탈비용 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("RentalfeeMetaDataNotFound")] RentalfeeMetaDataNotFound = 55007, + /// + /// 렌탈 계약 정보가 변경 되었습니다. + /// + [pbr::OriginalName("RentalContractInfoUpdate")] RentalContractInfoUpdate = 55008, + /// + /// 렌탈비용이 허용치 보다 낮습니다. + /// + [pbr::OriginalName("RentalCurrencyAmountIsTooLow")] RentalCurrencyAmountIsTooLow = 56009, + /// + /// 렌탈 재화 종류 오류 입니다. + /// + [pbr::OriginalName("RentalCurrencyTypeIsWrong")] RentalCurrencyTypeIsWrong = 56010, + /// + ///============================================================================================= + /// Package 관련 오류 : 56000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("PackageLastOrderRecodeDocException")] PackageLastOrderRecodeDocException = 56001, + /// + /// PackageRepeatDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("PackageRepeatDocException")] PackageRepeatDocException = 56002, + /// + ///============================================================================================= + /// BeaconShop 관련 오류 : 57000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BeaconShopException")] BeaconShopException = 57001, + /// + /// BeaconShop 클라이언트에서 보낸 argument값이 잘못되었습니다. + /// + [pbr::OriginalName("BeaconShopInvalidArgument")] BeaconShopInvalidArgument = 57002, + /// + /// Beacon이 랜탈마이홈에 배치되지 않았습니다. + /// + [pbr::OriginalName("BeaconShopBeaconIsNotInRentalHome")] BeaconShopBeaconIsNotInRentalHome = 57003, + /// + /// BeaconShop에 등록할 아이템을 찾지 못했습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundItem")] BeaconShopNotFoundItem = 57004, + /// + /// BeaconShop에 등록할 아이템 갯수가 부족합니다. + /// + [pbr::OriginalName("BeaconShopNotEnoughItem")] BeaconShopNotEnoughItem = 57005, + /// + /// BeaconShop에 등록할 수 없는 아이템입니다. + /// + [pbr::OriginalName("BeaconShopInvalidItemForSell")] BeaconShopInvalidItemForSell = 57006, + /// + /// BeaconShop에 등록할 아이템의 메타를 찾을수 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundMetaData")] BeaconShopNotFoundMetaData = 57007, + /// + /// BeaconShop에 판매가격이 최소값보다 낮습니다. + /// + [pbr::OriginalName("BeaconShopLowSellingPrice")] BeaconShopLowSellingPrice = 57008, + /// + /// BeaconShop에 등록 수수료가 부족합니다. + /// + [pbr::OriginalName("BeaconShopNotEnoughRegisterFee")] BeaconShopNotEnoughRegisterFee = 57009, + /// + /// BeaconShop에 등록된 슬롯이 가득 찼습니다. + /// + [pbr::OriginalName("BeaconShopSlotIsFull")] BeaconShopSlotIsFull = 57010, + /// + /// BeaconShop에 하루 등록 횟수를 넘었습니다. + /// + [pbr::OriginalName("BeaconShopOverOneDayRegisterLimit")] BeaconShopOverOneDayRegisterLimit = 57011, + /// + /// BeaconShop 생성에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedToCreate")] BeaconShopFailedToCreate = 57012, + /// + /// BeaconShop 게시판에 등록을 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedRegisterBoard")] BeaconShopFailedRegisterBoard = 57013, + /// + /// BeaconShop 게시판에서 지우지 못했습니다. + /// + [pbr::OriginalName("BeaconShopFailedDeleteBoard")] BeaconShopFailedDeleteBoard = 57014, + /// + /// BeaconShop 게시판에 해당 물품이 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundItemFromBoard")] BeaconShopNotFoundItemFromBoard = 57015, + /// + /// BeaconShopProfileDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopProfileException")] BeaconShopProfileException = 57016, + /// + /// BeaconShop 등록에 필요한 골드가 부족합니다. + /// + [pbr::OriginalName("BeaconShopNotEnoughRegisterGold")] BeaconShopNotEnoughRegisterGold = 57017, + /// + /// BeaconShop 구매시 Item Amount가 부족합니다. + /// + [pbr::OriginalName("BeaconShopLackOfItemAmount")] BeaconShopLackOfItemAmount = 57018, + /// + /// BeaconShop 게시판에 아이템 목록 가져오기에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedGetBoardItem")] BeaconShopFailedGetBoardItem = 57019, + /// + /// BeaconShopSoldRecordDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopSoldRecordException")] BeaconShopSoldRecordException = 57020, + /// + /// BeaconShop BeaconInventory 불러오기에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedReloadBeaconShopInven")] BeaconShopFailedReloadBeaconShopInven = 57021, + /// + /// BeaconShop 새로운 정보가 갱신되었습니다. + /// + [pbr::OriginalName("BeaconShopUpdateNewData")] BeaconShopUpdateNewData = 57022, + /// + /// BeaconShop db 불러오기에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedUpdateDataFromDb")] BeaconShopFailedUpdateDataFromDb = 57023, + /// + /// BeaconShop 판매 내역이 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundSoldRecords")] BeaconShopNotFoundSoldRecords = 57024, + /// + /// BeaconShopProfileDoc이 없습니다. + /// + [pbr::OriginalName("BeaconShopProfileDocIsEmpty")] BeaconShopProfileDocIsEmpty = 57025, + /// + /// BeaconShopSoldPriceDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopSoldPriceException")] BeaconShopSoldPriceException = 57026, + /// + /// BeaconShop 정산할 내용이 없습니다.. + /// + [pbr::OriginalName("BeaconShopNotFoundSoldPrice")] BeaconShopNotFoundSoldPrice = 57027, + /// + /// 없는 task + /// + [pbr::OriginalName("UgqInvalidTaskAction")] UgqInvalidTaskAction = 60001, + /// + /// disable 된 task + /// + [pbr::OriginalName("UgqTaskActionDisabled")] UgqTaskActionDisabled = 60002, + /// + /// 없는 dialog type + /// + [pbr::OriginalName("UgqInvalidDialogType")] UgqInvalidDialogType = 60003, + /// + /// 없는 dialog 조건 + /// + [pbr::OriginalName("UgqInvalidDialogCondition")] UgqInvalidDialogCondition = 60004, + /// + /// Validation 에러 + /// + [pbr::OriginalName("UgqValidationError")] UgqValidationError = 60005, + /// + /// 엔티티 없음 + /// + [pbr::OriginalName("UgqNullEntity")] UgqNullEntity = 60006, + /// + /// 상태 변경 실패 + /// + [pbr::OriginalName("UgqStateChangeError")] UgqStateChangeError = 60007, + /// + /// 퀘스트 슬롯 부족 + /// + [pbr::OriginalName("UgqNotEnoughQuestSlot")] UgqNotEnoughQuestSlot = 60008, + /// + /// 편집 불가 상태 + /// + [pbr::OriginalName("UgqNotAllowEdit")] UgqNotAllowEdit = 60009, + /// + /// Dialog가 Task에 없음 + /// + [pbr::OriginalName("UgqDialogIsNotInTask")] UgqDialogIsNotInTask = 60010, + /// + /// 이미지 입력 없음 + /// + [pbr::OriginalName("UgqRequireImage")] UgqRequireImage = 60011, + /// + /// 비컨 입력 필요 + /// + [pbr::OriginalName("UgqRequireBeacon")] UgqRequireBeacon = 60012, + /// + /// 하나의 비컨만 입력해야 함 + /// + [pbr::OriginalName("UgqBeaconInputError")] UgqBeaconInputError = 60013, + /// + /// 게임 DB 접근 에러 + /// + [pbr::OriginalName("UgqGameDBAccessError")] UgqGameDbaccessError = 60014, + /// + /// 내 소유 UgcNpc가 아님 + /// + [pbr::OriginalName("UgqNotOwnUgcNpc")] UgqNotOwnUgcNpc = 60015, + /// + /// 이미 계정이 존재함 + /// + [pbr::OriginalName("UgqAlreadyExistsAccount")] UgqAlreadyExistsAccount = 60016, + /// + /// 예외 발생 + /// + [pbr::OriginalName("UgqServerException")] UgqServerException = 60017, + /// + /// 유효하지 않은 웹포탈 인증 토큰 + /// + [pbr::OriginalName("UgqInvalidWebPortalToken")] UgqInvalidWebPortalToken = 60018, + /// + /// 유효하지 않은 토큰 + /// + [pbr::OriginalName("UgqInvalidToken")] UgqInvalidToken = 60019, + /// + /// 메타버스 계정이 필요함 + /// + [pbr::OriginalName("UgqRequireAccount")] UgqRequireAccount = 60020, + /// + /// 포인트 부족함 + /// + [pbr::OriginalName("UgqNotEnoughCreatorPoint")] UgqNotEnoughCreatorPoint = 60021, + /// + /// 최대 슬롯 수 제한 + /// + [pbr::OriginalName("UgqSlotLimit")] UgqSlotLimit = 60022, + /// + /// 트랜잭션 재시도 횟수 초과 + /// + [pbr::OriginalName("UgqExceedTransactionRetry")] UgqExceedTransactionRetry = 60023, + /// + /// 메타버스에 온라인 상태임 + /// + [pbr::OriginalName("UgqMetaverseOnline")] UgqMetaverseOnline = 60024, + /// + /// 인증 상태가 제거됨 + /// + [pbr::OriginalName("UgqAuthRemoved")] UgqAuthRemoved = 60025, + /// + /// 잘못된 프리셋 이미지 + /// + [pbr::OriginalName("UgqInvalidPresetImage")] UgqInvalidPresetImage = 60026, + /// + /// 재화 부족 + /// + [pbr::OriginalName("UgqNotEnoughCurrency")] UgqNotEnoughCurrency = 60027, + /// + /// 재화 사용 오류 발생 + /// + [pbr::OriginalName("UgqCurrencyError")] UgqCurrencyError = 60028, + /// + /// 이미 등급 변경 예약이 존재함 + /// + [pbr::OriginalName("UgqAlreadyExistsReserveGrade")] UgqAlreadyExistsReserveGrade = 60029, + /// + /// 유효하지 않은 예약 시간 + /// + [pbr::OriginalName("UgqInvalidReserveTime")] UgqInvalidReserveTime = 60030, + /// + /// 같은 등급으로 변경이 불가능. + /// + [pbr::OriginalName("UgqSameGradeReserve")] UgqSameGradeReserve = 60031, + /// + ///============================================================================================= + /// UGQ 관련 오류. InGame Api 오류 + ///============================================================================================= + /// + [pbr::OriginalName("UgqAlreadyBookmarked")] UgqAlreadyBookmarked = 61001, + /// + /// 북마크 안되어 있음 + /// + [pbr::OriginalName("UgqNotBookmarked")] UgqNotBookmarked = 61002, + /// + /// 이미 종아요 + /// + [pbr::OriginalName("UgqAlreadyLiked")] UgqAlreadyLiked = 61003, + /// + /// 좋아요 안되어 있음 + /// + [pbr::OriginalName("UgqNotLiked")] UgqNotLiked = 61004, + /// + /// 이미 신고함 + /// + [pbr::OriginalName("UgqAlreadyReported")] UgqAlreadyReported = 61005, + /// + /// 내 퀘스트가 아님 + /// + [pbr::OriginalName("UgqNotOwnQuest")] UgqNotOwnQuest = 61006, + /// + /// 잘못된 상태 + /// + [pbr::OriginalName("UgqInvalidState")] UgqInvalidState = 61007, + /// + ///============================================================================================= + /// NFT 관련 오류 : 62000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("NftForOwnerAllGetFailed")] NftForOwnerAllGetFailed = 62001, + /// + ///============================================================================================= + /// Broker Api Server 오류: 70000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("InternalServerError")] InternalServerError = 70001, + /// + /// Rdb에 장애가 발생했습니다. + /// + [pbr::OriginalName("RdbError")] RdbError = 70002, + /// + /// Dynamo에 장애가 발생했습니다. + /// + [pbr::OriginalName("DynamoError")] DynamoError = 70003, + /// + /// Api request 형식에 오류가 있습니다. + /// + [pbr::OriginalName("InvalidRequest")] InvalidRequest = 70011, + /// + /// 플래닛 id가 존재하지 않습니다. + /// + [pbr::OriginalName("PlanetIdNotFound")] PlanetIdNotFound = 71001, + /// + /// 해당 플래닛의 SecretKey가 일치하지 않습니다. + /// + [pbr::OriginalName("PlanetSecretKeyDoesNotMatched")] PlanetSecretKeyDoesNotMatched = 71002, + /// + /// 플래닛 인증 토큰에 오류가 있습니다. + /// + [pbr::OriginalName("InvalidPlanetJwt")] InvalidPlanetJwt = 71003, + /// + /// 플래닛 인증 토큰 유효기간이 만료됐습니다. + /// + [pbr::OriginalName("ExpiredPlanetJwt")] ExpiredPlanetJwt = 71004, + /// + /// 메타버스에 유저의 클라이언트 로그인되어 있습니다. 메타버스 앱을 정상 로그아웃 후 다시 시도해 주세요. + /// + [pbr::OriginalName("MetaverseClientOnConnected")] MetaverseClientOnConnected = 71201, + /// + /// 유저 인증 토큰에 오류가 있습니다. + /// + [pbr::OriginalName("InvalidUserJwt")] InvalidUserJwt = 71202, + /// + /// 유저 인증 토큰 유효기간이 만료됐습니다. + /// + [pbr::OriginalName("ExpiredUserJwt")] ExpiredUserJwt = 71203, + /// + /// 계정을 찾을 수 없습니다. + /// + [pbr::OriginalName("AccountNotFound")] AccountNotFound = 71301, + /// + /// 유저를 찾을 수 없습니다. + /// + [pbr::OriginalName("UserNotFound")] UserNotFound = 71302, + /// + /// 교환 주문 아이디가 존재하지 않습니다. + /// + [pbr::OriginalName("ExchangeOrderIdNotFound")] ExchangeOrderIdNotFound = 72001, + /// + /// 총 교환 주문 수량이 초과되었습니다. + /// + [pbr::OriginalName("ExchangeTotalOrderDailyLimitExceeded")] ExchangeTotalOrderDailyLimitExceeded = 72002, + /// + /// 유저 교환 주문 수량이 초과되었습니다. + /// + [pbr::OriginalName("ExchangeUserOrderDailyLimitExceeded")] ExchangeUserOrderDailyLimitExceeded = 72003, + /// + ///============================================================================================= + /// Bot 관련 오류. + ///============================================================================================= + /// + [pbr::OriginalName("BotPlayerIsNull")] BotPlayerIsNull = 962001, + /// + /// ClientToLoginRes가 null 입니다. + /// + [pbr::OriginalName("ClientToLoginResIsNull")] ClientToLoginResIsNull = 962002, + /// + /// ClientToLoginMessage가 null 입니다. + /// + [pbr::OriginalName("ClientToLoginMessageIsNull")] ClientToLoginMessageIsNull = 962003, + /// + /// ClientToGameRes가 null 입니다. + /// + [pbr::OriginalName("ClientToGameResIsNull")] ClientToGameResIsNull = 962004, + /// + /// ClientToGameMessage가 null 입니다. + /// + [pbr::OriginalName("ClientToGameMessageIsNull")] ClientToGameMessageIsNull = 962005, + /// + /// Server 연결 실패 + /// + [pbr::OriginalName("ConnectedToServerFail")] ConnectedToServerFail = 962006, + /// + /// 시나리오에 param 설정이 필요합니다. + /// + [pbr::OriginalName("ScenarionParamIsNull")] ScenarionParamIsNull = 962007, + /// + ///============================================================================================= + /// 전투 관련 오류. 삭제 가능성 있어서 뒷번호로 배정 + /// running mode도 아래에 배정 + ///============================================================================================= + /// + [pbr::OriginalName("BattleRoomContentsTypeOnly")] BattleRoomContentsTypeOnly = 11000001, + /// + /// 배틀인스턴스 타입이 잘못 됐습니다. + /// + [pbr::OriginalName("BattleInstanceTypeError")] BattleInstanceTypeError = 11000002, + /// + /// 이미 존재하는 인스턴스 입니다. + /// + [pbr::OriginalName("BattleInstanceInfoAlreadyExist")] BattleInstanceInfoAlreadyExist = 11000003, + /// + /// 존재하지 않는 인스턴스 입니다. + /// + [pbr::OriginalName("BattleInstanceInfoNotExist")] BattleInstanceInfoNotExist = 11000004, + /// + /// 플에이어 객체 + /// + [pbr::OriginalName("BattleInstanceJoinPlayerError")] BattleInstanceJoinPlayerError = 11000005, + /// + /// 사용 가능한 스폰 포인트가 없습니다. + /// + [pbr::OriginalName("BattleInstanceUsableSpawnPointNotExist")] BattleInstanceUsableSpawnPointNotExist = 11000006, + /// + /// 배틀 인스턴스 비활성화 상태 입니다. + /// + [pbr::OriginalName("BattleInstanceInActive")] BattleInstanceInActive = 11000007, + /// + /// 배틀 인스턴스 앵커 정보가 없습니다. + /// + [pbr::OriginalName("BattleInstanceNotExistAnchors")] BattleInstanceNotExistAnchors = 11000008, + /// + /// 배틀 인스턴스 pod 추가에 실패하였습니다. + /// + [pbr::OriginalName("BattleInstanceAddPodCombatFail")] BattleInstanceAddPodCombatFail = 11000009, + /// + /// 배틀 인스턴스 오브젝트 메타가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceObjectMetaNotExist")] BattleInstanceObjectMetaNotExist = 11000010, + /// + /// 배틀 인스턴스 오브젝트 상호 작용 가능한 시간이 아직 안됐습니다. + /// + [pbr::OriginalName("BattleInstanceObjectInteractionNotyetTime")] BattleInstanceObjectInteractionNotyetTime = 11000011, + /// + /// 배틀 인스턴스 오브젝트 정보가 없습니다. + /// + [pbr::OriginalName("BattleInstanceObjectNotExist")] BattleInstanceObjectNotExist = 11000012, + /// + /// pod combat이 이미 점유중 + /// + [pbr::OriginalName("BattleInstancePodCombatAlreadyOccupy")] BattleInstancePodCombatAlreadyOccupy = 11000013, + /// + /// 배틀 오브젝트가 활성화 되지 않았습니다. + /// + [pbr::OriginalName("BattleInstanceObjectInteractionNotActive")] BattleInstanceObjectInteractionNotActive = 11000015, + /// + /// Config Meta에 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceMetaConfigNotExistData")] BattleInstanceMetaConfigNotExistData = 11000016, + /// + /// Reward Meta에 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceMetaRewardNotExistData")] BattleInstanceMetaRewardNotExistData = 11000017, + /// + /// 픽업 포드 생성시간 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstancePickupPodGeneratedTimeNotExist")] BattleInstancePickupPodGeneratedTimeNotExist = 11000018, + /// + /// 픽업 포드 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstancePickupPodNotExistData")] BattleInstancePickupPodNotExistData = 11000019, + /// + /// 인스턴스에 유저정보가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceNotExistPlayerInfo")] BattleInstanceNotExistPlayerInfo = 11000020, + /// + /// 배틀 오브젝트 상호작용에 실패 하였습니다. + /// + [pbr::OriginalName("BattleInstanceInteractionFail")] BattleInstanceInteractionFail = 11000021, + /// + /// 픽업포드 리워드 생성에 실패 하였습니다. + /// + [pbr::OriginalName("BattleInstancePickupPodRewardAllocateError")] BattleInstancePickupPodRewardAllocateError = 11000022, + /// + /// 배틀 이벤트 정보가 없습니다. + /// + [pbr::OriginalName("BattleInstanceNotExistEventInfo")] BattleInstanceNotExistEventInfo = 11000023, + /// + /// 배틀 이벤트, 라운드가 거의다 끝나가는 시간입니다. + /// + [pbr::OriginalName("BattleInstanceClosingTime")] BattleInstanceClosingTime = 11000024, + /// + /// 배틀 인스턴스 시퀀스 파싱 에러 + /// + [pbr::OriginalName("BattleInstanceSeqParseError")] BattleInstanceSeqParseError = 11000025, + /// + /// 게임모드 조인 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeJoinHandlerNotExist")] GameModeJoinHandlerNotExist = 11000100, + /// + /// 게임모드 Init 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeInitHandlerNotExist")] GameModeInitHandlerNotExist = 11000101, + /// + /// 게임모드 조인 성공 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeJoinSuccessHandlerNotExist")] GameModeJoinSuccessHandlerNotExist = 11000102, + [pbr::OriginalName("DupLogin")] DupLogin = 1, + [pbr::OriginalName("Moving")] Moving = 2, + [pbr::OriginalName("DbError")] DbError = 3, + [pbr::OriginalName("KickFail")] KickFail = 4, + [pbr::OriginalName("NotCorrectPassword")] NotCorrectPassword = 5, + [pbr::OriginalName("NotFoundUser")] NotFoundUser = 6, + [pbr::OriginalName("NoGameServer")] NoGameServer = 7, + [pbr::OriginalName("LoginPending")] LoginPending = 8, + [pbr::OriginalName("NotImplemented")] NotImplemented = 9, + [pbr::OriginalName("NotExistSelectedCharacter")] NotExistSelectedCharacter = 10, + [pbr::OriginalName("NotExistCharacter")] NotExistCharacter = 11, + [pbr::OriginalName("ServerLogicError")] ServerLogicError = 12, + [pbr::OriginalName("NoPermissions")] NoPermissions = 14, + [pbr::OriginalName("RedisFail")] RedisFail = 15, + [pbr::OriginalName("LoginFail")] LoginFail = 1000, + [pbr::OriginalName("DuplicatedUser")] DuplicatedUser = 1001, + [pbr::OriginalName("InvalidToken")] InvalidToken = 1002, + [pbr::OriginalName("NotCorrectServer")] NotCorrectServer = 1003, + [pbr::OriginalName("Inspection")] Inspection = 1004, + [pbr::OriginalName("BlackList")] BlackList = 1005, + [pbr::OriginalName("ServerFull")] ServerFull = 1006, + [pbr::OriginalName("NotFoundServer")] NotFoundServer = 1007, + [pbr::OriginalName("NotFoundTable")] NotFoundTable = 1008, + [pbr::OriginalName("TableError")] TableError = 1009, + [pbr::OriginalName("InvalidCondition")] InvalidCondition = 1100, + [pbr::OriginalName("CharCreateFail")] CharCreateFail = 2000, + [pbr::OriginalName("CharSelectFail")] CharSelectFail = 2100, + [pbr::OriginalName("CreateRoomFail")] CreateRoomFail = 3000, + [pbr::OriginalName("JoinRoomFail")] JoinRoomFail = 3100, + [pbr::OriginalName("JoinInstanceFail")] JoinInstanceFail = 3200, + [pbr::OriginalName("NotExistInstanceTicket")] NotExistInstanceTicket = 3201, + [pbr::OriginalName("LeaveInstanceFail")] LeaveInstanceFail = 3300, + [pbr::OriginalName("NotExistInstanceRoom")] NotExistInstanceRoom = 3400, + [pbr::OriginalName("NotCorrectInstanceRoom")] NotCorrectInstanceRoom = 3401, + [pbr::OriginalName("PosIsEmpty")] PosIsEmpty = 3402, + [pbr::OriginalName("EnterMyHomeFail")] EnterMyHomeFail = 3800, + [pbr::OriginalName("LeaveMyHomeFail")] LeaveMyHomeFail = 3900, + [pbr::OriginalName("ExchangeMyHomeFail")] ExchangeMyHomeFail = 4000, + [pbr::OriginalName("NotFoundMyHomeData")] NotFoundMyHomeData = 4001, + [pbr::OriginalName("NotMyHomeOwner")] NotMyHomeOwner = 4002, + [pbr::OriginalName("AlreadyHaveMyHome")] AlreadyHaveMyHome = 4003, + [pbr::OriginalName("ExchangeMyHomePropFail")] ExchangeMyHomePropFail = 4100, + [pbr::OriginalName("ExchangeBuildingFail")] ExchangeBuildingFail = 4300, + [pbr::OriginalName("ExchangeBuildingLFPropFail")] ExchangeBuildingLfpropFail = 4400, + [pbr::OriginalName("ExchangeInstanceFail")] ExchangeInstanceFail = 4500, + [pbr::OriginalName("ExchangeSocialActionSlotFail")] ExchangeSocialActionSlotFail = 4600, + [pbr::OriginalName("NotFoundSocialActionData")] NotFoundSocialActionData = 4602, + [pbr::OriginalName("NotInSocialActionSlot")] NotInSocialActionSlot = 4603, + [pbr::OriginalName("AlreadyHaveSocialAction")] AlreadyHaveSocialAction = 4604, + [pbr::OriginalName("NotFoundBuildingData")] NotFoundBuildingData = 4652, + [pbr::OriginalName("NotFoundFloorInfo")] NotFoundFloorInfo = 4653, + [pbr::OriginalName("NotFoundIndunData")] NotFoundIndunData = 4655, + [pbr::OriginalName("EnterFittingRoomFail")] EnterFittingRoomFail = 4700, + [pbr::OriginalName("NotEntetedFittingRoom")] NotEntetedFittingRoom = 4701, + [pbr::OriginalName("MakeFailOtp")] MakeFailOtp = 4702, + [pbr::OriginalName("NotExistRoomInfoForEnter")] NotExistRoomInfoForEnter = 4703, + [pbr::OriginalName("NotExistGameServerForEnter")] NotExistGameServerForEnter = 4704, + [pbr::OriginalName("PositionSaveFail")] PositionSaveFail = 4705, + [pbr::OriginalName("ExchangeEmotionSlotFail")] ExchangeEmotionSlotFail = 4800, + [pbr::OriginalName("EmotionSlotOutOfRange")] EmotionSlotOutOfRange = 4801, + [pbr::OriginalName("NotFoundAnchorGuidInMap")] NotFoundAnchorGuidInMap = 4899, + [pbr::OriginalName("NotFoundAnchorGuid")] NotFoundAnchorGuid = 4900, + [pbr::OriginalName("PropIsOccupied")] PropIsOccupied = 4901, + [pbr::OriginalName("PropIsUsed")] PropIsUsed = 4902, + [pbr::OriginalName("PropTypeisWrong")] PropTypeisWrong = 4903, + [pbr::OriginalName("NotFoundEmotionData")] NotFoundEmotionData = 4920, + [pbr::OriginalName("NotInEmotionSlot")] NotInEmotionSlot = 4921, + [pbr::OriginalName("CreateItemFail")] CreateItemFail = 4996, + [pbr::OriginalName("AddItemFail")] AddItemFail = 4997, + [pbr::OriginalName("DeleteItemFail")] DeleteItemFail = 4998, + [pbr::OriginalName("NoMoreAddItem")] NoMoreAddItem = 4999, + [pbr::OriginalName("NotFoundItem")] NotFoundItem = 5000, + [pbr::OriginalName("NotEnoughItem")] NotEnoughItem = 5001, + [pbr::OriginalName("InvalidSlotIndex")] InvalidSlotIndex = 5002, + [pbr::OriginalName("DuplicatedItemGuid")] DuplicatedItemGuid = 5003, + [pbr::OriginalName("NotFoundItemTableId")] NotFoundItemTableId = 5004, + [pbr::OriginalName("NotSelectedChar")] NotSelectedChar = 5005, + [pbr::OriginalName("NotFoundMap")] NotFoundMap = 5006, + [pbr::OriginalName("NotEmptySlot")] NotEmptySlot = 5007, + [pbr::OriginalName("EmptySlot")] EmptySlot = 5008, + [pbr::OriginalName("NotFoundBuffTableId")] NotFoundBuffTableId = 5009, + [pbr::OriginalName("NotFoundBuff")] NotFoundBuff = 5010, + [pbr::OriginalName("NotExistForecedMoveInfo")] NotExistForecedMoveInfo = 5011, + [pbr::OriginalName("DuplicatedNickName")] DuplicatedNickName = 5013, + [pbr::OriginalName("AlreadySetNickName")] AlreadySetNickName = 5014, + [pbr::OriginalName("DisallowedCharacters")] DisallowedCharacters = 5015, + [pbr::OriginalName("DbUpdateFailed")] DbUpdateFailed = 5016, + [pbr::OriginalName("InvalidArgument")] InvalidArgument = 5017, + [pbr::OriginalName("MailSystemError")] MailSystemError = 5030, + [pbr::OriginalName("InvalidTarget")] InvalidTarget = 5031, + [pbr::OriginalName("NotFoundMail")] NotFoundMail = 5032, + [pbr::OriginalName("EmptyItemInMail")] EmptyItemInMail = 5033, + [pbr::OriginalName("MailSendCountOver")] MailSendCountOver = 5034, + [pbr::OriginalName("ChangedNickName")] ChangedNickName = 5035, + [pbr::OriginalName("StateChangeFailed")] StateChangeFailed = 5040, + [pbr::OriginalName("NotFoundTarget")] NotFoundTarget = 5050, + [pbr::OriginalName("BlockedFromTarget")] BlockedFromTarget = 5051, + [pbr::OriginalName("LogOffTarget")] LogOffTarget = 5052, + [pbr::OriginalName("CantSendToSelf")] CantSendToSelf = 5053, + [pbr::OriginalName("BuffTypeIsWrong")] BuffTypeIsWrong = 5070, + [pbr::OriginalName("RegisterToolSlotFail")] RegisterToolSlotFail = 5080, + [pbr::OriginalName("DeregisterToolSlotFail")] DeregisterToolSlotFail = 5081, + [pbr::OriginalName("ToolSlotOutOfRange")] ToolSlotOutOfRange = 5082, + [pbr::OriginalName("NotFoundToolSlot")] NotFoundToolSlot = 5083, + [pbr::OriginalName("EmptyToolSlot")] EmptyToolSlot = 5084, + [pbr::OriginalName("FolderNameExceededLength")] FolderNameExceededLength = 5090, + [pbr::OriginalName("FolderNameAlreadyExist")] FolderNameAlreadyExist = 5091, + [pbr::OriginalName("FolderNameNotExist")] FolderNameNotExist = 5092, + [pbr::OriginalName("FolderNameAlreadyMaxHoldCount")] FolderNameAlreadyMaxHoldCount = 5093, + [pbr::OriginalName("FolderCountExceed")] FolderCountExceed = 5094, + [pbr::OriginalName("FolderCreateFail")] FolderCreateFail = 5095, + [pbr::OriginalName("FolderReNameCannotDefault")] FolderReNameCannotDefault = 5096, + [pbr::OriginalName("FolderOdertypeInvalid")] FolderOdertypeInvalid = 5097, + [pbr::OriginalName("FriendRequestNotExistInfo")] FriendRequestNotExistInfo = 5100, + [pbr::OriginalName("FriendRequestAlreadySend")] FriendRequestAlreadySend = 5101, + [pbr::OriginalName("FriendRequestAlreadyReceive")] FriendRequestAlreadyReceive = 5102, + [pbr::OriginalName("FriendRequestCantSendToSelf")] FriendRequestCantSendToSelf = 5103, + [pbr::OriginalName("FriendRequestNotExistReceivedInfo")] FriendRequestNotExistReceivedInfo = 5104, + [pbr::OriginalName("FriendRequestAlreadyFriend")] FriendRequestAlreadyFriend = 5105, + [pbr::OriginalName("InvalidType")] InvalidType = 5106, + [pbr::OriginalName("InvalidAttributeSlot")] InvalidAttributeSlot = 5107, + [pbr::OriginalName("FriendRequestCannotSendBlockUser")] FriendRequestCannotSendBlockUser = 5119, + [pbr::OriginalName("AddFriendAlreadyBlockUser")] AddFriendAlreadyBlockUser = 5120, + [pbr::OriginalName("AddFriendNotExistCharacter")] AddFriendNotExistCharacter = 5121, + [pbr::OriginalName("AddFriendAlreadyFriend")] AddFriendAlreadyFriend = 5122, + [pbr::OriginalName("AddFriendAlreadyCancelRequest")] AddFriendAlreadyCancelRequest = 5123, + [pbr::OriginalName("AddFriendAlreadyExpired")] AddFriendAlreadyExpired = 5124, + [pbr::OriginalName("FriendInfoNotExist")] FriendInfoNotExist = 5130, + [pbr::OriginalName("FriendInfoMyCountAlreadyMaxCount")] FriendInfoMyCountAlreadyMaxCount = 5131, + [pbr::OriginalName("FriendInfoOthersCountAlreadyMaxCount")] FriendInfoOthersCountAlreadyMaxCount = 5132, + [pbr::OriginalName("FriendInfoOffline")] FriendInfoOffline = 5133, + [pbr::OriginalName("FriendInfoAlreadyExist")] FriendInfoAlreadyExist = 5134, + [pbr::OriginalName("FriendInviteMyPosIsNotMyHome")] FriendInviteMyPosIsNotMyHome = 5140, + [pbr::OriginalName("FriendInviteDontDisturbState")] FriendInviteDontDisturbState = 5141, + [pbr::OriginalName("FriendInviteExpireTimeRemain")] FriendInviteExpireTimeRemain = 5142, + [pbr::OriginalName("FriendInviteWaitingOtherInvite")] FriendInviteWaitingOtherInvite = 5143, + [pbr::OriginalName("FriendInviteAlreadyExpire")] FriendInviteAlreadyExpire = 5144, + [pbr::OriginalName("FriendKickMyPosIsNotMyHome")] FriendKickMyPosIsNotMyHome = 5145, + [pbr::OriginalName("FriendKickMemberNotExist")] FriendKickMemberNotExist = 5146, + [pbr::OriginalName("FriendIsInAnotherMyhome")] FriendIsInAnotherMyhome = 5147, + [pbr::OriginalName("BlockUserMaxCount")] BlockUserMaxCount = 5150, + [pbr::OriginalName("BlockUserAlreadyBlock")] BlockUserAlreadyBlock = 5151, + [pbr::OriginalName("BlockUserCannotSendMailTo")] BlockUserCannotSendMailTo = 5152, + [pbr::OriginalName("BlockedByOther")] BlockedByOther = 5153, + [pbr::OriginalName("BlockUserCannotWhisperTo")] BlockUserCannotWhisperTo = 5154, + [pbr::OriginalName("BlockInfoEmpty")] BlockInfoEmpty = 5155, + [pbr::OriginalName("BlockUserCannotInviteParty")] BlockUserCannotInviteParty = 5156, + [pbr::OriginalName("CartSellTypeMissMatchWithTable")] CartSellTypeMissMatchWithTable = 5200, + [pbr::OriginalName("CartFullStackofItem")] CartFullStackofItem = 5201, + [pbr::OriginalName("CartisFull")] CartisFull = 5202, + [pbr::OriginalName("BanNickName")] BanNickName = 5203, + [pbr::OriginalName("CurrencyNotFoundData")] CurrencyNotFoundData = 5400, + [pbr::OriginalName("CurrencyNotEnough")] CurrencyNotEnough = 5401, + [pbr::OriginalName("CurrencyInvalidValue")] CurrencyInvalidValue = 5402, + [pbr::OriginalName("StartBuffFailed")] StartBuffFailed = 5500, + [pbr::OriginalName("StopBuffFailed")] StopBuffFailed = 5501, + [pbr::OriginalName("NotFoundNickName")] NotFoundNickName = 5600, + [pbr::OriginalName("NotFoundTattooAttributeData")] NotFoundTattooAttributeData = 5800, + [pbr::OriginalName("NotFoundShopId")] NotFoundShopId = 5900, + [pbr::OriginalName("TimeOverForPurchase")] TimeOverForPurchase = 5901, + [pbr::OriginalName("NotEnoughAttribute")] NotEnoughAttribute = 5902, + [pbr::OriginalName("InvalidGender")] InvalidGender = 5903, + [pbr::OriginalName("IsEquipItem")] IsEquipItem = 5904, + [pbr::OriginalName("InvalidItem")] InvalidItem = 5905, + [pbr::OriginalName("AlreadyRegistered")] AlreadyRegistered = 5906, + [pbr::OriginalName("NotFoundShopItem")] NotFoundShopItem = 5907, + [pbr::OriginalName("NotEnoughShopItem")] NotEnoughShopItem = 5908, + [pbr::OriginalName("InvalidItemCount")] InvalidItemCount = 5909, + [pbr::OriginalName("InventoryFull")] InventoryFull = 5910, + [pbr::OriginalName("NotFoundProductId")] NotFoundProductId = 5911, + [pbr::OriginalName("RandomBoxItemDataInvalid")] RandomBoxItemDataInvalid = 6100, + [pbr::OriginalName("NotExistGachaData")] NotExistGachaData = 6101, +} + +#endregion + +#region Messages +/// +/// 각종 결과 전달용 정보 - kangms +/// +[global::System.SerializableAttribute] +public sealed partial class Result : pb::IMessage +#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage +#endif +{ + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Result()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::DefineResultReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Result() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Result(Result other) : this() { + errorCode_ = other.errorCode_; + resultString_ = other.resultString_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Result Clone() { + return new Result(this); + } + + /// Field number for the "errorCode" field. + public const int ErrorCodeFieldNumber = 1; + private global::ServerErrorCode errorCode_ = global::ServerErrorCode.Success; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::ServerErrorCode ErrorCode { + get { return errorCode_; } + set { + errorCode_ = value; + } + } + + /// Field number for the "resultString" field. + public const int ResultStringFieldNumber = 2; + private string resultString_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string ResultString { + get { return resultString_; } + set { + resultString_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as Result); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(Result other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (ErrorCode != other.ErrorCode) return false; + if (ResultString != other.ResultString) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (ErrorCode != global::ServerErrorCode.Success) hash ^= ErrorCode.GetHashCode(); + if (ResultString.Length != 0) hash ^= ResultString.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (ErrorCode != global::ServerErrorCode.Success) { + output.WriteRawTag(8); + output.WriteEnum((int) ErrorCode); + } + if (ResultString.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ResultString); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (ErrorCode != global::ServerErrorCode.Success) { + output.WriteRawTag(8); + output.WriteEnum((int) ErrorCode); + } + if (ResultString.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ResultString); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (ErrorCode != global::ServerErrorCode.Success) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ErrorCode); + } + if (ResultString.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ResultString); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(Result other) { + if (other == null) { + return; + } + if (other.ErrorCode != global::ServerErrorCode.Success) { + ErrorCode = other.ErrorCode; + } + if (other.ResultString.Length != 0) { + ResultString = other.ResultString; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + ErrorCode = (global::ServerErrorCode) input.ReadEnum(); + break; + } + case 18: { + ResultString = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + ErrorCode = (global::ServerErrorCode) input.ReadEnum(); + break; + } + case 18: { + ResultString = input.ReadString(); + break; + } + } + } + } + #endif + +} + +#endregion + + +#endregion Designer generated code diff --git a/Protocol/out-Proto/DefineResult.cs.r128445 b/Protocol/out-Proto/DefineResult.cs.r128445 new file mode 100644 index 0000000..3142c0d --- /dev/null +++ b/Protocol/out-Proto/DefineResult.cs.r128445 @@ -0,0 +1,4895 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Define_Result.proto +// +#pragma warning disable 1591, 0612, 3021, 8981 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +/// Holder for reflection information generated from Define_Result.proto +public static partial class DefineResultReflection { + + #region Descriptor + /// File descriptor for Define_Result.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static DefineResultReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChNEZWZpbmVfUmVzdWx0LnByb3RvIkMKBlJlc3VsdBIjCgllcnJvckNvZGUY", + "ASABKA4yEC5TZXJ2ZXJFcnJvckNvZGUSFAoMcmVzdWx0U3RyaW5nGAIgASgJ", + "Kqj8AQoPU2VydmVyRXJyb3JDb2RlEgsKB1N1Y2Nlc3MQABIdChBSZXN1bHRD", + "b2RlTm90U2V0EP///////////wESFgoRVHJ5Q2F0Y2hFeGNlcHRpb24QkU4S", + "FAoPRG90TmV0RXhjZXB0aW9uEJJOEhYKEVByb3VkTmV0RXhjZXB0aW9uEJNO", + "EhYKEVJhYmJpdE1xRXhjZXB0aW9uEJROEhYKEUR5bmFtb0RiRXhjZXB0aW9u", + "EJVOEh4KGUR5bmFtb0RiVHJhbnNhY3RFeGNlcHRpb24Qlk4SEwoOUmVkaXNF", + "eGNlcHRpb24Ql04SFgoRTWV0YUluZm9FeGNlcHRpb24QmE4SFQoQTXlTcWxE", + "YkV4Y2VwdGlvbhCZThIVChBNb25nb0RiRXhjZXB0aW9uEJpOEiUKIE5sb2dX", + "aXRoQXdzQ2xvdWRXYXRjaFNldHVwRmFpbGVkEK9OEhcKEk5sb2dOb3RJbml0", + "aWFsaXplZBCwThIUCg9Mb2dBY3Rpb25Jc051bGwQw04SFgoRTG9nQXBwZW5k", + "ZXJJc051bGwQxE4SFwoSTG9nRm9ybWF0dGVySXNOdWxsEMVOEhkKFExvZ0Fj", + "dGlvblR5cGVJbnZhbGlkEMZOEhIKDVJtaUhvc3RJc051bGwQ9U4SHQoYUm1p", + "SG9zdEhhbmRsZXJCaW5kRmFpbGVkEPZOEhkKFFN1YkhhbmRsZXJCaW5kRmFp", + "bGVkEPdOEhYKEVByb3h5QXR0YWNoRmFpbGVkEPhOEh4KGVNldE1lc3NhZ2VN", + "YXhMZW5ndGhGYWlsZWQQ+U4SJAofUGFja2V0UmVjdkhhbmRsZXJSZWdpc3Rl", + "ckZhaWxlZBCnTxIkCh9QYWNrZXRTZW5kSGFuZGxlclJlZ2lzdGVyRmFpbGVk", + "EKhPEhYKEVBhY2tldFJlY3ZJbnZhbGlkEKlPEh4KGVJhY2tldFJlY3ZIYW5k", + "bGVyTm90Rm91bmQQqk8SHgoZTGFyZ2VQYWNrZXROb3RBbGxSZWNlaXZlZBCr", + "TxIcChdMYXJnZVBhY2tldFJlY3ZUaW1lT3ZlchCsTxIXChJEYlF1ZXJ5VHlw", + "ZUludmFsaWQQ2U8SKQokRHluYW1vRGJUcmFuc2FjdGlvbkNhbmNlbGVkRXhj", + "ZXB0aW9uEONPEiQKH0R5bmFtb0RiQW1hem9uRHluYW1vRGJFeGNlcHRpb24Q", + "5E8SIwoeRHluYW1vRGJBbWF6b25TZXJ2aWNlRXhjZXB0aW9uEOVPEh0KGER5", + "bmFtb0RiQ29uZmlnTG9hZEZhaWxlZBDmTxIaChVEeW5hbW9EYkNvbm5lY3RG", + "YWlsZWQQ508SHgoZRHluYW1vRGJUYWJsZUNyZWF0ZUZhaWxlZBDoTxIeChlE", + "eW5hbW9EYlRhYmxlTm90Q29ubmVjdGVkEOlPEhgKE0R5bmFtb0RiUXVlcnlG", + "YWlsZWQQ6k8SHQoYRHluYW1vRGJJdGVtU2l6ZUV4Y2VlZGVkEOtPEiIKHUR5", + "bmFtb0RiUXVlcnlOb01hdGNoQXR0cmlidXRlEOxPEikKJER5bmFtb0RiVHJh", + "bnNhY3Rpb25Db25mbGljdEV4Y2VwdGlvbhDtTxIcChdEeW5hbW9EYkV4cHJl", + "c3Npb25FcnJvchDuTxIfChpEeW5hbW9EYlByaW1hcnlLZXlOb3RGb3VuZBDv", + "TxIdChhEeW5hbW9EYlRhYmxlTmFtZUludmFsaWQQ8E8SHAoXRHluYW1vRGJD", + "b25uZWN0b3JJc051bGwQ8U8SIAobRHluYW1vRGJUYWJsZU5hbWVEdXBsaWNh", + "dGVkEPJPEhsKFkR5bmFtb0RiUXVlcnlFeGNlcHRpb24Q908SHQoYRHluYW1v", + "RGJRdWVyeU5vUmVxdWVzdGVkEPhPEicKIkR5bmFtb0RiUXVlcnlOb3RGb3Vu", + "ZERvY3VtZW50UXVlcnkQ+U8SKwomRHluYW1vRGJRdWVyeUV4Y2VwdGlvbk5v", + "dGlmaWVyTm90Rm91bmQQ+k8SKQokRHluYW1vRGJEb2N1bWVudElzTnVsbElu", + "UXVlcnlDb250ZXh0EIFQEh4KGUR5bmFtb0RiRG9jdW1lbnRJc0ludmFsaWQQ", + "glASLAonRHluYW1vRGJEb2N1bWVudFF1ZXJ5Q29udGV4dFR5cGVJbnZhbGlk", + "EINQEiQKH0R5bmFtb0RiRG9jdW1lbnRDb3B5RmFpbGVkVG9Eb2MQhFASIQoc", + "RHluYW1vRGJEb2N1bWVudFVwc2VydEZhaWxlZBCFUBIhChxEeW5hbW9EYkl0", + "ZW1SZXF1ZXN0SXNJbnZhbGlkEItQEi8KKkR5bmFtb0RiSXRlbVJlcXVlc3RR", + "dWVyeUNvbnRleHRUeXBlSW52YWxpZBCMUBItCihEeW5hbW9EYkl0ZW1SZXF1", + "ZXN0VXBkYXRlRXhwcmVzc2lvbkVtcHR5EI1QEhkKFER5bmFtb0RiRG9jUGtJ", + "bnZhbGlkEJVQEhkKFER5bmFtb0RiRG9jU2tJbnZhbGlkEJZQEiQKH0R5bmFt", + "b0RiRG9jQXR0cmliVHlwZUR1cGxpY2F0ZWQQl1ASJAofRHluYW1vRGJEb2ND", + "b3B5RmFpbGVkVG9Eb2N1bWVudBCYUBImCiFEeW5hbW9EYkRvY0NvcHlGYWls", + "ZWRGcm9tRG9jdW1lbnQQmVASHAoXRHluYW1vRGJEb2NUeXBlTm90TWF0Y2gQ", + "mlASGwoWRHluYW1vRGJSZXF1ZXN0SW52YWxpZBCbUBIkCh9EeW5hbW9EYkRv", + "Y0F0dHJpYnV0ZVN0YXRlTm90U2V0EJxQEicKIkR5bmFtb0RiRG9jQXR0cmli", + "V3JhcHBlckNvcHlGYWlsZWQQnVASJgohRHluYW1vRGJEb2NBdHRyaWJ1dGVH", + "ZXR0aW5nRmFpbGVkEJ5QEh8KGkR5bmFtb0RiRG9jTGlua1BrU2tJbnZhbGlk", + "EJ9QEiMKHkR5bmFtb0RiRG9jQXR0cmliV3JhcHBlcklzTnVsbBCgUBIgChtN", + "eVNxbENvbm5lY3Rpb25DcmVhdGVGYWlsZWQQqVASHgoZTXlTcWxDb25uZWN0", + "aW9uT3BlbkZhaWxlZBCqUBIaChVNeVNxbERiUXVlcnlFeGNlcHRpb24Qq1AS", + "IQocTW9uZ29EYkluaXRBbmRWZWZpZnlEYkZhaWxlZBCzUBIdChhSZWRpc1Nl", + "cnZlckNvbm5lY3RGYWlsZWQQvVASHAoXUmVkaXNTdHJpbmdzV3JpdGVGYWls", + "ZWQQvlASGwoWUmVkaXNTdHJpbmdzUmVhZEZhaWxlZBC/UBIZChRSZWRpc1Nl", + "dHNXcml0ZUZhaWxlZBDAUBIYChNSZWRpc1NldHNSZWFkRmFpbGVkEMFQEh8K", + "GlJlZGlzU29ydGVkU2V0c1dyaXRlRmFpbGVkEMJQEh4KGVJlZGlzU29ydGVk", + "U2V0c1JlYWRGYWlsZWQQw1ASGwoWUmVkaXNIYXNoZXNXcml0ZUZhaWxlZBDE", + "UBIaChVSZWRpc0hhc2hlc1JlYWRGYWlsZWQQxVASGgoVUmVkaXNMaXN0c1dy", + "aXRlRmFpbGVkEMZQEhkKFFJlZGlzTGlzdHNSZWFkRmFpbGVkEMdQEhsKFlJl", + "ZGlzUmVxdWVzdEtleUlzRW1wdHkQyFASHQoYUmVkaXNMb2dpbkNhY2hlR2V0", + "RmFpbGVkEMlQEh0KGFJlZGlzTG9naW5DYWNoZVNldEZhaWxlZBDKUBIgChtS", + "ZWRpc1ByaXZhdGVDYWNoZUR1cGxpY2F0ZWQQy1ASJQogUmVkaXNHbG9iYWxT", + "aGFyZWRDYWNoZUR1cGxpY2F0ZWQQzFASKQokUmVkaXNMb2dpbkNhY2hlT3du", + "ZXJVc2VyR3VpZE5vdE1hdGNoEM1QEiMKHlJlZGlzR2xvYmFsUGFydHlDYWNo", + "ZUdldEZhaWxlZBDOUBIlCiBSZWRpc0dsb2JhbFBhcnR5Q2FjaGVXcml0ZUZh", + "aWxlZBDPUBIrCiZSZWRpc0dsb2JhbFBhcnR5TWVtYmVyQ2FjaGVXcml0ZUZh", + "aWxlZBDQUBIrCiZSZWRpc0dsb2JhbFBhcnR5U2VydmVyQ2FjaGVXcml0ZUZh", + "aWxlZBDRUBI0Ci9SZWRpc0dsb2JhbFBhcnR5SW52aXRlUGFydHlTZW5kQ2Fj", + "aGVXcml0ZUZhaWxlZBDSUBIoCiNSZWRpc0luc3RhbmNlUm9vbUluZm9DYWNo", + "ZUdldEZhaWxlZBDTUBIpCiRSZWRpc1VnY05wY1RvdGFsUmFua0NhY2hlV3Jp", + "dGVGYWlsZWQQ1FASIAobUmVkaXNSZXF1ZXN0SGFuZGxlck5vdEZvdW5kENVQ", + "EiAKG1JhYmJpdE1xQ29uc3VtZXJTdGFydEZhaWxlZBChURIaChVSYWJiaXRN", + "cUNvbm5lY3RGYWlsZWQQolESGQoUUmFiYml0TWVzc2FnZVRpbWVPbGQQo1ES", + "IAobUmFiYml0TXFDaGFubmVsQ3JlYXRlRmFpbGVkEKRREhkKFFMzQ2xpZW50", + "Q3JlYXRlRmFpbGVkEIVSEhkKFFMzQnVja2V0Q3JlYXRlRmFpbGVkEIZSEhcK", + "ElMzRmlsZVVwbG9hZEZhaWxlZBCHUhIXChJTM0ZpbGVEZWxldGVGYWlsZWQQ", + "iFISFAoPUzNGaWxlR2V0RmFpbGVkEIlSEhcKEk1ldGFEYXRhTG9hZEZhaWxl", + "ZBC3UhIUCg9JbnZhbGlkTWV0YURhdGEQuFISEgoNTWV0YUlkSW52YWxpZBC5", + "UhIUCg9Kc29uVHlwZUludmFsaWQQ81ISIQocSnNvbkNvbnZlcnREZXNlcmlh", + "bGl6ZUZhaWxlZBD0UhIdChhTZXJ2ZXJDb25maWdGaWxlTm90Rm91bmQQzVMS", + "FgoRU2VydmVyVHlwZUludmFsaWQQzlMSJwoiQWxyZWFkeVJ1bm5pbmdTZXJ2", + "ZXJXaXRoTGlzdGVuUG9ydBDPUxIZChROb3RGb3VuZENhY2hlU3RvcmFnZRDQ", + "UxIWChFGdW5jdGlvblBhcmFtTnVsbBDRUxIZChRGdW5jdGlvbkludmFsaWRQ", + "YXJhbRDSUxIcChdDbGllbnRMaXN0ZW5Qb3J0SW52YWxpZBDTUxIZChROb3RP", + "dmVycmlkZUludGVyZmFjZRDUUxIaChVTZXJ2ZXJPblJ1bm5pbmdGYWlsZWQQ", + "1VMSFwoSU2VydmljZVR5cGVJbnZhbGlkENZTEhsKFkZ1bmN0aW9uTm90SW1w", + "bGVtZW50ZWQQ11MSLgopQ2xhc3NEb2VzTm90SW1wbGVtZW50SW50ZXJmYWNl", + "SW5oZXJpdGFuY2UQ2FMSFwoSUnVsZVR5cGVEdXBsaWNhdGVkENlTEhgKE0Ns", + "YXNzVHlwZUNhc3RJc051bGwQ2lMSIgodUGVyaW9kaWNUYXNrQWxyZWFkeVJl", + "Z2lzdGVyZWQQ21MSIgodRW50aXR5VGlja2VyQWxyZWFkeVJlZ2lzdGVyZWQQ", + "3FMSGQoURW50aXR5VGlja2VyTm90Rm91bmQQ3VMSFwoSRW50aXR5QmFzZU5v", + "dEZvdW5kEN5TEhgKE1ZhbGlkU2VydmVyTm90Rm91bmQQ31MSIAobVGFyZ2V0", + "U2VydmVyVXNlckNvdW50RXhjZWVkEOBTEhcKElRhcmdldFVzZXJOb3RGb3Vu", + "ZBDhUxIXChJUYXJnZXRVc2VyTm90TG9nSW4Q4lMSEAoLTm90RXhpc3RNYXAQ", + "41MSIgodRmFpbGVkVG9SZXNlcnZlRW50ZXJDb25kaXRpb24Q5FMSHQoYRmFp", + "bGVkVG9SZXNlcnZhdGlvbkVudGVyEOVTEhsKFk93bmVyRW50aXR5VHlwZUlu", + "dmFsaWQQ5lMSHAoXT3duZXJFbnRpdHlDYW5ub3RGaWxsdXAQ51MSFQoQT3du", + "ZXJHdWlkSW52YWxpZBDoUxIhChxEYWlseVRpbWVFdmVudEFkZGl0aW9uRmFp", + "bGVkEOlTEiQKH1Byb2dyYW1WZXJzaW9uUGF0aFRva2VuTm90Rm91bmQQ6lMS", + "HQoYQ3VycmVudGx5UHJvY2Vzc2luZ1N0YXRlEOtTEhkKFFNlcnZlclVybFR5", + "cGVJbnZhbGlkEOxTEiMKHlNlcnZlclVybFR5cGVBbHJlYWR5UmVnaXN0ZXJl", + "ZBDtUxIcChdTZXJ2ZXJPZmZsaW5lTW9kZUVuYWJsZRDuUxIRCgxPd25lcklu", + "dmFsaWQQ71MSGwoWTW9kdWxlQWxyZWFkeVJlZ2lzdHJlZBDwUxIlCiBNb2R1", + "bGVBbHJlYWR5QWRkSW5pdGlhbGl6ZU1vZHVsZRDxUxITCg5Nb2R1bGVOb3RG", + "b3VuZBDyUxIdChhQcm9ncmFtVmVyc2lvbkxvYWRGYWlsZWQQ81MSGQoUU2Vy", + "dmVyQWxyZWFkeVJ1bm5pbmcQ9FMSJAofTWV0YURhdGFDb3B5VG9EeW5hbW9E", + "YkRvY0ZhaWxlZBDiVBIoCiNNZXRhRGF0YUNvcHlUb0VudGl0eUF0dHJpYnV0", + "ZUZhaWxlZBDjVBIhChxEeW5hbW9EYkRvY0NvcHlUb0NhY2hlRmFpbGVkEORU", + "EisKJkR5bmFtb0RiRG9jQ29weVRvRW50aXR5QXR0cmlidXRlRmFpbGVkEOVU", + "EiUKIENhY2hlQ29weVRvRW50aXR5QXR0cmlidXRlRmFpbGVkEOZUEiEKHENh", + "Y2hlQ29weVRvRHluYW1vRGJEb2NGYWlsZWQQ51QSJQogRW50aXR5QXR0cmli", + "dXRlQ29weVRvQ2FjaGVGYWlsZWQQ6FQSKwomRW50aXR5QXR0cmlidXRlQ29w", + "eVRvRHluYW1vRGJEb2NGYWlsZWQQ6VQSOQo0RW50aXR5QXR0cmlidXRlQ29w", + "eVRvRW50aXR5QXR0cmlidXRlVHJhbnNhY3RvckZhaWxlZBDqVBI5CjRFbnRp", + "dHlBdHRyaWJ1dGVUcmFuc2FjdG9yQ29weVRvRW50aXR5QXR0cmlidXRlRmFp", + "bGVkEOtUEjUKMEVudGl0eUF0dHJpYnV0ZVRyYW5zYWN0b3JDb3B5VG9EeW5h", + "bW9EYkRvY0ZhaWxlZBDsVBITCg5BdHRyaWJOb3RGb3VuZBDtVBIZChRBdHRy", + "aWJQYXRoTWFrZUZhaWxlZBDuVBIeChlFbnRpdHlBdHRyaWJ1dGVDYXN0RmFp", + "bGVkEO9UEh4KGVN0cmluZ0NvbnZlcnRUb0VudW1GYWlsZWQQ8FQSHgoZTWV0", + "YVNjaGVtYVZlcnNpb25Ob3RNYXRjaBCVVRIcChdNZXRhRGF0YVZlcnNpb25O", + "b3RNYXRjaBCWVRIaChVQYWNrZXRWZXJzaW9uTm90TWF0Y2gQl1USHwoaQ2xp", + "ZW50TG9naWNWZXJzaW9uTm90TWF0Y2gQmFUSHAoXUmVzb3VyY2VWZXJzaW9u", + "Tm90TWF0Y2gQmVUSHwoaQ2xpZW50UHJvZ3JhbVZlcnNpb25Jc051bGwQmlUS", + "EwoOVGVzdElkTm90QWxsb3cQ+VUSEQoMQm90ZE5vdEFsbG93EPpVEhkKFEFj", + "Y291bnRJZExlbmd0aFNob3J0EPtVEiQKH0FjY291bnRJZE5vdEZvdW5kSW5T", + "c29BY2NvdW50RGIQ/FUSIQocTWV0YURhdGFOb3RGb3VuZEJ5VGVzdFVzZXJJ", + "ZBD9VRIcChdBY2NvdW50UGFzc3dvcmROb3RNYXRjaBD+VRInCiJVc2VyRGF0", + "YUNvbnZlcnRUb0FjY291bnRBdHRyRmFpbGVkEP9VEiQKH0FjY291bnRCYXNl", + "QXR0cmliSW5zZXJ0RGJGYWlsZWQQgFYSGAoTTm9TZXJ2ZXJDb25uZWN0YWJs", + "ZRCBVhITCg5CbG9ja2VkQWNjb3VudBCCVhIsCidTc29BY2NvdW50QXV0aFdp", + "dGhMYXVuY2hlckxvZ2luTm90QWxsb3cQg1YSIgodQ2xpZW50U3RhbmRhbG9u", + "ZUxvZ2luTm90QWxsb3cQhFYSGQoUUGxhdGZvcm1UeXBlTm90QWxsb3cQhVYS", + "JgohQWNjb3VudENhbk5vdFJlYWRGcm9tU3NvQWNjb3VudERiEIZWEiEKHFNz", + "b0FjY291bnRBdXRoSnd0Q2hlY2tGYWlsZWQQh1YSKQokVXNlcklkS2V5Tm90", + "Rm91bmRJblNzb0FjY291bnRBdXRoSnd0EIhWEigKI1VzZXJJZFZhbHVlRW1w", + "dHlJblNzb0FjY291bnRBdXRoSnd0EIlWEi4KKUFjY291bnRUeXBlS2V5Tm90", + "Rm91bmRJblNzb0FjY291bnRBdXRoSnd0EIpWEjAKK0FjY291bnRUeXBlVmFs", + "dWVOb3RBbGxvd0luU3NvQWNjb3VudEF1dGhKd3QQi1YSKAojQWNjb3VudEJh", + "c2VEb2NOb3RGb3VuZEluTWV0YXZlcnNlRGIQjFYSLgopQWNjZXNzVG9rZW5L", + "ZXlOb3RBbGxvd0luU3NvQWNjb3VudEF1dGhKd3QQjVYSJgohQWNjZXNzVG9r", + "ZW5Ob3RNYXRjaEluU3NvQWNjb3VudERiEI5WEhgKE0FjY291bnRUeXBlTm90", + "QWxsb3cQj1YSGgoVQWNjb3VudEJhc2VEb2NOb3RMb2FkEJBWEhcKEk5vdEVu", + "b3VnaEF1dGhvcml0eRCuVhIZChRBY2NvdW50QmFzZURvY0lzTnVsbBCvVhIV", + "ChBBY2NvdW50SWRJbnZhbGlkELBWEhsKFkFjY291bnRXaXRob3V0VXNlckd1", + "aWQQsVYSIgodU3NvQWNjb3VudEF1dGhKd3RUb2tlbkV4cGlyZWQQslYSHwoa", + "U3NvQWNjb3VudEF1dGhKd3RFeGNlcHRpb24Qs1YSJAofVHJhbnNhY3Rpb25S", + "dW5uZXJBbHJlYWR5UnVubmluZxDBVxIeChlUcmFuc2FjdGlvblJ1bm5lck5v", + "dEZvdW5kEMJXEhYKEUVudGl0eUd1aWRJbnZhbGlkEKVYEhsKFkVudGl0eUF0", + "dHJpYkR1cGxpY2F0ZWQQplgSGgoVRW50aXR5QXR0cmlidXRlSXNOdWxsEKdY", + "EhwKF0VudGl0eUF0dHJpYnV0ZU5vdEZvdW5kEKhYEiAKG0VudGl0eUF0dHJp", + "YnV0ZVN0YXRlSW52YWxpZBCpWBIWChFFbnRpdHlUeXBlSW52YWxpZBCqWBIW", + "ChFFbnRpdHlMaW5rZWRUb01hcBCrWBIZChRFbnRpdHlOb3RMaW5rZWRUb01h", + "cBCsWBIaChVFbnRpdHlTdGF0ZU5vdERhbmNpbmcQrVgSGwoWRW50aXR5QWN0", + "aW9uRHVwbGljYXRlZBCJWRIZChRFbnRpdHlBY3Rpb25Ob3RGb3VuZBCKWRId", + "ChhFbnRpdHlCYXNlSGZzbUluaXRGYWlsZWQQ7VkSIAobUmVkaXNHbG9iYWxF", + "bnRpdHlEdXBsaWNhdGVkENFaEhoKFVVzZXJJc1N3aXRjaGluZ1NlcnZlchC1", + "WxIdChhVc2VySXNOb3RTd2l0Y2hpbmdTZXJ2ZXIQtlsSHwoaU2VydmVyU3dp", + "dGNoaW5nT3RwTm90TWF0Y2gQt1sSIwoeQ29ubmVjdGVkU2VydmVySXNOb3RE", + "ZXN0U2VydmVyELhbEhsKFkludmFsaWRSZXNlcnZhdGlvblVzZXIQuVsSFgoR", + "SW52YWxpZFJldHVyblVzZXIQulsSKQokVXNlck5pY2tuYW1lTm90QWxsb3dX", + "aXRoU3BlY2lhbENoYXJzEOFdEiwKJ1VzZXJOaWNrbmFtZUFsbG93ZWRNaW4y", + "VG9NYXg4V2l0aEtvcmVhbhDiXRIuCilVc2VyTmlja25hbWVBbGxvd2VkTWlu", + "NFRvTWF4MTZXaXRoRW5nbGlzaBDjXRItCihVc2VyTmlja25hbWVOb3RBbGxv", + "d2VkTnVtYmVyQXRGaXJzdENoYXJzEORdEh4KGVVzZXJOaWNrbmFtZU5vdEFs", + "bG93Q2hhcnMQ5V0SLQooVXNlck5pY2tuYW1lTm90QWxsb3dXaXRoSW5pdGlh", + "bGlzbUtvcmVhbhDmXRIUCg9Vc2VyTmlja25hbWVCYW4Q510SGAoTVXNlckR1", + "cGxpY2F0ZWRMb2dpbhDoXRIRCgxVc2VyTm90TG9naW4Q6V0SKQokVXNlckNy", + "ZWF0aW9uRm9yRHluYW1vRGJEb2NEdXBsaWNhdGVkEOpdEikKJFVzZXJHdWlk", + "QXBwbHlUb1JlZkF0dHJpYnV0ZUFsbEZhaWxlZBDrXRImCiFUZXN0VXNlclBy", + "ZXBhcmVDcmVhdGVOb3RDb21wbGV0ZWQQ7F0SKQokRGVmYXVsdFVzZXJQcmVw", + "YXJlQ3JlYXRlTm90Q29tcGxldGVkEO1dEiAKG1VzZXJQcmVwYXJlTG9hZE5v", + "dENvbXBsZXRlZBDuXRIbChZVc2VyTmlja25hbWVOb3RDcmVhdGVkEO9dEh8K", + "GlVzZXJOaWNrbmFtZUFscmVhZHlDcmVhdGVkEPBdEh8KGlVzZXJDcmVhdGVT", + "dGVwTm90Q29tcGxldGVkEPFdEhgKE1VzZXJDcmVhdGVDb21wbGV0ZWQQ8l0S", + "GwoWVXNlclN1YktleUJpbmRUb0ZhaWxlZBDzXRIcChdVc2VyU3ViS2V5UmVw", + "bGFjZUZhaWxlZBD0XRIUCg9Vc2VyR3VpZEludmFsaWQQ9V0SGgoVVXNlck5p", + "Y2tuYW1lRG9jSXNOdWxsEPZdEhIKDVVzZXJEb2NJc051bGwQ910SGQoUVXNl", + "ckd1aWRBbHJlYWR5QWRkZWQQ+F0SGwoWVXNlck5pY2tuYW1lRHVwbGljYXRl", + "ZBD5XRIjCh5Vc2VyTmlja25hbWVBbGxvd2VkTWluMlRvTWF4MTIQ+l0SIAob", + "VXNlck5pY2tuYW1lU2VhcmNoUGFnZVdyb25nEPtdEhYKEVVzZXJOaWNrbmFt", + "ZUVtcHR5EPxdEiEKHFVzZXJDb250ZW50c1NldHRpbmdEb2NJc051bGwQ/V0S", + "FgoRVXNlck1vbmV5RG9jRW1wdHkQ/l0SIQocVXNlclJlcG9ydEludmFsaWRU", + "aXRsZUxlbmd0aBDFXhIjCh5Vc2VyUmVwb3J0SW52YWxpZENvbnRlbnRMZW5n", + "dGgQxl4SGwoWVXNlclJlcG9ydERvY0V4Y2VwdGlvbhDHXhIrCiZUZXN0Q2hh", + "cmFjdGVyUHJlcGFyZUNyZWF0ZU5vdENvbXBsZXRlZBDJZRIuCilEZWZhdWx0", + "Q2hhcmFjdGVyUHJlcGFyZUNyZWF0ZU5vdENvbXBsZXRlZBDUZRIlCiBDaGFy", + "YWN0ZXJQcmVwYXJlTG9hZE5vdENvbXBsZXRlZBDVZRIsCidDaGFyYWN0ZXJC", + "YXNlRG9jTG9hZER1cGxpY2F0ZWRDaGFyYWN0ZXIQ1mUSGQoUQ2hhcmFjdGVy", + "Tm90U2VsZWN0ZWQQ12USFgoRQ2hhcmFjdGVyTm90Rm91bmQQ2GUSGwoWQ2hh", + "cmFjdGVyQmFzZURvY05vUmVhZBDZZRIlCiBDaGFyYWN0ZXJDdXN0b21pemlu", + "Z05vdENvbXBsZXRlZBDaZRIkCh9DaGFyYWN0ZXJDcmVhdGVTdGVwTm90Q29t", + "cGxldGVkENtlEikKJENoYXJhY3RlckN1c3RvbWl6aW5nQWxyZWFkeUNvbXBs", + "ZXRlZBDcZRIfChpDaGFyYWN0ZXJQcmVwYXJlTm90Q3JlYXRlZBDdZRIdChhD", + "aGFyYWN0ZXJDcmVhdGVDb21wbGV0ZWQQ3mUSGwoWQ2hhcmFjdGVyQmFzZURv", + "Y0lzTnVsbBDfZRIVChBBYmlsaXR5Tm90RW5vdWdoEPVnEhkKFEl0ZW1NZXRh", + "RGF0YU5vdEZvdW5kELFtEhQKD0l0ZW1HdWlkSW52YWxpZBCybRISCg1JdGVt", + "RG9jSXNOdWxsELNtEicKIkl0ZW1EZWZhdWx0QXR0cmlidXRlTm90Rm91bmRJ", + "bk1ldGEQtG0SIwoeSXRlbUxldmVsRW5jaGFudE5vdEZvdW5kSW5NZXRhELVt", + "Eh4KGUl0ZW1FbmNoYW50Tm90Rm91bmRJbk1ldGEQtm0SKwomSXRlbUF0dHJp", + "YnV0ZVJhbmRvbUdyb3VwTm90Rm91bmRJbk1ldGEQt20SLwoqSXRlbUF0dHJp", + "YnV0ZVJhbmRvbUdyb3VwVG90YWxXZWlnaHRJbnZhbGlkELhtEhEKDEl0ZW1O", + "b3RGb3VuZBC5bRIeChlJdGVtQ2xvdGhJbnZhbGlkTGFyZ2VUeXBlELptEh4K", + "GUl0ZW1DbG90aEludmFsaWRTbWFsbFR5cGUQu20SGgoVSXRlbVN0YWNrQ291", + "bnRJbnZhbGlkELxtEhcKEkl0ZW1NYXhDb3VudEV4Y2VlZBC9bRIeChlJdGVt", + "RG9jTG9hZER1cGxpY2F0ZWRJdGVtEL5tEhkKFENsb3RoU2xvdFR5cGVJbnZh", + "bGlkEL9tEhwKF0l0ZW1TdGFja0NvdW50Tm90RW5vdWdoEMBtEhcKEkl0ZW1D", + "b3VudE5vdEVub3VnaBDBbRIYChNJdGVtSW52YWxpZEl0ZW1UeXBlEMJtEh0K", + "GEl0ZW1Ub29sTWV0YURhdGFOb3RGb3VuZBDDbRIVChBJdGVtVG9vbE5vdEZv", + "dW5kEMRtEh0KGEl0ZW1Ub29sTm90QWN0aXZhdGVTdGF0ZRDFbRIYChNUb29s", + "QWN0aW9uRG9jSXNOdWxsEMZtEiUKIFRvb2xBY3Rpb25BbHJlYWR5VW5hY3Rp", + "dmF0ZVN0YXRlEMdtEiMKHlRvb2xBY3Rpb25BbHJlYWR5QWN0aXZhdGVTdGF0", + "ZRDIbRIXChJJdGVtVGF0dG9vTm90Rm91bmQQyW0SJQogSXRlbUF0dHJpYnV0", + "ZUVuY2hhbnRNZXRhTm90Rm91bmQQym0SIwoeSXRlbUF0dHJpYnV0ZUNoYW5n", + "ZU5vdFNlbGVjdGVkEMttEiQKH0l0ZW1QYXJzaW5nRnJvbVN0cmluZ1RvSW50", + "RXJvcnIQzG0SKQokSXRlbVZhbHVlUGFyc2luZ0Zyb21TdHJpbmdUb0ludEVy", + "b3JyEM1tEiYKIUl0ZW1GaXJzdFB1cmNoYXNlSGlzdG9yeURvY0lzTnVsbBDO", + "bRIyCi1JdGVtRmlyc3RQdXJjaGFzZUhpc3RvcnlEb2NMb2FkRHVwbGljYXRl", + "ZEl0ZW0Qz20SKQokSXRlbUZpcnN0UHVyY2hhc2VIaXN0b3J5QWxyZWFkeUV4", + "aXN0ENBtEiwKJ0l0ZW1GaXJzdFB1cmNoYXNlRGlzY291bnRJdGVtQ291bnRX", + "cm9uZxDRbRIfChpJdGVtQXR0cmlidXRlSWRUeXBlSW52YWxpZBDSbRIUCg9J", + "dGVtQWxsb2NGYWlsZWQQ020SFwoSSXRlbUd1aWREdXBsaWNhdGVkENRtEhgK", + "E0l0ZW1MZXZlbEN1cnJlbnRNYXgQ1W0SFwoSSXRlbUNhbk5vdEJlU3RvcmVk", + "ENZtEhwKF0l0ZW1Vc2VGdW5jdGlvbk5vdEZvdW5kEJVuEh0KGEl0ZW1Vc2VR", + "dWVzdE1haWxDb3VudE1heBCWbhIjCh5JdGVtVXNlTm90RXhpc3RBc3NpZ25h", + "YmxlUXVlc3QQl24SGwoWSXRlbVVzZUFscmVhZHlIYXNRdWVzdBCYbhIfChpJ", + "dGVtVXNlQWxyZWFkeUhhc1F1ZXN0TWFpbBCZbhIjCh5CYWdSdWxlSXRlbUxh", + "cmdlVHlwZUR1cGxpY2F0ZWQQmXUSKQokVG9vbEVxdWlwUnVsZUl0ZW1MYXJn", + "ZVR5cGVEdXBsaWNhdGVkEJp1EioKJUNsb3RoRXF1aXBSdWxlSXRlbUxhcmdl", + "VHlwZUR1cGxpY2F0ZWQQm3USKwomVGF0dG9vRXF1aXBSdWxlSXRlbUxhcmdl", + "VHlwZUR1cGxpY2F0ZWQQnHUSGgoVSW52ZW50b3J5UnVsZU5vdEZvdW5kEJ11", + "EhcKEkVxdWlwSW52ZW5Ob3RGb3VuZBCedRIYChNTbG90c0FscmVhZHlFcXVp", + "cGVkEJ91EhoKFVNsb3RzQWxyZWFkeVVuZXF1aXBlZBCgdRISCg1CYWdJc0l0", + "ZW1GdWxsEKF1EhMKDkJhZ0lzSXRlbUVtcHR5EKJ1EhoKFUJhZ0RlbHRhSXRl", + "bUR1cGxpYXRlZBCjdRIUCg9CYWdJdGVtTm90Rm91bmQQpHUSKAojQ2xvdGhF", + "cXVpcFJ1bGVDbG90aFNsb3RUeXBlTm90Rm91bmQQpXUSJgohVG9vbEVxdWlw", + "UnVsZVRvb2xTbG90VHlwZU5vdEZvdW5kEKZ1EioKJVRhdHRvb0VxdWlwUnVs", + "ZVRhdHRvb1Nsb3RUeXBlTm90Rm91bmQQp3USGAoTQmFnVGFiVHlwZUFkZEZh", + "aWxlZBCodRIWChFCYWdUYWJUeXBlSW52YWxpZBCpdRIXChJCYWdUYWJUeXBl", + "Tm90Rm91bmQQqnUSGwoWQmFnVGFiQ291bnRNZXJnZUZhaWxlZBCrdRIfChpJ", + "bnZlbnRvcnlFbnRpdHlUeXBlSW52YWxpZBCsdRIaChVJbnZlbkVxdWlwVHlw", + "ZUludmFsaWQQrXUSGgoVQmFnSXNSZXNlcnZlZEl0ZW1GdWxsEK51EhsKFkJh", + "Z0lzUmVzZXJ2ZWRJdGVtRW1wdHkQr3USFgoRRXF1aXBTbG90Tm90TWF0Y2gQ", + "sHUSGAoTRXF1aXBTbG90T3V0T2ZSYW5nZRCxdRIeChlTbG90c0FscmVhZHlS", + "ZXNlcnZlZEVxdWlwELJ1EiAKG1Nsb3RzQWxyZWFkeVJlc2VydmVkVW5lcXVp", + "cBCzdRIVChBTbG90VHlwZU5vdEZvdW5kELR1Eh8KGlVnY05wY01ldGFHdWlk", + "QWxyZWFkeUFkZGVkEP11EhQKD1VnY05wY0RvY0lzTnVsbBD+dRIZChRVZ2NO", + "cGNNYXhDb3VudEV4Y2VlZBD/dRIdChhVZ2NOcGNDbG90aEl0ZW1Ob3RFbm91", + "Z2gQgHYSIgodVWdjTnBjRG9jTG9hZER1cGxpY2F0ZWRVZ2NOcGMQgXYSIgod", + "VWdjTnBjRGVzY3JpcHRpb25MZW5ndGhFeGNlZWQQgnYSIwoeVWdjTnBjV29y", + "ZFNjZW5hcmlvTGVuZ3RoRXhjZWVkEIN2Eh8KGlVnY05wY0dyZWV0aW5nTGVu", + "Z3RoRXhjZWVkEIR2Eh4KGVVnY05wY1RhdHRvb0l0ZW1Ob3RFbm91Z2gQhXYS", + "JwoiVWdjTnBjSGFiaXRTb2NpYWxBY3Rpb25Db3VudEV4Y2VlZBCGdhIqCiVV", + "Z2NOcGNEaWFsb2d1ZVNvY2lhbEFjdGlvbkNvdW50RXhjZWVkEId2Eh0KGFVn", + "Y05wY05pY2tuYW1lRHVwbGljYXRlZBCIdhITCg5VZ2NOcGNOb3RGb3VuZBCJ", + "dhIjCh5VZ2NOcGNJbnRyb2R1Y3Rpb25MZW5ndGhFeGNlZWQQinYSFwoSVWdj", + "TnBjTWF4VGFnRXhjZWVkEIt2EhgKE1VnY05wY05pY2tuYW1lRW1wdHkQjHYS", + "JgohVWdjTnBjQWxyZWFkeVJlZ2lzdGVyZWRJbkdhbWVab25lEI12EiIKHVVn", + "Y05wY05vdFJlZ2lzdGVyZWRJbkdhbWVab25lEI52EiQKH1VnY05wY0xpa2VT", + "ZWxlY3RlZUNvdW50Tm90Rm91bmQQj3YSIwoeVWdjTnBjTGlrZVNlbGVjdGVk", + "RmxhZ05vdEZvdW5kEJB2Eh8KGlVnY05wY0R1cGxpY2F0ZUluTXlob21lVWdj", + "EJF2EhcKElVnY05wY0xvY2F0ZWRTdGF0ZRCSdhIbChZVZ2NOcGNNZXRhRGF0", + "YU5vdEZvdW5kEJN2EiMKHkJlYWNvbkFwcFByb2ZpbGVVcGxvYWRDb29sVGlt", + "ZRCUdhIaChVCZWFjb25Cb2R5SXRlbUludmFsaWQQlXYSHAoXVWdjTnBjSGFz", + "QmVhY29uU2hvcEl0ZW0QlnYSHwoaVWdjTnBjUmFua0VudGl0eUlzTm90Rm91", + "bmQQ4XYSGQoUVWdjTnBjUmFua091dE9mUmFuZ2UQ4nYSIwoeRmFybWluZ0Vm", + "ZmVjdERvY0xpbmtQa1NrTm90U2V0EMV3Ei0KKEZhcm1pbmdFZmZlY3RBbHJl", + "YWR5UmVnaXN0ZXJlZEluR2FtZVpvbmUQxncSEwoORmFybWluZ0FscmVhZHkQ", + "x3cSEAoLRmFybWluZ0J5TWUQyHcSIAobRmFybWluZ1Byb3BNZXRhRGF0YU5v", + "dEZvdW5kEMl3EhsKFkZhcm1pbmdUcnlDb3VudEludmFsaWQQyncSFAoPRmFy", + "bWluZ05vdFN0YXRlEMt3EhkKFEZhcm1pbmdPd25lck5vdE1hdGNoEMx3EiUK", + "IEZhcm1pbmdTdW1tb25lZEVudGl0eVR5cGVJbnZhbGlkEM13EiQKH0Zhcm1p", + "bmdFZmZlY3ROb3RFeGlzdEluR2FtZVpvbmUQzncSEAoLRmFyaW1nU3RhdGUQ", + "z3cSGwoWRmFybWluZ1N0YW5kQnlOb3RTdGF0ZRDQdxIaChVGYXJtaW5nQW5j", + "aG9yTm90Rm91bmQQ0XcSJgohRmFybWluZ0JlYWNvbkRiSW5mb0ludGVncml0", + "eUVycm9yENJ3EhoKFUdhY2hhTWV0YURhdGFOb3RGb3VuZBCpeBIVChBHYWNo", + "YVJld2FyZEVtcHR5EKp4EhMKDk1hc3Rlck5vdEZvdW5kELl7EhUKEE1hc3Rl", + "ck5vdFJlbGF0ZWQQunsSFAoPR2FtZVpvbmVOb3RKb2luEJ18Eg4KCU1hcElz", + "TnVsbBCefBIcChdMb2NhdGlvblVuaXF1ZUlkSW52YWxpZBCffBITCg5Qcm9w", + "SXNPY2N1cGllZBCgfBIWChFQcm9wSXNOb3RPY2N1cGllZBChfBIUCg9Gcmll", + "bmREb2NJc051bGwQgX0SGgoVRnJpZW5kRm9sZGVyRG9jSXNOdWxsEIJ9EiIK", + "HFNvY2lhbEFjdGlvbk1ldGFEYXRhTm90Rm91bmQQ6YQBEhoKFFNvY2lhbEFj", + "dGlvbk5vdEZvdW5kEOqEARIvCilTb2NpYWxBY3Rpb25Eb2NMb2FkRHVwbGlj", + "YXRlZFNvY2lhbEFjdGlvbhDrhAESHgoYU29jaWFsQWN0aW9uQWxyZWFkeUV4", + "aXN0EOyEARIgChpTb2NpYWxBY3Rpb25TbG90T3V0T2ZSYW5nZRDthAESGwoV", + "U29jaWFsQWN0aW9uTm90T25TbG90EO6EARIbChVTb2NpYWxBY3Rpb25Eb2NJ", + "c051bGwQ74QBEhwKFkNoYW5uZWxNb3ZlU2FtZUNoYW5uZWwQ0YwBEiAKGkNo", + "YW5uZWxJbnZhbGlkTW92ZUNvb2xUaW1lENKMARIWChBOb3RDaGFubmVsU2Vy", + "dmVyENOMARIYChJPd25lZFJvb21Eb2NJc051bGwQuZQBEhMKDVJvb21Eb2NJ", + "c051bGwQupQBEhUKD1Jvb21Jc05vdE15SG9tZRC7lAESGgoUTWFwUmFuZ2VP", + "dXRPZkNlbGxQb3MQvJQBEhwKFk1hcEdyaWRCb3VuZE91dE9mUmFuZ2UQvZQB", + "EhkKE01hcEdyaWROb3RGb3VuZEdyaWQQvpQBEhkKE01hcEdyaWROb3RGb3Vu", + "ZENlbGwQv5QBEh8KGU1hcEdyaWRDZWxsTm90Rm91bmRQbGF5ZXIQwJQBEh8K", + "GU1hcEdyaWRDZWxsTm90Rm91bmRVZ2NOcGMQwZQBEhIKDE1haWxOb3RGb3Vu", + "ZBChnAESFgoQTWFpbEFscmVhZHlUYWtlbhCinAESGQoTTWFpbEludmFsaWRN", + "YWlsVHlwZRCjnAESHAoWTWFpbE1heFNlbmRDb3VudEV4Y2VlZBCknAESEwoN", + "TWFpbERvY0lzTnVsbBClnAESHQoXTWFpbEJsb2NrVXNlckNhbm5vdFNlbmQQ", + "ppwBEiYKIE1haWxNYXhUYXJnZXRSZWNlaXZlZENvdW50RXhjZWVkEKecARIW", + "ChBNYWlsQ2FudFNlbmRTZWxmEKicARIgChpNYWlsQ2FudERlbGV0ZUlmSXRl", + "bUV4aXN0cxCpnAESFgoQTWFpbERvY0V4Y2VwdGlvbhCqnAESGgoUTWFpbFBy", + "b2ZpbGVEb2NJc051bGwQzZ4BEh0KF01haWxQcm9maWxlRG9jRXhjZXB0aW9u", + "EM6eARIZChNTeXN0ZW1NYWlsRG9jSXNOdWxsEJWgARIYChJQYXJ0eUNhbm5v", + "dFNldEd1aWQQiaQBEiAKGlBhcnR5RmFpbGVkTWFrZVBhcnR5TWVtYmVyEIqk", + "ARIRCgtQYXJ0eUlzRnVsbBCLpAESGAoSQWxyZWFkeUludml0ZVBhcnR5EIyk", + "ARIZChNOb3RGb3VuZFBhcnR5SW52aXRlEI2kARITCg1Ob3RGb3VuZFBhcnR5", + "EI6kARIOCghOb3RQYXJ0eRCPpAESFAoOTm90UGFydHlMZWFkZXIQkKQBEhIK", + "DEpvaW5pbmdQYXJ0eRCRpAESFAoOTm90UGFydHlNZW1iZXIQkqQBEhMKDUFs", + "cmVhZHlTdW1tb24Qk6QBEh0KF1BhcnR5TGVhZGVyU2VydmVySXNGdWxsEJSk", + "ARIbChVJbnZpdGVNZW1iZXJJc0NvbmNlcnQQlaQBEhwKFkZhaWxUb1NlbmRJ", + "bnZpdGVNZW1iZXIQlqQBEhgKEkFscmVhZHlQYXJ0eU1lbWJlchCXpAESHQoX", + "SW52YWxpZFN1bW1vblNlcnZlclR5cGUQmKQBEh0KF1N1bW1vblVzZXJMaW1p", + "dERpc3RhbmNlEJmkARIZChNJbnZhbGlkU3VtbW9uTWVtYmVyEJqkARIaChRQ", + "YXJ0eUxlYWRlckxvZ2dlZE91dBCbpAESGwoVQWxyZWFkeVN0YXJ0UGFydHlW", + "b3RlEJykARIWChBOb1N0YXJ0UGFydHlWb3RlEJ2kARIeChhBbHJlYWR5UGFz", + "c1BhcnR5Vm90ZVRpbWUQnqQBEhoKFEludmFsaWRQYXJ0eVZvdGVUaW1lEJ+k", + "ARIbChVBbHJlYWR5UmVwbHlQYXJ0eVZvdGUQoKQBEhoKFEVtcHR5UGFydHlJ", + "bnN0YW5jZUlkEKGkARIYChJJbnZhbGlkSW52aXRlUGxhY2UQoqQBEh0KF0lu", + "dml0ZVBhcnR5SW52YWxpZFVzZXJzEKOkARIbChVTdW1tb25QYXJ0eU1lbWJl", + "ckZhaWwQpKQBEh4KGEludmFsaWRQYXJ0eVN0cmluZ0xlbmd0aBClpAESIQob", + "SW5jbHVkZUJhbldvcmRGcm9tUGFydHlOYW1lEKakARIiChxKb2luaW5nUGFy", + "dHlNZW1iZXJJbmZvSXNOdWxsEKekARIeChhJbnZhbGlkU3VtbW9uV29ybGRT", + "ZXJ2ZXIQqKQBEhsKFU5vdEV4aXN0UGFydHlJbnN0YW5jZRDvqwESGgoUQnVm", + "Zk1ldGFEYXRhTm90Rm91bmQQ8asBEh0KF0J1ZmZOb3RSZWdpc3RyeUNhdGVn", + "b3J5EPKrARISCgxCdWZmTm90Rm91bmQQ86sBEiEKG0J1ZmZJbnZhbGlkQnVm", + "ZkNhdGVnb3J5VHlwZRD0qwESIQobQnVmZkNhY2hlTG9hZER1cGxpY2F0ZWRC", + "dWZmEPWrARIeChhCdWZmSW52YWxpZEF0dHJpYnV0ZVR5cGUQ9qsBEh0KF1F1", + "ZXN0QXNzaW5nRGF0YU5vdEV4aXN0ENizARIXChFRdWVzdE1haWxOb3RFeGlz", + "dBDZswESFwoRUXVlc3RBbHJlYWR5RW5kZWQQ2rMBEhMKDVF1ZXN0Q291bnRN", + "YXgQ27MBEh0KF1F1ZXN0VHlwZUFzc2lnbkNvdW50TWF4ENyzARIWChBRdWVz", + "dEludmFsaWRUeXBlEN2zARIXChFRdWVzdEludmFsaWRWYWx1ZRDeswESFQoP", + "UXVlc3RJZE5vdEZvdW5kEN+zARIZChNRdWVzdEludmFsaWRUYXNrTnVtEOCz", + "ARIbChVRdWVzdFJlZnVzZU9ubHlOb3JtYWwQ4bMBEh4KGFF1ZXN0QWJhZG9u", + "Tm90RXhpc3RRdWVzdBDiswESGgoUUXVlc3RBbHJlYWR5Q29tcGxldGUQ47MB", + "EhYKEFF1ZXN0Tm90Q29tcGxldGUQ5LMBEhwKFlF1ZXN0QWJhbmRvbk9ubHlO", + "b3JtYWwQ5bMBEhgKElF1ZXN0TWFpbERvY0lzTnVsbBDmswESFAoOUXVlc3RE", + "b2NJc051bGwQ57MBEhcKEUVuZFF1ZXN0RG9jSXNOdWxsEOizARIfChlRdWVz", + "dE1ldGFCYXNlTm90SW1wbGVtZW50EOmzARIgChpRdWVzdE5vdGlmeVJlZGlz", + "UmVnaXN0RmFpbBDqswESFwoRUXVlc3RBbHJlYWR5RXhpc3QQ67MBEhsKFVdv", + "cmxkTWV0YURhdGFOb3RGb3VuZBDBuwESGgoUTGFja09mV29ybGRFbnRlckl0", + "ZW0QwrsBEh4KGFdvcmxkTWFwVHJlZURhdGFOb3RGb3VuZBDDuwESIwodV29y", + "bGRNYXBUcmVlQ2hpbGRMYW5kTm90Rm91bmQQxLsBEhkKE1Jvb21NYXBEYXRh", + "Tm90Rm91bmQQxbsBEhoKFExhbmRNZXRhRGF0YU5vdEZvdW5kELW/ARISCgxM", + "YW5kTm90Rm91bmQQtr8BEh8KGUxhbmREb2NMb2FkRHVwbGljYXRlZExhbmQQ", + "t78BEhMKDUxhbmREb2NJc051bGwQuL8BEhcKEU93bmVkTGFuZE5vdEZvdW5k", + "ELm/ARIpCiNPd25lZExhbmREb2NMb2FkRHVwbGljYXRlZE93bmVkTGFuZBC6", + "vwESGAoST3duZWRMYW5kRG9jSXNOdWxsELu/ARIdChdMYW5kTWFwVHJlZURh", + "dGFOb3RGb3VuZBC8vwESJgogTGFuZE1hcFRyZWVDaGlsZEJ1aWxkaW5nTm90", + "Rm91bmQQvb8BEhwKFkxhbmRCdWlsZGluZ0lzTm90RW1wdHkQvr8BEhkKE0xh", + "bmRNYXBEYXRhTm90Rm91bmQQv78BEhQKDkxhbmRFeGlzdE93bmVyEMC/ARIZ", + "ChNMYW5kRWRpdG9ySXNOb3RVc2VyEMG/ARIZChNMYW5kT3duZXJJc05vdE1h", + "dGNoEMK/ARIeChhCdWlsZGluZ01ldGFEYXRhTm90Rm91bmQQqcMBEhYKEEJ1", + "aWxkaW5nTm90Rm91bmQQqsMBEicKIUJ1aWxkaW5nRG9jTG9hZER1cGxpY2F0", + "ZWRCdWlsZGluZxCrwwESFwoRQnVpbGRpbmdEb2NJc051bGwQrMMBEhsKFU93", + "bmVkQnVpbGRpbmdOb3RGb3VuZBCtwwESMQorT3duZWRCdWlsZGluZ0RvY0xv", + "YWREdXBsaWNhdGVkT3duZWRCdWlsZGluZxCuwwESHAoWT3duZWRCdWlsZGlu", + "Z0RvY0lzTnVsbBCvwwESIQobQnVpbGRpbmdNYXBUcmVlRGF0YU5vdEZvdW5k", + "ELDDARImCiBCdWlsZGluZ01hcFRyZWVDaGlsZFJvb21Ob3RGb3VuZBCxwwES", + "HQoXQnVpbGRpbmdGbG9vcklzTm90RW1wdHkQssMBEh0KF0J1aWxkaW5nTWFw", + "RGF0YU5vdEZvdW5kELPDARIYChJCdWlsZGluZ0V4aXN0T3duZXIQtMMBEicK", + "IUJ1aWxkaW5nTWFwVHJlZVBhcmVudExhbmROb3RGb3VuZBChoAESHQoXQnVp", + "bGRpbmdPd25lcklzTm90TWF0Y2gQtsMBEhwKFk15SG9tZU1ldGFEYXRhTm90", + "Rm91bmQQnccBEhQKDk15SG9tZU5vdEZvdW5kEJ7HARIjCh1NeUhvbWVEb2NM", + "b2FkRHVwbGljYXRlZE15SG9tZRCfxwESGAoSTXlIb21lQWxyZWFkeUV4aXN0", + "EKDHARIVCg9NeUhvbWVEb2NJc051bGwQoccBEhUKD015SG9tZUlzTm90TWlu", + "ZRCixwESJAoeTXlIb21lQ2FudEV4Y2hhbmdlV2hlbkNyYWZ0aW5nEKPHARIi", + "ChxFZGl0YWJsZVJvb21NZXRhRGF0YU5vdEZvdW5kEKTHARInCiFFZGl0YWJs", + "ZUZyYW1ld29ya01ldGFEYXRhTm90Rm91bmQQpccBEhkKE0ludGVyaW9yUG9p", + "bnRFeGNlZWQQpscBEhkKE015aG9tZU5vdEVub3VnaFNsb3QQp8cBEhYKEE15", + "aG9tZUlzU2VsZWN0ZWQQqMcBEhsKFU15aG9tZU5hbWVMZW5ndGhTaG9ydBCp", + "xwESGgoUTXlob21lTmFtZUxlbmd0aExvbmcQqscBEhoKFE15aG9tZU5hbWVE", + "dXBsaWNhdGVkEKvHARIeChhNeWhvbWVJbnRlcnBob25lTm90RXhpc3QQrMcB", + "Eh4KGE15aG9tZVN0YXJ0UG9pbnROb3RFeGlzdBCtxwESHAoWTXlob21lSW50", + "ZXJwaG9uZUV4Y2VlZBCuxwESHAoWTXlob21lU3RhcnRQb2ludEV4Y2VlZBCv", + "xwESGwoVQ3JhZnRpbmdDbG90aGVzRXhjZWVkELDHARIbChVDcmFmdGluZ0Nv", + "b2tpbmdFeGNlZWQQsccBEh0KF0NyYWZ0aW5nRnVybml0dXJlRXhjZWVkELLH", + "ARIZChNBbmNob3JJc05vdEluTXlob21lELPHARImCiBEb05vdFJlbW92ZVBy", + "b2Nlc3NDcmFmdGluZ0FuY2hvchC0xwESGQoTQW5jaG9yR3VpZER1cGxpY2F0", + "ZRC1xwESFgoQTXlob21lSXNFZGl0dGluZxC2xwESFgoQTXlob21lSXNPblJl", + "bnRhbBC3xwESHwoZTXlob21lVWdjSW5mb0ZpbGVOb3RGb3VuZRC4xwESIQob", + "RWRpdGFibGVSb29tU2l6ZVR5cGVJbnZhbGlkELnHARIYChJDcmFmdGVyQ291", + "bnRFeGNlZWQQuscBEhsKFU1pbmltYXBNYXJrZXJOb3RGb3VuZBCRywESMQor", + "TWluaW1hcE1hcmtlckRvY0xvYWREdXBsaWNhdGVkTWluaW1hcE1hcmtlchCS", + "ywESHAoWTWluaW1hcE1hcmtlckRvY0lzTnVsbBCTywESGgoUQ2FydE1ldGFE", + "YXRhTm90Rm91bmQQhc8BEhgKEkNhcnRNYXhDb3VudEV4Y2VlZBCGzwESGwoV", + "Q2FydFN0YWNrQ291bnRJbnZhbGlkEIfPARIdChdDYXJ0U3RhY2tDb3VudE5v", + "dEVub3VnaBCIzwESFgoQQ2FydEl0ZW1Ob3RGb3VuZBCJzwESIQobQ2FydE5v", + "dFJlZ2lzdHJ5Q3VycmVuY3lUeXBlEIrPARIdChdDYXJ0SW52YWxpZEN1cnJl", + "bmN5VHlwZRCLzwESEwoNQ2FydERvY0lzTnVsbBCMzwESFgoQQ2FydERvY0V4", + "Y2VwdGlvbhCNzwESGAoSQ2hhdFNlbmRTZWxmRmFpbGVkEPnSARIZChNDaGF0", + "SW52YWxpZENoYXRUeXBlEPrSARIgChpDaGF0QmxvY2tVc2VyQ2Fubm90V2hp", + "c3BlchD70gESHgoYQ2hhdEludmFsaWRNZXNzYWdlTGVuZ3RoEPzSARIYChJD", + "aGF0SW5jbHVkZUJhbldvcmQQ/dIBEhkKE05vdGljZUNoYXREb2NJc051bGwQ", + "7dYBEiQKHkVzY2FwZVBvc2l0aW9uTm90QXZhaWxhYmxlVGltZRDh2gESHQoX", + "RXNjYXBlUG9zaXRpb25Eb2NJc051bGwQ4toBEhgKEkJsb2NrVXNlckRvY0lz", + "TnVsbBDV3gESHwoZQ2hhcmFjdGVyUHJvZmlsZURvY0lzTnVsbBDJ4gESIAoa", + "Q3VzdG9tRGVmaW5lZERhdGFEb2NJc051bGwQ9eQBEh4KGEN1c3RvbURlZmlu", + "ZWRVaURvY0lzTnVsbBC95gESGQoTR2FtZU9wdGlvbkRvY0lzTnVsbBCx6gES", + "HAoWR2FtZU9wdGlvbkRvY0V4Y2VwdGlvbhCy6gESFAoOTGV2ZWxEb2NJc051", + "bGwQpe4BEhcKEUxvY2F0aW9uRG9jSXNOdWxsEJnyARIUCg5Ob3RVc2FibGVQ", + "bGFjZRCa8gESIQobUmVkaXNMb2NhdGlvbkNhY2hlU2V0RmFpbGVkEJvyARIU", + "Cg5Nb25leURvY0lzTnVsbBCB+gESHwoZTW9uZXlDb250cm9sTm90SW5pdGlh", + "bGl6ZRCC+gESFAoOTW9uZXlOb3RFbm91Z2gQg/oBEhsKFU1vbmV5TWF4Q291", + "bnRFeGNlZWRlZBCE+gESHgoYQ3VycmVuY3lNZXRhRGF0YU5vdEZvdW5kEIX6", + "ARImCiBTaG9wUHJvZHVjdFRyYWRpbmdNZXRlckRvY0lzTnVsbBDpgQISFgoQ", + "U2hvcElzTXlIb21lSXRlbRDqgQISGAoSSW52YWxpZFNob3BCdXlUeXBlEOuB", + "AhIdChdTaG9wUHJvZHVjdENhbm5vdFJlbndhbBDsgQISHgoYU2hvcFByb2R1", + "Y3ROb3RSZW53YWxUaW1lEO2BAhInCiFTaG9wUHJvZHVjdFJlbmV3YWxDb3Vu", + "dEFscmVhZHlNYXgQ7oECEhgKElNob3BJdGVtQ2Fubm90U2VsbBDvgQISGAoS", + "UmV3YXJkSW5mb05vdEV4aXN0ENCJAhIXChFSZXdhcmRJbnZhbGlkVHlwZRDR", + "iQISHAoWUmV3YXJkSW52YWxpZFR5cGVWYWx1ZRDSiQISHgoYTm90UmVxdWly", + "ZUF0dHJpYnV0ZVZhbHVlENOJAhIuCihSZXdhcmRHcm91cElkUGFyc2luZ0Zy", + "b21TdHJpbmdUb0ludEVyb3JyENSJAhIWChBDbGFpbUludmFsaWRUeXBlELiR", + "AhIdChdDbGFpbU1lbWJlcnNoaXBOb3RFeGlzdBC5kQISFwoRQ2xhaW1JbmZv", + "Tm90RXhpc3QQupECEh4KGENsYWltUmV3YXJkTm90RW5vdWdoVGltZRC7kQIS", + "GQoTQ2xhaW1SZXdhcmRFdmVudEVuZBC8kQISFAoOQ2xhaW1Eb2NJc051bGwQ", + "vZECEhoKFENyYWZ0UmVjaXBlRG9jSXNOdWxsEKGZAhIYChJDcmFmdEhlbHBE", + "b2NJc051bGwQopkCEhQKDkNyYWZ0RG9jSXNOdWxsEKOZAhIeChhDcmFmdGlu", + "Z01ldGFEYXRhTm90Rm91bmQQpJkCEhcKEUNyYWZ0aW5nTm90RmluaXNoEKWZ", + "AhIfChlDcmFmdGluZ05vdENyYWZ0aW5nQW5jaG9yEKaZAhIdChdDcmFmdGlu", + "Z0FscmVhZHlDcmFmdGluZxCnmQISHwoZQ3JhZnRpbmdBbmNob3JJc05vdFBs", + "YWNlZBComQISIQobQ3JhZnRpbmdSZWNpcGVJc05vdFJlZ2lzdGVyEKmZAhIo", + "CiJDcmFmdGluZ0FuY2hvcklzTm90TWF0Y2hXaXRoUmVjaXBlEKqZAhIbChVD", + "cmFmdGluZ0hlbHBDb3VudE92ZXIQq5kCEiMKHUNyYWZ0aW5nSGVscFNhbWVV", + "c2VyQ291bnRPdmVyEKyZAhIjCh1DcmFmdGluZ0hlbHBSZWNlaXZlZENvdW50", + "T3ZlchCtmQISGQoTQ3JhZnRIZWxwRG9jSXNFbXB0eRCumQISFQoPQ3JhZnRE", + "b2NJc0VtcHR5EK+ZAhIbChVDcmFmdGluZ0FscmVhZHlGaW5pc2gQsJkCEiUK", + "H0NyYWZ0aW5nUmVjaXBlSXNBbHJlYWR5UmVnaXN0ZXIQsZkCEhcKEUNyYWZ0", + "RG9jRXhjZXB0aW9uELKZAhIbChVDcmFmdEhlbHBEb2NFeGNlcHRpb24Qs5kC", + "Eh0KF0NyYWZ0UmVjaXBlRG9jRXhjZXB0aW9uELSZAhIeChhDcmFmdEludmFs", + "aWRSZXF1ZXN0Q291bnQQtZkCEh8KGVVncUFwaVNlcnZlclJlcXVlc3RGYWls", + "ZWQQiaECEicKIVVncUFwaVNlcnZlckNvbnZlcnRUb09iamVjdEZhaWxlZBCK", + "oQISKwolVWdxQXBpU2VydmVySW52YWlsZFNlYXJjaENhdGVnb3J5VHlwZRCL", + "oQISIAoaVWdxUmVwb3J0SW52YWxpZFRleHRMZW5ndGgQjKECEhoKFFVncVF1", + "ZXN0TWV0YU5vdEV4aXN0EI2hAhIeChhVZ3FCZWdpbkNyZWF0b3JQb2ludEZh", + "aWwQjqECEhgKElVncVF1ZXN0U2h1dGRvd25lZBCPoQISIgocVWdxVGVzdFF1", + "ZXN0QWxyZWFkeUNvbXBsZXRlZBCQoQISNAouVWdxUmVhc3NpZ25Vc2luZ0l0", + "ZW1FcnJvckNhdXNlUXVlc3ROb3RDb21wbGV0ZRCRoQISNQovVWdxUmVhc3Np", + "Z25Vc2luZ0l0ZW1FcnJvckNhdXNlUXVlc3RBbHJlYWR5RXhpc3QQkqECEjYK", + "MFVncVJlYXNzaWduVXNpbmdJdGVtRXJyb3JDYXVzZU5ld1JldmlzaW9uVXBk", + "YXRlZBCToQISIQobVWdxUXVlc3REYXRhSW52YWxpZFJldmlzaW9uEJShAhIl", + "Ch9VZ3FBYm9ydENhbm5vdENhdXNlSW52YWxpZFN0YXRlEJWhAhIeChhVZ3FN", + "ZXRhR2VuZXJhdG9yTm90RXhpc3QQlqECEiEKG1VncVF1ZXN0RGF0YVJldmlz", + "aW9uVXBkYXRlZBCXoQISMwotVWdxUmV2aXNpb25DYW5ub3RTbWFsbGVyVGhh", + "blJlcXVlc3RlZFJldmlzaW9uEJihAhIdChdVZ3FSZXZpc2lvblN0YXRlTm90", + "TGl2ZRCZoQISJgogVWdxUmV2aXNpb25TdGF0ZU9ubHlMaXZlbkFuZFRlc3QQ", + "mqECEiYKIFVncUFwaVNlcnZlckh0dHBSZXF1ZXN0RXhjZXB0aW9uEJuhAhId", + "ChdVZ3FRdWVzdFJldmlzaW9uQ2hhbmdlZBCcoQISHwoZVWdxQXNzaWduQ2Fu", + "bm90T3duZWRRdWVzdBCdoQISJQofVWdxQWxyZWFkeU93bmVkT2xkUmV2aXNp", + "b25RdWVzdBCeoQISGQoTU2Vhc29uUGFzc0RvY0lzTnVsbBDxqAISIAoaU2Vh", + "c29uUGFzc01ldGFEYXRhTm90Rm91bmQQ8qgCEiYKIFNlYXNvblBhc3NSZXdh", + "cmRNZXRhRGF0YU5vdEZvdW5kEPOoAhIYChJTZWFzb25QYXNzTWF4R3JhZGUQ", + "9KgCEh0KF1NlYXNvblBhc3NOb3RBYmxlUGVyaW9kEPWoAhIhChtTZWFzb25Q", + "YXNzQWxyZWFkeUJ1eUNoYXJnZWQQ9qgCEiIKHFNlYXNvblBhc3NBbHJlYWR5", + "VGFrZW5SZXdhcmQQ96gCEh4KGFNlYXNvblBhc3NOb3RFbm91Z2hHcmFkZRD4", + "qAISHwoZU2Vhc29uUGFzc05lZWRDaGFyZ2VkUGFzcxD5qAISGgoUU2Vhc29u", + "UGFzc0ludmFsaWRFeHAQ+qgCEhwKFlNlYXNvblBhc3NEb2NFeGNlcHRpb24Q", + "+6gCEiQKHkxhbmRBdWN0aW9uUmVkaXNDYWNoZVNldEZhaWxlZBDZsAISGQoT", + "TGFuZEF1Y3Rpb25Ob3RGb3VuZBDasAISJQofTGFuZEF1Y3Rpb25JbnZhbGlk", + "QXVjdGlvbk51bWJlchDbsAISJwohTGFuZEF1Y3Rpb25CaWRDdXJyZW5jeVR5", + "cGVJbnZhbGlkELTqARIiChxMYW5kQXVjdGlvbkJpZFByaWNlTm90RW5vdWdo", + "EN2wAhIiChxMYW5kQXVjdGlvbkVtcHR5Q2FjaGVJblJlZGlzEN6wAhIkCh5M", + "YW5kQXVjdGlvblJlZ2lzdHJ5SW5mb0ludmFsaWQQ37ACEhsKFUxhbmRBdWN0", + "aW9uTm90U3RhcnRlZBDgsAISKgokTGFuZEF1Y3Rpb25NaXNtYXRjaEJldHdl", + "ZW5DYWNoZUFuZERiEOGwAhIfChlMYW5kQXVjdGlvbkFscmVhZHlTdGFydGVk", + "EOKwAhIfChlMYW5kQXVjdGlvbkxhbmRJdGVtTm90U2V0EOOwAhIiChxMYW5k", + "QXVjdGlvbkVkaXRvclR5cGVJbnZhbGlkEOSwAhIdChdMYW5kQXVjdGlvbkFs", + "cmVhZHlFbmRlZBDlsAISKgokTGFuZEF1Y3Rpb25IaWdoZXN0QmlkVXNlckF0", + "dHJpYkVycm9yEOawAhIqCiRMYW5kQXVjdGlvblJlZnVuZEJpZFByaWNlQXR0", + "cmliRXJyb3IQ57ACEjAKKkxhbmRBdWN0aW9uQmlkZGVyUmVmdW5kQmlkUHJp", + "Y2VBdHRyaWJFcnJvchDosAISIAoaR2FtZUNvbmZpZ01ldGFEYXRhTm90Rm91", + "bmQQwbgCEioKJEF0dHJpYnV0ZURlZmluZWl0aW9uTWV0YURhdGFOb3RGb3Vu", + "ZBDCuAISIQobUmVxdWlyZW1lbnRNZXRhRGF0YU5vdEZvdW5kEMO4AhIgChpT", + "eXN0ZW1NYWlsTWV0YURhdGFOb3RGb3VuZBDEuAISHgoYSW50ZXJpb3JNZXRh", + "RGF0YU5vdEZvdW5kEMW4AhIfChlQcm9wR3JvdXBNZXRhRGF0YU5vdEZvdW5k", + "EMa4AhIXChFBaUNoYXRTZXJ2ZXJTdGFydBCpwAISGwoVQWlDaGF0U2VydmVy", + "UmVxRmFpbGVkEKrAAhIcChZBaUNoYXRTZXJ2ZXJCYWRyZXF1ZXN0EKvAAhIb", + "ChVBaUNoYXRTZXJ2ZXJGb3JiaWRkZW4QrMACEh4KGEFpQ2hhdFNlcnZlclVu", + "YXV0aG9yaXplZBCtwAISHgoYQWlDaGF0U2VydmVyVXNlck5vdEZvdW5kEK7A", + "AhIjCh1BaUNoYXRTZXJ2ZXJDaGFyYWN0ZXJOb3RGb3VuZBCvwAISIgocQWlD", + "aGF0U2VydmVyUmVhY3Rpb25Ob3RGb3VuZBCwwAISKAoiQWlDaGF0U2VydmVy", + "RXhhbXBsZURpYWxvbmdOb3RGb3VuZBCxwAISIQobQWlDaGF0U2VydmVyU2Vz", + "c2lvbk5vdEZvdW5kELLAAhIfChlBaUNoYXRTZXJ2ZXJNb2RlbE5vdEZvdW5k", + "ELPAAhIgChpBaUNoYXRTZXJ2ZXJOb3RFbm91Z2hQb2ludBC0wAISIwodQWlD", + "aGF0U2VydmVySW52YWxpZFBhcmFtZXRlcnMQtcACEiYKIEFpQ2hhdFNlcnZl", + "clNlc3Npb25MaW1pdEV4Y2VlZGVkELbAAhIiChxBaUNoYXRTZXJ2ZXJJbnZh", + "bGlkU2lnbmF0dXJlELfAAhIjCh1BaUNoYXRTZXJ2ZXJVc2VyQWxyZWFkeUV4", + "aXN0cxC4wAISHgoYQWlDaGF0U2VydmVyUmVtb3ZlRmFpbGVkELnAAhIfChlB", + "aUNoYXRTZXJ2ZXJEdXBsaWNhdGVHdWlkELrAAhIdChdBaUNoYXRTZXJ2ZXJE", + "dXBsaWNhdGVJZBC7wAISHgoYQWlDaGF0U2VydmVyQ2hhdE5vdEZvdW5kELzA", + "AhIeChhBaUNoYXRTZXJ2ZXJUb2tlbkV4cGlyZWQQvcACEiAKGkFpQ2hhdFNl", + "cnZlckludGVybmFsU2VydmVyEL7AAhIgChpBaUNoYXRTZXJ2ZXJJbnZhbGlk", + "TWVzc2FnZRC/wAISIQobQWlDaGF0U2VydmVyVXNlck9ubHlGZWF0dXJlEMDA", + "AhIgChpBaUNoYXRTZXJ2ZXJPd25lcnNoaXBFcnJvchDBwAISKgokQWlDaGF0", + "U2VydmVyQ2hhcmdlT3JkZXJOb3RGb3VuZEVycm9yEMLAAhIVCg9BaUNoYXRT", + "ZXJ2ZXJFbmQQjMECEiIKHEFpQ2hhdFNlcnZlclJldHJ5Q2hhcmdlUG9pbnQQ", + "jcECEhoKFEFpQ2hhdFNlcnZlckluYWN0aXZlEI7BAhIYChJBaUNoYXREb2NF", + "eGNlcHRpb24Qj8ECEg8KCU5wY0lzQnVzeRCRyAISFwoRTGFja09mRGFpbHlD", + "YWxpdW0Q+c8CEhcKEUxhY2tPZlRvdGFsQ2FsaXVtEPrPAhIeChhMYWNrT2ZD", + "b21taXNzaW9uQ3VycmVuY3kQ+88CEh8KGUxhY2tPZkNvbW1pc3Npb25NYXRl", + "cmlhbHMQ/M8CEhsKFUludmFsaWRNYXRlcmlhbFNsb3RJZBD9zwISFgoQRmFp", + "bFRvTG9hZENhbGl1bRD+zwISHAoWRmFpbFRvU2F2ZUNhbGl1bUR5bmFtbxD/", + "zwISGQoTTGFja09mQ29udmVydENhbGl1bRCr0AISHwoZR2V0RmFpbEVjaG9T", + "eXN0ZW1SZXNwb25zZRDc0AISIgocRmFpbFRvR2V0RWNob1N5c3RlbUh0dHBF", + "cnJvchDd0AISJAoeRmFpbFRvR2V0RWNob1N5c3RlbU1lc3NhZ2VOdWxsEN7Q", + "AhIiChxGYWlsVG9HZXRFY2hvU3lzdGVtRXhjZXB0aW9uEN/QAhIaChRGYWls", + "VG9TZW5kRWNob1N5c3RlbRDg0AISHwoZRmFpbFRvR2V0RWNob1N5c3RlbVJv", + "bGxVcBDh0AISHQoXQWNjb3VudExvZ2luQmxvY2tFbmFibGUQ0YYDEhsKFUlu", + "c3RhbmNlUm9vbUV4Y2VwdGlvbhC5jgMSJgogSW5zdGFuY2VSb29tQ2Fubm90", + "V3JpdGVFeHRyYUluZm8Quo4DEiYKIEluc3RhbmNlUm9vbU5vdENoYXJnZWRT", + "ZWFzb25QYXNzELuOAxIeChhJbnN0YW5jZU1ldGFEYXRhTm90Rm91bmQQvI4D", + "EiQKHkluc3RhbmNlTWV0YURhdGFPdmVyTGltaXRXcm9uZxC9jgMSHwoZSW5z", + "dGFuY2VBY2Nlc3NUeXBlSW52YWxpZBC+jgMSIQobSW5zdGFuY2VBY2Nlc3NJ", + "dGVtTm90RW5vdWdoEL+OAxIoCiJJbnN0YW5jZUFjY2Vzc1NlYXNvblBhc3NO", + "b3RDaGFyZ2VkEMCOAxIYChJJbnN0YW5jZVJvb21Jc0Z1bGwQwY4DEh4KGElu", + "c3RhbmNlUm9vbUlkRHVwbGljYXRlZBDCjgMSIQobSW5zdGFuY2VSb29tTm90", + "RXhpc3RBdFJlZGlzEMOOAxIeChhUYXNrUmVzZXJ2YXRpb25Eb2NJc051bGwQ", + "oZYDEiIKHEJpbGxpbmdHZXRQdXJjaGFzZUluZm9GYWlsZWQQiZ4DEh4KGEJp", + "bGxpbmdVcGRhdGVTdGF0ZUZhaWxlZBCKngMSHQoXQmlsbGluZ0ludmFsaWRT", + "dGF0ZVR5cGUQi54DEikKI0JpbGxpbmdGYWlsZWRQYXJzZVByb2R1Y3RNZXRh", + "SWRUeXBlEIyeAxIdChdCaWxsaW5nU3RhdGVUeXBlSW52YWxpZBCNngMSIwod", + "QmlsbGluZ1N0YXRlVHlwZUNhbnRCZUNoYW5nZWQQjp4DEhwKFkJpbGxpbmdT", + "dGF0ZVR5cGVSZWZ1bmQQj54DEiQKHkJpbGxpbmdTdGF0ZVR5cGVSZWZ1bmRD", + "b21wbGV0ZRCQngMSGgoUVGF4aU1ldGFEYXRhTm90Rm91bmQQ8aUDEhUKD1Rh", + "eGlUeXBlSW52YWxpZBDypQMSGgoUV2FycE1ldGFEYXRhTm90Rm91bmQQ86UD", + "EhUKD1dhcnBUeXBlSW52YWxpZBD0pQMSFAoOUmVudGFsTm90Rm91bmQQ2a0D", + "EiMKHVJlbnRhbERvY0xvYWREdXBsaWNhdGVkUmVudGFsENqtAxIVCg9SZW50", + "YWxEb2NJc051bGwQ260DEhwKFlJlbnRhbE5vdEF2YWlsYWJsZUxhbmQQ3K0D", + "EhoKFFJlbnRhbEFkZHJlc3NJbnZhbGlkEN2tAxIdChdSZW50YWxBZGRyZXNz", + "SXNOb3RFbXB0eRDerQMSHwoZUmVudGFsZmVlTWV0YURhdGFOb3RGb3VuZBDf", + "rQMSHgoYUmVudGFsQ29udHJhY3RJbmZvVXBkYXRlEOCtAxIiChxSZW50YWxD", + "dXJyZW5jeUFtb3VudElzVG9vTG93EMm1AxIfChlSZW50YWxDdXJyZW5jeVR5", + "cGVJc1dyb25nEMq1AxIoCiJQYWNrYWdlTGFzdE9yZGVyUmVjb2RlRG9jRXhj", + "ZXB0aW9uEMG1AxIfChlQYWNrYWdlUmVwZWF0RG9jRXhjZXB0aW9uEMK1AxIZ", + "ChNCZWFjb25TaG9wRXhjZXB0aW9uEKm9AxIfChlCZWFjb25TaG9wSW52YWxp", + "ZEFyZ3VtZW50EKq9AxInCiFCZWFjb25TaG9wQmVhY29uSXNOb3RJblJlbnRh", + "bEhvbWUQq70DEhwKFkJlYWNvblNob3BOb3RGb3VuZEl0ZW0QrL0DEh0KF0Jl", + "YWNvblNob3BOb3RFbm91Z2hJdGVtEK29AxIiChxCZWFjb25TaG9wSW52YWxp", + "ZEl0ZW1Gb3JTZWxsEK69AxIgChpCZWFjb25TaG9wTm90Rm91bmRNZXRhRGF0", + "YRCvvQMSHwoZQmVhY29uU2hvcExvd1NlbGxpbmdQcmljZRCwvQMSJAoeQmVh", + "Y29uU2hvcE5vdEVub3VnaFJlZ2lzdGVyRmVlELG9AxIaChRCZWFjb25TaG9w", + "U2xvdElzRnVsbBCyvQMSJwohQmVhY29uU2hvcE92ZXJPbmVEYXlSZWdpc3Rl", + "ckxpbWl0ELO9AxIeChhCZWFjb25TaG9wRmFpbGVkVG9DcmVhdGUQtL0DEiMK", + "HUJlYWNvblNob3BGYWlsZWRSZWdpc3RlckJvYXJkELW9AxIhChtCZWFjb25T", + "aG9wRmFpbGVkRGVsZXRlQm9hcmQQtr0DEiUKH0JlYWNvblNob3BOb3RGb3Vu", + "ZEl0ZW1Gcm9tQm9hcmQQt70DEiAKGkJlYWNvblNob3BQcm9maWxlRXhjZXB0", + "aW9uELi9AxIlCh9CZWFjb25TaG9wTm90RW5vdWdoUmVnaXN0ZXJHb2xkELm9", + "AxIgChpCZWFjb25TaG9wTGFja09mSXRlbUFtb3VudBC6vQMSIgocQmVhY29u", + "U2hvcEZhaWxlZEdldEJvYXJkSXRlbRC7vQMSIwodQmVhY29uU2hvcFNvbGRS", + "ZWNvcmRFeGNlcHRpb24QvL0DEisKJUJlYWNvblNob3BGYWlsZWRSZWxvYWRC", + "ZWFjb25TaG9wSW52ZW4Qvb0DEh0KF0JlYWNvblNob3BVcGRhdGVOZXdEYXRh", + "EL69AxImCiBCZWFjb25TaG9wRmFpbGVkVXBkYXRlRGF0YUZyb21EYhC/vQMS", + "IwodQmVhY29uU2hvcE5vdEZvdW5kU29sZFJlY29yZHMQwL0DEiEKG0JlYWNv", + "blNob3BQcm9maWxlRG9jSXNFbXB0eRDBvQMSIgocQmVhY29uU2hvcFNvbGRQ", + "cmljZUV4Y2VwdGlvbhDCvQMSIQobQmVhY29uU2hvcE5vdEZvdW5kU29sZFBy", + "aWNlEMO9AxIkCh5CZWFjb25TaG9wRmFpbGVkVG9GaW5kT3JVcGRhdGUQxL0D", + "EhsKFUJlYWNvblNob3BEYkV4Y2VwdGlvbhDFvQMSIAoaQmVhY29uU2hvcE92", + "ZXJTZWxsaW5nUHJpY2UQxr0DEiIKHEJlYWNvblNob3BPdmVyUmVudGFsU2Fm", + "ZVRpbWUQx70DEiMKHUJlYWNvblNob3BEZWFjdGl2ZUl0ZW1Gb3JTZWxsEMi9", + "AxIaChRVZ3FJbnZhbGlkVGFza0FjdGlvbhDh1AMSGwoVVWdxVGFza0FjdGlv", + "bkRpc2FibGVkEOLUAxIaChRVZ3FJbnZhbGlkRGlhbG9nVHlwZRDj1AMSHwoZ", + "VWdxSW52YWxpZERpYWxvZ0NvbmRpdGlvbhDk1AMSGAoSVWdxVmFsaWRhdGlv", + "bkVycm9yEOXUAxITCg1VZ3FOdWxsRW50aXR5EObUAxIZChNVZ3FTdGF0ZUNo", + "YW5nZUVycm9yEOfUAxIbChVVZ3FOb3RFbm91Z2hRdWVzdFNsb3QQ6NQDEhUK", + "D1VncU5vdEFsbG93RWRpdBDp1AMSGgoUVWdxRGlhbG9nSXNOb3RJblRhc2sQ", + "6tQDEhUKD1VncVJlcXVpcmVJbWFnZRDr1AMSFgoQVWdxUmVxdWlyZUJlYWNv", + "bhDs1AMSGQoTVWdxQmVhY29uSW5wdXRFcnJvchDt1AMSGgoUVWdxR2FtZURC", + "QWNjZXNzRXJyb3IQ7tQDEhUKD1VncU5vdE93blVnY05wYxDv1AMSHQoXVWdx", + "QWxyZWFkeUV4aXN0c0FjY291bnQQ8NQDEhgKElVncVNlcnZlckV4Y2VwdGlv", + "bhDx1AMSHgoYVWdxSW52YWxpZFdlYlBvcnRhbFRva2VuEPLUAxIVCg9VZ3FJ", + "bnZhbGlkVG9rZW4Q89QDEhcKEVVncVJlcXVpcmVBY2NvdW50EPTUAxIeChhV", + "Z3FOb3RFbm91Z2hDcmVhdG9yUG9pbnQQ9dQDEhIKDFVncVNsb3RMaW1pdBD2", + "1AMSHwoZVWdxRXhjZWVkVHJhbnNhY3Rpb25SZXRyeRD31AMSGAoSVWdxTWV0", + "YXZlcnNlT25saW5lEPjUAxIUCg5VZ3FBdXRoUmVtb3ZlZBD51AMSGwoVVWdx", + "SW52YWxpZFByZXNldEltYWdlEPrUAxIaChRVZ3FOb3RFbm91Z2hDdXJyZW5j", + "eRD71AMSFgoQVWdxQ3VycmVuY3lFcnJvchD81AMSIgocVWdxQWxyZWFkeUV4", + "aXN0c1Jlc2VydmVHcmFkZRD91AMSGwoVVWdxSW52YWxpZFJlc2VydmVUaW1l", + "EP7UAxIZChNVZ3FTYW1lR3JhZGVSZXNlcnZlEP/UAxIaChRVZ3FBbHJlYWR5", + "Qm9va21hcmtlZBDJ3AMSFgoQVWdxTm90Qm9va21hcmtlZBDK3AMSFQoPVWdx", + "QWxyZWFkeUxpa2VkEMvcAxIRCgtVZ3FOb3RMaWtlZBDM3AMSGAoSVWdxQWxy", + "ZWFkeVJlcG9ydGVkEM3cAxIUCg5VZ3FOb3RPd25RdWVzdBDO3AMSFQoPVWdx", + "SW52YWxpZFN0YXRlEM/cAxIdChdOZnRGb3JPd25lckFsbEdldEZhaWxlZBCx", + "5AMSGQoTSW50ZXJuYWxTZXJ2ZXJFcnJvchDxogQSDgoIUmRiRXJyb3IQ8qIE", + "EhEKC0R5bmFtb0Vycm9yEPOiBBIUCg5JbnZhbGlkUmVxdWVzdBD7ogQSFgoQ", + "UGxhbmV0SWROb3RGb3VuZBDZqgQSIwodUGxhbmV0U2VjcmV0S2V5RG9lc05v", + "dE1hdGNoZWQQ2qoEEhYKEEludmFsaWRQbGFuZXRKd3QQ26oEEhYKEEV4cGly", + "ZWRQbGFuZXRKd3QQ3KoEEiAKGk1ldGF2ZXJzZUNsaWVudE9uQ29ubmVjdGVk", + "EKGsBBIUCg5JbnZhbGlkVXNlckp3dBCirAQSFAoORXhwaXJlZFVzZXJKd3QQ", + "o6wEEhUKD0FjY291bnROb3RGb3VuZBCFrQQSEgoMVXNlck5vdEZvdW5kEIat", + "BBIdChdFeGNoYW5nZU9yZGVySWROb3RGb3VuZBDBsgQSKgokRXhjaGFuZ2VU", + "b3RhbE9yZGVyRGFpbHlMaW1pdEV4Y2VlZGVkEMKyBBIpCiNFeGNoYW5nZVVz", + "ZXJPcmRlckRhaWx5TGltaXRFeGNlZWRlZBDDsgQSFQoPQm90UGxheWVySXNO", + "dWxsENHbOhIcChZDbGllbnRUb0xvZ2luUmVzSXNOdWxsENLbOhIgChpDbGll", + "bnRUb0xvZ2luTWVzc2FnZUlzTnVsbBDT2zoSGwoVQ2xpZW50VG9HYW1lUmVz", + "SXNOdWxsENTbOhIfChlDbGllbnRUb0dhbWVNZXNzYWdlSXNOdWxsENXbOhIb", + "ChVDb25uZWN0ZWRUb1NlcnZlckZhaWwQ1ts6EhoKFFNjZW5hcmlvblBhcmFt", + "SXNOdWxsENfbOhIhChpCYXR0bGVSb29tQ29udGVudHNUeXBlT25seRDBsZ8F", + "Eh4KF0JhdHRsZUluc3RhbmNlVHlwZUVycm9yEMKxnwUSJQoeQmF0dGxlSW5z", + "dGFuY2VJbmZvQWxyZWFkeUV4aXN0EMOxnwUSIQoaQmF0dGxlSW5zdGFuY2VJ", + "bmZvTm90RXhpc3QQxLGfBRIkCh1CYXR0bGVJbnN0YW5jZUpvaW5QbGF5ZXJF", + "cnJvchDFsZ8FEi0KJkJhdHRsZUluc3RhbmNlVXNhYmxlU3Bhd25Qb2ludE5v", + "dEV4aXN0EMaxnwUSHQoWQmF0dGxlSW5zdGFuY2VJbkFjdGl2ZRDHsZ8FEiQK", + "HUJhdHRsZUluc3RhbmNlTm90RXhpc3RBbmNob3JzEMixnwUSJQoeQmF0dGxl", + "SW5zdGFuY2VBZGRQb2RDb21iYXRGYWlsEMmxnwUSJwogQmF0dGxlSW5zdGFu", + "Y2VPYmplY3RNZXRhTm90RXhpc3QQyrGfBRIwCilCYXR0bGVJbnN0YW5jZU9i", + "amVjdEludGVyYWN0aW9uTm90eWV0VGltZRDLsZ8FEiMKHEJhdHRsZUluc3Rh", + "bmNlT2JqZWN0Tm90RXhpc3QQzLGfBRIrCiRCYXR0bGVJbnN0YW5jZVBvZENv", + "bWJhdEFscmVhZHlPY2N1cHkQzbGfBRIvCihCYXR0bGVJbnN0YW5jZU9iamVj", + "dEludGVyYWN0aW9uTm90QWN0aXZlEM+xnwUSKwokQmF0dGxlSW5zdGFuY2VN", + "ZXRhQ29uZmlnTm90RXhpc3REYXRhENCxnwUSKwokQmF0dGxlSW5zdGFuY2VN", + "ZXRhUmV3YXJkTm90RXhpc3REYXRhENGxnwUSMwosQmF0dGxlSW5zdGFuY2VQ", + "aWNrdXBQb2RHZW5lcmF0ZWRUaW1lTm90RXhpc3QQ0rGfBRIqCiNCYXR0bGVJ", + "bnN0YW5jZVBpY2t1cFBvZE5vdEV4aXN0RGF0YRDTsZ8FEicKIEJhdHRsZUlu", + "c3RhbmNlTm90RXhpc3RQbGF5ZXJJbmZvENSxnwUSJAodQmF0dGxlSW5zdGFu", + "Y2VJbnRlcmFjdGlvbkZhaWwQ1bGfBRIxCipCYXR0bGVJbnN0YW5jZVBpY2t1", + "cFBvZFJld2FyZEFsbG9jYXRlRXJyb3IQ1rGfBRImCh9CYXR0bGVJbnN0YW5j", + "ZU5vdEV4aXN0RXZlbnRJbmZvENexnwUSIAoZQmF0dGxlSW5zdGFuY2VDbG9z", + "aW5nVGltZRDYsZ8FEiIKG0JhdHRsZUluc3RhbmNlU2VxUGFyc2VFcnJvchDZ", + "sZ8FEiIKG0dhbWVNb2RlSm9pbkhhbmRsZXJOb3RFeGlzdBCksp8FEiIKG0dh", + "bWVNb2RlSW5pdEhhbmRsZXJOb3RFeGlzdBClsp8FEikKIkdhbWVNb2RlSm9p", + "blN1Y2Nlc3NIYW5kbGVyTm90RXhpc3QQprKfBRIZChJHYW1lTW9kZUNyZWF0", + "ZUZhaWwQp7KfBRIaChNHYW1lTW9kZUNsYXNzSXNOdWxsEKiynwUSGwoUR2Ft", + "ZU1vZGVBbHJlYWR5RXhpc3QQqbKfBRIgChlHYW1lTW9kZUludmFsaWRBbmNo", + "b3JHdWlkEKqynwUSKgojU2VydmVyTWV0cmljc1RyaWdnZXJIYW5kbGVyTm90", + "Rm91bmQQ/brXLxIMCghEdXBMb2dpbhABEgoKBk1vdmluZxACEgsKB0RiRXJy", + "b3IQAxIMCghLaWNrRmFpbBAEEhYKEk5vdENvcnJlY3RQYXNzd29yZBAFEhAK", + "DE5vdEZvdW5kVXNlchAGEhAKDE5vR2FtZVNlcnZlchAHEhAKDExvZ2luUGVu", + "ZGluZxAIEhIKDk5vdEltcGxlbWVudGVkEAkSHQoZTm90RXhpc3RTZWxlY3Rl", + "ZENoYXJhY3RlchAKEhUKEU5vdEV4aXN0Q2hhcmFjdGVyEAsSFAoQU2VydmVy", + "TG9naWNFcnJvchAMEhEKDU5vUGVybWlzc2lvbnMQDhINCglSZWRpc0ZhaWwQ", + "DxIOCglMb2dpbkZhaWwQ6AcSEwoORHVwbGljYXRlZFVzZXIQ6QcSEQoMSW52", + "YWxpZFRva2VuEOoHEhUKEE5vdENvcnJlY3RTZXJ2ZXIQ6wcSDwoKSW5zcGVj", + "dGlvbhDsBxIOCglCbGFja0xpc3QQ7QcSDwoKU2VydmVyRnVsbBDuBxITCg5O", + "b3RGb3VuZFNlcnZlchDvBxISCg1Ob3RGb3VuZFRhYmxlEPAHEg8KClRhYmxl", + "RXJyb3IQ8QcSFQoQSW52YWxpZENvbmRpdGlvbhDMCBITCg5DaGFyQ3JlYXRl", + "RmFpbBDQDxITCg5DaGFyU2VsZWN0RmFpbBC0EBITCg5DcmVhdGVSb29tRmFp", + "bBC4FxIRCgxKb2luUm9vbUZhaWwQnBgSFQoQSm9pbkluc3RhbmNlRmFpbBCA", + "GRIbChZOb3RFeGlzdEluc3RhbmNlVGlja2V0EIEZEhYKEUxlYXZlSW5zdGFu", + "Y2VGYWlsEOQZEhkKFE5vdEV4aXN0SW5zdGFuY2VSb29tEMgaEhsKFk5vdENv", + "cnJlY3RJbnN0YW5jZVJvb20QyRoSDwoKUG9zSXNFbXB0eRDKGhIUCg9FbnRl", + "ck15SG9tZUZhaWwQ2B0SFAoPTGVhdmVNeUhvbWVGYWlsELweEhcKEkV4Y2hh", + "bmdlTXlIb21lRmFpbBCgHxIXChJOb3RGb3VuZE15SG9tZURhdGEQoR8SEwoO", + "Tm90TXlIb21lT3duZXIQoh8SFgoRQWxyZWFkeUhhdmVNeUhvbWUQox8SGwoW", + "RXhjaGFuZ2VNeUhvbWVQcm9wRmFpbBCEIBIZChRFeGNoYW5nZUJ1aWxkaW5n", + "RmFpbBDMIRIfChpFeGNoYW5nZUJ1aWxkaW5nTEZQcm9wRmFpbBCwIhIZChRF", + "eGNoYW5nZUluc3RhbmNlRmFpbBCUIxIhChxFeGNoYW5nZVNvY2lhbEFjdGlv", + "blNsb3RGYWlsEPgjEh0KGE5vdEZvdW5kU29jaWFsQWN0aW9uRGF0YRD6IxIa", + "ChVOb3RJblNvY2lhbEFjdGlvblNsb3QQ+yMSHAoXQWxyZWFkeUhhdmVTb2Np", + "YWxBY3Rpb24Q/CMSGQoUTm90Rm91bmRCdWlsZGluZ0RhdGEQrCQSFgoRTm90", + "Rm91bmRGbG9vckluZm8QrSQSFgoRTm90Rm91bmRJbmR1bkRhdGEQryQSGQoU", + "RW50ZXJGaXR0aW5nUm9vbUZhaWwQ3CQSGgoVTm90RW50ZXRlZEZpdHRpbmdS", + "b29tEN0kEhAKC01ha2VGYWlsT3RwEN4kEh0KGE5vdEV4aXN0Um9vbUluZm9G", + "b3JFbnRlchDfJBIfChpOb3RFeGlzdEdhbWVTZXJ2ZXJGb3JFbnRlchDgJBIV", + "ChBQb3NpdGlvblNhdmVGYWlsEOEkEhwKF0V4Y2hhbmdlRW1vdGlvblNsb3RG", + "YWlsEMAlEhoKFUVtb3Rpb25TbG90T3V0T2ZSYW5nZRDBJRIcChdOb3RGb3Vu", + "ZEFuY2hvckd1aWRJbk1hcBCjJhIXChJOb3RGb3VuZEFuY2hvckd1aWQQpCYS", + "DwoKUHJvcElzVXNlZBCmJhIUCg9Qcm9wVHlwZWlzV3JvbmcQpyYSGAoTTm90", + "Rm91bmRFbW90aW9uRGF0YRC4JhIVChBOb3RJbkVtb3Rpb25TbG90ELkmEhMK", + "DkNyZWF0ZUl0ZW1GYWlsEIQnEhAKC0FkZEl0ZW1GYWlsEIUnEhMKDkRlbGV0", + "ZUl0ZW1GYWlsEIYnEhIKDU5vTW9yZUFkZEl0ZW0QhycSEQoMTm90Rm91bmRJ", + "dGVtEIgnEhIKDU5vdEVub3VnaEl0ZW0QiScSFQoQSW52YWxpZFNsb3RJbmRl", + "eBCKJxIXChJEdXBsaWNhdGVkSXRlbUd1aWQQiycSGAoTTm90Rm91bmRJdGVt", + "VGFibGVJZBCMJxIUCg9Ob3RTZWxlY3RlZENoYXIQjScSEAoLTm90Rm91bmRN", + "YXAQjicSEQoMTm90RW1wdHlTbG90EI8nEg4KCUVtcHR5U2xvdBCQJxIYChNO", + "b3RGb3VuZEJ1ZmZUYWJsZUlkEJEnEhEKDE5vdEZvdW5kQnVmZhCSJxIcChdO", + "b3RFeGlzdEZvcmVjZWRNb3ZlSW5mbxCTJxIXChJEdXBsaWNhdGVkTmlja05h", + "bWUQlScSFwoSQWxyZWFkeVNldE5pY2tOYW1lEJYnEhkKFERpc2FsbG93ZWRD", + "aGFyYWN0ZXJzEJcnEhMKDkRiVXBkYXRlRmFpbGVkEJgnEhQKD0ludmFsaWRB", + "cmd1bWVudBCZJxIUCg9NYWlsU3lzdGVtRXJyb3IQpicSEgoNSW52YWxpZFRh", + "cmdldBCnJxIRCgxOb3RGb3VuZE1haWwQqCcSFAoPRW1wdHlJdGVtSW5NYWls", + "EKknEhYKEU1haWxTZW5kQ291bnRPdmVyEKonEhQKD0NoYW5nZWROaWNrTmFt", + "ZRCrJxIWChFTdGF0ZUNoYW5nZUZhaWxlZBCwJxITCg5Ob3RGb3VuZFRhcmdl", + "dBC6JxIWChFCbG9ja2VkRnJvbVRhcmdldBC7JxIRCgxMb2dPZmZUYXJnZXQQ", + "vCcSEwoOQ2FudFNlbmRUb1NlbGYQvScSFAoPQnVmZlR5cGVJc1dyb25nEM4n", + "EhkKFFJlZ2lzdGVyVG9vbFNsb3RGYWlsENgnEhsKFkRlcmVnaXN0ZXJUb29s", + "U2xvdEZhaWwQ2ScSFwoSVG9vbFNsb3RPdXRPZlJhbmdlENonEhUKEE5vdEZv", + "dW5kVG9vbFNsb3QQ2ycSEgoNRW1wdHlUb29sU2xvdBDcJxIdChhGb2xkZXJO", + "YW1lRXhjZWVkZWRMZW5ndGgQ4icSGwoWRm9sZGVyTmFtZUFscmVhZHlFeGlz", + "dBDjJxIXChJGb2xkZXJOYW1lTm90RXhpc3QQ5CcSIgodRm9sZGVyTmFtZUFs", + "cmVhZHlNYXhIb2xkQ291bnQQ5ScSFgoRRm9sZGVyQ291bnRFeGNlZWQQ5icS", + "FQoQRm9sZGVyQ3JlYXRlRmFpbBDnJxIeChlGb2xkZXJSZU5hbWVDYW5ub3RE", + "ZWZhdWx0EOgnEhoKFUZvbGRlck9kZXJ0eXBlSW52YWxpZBDpJxIeChlGcmll", + "bmRSZXF1ZXN0Tm90RXhpc3RJbmZvEOwnEh0KGEZyaWVuZFJlcXVlc3RBbHJl", + "YWR5U2VuZBDtJxIgChtGcmllbmRSZXF1ZXN0QWxyZWFkeVJlY2VpdmUQ7icS", + "IAobRnJpZW5kUmVxdWVzdENhbnRTZW5kVG9TZWxmEO8nEiYKIUZyaWVuZFJl", + "cXVlc3ROb3RFeGlzdFJlY2VpdmVkSW5mbxDwJxIfChpGcmllbmRSZXF1ZXN0", + "QWxyZWFkeUZyaWVuZBDxJxIQCgtJbnZhbGlkVHlwZRDyJxIZChRJbnZhbGlk", + "QXR0cmlidXRlU2xvdBDzJxIlCiBGcmllbmRSZXF1ZXN0Q2Fubm90U2VuZEJs", + "b2NrVXNlchD/JxIeChlBZGRGcmllbmRBbHJlYWR5QmxvY2tVc2VyEIAoEh8K", + "GkFkZEZyaWVuZE5vdEV4aXN0Q2hhcmFjdGVyEIEoEhsKFkFkZEZyaWVuZEFs", + "cmVhZHlGcmllbmQQgigSIgodQWRkRnJpZW5kQWxyZWFkeUNhbmNlbFJlcXVl", + "c3QQgygSHAoXQWRkRnJpZW5kQWxyZWFkeUV4cGlyZWQQhCgSFwoSRnJpZW5k", + "SW5mb05vdEV4aXN0EIooEiUKIEZyaWVuZEluZm9NeUNvdW50QWxyZWFkeU1h", + "eENvdW50EIsoEikKJEZyaWVuZEluZm9PdGhlcnNDb3VudEFscmVhZHlNYXhD", + "b3VudBCMKBIWChFGcmllbmRJbmZvT2ZmbGluZRCNKBIbChZGcmllbmRJbmZv", + "QWxyZWFkeUV4aXN0EI4oEiEKHEZyaWVuZEludml0ZU15UG9zSXNOb3RNeUhv", + "bWUQlCgSIQocRnJpZW5kSW52aXRlRG9udERpc3R1cmJTdGF0ZRCVKBIhChxG", + "cmllbmRJbnZpdGVFeHBpcmVUaW1lUmVtYWluEJYoEiMKHkZyaWVuZEludml0", + "ZVdhaXRpbmdPdGhlckludml0ZRCXKBIeChlGcmllbmRJbnZpdGVBbHJlYWR5", + "RXhwaXJlEJgoEh8KGkZyaWVuZEtpY2tNeVBvc0lzTm90TXlIb21lEJkoEh0K", + "GEZyaWVuZEtpY2tNZW1iZXJOb3RFeGlzdBCaKBIcChdGcmllbmRJc0luQW5v", + "dGhlck15aG9tZRCbKBIWChFCbG9ja1VzZXJNYXhDb3VudBCeKBIaChVCbG9j", + "a1VzZXJBbHJlYWR5QmxvY2sQnygSHgoZQmxvY2tVc2VyQ2Fubm90U2VuZE1h", + "aWxUbxCgKBITCg5CbG9ja2VkQnlPdGhlchChKBIdChhCbG9ja1VzZXJDYW5u", + "b3RXaGlzcGVyVG8QoigSEwoOQmxvY2tJbmZvRW1wdHkQoygSHwoaQmxvY2tV", + "c2VyQ2Fubm90SW52aXRlUGFydHkQpCgSIwoeQ2FydFNlbGxUeXBlTWlzc01h", + "dGNoV2l0aFRhYmxlENAoEhgKE0NhcnRGdWxsU3RhY2tvZkl0ZW0Q0SgSDwoK", + "Q2FydGlzRnVsbBDSKBIQCgtCYW5OaWNrTmFtZRDTKBIZChRDdXJyZW5jeU5v", + "dEZvdW5kRGF0YRCYKhIWChFDdXJyZW5jeU5vdEVub3VnaBCZKhIZChRDdXJy", + "ZW5jeUludmFsaWRWYWx1ZRCaKhIUCg9TdGFydEJ1ZmZGYWlsZWQQ/CoSEwoO", + "U3RvcEJ1ZmZGYWlsZWQQ/SoSFQoQTm90Rm91bmROaWNrTmFtZRDgKxIgChtO", + "b3RGb3VuZFRhdHRvb0F0dHJpYnV0ZURhdGEQqC0SEwoOTm90Rm91bmRTaG9w", + "SWQQjC4SGAoTVGltZU92ZXJGb3JQdXJjaGFzZRCNLhIXChJOb3RFbm91Z2hB", + "dHRyaWJ1dGUQji4SEgoNSW52YWxpZEdlbmRlchCPLhIQCgtJc0VxdWlwSXRl", + "bRCQLhIQCgtJbnZhbGlkSXRlbRCRLhIWChFBbHJlYWR5UmVnaXN0ZXJlZBCS", + "LhIVChBOb3RGb3VuZFNob3BJdGVtEJMuEhYKEU5vdEVub3VnaFNob3BJdGVt", + "EJQuEhUKEEludmFsaWRJdGVtQ291bnQQlS4SEgoNSW52ZW50b3J5RnVsbBCW", + "LhIWChFOb3RGb3VuZFByb2R1Y3RJZBCXLhIdChhSYW5kb21Cb3hJdGVtRGF0", + "YUludmFsaWQQ1C8SFgoRTm90RXhpc3RHYWNoYURhdGEQ1S9CLworY29tLmNh", + "bGl2ZXJzZS5hZG1pbi5kb21haW4uUmFiYml0TXEubWVzc2FnZVABYgZwcm90", + "bzM=")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ServerErrorCode), }, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Result), global::Result.Parser, new[]{ "ErrorCode", "ResultString" }, null, null, null, null) + })); + } + #endregion + +} +#region Enums +/// +/// 나중에 Error 카테고리별 범위 구성을 설정 해야 한다. - kangms +/// +public enum ServerErrorCode { + [pbr::OriginalName("Success")] Success = 0, + /// + ///============================================================================================= + /// 결과 코드 관련 오류 + ///============================================================================================= + /// + [pbr::OriginalName("ResultCodeNotSet")] ResultCodeNotSet = -1, + /// + ///============================================================================================= + /// 시스템 오류 : 10000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TryCatchException")] TryCatchException = 10001, + /// + /// DotNet 예외가 발생 했습니다. + /// + [pbr::OriginalName("DotNetException")] DotNetException = 10002, + /// + /// ProudNet 예외가 발생 했습니다. + /// + [pbr::OriginalName("ProudNetException")] ProudNetException = 10003, + /// + /// RabbitMQ 예외가 발생 했습니다. + /// + [pbr::OriginalName("RabbitMqException")] RabbitMqException = 10004, + /// + /// DynamoDB 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbException")] DynamoDbException = 10005, + /// + /// DynamoDB Transact 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbTransactException")] DynamoDbTransactException = 10006, + /// + /// Redis 예외가 발생 했습니다. + /// + [pbr::OriginalName("RedisException")] RedisException = 10007, + /// + /// Meta 스키마 및 데이터 예외가 발생 했습니다. + /// + [pbr::OriginalName("MetaInfoException")] MetaInfoException = 10008, + /// + /// MySqlDB 예외가 발생 했습니다. + /// + [pbr::OriginalName("MySqlDbException")] MySqlDbException = 10009, + /// + /// MongoDb 예외가 발생 했습니다. + /// + [pbr::OriginalName("MongoDbException")] MongoDbException = 10010, + /// + ///============================================================================================= + /// NLog 관련 오류 : 10030 ~ + ///============================================================================================= + /// + [pbr::OriginalName("NlogWithAwsCloudWatchSetupFailed")] NlogWithAwsCloudWatchSetupFailed = 10031, + /// + /// NLog 객체가 초기화되지 않은 상태 입니다. + /// + [pbr::OriginalName("NlogNotInitialized")] NlogNotInitialized = 10032, + /// + ///============================================================================================= + /// 비즈니스 로그 관련 오류 : 10050 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LogActionIsNull")] LogActionIsNull = 10051, + /// + /// LogAppender 객체가 Null 입니다. + /// + [pbr::OriginalName("LogAppenderIsNull")] LogAppenderIsNull = 10052, + /// + /// LogFormatter 객체가 Null 입니다. + /// + [pbr::OriginalName("LogFormatterIsNull")] LogFormatterIsNull = 10053, + /// + /// LogActionType 오류 입니다. + /// + [pbr::OriginalName("LogActionTypeInvalid")] LogActionTypeInvalid = 10054, + /// + ///============================================================================================= + /// 네트워크 오류 : 10100 ~ + ///============================================================================================= + /// ProudNet 오류 + /// + [pbr::OriginalName("RmiHostIsNull")] RmiHostIsNull = 10101, + /// + /// Rmi Host 핸들러에 바인딩을 실패 했습니다. + /// + [pbr::OriginalName("RmiHostHandlerBindFailed")] RmiHostHandlerBindFailed = 10102, + /// + /// Stub 핸들러에 바인딩을 실패 했습니다. + /// + [pbr::OriginalName("SubHandlerBindFailed")] SubHandlerBindFailed = 10103, + /// + /// Proxy 연결을 실패 했습니다. + /// + [pbr::OriginalName("ProxyAttachFailed")] ProxyAttachFailed = 10104, + /// + /// 패킷 최대 사이즈 설정 실패. + /// + [pbr::OriginalName("SetMessageMaxLengthFailed")] SetMessageMaxLengthFailed = 10105, + /// + /// 네트워크 모듈 오류 + /// + [pbr::OriginalName("PacketRecvHandlerRegisterFailed")] PacketRecvHandlerRegisterFailed = 10151, + /// + /// 패킷 송신 핸들러에 등록을 실패 했습니다. + /// + [pbr::OriginalName("PacketSendHandlerRegisterFailed")] PacketSendHandlerRegisterFailed = 10152, + /// + /// 패킷 오류 + /// + [pbr::OriginalName("PacketRecvInvalid")] PacketRecvInvalid = 10153, + /// + /// 수신 패킷 핸들러를 찾지 못했습니다. + /// + [pbr::OriginalName("RacketRecvHandlerNotFound")] RacketRecvHandlerNotFound = 10154, + /// + /// 대용량 패킷이 모두 수신되지 않았습니다. + /// + [pbr::OriginalName("LargePacketNotAllReceived")] LargePacketNotAllReceived = 10155, + /// + /// 대용량 패킷이 수신 대기 시간이 오래 지났다. + /// + [pbr::OriginalName("LargePacketRecvTimeOver")] LargePacketRecvTimeOver = 10156, + /// + ///============================================================================================= + /// DB 오류 : 10200 ~ + ///============================================================================================= + /// DB 공통 오류 + /// + [pbr::OriginalName("DbQueryTypeInvalid")] DbQueryTypeInvalid = 10201, + /// + /// DynamoDB 오류 + /// + [pbr::OriginalName("DynamoDbTransactionCanceledException")] DynamoDbTransactionCanceledException = 10211, + /// + /// DynamoDB AmazonDynamoDBException 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbAmazonDynamoDbException")] DynamoDbAmazonDynamoDbException = 10212, + /// + /// DynamoDB AmazonServiceException 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbAmazonServiceException")] DynamoDbAmazonServiceException = 10213, + /// + /// DynamoDB Config 파일 로딩을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbConfigLoadFailed")] DynamoDbConfigLoadFailed = 10214, + /// + /// DynamoDB 연결을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbConnectFailed")] DynamoDbConnectFailed = 10215, + /// + /// DynamoDB Table 생성을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbTableCreateFailed")] DynamoDbTableCreateFailed = 10216, + /// + /// DynamoDB Table과 연결이 되어있지 않습니다. + /// + [pbr::OriginalName("DynamoDbTableNotConnected")] DynamoDbTableNotConnected = 10217, + /// + /// DynamoDB Query를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbQueryFailed")] DynamoDbQueryFailed = 10218, + /// + /// DynamoDB Item 저장 크기를 초과 했습니다. + /// + [pbr::OriginalName("DynamoDbItemSizeExceeded")] DynamoDbItemSizeExceeded = 10219, + /// + /// DynamoDB Query와 일치하는 Attribute가 아닙니다. + /// + [pbr::OriginalName("DynamoDbQueryNoMatchAttribute")] DynamoDbQueryNoMatchAttribute = 10220, + /// + /// DynamoDB TransactionConflictException 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbTransactionConflictException")] DynamoDbTransactionConflictException = 10221, + /// + /// DynamoDB Expression 오류 입니다. + /// + [pbr::OriginalName("DynamoDbExpressionError")] DynamoDbExpressionError = 10222, + /// + /// DynamoDB PrimaryKey 를 찾을 수 없습니다. + /// + [pbr::OriginalName("DynamoDbPrimaryKeyNotFound")] DynamoDbPrimaryKeyNotFound = 10223, + /// + /// DynamoDB Table Name 오류 입니다. + /// + [pbr::OriginalName("DynamoDbTableNameInvalid")] DynamoDbTableNameInvalid = 10224, + /// + /// DynamoDB 연결 객체가 Null 입니다. + /// + [pbr::OriginalName("DynamoDbConnectorIsNull")] DynamoDbConnectorIsNull = 10225, + /// + /// DynamoDB Table 이름이 중복 되었습니다. + /// + [pbr::OriginalName("DynamoDbTableNameDuplicated")] DynamoDbTableNameDuplicated = 10226, + /// + /// DynamoDB 쿼리 오류 + /// + [pbr::OriginalName("DynamoDbQueryException")] DynamoDbQueryException = 10231, + /// + /// DynamoDbQuery Request를 가지고 있지 않습니다. + /// + [pbr::OriginalName("DynamoDbQueryNoRequested")] DynamoDbQueryNoRequested = 10232, + /// + /// DynamoDbQuery Document를 찾을 수 없습니다. + /// + [pbr::OriginalName("DynamoDbQueryNotFoundDocumentQuery")] DynamoDbQueryNotFoundDocumentQuery = 10233, + /// + /// DynamoDbQuery ExceptionNotifier를 찾을 수 없습니다. + /// + [pbr::OriginalName("DynamoDbQueryExceptionNotifierNotFound")] DynamoDbQueryExceptionNotifierNotFound = 10234, + /// + /// DynamoDB Document 오류 + /// + [pbr::OriginalName("DynamoDbDocumentIsNullInQueryContext")] DynamoDbDocumentIsNullInQueryContext = 10241, + /// + /// DynamoDbDocument내의 정보에 오류가 있습니다. + /// + [pbr::OriginalName("DynamoDbDocumentIsInvalid")] DynamoDbDocumentIsInvalid = 10242, + /// + /// DynamoDbDocumentQueryContext내의 Type 정보 오류 입니다. + /// + [pbr::OriginalName("DynamoDbDocumentQueryContextTypeInvalid")] DynamoDbDocumentQueryContextTypeInvalid = 10243, + /// + /// DynamoDbDocument를 Doc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocumentCopyFailedToDoc")] DynamoDbDocumentCopyFailedToDoc = 10244, + /// + /// DynamoDbDocument Upsert를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocumentUpsertFailed")] DynamoDbDocumentUpsertFailed = 10245, + /// + /// DynamoDB ItemRequest 오류 + /// + [pbr::OriginalName("DynamoDbItemRequestIsInvalid")] DynamoDbItemRequestIsInvalid = 10251, + /// + /// DynamoDbItemRequestQueryContext내의 Type 정보 오류 입니다. + /// + [pbr::OriginalName("DynamoDbItemRequestQueryContextTypeInvalid")] DynamoDbItemRequestQueryContextTypeInvalid = 10252, + /// + /// DynamoDbItemRequest내에 UpdateExpression 정보가 비어 있다. + /// + [pbr::OriginalName("DynamoDbItemRequestUpdateExpressionEmpty")] DynamoDbItemRequestUpdateExpressionEmpty = 10253, + /// + /// DynamoDB Custom Doc 오류 + /// + [pbr::OriginalName("DynamoDbDocPkInvalid")] DynamoDbDocPkInvalid = 10261, + /// + /// DynamoDbDoc의 SK 값이 오류 입니다. + /// + [pbr::OriginalName("DynamoDbDocSkInvalid")] DynamoDbDocSkInvalid = 10262, + /// + /// DynamoDbDoc의 AttribType이 중복 되었습니다. + /// + [pbr::OriginalName("DynamoDbDocAttribTypeDuplicated")] DynamoDbDocAttribTypeDuplicated = 10263, + /// + /// Doc를 DynamoDbDocument에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyFailedToDocument")] DynamoDbDocCopyFailedToDocument = 10264, + /// + /// DynamoDbDocument를 Doc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyFailedFromDocument")] DynamoDbDocCopyFailedFromDocument = 10265, + /// + /// DynamoDbDocType이 일치하지 않습니다. + /// + [pbr::OriginalName("DynamoDbDocTypeNotMatch")] DynamoDbDocTypeNotMatch = 10266, + /// + /// DynamoDbRequest 오류 입니다. + /// + [pbr::OriginalName("DynamoDbRequestInvalid")] DynamoDbRequestInvalid = 10267, + /// + /// DynamoDbDoc AttributeState 플래그를 선택하지 않았습니다. + /// + [pbr::OriginalName("DynamoDbDocAttributeStateNotSet")] DynamoDbDocAttributeStateNotSet = 10268, + /// + /// DynamoDbDoc AttribWrapper 복사를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocAttribWrapperCopyFailed")] DynamoDbDocAttribWrapperCopyFailed = 10269, + /// + /// DynamoDbDoc Attribute 가져오기를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocAttributeGettingFailed")] DynamoDbDocAttributeGettingFailed = 10270, + /// + /// DynamoDbDoc LinkPKSK 값 오류 입니다. + /// + [pbr::OriginalName("DynamoDbDocLinkPkSkInvalid")] DynamoDbDocLinkPkSkInvalid = 10271, + /// + /// DynamoDbDoc AttribWrapper가 Null 입니다. + /// + [pbr::OriginalName("DynamoDbDocAttribWrapperIsNull")] DynamoDbDocAttribWrapperIsNull = 10272, + /// + /// MySql 오류 + /// + [pbr::OriginalName("MySqlConnectionCreateFailed")] MySqlConnectionCreateFailed = 10281, + /// + /// MySqlConnection Open을 실패 했습니다. + /// + [pbr::OriginalName("MySqlConnectionOpenFailed")] MySqlConnectionOpenFailed = 10282, + /// + /// MySql 쿼리 오류 + /// + [pbr::OriginalName("MySqlDbQueryException")] MySqlDbQueryException = 10283, + /// + /// MongoDb 오류 + /// + [pbr::OriginalName("MongoDbInitAndVefifyDbFailed")] MongoDbInitAndVefifyDbFailed = 10291, + /// + ///============================================================================================= + /// Cache 오류 : 10300 ~ + ///============================================================================================= + /// Redis 오류 + /// + [pbr::OriginalName("RedisServerConnectFailed")] RedisServerConnectFailed = 10301, + /// + /// Redis Strings 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisStringsWriteFailed")] RedisStringsWriteFailed = 10302, + /// + /// Redis Strings 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisStringsReadFailed")] RedisStringsReadFailed = 10303, + /// + /// Redis Sets 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSetsWriteFailed")] RedisSetsWriteFailed = 10304, + /// + /// Redis Sets 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSetsReadFailed")] RedisSetsReadFailed = 10305, + /// + /// Redis SortedSets 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSortedSetsWriteFailed")] RedisSortedSetsWriteFailed = 10306, + /// + /// Redis SortedSets 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSortedSetsReadFailed")] RedisSortedSetsReadFailed = 10307, + /// + /// Redis Hashes 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisHashesWriteFailed")] RedisHashesWriteFailed = 10308, + /// + /// Redis Hashes 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisHashesReadFailed")] RedisHashesReadFailed = 10309, + /// + /// Redis Lists 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisListsWriteFailed")] RedisListsWriteFailed = 10310, + /// + /// Redis Lists 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisListsReadFailed")] RedisListsReadFailed = 10311, + /// + /// Redis Request의 Key 값이 없습니다. + /// + [pbr::OriginalName("RedisRequestKeyIsEmpty")] RedisRequestKeyIsEmpty = 10312, + /// + /// Redis에서 LoginCache 정보 조회를 실패했습니다. + /// + [pbr::OriginalName("RedisLoginCacheGetFailed")] RedisLoginCacheGetFailed = 10313, + /// + /// Redis에 LoginCache 정보를 저장을 실패했습니다. + /// + [pbr::OriginalName("RedisLoginCacheSetFailed")] RedisLoginCacheSetFailed = 10314, + /// + /// RedisPrivateCache가 중복 등록 되었습니다. + /// + [pbr::OriginalName("RedisPrivateCacheDuplicated")] RedisPrivateCacheDuplicated = 10315, + /// + /// RedisGlobalSharedCache가 중복 등록 되었습니다. + /// + [pbr::OriginalName("RedisGlobalSharedCacheDuplicated")] RedisGlobalSharedCacheDuplicated = 10316, + /// + /// RedisLoginCache Owner UserGuid가 일치하지 않습니다. + /// + [pbr::OriginalName("RedisLoginCacheOwnerUserGuidNotMatch")] RedisLoginCacheOwnerUserGuidNotMatch = 10317, + /// + /// Redis PartyCache 읽기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyCacheGetFailed")] RedisGlobalPartyCacheGetFailed = 10318, + /// + /// Redis PartyCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyCacheWriteFailed")] RedisGlobalPartyCacheWriteFailed = 10319, + /// + /// Redis PartyMemberCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyMemberCacheWriteFailed")] RedisGlobalPartyMemberCacheWriteFailed = 10320, + /// + /// Redis PartyServerCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyServerCacheWriteFailed")] RedisGlobalPartyServerCacheWriteFailed = 10321, + /// + /// Redis PartyInvitePartySendCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyInvitePartySendCacheWriteFailed")] RedisGlobalPartyInvitePartySendCacheWriteFailed = 10322, + /// + /// Redis에서 InstanceRoomInfoCache 정보 조회를 실패했습니다. + /// + [pbr::OriginalName("RedisInstanceRoomInfoCacheGetFailed")] RedisInstanceRoomInfoCacheGetFailed = 10323, + /// + /// Redis 에서 누적 랭킹 데이터 저정에 실패했습니다. + /// + [pbr::OriginalName("RedisUgcNpcTotalRankCacheWriteFailed")] RedisUgcNpcTotalRankCacheWriteFailed = 10324, + /// + /// RedisRequestHandler를 찾을 수 없습니다. + /// + [pbr::OriginalName("RedisRequestHandlerNotFound")] RedisRequestHandlerNotFound = 10325, + /// + ///============================================================================================= + /// Message Queue 오류 : 10400 ~ + ///============================================================================================= + /// RabbitMQ 오류 + /// + [pbr::OriginalName("RabbitMqConsumerStartFailed")] RabbitMqConsumerStartFailed = 10401, + /// + /// RabbitMQ 연결을 실패 했습니다. + /// + [pbr::OriginalName("RabbitMqConnectFailed")] RabbitMqConnectFailed = 10402, + /// + /// RabbitMQ 메시지 시간이 오래 됐습니다. + /// + [pbr::OriginalName("RabbitMessageTimeOld")] RabbitMessageTimeOld = 10403, + /// + /// RabbitMQ 채널 생성을 실패 했습니다. + /// + [pbr::OriginalName("RabbitMqChannelCreateFailed")] RabbitMqChannelCreateFailed = 10404, + /// + ///============================================================================================= + /// S3 오류 : 10500 ~ + ///============================================================================================= + /// S3 오류 + /// + [pbr::OriginalName("S3ClientCreateFailed")] S3ClientCreateFailed = 10501, + /// + /// S3 Bucket 생성을 실패 했습니다. + /// + [pbr::OriginalName("S3BucketCreateFailed")] S3BucketCreateFailed = 10502, + /// + /// S3 File Upload를 실패 했습니다. + /// + [pbr::OriginalName("S3FileUploadFailed")] S3FileUploadFailed = 10503, + /// + /// S3 File Delete에 실패 했습니다. + /// + [pbr::OriginalName("S3FileDeleteFailed")] S3FileDeleteFailed = 10504, + /// + /// S3 File Get에 실패 했습니다. + /// + [pbr::OriginalName("S3FileGetFailed")] S3FileGetFailed = 10505, + /// + ///============================================================================================= + /// Meta 스키마 및 데이터 기반 오류 : 10550 ~ + ///============================================================================================= + /// 스키마 오류 + /// 데이터 오류 + /// + [pbr::OriginalName("MetaDataLoadFailed")] MetaDataLoadFailed = 10551, + /// + /// Meta 데이터 오류 입니다. + /// + [pbr::OriginalName("InvalidMetaData")] InvalidMetaData = 10552, + /// + /// Meta Id 오류 입니다. + /// + [pbr::OriginalName("MetaIdInvalid")] MetaIdInvalid = 10553, + /// + ///============================================================================================= + /// 기타 라이브러리 오류 : 10610 ~ + ///============================================================================================= + /// Json 오류 + /// + [pbr::OriginalName("JsonTypeInvalid")] JsonTypeInvalid = 10611, + /// + /// JsonConvert Deserialize 오류 입니다. + /// + [pbr::OriginalName("JsonConvertDeserializeFailed")] JsonConvertDeserializeFailed = 10612, + /// + ///============================================================================================= + /// 서버 공통 오류 : 10700 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ServerConfigFileNotFound")] ServerConfigFileNotFound = 10701, + /// + /// 서버 타입 오류 입니다. + /// + [pbr::OriginalName("ServerTypeInvalid")] ServerTypeInvalid = 10702, + /// + /// 해당 리슨포트로 이미 실행중인 프로세스가 있습니다. + /// + [pbr::OriginalName("AlreadyRunningServerWithListenPort")] AlreadyRunningServerWithListenPort = 10703, + /// + /// 캐시 스토리지를 찾을 수 없습니다. + /// + [pbr::OriginalName("NotFoundCacheStorage")] NotFoundCacheStorage = 10704, + /// + /// 함수 파라메터중에 Null 값이 있어서 오류 입니다. + /// + [pbr::OriginalName("FunctionParamNull")] FunctionParamNull = 10705, + /// + /// 함수 파라메터 오류 입니다. + /// + [pbr::OriginalName("FunctionInvalidParam")] FunctionInvalidParam = 10706, + /// + /// 클라이언트 리슨 포트 오류 입니다. + /// + [pbr::OriginalName("ClientListenPortInvalid")] ClientListenPortInvalid = 10707, + /// + /// Interface 를 Override 하지 않았습니다. + /// + [pbr::OriginalName("NotOverrideInterface")] NotOverrideInterface = 10708, + /// + /// 서버 실행후 대기를 실패 했습니다. + /// + [pbr::OriginalName("ServerOnRunningFailed")] ServerOnRunningFailed = 10709, + /// + /// 서비스 타입 오류 입니다. + /// + [pbr::OriginalName("ServiceTypeInvalid")] ServiceTypeInvalid = 10710, + /// + /// 함수를 구현하지 않았습니다. + /// + [pbr::OriginalName("FunctionNotImplemented")] FunctionNotImplemented = 10711, + /// + /// Interface를 상속받아 구현하지 않았습니다. + /// + [pbr::OriginalName("ClassDoesNotImplementInterfaceInheritance")] ClassDoesNotImplementInterfaceInheritance = 10712, + /// + /// 정책 타입이 중복 되었습니다. + /// + [pbr::OriginalName("RuleTypeDuplicated")] RuleTypeDuplicated = 10713, + /// + /// ClassType 형변환은 null 입니다. + /// + [pbr::OriginalName("ClassTypeCastIsNull")] ClassTypeCastIsNull = 10714, + /// + /// 이미 등록된 PeriodicTask 입니다. + /// + [pbr::OriginalName("PeriodicTaskAlreadyRegistered")] PeriodicTaskAlreadyRegistered = 10715, + /// + /// 이미 등록된 EntityTicker 입니다. + /// + [pbr::OriginalName("EntityTickerAlreadyRegistered")] EntityTickerAlreadyRegistered = 10716, + /// + /// EntityTicker를 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityTickerNotFound")] EntityTickerNotFound = 10717, + /// + /// EntityBase를 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityBaseNotFound")] EntityBaseNotFound = 10718, + /// + /// 부합하는 서버가 없습니다. + /// + [pbr::OriginalName("ValidServerNotFound")] ValidServerNotFound = 10719, + /// + /// 해당 서버에 유저가 가득찼습니다. + /// + [pbr::OriginalName("TargetServerUserCountExceed")] TargetServerUserCountExceed = 10720, + /// + /// 해당 유저를 찾을 수 없습니다. + /// + [pbr::OriginalName("TargetUserNotFound")] TargetUserNotFound = 10721, + /// + /// 대상이 접속중이지 않습니다. + /// + [pbr::OriginalName("TargetUserNotLogIn")] TargetUserNotLogIn = 10722, + /// + /// 맵에서 찾을 수 없습니다. + /// + [pbr::OriginalName("NotExistMap")] NotExistMap = 10723, + /// + /// 조건에 맞지 않아 입장 예약을 실패했습니다. + /// + [pbr::OriginalName("FailedToReserveEnterCondition")] FailedToReserveEnterCondition = 10724, + /// + /// 입장 예약에 실패했습니다. + /// + [pbr::OriginalName("FailedToReservationEnter")] FailedToReservationEnter = 10725, + /// + /// OwnerEntityType 오류 입니다. + /// + [pbr::OriginalName("OwnerEntityTypeInvalid")] OwnerEntityTypeInvalid = 10726, + /// + /// OwnerEntity 정보를 채울 수 없습니다. + /// + [pbr::OriginalName("OwnerEntityCannotFillup")] OwnerEntityCannotFillup = 10727, + /// + /// Owner Guid 오류 입니다. + /// + [pbr::OriginalName("OwnerGuidInvalid")] OwnerGuidInvalid = 10728, + /// + /// DailyTimeEvent 등록 오류입니다. + /// + [pbr::OriginalName("DailyTimeEventAdditionFailed")] DailyTimeEventAdditionFailed = 10729, + /// + /// 프로그램 VersionPath 토큰을 찾을 수 없습니다. + /// + [pbr::OriginalName("ProgramVersionPathTokenNotFound")] ProgramVersionPathTokenNotFound = 10730, + /// + /// 현재 처리중 입니다. + /// + [pbr::OriginalName("CurrentlyProcessingState")] CurrentlyProcessingState = 10731, + /// + /// ServerUrlType 오류 입니다. + /// + [pbr::OriginalName("ServerUrlTypeInvalid")] ServerUrlTypeInvalid = 10732, + /// + /// ServerUrlType 이미 등록되어 있습니다. + /// + [pbr::OriginalName("ServerUrlTypeAlreadyRegistered")] ServerUrlTypeAlreadyRegistered = 10733, + /// + /// 서버 Offline 모드가 활성화 상태 입니다. + /// + [pbr::OriginalName("ServerOfflineModeEnable")] ServerOfflineModeEnable = 10734, + /// + /// Owner 정보 오류 입니다. + /// + [pbr::OriginalName("OwnerInvalid")] OwnerInvalid = 10735, + /// + /// 이미 등록된 Module 입니다. + /// + [pbr::OriginalName("ModuleAlreadyRegistred")] ModuleAlreadyRegistred = 10736, + /// + /// 이미 추가된 초기화용 Module 입니다. + /// + [pbr::OriginalName("ModuleAlreadyAddInitializeModule")] ModuleAlreadyAddInitializeModule = 10737, + /// + /// Moudle을 찾을 수 없습니다. + /// + [pbr::OriginalName("ModuleNotFound")] ModuleNotFound = 10738, + /// + /// 프로그램 Version 읽기를 실패 했습니다. + /// + [pbr::OriginalName("ProgramVersionLoadFailed")] ProgramVersionLoadFailed = 10739, + /// + /// 서버가 이미 실행중 입니다. + /// + [pbr::OriginalName("ServerAlreadyRunning")] ServerAlreadyRunning = 10740, + /// + ///============================================================================================= + /// 데이터 변환 및 복사 오류 : 10850 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MetaDataCopyToDynamoDbDocFailed")] MetaDataCopyToDynamoDbDocFailed = 10850, + /// + /// Meta 데이터를 EntityAttribute에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("MetaDataCopyToEntityAttributeFailed")] MetaDataCopyToEntityAttributeFailed = 10851, + /// + /// DynamoDbDoc를 Cache에 복사하는 것을 실패 헀습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyToCacheFailed")] DynamoDbDocCopyToCacheFailed = 10852, + /// + /// DynamoDbDoc를 EntityAttribute에 복사하는 것을 실패 헀습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyToEntityAttributeFailed")] DynamoDbDocCopyToEntityAttributeFailed = 10853, + /// + /// Cache를 EntityAttrib에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("CacheCopyToEntityAttributeFailed")] CacheCopyToEntityAttributeFailed = 10854, + /// + /// Cache를 DynamoDbDoc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("CacheCopyToDynamoDbDocFailed")] CacheCopyToDynamoDbDocFailed = 10855, + /// + /// EntityAttribute를 Cache에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCopyToCacheFailed")] EntityAttributeCopyToCacheFailed = 10856, + /// + /// EntityAttribute를 DynamoDbDoc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCopyToDynamoDbDocFailed")] EntityAttributeCopyToDynamoDbDocFailed = 10857, + /// + /// EntityAttribute를 EntityAttributeTransactor에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCopyToEntityAttributeTransactorFailed")] EntityAttributeCopyToEntityAttributeTransactorFailed = 10858, + /// + /// EntityAttributeTransactor를 EntityAttribute에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeTransactorCopyToEntityAttributeFailed")] EntityAttributeTransactorCopyToEntityAttributeFailed = 10859, + /// + /// EntityAttributeTransactor를 DynamoDbDoc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeTransactorCopyToDynamoDbDocFailed")] EntityAttributeTransactorCopyToDynamoDbDocFailed = 10860, + /// + /// Attrib를 찾을 수 없습니다. + /// + [pbr::OriginalName("AttribNotFound")] AttribNotFound = 10861, + /// + /// Attrib Path 구성을 실패 했습니다. + /// + [pbr::OriginalName("AttribPathMakeFailed")] AttribPathMakeFailed = 10862, + /// + /// EntityAttribute Casting을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCastFailed")] EntityAttributeCastFailed = 10863, + /// + /// 문자열을 Enum으로 변환하는 것을 실패 했습니다. + /// + [pbr::OriginalName("StringConvertToEnumFailed")] StringConvertToEnumFailed = 10864, + /// + ///============================================================================================= + /// 프로그램 버전 오류 : 10900 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MetaSchemaVersionNotMatch")] MetaSchemaVersionNotMatch = 10901, + /// + /// 메타 데이터 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("MetaDataVersionNotMatch")] MetaDataVersionNotMatch = 10902, + /// + /// 패킷 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("PacketVersionNotMatch")] PacketVersionNotMatch = 10903, + /// + /// 클라이언트 로직 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("ClientLogicVersionNotMatch")] ClientLogicVersionNotMatch = 10904, + /// + /// 리소스 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("ResourceVersionNotMatch")] ResourceVersionNotMatch = 10905, + /// + /// ClientProgramVersion 정보 null 입니다. + /// + [pbr::OriginalName("ClientProgramVersionIsNull")] ClientProgramVersionIsNull = 10906, + /// + ///============================================================================================= + /// 계정 인증 오류 : 11000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TestIdNotAllow")] TestIdNotAllow = 11001, + /// + /// Bot 계정은 허용되지 않습니다. + /// + [pbr::OriginalName("BotdNotAllow")] BotdNotAllow = 11002, + /// + /// 계정 id 길이가 짧습니다. + /// + [pbr::OriginalName("AccountIdLengthShort")] AccountIdLengthShort = 11003, + /// + /// 통합인증Db에서 Id를 찾을 수 없습니다. + /// + [pbr::OriginalName("AccountIdNotFoundInSsoAccountDb")] AccountIdNotFoundInSsoAccountDb = 11004, + /// + /// 테스트 계정으로 Meta 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("MetaDataNotFoundByTestUserId")] MetaDataNotFoundByTestUserId = 11005, + /// + /// 계정 비밀번호가 일치하지 않습니다. + /// + [pbr::OriginalName("AccountPasswordNotMatch")] AccountPasswordNotMatch = 11006, + /// + /// UserData 정보를 AccountAttr 정보로 변환을 실패 했습니다. + /// + [pbr::OriginalName("UserDataConvertToAccountAttrFailed")] UserDataConvertToAccountAttrFailed = 11007, + /// + /// AccountBaseAttrib 정보를 DB에 추가를 실패 했습니다. + /// + [pbr::OriginalName("AccountBaseAttribInsertDbFailed")] AccountBaseAttribInsertDbFailed = 11008, + /// + /// 접속 가능한 서버가 없습니다. + /// + [pbr::OriginalName("NoServerConnectable")] NoServerConnectable = 11009, + /// + /// 접속 제재 처리된 계정입니다. + /// + [pbr::OriginalName("BlockedAccount")] BlockedAccount = 11010, + /// + /// 통합계정인증과 런처 로그인을 허용하지 않습니다. + /// + [pbr::OriginalName("SsoAccountAuthWithLauncherLoginNotAllow")] SsoAccountAuthWithLauncherLoginNotAllow = 11011, + /// + /// 클라이언트 단독 로그인을 허용하지 않습니다. + /// + [pbr::OriginalName("ClientStandaloneLoginNotAllow")] ClientStandaloneLoginNotAllow = 11012, + /// + /// 접속 허용이 되지 않는 PlatformType 입니다. + /// + [pbr::OriginalName("PlatformTypeNotAllow")] PlatformTypeNotAllow = 11013, + /// + /// 통합계정DB에서 계정 정보를 읽지 못했습니다. + /// + [pbr::OriginalName("AccountCanNotReadFromSsoAccountDb")] AccountCanNotReadFromSsoAccountDb = 11014, + /// + /// 통합계정인증 JWT 체크를 실패 했습니다. + /// + [pbr::OriginalName("SsoAccountAuthJwtCheckFailed")] SsoAccountAuthJwtCheckFailed = 11015, + /// + /// 통합계정인증 JWT 안에 UserId Key 정보가 없습니다. + /// + [pbr::OriginalName("UserIdKeyNotFoundInSsoAccountAuthJwt")] UserIdKeyNotFoundInSsoAccountAuthJwt = 11016, + /// + /// 통합계정인증 JWT 안에 UserId Value 정보가 없습니다. + /// + [pbr::OriginalName("UserIdValueEmptyInSsoAccountAuthJwt")] UserIdValueEmptyInSsoAccountAuthJwt = 11017, + /// + /// 통합계정인증 JWT 안에 AccountType Key 정보가 없습니다. + /// + [pbr::OriginalName("AccountTypeKeyNotFoundInSsoAccountAuthJwt")] AccountTypeKeyNotFoundInSsoAccountAuthJwt = 11018, + /// + /// 통합계정인증 JWT 안에 AccountType Value 정보가 허용된 AccountType이 아닙니다. + /// + [pbr::OriginalName("AccountTypeValueNotAllowInSsoAccountAuthJwt")] AccountTypeValueNotAllowInSsoAccountAuthJwt = 11019, + /// + /// 메타버스Db에서 AccountBaseDoc를 찾을 수 없습니다. + /// + [pbr::OriginalName("AccountBaseDocNotFoundInMetaverseDb")] AccountBaseDocNotFoundInMetaverseDb = 11020, + /// + /// 통합계정인증 JWT 안에 AssessToken Key 정보가 없습니다. + /// + [pbr::OriginalName("AccessTokenKeyNotAllowInSsoAccountAuthJwt")] AccessTokenKeyNotAllowInSsoAccountAuthJwt = 11021, + /// + /// 통합인증Db의 AccessToken과 일치하지 않습니다. + /// + [pbr::OriginalName("AccessTokenNotMatchInSsoAccountDb")] AccessTokenNotMatchInSsoAccountDb = 11022, + /// + /// 현재의 AccountType은 허용되지 않습니다. + /// + [pbr::OriginalName("AccountTypeNotAllow")] AccountTypeNotAllow = 11023, + /// + /// AccountBaseDoc가 로드되지 않았습니다. + /// + [pbr::OriginalName("AccountBaseDocNotLoad")] AccountBaseDocNotLoad = 11024, + /// + /// 권한이 부족합니다. + /// + [pbr::OriginalName("NotEnoughAuthority")] NotEnoughAuthority = 11054, + /// + /// AccountBaseDoc이 null 입니다. + /// + [pbr::OriginalName("AccountBaseDocIsNull")] AccountBaseDocIsNull = 11055, + /// + /// Account Id 오류 입니다. + /// + [pbr::OriginalName("AccountIdInvalid")] AccountIdInvalid = 11056, + /// + /// Account에 UserGuid가 없습니다. + /// + [pbr::OriginalName("AccountWithoutUserGuid")] AccountWithoutUserGuid = 11057, + /// + /// 통합계정인증 JWT 이 기간만료 입니다. + /// + [pbr::OriginalName("SsoAccountAuthJwtTokenExpired")] SsoAccountAuthJwtTokenExpired = 11058, + /// + /// 통합계정인증 JWT 예외가 발생 했습니다. + /// + [pbr::OriginalName("SsoAccountAuthJwtException")] SsoAccountAuthJwtException = 11059, + /// + ///============================================================================================= + /// 엔티티 트랜잭션 관련 오류 : 11200 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TransactionRunnerAlreadyRunning")] TransactionRunnerAlreadyRunning = 11201, + /// + /// TransactionRunner를 찾을 수 없습니다. + /// + [pbr::OriginalName("TransactionRunnerNotFound")] TransactionRunnerNotFound = 11202, + /// + ///============================================================================================= + /// 엔티티 속성 관련 오류 : 11300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EntityGuidInvalid")] EntityGuidInvalid = 11301, + /// + /// EntityAttrib 중복 등록 되었습니다. + /// + [pbr::OriginalName("EntityAttribDuplicated")] EntityAttribDuplicated = 11302, + /// + /// EntityAttribute가 null 입니다. + /// + [pbr::OriginalName("EntityAttributeIsNull")] EntityAttributeIsNull = 11303, + /// + /// EntityAttribute 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityAttributeNotFound")] EntityAttributeNotFound = 11304, + /// + /// EntityAttribute 상태 오류 입니다. + /// + [pbr::OriginalName("EntityAttributeStateInvalid")] EntityAttributeStateInvalid = 11305, + /// + /// EntityType 오류 입니다. + /// + [pbr::OriginalName("EntityTypeInvalid")] EntityTypeInvalid = 11306, + /// + /// Entity가 Map에 연결되어 있습니다. + /// + [pbr::OriginalName("EntityLinkedToMap")] EntityLinkedToMap = 11307, + /// + /// Entity가 Map에 연결되어 있지 않습니다. + /// + [pbr::OriginalName("EntityNotLinkedToMap")] EntityNotLinkedToMap = 11308, + /// + /// 현재 dence 상태가 아닙니다. dance end 패킷이 날라올때 처리 + /// + [pbr::OriginalName("EntityStateNotDancing")] EntityStateNotDancing = 11309, + /// + ///============================================================================================= + /// 엔티티 얙션 관련 오류 : 11400 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EntityActionDuplicated")] EntityActionDuplicated = 11401, + /// + /// EntityAction을 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityActionNotFound")] EntityActionNotFound = 11402, + /// + ///============================================================================================= + /// 엔티티 상태 관련 오류 : 11500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EntityBaseHfsmInitFailed")] EntityBaseHfsmInitFailed = 11501, + /// + ///============================================================================================= + /// 글로벌 엔티티 오류 : 11600 ~ + ///============================================================================================= + /// + [pbr::OriginalName("RedisGlobalEntityDuplicated")] RedisGlobalEntityDuplicated = 11601, + /// + ///============================================================================================= + /// 접속 서버 변경 관련 오류 : 11700 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UserIsSwitchingServer")] UserIsSwitchingServer = 11701, + /// + /// 유저가 다른 서버로 접속을 변경하고 있지 않습니다. + /// + [pbr::OriginalName("UserIsNotSwitchingServer")] UserIsNotSwitchingServer = 11702, + /// + /// 접속 서버 변경 Otp 값이 일치하지 않습니다. + /// + [pbr::OriginalName("ServerSwitchingOtpNotMatch")] ServerSwitchingOtpNotMatch = 11703, + /// + /// 접속된 서버는 목적지 서버가 아닙니다. + /// + [pbr::OriginalName("ConnectedServerIsNotDestServer")] ConnectedServerIsNotDestServer = 11704, + /// + /// 예약된 유저가 아닙니다. + /// + [pbr::OriginalName("InvalidReservationUser")] InvalidReservationUser = 11705, + /// + /// 복귀 유저가 아닙니다. + /// + [pbr::OriginalName("InvalidReturnUser")] InvalidReturnUser = 11706, + /// + ///============================================================================================= + /// 유저 관련 오류 : 12000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UserNicknameNotAllowWithSpecialChars")] UserNicknameNotAllowWithSpecialChars = 12001, + /// + /// 유저 닉네임은 한글로 최소2 에서 최대8 글자까지 허용 합니다. + /// + [pbr::OriginalName("UserNicknameAllowedMin2ToMax8WithKorean")] UserNicknameAllowedMin2ToMax8WithKorean = 12002, + /// + /// 유저 닉네임은 영어로 최소4 에서 최대16 글자까지 허용 합니다. + /// + [pbr::OriginalName("UserNicknameAllowedMin4ToMax16WithEnglish")] UserNicknameAllowedMin4ToMax16WithEnglish = 12003, + /// + /// 유저 닉네임은 첫번째 글자에 숫자를 허용하지 않습니다. + /// + [pbr::OriginalName("UserNicknameNotAllowedNumberAtFirstChars")] UserNicknameNotAllowedNumberAtFirstChars = 12004, + /// + /// 유저 닉네임으로 허용되지 않는 문자 입니다. + /// + [pbr::OriginalName("UserNicknameNotAllowChars")] UserNicknameNotAllowChars = 12005, + /// + /// 유저 닉네임으로 한글 초성체를 허용하지 않습니다. + /// + [pbr::OriginalName("UserNicknameNotAllowWithInitialismKorean")] UserNicknameNotAllowWithInitialismKorean = 12006, + /// + /// 유저 닉네임이 금지어에 해당 합니다. + /// + [pbr::OriginalName("UserNicknameBan")] UserNicknameBan = 12007, + /// + /// 유저 중복 로그인 입니다. + /// + [pbr::OriginalName("UserDuplicatedLogin")] UserDuplicatedLogin = 12008, + /// + /// 유저가 로그인되어 있지 않습니다. + /// + [pbr::OriginalName("UserNotLogin")] UserNotLogin = 12009, + /// + /// 유저 생성을 위한 DynamoDbDoc가 중복 등록 되었습니다. + /// + [pbr::OriginalName("UserCreationForDynamoDbDocDuplicated")] UserCreationForDynamoDbDocDuplicated = 12010, + /// + /// UserGuid를 참조하는 모든 Attribute에 Guid를 적용하는 것을 실패했습니다. + /// + [pbr::OriginalName("UserGuidApplyToRefAttributeAllFailed")] UserGuidApplyToRefAttributeAllFailed = 12011, + /// + /// TestUserPrepareCreate 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("TestUserPrepareCreateNotCompleted")] TestUserPrepareCreateNotCompleted = 12012, + /// + /// DefaultUserPrepareCreate 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("DefaultUserPrepareCreateNotCompleted")] DefaultUserPrepareCreateNotCompleted = 12013, + /// + /// UserPrepareLoad 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("UserPrepareLoadNotCompleted")] UserPrepareLoadNotCompleted = 12014, + /// + /// 유저 닉네임이 생성되어 있지 않습니다. + /// + [pbr::OriginalName("UserNicknameNotCreated")] UserNicknameNotCreated = 12015, + /// + /// 유저 닉네임을 이미 생성 했습니다. + /// + [pbr::OriginalName("UserNicknameAlreadyCreated")] UserNicknameAlreadyCreated = 12016, + /// + /// 유저 생성 절차가 완료되지 않았습니다. + /// + [pbr::OriginalName("UserCreateStepNotCompleted")] UserCreateStepNotCompleted = 12017, + /// + /// 유저 생성이 완료 되었습니다. + /// + [pbr::OriginalName("UserCreateCompleted")] UserCreateCompleted = 12018, + /// + /// 유저 서브키 바인딩을 실패 했습니다. + /// + [pbr::OriginalName("UserSubKeyBindToFailed")] UserSubKeyBindToFailed = 12019, + /// + /// 유저 서브키 변경을 실패 했습니다. + /// + [pbr::OriginalName("UserSubKeyReplaceFailed")] UserSubKeyReplaceFailed = 12020, + /// + /// 유저 Guid 오류 입니다. + /// + [pbr::OriginalName("UserGuidInvalid")] UserGuidInvalid = 12021, + /// + /// UserNicknameDoc이 null 입니다. + /// + [pbr::OriginalName("UserNicknameDocIsNull")] UserNicknameDocIsNull = 12022, + /// + /// UserDoc이 null 입니다. + /// + [pbr::OriginalName("UserDocIsNull")] UserDocIsNull = 12023, + /// + /// UserGuid가 이미 등록되어 있습니다. + /// + [pbr::OriginalName("UserGuidAlreadyAdded")] UserGuidAlreadyAdded = 12024, + /// + /// User 닉네임이 중복 되었습니다. + /// + [pbr::OriginalName("UserNicknameDuplicated")] UserNicknameDuplicated = 12025, + /// + /// 유저 닉네임은 최소2 에서 최대12 글자까지 허용 합니다. + /// + [pbr::OriginalName("UserNicknameAllowedMin2ToMax12")] UserNicknameAllowedMin2ToMax12 = 12026, + /// + /// UserNickname 검색 페이지가 잘못되었습니다. + /// + [pbr::OriginalName("UserNicknameSearchPageWrong")] UserNicknameSearchPageWrong = 12027, + /// + /// 유저 닉네임이 없습니다. + /// + [pbr::OriginalName("UserNicknameEmpty")] UserNicknameEmpty = 12028, + /// + /// UserContentsSettingDoc이 null 입니다. + /// + [pbr::OriginalName("UserContentsSettingDocIsNull")] UserContentsSettingDocIsNull = 12029, + /// + /// 유저 MoneyDoc이 없습니다. + /// + [pbr::OriginalName("UserMoneyDocEmpty")] UserMoneyDocEmpty = 12030, + /// + ///============================================================================================= + /// 유저 신고하기 관련 오류 : 12100 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UserReportInvalidTitleLength")] UserReportInvalidTitleLength = 12101, + /// + /// 유저 신고하기 의 내용 길이 오류입니다. + /// + [pbr::OriginalName("UserReportInvalidContentLength")] UserReportInvalidContentLength = 12102, + /// + /// UserReport에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("UserReportDocException")] UserReportDocException = 12103, + /// + ///============================================================================================= + /// 캐릭터 관련 오류 : 13000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TestCharacterPrepareCreateNotCompleted")] TestCharacterPrepareCreateNotCompleted = 13001, + /// + /// DefaultCharacterPrepareCreate 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("DefaultCharacterPrepareCreateNotCompleted")] DefaultCharacterPrepareCreateNotCompleted = 13012, + /// + /// CharacterPrepareLoad 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("CharacterPrepareLoadNotCompleted")] CharacterPrepareLoadNotCompleted = 13013, + /// + /// CharacterBaseDoc 로딩중에 중복된 캐릭터가 발견되었습니다. + /// + [pbr::OriginalName("CharacterBaseDocLoadDuplicatedCharacter")] CharacterBaseDocLoadDuplicatedCharacter = 13014, + /// + /// 선택된 캐릭터가 없습니다. + /// + [pbr::OriginalName("CharacterNotSelected")] CharacterNotSelected = 13015, + /// + /// 캐릭터를 찾지 못했습니다. + /// + [pbr::OriginalName("CharacterNotFound")] CharacterNotFound = 13016, + /// + /// CharacterBaseDoc 읽지 않았습니다. + /// + [pbr::OriginalName("CharacterBaseDocNoRead")] CharacterBaseDocNoRead = 13017, + /// + /// 캐릭터 커스터마이징이 완료가 되지 않았습니다. + /// + [pbr::OriginalName("CharacterCustomizingNotCompleted")] CharacterCustomizingNotCompleted = 13018, + /// + /// 캐릭터 생성 절차가 완료되지 않았습니다. + /// + [pbr::OriginalName("CharacterCreateStepNotCompleted")] CharacterCreateStepNotCompleted = 13019, + /// + /// 캐릭터 커스터마이징이 이미 완료 되었습니다. + /// + [pbr::OriginalName("CharacterCustomizingAlreadyCompleted")] CharacterCustomizingAlreadyCompleted = 13020, + /// + /// 캐릭터 준비 단계에서 캐릭터 생성이 되지 않습니다. + /// + [pbr::OriginalName("CharacterPrepareNotCreated")] CharacterPrepareNotCreated = 13021, + /// + /// 캐릭터 생성이 완료 되었습니다. + /// + [pbr::OriginalName("CharacterCreateCompleted")] CharacterCreateCompleted = 13022, + /// + /// CharacterBaseDoc이 null 입니다. + /// + [pbr::OriginalName("CharacterBaseDocIsNull")] CharacterBaseDocIsNull = 13023, + /// + ///============================================================================================= + /// 능력치 관련 오류 : 13300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("AbilityNotEnough")] AbilityNotEnough = 13301, + /// + ///============================================================================================= + /// 아이템 관련 오류 : 14000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ItemMetaDataNotFound")] ItemMetaDataNotFound = 14001, + /// + /// 아이템 Guid 값 오류 입니다. + /// + [pbr::OriginalName("ItemGuidInvalid")] ItemGuidInvalid = 14002, + /// + /// ItemDoc 객체가 null 입니다. + /// + [pbr::OriginalName("ItemDocIsNull")] ItemDocIsNull = 14003, + /// + /// 아이템 DefaultAttribute를 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemDefaultAttributeNotFoundInMeta")] ItemDefaultAttributeNotFoundInMeta = 14004, + /// + /// 아이템 Level Enchant를 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemLevelEnchantNotFoundInMeta")] ItemLevelEnchantNotFoundInMeta = 14005, + /// + /// 아이템 Enchant를 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemEnchantNotFoundInMeta")] ItemEnchantNotFoundInMeta = 14006, + /// + /// 아이템 AttributeRandomGroup을 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemAttributeRandomGroupNotFoundInMeta")] ItemAttributeRandomGroupNotFoundInMeta = 14007, + /// + /// 아이템 AttributeRandomGroup의 TotalWeight 오류 입니다. + /// + [pbr::OriginalName("ItemAttributeRandomGroupTotalWeightInvalid")] ItemAttributeRandomGroupTotalWeightInvalid = 14008, + /// + /// 아이템을 찾지 못했습니다. + /// + [pbr::OriginalName("ItemNotFound")] ItemNotFound = 14009, + /// + /// 의상 아이템 LargeType 오류 입니다. + /// + [pbr::OriginalName("ItemClothInvalidLargeType")] ItemClothInvalidLargeType = 14010, + /// + /// 의상 아이템 SmallType 오류 입니다. + /// + [pbr::OriginalName("ItemClothInvalidSmallType")] ItemClothInvalidSmallType = 14011, + /// + /// 아이템 스택 개수 오류 입니다. + /// + [pbr::OriginalName("ItemStackCountInvalid")] ItemStackCountInvalid = 14012, + /// + /// 아이템 최대 보유 갯수를 초과 했습니다. + /// + [pbr::OriginalName("ItemMaxCountExceed")] ItemMaxCountExceed = 14013, + /// + /// ItemDoc 로딩중에 중복된 아이템이 발견되었습니다. + /// + [pbr::OriginalName("ItemDocLoadDuplicatedItem")] ItemDocLoadDuplicatedItem = 14014, + /// + /// ClothSlotType 오류 입니다. + /// + [pbr::OriginalName("ClothSlotTypeInvalid")] ClothSlotTypeInvalid = 14015, + /// + /// 아이템 스택 개수가 부족 합니다. + /// + [pbr::OriginalName("ItemStackCountNotEnough")] ItemStackCountNotEnough = 14016, + /// + /// 아이템 보유 개수가 부족 합니다. + /// + [pbr::OriginalName("ItemCountNotEnough")] ItemCountNotEnough = 14017, + /// + /// ItemType(LargeType, SmallType) 오류입니다. + /// + [pbr::OriginalName("ItemInvalidItemType")] ItemInvalidItemType = 14018, + /// + /// 아이템 Tool 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("ItemToolMetaDataNotFound")] ItemToolMetaDataNotFound = 14019, + /// + /// 아이템 Tool을 찾지 못했습니다. + /// + [pbr::OriginalName("ItemToolNotFound")] ItemToolNotFound = 14020, + /// + /// 아이템 Tool이 활성화 상태가 아닙니다. + /// + [pbr::OriginalName("ItemToolNotActivateState")] ItemToolNotActivateState = 14021, + /// + /// ToolActionDoc이 null 입니다. + /// + [pbr::OriginalName("ToolActionDocIsNull")] ToolActionDocIsNull = 14022, + /// + /// ToolAction이 이미 비활성화 상태 입니다. + /// + [pbr::OriginalName("ToolActionAlreadyUnactivateState")] ToolActionAlreadyUnactivateState = 14023, + /// + /// ToolAction이 이미 활성화 상태 입니다. + /// + [pbr::OriginalName("ToolActionAlreadyActivateState")] ToolActionAlreadyActivateState = 14024, + /// + /// 아이템 Tattoo가 없습니다. + /// + [pbr::OriginalName("ItemTattooNotFound")] ItemTattooNotFound = 14025, + /// + /// 아이템 Attribute Enchant 메타가 없습니다. + /// + [pbr::OriginalName("ItemAttributeEnchantMetaNotFound")] ItemAttributeEnchantMetaNotFound = 14026, + /// + /// 아이템 Attribute Change가 선택되지 않았습니다. + /// + [pbr::OriginalName("ItemAttributeChangeNotSelected")] ItemAttributeChangeNotSelected = 14027, + /// + /// 아이템 문자열을 정수로 변환하는데 오류가 발생했습니다. + /// + [pbr::OriginalName("ItemParsingFromStringToIntErorr")] ItemParsingFromStringToIntErorr = 14028, + /// + /// 아이템 개수 문자열을 정수로 변환하는데 오류가 발생했습니다. + /// + [pbr::OriginalName("ItemValueParsingFromStringToIntErorr")] ItemValueParsingFromStringToIntErorr = 14029, + /// + /// ItemFirstPurchaseHistoryDoc이 null 입니다. + /// + [pbr::OriginalName("ItemFirstPurchaseHistoryDocIsNull")] ItemFirstPurchaseHistoryDocIsNull = 14030, + /// + /// ItemFirstPurchaseHistoryDoc 로딩중에 중복된 아이템이 발견되었습니다. + /// + [pbr::OriginalName("ItemFirstPurchaseHistoryDocLoadDuplicatedItem")] ItemFirstPurchaseHistoryDocLoadDuplicatedItem = 14031, + /// + /// ItemFirstPurchaseHistory가 이미 존재합니다. + /// + [pbr::OriginalName("ItemFirstPurchaseHistoryAlreadyExist")] ItemFirstPurchaseHistoryAlreadyExist = 14032, + /// + /// 아이템 첫 구매 할인 아이템 개수가 잘못되었습니다. + /// + [pbr::OriginalName("ItemFirstPurchaseDiscountItemCountWrong")] ItemFirstPurchaseDiscountItemCountWrong = 14033, + /// + /// 아이템 AttributeIdType 오류 입니다. + /// + [pbr::OriginalName("ItemAttributeIdTypeInvalid")] ItemAttributeIdTypeInvalid = 14034, + /// + /// 아이템 할당 오류 입니다. + /// + [pbr::OriginalName("ItemAllocFailed")] ItemAllocFailed = 14035, + /// + /// 아이템 Guid 중복 오류 입니다. + /// + [pbr::OriginalName("ItemGuidDuplicated")] ItemGuidDuplicated = 14036, + /// + /// 아이템 레벨이 현재 최대 입니다. + /// + [pbr::OriginalName("ItemLevelCurrentMax")] ItemLevelCurrentMax = 14037, + /// + /// 보관할 수 없는 아이템 입니다. + /// + [pbr::OriginalName("ItemCanNotBeStored")] ItemCanNotBeStored = 14038, + /// + ///============================================================================================= + /// 아이템 액션 관련 오류 : 14101 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ItemUseFunctionNotFound")] ItemUseFunctionNotFound = 14101, + /// + /// 퀘스트 쿨타임 초기화 아이템 사용시 : 퀘스트 메일이 가득차서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseQuestMailCountMax")] ItemUseQuestMailCountMax = 14102, + /// + /// 퀘스트 쿨타임 초기화 아이템 사용시 : 이미 수행중이거나, 퀘스트메일이 존재해서 할당가능한 퀘스트가 없어서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseNotExistAssignableQuest")] ItemUseNotExistAssignableQuest = 14103, + /// + /// 퀘스트 할당 아이템 사용시 : 해당 퀘스트는 이미 진행중이어서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseAlreadyHasQuest")] ItemUseAlreadyHasQuest = 14104, + /// + /// 퀘스트 할당 아이템 사용시 : 해당 퀘스트메일은 이미 존재해서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseAlreadyHasQuestMail")] ItemUseAlreadyHasQuestMail = 14105, + /// + ///============================================================================================= + /// 인벤토리 관련 오류 : 15000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BagRuleItemLargeTypeDuplicated")] BagRuleItemLargeTypeDuplicated = 15001, + /// + /// ToolEquipRule에 ItemLargeType이 중복 되었습니다. + /// + [pbr::OriginalName("ToolEquipRuleItemLargeTypeDuplicated")] ToolEquipRuleItemLargeTypeDuplicated = 15002, + /// + /// ClosthEquipRule에 ItemLargeType이 중복 되었습니다. + /// + [pbr::OriginalName("ClothEquipRuleItemLargeTypeDuplicated")] ClothEquipRuleItemLargeTypeDuplicated = 15003, + /// + /// TattooEquipRule에 ItemLargeType이 중복 되었습니다. + /// + [pbr::OriginalName("TattooEquipRuleItemLargeTypeDuplicated")] TattooEquipRuleItemLargeTypeDuplicated = 15004, + /// + /// InventoryRule을 찾을 수 없습니다. + /// + [pbr::OriginalName("InventoryRuleNotFound")] InventoryRuleNotFound = 15005, + /// + /// EquipInven을 찾을 수 없습니다. + /// + [pbr::OriginalName("EquipInvenNotFound")] EquipInvenNotFound = 15006, + /// + /// 이미 장착된 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyEquiped")] SlotsAlreadyEquiped = 15007, + /// + /// 이미 장착 해제된 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyUnequiped")] SlotsAlreadyUnequiped = 15008, + /// + /// 가방에 아이템이 가득 찼습니다. + /// + [pbr::OriginalName("BagIsItemFull")] BagIsItemFull = 15009, + /// + /// 가방에 아이템이 비어 있습니다. + /// + [pbr::OriginalName("BagIsItemEmpty")] BagIsItemEmpty = 15010, + /// + /// 가방에 DeltaItem이 중복 되었습니다. + /// + [pbr::OriginalName("BagDeltaItemDupliated")] BagDeltaItemDupliated = 15011, + /// + /// 가방에서 아이템을 찾을 수 없습니다. + /// + [pbr::OriginalName("BagItemNotFound")] BagItemNotFound = 15012, + /// + /// ClothEquipRule에서 ClothSlotType을 찾을 수 없습니다. + /// + [pbr::OriginalName("ClothEquipRuleClothSlotTypeNotFound")] ClothEquipRuleClothSlotTypeNotFound = 15013, + /// + /// ToolEquipRule에서 ToolSlotType를 찾을 수 없습니다. + /// + [pbr::OriginalName("ToolEquipRuleToolSlotTypeNotFound")] ToolEquipRuleToolSlotTypeNotFound = 15014, + /// + /// TattooEquipRule에서 TattooSlotType을 찾을 수 없습니다. + /// + [pbr::OriginalName("TattooEquipRuleTattooSlotTypeNotFound")] TattooEquipRuleTattooSlotTypeNotFound = 15015, + /// + /// BagTabType 추가를 실패 했습니다. + /// + [pbr::OriginalName("BagTabTypeAddFailed")] BagTabTypeAddFailed = 15016, + /// + /// BagTabType 오류 입니다. + /// + [pbr::OriginalName("BagTabTypeInvalid")] BagTabTypeInvalid = 15017, + /// + /// BagTabType을 찾을 수 없습니다. + /// + [pbr::OriginalName("BagTabTypeNotFound")] BagTabTypeNotFound = 15018, + /// + /// BagTabCount Merge를 실패 했습니다. + /// + [pbr::OriginalName("BagTabCountMergeFailed")] BagTabCountMergeFailed = 15019, + /// + /// Inventory EntityType 오류 입니다. + /// + [pbr::OriginalName("InventoryEntityTypeInvalid")] InventoryEntityTypeInvalid = 15020, + /// + /// InvenEquipType 오류 입니다. + /// + [pbr::OriginalName("InvenEquipTypeInvalid")] InvenEquipTypeInvalid = 15021, + /// + /// 가방에 예약된 아이템이 가득 찼습니다. + /// + [pbr::OriginalName("BagIsReservedItemFull")] BagIsReservedItemFull = 15022, + /// + /// 가방에 예약된 아이템이 없습니다. + /// + [pbr::OriginalName("BagIsReservedItemEmpty")] BagIsReservedItemEmpty = 15023, + /// + /// 장착 슬롯이 일치하지 않습니다. + /// + [pbr::OriginalName("EquipSlotNotMatch")] EquipSlotNotMatch = 15024, + /// + /// 장착 슬롯 범위를 벗어났습니다. + /// + [pbr::OriginalName("EquipSlotOutOfRange")] EquipSlotOutOfRange = 15025, + /// + /// 이미 예약된 장착 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyReservedEquip")] SlotsAlreadyReservedEquip = 15026, + /// + /// 이미 예약된 장착 해제 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyReservedUnequip")] SlotsAlreadyReservedUnequip = 15027, + /// + /// SlotType을 찾을 수 없습니다. + /// + [pbr::OriginalName("SlotTypeNotFound")] SlotTypeNotFound = 15028, + /// + ///============================================================================================= + /// UgcNpc 관련 오류 : 15101 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UgcNpcMetaGuidAlreadyAdded")] UgcNpcMetaGuidAlreadyAdded = 15101, + /// + /// UgcNpcDoc이 null 입니다. + /// + [pbr::OriginalName("UgcNpcDocIsNull")] UgcNpcDocIsNull = 15102, + /// + /// UgcNpc 생성 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcMaxCountExceed")] UgcNpcMaxCountExceed = 15103, + /// + /// UgcNpc 의상 아이템이 부족 합니다. + /// + [pbr::OriginalName("UgcNpcClothItemNotEnough")] UgcNpcClothItemNotEnough = 15104, + /// + /// UgcNpcDoc 중복 로딩 되었습니다. + /// + [pbr::OriginalName("UgcNpcDocLoadDuplicatedUgcNpc")] UgcNpcDocLoadDuplicatedUgcNpc = 15105, + /// + /// UgcNpc 캐릭터 설명 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcDescriptionLengthExceed")] UgcNpcDescriptionLengthExceed = 15106, + /// + /// UgcNpc 세계관 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcWordScenarioLengthExceed")] UgcNpcWordScenarioLengthExceed = 15107, + /// + /// UgcNpc 인사말 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcGreetingLengthExceed")] UgcNpcGreetingLengthExceed = 15108, + /// + /// UgcNpc 타투 아이템이 부족 합니다. + /// + [pbr::OriginalName("UgcNpcTattooItemNotEnough")] UgcNpcTattooItemNotEnough = 15109, + /// + /// UgcNpc 자주 사용하는 소셜 액션 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcHabitSocialActionCountExceed")] UgcNpcHabitSocialActionCountExceed = 15110, + /// + /// UgcNpc 대화중 기본 소셜 액션 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcDialogueSocialActionCountExceed")] UgcNpcDialogueSocialActionCountExceed = 15111, + /// + /// UgcNpc 닉네임이 중복 되었습니다. + /// + [pbr::OriginalName("UgcNpcNicknameDuplicated")] UgcNpcNicknameDuplicated = 15112, + /// + /// UgcNpc를 찾을 수 없습니다. + /// + [pbr::OriginalName("UgcNpcNotFound")] UgcNpcNotFound = 15113, + /// + /// UgcNpc 자기소개 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcIntroductionLengthExceed")] UgcNpcIntroductionLengthExceed = 15114, + /// + /// UgcNpc Tag 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcMaxTagExceed")] UgcNpcMaxTagExceed = 15115, + /// + /// UgcNpc 닉네임이 없습니다. + /// + [pbr::OriginalName("UgcNpcNicknameEmpty")] UgcNpcNicknameEmpty = 15116, + /// + /// UgcNpc가 이미 게임존에 등록되어 있습니다. + /// + [pbr::OriginalName("UgcNpcAlreadyRegisteredInGameZone")] UgcNpcAlreadyRegisteredInGameZone = 15117, + /// + /// UgcNpc가 게임존에 등록되어 있지 않습니다. + /// + [pbr::OriginalName("UgcNpcNotRegisteredInGameZone")] UgcNpcNotRegisteredInGameZone = 15118, + /// + /// UgcNpcLikeSelecteeCount를 찾을 수 없습니다. + /// + [pbr::OriginalName("UgcNpcLikeSelecteeCountNotFound")] UgcNpcLikeSelecteeCountNotFound = 15119, + /// + /// UgcNpcLikeSelectedFlag를 찾을 수 없습니다. + /// + [pbr::OriginalName("UgcNpcLikeSelectedFlagNotFound")] UgcNpcLikeSelectedFlagNotFound = 15120, + /// + /// UgcNpc가 마이홈 Ugc에 중복 배치 되었습니다. + /// + [pbr::OriginalName("UgcNpcDuplicateInMyhomeUgc")] UgcNpcDuplicateInMyhomeUgc = 15121, + /// + /// UgcNpc가 배치된 상태 입니다. + /// + [pbr::OriginalName("UgcNpcLocatedState")] UgcNpcLocatedState = 15122, + /// + /// UgcNpc 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("UgcNpcMetaDataNotFound")] UgcNpcMetaDataNotFound = 15123, + /// + /// Beacon 앱 프로필 업로드 쿨타임 입니다. + /// + [pbr::OriginalName("BeaconAppProfileUploadCoolTime")] BeaconAppProfileUploadCoolTime = 15124, + /// + /// Beacon Body 아이템 오류 입니다. + /// + [pbr::OriginalName("BeaconBodyItemInvalid")] BeaconBodyItemInvalid = 15125, + /// + /// UgcNpc가 BeaconShopItem을 가지고 있습니다. + /// + [pbr::OriginalName("UgcNpcHasBeaconShopItem")] UgcNpcHasBeaconShopItem = 15126, + /// + ///============================================================================================= + /// UgcNpc Ranking 관련 오류 : 15201 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UgcNpcRankEntityIsNotFound")] UgcNpcRankEntityIsNotFound = 15201, + /// + /// ugc npc ranking 조회 범위를 초과했습니다. + /// + [pbr::OriginalName("UgcNpcRankOutOfRange")] UgcNpcRankOutOfRange = 15202, + /// + ///============================================================================================= + /// Farming Effect 관련 오류 : 15301 ~ + ///============================================================================================= + /// + [pbr::OriginalName("FarmingEffectDocLinkPkSkNotSet")] FarmingEffectDocLinkPkSkNotSet = 15301, + /// + /// FarmingEffect가 이미 게임존에 등록되어 있습니다. + /// + [pbr::OriginalName("FarmingEffectAlreadyRegisteredInGameZone")] FarmingEffectAlreadyRegisteredInGameZone = 15302, + /// + /// 이미 진행중인 Farming 입니다. + /// + [pbr::OriginalName("FarmingAlready")] FarmingAlready = 15303, + /// + /// 나는 Farming 중입니다. + /// + [pbr::OriginalName("FarmingByMe")] FarmingByMe = 15304, + /// + /// FarmingPropMeta 데이터를 찾을 수 없습니다. + /// + [pbr::OriginalName("FarmingPropMetaDataNotFound")] FarmingPropMetaDataNotFound = 15305, + /// + /// Farming 시도 횟수 오류 입니다. + /// + [pbr::OriginalName("FarmingTryCountInvalid")] FarmingTryCountInvalid = 15306, + /// + /// Farming 상태가 아닙니다. + /// + [pbr::OriginalName("FarmingNotState")] FarmingNotState = 15307, + /// + /// Farming 소유자 일치 하지 않습니다. + /// + [pbr::OriginalName("FarmingOwnerNotMatch")] FarmingOwnerNotMatch = 15308, + /// + /// FarmingSummonedEntityType 오류 입니다. + /// + [pbr::OriginalName("FarmingSummonedEntityTypeInvalid")] FarmingSummonedEntityTypeInvalid = 15309, + /// + /// FarmingEffect가 게임존에 존재하지 않습니다. + /// + [pbr::OriginalName("FarmingEffectNotExistInGameZone")] FarmingEffectNotExistInGameZone = 15310, + /// + /// Farming 상태 입니다. + /// + [pbr::OriginalName("FarimgState")] FarimgState = 15311, + /// + /// Farming StandBy 상태가 아닙니다. + /// + [pbr::OriginalName("FarmingStandByNotState")] FarmingStandByNotState = 15312, + /// + /// Farming Anchor를 찾을 수 없습니다. + /// + [pbr::OriginalName("FarmingAnchorNotFound")] FarmingAnchorNotFound = 15313, + /// + /// Farming 관련 Beacon Db 정보 무결성 오류 입니다. + /// + [pbr::OriginalName("FarmingBeaconDbInfoIntegrityError")] FarmingBeaconDbInfoIntegrityError = 15314, + /// + ///============================================================================================= + /// Gacha 관련 오류 : 15401 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GachaMetaDataNotFound")] GachaMetaDataNotFound = 15401, + /// + /// Gacha 보상 정보가 없습니다. + /// + [pbr::OriginalName("GachaRewardEmpty")] GachaRewardEmpty = 15402, + /// + ///============================================================================================= + /// Master 관련 오류 : 15800 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MasterNotFound")] MasterNotFound = 15801, + /// + /// Master 관계 설정이 없다. + /// + [pbr::OriginalName("MasterNotRelated")] MasterNotRelated = 15802, + /// + ///============================================================================================= + /// 게임존 관련 오류 : 15900 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GameZoneNotJoin")] GameZoneNotJoin = 15901, + /// + /// Map 객체가 null 입니다. + /// + [pbr::OriginalName("MapIsNull")] MapIsNull = 15902, + /// + /// LOCATION_UNIQUE_ID 값 오류 입니다. + /// + [pbr::OriginalName("LocationUniqueIdInvalid")] LocationUniqueIdInvalid = 15903, + /// + /// Prop이 점유중 입니다. + /// + [pbr::OriginalName("PropIsOccupied")] PropIsOccupied = 15904, + /// + /// Prop을 점유중이 아닙니다. + /// + [pbr::OriginalName("PropIsNotOccupied")] PropIsNotOccupied = 15905, + /// + ///============================================================================================= + /// 친구 관련 오류 : 16000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("FriendDocIsNull")] FriendDocIsNull = 16001, + /// + /// FriendFolderDoc이 null 입니다. + /// + [pbr::OriginalName("FriendFolderDocIsNull")] FriendFolderDocIsNull = 16002, + /// + ///============================================================================================= + /// 소셜 액션 관련 오류 : 17000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("SocialActionMetaDataNotFound")] SocialActionMetaDataNotFound = 17001, + /// + /// 소셜 액션을 찾지 못했습니다. + /// + [pbr::OriginalName("SocialActionNotFound")] SocialActionNotFound = 17002, + /// + /// SocialActionDoc 로딩중에 중복된 소셜 액션이 발견되었습니다. + /// + [pbr::OriginalName("SocialActionDocLoadDuplicatedSocialAction")] SocialActionDocLoadDuplicatedSocialAction = 17003, + /// + /// 소셜 액션이 이미 존재합니다. + /// + [pbr::OriginalName("SocialActionAlreadyExist")] SocialActionAlreadyExist = 17004, + /// + /// 소셜 액션 슬롯의 범위를 벗어났습니다. + /// + [pbr::OriginalName("SocialActionSlotOutOfRange")] SocialActionSlotOutOfRange = 17005, + /// + /// 소셜 액션이 슬롯에 존재하지 않습니다. + /// + [pbr::OriginalName("SocialActionNotOnSlot")] SocialActionNotOnSlot = 17006, + /// + /// SocialActionDoc이 null 입니다. + /// + [pbr::OriginalName("SocialActionDocIsNull")] SocialActionDocIsNull = 17007, + /// + ///============================================================================================= + /// 채널 관련 오류 : 18000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ChannelMoveSameChannel")] ChannelMoveSameChannel = 18001, + /// + /// 채널 이동 쿨타임이 지나지 않았습니다. + /// + [pbr::OriginalName("ChannelInvalidMoveCoolTime")] ChannelInvalidMoveCoolTime = 18002, + /// + /// 채널서버가 아닙니다. ( Indun 서버에서 이동요청시 발생 ) + /// + [pbr::OriginalName("NotChannelServer")] NotChannelServer = 18003, + /// + ///============================================================================================= + /// 던전 관련 오류 : 19000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("OwnedRoomDocIsNull")] OwnedRoomDocIsNull = 19001, + /// + /// RoomDoc이 null 입니다. + /// + [pbr::OriginalName("RoomDocIsNull")] RoomDocIsNull = 19002, + /// + /// Room이 마이홈이 아닙니다. + /// + [pbr::OriginalName("RoomIsNotMyHome")] RoomIsNotMyHome = 19003, + /// + /// MapRange의 범위를 벗어난 CellPos 입니다. + /// + [pbr::OriginalName("MapRangeOutOfCellPos")] MapRangeOutOfCellPos = 19004, + /// + /// MapGrid의 범위를 벗어 났습니다. + /// + [pbr::OriginalName("MapGridBoundOutOfRange")] MapGridBoundOutOfRange = 19005, + /// + /// Map내에서 Grid를 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridNotFoundGrid")] MapGridNotFoundGrid = 19006, + /// + /// MapGrid내에서 Cell을 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridNotFoundCell")] MapGridNotFoundCell = 19007, + /// + /// MapGrid내의 Cell에서 플레이어를 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridCellNotFoundPlayer")] MapGridCellNotFoundPlayer = 19008, + /// + /// MapGrid내의 Cell에서 UgcNpc를 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridCellNotFoundUgcNpc")] MapGridCellNotFoundUgcNpc = 19009, + /// + ///============================================================================================= + /// 메일 관련 오류 : 20000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MailNotFound")] MailNotFound = 20001, + /// + /// 이미 아이템을 받았습니다. + /// + [pbr::OriginalName("MailAlreadyTaken")] MailAlreadyTaken = 20002, + /// + /// 메일 MailType 오류 입니다. + /// + [pbr::OriginalName("MailInvalidMailType")] MailInvalidMailType = 20003, + /// + /// 보낼 수 있는 메일의 갯수를 초과 했습니다. + /// + [pbr::OriginalName("MailMaxSendCountExceed")] MailMaxSendCountExceed = 20004, + /// + /// 메일 Doc이 null 입니다. + /// + [pbr::OriginalName("MailDocIsNull")] MailDocIsNull = 20005, + /// + /// 메일 블락한 유저에게 보낼 수 없습니다. + /// + [pbr::OriginalName("MailBlockUserCannotSend")] MailBlockUserCannotSend = 20006, + /// + /// 타겟의 받을 수 있는 메일의 갯수를 초과 했습니다. + /// + [pbr::OriginalName("MailMaxTargetReceivedCountExceed")] MailMaxTargetReceivedCountExceed = 20007, + /// + /// 나에게 메일을 보낼 수 없습니다. + /// + [pbr::OriginalName("MailCantSendSelf")] MailCantSendSelf = 20008, + /// + /// 아이템이 있는 상태의 메일은 삭제할 수 없습니다. + /// + [pbr::OriginalName("MailCantDeleteIfItemExists")] MailCantDeleteIfItemExists = 20009, + /// + /// MailDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("MailDocException")] MailDocException = 20010, + /// + ///============================================================================================= + /// 메일 프로필 관련 오류 : 20300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MailProfileDocIsNull")] MailProfileDocIsNull = 20301, + /// + /// MailProfileDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("MailProfileDocException")] MailProfileDocException = 20302, + /// + ///============================================================================================= + /// 시스템 메일 관련 오류 : 20500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("SystemMailDocIsNull")] SystemMailDocIsNull = 20501, + /// + ///============================================================================================= + /// 파티 관련 오류 : 21000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("PartyCannotSetGuid")] PartyCannotSetGuid = 21001, + /// + /// Party Member 정보 생성 실패입니다. + /// + [pbr::OriginalName("PartyFailedMakePartyMember")] PartyFailedMakePartyMember = 21002, + /// + /// 파티 인원이 초과되었습니다. + /// + [pbr::OriginalName("PartyIsFull")] PartyIsFull = 21003, + /// + /// 이미 초대를 보냈습니다. + /// + [pbr::OriginalName("AlreadyInviteParty")] AlreadyInviteParty = 21004, + /// + /// 초대 정보를 확인할 수 없습니다. + /// + [pbr::OriginalName("NotFoundPartyInvite")] NotFoundPartyInvite = 21005, + /// + /// 파티 정보를 확인할 수 없습니다. + /// + [pbr::OriginalName("NotFoundParty")] NotFoundParty = 21006, + /// + /// 파티 상태가 아닙니다. + /// + [pbr::OriginalName("NotParty")] NotParty = 21007, + /// + /// 파티 리더가 아닙니다. + /// + [pbr::OriginalName("NotPartyLeader")] NotPartyLeader = 21008, + /// + /// 파티에 입장 중입니다. + /// + [pbr::OriginalName("JoiningParty")] JoiningParty = 21009, + /// + /// 파티원이 아닙니다. + /// + [pbr::OriginalName("NotPartyMember")] NotPartyMember = 21010, + /// + /// 이미 소환된 상태입니다. + /// + [pbr::OriginalName("AlreadySummon")] AlreadySummon = 21011, + /// + /// 파티 리더의 서버 허용인원이 초과되었습니다. + /// + [pbr::OriginalName("PartyLeaderServerIsFull")] PartyLeaderServerIsFull = 21012, + /// + /// 초대할 유저가 콘서트에 있습니다. + /// + [pbr::OriginalName("InviteMemberIsConcert")] InviteMemberIsConcert = 21013, + /// + /// 파티 초대 발송에 실패했습니다. + /// + [pbr::OriginalName("FailToSendInviteMember")] FailToSendInviteMember = 21014, + /// + /// 이미 파티에 소속되어 있습니다. + /// + [pbr::OriginalName("AlreadyPartyMember")] AlreadyPartyMember = 21015, + /// + /// 소환할 수 없는 서버에 있습니다. + /// + [pbr::OriginalName("InvalidSummonServerType")] InvalidSummonServerType = 21016, + /// + /// 소환 대상의 거리가 짧습니다. + /// + [pbr::OriginalName("SummonUserLimitDistance")] SummonUserLimitDistance = 21017, + /// + /// 소환 대상자가 아닙니다. + /// + [pbr::OriginalName("InvalidSummonMember")] InvalidSummonMember = 21018, + /// + /// 파티 리더가 logout 상태입니다. + /// + [pbr::OriginalName("PartyLeaderLoggedOut")] PartyLeaderLoggedOut = 21019, + /// + /// 진행 중인 Vote 가 있습니다. + /// + [pbr::OriginalName("AlreadyStartPartyVote")] AlreadyStartPartyVote = 21020, + /// + /// 진행 중인 Vote 가 없습니다. + /// + [pbr::OriginalName("NoStartPartyVote")] NoStartPartyVote = 21021, + /// + /// 투표 가능 시간이 지났습니다. + /// + [pbr::OriginalName("AlreadyPassPartyVoteTime")] AlreadyPassPartyVoteTime = 21022, + /// + /// 투표 시작 가능 시간이 지나지 않았습니다. + /// + [pbr::OriginalName("InvalidPartyVoteTime")] InvalidPartyVoteTime = 21023, + /// + /// 이미 투표를 하였습니다. + /// + [pbr::OriginalName("AlreadyReplyPartyVote")] AlreadyReplyPartyVote = 21024, + /// + /// 파티 인스턴스 가 없습니다. + /// + [pbr::OriginalName("EmptyPartyInstanceId")] EmptyPartyInstanceId = 21025, + /// + /// 초대할 수 없는 장소입니다. + /// + [pbr::OriginalName("InvalidInvitePlace")] InvalidInvitePlace = 21026, + /// + /// 초대 유저의 정보가 잘못되었습니다. + /// + [pbr::OriginalName("InvitePartyInvalidUsers")] InvitePartyInvalidUsers = 21027, + /// + /// 파티원 소환에 실패했습니다. + /// + [pbr::OriginalName("SummonPartyMemberFail")] SummonPartyMemberFail = 21028, + /// + /// 입력 글자수 최대 길이가 잘못되었습니다. + /// + [pbr::OriginalName("InvalidPartyStringLength")] InvalidPartyStringLength = 21029, + /// + /// 파티명에 금칙어가 있습니다. + /// + [pbr::OriginalName("IncludeBanWordFromPartyName")] IncludeBanWordFromPartyName = 21030, + /// + /// 파티원 정보가 없습니다. + /// + [pbr::OriginalName("JoiningPartyMemberInfoIsNull")] JoiningPartyMemberInfoIsNull = 21031, + /// + /// 서로 다른 월드에 있습니다. + /// + [pbr::OriginalName("InvalidSummonWorldServer")] InvalidSummonWorldServer = 21032, + [pbr::OriginalName("NotExistPartyInstance")] NotExistPartyInstance = 21999, + /// + ///============================================================================================= + /// 버프 관련 오류 : 22000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BuffMetaDataNotFound")] BuffMetaDataNotFound = 22001, + /// + /// 등록되지 않은 버프 카테고리 입니다. + /// + [pbr::OriginalName("BuffNotRegistryCategory")] BuffNotRegistryCategory = 22002, + /// + /// 버프를 찾지 못했습니다. + /// + [pbr::OriginalName("BuffNotFound")] BuffNotFound = 22003, + /// + /// 버프 BuffCategoryType 오류 입니다. + /// + [pbr::OriginalName("BuffInvalidBuffCategoryType")] BuffInvalidBuffCategoryType = 22004, + /// + /// BuffCache 로딩중에 중복된 버프가 발견되었습니다. + /// + [pbr::OriginalName("BuffCacheLoadDuplicatedBuff")] BuffCacheLoadDuplicatedBuff = 22005, + /// + /// 버프 AttributeType 오류 입니다. + /// + [pbr::OriginalName("BuffInvalidAttributeType")] BuffInvalidAttributeType = 22006, + /// + ///============================================================================================= + /// 퀘스트 관련 오류 : 23000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("QuestAssingDataNotExist")] QuestAssingDataNotExist = 23000, + /// + /// 퀘스트 메일이 없습니다. + /// + [pbr::OriginalName("QuestMailNotExist")] QuestMailNotExist = 23001, + /// + /// 이미 완료된 퀘스트 + /// + [pbr::OriginalName("QuestAlreadyEnded")] QuestAlreadyEnded = 23002, + /// + /// 할당 가능한 퀘스트 수 맥스 + /// + [pbr::OriginalName("QuestCountMax")] QuestCountMax = 23003, + /// + /// 타입별로 할당 가능한 퀘스트 수 맥스 + /// + [pbr::OriginalName("QuestTypeAssignCountMax")] QuestTypeAssignCountMax = 23004, + /// + /// 퀘스트 타입 오류 + /// + [pbr::OriginalName("QuestInvalidType")] QuestInvalidType = 23005, + /// + /// 퀘스트 값 오류 + /// + [pbr::OriginalName("QuestInvalidValue")] QuestInvalidValue = 23006, + /// + /// 퀘스트 아이디 없음 + /// + [pbr::OriginalName("QuestIdNotFound")] QuestIdNotFound = 23007, + /// + /// 잘못된 태스트 진행 번호 + /// + [pbr::OriginalName("QuestInvalidTaskNum")] QuestInvalidTaskNum = 23008, + /// + /// 퀘스트 거절은 일반 퀘스트만 가능 + /// + [pbr::OriginalName("QuestRefuseOnlyNormal")] QuestRefuseOnlyNormal = 23009, + /// + /// 포기하려는 퀘스트 정보 존재하지 않습니다. + /// + [pbr::OriginalName("QuestAbadonNotExistQuest")] QuestAbadonNotExistQuest = 23010, + /// + /// 이미 달성한 퀘스트 + /// + [pbr::OriginalName("QuestAlreadyComplete")] QuestAlreadyComplete = 23011, + /// + /// 퀘스트 미달성 + /// + [pbr::OriginalName("QuestNotComplete")] QuestNotComplete = 23012, + /// + /// 퀘스트 포기는 일반 퀘스트만 가능 + /// + [pbr::OriginalName("QuestAbandonOnlyNormal")] QuestAbandonOnlyNormal = 23013, + /// + /// QuestMailDoc이 null 입니다. + /// + [pbr::OriginalName("QuestMailDocIsNull")] QuestMailDocIsNull = 23014, + /// + /// QuestDoc이 null 입니다. + /// + [pbr::OriginalName("QuestDocIsNull")] QuestDocIsNull = 23015, + /// + /// EndQuestDoc이 null 입니다. + /// + [pbr::OriginalName("EndQuestDocIsNull")] EndQuestDocIsNull = 23016, + /// + /// QuestMetaBase가 구현되지 않았습니다. + /// + [pbr::OriginalName("QuestMetaBaseNotImplement")] QuestMetaBaseNotImplement = 23017, + /// + /// Quest notify 레디스에 등록 실패 + /// + [pbr::OriginalName("QuestNotifyRedisRegistFail")] QuestNotifyRedisRegistFail = 23018, + /// + /// Quest를 이미 보유중입니다. + /// + [pbr::OriginalName("QuestAlreadyExist")] QuestAlreadyExist = 23019, + /// + ///============================================================================================= + /// 월드 관련 오류 : 24000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("WorldMetaDataNotFound")] WorldMetaDataNotFound = 24001, + /// + /// 월드 입장 아이템 수량이 부족합니다. + /// + [pbr::OriginalName("LackOfWorldEnterItem")] LackOfWorldEnterItem = 24002, + /// + /// 월드 맵트리 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("WorldMapTreeDataNotFound")] WorldMapTreeDataNotFound = 24003, + /// + /// 월드 맵트리 하위 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("WorldMapTreeChildLandNotFound")] WorldMapTreeChildLandNotFound = 24004, + /// + /// 룸 맵 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("RoomMapDataNotFound")] RoomMapDataNotFound = 24005, + /// + ///============================================================================================= + /// 랜드 관련 오류 : 24500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LandMetaDataNotFound")] LandMetaDataNotFound = 24501, + /// + /// 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("LandNotFound")] LandNotFound = 24502, + /// + /// LandDoc 로딩중에 중복된 랜드가 발견되었습니다. + /// + [pbr::OriginalName("LandDocLoadDuplicatedLand")] LandDocLoadDuplicatedLand = 24503, + /// + /// LandDoc이 null 입니다. + /// + [pbr::OriginalName("LandDocIsNull")] LandDocIsNull = 24504, + /// + /// 소유 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("OwnedLandNotFound")] OwnedLandNotFound = 24505, + /// + /// OwnedLandDoc 로딩중에 중복된 랜드가 발견되었습니다. + /// + [pbr::OriginalName("OwnedLandDocLoadDuplicatedOwnedLand")] OwnedLandDocLoadDuplicatedOwnedLand = 24506, + /// + /// OwnedLandDoc이 null 입니다. + /// + [pbr::OriginalName("OwnedLandDocIsNull")] OwnedLandDocIsNull = 24507, + /// + /// 랜드 맵트리 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("LandMapTreeDataNotFound")] LandMapTreeDataNotFound = 24508, + /// + /// 랜드 맵트리 하위 빌딩을 찾지 못했습니다. + /// + [pbr::OriginalName("LandMapTreeChildBuildingNotFound")] LandMapTreeChildBuildingNotFound = 24509, + /// + /// 랜드 빌딩이 비어 있지 않습니다. + /// + [pbr::OriginalName("LandBuildingIsNotEmpty")] LandBuildingIsNotEmpty = 24510, + /// + /// 랜드 맵 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("LandMapDataNotFound")] LandMapDataNotFound = 24511, + /// + /// 랜드 오너가 존재합니다. + /// + [pbr::OriginalName("LandExistOwner")] LandExistOwner = 24512, + /// + /// 랜드 EditorType 이 USER 가 아닙니다. + /// + [pbr::OriginalName("LandEditorIsNotUser")] LandEditorIsNotUser = 24513, + /// + /// 랜드 오너가 아닙니다. + /// + [pbr::OriginalName("LandOwnerIsNotMatch")] LandOwnerIsNotMatch = 24514, + /// + ///============================================================================================= + /// 빌딩 관련 오류 : 25000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BuildingMetaDataNotFound")] BuildingMetaDataNotFound = 25001, + /// + /// 빌딩을 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingNotFound")] BuildingNotFound = 25002, + /// + /// BuildingDoc 로딩중에 중복된 빌딩이 발견되었습니다. + /// + [pbr::OriginalName("BuildingDocLoadDuplicatedBuilding")] BuildingDocLoadDuplicatedBuilding = 25003, + /// + /// BuildingDoc이 null 입니다. + /// + [pbr::OriginalName("BuildingDocIsNull")] BuildingDocIsNull = 25004, + /// + /// 소유 빌딩을 찾지 못했습니다. + /// + [pbr::OriginalName("OwnedBuildingNotFound")] OwnedBuildingNotFound = 25005, + /// + /// OwnedBuildingDoc 로딩중에 중복된 빌딩이 발견되었습니다. + /// + [pbr::OriginalName("OwnedBuildingDocLoadDuplicatedOwnedBuilding")] OwnedBuildingDocLoadDuplicatedOwnedBuilding = 25006, + /// + /// OwnedBuildingDoc이 null 입니다. + /// + [pbr::OriginalName("OwnedBuildingDocIsNull")] OwnedBuildingDocIsNull = 25007, + /// + /// 빌딩 맵트리 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapTreeDataNotFound")] BuildingMapTreeDataNotFound = 25008, + /// + /// 빌딩 맵트리 하위 룸을 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapTreeChildRoomNotFound")] BuildingMapTreeChildRoomNotFound = 25009, + /// + /// 빌딩 층이 비어 있지 않습니다. + /// + [pbr::OriginalName("BuildingFloorIsNotEmpty")] BuildingFloorIsNotEmpty = 25010, + /// + /// 빌딩 맵 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapDataNotFound")] BuildingMapDataNotFound = 25011, + /// + /// 빌딩 오너가 존재합니다. + /// + [pbr::OriginalName("BuildingExistOwner")] BuildingExistOwner = 25012, + /// + /// 빌딩 맵트리 상위 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapTreeParentLandNotFound")] BuildingMapTreeParentLandNotFound = 20513, + /// + /// 빌딩 오너가 아닙니다. + /// + [pbr::OriginalName("BuildingOwnerIsNotMatch")] BuildingOwnerIsNotMatch = 25014, + /// + ///============================================================================================= + /// 마이홈 관련 오류 : 25500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MyHomeMetaDataNotFound")] MyHomeMetaDataNotFound = 25501, + /// + /// 마이홈을 찾지 못했습니다. + /// + [pbr::OriginalName("MyHomeNotFound")] MyHomeNotFound = 25502, + /// + /// MyHomeDoc 로딩중에 중복된 마이홈이 발견되었습니다. + /// + [pbr::OriginalName("MyHomeDocLoadDuplicatedMyHome")] MyHomeDocLoadDuplicatedMyHome = 25503, + /// + /// 마이홈이 이미 존재합니다. + /// + [pbr::OriginalName("MyHomeAlreadyExist")] MyHomeAlreadyExist = 25504, + /// + /// MyHomeDoc이 null 입니다. + /// + [pbr::OriginalName("MyHomeDocIsNull")] MyHomeDocIsNull = 25505, + /// + /// 마이홈이 내 것이 아닙니다. + /// + [pbr::OriginalName("MyHomeIsNotMine")] MyHomeIsNotMine = 25506, + /// + /// 제작중일때는 마이홈을 변경할수 없습니다. + /// + [pbr::OriginalName("MyHomeCantExchangeWhenCrafting")] MyHomeCantExchangeWhenCrafting = 25507, + /// + /// EditableRoom 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("EditableRoomMetaDataNotFound")] EditableRoomMetaDataNotFound = 25508, + /// + /// EditableFramework 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("EditableFrameworkMetaDataNotFound")] EditableFrameworkMetaDataNotFound = 25509, + /// + /// 사용 가능한 InteriorPoint를 초과 하였습니다. + /// + [pbr::OriginalName("InteriorPointExceed")] InteriorPointExceed = 25510, + /// + /// 마이홈 슬롯이 부족 합니다. + /// + [pbr::OriginalName("MyhomeNotEnoughSlot")] MyhomeNotEnoughSlot = 25511, + /// + /// 마이홈이 선택되어 있습니다. + /// + [pbr::OriginalName("MyhomeIsSelected")] MyhomeIsSelected = 25512, + /// + /// 마이홈 이름 문자열 길이가 짧습니다 + /// + [pbr::OriginalName("MyhomeNameLengthShort")] MyhomeNameLengthShort = 25513, + /// + /// 마이홈 이름 문자열 길이가 깁니다. + /// + [pbr::OriginalName("MyhomeNameLengthLong")] MyhomeNameLengthLong = 25514, + /// + /// 마이홈 이름이 중복 되었습니다. + /// + [pbr::OriginalName("MyhomeNameDuplicated")] MyhomeNameDuplicated = 25515, + /// + /// 마이홈 인터폰이 없습니다. + /// + [pbr::OriginalName("MyhomeInterphoneNotExist")] MyhomeInterphoneNotExist = 25516, + /// + /// 마이홈 시작 지점이 없습니다. + /// + [pbr::OriginalName("MyhomeStartPointNotExist")] MyhomeStartPointNotExist = 25517, + /// + /// 마이홈 인터폰이 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("MyhomeInterphoneExceed")] MyhomeInterphoneExceed = 25518, + /// + /// 마이홈 시작 지점이 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("MyhomeStartPointExceed")] MyhomeStartPointExceed = 25519, + /// + /// 의상 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CraftingClothesExceed")] CraftingClothesExceed = 25520, + /// + /// 요리 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CraftingCookingExceed")] CraftingCookingExceed = 25521, + /// + /// 가구 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CraftingFurnitureExceed")] CraftingFurnitureExceed = 25522, + /// + /// 앵커가 마이홈에 배치되어 있지 않습니다. + /// + [pbr::OriginalName("AnchorIsNotInMyhome")] AnchorIsNotInMyhome = 25523, + /// + /// 제작 진행중인 제작대를 제거 할 수 없습니다. + /// + [pbr::OriginalName("DoNotRemoveProcessCraftingAnchor")] DoNotRemoveProcessCraftingAnchor = 25524, + /// + /// 앵커 Guid가 중복 되었습니다. + /// + [pbr::OriginalName("AnchorGuidDuplicate")] AnchorGuidDuplicate = 25525, + /// + /// 마이홈이 편집 중 입니다. + /// + [pbr::OriginalName("MyhomeIsEditting")] MyhomeIsEditting = 25526, + /// + /// 마이홈이 렌탈 중 입니다. + /// + [pbr::OriginalName("MyhomeIsOnRental")] MyhomeIsOnRental = 25527, + /// + /// 마이홈 Ugc Info 파일을 찾지 못했습니다. + /// + [pbr::OriginalName("MyhomeUgcInfoFileNotFoune")] MyhomeUgcInfoFileNotFoune = 25528, + /// + /// EditableRoom SizeType 오류 입니다. + /// + [pbr::OriginalName("EditableRoomSizeTypeInvalid")] EditableRoomSizeTypeInvalid = 25529, + /// + /// 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CrafterCountExceed")] CrafterCountExceed = 25530, + /// + ///============================================================================================= + /// 미니맵 마커 관련 오류 : 26000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MinimapMarkerNotFound")] MinimapMarkerNotFound = 26001, + /// + /// MinimapMarkerDoc 로딩중에 중복된 미니맵 마커가 발견되었습니다. + /// + [pbr::OriginalName("MinimapMarkerDocLoadDuplicatedMinimapMarker")] MinimapMarkerDocLoadDuplicatedMinimapMarker = 26002, + /// + /// MinimapMarkerDoc이 null 입니다. + /// + [pbr::OriginalName("MinimapMarkerDocIsNull")] MinimapMarkerDocIsNull = 26003, + /// + ///============================================================================================= + /// 카트 관련 오류 : 26500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CartMetaDataNotFound")] CartMetaDataNotFound = 26501, + /// + /// 카트 용량이 가득 찼습니다. + /// + [pbr::OriginalName("CartMaxCountExceed")] CartMaxCountExceed = 26502, + /// + /// 카트에 아이템 스텍이 가득 찼습니다. + /// + [pbr::OriginalName("CartStackCountInvalid")] CartStackCountInvalid = 26503, + /// + /// 카트의 아이템 갯수가 요청값보다 낮습니다. + /// + [pbr::OriginalName("CartStackCountNotEnough")] CartStackCountNotEnough = 26504, + /// + /// 카트에서 아이템을 찾지 못했습니다. + /// + [pbr::OriginalName("CartItemNotFound")] CartItemNotFound = 26505, + /// + /// 등록되지 않은 재화타입 입니다. + /// + [pbr::OriginalName("CartNotRegistryCurrencyType")] CartNotRegistryCurrencyType = 26506, + /// + /// 카트 CurrencyType 오류 입니다. + /// + [pbr::OriginalName("CartInvalidCurrencyType")] CartInvalidCurrencyType = 26507, + /// + /// CartDoc이 null 입니다. + /// + [pbr::OriginalName("CartDocIsNull")] CartDocIsNull = 26508, + /// + /// CartDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CartDocException")] CartDocException = 26509, + /// + ///============================================================================================= + /// 채팅 관련 오류 : 27000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ChatSendSelfFailed")] ChatSendSelfFailed = 27001, + /// + /// 채팅 ChatType 오류 입니다. + /// + [pbr::OriginalName("ChatInvalidChatType")] ChatInvalidChatType = 27002, + /// + /// 귓속말을 블락한 유저에게 보낼 수 없습니다. + /// + [pbr::OriginalName("ChatBlockUserCannotWhisper")] ChatBlockUserCannotWhisper = 27003, + /// + /// 채팅 메시지 길이를 초과했습니다. + /// + [pbr::OriginalName("ChatInvalidMessageLength")] ChatInvalidMessageLength = 27004, + /// + /// 금칙어가 포함되어 있습니다. + /// + [pbr::OriginalName("ChatIncludeBanWord")] ChatIncludeBanWord = 27005, + /// + /// NoticeChatDoc이 null 입니다. + /// + [pbr::OriginalName("NoticeChatDocIsNull")] NoticeChatDocIsNull = 27501, + /// + ///============================================================================================= + /// 탈출 관련 오류 : 28000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EscapePositionNotAvailableTime")] EscapePositionNotAvailableTime = 28001, + /// + /// EscapePositionDoc이 null 입니다. + /// + [pbr::OriginalName("EscapePositionDocIsNull")] EscapePositionDocIsNull = 28002, + /// + ///============================================================================================= + /// 블록 유저 관련 오류 : 28500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BlockUserDocIsNull")] BlockUserDocIsNull = 28501, + /// + ///============================================================================================= + /// 캐릭터 프로필 관련 오류 : 29000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CharacterProfileDocIsNull")] CharacterProfileDocIsNull = 29001, + /// + ///============================================================================================= + /// 커스텀 디파인 Data 관련 오류 : 29300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CustomDefinedDataDocIsNull")] CustomDefinedDataDocIsNull = 29301, + /// + ///============================================================================================= + /// 커스텀 디파인 UI 관련 오류 : 29500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CustomDefinedUiDocIsNull")] CustomDefinedUiDocIsNull = 29501, + /// + ///============================================================================================= + /// 게임 옵션 관련 오류 : 30000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GameOptionDocIsNull")] GameOptionDocIsNull = 30001, + /// + /// GameOptionDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("GameOptionDocException")] GameOptionDocException = 30002, + /// + ///============================================================================================= + /// 레벨 관련 오류 : 30500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LevelDocIsNull")] LevelDocIsNull = 30501, + /// + ///============================================================================================= + /// 위치 관련 오류 : 31000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LocationDocIsNull")] LocationDocIsNull = 31001, + /// + /// 사용 할 수 없는 장소 입니다. + /// + [pbr::OriginalName("NotUsablePlace")] NotUsablePlace = 31002, + /// + /// Redis에 LocationCache 정보 저장을 실패했습니다. + /// + [pbr::OriginalName("RedisLocationCacheSetFailed")] RedisLocationCacheSetFailed = 31003, + /// + ///============================================================================================= + /// 재화 관련 오류 : 32000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MoneyDocIsNull")] MoneyDocIsNull = 32001, + /// + /// CurrencyControl이 초기화되지 않았습니다. + /// + [pbr::OriginalName("MoneyControlNotInitialize")] MoneyControlNotInitialize = 32002, + /// + /// 금전이 부족합니다. + /// + [pbr::OriginalName("MoneyNotEnough")] MoneyNotEnough = 32003, + /// + /// 금전 최대 보유량을 초과 했습니다. + /// + [pbr::OriginalName("MoneyMaxCountExceeded")] MoneyMaxCountExceeded = 32004, + /// + /// 재화 메타 데이터를 찾을 수 없습니다. + /// + [pbr::OriginalName("CurrencyMetaDataNotFound")] CurrencyMetaDataNotFound = 32005, + /// + ///============================================================================================= + /// 상점 관련 오류 : 33000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ShopProductTradingMeterDocIsNull")] ShopProductTradingMeterDocIsNull = 33001, + /// + /// MyHome 에 설치된 Item 입니다. + /// + [pbr::OriginalName("ShopIsMyHomeItem")] ShopIsMyHomeItem = 33002, + /// + /// ShopProduct 에 Shop Buy Type 이 잘못되었습니다. + /// + [pbr::OriginalName("InvalidShopBuyType")] InvalidShopBuyType = 33003, + /// + /// 리뉴얼 할 수 없는 상점입니다. + /// + [pbr::OriginalName("ShopProductCannotRenwal")] ShopProductCannotRenwal = 33004, + /// + /// 자동 갱신시간 전 후 1분 동안은 갱신 할 수 없다. + /// + [pbr::OriginalName("ShopProductNotRenwalTime")] ShopProductNotRenwalTime = 33005, + /// + /// 갱신 가능한 횟수를 이미 다 사용했습니다. + /// + [pbr::OriginalName("ShopProductRenewalCountAlreadyMax")] ShopProductRenewalCountAlreadyMax = 33006, + /// + /// 재판매할 수 없는 상품입니다. + /// + [pbr::OriginalName("ShopItemCannotSell")] ShopItemCannotSell = 33007, + /// + ///============================================================================================= + /// 보상 관련 오류 : 34000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("RewardInfoNotExist")] RewardInfoNotExist = 34000, + /// + /// 보상 타입 오류 + /// + [pbr::OriginalName("RewardInvalidType")] RewardInvalidType = 34001, + /// + /// 보상 타입 값 오류 + /// + [pbr::OriginalName("RewardInvalidTypeValue")] RewardInvalidTypeValue = 34002, + /// + /// + /// + [pbr::OriginalName("NotRequireAttributeValue")] NotRequireAttributeValue = 34003, + /// + /// 보상 그룹 아이디 문자열을 정수로 변환하는데 오류가 발생했습니다. + /// + [pbr::OriginalName("RewardGroupIdParsingFromStringToIntErorr")] RewardGroupIdParsingFromStringToIntErorr = 34004, + /// + ///============================================================================================= + /// Claime 관련 오류 : 35000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ClaimInvalidType")] ClaimInvalidType = 35000, + /// + ///클레임 멤버십 없음 + /// + [pbr::OriginalName("ClaimMembershipNotExist")] ClaimMembershipNotExist = 35001, + /// + ///클레임 정보 없음 + /// + [pbr::OriginalName("ClaimInfoNotExist")] ClaimInfoNotExist = 35002, + /// + ///클레임 보상시간이 안됐다 + /// + [pbr::OriginalName("ClaimRewardNotEnoughTime")] ClaimRewardNotEnoughTime = 35003, + /// + ///클레임 보상 이벤트 종료 + /// + [pbr::OriginalName("ClaimRewardEventEnd")] ClaimRewardEventEnd = 35004, + /// + ///ClaimDoc 존재하지 않음 + /// + [pbr::OriginalName("ClaimDocIsNull")] ClaimDocIsNull = 35005, + /// + ///============================================================================================= + /// 제작 관련 오류 : 36000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CraftRecipeDocIsNull")] CraftRecipeDocIsNull = 36001, + /// + /// CraftHelpDoc이 null 입니다. + /// + [pbr::OriginalName("CraftHelpDocIsNull")] CraftHelpDocIsNull = 36002, + /// + /// CraftDoc이 null 입니다. + /// + [pbr::OriginalName("CraftDocIsNull")] CraftDocIsNull = 36003, + /// + /// Crafting 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("CraftingMetaDataNotFound")] CraftingMetaDataNotFound = 36004, + /// + /// 제작이 완료되지 않았습니다. + /// + [pbr::OriginalName("CraftingNotFinish")] CraftingNotFinish = 36005, + /// + /// 제작중이지 않은 제작대 입니다. + /// + [pbr::OriginalName("CraftingNotCraftingAnchor")] CraftingNotCraftingAnchor = 36006, + /// + /// 제작대가 이미 사용중입니다. + /// + [pbr::OriginalName("CraftingAlreadyCrafting")] CraftingAlreadyCrafting = 36007, + /// + /// 제작대 프랍이 배치되어 있지 않습니다. + /// + [pbr::OriginalName("CraftingAnchorIsNotPlaced")] CraftingAnchorIsNotPlaced = 36008, + /// + /// 제작대 레시피가 등록되어 있지 않습니다. + /// + [pbr::OriginalName("CraftingRecipeIsNotRegister")] CraftingRecipeIsNotRegister = 36009, + /// + /// 레시피와 해당하지 않는 제작대 입니다. + /// + [pbr::OriginalName("CraftingAnchorIsNotMatchWithRecipe")] CraftingAnchorIsNotMatchWithRecipe = 36010, + /// + /// 도움 줄수 있는 횟수가 초과했습니다. + /// + [pbr::OriginalName("CraftingHelpCountOver")] CraftingHelpCountOver = 36011, + /// + /// 같은 유저에게 도움줄 횟수가 초과했습니다. + /// + [pbr::OriginalName("CraftingHelpSameUserCountOver")] CraftingHelpSameUserCountOver = 36012, + /// + /// 도움 받을수 있는 횟수가 초과했습니다. + /// + [pbr::OriginalName("CraftingHelpReceivedCountOver")] CraftingHelpReceivedCountOver = 36013, + /// + /// CraftHelpDoc이 비어있습니다. + /// + [pbr::OriginalName("CraftHelpDocIsEmpty")] CraftHelpDocIsEmpty = 36014, + /// + /// CraftDoc이 비어있습니다. + /// + [pbr::OriginalName("CraftDocIsEmpty")] CraftDocIsEmpty = 36015, + /// + /// 이미 제작이 완료되어 도움을 줄수 없습니다. + /// + [pbr::OriginalName("CraftingAlreadyFinish")] CraftingAlreadyFinish = 36016, + /// + /// 제작대 레시피가 이미 등록되어 있습니다. + /// + [pbr::OriginalName("CraftingRecipeIsAlreadyRegister")] CraftingRecipeIsAlreadyRegister = 36017, + /// + /// CraftDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CraftDocException")] CraftDocException = 36018, + /// + /// CraftHelpDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CraftHelpDocException")] CraftHelpDocException = 36019, + /// + /// CraftRecipeDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CraftRecipeDocException")] CraftRecipeDocException = 36020, + /// + /// 잘못된 제작 갯수의 요청입니다. + /// + [pbr::OriginalName("CraftInvalidRequestCount")] CraftInvalidRequestCount = 36021, + /// + ///============================================================================================= + /// UGQ 관련 오류 : 37000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UgqApiServerRequestFailed")] UgqApiServerRequestFailed = 37001, + /// + /// API Server와의 통신 후 객체 변환 중 오류발생 + /// + [pbr::OriginalName("UgqApiServerConvertToObjectFailed")] UgqApiServerConvertToObjectFailed = 37002, + /// + /// API Server검색 카테고리가 유효하지 않습니다. + /// + [pbr::OriginalName("UgqApiServerInvaildSearchCategoryType")] UgqApiServerInvaildSearchCategoryType = 37003, + /// + /// ugc 신고하기의 내용 길이가 맞지 않습니다. + /// + [pbr::OriginalName("UgqReportInvalidTextLength")] UgqReportInvalidTextLength = 37004, + /// + /// UGQ 메타 정보가 없습니다. + /// + [pbr::OriginalName("UgqQuestMetaNotExist")] UgqQuestMetaNotExist = 37005, + /// + /// UGQ 재화를 차감하기 위한 request 요청이 실패 + /// + [pbr::OriginalName("UgqBeginCreatorPointFail")] UgqBeginCreatorPointFail = 37006, + /// + /// shutdown 된 Ugq Revision 입니다. + /// + [pbr::OriginalName("UgqQuestShutdowned")] UgqQuestShutdowned = 37007, + /// + /// 이미 Test Complete 한 단계입니다. + /// + [pbr::OriginalName("UgqTestQuestAlreadyCompleted")] UgqTestQuestAlreadyCompleted = 37008, + /// + /// 아이템 사용해서 UGQ를 재 할당 받으려고 했으나, 해당 퀘스트에 대해서 완료한 기록이 없어서 에러 + /// + [pbr::OriginalName("UgqReassignUsingItemErrorCauseQuestNotComplete")] UgqReassignUsingItemErrorCauseQuestNotComplete = 37009, + /// + /// 아이템 사용해서 UGQ를 재 할당 받으려고 했으나, 이미 진행중인 퀘스트 존재 + /// + [pbr::OriginalName("UgqReassignUsingItemErrorCauseQuestAlreadyExist")] UgqReassignUsingItemErrorCauseQuestAlreadyExist = 37010, + /// + /// 아이템 사용해서 UGQ를 재 할당 받으려고 했으나, 새로운 리비전이 업데이트 되었음 + /// + [pbr::OriginalName("UgqReassignUsingItemErrorCauseNewRevisionUpdated")] UgqReassignUsingItemErrorCauseNewRevisionUpdated = 37011, + /// + /// UGQ데이터의 리비전 정보가 유효하지 않습니다. + /// + [pbr::OriginalName("UgqQuestDataInvalidRevision")] UgqQuestDataInvalidRevision = 37012, + /// + /// UGQ state가 유효하지 않아 포기할수 없다. + /// + [pbr::OriginalName("UgqAbortCannotCauseInvalidState")] UgqAbortCannotCauseInvalidState = 37013, + /// + /// UGQ Meta 생성 클래스가 존재 하지 않습니다. + /// + [pbr::OriginalName("UgqMetaGeneratorNotExist")] UgqMetaGeneratorNotExist = 37014, + /// + /// UGQ Revision이 업데이트가 되었습니다. + /// + [pbr::OriginalName("UgqQuestDataRevisionUpdated")] UgqQuestDataRevisionUpdated = 37015, + /// + /// UGQ 최신 리비전은 요청한 리비전보다 작을 수 없습니다. + /// + [pbr::OriginalName("UgqRevisionCannotSmallerThanRequestedRevision")] UgqRevisionCannotSmallerThanRequestedRevision = 37016, + /// + /// UGQ 리비전의 상태가 Live가 아닙니다. + /// + [pbr::OriginalName("UgqRevisionStateNotLive")] UgqRevisionStateNotLive = 37017, + /// + /// UGQ 리비전의 상태가 Live 혹은 Test 상태인경우만 ugq 데이터 호출이 가능 + /// + [pbr::OriginalName("UgqRevisionStateOnlyLivenAndTest")] UgqRevisionStateOnlyLivenAndTest = 37018, + /// + /// api 서버를 못찾을 경우 + /// + [pbr::OriginalName("UgqApiServerHttpRequestException")] UgqApiServerHttpRequestException = 37019, + /// + /// 리비전이 변경됐습니다. + /// + [pbr::OriginalName("UgqQuestRevisionChanged")] UgqQuestRevisionChanged = 37020, + /// + /// 본인 소유의 ugq는 본인이 받을수 없습니다. + /// + [pbr::OriginalName("UgqAssignCannotOwnedQuest")] UgqAssignCannotOwnedQuest = 37021, + /// + /// 이미 이전 리비전의 퀘스트를 소유중입니다. + /// + [pbr::OriginalName("UgqAlreadyOwnedOldRevisionQuest")] UgqAlreadyOwnedOldRevisionQuest = 37022, + /// + ///============================================================================================= + /// 시즌 패스 관련 오류 : 38000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("SeasonPassDocIsNull")] SeasonPassDocIsNull = 38001, + /// + /// SeasonPass 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("SeasonPassMetaDataNotFound")] SeasonPassMetaDataNotFound = 38002, + /// + /// SeasonPassReward 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("SeasonPassRewardMetaDataNotFound")] SeasonPassRewardMetaDataNotFound = 38003, + /// + /// 최대 등급에 도달해 더이상 경험치 획득할수 없습니다. + /// + [pbr::OriginalName("SeasonPassMaxGrade")] SeasonPassMaxGrade = 38004, + /// + /// 시즌 패스 기간이 아닙니다. + /// + [pbr::OriginalName("SeasonPassNotAblePeriod")] SeasonPassNotAblePeriod = 38005, + /// + /// 유료 시즌 패스를 이미 구입 했습니다. + /// + [pbr::OriginalName("SeasonPassAlreadyBuyCharged")] SeasonPassAlreadyBuyCharged = 38006, + /// + /// 이미 수령한 보상입니다. + /// + [pbr::OriginalName("SeasonPassAlreadyTakenReward")] SeasonPassAlreadyTakenReward = 38007, + /// + /// 해당 등급에 도달하지 못한 보상입니다. + /// + [pbr::OriginalName("SeasonPassNotEnoughGrade")] SeasonPassNotEnoughGrade = 38008, + /// + /// 해당 보상은 유료 시즌 패스가 필요합니다. + /// + [pbr::OriginalName("SeasonPassNeedChargedPass")] SeasonPassNeedChargedPass = 38009, + /// + /// 경험치 획득이 양수가 아닙니다. + /// + [pbr::OriginalName("SeasonPassInvalidExp")] SeasonPassInvalidExp = 38010, + /// + /// SeasonPassDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("SeasonPassDocException")] SeasonPassDocException = 38011, + /// + ///============================================================================================= + /// 랜드 경매 관련 오류 : 39000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LandAuctionRedisCacheSetFailed")] LandAuctionRedisCacheSetFailed = 39001, + /// + /// LandAuction 객체를 찾을 수 없습니다. + /// + [pbr::OriginalName("LandAuctionNotFound")] LandAuctionNotFound = 39002, + /// + /// LandAuction의 Auction Number 오류 입니다. + /// + [pbr::OriginalName("LandAuctionInvalidAuctionNumber")] LandAuctionInvalidAuctionNumber = 39003, + /// + /// LandAuction 입찰 금전 타입 오류 입니다. + /// + [pbr::OriginalName("LandAuctionBidCurrencyTypeInvalid")] LandAuctionBidCurrencyTypeInvalid = 30004, + /// + /// LandAuction 입찰금이 부족 합니다. + /// + [pbr::OriginalName("LandAuctionBidPriceNotEnough")] LandAuctionBidPriceNotEnough = 39005, + /// + /// LandAuction 캐시 정보가 Redis에 없습니다. + /// + [pbr::OriginalName("LandAuctionEmptyCacheInRedis")] LandAuctionEmptyCacheInRedis = 39006, + /// + /// LandAuctionRegistryDoc에 입력되어 있는 정보 오류 입니다. + /// + [pbr::OriginalName("LandAuctionRegistryInfoInvalid")] LandAuctionRegistryInfoInvalid = 39007, + /// + /// LandAuction가 시작 상태가 아닙니다. + /// + [pbr::OriginalName("LandAuctionNotStarted")] LandAuctionNotStarted = 39008, + /// + /// LandAuction 관련 Cache 와 DB 정보가 일치하지 않습니다. + /// + [pbr::OriginalName("LandAuctionMismatchBetweenCacheAndDb")] LandAuctionMismatchBetweenCacheAndDb = 39009, + /// + /// LandAuction가 이미 시작되었습니다. + /// + [pbr::OriginalName("LandAuctionAlreadyStarted")] LandAuctionAlreadyStarted = 39010, + /// + /// LandAuction 관련 Land 메타 정보에 LandItem 설정이 안되어 있습니다. + /// + [pbr::OriginalName("LandAuctionLandItemNotSet")] LandAuctionLandItemNotSet = 39011, + /// + /// LandAuction 관련 Land 메타 정보에 EditorType 오류 입니다. + /// + [pbr::OriginalName("LandAuctionEditorTypeInvalid")] LandAuctionEditorTypeInvalid = 39012, + /// + /// LandAuction 관련 이미 종료된 랜드 경매 입니다. + /// + [pbr::OriginalName("LandAuctionAlreadyEnded")] LandAuctionAlreadyEnded = 39013, + /// + /// LandAuction 관련 HighestBidUserAttrib 오류 입니다. + /// + [pbr::OriginalName("LandAuctionHighestBidUserAttribError")] LandAuctionHighestBidUserAttribError = 39014, + /// + /// LandAuction 관련 RefundBidPriceAttribError 오류 입니다. + /// + [pbr::OriginalName("LandAuctionRefundBidPriceAttribError")] LandAuctionRefundBidPriceAttribError = 39015, + /// + /// LandAuction 관련 현재 입찰자 RefundBidPriceAttribError 오류 입니다. + /// + [pbr::OriginalName("LandAuctionBidderRefundBidPriceAttribError")] LandAuctionBidderRefundBidPriceAttribError = 39016, + /// + ///============================================================================================= + /// Meta 데이터 오류 : 40000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GameConfigMetaDataNotFound")] GameConfigMetaDataNotFound = 40001, + /// + /// AttributeDefineition 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("AttributeDefineitionMetaDataNotFound")] AttributeDefineitionMetaDataNotFound = 40002, + /// + /// Requirement 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("RequirementMetaDataNotFound")] RequirementMetaDataNotFound = 40003, + /// + /// SystemMail 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("SystemMailMetaDataNotFound")] SystemMailMetaDataNotFound = 40004, + /// + /// Interior 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("InteriorMetaDataNotFound")] InteriorMetaDataNotFound = 40005, + /// + /// PropGroup 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("PropGroupMetaDataNotFound")] PropGroupMetaDataNotFound = 40006, + /// + ///============================================================================================= + /// Ai Chat 서버 오류 : 41000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("AiChatServerStart")] AiChatServerStart = 41001, + /// + /// AI Chat 서버 요청이 실패했습니다. + /// + [pbr::OriginalName("AiChatServerReqFailed")] AiChatServerReqFailed = 41002, + /// + /// Request가 잘못되었습니다. + /// + [pbr::OriginalName("AiChatServerBadrequest")] AiChatServerBadrequest = 41003, + /// + /// + /// + [pbr::OriginalName("AiChatServerForbidden")] AiChatServerForbidden = 41004, + /// + /// 인증되지 않은 사용자입니다. + /// + [pbr::OriginalName("AiChatServerUnauthorized")] AiChatServerUnauthorized = 41005, + /// + /// 사용자를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerUserNotFound")] AiChatServerUserNotFound = 41006, + /// + /// 캐릭터를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerCharacterNotFound")] AiChatServerCharacterNotFound = 41007, + /// + /// 리액션를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerReactionNotFound")] AiChatServerReactionNotFound = 41008, + /// + /// 예시문구를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerExampleDialongNotFound")] AiChatServerExampleDialongNotFound = 41009, + /// + /// 세션을 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerSessionNotFound")] AiChatServerSessionNotFound = 41010, + /// + /// 모델을 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerModelNotFound")] AiChatServerModelNotFound = 41011, + /// + /// 포인트가 충분하지 않습니다. + /// + [pbr::OriginalName("AiChatServerNotEnoughPoint")] AiChatServerNotEnoughPoint = 41012, + /// + /// 인자가 잘못되었습니다. + /// + [pbr::OriginalName("AiChatServerInvalidParameters")] AiChatServerInvalidParameters = 41013, + /// + /// 세션 리미트를 초과하였습니다. + /// + [pbr::OriginalName("AiChatServerSessionLimitExceeded")] AiChatServerSessionLimitExceeded = 41014, + /// + /// 서명이 잘못되었습니다. + /// + [pbr::OriginalName("AiChatServerInvalidSignature")] AiChatServerInvalidSignature = 41015, + /// + /// 유저가 이미 존재합니다. + /// + [pbr::OriginalName("AiChatServerUserAlreadyExists")] AiChatServerUserAlreadyExists = 41016, + /// + /// 삭제에 실패했습니다. + /// + [pbr::OriginalName("AiChatServerRemoveFailed")] AiChatServerRemoveFailed = 41017, + /// + /// 중복된 Guid입니다. + /// + [pbr::OriginalName("AiChatServerDuplicateGuid")] AiChatServerDuplicateGuid = 41018, + /// + /// 중복된 Id입니다. + /// + [pbr::OriginalName("AiChatServerDuplicateId")] AiChatServerDuplicateId = 41019, + /// + /// 채팅을 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerChatNotFound")] AiChatServerChatNotFound = 41020, + /// + /// JWT 사용 기간이 만료 되었습니다. + /// + [pbr::OriginalName("AiChatServerTokenExpired")] AiChatServerTokenExpired = 41021, + /// + /// 서버 내부 오류입니다. + /// + [pbr::OriginalName("AiChatServerInternalServer")] AiChatServerInternalServer = 41022, + /// + /// 잘못된 메시지입니다. + /// + [pbr::OriginalName("AiChatServerInvalidMessage")] AiChatServerInvalidMessage = 41023, + /// + /// 유저만 사용 가능한 API 입니다. 현재 JWT 의 role 이 user가 아닙니다. + /// + [pbr::OriginalName("AiChatServerUserOnlyFeature")] AiChatServerUserOnlyFeature = 41024, + /// + /// 해당 API에 접근 할 권한이 없습니다. 해당 리소스의 소유자 또는 운영자만 접근 가능합니다. + /// + [pbr::OriginalName("AiChatServerOwnershipError")] AiChatServerOwnershipError = 41025, + /// + /// 오더를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerChargeOrderNotFoundError")] AiChatServerChargeOrderNotFoundError = 41026, + /// + /// 클라이언트용 Ai Chat Error Code EndPoint + /// + [pbr::OriginalName("AiChatServerEnd")] AiChatServerEnd = 41100, + /// + /// 포인트 충전 재시도 + /// + [pbr::OriginalName("AiChatServerRetryChargePoint")] AiChatServerRetryChargePoint = 41101, + /// + /// Aichat 비활성화 상태입니다. + /// + [pbr::OriginalName("AiChatServerInactive")] AiChatServerInactive = 41102, + /// + /// AiChatDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("AiChatDocException")] AiChatDocException = 41103, + /// + ///============================================================================================= + /// Npc State 관련 오류 + ///============================================================================================= + /// + [pbr::OriginalName("NpcIsBusy")] NpcIsBusy = 42001, + /// + ///============================================================================================= + /// 칼리움 컨버터 관련 오류 : 43000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LackOfDailyCalium")] LackOfDailyCalium = 43001, + /// + /// 전체 변환 제공 Calium 이 부족합니다. + /// + [pbr::OriginalName("LackOfTotalCalium")] LackOfTotalCalium = 43002, + /// + /// Calium 변환에 필요한 재화가 부족합니다. + /// + [pbr::OriginalName("LackOfCommissionCurrency")] LackOfCommissionCurrency = 43003, + /// + /// 인벤토리에 요청한 변환 Material 수량이 부족합니다. + /// + [pbr::OriginalName("LackOfCommissionMaterials")] LackOfCommissionMaterials = 43004, + /// + /// 입력한 SlotId 가 잘못되었습니다. + /// + [pbr::OriginalName("InvalidMaterialSlotId")] InvalidMaterialSlotId = 43005, + /// + /// Calium Data 로딩에 실패했습니다. + /// + [pbr::OriginalName("FailToLoadCalium")] FailToLoadCalium = 43006, + /// + /// Calium Data 저장에 실패했습니다. + /// + [pbr::OriginalName("FailToSaveCaliumDynamo")] FailToSaveCaliumDynamo = 43007, + /// + ///============================================================================================= + /// 칼리움 교환소 관련 오류 : 43050 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LackOfConvertCalium")] LackOfConvertCalium = 43051, + /// + ///============================================================================================= + /// Web3 관련 오류 : 43100 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GetFailEchoSystemResponse")] GetFailEchoSystemResponse = 43100, + /// + /// EchoSystem 데이터 응답 오류 ( HttpError ) + /// + [pbr::OriginalName("FailToGetEchoSystemHttpError")] FailToGetEchoSystemHttpError = 43101, + /// + /// EchoSystem 데이터 응답 오류 ( 응답값 : null ) + /// + [pbr::OriginalName("FailToGetEchoSystemMessageNull")] FailToGetEchoSystemMessageNull = 43102, + /// + /// EchoSystem 데이터 응답 오류 ( Http 예외 발생 ) + /// + [pbr::OriginalName("FailToGetEchoSystemException")] FailToGetEchoSystemException = 43103, + /// + /// EchoSystem 데이터 전송 오류 + /// + [pbr::OriginalName("FailToSendEchoSystem")] FailToSendEchoSystem = 43104, + /// + /// EchoSystem Rollup Data 획득 실패 + /// + [pbr::OriginalName("FailToGetEchoSystemRollUp")] FailToGetEchoSystemRollUp = 43105, + /// + ///============================================================================================= + /// 서비스 관련 오류 + ///============================================================================================= + /// + [pbr::OriginalName("AccountLoginBlockEnable")] AccountLoginBlockEnable = 50001, + /// + ///============================================================================================= + /// 인스턴스룸 관련 오류 : 51000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("InstanceRoomException")] InstanceRoomException = 51001, + /// + /// 인스턴스 룸 추가 데이터 저장 오류입니다. + /// + [pbr::OriginalName("InstanceRoomCannotWriteExtraInfo")] InstanceRoomCannotWriteExtraInfo = 51002, + /// + /// 인스턴스 룸을 위한 시즌패스 구매를 하지 않았습니다. + /// + [pbr::OriginalName("InstanceRoomNotChargedSeasonPass")] InstanceRoomNotChargedSeasonPass = 51003, + /// + /// 인스턴스 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("InstanceMetaDataNotFound")] InstanceMetaDataNotFound = 51004, + /// + /// 인스턴스 메타 데이타 OverLimit 가 잘못되었습니다. + /// + [pbr::OriginalName("InstanceMetaDataOverLimitWrong")] InstanceMetaDataOverLimitWrong = 51005, + /// + /// AccessType 오류 입니다. + /// + [pbr::OriginalName("InstanceAccessTypeInvalid")] InstanceAccessTypeInvalid = 51006, + /// + /// 인스턴스 입장에 필요한 아이템이 충분하지 않습니다. + /// + [pbr::OriginalName("InstanceAccessItemNotEnough")] InstanceAccessItemNotEnough = 51007, + /// + /// 인스턴스 입장에 필요한 시즌패스를 구매 하지 않았습니다. + /// + [pbr::OriginalName("InstanceAccessSeasonPassNotCharged")] InstanceAccessSeasonPassNotCharged = 51008, + /// + /// 인스턴스 룸이 가득 찼습니다. + /// + [pbr::OriginalName("InstanceRoomIsFull")] InstanceRoomIsFull = 51009, + /// + /// 인스턴스 룸 Id 가 중복 되었습니다. + /// + [pbr::OriginalName("InstanceRoomIdDuplicated")] InstanceRoomIdDuplicated = 51010, + /// + /// 인스턴스 룸이 레디스에 존재하지 않습니다. + /// + [pbr::OriginalName("InstanceRoomNotExistAtRedis")] InstanceRoomNotExistAtRedis = 51011, + /// + ///============================================================================================= + /// Task Reservation 관련 오류 : 52000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TaskReservationDocIsNull")] TaskReservationDocIsNull = 52001, + /// + ///============================================================================================= + /// Billing 관련 오류. Web api : 53000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BillingGetPurchaseInfoFailed")] BillingGetPurchaseInfoFailed = 53001, + /// + /// billing 상태 업데이트 실패 입니다. + /// + [pbr::OriginalName("BillingUpdateStateFailed")] BillingUpdateStateFailed = 53002, + /// + /// billing 확인되지 않은 상태 입니다. + /// + [pbr::OriginalName("BillingInvalidStateType")] BillingInvalidStateType = 53003, + /// + /// billing productMetaId의 타입 변환에 실패했습니다. + /// + [pbr::OriginalName("BillingFailedParseProductMetaIdType")] BillingFailedParseProductMetaIdType = 53004, + /// + /// billing 정의되어 있지 않은 stateType입니다. + /// + [pbr::OriginalName("BillingStateTypeInvalid")] BillingStateTypeInvalid = 53005, + /// + /// billing 변환 할수 없는 stateType입니다. + /// + [pbr::OriginalName("BillingStateTypeCantBeChanged")] BillingStateTypeCantBeChanged = 53006, + /// + /// billing 환불 처리중 입니다. + /// + [pbr::OriginalName("BillingStateTypeRefund")] BillingStateTypeRefund = 53007, + /// + /// billing 환불 처리가 완료된 상품입니다. + /// + [pbr::OriginalName("BillingStateTypeRefundComplete")] BillingStateTypeRefundComplete = 53008, + /// + ///============================================================================================= + /// 이동 관련 오류 : 54000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TaxiMetaDataNotFound")] TaxiMetaDataNotFound = 54001, + /// + /// TaxiType 오류 입니다. + /// + [pbr::OriginalName("TaxiTypeInvalid")] TaxiTypeInvalid = 54002, + /// + /// 워프 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("WarpMetaDataNotFound")] WarpMetaDataNotFound = 54003, + /// + /// WarpTyoe 오류 입니다. + /// + [pbr::OriginalName("WarpTypeInvalid")] WarpTypeInvalid = 54004, + /// + ///============================================================================================= + /// 렌탈 관련 오류 : 55000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("RentalNotFound")] RentalNotFound = 55001, + /// + /// RentalDoc 로딩중에 중복된 미니맵 마커가 발견되었습니다. + /// + [pbr::OriginalName("RentalDocLoadDuplicatedRental")] RentalDocLoadDuplicatedRental = 55002, + /// + /// RentalDoc이 null 입니다. + /// + [pbr::OriginalName("RentalDocIsNull")] RentalDocIsNull = 55003, + /// + /// 렌탈이 불가능한 랜드 입니다. + /// + [pbr::OriginalName("RentalNotAvailableLand")] RentalNotAvailableLand = 55004, + /// + /// 렌탈 주소가 유효하지 않습니다. + /// + [pbr::OriginalName("RentalAddressInvalid")] RentalAddressInvalid = 55005, + /// + /// 렌탈 주소가 비어 있지 않습니다. + /// + [pbr::OriginalName("RentalAddressIsNotEmpty")] RentalAddressIsNotEmpty = 55006, + /// + /// 렌탈비용 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("RentalfeeMetaDataNotFound")] RentalfeeMetaDataNotFound = 55007, + /// + /// 렌탈 계약 정보가 변경 되었습니다. + /// + [pbr::OriginalName("RentalContractInfoUpdate")] RentalContractInfoUpdate = 55008, + /// + /// 렌탈비용이 허용치 보다 낮습니다. + /// + [pbr::OriginalName("RentalCurrencyAmountIsTooLow")] RentalCurrencyAmountIsTooLow = 56009, + /// + /// 렌탈 재화 종류 오류 입니다. + /// + [pbr::OriginalName("RentalCurrencyTypeIsWrong")] RentalCurrencyTypeIsWrong = 56010, + /// + ///============================================================================================= + /// Package 관련 오류 : 56000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("PackageLastOrderRecodeDocException")] PackageLastOrderRecodeDocException = 56001, + /// + /// PackageRepeatDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("PackageRepeatDocException")] PackageRepeatDocException = 56002, + /// + ///============================================================================================= + /// BeaconShop 관련 오류 : 57000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BeaconShopException")] BeaconShopException = 57001, + /// + /// BeaconShop 클라이언트에서 보낸 argument값이 잘못되었습니다. + /// + [pbr::OriginalName("BeaconShopInvalidArgument")] BeaconShopInvalidArgument = 57002, + /// + /// Beacon이 랜탈마이홈에 배치되지 않았습니다. + /// + [pbr::OriginalName("BeaconShopBeaconIsNotInRentalHome")] BeaconShopBeaconIsNotInRentalHome = 57003, + /// + /// BeaconShop에 등록할 아이템을 찾지 못했습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundItem")] BeaconShopNotFoundItem = 57004, + /// + /// BeaconShop에 등록할 아이템 갯수가 부족합니다. + /// + [pbr::OriginalName("BeaconShopNotEnoughItem")] BeaconShopNotEnoughItem = 57005, + /// + /// BeaconShop에 등록할 수 없는 아이템입니다. + /// + [pbr::OriginalName("BeaconShopInvalidItemForSell")] BeaconShopInvalidItemForSell = 57006, + /// + /// BeaconShop에 등록할 아이템의 메타를 찾을수 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundMetaData")] BeaconShopNotFoundMetaData = 57007, + /// + /// BeaconShop에 판매가격이 최소값보다 낮습니다. + /// + [pbr::OriginalName("BeaconShopLowSellingPrice")] BeaconShopLowSellingPrice = 57008, + /// + /// BeaconShop에 등록 수수료가 부족합니다. + /// + [pbr::OriginalName("BeaconShopNotEnoughRegisterFee")] BeaconShopNotEnoughRegisterFee = 57009, + /// + /// BeaconShop에 등록된 슬롯이 가득 찼습니다. + /// + [pbr::OriginalName("BeaconShopSlotIsFull")] BeaconShopSlotIsFull = 57010, + /// + /// BeaconShop에 하루 등록 횟수를 넘었습니다. + /// + [pbr::OriginalName("BeaconShopOverOneDayRegisterLimit")] BeaconShopOverOneDayRegisterLimit = 57011, + /// + /// BeaconShop 생성에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedToCreate")] BeaconShopFailedToCreate = 57012, + /// + /// BeaconShop 게시판에 등록을 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedRegisterBoard")] BeaconShopFailedRegisterBoard = 57013, + /// + /// BeaconShop 게시판에서 지우지 못했습니다. + /// + [pbr::OriginalName("BeaconShopFailedDeleteBoard")] BeaconShopFailedDeleteBoard = 57014, + /// + /// BeaconShop 게시판에 해당 물품이 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundItemFromBoard")] BeaconShopNotFoundItemFromBoard = 57015, + /// + /// BeaconShopProfileDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopProfileException")] BeaconShopProfileException = 57016, + /// + /// BeaconShop 등록에 필요한 골드가 부족합니다. + /// + [pbr::OriginalName("BeaconShopNotEnoughRegisterGold")] BeaconShopNotEnoughRegisterGold = 57017, + /// + /// BeaconShop 구매시 Item Amount가 부족합니다. + /// + [pbr::OriginalName("BeaconShopLackOfItemAmount")] BeaconShopLackOfItemAmount = 57018, + /// + /// BeaconShop 게시판에 아이템 목록 가져오기에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedGetBoardItem")] BeaconShopFailedGetBoardItem = 57019, + /// + /// BeaconShopSoldRecordDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopSoldRecordException")] BeaconShopSoldRecordException = 57020, + /// + /// BeaconShop BeaconInventory 불러오기에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedReloadBeaconShopInven")] BeaconShopFailedReloadBeaconShopInven = 57021, + /// + /// BeaconShop 새로운 정보가 갱신되었습니다. + /// + [pbr::OriginalName("BeaconShopUpdateNewData")] BeaconShopUpdateNewData = 57022, + /// + /// BeaconShop db 불러오기에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedUpdateDataFromDb")] BeaconShopFailedUpdateDataFromDb = 57023, + /// + /// BeaconShop 판매 내역이 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundSoldRecords")] BeaconShopNotFoundSoldRecords = 57024, + /// + /// BeaconShopProfileDoc이 없습니다. + /// + [pbr::OriginalName("BeaconShopProfileDocIsEmpty")] BeaconShopProfileDocIsEmpty = 57025, + /// + /// BeaconShopSoldPriceDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopSoldPriceException")] BeaconShopSoldPriceException = 57026, + /// + /// BeaconShop 정산할 내용이 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundSoldPrice")] BeaconShopNotFoundSoldPrice = 57027, + /// + /// BeaconShop db에서 찾지 못하거나 update에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedToFindOrUpdate")] BeaconShopFailedToFindOrUpdate = 57028, + /// + /// BeaconShop db에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopDbException")] BeaconShopDbException = 57029, + /// + /// BeaconShop에 판매가격이 최대값보다 높습니다. + /// + [pbr::OriginalName("BeaconShopOverSellingPrice")] BeaconShopOverSellingPrice = 57030, + /// + /// BeaconShop이 있는 마이홈 랜탈 기간이 곧 만료됩니다. + /// + [pbr::OriginalName("BeaconShopOverRentalSafeTime")] BeaconShopOverRentalSafeTime = 57031, + /// + /// BeaconShop에 판매 아이템의 판매가 비활성화 되었습니다. + /// + [pbr::OriginalName("BeaconShopDeactiveItemForSell")] BeaconShopDeactiveItemForSell = 57032, + /// + /// 없는 task + /// + [pbr::OriginalName("UgqInvalidTaskAction")] UgqInvalidTaskAction = 60001, + /// + /// disable 된 task + /// + [pbr::OriginalName("UgqTaskActionDisabled")] UgqTaskActionDisabled = 60002, + /// + /// 없는 dialog type + /// + [pbr::OriginalName("UgqInvalidDialogType")] UgqInvalidDialogType = 60003, + /// + /// 없는 dialog 조건 + /// + [pbr::OriginalName("UgqInvalidDialogCondition")] UgqInvalidDialogCondition = 60004, + /// + /// Validation 에러 + /// + [pbr::OriginalName("UgqValidationError")] UgqValidationError = 60005, + /// + /// 엔티티 없음 + /// + [pbr::OriginalName("UgqNullEntity")] UgqNullEntity = 60006, + /// + /// 상태 변경 실패 + /// + [pbr::OriginalName("UgqStateChangeError")] UgqStateChangeError = 60007, + /// + /// 퀘스트 슬롯 부족 + /// + [pbr::OriginalName("UgqNotEnoughQuestSlot")] UgqNotEnoughQuestSlot = 60008, + /// + /// 편집 불가 상태 + /// + [pbr::OriginalName("UgqNotAllowEdit")] UgqNotAllowEdit = 60009, + /// + /// Dialog가 Task에 없음 + /// + [pbr::OriginalName("UgqDialogIsNotInTask")] UgqDialogIsNotInTask = 60010, + /// + /// 이미지 입력 없음 + /// + [pbr::OriginalName("UgqRequireImage")] UgqRequireImage = 60011, + /// + /// 비컨 입력 필요 + /// + [pbr::OriginalName("UgqRequireBeacon")] UgqRequireBeacon = 60012, + /// + /// 하나의 비컨만 입력해야 함 + /// + [pbr::OriginalName("UgqBeaconInputError")] UgqBeaconInputError = 60013, + /// + /// 게임 DB 접근 에러 + /// + [pbr::OriginalName("UgqGameDBAccessError")] UgqGameDbaccessError = 60014, + /// + /// 내 소유 UgcNpc가 아님 + /// + [pbr::OriginalName("UgqNotOwnUgcNpc")] UgqNotOwnUgcNpc = 60015, + /// + /// 이미 계정이 존재함 + /// + [pbr::OriginalName("UgqAlreadyExistsAccount")] UgqAlreadyExistsAccount = 60016, + /// + /// 예외 발생 + /// + [pbr::OriginalName("UgqServerException")] UgqServerException = 60017, + /// + /// 유효하지 않은 웹포탈 인증 토큰 + /// + [pbr::OriginalName("UgqInvalidWebPortalToken")] UgqInvalidWebPortalToken = 60018, + /// + /// 유효하지 않은 토큰 + /// + [pbr::OriginalName("UgqInvalidToken")] UgqInvalidToken = 60019, + /// + /// 메타버스 계정이 필요함 + /// + [pbr::OriginalName("UgqRequireAccount")] UgqRequireAccount = 60020, + /// + /// 포인트 부족함 + /// + [pbr::OriginalName("UgqNotEnoughCreatorPoint")] UgqNotEnoughCreatorPoint = 60021, + /// + /// 최대 슬롯 수 제한 + /// + [pbr::OriginalName("UgqSlotLimit")] UgqSlotLimit = 60022, + /// + /// 트랜잭션 재시도 횟수 초과 + /// + [pbr::OriginalName("UgqExceedTransactionRetry")] UgqExceedTransactionRetry = 60023, + /// + /// 메타버스에 온라인 상태임 + /// + [pbr::OriginalName("UgqMetaverseOnline")] UgqMetaverseOnline = 60024, + /// + /// 인증 상태가 제거됨 + /// + [pbr::OriginalName("UgqAuthRemoved")] UgqAuthRemoved = 60025, + /// + /// 잘못된 프리셋 이미지 + /// + [pbr::OriginalName("UgqInvalidPresetImage")] UgqInvalidPresetImage = 60026, + /// + /// 재화 부족 + /// + [pbr::OriginalName("UgqNotEnoughCurrency")] UgqNotEnoughCurrency = 60027, + /// + /// 재화 사용 오류 발생 + /// + [pbr::OriginalName("UgqCurrencyError")] UgqCurrencyError = 60028, + /// + /// 이미 등급 변경 예약이 존재함 + /// + [pbr::OriginalName("UgqAlreadyExistsReserveGrade")] UgqAlreadyExistsReserveGrade = 60029, + /// + /// 유효하지 않은 예약 시간 + /// + [pbr::OriginalName("UgqInvalidReserveTime")] UgqInvalidReserveTime = 60030, + /// + /// 같은 등급으로 변경이 불가능. + /// + [pbr::OriginalName("UgqSameGradeReserve")] UgqSameGradeReserve = 60031, + /// + ///============================================================================================= + /// UGQ 관련 오류. InGame Api 오류 + ///============================================================================================= + /// + [pbr::OriginalName("UgqAlreadyBookmarked")] UgqAlreadyBookmarked = 61001, + /// + /// 북마크 안되어 있음 + /// + [pbr::OriginalName("UgqNotBookmarked")] UgqNotBookmarked = 61002, + /// + /// 이미 종아요 + /// + [pbr::OriginalName("UgqAlreadyLiked")] UgqAlreadyLiked = 61003, + /// + /// 좋아요 안되어 있음 + /// + [pbr::OriginalName("UgqNotLiked")] UgqNotLiked = 61004, + /// + /// 이미 신고함 + /// + [pbr::OriginalName("UgqAlreadyReported")] UgqAlreadyReported = 61005, + /// + /// 내 퀘스트가 아님 + /// + [pbr::OriginalName("UgqNotOwnQuest")] UgqNotOwnQuest = 61006, + /// + /// 잘못된 상태 + /// + [pbr::OriginalName("UgqInvalidState")] UgqInvalidState = 61007, + /// + ///============================================================================================= + /// NFT 관련 오류 : 62000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("NftForOwnerAllGetFailed")] NftForOwnerAllGetFailed = 62001, + /// + ///============================================================================================= + /// Broker Api Server 오류: 70000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("InternalServerError")] InternalServerError = 70001, + /// + /// Rdb에 장애가 발생했습니다. + /// + [pbr::OriginalName("RdbError")] RdbError = 70002, + /// + /// Dynamo에 장애가 발생했습니다. + /// + [pbr::OriginalName("DynamoError")] DynamoError = 70003, + /// + /// Api request 형식에 오류가 있습니다. + /// + [pbr::OriginalName("InvalidRequest")] InvalidRequest = 70011, + /// + /// 플래닛 id가 존재하지 않습니다. + /// + [pbr::OriginalName("PlanetIdNotFound")] PlanetIdNotFound = 71001, + /// + /// 해당 플래닛의 SecretKey가 일치하지 않습니다. + /// + [pbr::OriginalName("PlanetSecretKeyDoesNotMatched")] PlanetSecretKeyDoesNotMatched = 71002, + /// + /// 플래닛 인증 토큰에 오류가 있습니다. + /// + [pbr::OriginalName("InvalidPlanetJwt")] InvalidPlanetJwt = 71003, + /// + /// 플래닛 인증 토큰 유효기간이 만료됐습니다. + /// + [pbr::OriginalName("ExpiredPlanetJwt")] ExpiredPlanetJwt = 71004, + /// + /// 메타버스에 유저의 클라이언트 로그인되어 있습니다. 메타버스 앱을 정상 로그아웃 후 다시 시도해 주세요. + /// + [pbr::OriginalName("MetaverseClientOnConnected")] MetaverseClientOnConnected = 71201, + /// + /// 유저 인증 토큰에 오류가 있습니다. + /// + [pbr::OriginalName("InvalidUserJwt")] InvalidUserJwt = 71202, + /// + /// 유저 인증 토큰 유효기간이 만료됐습니다. + /// + [pbr::OriginalName("ExpiredUserJwt")] ExpiredUserJwt = 71203, + /// + /// 계정을 찾을 수 없습니다. + /// + [pbr::OriginalName("AccountNotFound")] AccountNotFound = 71301, + /// + /// 유저를 찾을 수 없습니다. + /// + [pbr::OriginalName("UserNotFound")] UserNotFound = 71302, + /// + /// 교환 주문 아이디가 존재하지 않습니다. + /// + [pbr::OriginalName("ExchangeOrderIdNotFound")] ExchangeOrderIdNotFound = 72001, + /// + /// 총 교환 주문 수량이 초과되었습니다. + /// + [pbr::OriginalName("ExchangeTotalOrderDailyLimitExceeded")] ExchangeTotalOrderDailyLimitExceeded = 72002, + /// + /// 유저 교환 주문 수량이 초과되었습니다. + /// + [pbr::OriginalName("ExchangeUserOrderDailyLimitExceeded")] ExchangeUserOrderDailyLimitExceeded = 72003, + /// + ///============================================================================================= + /// Bot 관련 오류. + ///============================================================================================= + /// + [pbr::OriginalName("BotPlayerIsNull")] BotPlayerIsNull = 962001, + /// + /// ClientToLoginRes가 null 입니다. + /// + [pbr::OriginalName("ClientToLoginResIsNull")] ClientToLoginResIsNull = 962002, + /// + /// ClientToLoginMessage가 null 입니다. + /// + [pbr::OriginalName("ClientToLoginMessageIsNull")] ClientToLoginMessageIsNull = 962003, + /// + /// ClientToGameRes가 null 입니다. + /// + [pbr::OriginalName("ClientToGameResIsNull")] ClientToGameResIsNull = 962004, + /// + /// ClientToGameMessage가 null 입니다. + /// + [pbr::OriginalName("ClientToGameMessageIsNull")] ClientToGameMessageIsNull = 962005, + /// + /// Server 연결 실패 + /// + [pbr::OriginalName("ConnectedToServerFail")] ConnectedToServerFail = 962006, + /// + /// 시나리오에 param 설정이 필요합니다. + /// + [pbr::OriginalName("ScenarionParamIsNull")] ScenarionParamIsNull = 962007, + /// + ///============================================================================================= + /// 전투 관련 오류. 삭제 가능성 있어서 뒷번호로 배정 + /// running mode도 아래에 배정 + ///============================================================================================= + /// + [pbr::OriginalName("BattleRoomContentsTypeOnly")] BattleRoomContentsTypeOnly = 11000001, + /// + /// 배틀인스턴스 타입이 잘못 됐습니다. + /// + [pbr::OriginalName("BattleInstanceTypeError")] BattleInstanceTypeError = 11000002, + /// + /// 이미 존재하는 인스턴스 입니다. + /// + [pbr::OriginalName("BattleInstanceInfoAlreadyExist")] BattleInstanceInfoAlreadyExist = 11000003, + /// + /// 존재하지 않는 인스턴스 입니다. + /// + [pbr::OriginalName("BattleInstanceInfoNotExist")] BattleInstanceInfoNotExist = 11000004, + /// + /// 플에이어 객체 + /// + [pbr::OriginalName("BattleInstanceJoinPlayerError")] BattleInstanceJoinPlayerError = 11000005, + /// + /// 사용 가능한 스폰 포인트가 없습니다. + /// + [pbr::OriginalName("BattleInstanceUsableSpawnPointNotExist")] BattleInstanceUsableSpawnPointNotExist = 11000006, + /// + /// 배틀 인스턴스 비활성화 상태 입니다. + /// + [pbr::OriginalName("BattleInstanceInActive")] BattleInstanceInActive = 11000007, + /// + /// 배틀 인스턴스 앵커 정보가 없습니다. + /// + [pbr::OriginalName("BattleInstanceNotExistAnchors")] BattleInstanceNotExistAnchors = 11000008, + /// + /// 배틀 인스턴스 pod 추가에 실패하였습니다. + /// + [pbr::OriginalName("BattleInstanceAddPodCombatFail")] BattleInstanceAddPodCombatFail = 11000009, + /// + /// 배틀 인스턴스 오브젝트 메타가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceObjectMetaNotExist")] BattleInstanceObjectMetaNotExist = 11000010, + /// + /// 배틀 인스턴스 오브젝트 상호 작용 가능한 시간이 아직 안됐습니다. + /// + [pbr::OriginalName("BattleInstanceObjectInteractionNotyetTime")] BattleInstanceObjectInteractionNotyetTime = 11000011, + /// + /// 배틀 인스턴스 오브젝트 정보가 없습니다. + /// + [pbr::OriginalName("BattleInstanceObjectNotExist")] BattleInstanceObjectNotExist = 11000012, + /// + /// pod combat이 이미 점유중 + /// + [pbr::OriginalName("BattleInstancePodCombatAlreadyOccupy")] BattleInstancePodCombatAlreadyOccupy = 11000013, + /// + /// 배틀 오브젝트가 활성화 되지 않았습니다. + /// + [pbr::OriginalName("BattleInstanceObjectInteractionNotActive")] BattleInstanceObjectInteractionNotActive = 11000015, + /// + /// Config Meta에 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceMetaConfigNotExistData")] BattleInstanceMetaConfigNotExistData = 11000016, + /// + /// Reward Meta에 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceMetaRewardNotExistData")] BattleInstanceMetaRewardNotExistData = 11000017, + /// + /// 픽업 포드 생성시간 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstancePickupPodGeneratedTimeNotExist")] BattleInstancePickupPodGeneratedTimeNotExist = 11000018, + /// + /// 픽업 포드 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstancePickupPodNotExistData")] BattleInstancePickupPodNotExistData = 11000019, + /// + /// 인스턴스에 유저정보가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceNotExistPlayerInfo")] BattleInstanceNotExistPlayerInfo = 11000020, + /// + /// 배틀 오브젝트 상호작용에 실패 하였습니다. + /// + [pbr::OriginalName("BattleInstanceInteractionFail")] BattleInstanceInteractionFail = 11000021, + /// + /// 픽업포드 리워드 생성에 실패 하였습니다. + /// + [pbr::OriginalName("BattleInstancePickupPodRewardAllocateError")] BattleInstancePickupPodRewardAllocateError = 11000022, + /// + /// 배틀 이벤트 정보가 없습니다. + /// + [pbr::OriginalName("BattleInstanceNotExistEventInfo")] BattleInstanceNotExistEventInfo = 11000023, + /// + /// 배틀 이벤트, 라운드가 거의다 끝나가는 시간입니다. + /// + [pbr::OriginalName("BattleInstanceClosingTime")] BattleInstanceClosingTime = 11000024, + /// + /// 배틀 인스턴스 시퀀스 파싱 에러 + /// + [pbr::OriginalName("BattleInstanceSeqParseError")] BattleInstanceSeqParseError = 11000025, + /// + /// 게임모드 조인 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeJoinHandlerNotExist")] GameModeJoinHandlerNotExist = 11000100, + /// + /// 게임모드 Init 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeInitHandlerNotExist")] GameModeInitHandlerNotExist = 11000101, + /// + /// 게임모드 조인 성공 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeJoinSuccessHandlerNotExist")] GameModeJoinSuccessHandlerNotExist = 11000102, + /// + /// 게임모드 생성 실패 + /// + [pbr::OriginalName("GameModeCreateFail")] GameModeCreateFail = 11000103, + /// + /// 게임모드가 null + /// + [pbr::OriginalName("GameModeClassIsNull")] GameModeClassIsNull = 11000104, + /// + /// 게임모드가 존재 + /// + [pbr::OriginalName("GameModeAlreadyExist")] GameModeAlreadyExist = 11000105, + /// + /// 유효하지 않은 AnchorGuid 입니다. + /// + [pbr::OriginalName("GameModeInvalidAnchorGuid")] GameModeInvalidAnchorGuid = 11000106, + /// + ///============================================================================================= + /// Server Metrics 오류: 99999100 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ServerMetricsTriggerHandlerNotFound")] ServerMetricsTriggerHandlerNotFound = 99999101, + [pbr::OriginalName("DupLogin")] DupLogin = 1, + [pbr::OriginalName("Moving")] Moving = 2, + [pbr::OriginalName("DbError")] DbError = 3, + [pbr::OriginalName("KickFail")] KickFail = 4, + [pbr::OriginalName("NotCorrectPassword")] NotCorrectPassword = 5, + [pbr::OriginalName("NotFoundUser")] NotFoundUser = 6, + [pbr::OriginalName("NoGameServer")] NoGameServer = 7, + [pbr::OriginalName("LoginPending")] LoginPending = 8, + [pbr::OriginalName("NotImplemented")] NotImplemented = 9, + [pbr::OriginalName("NotExistSelectedCharacter")] NotExistSelectedCharacter = 10, + [pbr::OriginalName("NotExistCharacter")] NotExistCharacter = 11, + [pbr::OriginalName("ServerLogicError")] ServerLogicError = 12, + [pbr::OriginalName("NoPermissions")] NoPermissions = 14, + [pbr::OriginalName("RedisFail")] RedisFail = 15, + [pbr::OriginalName("LoginFail")] LoginFail = 1000, + [pbr::OriginalName("DuplicatedUser")] DuplicatedUser = 1001, + [pbr::OriginalName("InvalidToken")] InvalidToken = 1002, + [pbr::OriginalName("NotCorrectServer")] NotCorrectServer = 1003, + [pbr::OriginalName("Inspection")] Inspection = 1004, + [pbr::OriginalName("BlackList")] BlackList = 1005, + [pbr::OriginalName("ServerFull")] ServerFull = 1006, + [pbr::OriginalName("NotFoundServer")] NotFoundServer = 1007, + [pbr::OriginalName("NotFoundTable")] NotFoundTable = 1008, + [pbr::OriginalName("TableError")] TableError = 1009, + [pbr::OriginalName("InvalidCondition")] InvalidCondition = 1100, + [pbr::OriginalName("CharCreateFail")] CharCreateFail = 2000, + [pbr::OriginalName("CharSelectFail")] CharSelectFail = 2100, + [pbr::OriginalName("CreateRoomFail")] CreateRoomFail = 3000, + [pbr::OriginalName("JoinRoomFail")] JoinRoomFail = 3100, + [pbr::OriginalName("JoinInstanceFail")] JoinInstanceFail = 3200, + [pbr::OriginalName("NotExistInstanceTicket")] NotExistInstanceTicket = 3201, + [pbr::OriginalName("LeaveInstanceFail")] LeaveInstanceFail = 3300, + [pbr::OriginalName("NotExistInstanceRoom")] NotExistInstanceRoom = 3400, + [pbr::OriginalName("NotCorrectInstanceRoom")] NotCorrectInstanceRoom = 3401, + [pbr::OriginalName("PosIsEmpty")] PosIsEmpty = 3402, + [pbr::OriginalName("EnterMyHomeFail")] EnterMyHomeFail = 3800, + [pbr::OriginalName("LeaveMyHomeFail")] LeaveMyHomeFail = 3900, + [pbr::OriginalName("ExchangeMyHomeFail")] ExchangeMyHomeFail = 4000, + [pbr::OriginalName("NotFoundMyHomeData")] NotFoundMyHomeData = 4001, + [pbr::OriginalName("NotMyHomeOwner")] NotMyHomeOwner = 4002, + [pbr::OriginalName("AlreadyHaveMyHome")] AlreadyHaveMyHome = 4003, + [pbr::OriginalName("ExchangeMyHomePropFail")] ExchangeMyHomePropFail = 4100, + [pbr::OriginalName("ExchangeBuildingFail")] ExchangeBuildingFail = 4300, + [pbr::OriginalName("ExchangeBuildingLFPropFail")] ExchangeBuildingLfpropFail = 4400, + [pbr::OriginalName("ExchangeInstanceFail")] ExchangeInstanceFail = 4500, + [pbr::OriginalName("ExchangeSocialActionSlotFail")] ExchangeSocialActionSlotFail = 4600, + [pbr::OriginalName("NotFoundSocialActionData")] NotFoundSocialActionData = 4602, + [pbr::OriginalName("NotInSocialActionSlot")] NotInSocialActionSlot = 4603, + [pbr::OriginalName("AlreadyHaveSocialAction")] AlreadyHaveSocialAction = 4604, + [pbr::OriginalName("NotFoundBuildingData")] NotFoundBuildingData = 4652, + [pbr::OriginalName("NotFoundFloorInfo")] NotFoundFloorInfo = 4653, + [pbr::OriginalName("NotFoundIndunData")] NotFoundIndunData = 4655, + [pbr::OriginalName("EnterFittingRoomFail")] EnterFittingRoomFail = 4700, + [pbr::OriginalName("NotEntetedFittingRoom")] NotEntetedFittingRoom = 4701, + [pbr::OriginalName("MakeFailOtp")] MakeFailOtp = 4702, + [pbr::OriginalName("NotExistRoomInfoForEnter")] NotExistRoomInfoForEnter = 4703, + [pbr::OriginalName("NotExistGameServerForEnter")] NotExistGameServerForEnter = 4704, + [pbr::OriginalName("PositionSaveFail")] PositionSaveFail = 4705, + [pbr::OriginalName("ExchangeEmotionSlotFail")] ExchangeEmotionSlotFail = 4800, + [pbr::OriginalName("EmotionSlotOutOfRange")] EmotionSlotOutOfRange = 4801, + [pbr::OriginalName("NotFoundAnchorGuidInMap")] NotFoundAnchorGuidInMap = 4899, + [pbr::OriginalName("NotFoundAnchorGuid")] NotFoundAnchorGuid = 4900, + [pbr::OriginalName("PropIsUsed")] PropIsUsed = 4902, + [pbr::OriginalName("PropTypeisWrong")] PropTypeisWrong = 4903, + [pbr::OriginalName("NotFoundEmotionData")] NotFoundEmotionData = 4920, + [pbr::OriginalName("NotInEmotionSlot")] NotInEmotionSlot = 4921, + [pbr::OriginalName("CreateItemFail")] CreateItemFail = 4996, + [pbr::OriginalName("AddItemFail")] AddItemFail = 4997, + [pbr::OriginalName("DeleteItemFail")] DeleteItemFail = 4998, + [pbr::OriginalName("NoMoreAddItem")] NoMoreAddItem = 4999, + [pbr::OriginalName("NotFoundItem")] NotFoundItem = 5000, + [pbr::OriginalName("NotEnoughItem")] NotEnoughItem = 5001, + [pbr::OriginalName("InvalidSlotIndex")] InvalidSlotIndex = 5002, + [pbr::OriginalName("DuplicatedItemGuid")] DuplicatedItemGuid = 5003, + [pbr::OriginalName("NotFoundItemTableId")] NotFoundItemTableId = 5004, + [pbr::OriginalName("NotSelectedChar")] NotSelectedChar = 5005, + [pbr::OriginalName("NotFoundMap")] NotFoundMap = 5006, + [pbr::OriginalName("NotEmptySlot")] NotEmptySlot = 5007, + [pbr::OriginalName("EmptySlot")] EmptySlot = 5008, + [pbr::OriginalName("NotFoundBuffTableId")] NotFoundBuffTableId = 5009, + [pbr::OriginalName("NotFoundBuff")] NotFoundBuff = 5010, + [pbr::OriginalName("NotExistForecedMoveInfo")] NotExistForecedMoveInfo = 5011, + [pbr::OriginalName("DuplicatedNickName")] DuplicatedNickName = 5013, + [pbr::OriginalName("AlreadySetNickName")] AlreadySetNickName = 5014, + [pbr::OriginalName("DisallowedCharacters")] DisallowedCharacters = 5015, + [pbr::OriginalName("DbUpdateFailed")] DbUpdateFailed = 5016, + [pbr::OriginalName("InvalidArgument")] InvalidArgument = 5017, + [pbr::OriginalName("MailSystemError")] MailSystemError = 5030, + [pbr::OriginalName("InvalidTarget")] InvalidTarget = 5031, + [pbr::OriginalName("NotFoundMail")] NotFoundMail = 5032, + [pbr::OriginalName("EmptyItemInMail")] EmptyItemInMail = 5033, + [pbr::OriginalName("MailSendCountOver")] MailSendCountOver = 5034, + [pbr::OriginalName("ChangedNickName")] ChangedNickName = 5035, + [pbr::OriginalName("StateChangeFailed")] StateChangeFailed = 5040, + [pbr::OriginalName("NotFoundTarget")] NotFoundTarget = 5050, + [pbr::OriginalName("BlockedFromTarget")] BlockedFromTarget = 5051, + [pbr::OriginalName("LogOffTarget")] LogOffTarget = 5052, + [pbr::OriginalName("CantSendToSelf")] CantSendToSelf = 5053, + [pbr::OriginalName("BuffTypeIsWrong")] BuffTypeIsWrong = 5070, + [pbr::OriginalName("RegisterToolSlotFail")] RegisterToolSlotFail = 5080, + [pbr::OriginalName("DeregisterToolSlotFail")] DeregisterToolSlotFail = 5081, + [pbr::OriginalName("ToolSlotOutOfRange")] ToolSlotOutOfRange = 5082, + [pbr::OriginalName("NotFoundToolSlot")] NotFoundToolSlot = 5083, + [pbr::OriginalName("EmptyToolSlot")] EmptyToolSlot = 5084, + [pbr::OriginalName("FolderNameExceededLength")] FolderNameExceededLength = 5090, + [pbr::OriginalName("FolderNameAlreadyExist")] FolderNameAlreadyExist = 5091, + [pbr::OriginalName("FolderNameNotExist")] FolderNameNotExist = 5092, + [pbr::OriginalName("FolderNameAlreadyMaxHoldCount")] FolderNameAlreadyMaxHoldCount = 5093, + [pbr::OriginalName("FolderCountExceed")] FolderCountExceed = 5094, + [pbr::OriginalName("FolderCreateFail")] FolderCreateFail = 5095, + [pbr::OriginalName("FolderReNameCannotDefault")] FolderReNameCannotDefault = 5096, + [pbr::OriginalName("FolderOdertypeInvalid")] FolderOdertypeInvalid = 5097, + [pbr::OriginalName("FriendRequestNotExistInfo")] FriendRequestNotExistInfo = 5100, + [pbr::OriginalName("FriendRequestAlreadySend")] FriendRequestAlreadySend = 5101, + [pbr::OriginalName("FriendRequestAlreadyReceive")] FriendRequestAlreadyReceive = 5102, + [pbr::OriginalName("FriendRequestCantSendToSelf")] FriendRequestCantSendToSelf = 5103, + [pbr::OriginalName("FriendRequestNotExistReceivedInfo")] FriendRequestNotExistReceivedInfo = 5104, + [pbr::OriginalName("FriendRequestAlreadyFriend")] FriendRequestAlreadyFriend = 5105, + [pbr::OriginalName("InvalidType")] InvalidType = 5106, + [pbr::OriginalName("InvalidAttributeSlot")] InvalidAttributeSlot = 5107, + [pbr::OriginalName("FriendRequestCannotSendBlockUser")] FriendRequestCannotSendBlockUser = 5119, + [pbr::OriginalName("AddFriendAlreadyBlockUser")] AddFriendAlreadyBlockUser = 5120, + [pbr::OriginalName("AddFriendNotExistCharacter")] AddFriendNotExistCharacter = 5121, + [pbr::OriginalName("AddFriendAlreadyFriend")] AddFriendAlreadyFriend = 5122, + [pbr::OriginalName("AddFriendAlreadyCancelRequest")] AddFriendAlreadyCancelRequest = 5123, + [pbr::OriginalName("AddFriendAlreadyExpired")] AddFriendAlreadyExpired = 5124, + [pbr::OriginalName("FriendInfoNotExist")] FriendInfoNotExist = 5130, + [pbr::OriginalName("FriendInfoMyCountAlreadyMaxCount")] FriendInfoMyCountAlreadyMaxCount = 5131, + [pbr::OriginalName("FriendInfoOthersCountAlreadyMaxCount")] FriendInfoOthersCountAlreadyMaxCount = 5132, + [pbr::OriginalName("FriendInfoOffline")] FriendInfoOffline = 5133, + [pbr::OriginalName("FriendInfoAlreadyExist")] FriendInfoAlreadyExist = 5134, + [pbr::OriginalName("FriendInviteMyPosIsNotMyHome")] FriendInviteMyPosIsNotMyHome = 5140, + [pbr::OriginalName("FriendInviteDontDisturbState")] FriendInviteDontDisturbState = 5141, + [pbr::OriginalName("FriendInviteExpireTimeRemain")] FriendInviteExpireTimeRemain = 5142, + [pbr::OriginalName("FriendInviteWaitingOtherInvite")] FriendInviteWaitingOtherInvite = 5143, + [pbr::OriginalName("FriendInviteAlreadyExpire")] FriendInviteAlreadyExpire = 5144, + [pbr::OriginalName("FriendKickMyPosIsNotMyHome")] FriendKickMyPosIsNotMyHome = 5145, + [pbr::OriginalName("FriendKickMemberNotExist")] FriendKickMemberNotExist = 5146, + [pbr::OriginalName("FriendIsInAnotherMyhome")] FriendIsInAnotherMyhome = 5147, + [pbr::OriginalName("BlockUserMaxCount")] BlockUserMaxCount = 5150, + [pbr::OriginalName("BlockUserAlreadyBlock")] BlockUserAlreadyBlock = 5151, + [pbr::OriginalName("BlockUserCannotSendMailTo")] BlockUserCannotSendMailTo = 5152, + [pbr::OriginalName("BlockedByOther")] BlockedByOther = 5153, + [pbr::OriginalName("BlockUserCannotWhisperTo")] BlockUserCannotWhisperTo = 5154, + [pbr::OriginalName("BlockInfoEmpty")] BlockInfoEmpty = 5155, + [pbr::OriginalName("BlockUserCannotInviteParty")] BlockUserCannotInviteParty = 5156, + [pbr::OriginalName("CartSellTypeMissMatchWithTable")] CartSellTypeMissMatchWithTable = 5200, + [pbr::OriginalName("CartFullStackofItem")] CartFullStackofItem = 5201, + [pbr::OriginalName("CartisFull")] CartisFull = 5202, + [pbr::OriginalName("BanNickName")] BanNickName = 5203, + [pbr::OriginalName("CurrencyNotFoundData")] CurrencyNotFoundData = 5400, + [pbr::OriginalName("CurrencyNotEnough")] CurrencyNotEnough = 5401, + [pbr::OriginalName("CurrencyInvalidValue")] CurrencyInvalidValue = 5402, + [pbr::OriginalName("StartBuffFailed")] StartBuffFailed = 5500, + [pbr::OriginalName("StopBuffFailed")] StopBuffFailed = 5501, + [pbr::OriginalName("NotFoundNickName")] NotFoundNickName = 5600, + [pbr::OriginalName("NotFoundTattooAttributeData")] NotFoundTattooAttributeData = 5800, + [pbr::OriginalName("NotFoundShopId")] NotFoundShopId = 5900, + [pbr::OriginalName("TimeOverForPurchase")] TimeOverForPurchase = 5901, + [pbr::OriginalName("NotEnoughAttribute")] NotEnoughAttribute = 5902, + [pbr::OriginalName("InvalidGender")] InvalidGender = 5903, + [pbr::OriginalName("IsEquipItem")] IsEquipItem = 5904, + [pbr::OriginalName("InvalidItem")] InvalidItem = 5905, + [pbr::OriginalName("AlreadyRegistered")] AlreadyRegistered = 5906, + [pbr::OriginalName("NotFoundShopItem")] NotFoundShopItem = 5907, + [pbr::OriginalName("NotEnoughShopItem")] NotEnoughShopItem = 5908, + [pbr::OriginalName("InvalidItemCount")] InvalidItemCount = 5909, + [pbr::OriginalName("InventoryFull")] InventoryFull = 5910, + [pbr::OriginalName("NotFoundProductId")] NotFoundProductId = 5911, + [pbr::OriginalName("RandomBoxItemDataInvalid")] RandomBoxItemDataInvalid = 6100, + [pbr::OriginalName("NotExistGachaData")] NotExistGachaData = 6101, +} + +#endregion + +#region Messages +/// +/// 각종 결과 전달용 정보 - kangms +/// +[global::System.SerializableAttribute] +public sealed partial class Result : pb::IMessage +#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage +#endif +{ + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Result()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::DefineResultReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Result() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Result(Result other) : this() { + errorCode_ = other.errorCode_; + resultString_ = other.resultString_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Result Clone() { + return new Result(this); + } + + /// Field number for the "errorCode" field. + public const int ErrorCodeFieldNumber = 1; + private global::ServerErrorCode errorCode_ = global::ServerErrorCode.Success; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::ServerErrorCode ErrorCode { + get { return errorCode_; } + set { + errorCode_ = value; + } + } + + /// Field number for the "resultString" field. + public const int ResultStringFieldNumber = 2; + private string resultString_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string ResultString { + get { return resultString_; } + set { + resultString_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as Result); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(Result other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (ErrorCode != other.ErrorCode) return false; + if (ResultString != other.ResultString) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (ErrorCode != global::ServerErrorCode.Success) hash ^= ErrorCode.GetHashCode(); + if (ResultString.Length != 0) hash ^= ResultString.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (ErrorCode != global::ServerErrorCode.Success) { + output.WriteRawTag(8); + output.WriteEnum((int) ErrorCode); + } + if (ResultString.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ResultString); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (ErrorCode != global::ServerErrorCode.Success) { + output.WriteRawTag(8); + output.WriteEnum((int) ErrorCode); + } + if (ResultString.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ResultString); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (ErrorCode != global::ServerErrorCode.Success) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ErrorCode); + } + if (ResultString.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ResultString); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(Result other) { + if (other == null) { + return; + } + if (other.ErrorCode != global::ServerErrorCode.Success) { + ErrorCode = other.ErrorCode; + } + if (other.ResultString.Length != 0) { + ResultString = other.ResultString; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + ErrorCode = (global::ServerErrorCode) input.ReadEnum(); + break; + } + case 18: { + ResultString = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + ErrorCode = (global::ServerErrorCode) input.ReadEnum(); + break; + } + case 18: { + ResultString = input.ReadString(); + break; + } + } + } + } + #endif + +} + +#endregion + + +#endregion Designer generated code diff --git a/Protocol/out-Proto/DefineResult.cs.r131478 b/Protocol/out-Proto/DefineResult.cs.r131478 new file mode 100644 index 0000000..f2c4536 --- /dev/null +++ b/Protocol/out-Proto/DefineResult.cs.r131478 @@ -0,0 +1,4950 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Define_Result.proto +// +#pragma warning disable 1591, 0612, 3021, 8981 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +/// Holder for reflection information generated from Define_Result.proto +public static partial class DefineResultReflection { + + #region Descriptor + /// File descriptor for Define_Result.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static DefineResultReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChNEZWZpbmVfUmVzdWx0LnByb3RvIkMKBlJlc3VsdBIjCgllcnJvckNvZGUY", + "ASABKA4yEC5TZXJ2ZXJFcnJvckNvZGUSFAoMcmVzdWx0U3RyaW5nGAIgASgJ", + "KraAAgoPU2VydmVyRXJyb3JDb2RlEgsKB1N1Y2Nlc3MQABIdChBSZXN1bHRD", + "b2RlTm90U2V0EP///////////wESFgoRVHJ5Q2F0Y2hFeGNlcHRpb24QkU4S", + "FAoPRG90TmV0RXhjZXB0aW9uEJJOEhYKEVByb3VkTmV0RXhjZXB0aW9uEJNO", + "EhYKEVJhYmJpdE1xRXhjZXB0aW9uEJROEhYKEUR5bmFtb0RiRXhjZXB0aW9u", + "EJVOEh4KGUR5bmFtb0RiVHJhbnNhY3RFeGNlcHRpb24Qlk4SEwoOUmVkaXNF", + "eGNlcHRpb24Ql04SFgoRTWV0YUluZm9FeGNlcHRpb24QmE4SFQoQTXlTcWxE", + "YkV4Y2VwdGlvbhCZThIVChBNb25nb0RiRXhjZXB0aW9uEJpOEiUKIE5sb2dX", + "aXRoQXdzQ2xvdWRXYXRjaFNldHVwRmFpbGVkEK9OEhcKEk5sb2dOb3RJbml0", + "aWFsaXplZBCwThIUCg9Mb2dBY3Rpb25Jc051bGwQw04SFgoRTG9nQXBwZW5k", + "ZXJJc051bGwQxE4SFwoSTG9nRm9ybWF0dGVySXNOdWxsEMVOEhkKFExvZ0Fj", + "dGlvblR5cGVJbnZhbGlkEMZOEhIKDVJtaUhvc3RJc051bGwQ9U4SHQoYUm1p", + "SG9zdEhhbmRsZXJCaW5kRmFpbGVkEPZOEhkKFFN1YkhhbmRsZXJCaW5kRmFp", + "bGVkEPdOEhYKEVByb3h5QXR0YWNoRmFpbGVkEPhOEh4KGVNldE1lc3NhZ2VN", + "YXhMZW5ndGhGYWlsZWQQ+U4SJAofUGFja2V0UmVjdkhhbmRsZXJSZWdpc3Rl", + "ckZhaWxlZBCnTxIkCh9QYWNrZXRTZW5kSGFuZGxlclJlZ2lzdGVyRmFpbGVk", + "EKhPEhYKEVBhY2tldFJlY3ZJbnZhbGlkEKlPEh4KGVJhY2tldFJlY3ZIYW5k", + "bGVyTm90Rm91bmQQqk8SHgoZTGFyZ2VQYWNrZXROb3RBbGxSZWNlaXZlZBCr", + "TxIcChdMYXJnZVBhY2tldFJlY3ZUaW1lT3ZlchCsTxIiCh1MYXJnZVBhY2tl", + "dFByb2Nlc3NUeXBlSW52YWxpZBCtTxIaChVMYXJnZVBhY2tldERhdGFJc051", + "bGwQrk8SGQoUTGFyZ2VQYWNrZXRFeGNlcHRpb24Qr08SFwoSRGJRdWVyeVR5", + "cGVJbnZhbGlkENlPEikKJER5bmFtb0RiVHJhbnNhY3Rpb25DYW5jZWxlZEV4", + "Y2VwdGlvbhDjTxIkCh9EeW5hbW9EYkFtYXpvbkR5bmFtb0RiRXhjZXB0aW9u", + "EORPEiMKHkR5bmFtb0RiQW1hem9uU2VydmljZUV4Y2VwdGlvbhDlTxIdChhE", + "eW5hbW9EYkNvbmZpZ0xvYWRGYWlsZWQQ5k8SGgoVRHluYW1vRGJDb25uZWN0", + "RmFpbGVkEOdPEh4KGUR5bmFtb0RiVGFibGVDcmVhdGVGYWlsZWQQ6E8SHgoZ", + "RHluYW1vRGJUYWJsZU5vdENvbm5lY3RlZBDpTxIYChNEeW5hbW9EYlF1ZXJ5", + "RmFpbGVkEOpPEh0KGER5bmFtb0RiSXRlbVNpemVFeGNlZWRlZBDrTxIiCh1E", + "eW5hbW9EYlF1ZXJ5Tm9NYXRjaEF0dHJpYnV0ZRDsTxIpCiREeW5hbW9EYlRy", + "YW5zYWN0aW9uQ29uZmxpY3RFeGNlcHRpb24Q7U8SHAoXRHluYW1vRGJFeHBy", + "ZXNzaW9uRXJyb3IQ7k8SHwoaRHluYW1vRGJQcmltYXJ5S2V5Tm90Rm91bmQQ", + "708SHQoYRHluYW1vRGJUYWJsZU5hbWVJbnZhbGlkEPBPEhwKF0R5bmFtb0Ri", + "Q29ubmVjdG9ySXNOdWxsEPFPEiAKG0R5bmFtb0RiVGFibGVOYW1lRHVwbGlj", + "YXRlZBDyTxIbChZEeW5hbW9EYlF1ZXJ5RXhjZXB0aW9uEPdPEh0KGER5bmFt", + "b0RiUXVlcnlOb1JlcXVlc3RlZBD4TxInCiJEeW5hbW9EYlF1ZXJ5Tm90Rm91", + "bmREb2N1bWVudFF1ZXJ5EPlPEisKJkR5bmFtb0RiUXVlcnlFeGNlcHRpb25O", + "b3RpZmllck5vdEZvdW5kEPpPEikKJER5bmFtb0RiRG9jdW1lbnRJc051bGxJ", + "blF1ZXJ5Q29udGV4dBCBUBIeChlEeW5hbW9EYkRvY3VtZW50SXNJbnZhbGlk", + "EIJQEiwKJ0R5bmFtb0RiRG9jdW1lbnRRdWVyeUNvbnRleHRUeXBlSW52YWxp", + "ZBCDUBIkCh9EeW5hbW9EYkRvY3VtZW50Q29weUZhaWxlZFRvRG9jEIRQEiEK", + "HER5bmFtb0RiRG9jdW1lbnRVcHNlcnRGYWlsZWQQhVASIQocRHluYW1vRGJJ", + "dGVtUmVxdWVzdElzSW52YWxpZBCLUBIvCipEeW5hbW9EYkl0ZW1SZXF1ZXN0", + "UXVlcnlDb250ZXh0VHlwZUludmFsaWQQjFASLQooRHluYW1vRGJJdGVtUmVx", + "dWVzdFVwZGF0ZUV4cHJlc3Npb25FbXB0eRCNUBIZChREeW5hbW9EYkRvY1Br", + "SW52YWxpZBCVUBIZChREeW5hbW9EYkRvY1NrSW52YWxpZBCWUBIkCh9EeW5h", + "bW9EYkRvY0F0dHJpYlR5cGVEdXBsaWNhdGVkEJdQEiQKH0R5bmFtb0RiRG9j", + "Q29weUZhaWxlZFRvRG9jdW1lbnQQmFASJgohRHluYW1vRGJEb2NDb3B5RmFp", + "bGVkRnJvbURvY3VtZW50EJlQEhwKF0R5bmFtb0RiRG9jVHlwZU5vdE1hdGNo", + "EJpQEhsKFkR5bmFtb0RiUmVxdWVzdEludmFsaWQQm1ASJAofRHluYW1vRGJE", + "b2NBdHRyaWJ1dGVTdGF0ZU5vdFNldBCcUBInCiJEeW5hbW9EYkRvY0F0dHJp", + "YldyYXBwZXJDb3B5RmFpbGVkEJ1QEiYKIUR5bmFtb0RiRG9jQXR0cmlidXRl", + "R2V0dGluZ0ZhaWxlZBCeUBIfChpEeW5hbW9EYkRvY0xpbmtQa1NrSW52YWxp", + "ZBCfUBIjCh5EeW5hbW9EYkRvY0F0dHJpYldyYXBwZXJJc051bGwQoFASIAob", + "TXlTcWxDb25uZWN0aW9uQ3JlYXRlRmFpbGVkEKlQEh4KGU15U3FsQ29ubmVj", + "dGlvbk9wZW5GYWlsZWQQqlASGgoVTXlTcWxEYlF1ZXJ5RXhjZXB0aW9uEKtQ", + "EiEKHE1vbmdvRGJJbml0QW5kVmVmaWZ5RGJGYWlsZWQQs1ASHQoYUmVkaXNT", + "ZXJ2ZXJDb25uZWN0RmFpbGVkEL1QEhwKF1JlZGlzU3RyaW5nc1dyaXRlRmFp", + "bGVkEL5QEhsKFlJlZGlzU3RyaW5nc1JlYWRGYWlsZWQQv1ASGQoUUmVkaXNT", + "ZXRzV3JpdGVGYWlsZWQQwFASGAoTUmVkaXNTZXRzUmVhZEZhaWxlZBDBUBIf", + "ChpSZWRpc1NvcnRlZFNldHNXcml0ZUZhaWxlZBDCUBIeChlSZWRpc1NvcnRl", + "ZFNldHNSZWFkRmFpbGVkEMNQEhsKFlJlZGlzSGFzaGVzV3JpdGVGYWlsZWQQ", + "xFASGgoVUmVkaXNIYXNoZXNSZWFkRmFpbGVkEMVQEhoKFVJlZGlzTGlzdHNX", + "cml0ZUZhaWxlZBDGUBIZChRSZWRpc0xpc3RzUmVhZEZhaWxlZBDHUBIbChZS", + "ZWRpc1JlcXVlc3RLZXlJc0VtcHR5EMhQEh0KGFJlZGlzTG9naW5DYWNoZUdl", + "dEZhaWxlZBDJUBIdChhSZWRpc0xvZ2luQ2FjaGVTZXRGYWlsZWQQylASIAob", + "UmVkaXNQcml2YXRlQ2FjaGVEdXBsaWNhdGVkEMtQEiUKIFJlZGlzR2xvYmFs", + "U2hhcmVkQ2FjaGVEdXBsaWNhdGVkEMxQEikKJFJlZGlzTG9naW5DYWNoZU93", + "bmVyVXNlckd1aWROb3RNYXRjaBDNUBIjCh5SZWRpc0dsb2JhbFBhcnR5Q2Fj", + "aGVHZXRGYWlsZWQQzlASJQogUmVkaXNHbG9iYWxQYXJ0eUNhY2hlV3JpdGVG", + "YWlsZWQQz1ASKwomUmVkaXNHbG9iYWxQYXJ0eU1lbWJlckNhY2hlV3JpdGVG", + "YWlsZWQQ0FASKwomUmVkaXNHbG9iYWxQYXJ0eVNlcnZlckNhY2hlV3JpdGVG", + "YWlsZWQQ0VASNAovUmVkaXNHbG9iYWxQYXJ0eUludml0ZVBhcnR5U2VuZENh", + "Y2hlV3JpdGVGYWlsZWQQ0lASKAojUmVkaXNJbnN0YW5jZVJvb21JbmZvQ2Fj", + "aGVHZXRGYWlsZWQQ01ASKQokUmVkaXNVZ2NOcGNUb3RhbFJhbmtDYWNoZVdy", + "aXRlRmFpbGVkENRQEiAKG1JlZGlzUmVxdWVzdEhhbmRsZXJOb3RGb3VuZBDV", + "UBIgChtSYWJiaXRNcUNvbnN1bWVyU3RhcnRGYWlsZWQQoVESGgoVUmFiYml0", + "TXFDb25uZWN0RmFpbGVkEKJREhkKFFJhYmJpdE1lc3NhZ2VUaW1lT2xkEKNR", + "EiAKG1JhYmJpdE1xQ2hhbm5lbENyZWF0ZUZhaWxlZBCkURIZChRTM0NsaWVu", + "dENyZWF0ZUZhaWxlZBCFUhIZChRTM0J1Y2tldENyZWF0ZUZhaWxlZBCGUhIX", + "ChJTM0ZpbGVVcGxvYWRGYWlsZWQQh1ISFwoSUzNGaWxlRGVsZXRlRmFpbGVk", + "EIhSEhQKD1MzRmlsZUdldEZhaWxlZBCJUhIXChJNZXRhRGF0YUxvYWRGYWls", + "ZWQQt1ISFAoPSW52YWxpZE1ldGFEYXRhELhSEhIKDU1ldGFJZEludmFsaWQQ", + "uVISFAoPSnNvblR5cGVJbnZhbGlkEPNSEiEKHEpzb25Db252ZXJ0RGVzZXJp", + "YWxpemVGYWlsZWQQ9FISHQoYU2VydmVyQ29uZmlnRmlsZU5vdEZvdW5kEM1T", + "EhYKEVNlcnZlclR5cGVJbnZhbGlkEM5TEicKIkFscmVhZHlSdW5uaW5nU2Vy", + "dmVyV2l0aExpc3RlblBvcnQQz1MSGQoUTm90Rm91bmRDYWNoZVN0b3JhZ2UQ", + "0FMSFgoRRnVuY3Rpb25QYXJhbU51bGwQ0VMSGQoURnVuY3Rpb25JbnZhbGlk", + "UGFyYW0Q0lMSHAoXQ2xpZW50TGlzdGVuUG9ydEludmFsaWQQ01MSGQoUTm90", + "T3ZlcnJpZGVJbnRlcmZhY2UQ1FMSGgoVU2VydmVyT25SdW5uaW5nRmFpbGVk", + "ENVTEhcKElNlcnZpY2VUeXBlSW52YWxpZBDWUxIbChZGdW5jdGlvbk5vdElt", + "cGxlbWVudGVkENdTEi4KKUNsYXNzRG9lc05vdEltcGxlbWVudEludGVyZmFj", + "ZUluaGVyaXRhbmNlENhTEhcKElJ1bGVUeXBlRHVwbGljYXRlZBDZUxIYChND", + "bGFzc1R5cGVDYXN0SXNOdWxsENpTEiIKHVBlcmlvZGljVGFza0FscmVhZHlS", + "ZWdpc3RlcmVkENtTEiIKHUVudGl0eVRpY2tlckFscmVhZHlSZWdpc3RlcmVk", + "ENxTEhkKFEVudGl0eVRpY2tlck5vdEZvdW5kEN1TEhcKEkVudGl0eUJhc2VO", + "b3RGb3VuZBDeUxIYChNWYWxpZFNlcnZlck5vdEZvdW5kEN9TEiAKG1Rhcmdl", + "dFNlcnZlclVzZXJDb3VudEV4Y2VlZBDgUxIXChJUYXJnZXRVc2VyTm90Rm91", + "bmQQ4VMSFwoSVGFyZ2V0VXNlck5vdExvZ0luEOJTEhAKC05vdEV4aXN0TWFw", + "EONTEiIKHUZhaWxlZFRvUmVzZXJ2ZUVudGVyQ29uZGl0aW9uEORTEh0KGEZh", + "aWxlZFRvUmVzZXJ2YXRpb25FbnRlchDlUxIbChZPd25lckVudGl0eVR5cGVJ", + "bnZhbGlkEOZTEhwKF093bmVyRW50aXR5Q2Fubm90RmlsbHVwEOdTEhUKEE93", + "bmVyR3VpZEludmFsaWQQ6FMSIQocRGFpbHlUaW1lRXZlbnRBZGRpdGlvbkZh", + "aWxlZBDpUxIkCh9Qcm9ncmFtVmVyc2lvblBhdGhUb2tlbk5vdEZvdW5kEOpT", + "Eh0KGEN1cnJlbnRseVByb2Nlc3NpbmdTdGF0ZRDrUxIZChRTZXJ2ZXJVcmxU", + "eXBlSW52YWxpZBDsUxIjCh5TZXJ2ZXJVcmxUeXBlQWxyZWFkeVJlZ2lzdGVy", + "ZWQQ7VMSHAoXU2VydmVyT2ZmbGluZU1vZGVFbmFibGUQ7lMSEQoMT3duZXJJ", + "bnZhbGlkEO9TEhsKFk1vZHVsZUFscmVhZHlSZWdpc3RyZWQQ8FMSJQogTW9k", + "dWxlQWxyZWFkeUFkZEluaXRpYWxpemVNb2R1bGUQ8VMSEwoOTW9kdWxlTm90", + "Rm91bmQQ8lMSHQoYUHJvZ3JhbVZlcnNpb25Mb2FkRmFpbGVkEPNTEhkKFFNl", + "cnZlckFscmVhZHlSdW5uaW5nEPRTEiQKH01ldGFEYXRhQ29weVRvRHluYW1v", + "RGJEb2NGYWlsZWQQ4lQSKAojTWV0YURhdGFDb3B5VG9FbnRpdHlBdHRyaWJ1", + "dGVGYWlsZWQQ41QSIQocRHluYW1vRGJEb2NDb3B5VG9DYWNoZUZhaWxlZBDk", + "VBIrCiZEeW5hbW9EYkRvY0NvcHlUb0VudGl0eUF0dHJpYnV0ZUZhaWxlZBDl", + "VBIlCiBDYWNoZUNvcHlUb0VudGl0eUF0dHJpYnV0ZUZhaWxlZBDmVBIhChxD", + "YWNoZUNvcHlUb0R5bmFtb0RiRG9jRmFpbGVkEOdUEiUKIEVudGl0eUF0dHJp", + "YnV0ZUNvcHlUb0NhY2hlRmFpbGVkEOhUEisKJkVudGl0eUF0dHJpYnV0ZUNv", + "cHlUb0R5bmFtb0RiRG9jRmFpbGVkEOlUEjkKNEVudGl0eUF0dHJpYnV0ZUNv", + "cHlUb0VudGl0eUF0dHJpYnV0ZVRyYW5zYWN0b3JGYWlsZWQQ6lQSOQo0RW50", + "aXR5QXR0cmlidXRlVHJhbnNhY3RvckNvcHlUb0VudGl0eUF0dHJpYnV0ZUZh", + "aWxlZBDrVBI1CjBFbnRpdHlBdHRyaWJ1dGVUcmFuc2FjdG9yQ29weVRvRHlu", + "YW1vRGJEb2NGYWlsZWQQ7FQSEwoOQXR0cmliTm90Rm91bmQQ7VQSGQoUQXR0", + "cmliUGF0aE1ha2VGYWlsZWQQ7lQSHgoZRW50aXR5QXR0cmlidXRlQ2FzdEZh", + "aWxlZBDvVBIeChlTdHJpbmdDb252ZXJ0VG9FbnVtRmFpbGVkEPBUEh4KGU1l", + "dGFTY2hlbWFWZXJzaW9uTm90TWF0Y2gQlVUSHAoXTWV0YURhdGFWZXJzaW9u", + "Tm90TWF0Y2gQllUSGgoVUGFja2V0VmVyc2lvbk5vdE1hdGNoEJdVEh8KGkNs", + "aWVudExvZ2ljVmVyc2lvbk5vdE1hdGNoEJhVEhwKF1Jlc291cmNlVmVyc2lv", + "bk5vdE1hdGNoEJlVEh8KGkNsaWVudFByb2dyYW1WZXJzaW9uSXNOdWxsEJpV", + "EhMKDlRlc3RJZE5vdEFsbG93EPlVEhEKDEJvdGROb3RBbGxvdxD6VRIZChRB", + "Y2NvdW50SWRMZW5ndGhTaG9ydBD7VRIkCh9BY2NvdW50SWROb3RGb3VuZElu", + "U3NvQWNjb3VudERiEPxVEiEKHE1ldGFEYXRhTm90Rm91bmRCeVRlc3RVc2Vy", + "SWQQ/VUSHAoXQWNjb3VudFBhc3N3b3JkTm90TWF0Y2gQ/lUSJwoiVXNlckRh", + "dGFDb252ZXJ0VG9BY2NvdW50QXR0ckZhaWxlZBD/VRIkCh9BY2NvdW50QmFz", + "ZUF0dHJpYkluc2VydERiRmFpbGVkEIBWEhgKE05vU2VydmVyQ29ubmVjdGFi", + "bGUQgVYSEwoOQmxvY2tlZEFjY291bnQQglYSLAonU3NvQWNjb3VudEF1dGhX", + "aXRoTGF1bmNoZXJMb2dpbk5vdEFsbG93EINWEiIKHUNsaWVudFN0YW5kYWxv", + "bmVMb2dpbk5vdEFsbG93EIRWEhkKFFBsYXRmb3JtVHlwZU5vdEFsbG93EIVW", + "EiYKIUFjY291bnRDYW5Ob3RSZWFkRnJvbVNzb0FjY291bnREYhCGVhIhChxT", + "c29BY2NvdW50QXV0aEp3dENoZWNrRmFpbGVkEIdWEikKJFVzZXJJZEtleU5v", + "dEZvdW5kSW5Tc29BY2NvdW50QXV0aEp3dBCIVhIoCiNVc2VySWRWYWx1ZUVt", + "cHR5SW5Tc29BY2NvdW50QXV0aEp3dBCJVhIuCilBY2NvdW50VHlwZUtleU5v", + "dEZvdW5kSW5Tc29BY2NvdW50QXV0aEp3dBCKVhIwCitBY2NvdW50VHlwZVZh", + "bHVlTm90QWxsb3dJblNzb0FjY291bnRBdXRoSnd0EItWEigKI0FjY291bnRC", + "YXNlRG9jTm90Rm91bmRJbk1ldGF2ZXJzZURiEIxWEi4KKUFjY2Vzc1Rva2Vu", + "S2V5Tm90QWxsb3dJblNzb0FjY291bnRBdXRoSnd0EI1WEiYKIUFjY2Vzc1Rv", + "a2VuTm90TWF0Y2hJblNzb0FjY291bnREYhCOVhIYChNBY2NvdW50VHlwZU5v", + "dEFsbG93EI9WEhoKFUFjY291bnRCYXNlRG9jTm90TG9hZBCQVhIXChJOb3RF", + "bm91Z2hBdXRob3JpdHkQrlYSGQoUQWNjb3VudEJhc2VEb2NJc051bGwQr1YS", + "FQoQQWNjb3VudElkSW52YWxpZBCwVhIbChZBY2NvdW50V2l0aG91dFVzZXJH", + "dWlkELFWEiIKHVNzb0FjY291bnRBdXRoSnd0VG9rZW5FeHBpcmVkELJWEh8K", + "GlNzb0FjY291bnRBdXRoSnd0RXhjZXB0aW9uELNWEiQKH1RyYW5zYWN0aW9u", + "UnVubmVyQWxyZWFkeVJ1bm5pbmcQwVcSHgoZVHJhbnNhY3Rpb25SdW5uZXJO", + "b3RGb3VuZBDCVxIWChFFbnRpdHlHdWlkSW52YWxpZBClWBIbChZFbnRpdHlB", + "dHRyaWJEdXBsaWNhdGVkEKZYEhoKFUVudGl0eUF0dHJpYnV0ZUlzTnVsbBCn", + "WBIcChdFbnRpdHlBdHRyaWJ1dGVOb3RGb3VuZBCoWBIgChtFbnRpdHlBdHRy", + "aWJ1dGVTdGF0ZUludmFsaWQQqVgSFgoRRW50aXR5VHlwZUludmFsaWQQqlgS", + "FgoRRW50aXR5TGlua2VkVG9NYXAQq1gSGQoURW50aXR5Tm90TGlua2VkVG9N", + "YXAQrFgSGgoVRW50aXR5U3RhdGVOb3REYW5jaW5nEK1YEhsKFkVudGl0eUFj", + "dGlvbkR1cGxpY2F0ZWQQiVkSGQoURW50aXR5QWN0aW9uTm90Rm91bmQQilkS", + "HQoYRW50aXR5QmFzZUhmc21Jbml0RmFpbGVkEO1ZEiAKG1JlZGlzR2xvYmFs", + "RW50aXR5RHVwbGljYXRlZBDRWhIaChVVc2VySXNTd2l0Y2hpbmdTZXJ2ZXIQ", + "tVsSHQoYVXNlcklzTm90U3dpdGNoaW5nU2VydmVyELZbEh8KGlNlcnZlclN3", + "aXRjaGluZ090cE5vdE1hdGNoELdbEiMKHkNvbm5lY3RlZFNlcnZlcklzTm90", + "RGVzdFNlcnZlchC4WxIbChZJbnZhbGlkUmVzZXJ2YXRpb25Vc2VyELlbEhYK", + "EUludmFsaWRSZXR1cm5Vc2VyELpbEikKJFVzZXJOaWNrbmFtZU5vdEFsbG93", + "V2l0aFNwZWNpYWxDaGFycxDhXRIsCidVc2VyTmlja25hbWVBbGxvd2VkTWlu", + "MlRvTWF4OFdpdGhLb3JlYW4Q4l0SLgopVXNlck5pY2tuYW1lQWxsb3dlZE1p", + "bjRUb01heDE2V2l0aEVuZ2xpc2gQ410SLQooVXNlck5pY2tuYW1lTm90QWxs", + "b3dlZE51bWJlckF0Rmlyc3RDaGFycxDkXRIeChlVc2VyTmlja25hbWVOb3RB", + "bGxvd0NoYXJzEOVdEi0KKFVzZXJOaWNrbmFtZU5vdEFsbG93V2l0aEluaXRp", + "YWxpc21Lb3JlYW4Q5l0SFAoPVXNlck5pY2tuYW1lQmFuEOddEhgKE1VzZXJE", + "dXBsaWNhdGVkTG9naW4Q6F0SEQoMVXNlck5vdExvZ2luEOldEikKJFVzZXJD", + "cmVhdGlvbkZvckR5bmFtb0RiRG9jRHVwbGljYXRlZBDqXRIpCiRVc2VyR3Vp", + "ZEFwcGx5VG9SZWZBdHRyaWJ1dGVBbGxGYWlsZWQQ610SJgohVGVzdFVzZXJQ", + "cmVwYXJlQ3JlYXRlTm90Q29tcGxldGVkEOxdEikKJERlZmF1bHRVc2VyUHJl", + "cGFyZUNyZWF0ZU5vdENvbXBsZXRlZBDtXRIgChtVc2VyUHJlcGFyZUxvYWRO", + "b3RDb21wbGV0ZWQQ7l0SGwoWVXNlck5pY2tuYW1lTm90Q3JlYXRlZBDvXRIf", + "ChpVc2VyTmlja25hbWVBbHJlYWR5Q3JlYXRlZBDwXRIfChpVc2VyQ3JlYXRl", + "U3RlcE5vdENvbXBsZXRlZBDxXRIYChNVc2VyQ3JlYXRlQ29tcGxldGVkEPJd", + "EhsKFlVzZXJTdWJLZXlCaW5kVG9GYWlsZWQQ810SHAoXVXNlclN1YktleVJl", + "cGxhY2VGYWlsZWQQ9F0SFAoPVXNlckd1aWRJbnZhbGlkEPVdEhoKFVVzZXJO", + "aWNrbmFtZURvY0lzTnVsbBD2XRISCg1Vc2VyRG9jSXNOdWxsEPddEhkKFFVz", + "ZXJHdWlkQWxyZWFkeUFkZGVkEPhdEhsKFlVzZXJOaWNrbmFtZUR1cGxpY2F0", + "ZWQQ+V0SIwoeVXNlck5pY2tuYW1lQWxsb3dlZE1pbjJUb01heDEyEPpdEiAK", + "G1VzZXJOaWNrbmFtZVNlYXJjaFBhZ2VXcm9uZxD7XRIWChFVc2VyTmlja25h", + "bWVFbXB0eRD8XRIhChxVc2VyQ29udGVudHNTZXR0aW5nRG9jSXNOdWxsEP1d", + "EhYKEVVzZXJNb25leURvY0VtcHR5EP5dEiEKHFVzZXJSZXBvcnRJbnZhbGlk", + "VGl0bGVMZW5ndGgQxV4SIwoeVXNlclJlcG9ydEludmFsaWRDb250ZW50TGVu", + "Z3RoEMZeEhsKFlVzZXJSZXBvcnREb2NFeGNlcHRpb24Qx14SKwomVGVzdENo", + "YXJhY3RlclByZXBhcmVDcmVhdGVOb3RDb21wbGV0ZWQQyWUSLgopRGVmYXVs", + "dENoYXJhY3RlclByZXBhcmVDcmVhdGVOb3RDb21wbGV0ZWQQ1GUSJQogQ2hh", + "cmFjdGVyUHJlcGFyZUxvYWROb3RDb21wbGV0ZWQQ1WUSLAonQ2hhcmFjdGVy", + "QmFzZURvY0xvYWREdXBsaWNhdGVkQ2hhcmFjdGVyENZlEhkKFENoYXJhY3Rl", + "ck5vdFNlbGVjdGVkENdlEhYKEUNoYXJhY3Rlck5vdEZvdW5kENhlEhsKFkNo", + "YXJhY3RlckJhc2VEb2NOb1JlYWQQ2WUSJQogQ2hhcmFjdGVyQ3VzdG9taXpp", + "bmdOb3RDb21wbGV0ZWQQ2mUSJAofQ2hhcmFjdGVyQ3JlYXRlU3RlcE5vdENv", + "bXBsZXRlZBDbZRIpCiRDaGFyYWN0ZXJDdXN0b21pemluZ0FscmVhZHlDb21w", + "bGV0ZWQQ3GUSHwoaQ2hhcmFjdGVyUHJlcGFyZU5vdENyZWF0ZWQQ3WUSHQoY", + "Q2hhcmFjdGVyQ3JlYXRlQ29tcGxldGVkEN5lEhsKFkNoYXJhY3RlckJhc2VE", + "b2NJc051bGwQ32USFQoQQWJpbGl0eU5vdEVub3VnaBD1ZxIZChRJdGVtTWV0", + "YURhdGFOb3RGb3VuZBCxbRIUCg9JdGVtR3VpZEludmFsaWQQsm0SEgoNSXRl", + "bURvY0lzTnVsbBCzbRInCiJJdGVtRGVmYXVsdEF0dHJpYnV0ZU5vdEZvdW5k", + "SW5NZXRhELRtEiMKHkl0ZW1MZXZlbEVuY2hhbnROb3RGb3VuZEluTWV0YRC1", + "bRIeChlJdGVtRW5jaGFudE5vdEZvdW5kSW5NZXRhELZtEisKJkl0ZW1BdHRy", + "aWJ1dGVSYW5kb21Hcm91cE5vdEZvdW5kSW5NZXRhELdtEi8KKkl0ZW1BdHRy", + "aWJ1dGVSYW5kb21Hcm91cFRvdGFsV2VpZ2h0SW52YWxpZBC4bRIRCgxJdGVt", + "Tm90Rm91bmQQuW0SHgoZSXRlbUNsb3RoSW52YWxpZExhcmdlVHlwZRC6bRIe", + "ChlJdGVtQ2xvdGhJbnZhbGlkU21hbGxUeXBlELttEhoKFUl0ZW1TdGFja0Nv", + "dW50SW52YWxpZBC8bRIXChJJdGVtTWF4Q291bnRFeGNlZWQQvW0SHgoZSXRl", + "bURvY0xvYWREdXBsaWNhdGVkSXRlbRC+bRIZChRDbG90aFNsb3RUeXBlSW52", + "YWxpZBC/bRIcChdJdGVtU3RhY2tDb3VudE5vdEVub3VnaBDAbRIXChJJdGVt", + "Q291bnROb3RFbm91Z2gQwW0SGAoTSXRlbUludmFsaWRJdGVtVHlwZRDCbRId", + "ChhJdGVtVG9vbE1ldGFEYXRhTm90Rm91bmQQw20SFQoQSXRlbVRvb2xOb3RG", + "b3VuZBDEbRIdChhJdGVtVG9vbE5vdEFjdGl2YXRlU3RhdGUQxW0SGAoTVG9v", + "bEFjdGlvbkRvY0lzTnVsbBDGbRIlCiBUb29sQWN0aW9uQWxyZWFkeVVuYWN0", + "aXZhdGVTdGF0ZRDHbRIjCh5Ub29sQWN0aW9uQWxyZWFkeUFjdGl2YXRlU3Rh", + "dGUQyG0SFwoSSXRlbVRhdHRvb05vdEZvdW5kEMltEiUKIEl0ZW1BdHRyaWJ1", + "dGVFbmNoYW50TWV0YU5vdEZvdW5kEMptEiMKHkl0ZW1BdHRyaWJ1dGVDaGFu", + "Z2VOb3RTZWxlY3RlZBDLbRIkCh9JdGVtUGFyc2luZ0Zyb21TdHJpbmdUb0lu", + "dEVyb3JyEMxtEikKJEl0ZW1WYWx1ZVBhcnNpbmdGcm9tU3RyaW5nVG9JbnRF", + "cm9ychDNbRImCiFJdGVtRmlyc3RQdXJjaGFzZUhpc3RvcnlEb2NJc051bGwQ", + "zm0SMgotSXRlbUZpcnN0UHVyY2hhc2VIaXN0b3J5RG9jTG9hZER1cGxpY2F0", + "ZWRJdGVtEM9tEikKJEl0ZW1GaXJzdFB1cmNoYXNlSGlzdG9yeUFscmVhZHlF", + "eGlzdBDQbRIsCidJdGVtRmlyc3RQdXJjaGFzZURpc2NvdW50SXRlbUNvdW50", + "V3JvbmcQ0W0SHwoaSXRlbUF0dHJpYnV0ZUlkVHlwZUludmFsaWQQ0m0SFAoP", + "SXRlbUFsbG9jRmFpbGVkENNtEhcKEkl0ZW1HdWlkRHVwbGljYXRlZBDUbRIY", + "ChNJdGVtTGV2ZWxDdXJyZW50TWF4ENVtEhcKEkl0ZW1DYW5Ob3RCZVN0b3Jl", + "ZBDWbRIcChdJdGVtVXNlRnVuY3Rpb25Ob3RGb3VuZBCVbhIdChhJdGVtVXNl", + "UXVlc3RNYWlsQ291bnRNYXgQlm4SIwoeSXRlbVVzZU5vdEV4aXN0QXNzaWdu", + "YWJsZVF1ZXN0EJduEhsKFkl0ZW1Vc2VBbHJlYWR5SGFzUXVlc3QQmG4SHwoa", + "SXRlbVVzZUFscmVhZHlIYXNRdWVzdE1haWwQmW4SIwoeQmFnUnVsZUl0ZW1M", + "YXJnZVR5cGVEdXBsaWNhdGVkEJl1EikKJFRvb2xFcXVpcFJ1bGVJdGVtTGFy", + "Z2VUeXBlRHVwbGljYXRlZBCadRIqCiVDbG90aEVxdWlwUnVsZUl0ZW1MYXJn", + "ZVR5cGVEdXBsaWNhdGVkEJt1EisKJlRhdHRvb0VxdWlwUnVsZUl0ZW1MYXJn", + "ZVR5cGVEdXBsaWNhdGVkEJx1EhoKFUludmVudG9yeVJ1bGVOb3RGb3VuZBCd", + "dRIXChJFcXVpcEludmVuTm90Rm91bmQQnnUSGAoTU2xvdHNBbHJlYWR5RXF1", + "aXBlZBCfdRIaChVTbG90c0FscmVhZHlVbmVxdWlwZWQQoHUSEgoNQmFnSXNJ", + "dGVtRnVsbBChdRITCg5CYWdJc0l0ZW1FbXB0eRCidRIaChVCYWdEZWx0YUl0", + "ZW1EdXBsaWF0ZWQQo3USFAoPQmFnSXRlbU5vdEZvdW5kEKR1EigKI0Nsb3Ro", + "RXF1aXBSdWxlQ2xvdGhTbG90VHlwZU5vdEZvdW5kEKV1EiYKIVRvb2xFcXVp", + "cFJ1bGVUb29sU2xvdFR5cGVOb3RGb3VuZBCmdRIqCiVUYXR0b29FcXVpcFJ1", + "bGVUYXR0b29TbG90VHlwZU5vdEZvdW5kEKd1EhgKE0JhZ1RhYlR5cGVBZGRG", + "YWlsZWQQqHUSFgoRQmFnVGFiVHlwZUludmFsaWQQqXUSFwoSQmFnVGFiVHlw", + "ZU5vdEZvdW5kEKp1EhsKFkJhZ1RhYkNvdW50TWVyZ2VGYWlsZWQQq3USHwoa", + "SW52ZW50b3J5RW50aXR5VHlwZUludmFsaWQQrHUSGgoVSW52ZW5FcXVpcFR5", + "cGVJbnZhbGlkEK11EhoKFUJhZ0lzUmVzZXJ2ZWRJdGVtRnVsbBCudRIbChZC", + "YWdJc1Jlc2VydmVkSXRlbUVtcHR5EK91EhYKEUVxdWlwU2xvdE5vdE1hdGNo", + "ELB1EhgKE0VxdWlwU2xvdE91dE9mUmFuZ2UQsXUSHgoZU2xvdHNBbHJlYWR5", + "UmVzZXJ2ZWRFcXVpcBCydRIgChtTbG90c0FscmVhZHlSZXNlcnZlZFVuZXF1", + "aXAQs3USFQoQU2xvdFR5cGVOb3RGb3VuZBC0dRIfChpVZ2NOcGNNZXRhR3Vp", + "ZEFscmVhZHlBZGRlZBD9dRIUCg9VZ2NOcGNEb2NJc051bGwQ/nUSGQoUVWdj", + "TnBjTWF4Q291bnRFeGNlZWQQ/3USHQoYVWdjTnBjQ2xvdGhJdGVtTm90RW5v", + "dWdoEIB2EiIKHVVnY05wY0RvY0xvYWREdXBsaWNhdGVkVWdjTnBjEIF2EiIK", + "HVVnY05wY0Rlc2NyaXB0aW9uTGVuZ3RoRXhjZWVkEIJ2EiMKHlVnY05wY1dv", + "cmRTY2VuYXJpb0xlbmd0aEV4Y2VlZBCDdhIfChpVZ2NOcGNHcmVldGluZ0xl", + "bmd0aEV4Y2VlZBCEdhIeChlVZ2NOcGNUYXR0b29JdGVtTm90RW5vdWdoEIV2", + "EicKIlVnY05wY0hhYml0U29jaWFsQWN0aW9uQ291bnRFeGNlZWQQhnYSKgol", + "VWdjTnBjRGlhbG9ndWVTb2NpYWxBY3Rpb25Db3VudEV4Y2VlZBCHdhIdChhV", + "Z2NOcGNOaWNrbmFtZUR1cGxpY2F0ZWQQiHYSEwoOVWdjTnBjTm90Rm91bmQQ", + "iXYSIwoeVWdjTnBjSW50cm9kdWN0aW9uTGVuZ3RoRXhjZWVkEIp2EhcKElVn", + "Y05wY01heFRhZ0V4Y2VlZBCLdhIYChNVZ2NOcGNOaWNrbmFtZUVtcHR5EIx2", + "EiYKIVVnY05wY0FscmVhZHlSZWdpc3RlcmVkSW5HYW1lWm9uZRCNdhIiCh1V", + "Z2NOcGNOb3RSZWdpc3RlcmVkSW5HYW1lWm9uZRCOdhIkCh9VZ2NOcGNMaWtl", + "U2VsZWN0ZWVDb3VudE5vdEZvdW5kEI92EiMKHlVnY05wY0xpa2VTZWxlY3Rl", + "ZEZsYWdOb3RGb3VuZBCQdhIfChpVZ2NOcGNEdXBsaWNhdGVJbk15aG9tZVVn", + "YxCRdhIXChJVZ2NOcGNMb2NhdGVkU3RhdGUQknYSGwoWVWdjTnBjTWV0YURh", + "dGFOb3RGb3VuZBCTdhIjCh5CZWFjb25BcHBQcm9maWxlVXBsb2FkQ29vbFRp", + "bWUQlHYSGgoVQmVhY29uQm9keUl0ZW1JbnZhbGlkEJV2EhwKF1VnY05wY0hh", + "c0JlYWNvblNob3BJdGVtEJZ2Eh8KGlVnY05wY1JhbmtFbnRpdHlJc05vdEZv", + "dW5kEOF2EhkKFFVnY05wY1JhbmtPdXRPZlJhbmdlEOJ2EiMKHkZhcm1pbmdF", + "ZmZlY3REb2NMaW5rUGtTa05vdFNldBDFdxItCihGYXJtaW5nRWZmZWN0QWxy", + "ZWFkeVJlZ2lzdGVyZWRJbkdhbWVab25lEMZ3EhMKDkZhcm1pbmdBbHJlYWR5", + "EMd3EhAKC0Zhcm1pbmdCeU1lEMh3EiAKG0Zhcm1pbmdQcm9wTWV0YURhdGFO", + "b3RGb3VuZBDJdxIbChZGYXJtaW5nVHJ5Q291bnRJbnZhbGlkEMp3EhQKD0Zh", + "cm1pbmdOb3RTdGF0ZRDLdxIZChRGYXJtaW5nT3duZXJOb3RNYXRjaBDMdxIl", + "CiBGYXJtaW5nU3VtbW9uZWRFbnRpdHlUeXBlSW52YWxpZBDNdxIkCh9GYXJt", + "aW5nRWZmZWN0Tm90RXhpc3RJbkdhbWVab25lEM53EhAKC0ZhcmltZ1N0YXRl", + "EM93EhsKFkZhcm1pbmdTdGFuZEJ5Tm90U3RhdGUQ0HcSGgoVRmFybWluZ0Fu", + "Y2hvck5vdEZvdW5kENF3EiYKIUZhcm1pbmdCZWFjb25EYkluZm9JbnRlZ3Jp", + "dHlFcnJvchDSdxIaChVHYWNoYU1ldGFEYXRhTm90Rm91bmQQqXgSFQoQR2Fj", + "aGFSZXdhcmRFbXB0eRCqeBITCg5NYXN0ZXJOb3RGb3VuZBC5exIVChBNYXN0", + "ZXJOb3RSZWxhdGVkELp7EhQKD0dhbWVab25lTm90Sm9pbhCdfBIOCglNYXBJ", + "c051bGwQnnwSHAoXTG9jYXRpb25VbmlxdWVJZEludmFsaWQQn3wSEwoOUHJv", + "cElzT2NjdXBpZWQQoHwSFgoRUHJvcElzTm90T2NjdXBpZWQQoXwSFAoPRnJp", + "ZW5kRG9jSXNOdWxsEIF9EhoKFUZyaWVuZEZvbGRlckRvY0lzTnVsbBCCfRIi", + "ChxTb2NpYWxBY3Rpb25NZXRhRGF0YU5vdEZvdW5kEOmEARIaChRTb2NpYWxB", + "Y3Rpb25Ob3RGb3VuZBDqhAESLwopU29jaWFsQWN0aW9uRG9jTG9hZER1cGxp", + "Y2F0ZWRTb2NpYWxBY3Rpb24Q64QBEh4KGFNvY2lhbEFjdGlvbkFscmVhZHlF", + "eGlzdBDshAESIAoaU29jaWFsQWN0aW9uU2xvdE91dE9mUmFuZ2UQ7YQBEhsK", + "FVNvY2lhbEFjdGlvbk5vdE9uU2xvdBDuhAESGwoVU29jaWFsQWN0aW9uRG9j", + "SXNOdWxsEO+EARIcChZDaGFubmVsTW92ZVNhbWVDaGFubmVsENGMARIgChpD", + "aGFubmVsSW52YWxpZE1vdmVDb29sVGltZRDSjAESFgoQTm90Q2hhbm5lbFNl", + "cnZlchDTjAESGAoST3duZWRSb29tRG9jSXNOdWxsELmUARITCg1Sb29tRG9j", + "SXNOdWxsELqUARIVCg9Sb29tSXNOb3RNeUhvbWUQu5QBEhoKFE1hcFJhbmdl", + "T3V0T2ZDZWxsUG9zELyUARIcChZNYXBHcmlkQm91bmRPdXRPZlJhbmdlEL2U", + "ARIZChNNYXBHcmlkTm90Rm91bmRHcmlkEL6UARIZChNNYXBHcmlkTm90Rm91", + "bmRDZWxsEL+UARIfChlNYXBHcmlkQ2VsbE5vdEZvdW5kUGxheWVyEMCUARIf", + "ChlNYXBHcmlkQ2VsbE5vdEZvdW5kVWdjTnBjEMGUARISCgxNYWlsTm90Rm91", + "bmQQoZwBEhYKEE1haWxBbHJlYWR5VGFrZW4QopwBEhkKE01haWxJbnZhbGlk", + "TWFpbFR5cGUQo5wBEhwKFk1haWxNYXhTZW5kQ291bnRFeGNlZWQQpJwBEhMK", + "DU1haWxEb2NJc051bGwQpZwBEh0KF01haWxCbG9ja1VzZXJDYW5ub3RTZW5k", + "EKacARImCiBNYWlsTWF4VGFyZ2V0UmVjZWl2ZWRDb3VudEV4Y2VlZBCnnAES", + "FgoQTWFpbENhbnRTZW5kU2VsZhConAESIAoaTWFpbENhbnREZWxldGVJZkl0", + "ZW1FeGlzdHMQqZwBEhYKEE1haWxEb2NFeGNlcHRpb24QqpwBEhoKFE1haWxQ", + "cm9maWxlRG9jSXNOdWxsEM2eARIdChdNYWlsUHJvZmlsZURvY0V4Y2VwdGlv", + "bhDOngESGQoTU3lzdGVtTWFpbERvY0lzTnVsbBCVoAESGAoSUGFydHlDYW5u", + "b3RTZXRHdWlkEImkARIgChpQYXJ0eUZhaWxlZE1ha2VQYXJ0eU1lbWJlchCK", + "pAESEQoLUGFydHlJc0Z1bGwQi6QBEhgKEkFscmVhZHlJbnZpdGVQYXJ0eRCM", + "pAESGQoTTm90Rm91bmRQYXJ0eUludml0ZRCNpAESEwoNTm90Rm91bmRQYXJ0", + "eRCOpAESDgoITm90UGFydHkQj6QBEhQKDk5vdFBhcnR5TGVhZGVyEJCkARIS", + "CgxKb2luaW5nUGFydHkQkaQBEhQKDk5vdFBhcnR5TWVtYmVyEJKkARITCg1B", + "bHJlYWR5U3VtbW9uEJOkARIdChdQYXJ0eUxlYWRlclNlcnZlcklzRnVsbBCU", + "pAESGwoVSW52aXRlTWVtYmVySXNDb25jZXJ0EJWkARIcChZGYWlsVG9TZW5k", + "SW52aXRlTWVtYmVyEJakARIYChJBbHJlYWR5UGFydHlNZW1iZXIQl6QBEh0K", + "F0ludmFsaWRTdW1tb25TZXJ2ZXJUeXBlEJikARIdChdTdW1tb25Vc2VyTGlt", + "aXREaXN0YW5jZRCZpAESGQoTSW52YWxpZFN1bW1vbk1lbWJlchCapAESGgoU", + "UGFydHlMZWFkZXJMb2dnZWRPdXQQm6QBEhsKFUFscmVhZHlTdGFydFBhcnR5", + "Vm90ZRCcpAESFgoQTm9TdGFydFBhcnR5Vm90ZRCdpAESHgoYQWxyZWFkeVBh", + "c3NQYXJ0eVZvdGVUaW1lEJ6kARIaChRJbnZhbGlkUGFydHlWb3RlVGltZRCf", + "pAESGwoVQWxyZWFkeVJlcGx5UGFydHlWb3RlEKCkARIaChRFbXB0eVBhcnR5", + "SW5zdGFuY2VJZBChpAESGAoSSW52YWxpZEludml0ZVBsYWNlEKKkARIdChdJ", + "bnZpdGVQYXJ0eUludmFsaWRVc2VycxCjpAESGwoVU3VtbW9uUGFydHlNZW1i", + "ZXJGYWlsEKSkARIeChhJbnZhbGlkUGFydHlTdHJpbmdMZW5ndGgQpaQBEiEK", + "G0luY2x1ZGVCYW5Xb3JkRnJvbVBhcnR5TmFtZRCmpAESIgocSm9pbmluZ1Bh", + "cnR5TWVtYmVySW5mb0lzTnVsbBCnpAESHgoYSW52YWxpZFN1bW1vbldvcmxk", + "U2VydmVyEKikARIbChVOb3RFeGlzdFBhcnR5SW5zdGFuY2UQ76sBEhoKFEJ1", + "ZmZNZXRhRGF0YU5vdEZvdW5kEPGrARIdChdCdWZmTm90UmVnaXN0cnlDYXRl", + "Z29yeRDyqwESEgoMQnVmZk5vdEZvdW5kEPOrARIhChtCdWZmSW52YWxpZEJ1", + "ZmZDYXRlZ29yeVR5cGUQ9KsBEiEKG0J1ZmZDYWNoZUxvYWREdXBsaWNhdGVk", + "QnVmZhD1qwESHgoYQnVmZkludmFsaWRBdHRyaWJ1dGVUeXBlEParARIdChdR", + "dWVzdEFzc2luZ0RhdGFOb3RFeGlzdBDYswESFwoRUXVlc3RNYWlsTm90RXhp", + "c3QQ2bMBEhcKEVF1ZXN0QWxyZWFkeUVuZGVkENqzARITCg1RdWVzdENvdW50", + "TWF4ENuzARIdChdRdWVzdFR5cGVBc3NpZ25Db3VudE1heBDcswESFgoQUXVl", + "c3RJbnZhbGlkVHlwZRDdswESFwoRUXVlc3RJbnZhbGlkVmFsdWUQ3rMBEhUK", + "D1F1ZXN0SWROb3RGb3VuZBDfswESGQoTUXVlc3RJbnZhbGlkVGFza051bRDg", + "swESGwoVUXVlc3RSZWZ1c2VPbmx5Tm9ybWFsEOGzARIeChhRdWVzdEFiYWRv", + "bk5vdEV4aXN0UXVlc3QQ4rMBEhoKFFF1ZXN0QWxyZWFkeUNvbXBsZXRlEOOz", + "ARIWChBRdWVzdE5vdENvbXBsZXRlEOSzARIcChZRdWVzdEFiYW5kb25Pbmx5", + "Tm9ybWFsEOWzARIYChJRdWVzdE1haWxEb2NJc051bGwQ5rMBEhQKDlF1ZXN0", + "RG9jSXNOdWxsEOezARIXChFFbmRRdWVzdERvY0lzTnVsbBDoswESHwoZUXVl", + "c3RNZXRhQmFzZU5vdEltcGxlbWVudBDpswESIAoaUXVlc3ROb3RpZnlSZWRp", + "c1JlZ2lzdEZhaWwQ6rMBEhcKEVF1ZXN0QWxyZWFkeUV4aXN0EOuzARIbChVX", + "b3JsZE1ldGFEYXRhTm90Rm91bmQQwbsBEhoKFExhY2tPZldvcmxkRW50ZXJJ", + "dGVtEMK7ARIeChhXb3JsZE1hcFRyZWVEYXRhTm90Rm91bmQQw7sBEiMKHVdv", + "cmxkTWFwVHJlZUNoaWxkTGFuZE5vdEZvdW5kEMS7ARIZChNSb29tTWFwRGF0", + "YU5vdEZvdW5kEMW7ARIaChRMYW5kTWV0YURhdGFOb3RGb3VuZBC1vwESEgoM", + "TGFuZE5vdEZvdW5kELa/ARIfChlMYW5kRG9jTG9hZER1cGxpY2F0ZWRMYW5k", + "ELe/ARITCg1MYW5kRG9jSXNOdWxsELi/ARIXChFPd25lZExhbmROb3RGb3Vu", + "ZBC5vwESKQojT3duZWRMYW5kRG9jTG9hZER1cGxpY2F0ZWRPd25lZExhbmQQ", + "ur8BEhgKEk93bmVkTGFuZERvY0lzTnVsbBC7vwESHQoXTGFuZE1hcFRyZWVE", + "YXRhTm90Rm91bmQQvL8BEiYKIExhbmRNYXBUcmVlQ2hpbGRCdWlsZGluZ05v", + "dEZvdW5kEL2/ARIcChZMYW5kQnVpbGRpbmdJc05vdEVtcHR5EL6/ARIZChNM", + "YW5kTWFwRGF0YU5vdEZvdW5kEL+/ARIUCg5MYW5kRXhpc3RPd25lchDAvwES", + "GQoTTGFuZEVkaXRvcklzTm90VXNlchDBvwESGQoTTGFuZE93bmVySXNOb3RN", + "YXRjaBDCvwESHgoYQnVpbGRpbmdNZXRhRGF0YU5vdEZvdW5kEKnDARIWChBC", + "dWlsZGluZ05vdEZvdW5kEKrDARInCiFCdWlsZGluZ0RvY0xvYWREdXBsaWNh", + "dGVkQnVpbGRpbmcQq8MBEhcKEUJ1aWxkaW5nRG9jSXNOdWxsEKzDARIbChVP", + "d25lZEJ1aWxkaW5nTm90Rm91bmQQrcMBEjEKK093bmVkQnVpbGRpbmdEb2NM", + "b2FkRHVwbGljYXRlZE93bmVkQnVpbGRpbmcQrsMBEhwKFk93bmVkQnVpbGRp", + "bmdEb2NJc051bGwQr8MBEiEKG0J1aWxkaW5nTWFwVHJlZURhdGFOb3RGb3Vu", + "ZBCwwwESJgogQnVpbGRpbmdNYXBUcmVlQ2hpbGRSb29tTm90Rm91bmQQscMB", + "Eh0KF0J1aWxkaW5nRmxvb3JJc05vdEVtcHR5ELLDARIdChdCdWlsZGluZ01h", + "cERhdGFOb3RGb3VuZBCzwwESGAoSQnVpbGRpbmdFeGlzdE93bmVyELTDARIn", + "CiFCdWlsZGluZ01hcFRyZWVQYXJlbnRMYW5kTm90Rm91bmQQoaABEh0KF0J1", + "aWxkaW5nT3duZXJJc05vdE1hdGNoELbDARIcChZNeUhvbWVNZXRhRGF0YU5v", + "dEZvdW5kEJ3HARIUCg5NeUhvbWVOb3RGb3VuZBCexwESIwodTXlIb21lRG9j", + "TG9hZER1cGxpY2F0ZWRNeUhvbWUQn8cBEhgKEk15SG9tZUFscmVhZHlFeGlz", + "dBCgxwESFQoPTXlIb21lRG9jSXNOdWxsEKHHARIVCg9NeUhvbWVJc05vdE1p", + "bmUQoscBEiQKHk15SG9tZUNhbnRFeGNoYW5nZVdoZW5DcmFmdGluZxCjxwES", + "IgocRWRpdGFibGVSb29tTWV0YURhdGFOb3RGb3VuZBCkxwESJwohRWRpdGFi", + "bGVGcmFtZXdvcmtNZXRhRGF0YU5vdEZvdW5kEKXHARIZChNJbnRlcmlvclBv", + "aW50RXhjZWVkEKbHARIZChNNeWhvbWVOb3RFbm91Z2hTbG90EKfHARIWChBN", + "eWhvbWVJc1NlbGVjdGVkEKjHARIbChVNeWhvbWVOYW1lTGVuZ3RoU2hvcnQQ", + "qccBEhoKFE15aG9tZU5hbWVMZW5ndGhMb25nEKrHARIaChRNeWhvbWVOYW1l", + "RHVwbGljYXRlZBCrxwESHgoYTXlob21lSW50ZXJwaG9uZU5vdEV4aXN0EKzH", + "ARIeChhNeWhvbWVTdGFydFBvaW50Tm90RXhpc3QQrccBEhwKFk15aG9tZUlu", + "dGVycGhvbmVFeGNlZWQQrscBEhwKFk15aG9tZVN0YXJ0UG9pbnRFeGNlZWQQ", + "r8cBEhsKFUNyYWZ0aW5nQ2xvdGhlc0V4Y2VlZBCwxwESGwoVQ3JhZnRpbmdD", + "b29raW5nRXhjZWVkELHHARIdChdDcmFmdGluZ0Z1cm5pdHVyZUV4Y2VlZBCy", + "xwESGQoTQW5jaG9ySXNOb3RJbk15aG9tZRCzxwESJgogRG9Ob3RSZW1vdmVQ", + "cm9jZXNzQ3JhZnRpbmdBbmNob3IQtMcBEhkKE0FuY2hvckd1aWREdXBsaWNh", + "dGUQtccBEhYKEE15aG9tZUlzRWRpdHRpbmcQtscBEhYKEE15aG9tZUlzT25S", + "ZW50YWwQt8cBEh8KGU15aG9tZVVnY0luZm9GaWxlTm90Rm91bmUQuMcBEiEK", + "G0VkaXRhYmxlUm9vbVNpemVUeXBlSW52YWxpZBC5xwESGAoSQ3JhZnRlckNv", + "dW50RXhjZWVkELrHARIbChVNaW5pbWFwTWFya2VyTm90Rm91bmQQkcsBEjEK", + "K01pbmltYXBNYXJrZXJEb2NMb2FkRHVwbGljYXRlZE1pbmltYXBNYXJrZXIQ", + "kssBEhwKFk1pbmltYXBNYXJrZXJEb2NJc051bGwQk8sBEhoKFENhcnRNZXRh", + "RGF0YU5vdEZvdW5kEIXPARIYChJDYXJ0TWF4Q291bnRFeGNlZWQQhs8BEhsK", + "FUNhcnRTdGFja0NvdW50SW52YWxpZBCHzwESHQoXQ2FydFN0YWNrQ291bnRO", + "b3RFbm91Z2gQiM8BEhYKEENhcnRJdGVtTm90Rm91bmQQic8BEiEKG0NhcnRO", + "b3RSZWdpc3RyeUN1cnJlbmN5VHlwZRCKzwESHQoXQ2FydEludmFsaWRDdXJy", + "ZW5jeVR5cGUQi88BEhMKDUNhcnREb2NJc051bGwQjM8BEhYKEENhcnREb2NF", + "eGNlcHRpb24Qjc8BEhgKEkNoYXRTZW5kU2VsZkZhaWxlZBD50gESGQoTQ2hh", + "dEludmFsaWRDaGF0VHlwZRD60gESIAoaQ2hhdEJsb2NrVXNlckNhbm5vdFdo", + "aXNwZXIQ+9IBEh4KGENoYXRJbnZhbGlkTWVzc2FnZUxlbmd0aBD80gESGAoS", + "Q2hhdEluY2x1ZGVCYW5Xb3JkEP3SARIZChNOb3RpY2VDaGF0RG9jSXNOdWxs", + "EO3WARIkCh5Fc2NhcGVQb3NpdGlvbk5vdEF2YWlsYWJsZVRpbWUQ4doBEh0K", + "F0VzY2FwZVBvc2l0aW9uRG9jSXNOdWxsEOLaARIYChJCbG9ja1VzZXJEb2NJ", + "c051bGwQ1d4BEh8KGUNoYXJhY3RlclByb2ZpbGVEb2NJc051bGwQyeIBEiAK", + "GkN1c3RvbURlZmluZWREYXRhRG9jSXNOdWxsEPXkARIeChhDdXN0b21EZWZp", + "bmVkVWlEb2NJc051bGwQveYBEhkKE0dhbWVPcHRpb25Eb2NJc051bGwQseoB", + "EhwKFkdhbWVPcHRpb25Eb2NFeGNlcHRpb24QsuoBEhQKDkxldmVsRG9jSXNO", + "dWxsEKXuARIXChFMb2NhdGlvbkRvY0lzTnVsbBCZ8gESFAoOTm90VXNhYmxl", + "UGxhY2UQmvIBEiEKG1JlZGlzTG9jYXRpb25DYWNoZVNldEZhaWxlZBCb8gES", + "FAoOTW9uZXlEb2NJc051bGwQgfoBEh8KGU1vbmV5Q29udHJvbE5vdEluaXRp", + "YWxpemUQgvoBEhQKDk1vbmV5Tm90RW5vdWdoEIP6ARIbChVNb25leU1heENv", + "dW50RXhjZWVkZWQQhPoBEh4KGEN1cnJlbmN5TWV0YURhdGFOb3RGb3VuZBCF", + "+gESJgogU2hvcFByb2R1Y3RUcmFkaW5nTWV0ZXJEb2NJc051bGwQ6YECEhYK", + "EFNob3BJc015SG9tZUl0ZW0Q6oECEhgKEkludmFsaWRTaG9wQnV5VHlwZRDr", + "gQISHQoXU2hvcFByb2R1Y3RDYW5ub3RSZW53YWwQ7IECEh4KGFNob3BQcm9k", + "dWN0Tm90UmVud2FsVGltZRDtgQISJwohU2hvcFByb2R1Y3RSZW5ld2FsQ291", + "bnRBbHJlYWR5TWF4EO6BAhIYChJTaG9wSXRlbUNhbm5vdFNlbGwQ74ECEhgK", + "ElJld2FyZEluZm9Ob3RFeGlzdBDQiQISFwoRUmV3YXJkSW52YWxpZFR5cGUQ", + "0YkCEhwKFlJld2FyZEludmFsaWRUeXBlVmFsdWUQ0okCEh4KGE5vdFJlcXVp", + "cmVBdHRyaWJ1dGVWYWx1ZRDTiQISLgooUmV3YXJkR3JvdXBJZFBhcnNpbmdG", + "cm9tU3RyaW5nVG9JbnRFcm9ychDUiQISFgoQQ2xhaW1JbnZhbGlkVHlwZRC4", + "kQISHQoXQ2xhaW1NZW1iZXJzaGlwTm90RXhpc3QQuZECEhcKEUNsYWltSW5m", + "b05vdEV4aXN0ELqRAhIeChhDbGFpbVJld2FyZE5vdEVub3VnaFRpbWUQu5EC", + "EhkKE0NsYWltUmV3YXJkRXZlbnRFbmQQvJECEhQKDkNsYWltRG9jSXNOdWxs", + "EL2RAhIaChRDcmFmdFJlY2lwZURvY0lzTnVsbBChmQISGAoSQ3JhZnRIZWxw", + "RG9jSXNOdWxsEKKZAhIUCg5DcmFmdERvY0lzTnVsbBCjmQISHgoYQ3JhZnRp", + "bmdNZXRhRGF0YU5vdEZvdW5kEKSZAhIXChFDcmFmdGluZ05vdEZpbmlzaBCl", + "mQISHwoZQ3JhZnRpbmdOb3RDcmFmdGluZ0FuY2hvchCmmQISHQoXQ3JhZnRp", + "bmdBbHJlYWR5Q3JhZnRpbmcQp5kCEh8KGUNyYWZ0aW5nQW5jaG9ySXNOb3RQ", + "bGFjZWQQqJkCEiEKG0NyYWZ0aW5nUmVjaXBlSXNOb3RSZWdpc3RlchCpmQIS", + "KAoiQ3JhZnRpbmdBbmNob3JJc05vdE1hdGNoV2l0aFJlY2lwZRCqmQISGwoV", + "Q3JhZnRpbmdIZWxwQ291bnRPdmVyEKuZAhIjCh1DcmFmdGluZ0hlbHBTYW1l", + "VXNlckNvdW50T3ZlchCsmQISIwodQ3JhZnRpbmdIZWxwUmVjZWl2ZWRDb3Vu", + "dE92ZXIQrZkCEhkKE0NyYWZ0SGVscERvY0lzRW1wdHkQrpkCEhUKD0NyYWZ0", + "RG9jSXNFbXB0eRCvmQISGwoVQ3JhZnRpbmdBbHJlYWR5RmluaXNoELCZAhIl", + "Ch9DcmFmdGluZ1JlY2lwZUlzQWxyZWFkeVJlZ2lzdGVyELGZAhIXChFDcmFm", + "dERvY0V4Y2VwdGlvbhCymQISGwoVQ3JhZnRIZWxwRG9jRXhjZXB0aW9uELOZ", + "AhIdChdDcmFmdFJlY2lwZURvY0V4Y2VwdGlvbhC0mQISHgoYQ3JhZnRJbnZh", + "bGlkUmVxdWVzdENvdW50ELWZAhIfChlVZ3FBcGlTZXJ2ZXJSZXF1ZXN0RmFp", + "bGVkEImhAhInCiFVZ3FBcGlTZXJ2ZXJDb252ZXJ0VG9PYmplY3RGYWlsZWQQ", + "iqECEisKJVVncUFwaVNlcnZlckludmFpbGRTZWFyY2hDYXRlZ29yeVR5cGUQ", + "i6ECEiAKGlVncVJlcG9ydEludmFsaWRUZXh0TGVuZ3RoEIyhAhIaChRVZ3FR", + "dWVzdE1ldGFOb3RFeGlzdBCNoQISHgoYVWdxQmVnaW5DcmVhdG9yUG9pbnRG", + "YWlsEI6hAhIYChJVZ3FRdWVzdFNodXRkb3duZWQQj6ECEiIKHFVncVRlc3RR", + "dWVzdEFscmVhZHlDb21wbGV0ZWQQkKECEjQKLlVncVJlYXNzaWduVXNpbmdJ", + "dGVtRXJyb3JDYXVzZVF1ZXN0Tm90Q29tcGxldGUQkaECEjUKL1VncVJlYXNz", + "aWduVXNpbmdJdGVtRXJyb3JDYXVzZVF1ZXN0QWxyZWFkeUV4aXN0EJKhAhI2", + "CjBVZ3FSZWFzc2lnblVzaW5nSXRlbUVycm9yQ2F1c2VOZXdSZXZpc2lvblVw", + "ZGF0ZWQQk6ECEiEKG1VncVF1ZXN0RGF0YUludmFsaWRSZXZpc2lvbhCUoQIS", + "JQofVWdxQWJvcnRDYW5ub3RDYXVzZUludmFsaWRTdGF0ZRCVoQISHgoYVWdx", + "TWV0YUdlbmVyYXRvck5vdEV4aXN0EJahAhIhChtVZ3FRdWVzdERhdGFSZXZp", + "c2lvblVwZGF0ZWQQl6ECEjMKLVVncVJldmlzaW9uQ2Fubm90U21hbGxlclRo", + "YW5SZXF1ZXN0ZWRSZXZpc2lvbhCYoQISHQoXVWdxUmV2aXNpb25TdGF0ZU5v", + "dExpdmUQmaECEiYKIFVncVJldmlzaW9uU3RhdGVPbmx5TGl2ZW5BbmRUZXN0", + "EJqhAhImCiBVZ3FBcGlTZXJ2ZXJIdHRwUmVxdWVzdEV4Y2VwdGlvbhCboQIS", + "HQoXVWdxUXVlc3RSZXZpc2lvbkNoYW5nZWQQnKECEh8KGVVncUFzc2lnbkNh", + "bm5vdE93bmVkUXVlc3QQnaECEiUKH1VncUFscmVhZHlPd25lZE9sZFJldmlz", + "aW9uUXVlc3QQnqECEhkKE1NlYXNvblBhc3NEb2NJc051bGwQ8agCEiAKGlNl", + "YXNvblBhc3NNZXRhRGF0YU5vdEZvdW5kEPKoAhImCiBTZWFzb25QYXNzUmV3", + "YXJkTWV0YURhdGFOb3RGb3VuZBDzqAISGAoSU2Vhc29uUGFzc01heEdyYWRl", + "EPSoAhIdChdTZWFzb25QYXNzTm90QWJsZVBlcmlvZBD1qAISIQobU2Vhc29u", + "UGFzc0FscmVhZHlCdXlDaGFyZ2VkEPaoAhIiChxTZWFzb25QYXNzQWxyZWFk", + "eVRha2VuUmV3YXJkEPeoAhIeChhTZWFzb25QYXNzTm90RW5vdWdoR3JhZGUQ", + "+KgCEh8KGVNlYXNvblBhc3NOZWVkQ2hhcmdlZFBhc3MQ+agCEhoKFFNlYXNv", + "blBhc3NJbnZhbGlkRXhwEPqoAhIcChZTZWFzb25QYXNzRG9jRXhjZXB0aW9u", + "EPuoAhIkCh5MYW5kQXVjdGlvblJlZGlzQ2FjaGVTZXRGYWlsZWQQ2bACEhkK", + "E0xhbmRBdWN0aW9uTm90Rm91bmQQ2rACEiUKH0xhbmRBdWN0aW9uSW52YWxp", + "ZEF1Y3Rpb25OdW1iZXIQ27ACEicKIUxhbmRBdWN0aW9uQmlkQ3VycmVuY3lU", + "eXBlSW52YWxpZBC06gESIgocTGFuZEF1Y3Rpb25CaWRQcmljZU5vdEVub3Vn", + "aBDdsAISIgocTGFuZEF1Y3Rpb25FbXB0eUNhY2hlSW5SZWRpcxDesAISJAoe", + "TGFuZEF1Y3Rpb25SZWdpc3RyeUluZm9JbnZhbGlkEN+wAhIbChVMYW5kQXVj", + "dGlvbk5vdFN0YXJ0ZWQQ4LACEioKJExhbmRBdWN0aW9uTWlzbWF0Y2hCZXR3", + "ZWVuQ2FjaGVBbmREYhDhsAISHwoZTGFuZEF1Y3Rpb25BbHJlYWR5U3RhcnRl", + "ZBDisAISHwoZTGFuZEF1Y3Rpb25MYW5kSXRlbU5vdFNldBDjsAISIgocTGFu", + "ZEF1Y3Rpb25FZGl0b3JUeXBlSW52YWxpZBDksAISHQoXTGFuZEF1Y3Rpb25B", + "bHJlYWR5RW5kZWQQ5bACEioKJExhbmRBdWN0aW9uSGlnaGVzdEJpZFVzZXJB", + "dHRyaWJFcnJvchDmsAISKgokTGFuZEF1Y3Rpb25SZWZ1bmRCaWRQcmljZUF0", + "dHJpYkVycm9yEOewAhIwCipMYW5kQXVjdGlvbkJpZGRlclJlZnVuZEJpZFBy", + "aWNlQXR0cmliRXJyb3IQ6LACEiAKGkdhbWVDb25maWdNZXRhRGF0YU5vdEZv", + "dW5kEMG4AhIqCiRBdHRyaWJ1dGVEZWZpbmVpdGlvbk1ldGFEYXRhTm90Rm91", + "bmQQwrgCEiEKG1JlcXVpcmVtZW50TWV0YURhdGFOb3RGb3VuZBDDuAISIAoa", + "U3lzdGVtTWFpbE1ldGFEYXRhTm90Rm91bmQQxLgCEh4KGEludGVyaW9yTWV0", + "YURhdGFOb3RGb3VuZBDFuAISHwoZUHJvcEdyb3VwTWV0YURhdGFOb3RGb3Vu", + "ZBDGuAISFwoRQWlDaGF0U2VydmVyU3RhcnQQqcACEhsKFUFpQ2hhdFNlcnZl", + "clJlcUZhaWxlZBCqwAISHAoWQWlDaGF0U2VydmVyQmFkcmVxdWVzdBCrwAIS", + "GwoVQWlDaGF0U2VydmVyRm9yYmlkZGVuEKzAAhIeChhBaUNoYXRTZXJ2ZXJV", + "bmF1dGhvcml6ZWQQrcACEh4KGEFpQ2hhdFNlcnZlclVzZXJOb3RGb3VuZBCu", + "wAISIwodQWlDaGF0U2VydmVyQ2hhcmFjdGVyTm90Rm91bmQQr8ACEiIKHEFp", + "Q2hhdFNlcnZlclJlYWN0aW9uTm90Rm91bmQQsMACEigKIkFpQ2hhdFNlcnZl", + "ckV4YW1wbGVEaWFsb25nTm90Rm91bmQQscACEiEKG0FpQ2hhdFNlcnZlclNl", + "c3Npb25Ob3RGb3VuZBCywAISHwoZQWlDaGF0U2VydmVyTW9kZWxOb3RGb3Vu", + "ZBCzwAISIAoaQWlDaGF0U2VydmVyTm90RW5vdWdoUG9pbnQQtMACEiMKHUFp", + "Q2hhdFNlcnZlckludmFsaWRQYXJhbWV0ZXJzELXAAhImCiBBaUNoYXRTZXJ2", + "ZXJTZXNzaW9uTGltaXRFeGNlZWRlZBC2wAISIgocQWlDaGF0U2VydmVySW52", + "YWxpZFNpZ25hdHVyZRC3wAISIwodQWlDaGF0U2VydmVyVXNlckFscmVhZHlF", + "eGlzdHMQuMACEh4KGEFpQ2hhdFNlcnZlclJlbW92ZUZhaWxlZBC5wAISHwoZ", + "QWlDaGF0U2VydmVyRHVwbGljYXRlR3VpZBC6wAISHQoXQWlDaGF0U2VydmVy", + "RHVwbGljYXRlSWQQu8ACEh4KGEFpQ2hhdFNlcnZlckNoYXROb3RGb3VuZBC8", + "wAISHgoYQWlDaGF0U2VydmVyVG9rZW5FeHBpcmVkEL3AAhIgChpBaUNoYXRT", + "ZXJ2ZXJJbnRlcm5hbFNlcnZlchC+wAISIAoaQWlDaGF0U2VydmVySW52YWxp", + "ZE1lc3NhZ2UQv8ACEiEKG0FpQ2hhdFNlcnZlclVzZXJPbmx5RmVhdHVyZRDA", + "wAISIAoaQWlDaGF0U2VydmVyT3duZXJzaGlwRXJyb3IQwcACEioKJEFpQ2hh", + "dFNlcnZlckNoYXJnZU9yZGVyTm90Rm91bmRFcnJvchDCwAISFQoPQWlDaGF0", + "U2VydmVyRW5kEIzBAhIiChxBaUNoYXRTZXJ2ZXJSZXRyeUNoYXJnZVBvaW50", + "EI3BAhIaChRBaUNoYXRTZXJ2ZXJJbmFjdGl2ZRCOwQISGAoSQWlDaGF0RG9j", + "RXhjZXB0aW9uEI/BAhIPCglOcGNJc0J1c3kQkcgCEhcKEUxhY2tPZkRhaWx5", + "Q2FsaXVtEPnPAhIXChFMYWNrT2ZUb3RhbENhbGl1bRD6zwISHgoYTGFja09m", + "Q29tbWlzc2lvbkN1cnJlbmN5EPvPAhIfChlMYWNrT2ZDb21taXNzaW9uTWF0", + "ZXJpYWxzEPzPAhIbChVJbnZhbGlkTWF0ZXJpYWxTbG90SWQQ/c8CEhYKEEZh", + "aWxUb0xvYWRDYWxpdW0Q/s8CEhwKFkZhaWxUb1NhdmVDYWxpdW1EeW5hbW8Q", + "/88CEhkKE0xhY2tPZkNvbnZlcnRDYWxpdW0Qq9ACEh8KGUdldEZhaWxFY2hv", + "U3lzdGVtUmVzcG9uc2UQ3NACEiIKHEZhaWxUb0dldEVjaG9TeXN0ZW1IdHRw", + "RXJyb3IQ3dACEiQKHkZhaWxUb0dldEVjaG9TeXN0ZW1NZXNzYWdlTnVsbBDe", + "0AISIgocRmFpbFRvR2V0RWNob1N5c3RlbUV4Y2VwdGlvbhDf0AISGgoURmFp", + "bFRvU2VuZEVjaG9TeXN0ZW0Q4NACEh8KGUZhaWxUb0dldEVjaG9TeXN0ZW1S", + "b2xsVXAQ4dACEh0KF0FjY291bnRMb2dpbkJsb2NrRW5hYmxlENGGAxIbChVJ", + "bnN0YW5jZVJvb21FeGNlcHRpb24QuY4DEiYKIEluc3RhbmNlUm9vbUNhbm5v", + "dFdyaXRlRXh0cmFJbmZvELqOAxImCiBJbnN0YW5jZVJvb21Ob3RDaGFyZ2Vk", + "U2Vhc29uUGFzcxC7jgMSHgoYSW5zdGFuY2VNZXRhRGF0YU5vdEZvdW5kELyO", + "AxIkCh5JbnN0YW5jZU1ldGFEYXRhT3ZlckxpbWl0V3JvbmcQvY4DEh8KGUlu", + "c3RhbmNlQWNjZXNzVHlwZUludmFsaWQQvo4DEiEKG0luc3RhbmNlQWNjZXNz", + "SXRlbU5vdEVub3VnaBC/jgMSKAoiSW5zdGFuY2VBY2Nlc3NTZWFzb25QYXNz", + "Tm90Q2hhcmdlZBDAjgMSGAoSSW5zdGFuY2VSb29tSXNGdWxsEMGOAxIeChhJ", + "bnN0YW5jZVJvb21JZER1cGxpY2F0ZWQQwo4DEiEKG0luc3RhbmNlUm9vbU5v", + "dEV4aXN0QXRSZWRpcxDDjgMSHgoYVGFza1Jlc2VydmF0aW9uRG9jSXNOdWxs", + "EKGWAxIiChxCaWxsaW5nR2V0UHVyY2hhc2VJbmZvRmFpbGVkEImeAxIeChhC", + "aWxsaW5nVXBkYXRlU3RhdGVGYWlsZWQQip4DEh0KF0JpbGxpbmdJbnZhbGlk", + "U3RhdGVUeXBlEIueAxIpCiNCaWxsaW5nRmFpbGVkUGFyc2VQcm9kdWN0TWV0", + "YUlkVHlwZRCMngMSHQoXQmlsbGluZ1N0YXRlVHlwZUludmFsaWQQjZ4DEiMK", + "HUJpbGxpbmdTdGF0ZVR5cGVDYW50QmVDaGFuZ2VkEI6eAxIcChZCaWxsaW5n", + "U3RhdGVUeXBlUmVmdW5kEI+eAxIkCh5CaWxsaW5nU3RhdGVUeXBlUmVmdW5k", + "Q29tcGxldGUQkJ4DEhoKFFRheGlNZXRhRGF0YU5vdEZvdW5kEPGlAxIVCg9U", + "YXhpVHlwZUludmFsaWQQ8qUDEhoKFFdhcnBNZXRhRGF0YU5vdEZvdW5kEPOl", + "AxIVCg9XYXJwVHlwZUludmFsaWQQ9KUDEhQKDlJlbnRhbE5vdEZvdW5kENmt", + "AxIjCh1SZW50YWxEb2NMb2FkRHVwbGljYXRlZFJlbnRhbBDarQMSFQoPUmVu", + "dGFsRG9jSXNOdWxsENutAxIcChZSZW50YWxOb3RBdmFpbGFibGVMYW5kENyt", + "AxIaChRSZW50YWxBZGRyZXNzSW52YWxpZBDdrQMSHQoXUmVudGFsQWRkcmVz", + "c0lzTm90RW1wdHkQ3q0DEh8KGVJlbnRhbGZlZU1ldGFEYXRhTm90Rm91bmQQ", + "360DEh4KGFJlbnRhbENvbnRyYWN0SW5mb1VwZGF0ZRDgrQMSIgocUmVudGFs", + "Q3VycmVuY3lBbW91bnRJc1Rvb0xvdxDJtQMSHwoZUmVudGFsQ3VycmVuY3lU", + "eXBlSXNXcm9uZxDKtQMSKAoiUGFja2FnZUxhc3RPcmRlclJlY29kZURvY0V4", + "Y2VwdGlvbhDBtQMSHwoZUGFja2FnZVJlcGVhdERvY0V4Y2VwdGlvbhDCtQMS", + "GQoTQmVhY29uU2hvcEV4Y2VwdGlvbhCpvQMSHwoZQmVhY29uU2hvcEludmFs", + "aWRBcmd1bWVudBCqvQMSJwohQmVhY29uU2hvcEJlYWNvbklzTm90SW5SZW50", + "YWxIb21lEKu9AxIcChZCZWFjb25TaG9wTm90Rm91bmRJdGVtEKy9AxIdChdC", + "ZWFjb25TaG9wTm90RW5vdWdoSXRlbRCtvQMSIgocQmVhY29uU2hvcEludmFs", + "aWRJdGVtRm9yU2VsbBCuvQMSIAoaQmVhY29uU2hvcE5vdEZvdW5kTWV0YURh", + "dGEQr70DEh8KGUJlYWNvblNob3BMb3dTZWxsaW5nUHJpY2UQsL0DEiQKHkJl", + "YWNvblNob3BOb3RFbm91Z2hSZWdpc3RlckZlZRCxvQMSGgoUQmVhY29uU2hv", + "cFNsb3RJc0Z1bGwQsr0DEicKIUJlYWNvblNob3BPdmVyT25lRGF5UmVnaXN0", + "ZXJMaW1pdBCzvQMSHgoYQmVhY29uU2hvcEZhaWxlZFRvQ3JlYXRlELS9AxIj", + "Ch1CZWFjb25TaG9wRmFpbGVkUmVnaXN0ZXJCb2FyZBC1vQMSIQobQmVhY29u", + "U2hvcEZhaWxlZERlbGV0ZUJvYXJkELa9AxIlCh9CZWFjb25TaG9wTm90Rm91", + "bmRJdGVtRnJvbUJvYXJkELe9AxIgChpCZWFjb25TaG9wUHJvZmlsZUV4Y2Vw", + "dGlvbhC4vQMSJQofQmVhY29uU2hvcE5vdEVub3VnaFJlZ2lzdGVyR29sZBC5", + "vQMSIAoaQmVhY29uU2hvcExhY2tPZkl0ZW1BbW91bnQQur0DEiIKHEJlYWNv", + "blNob3BGYWlsZWRHZXRCb2FyZEl0ZW0Qu70DEiMKHUJlYWNvblNob3BTb2xk", + "UmVjb3JkRXhjZXB0aW9uELy9AxIrCiVCZWFjb25TaG9wRmFpbGVkUmVsb2Fk", + "QmVhY29uU2hvcEludmVuEL29AxIdChdCZWFjb25TaG9wVXBkYXRlTmV3RGF0", + "YRC+vQMSJgogQmVhY29uU2hvcEZhaWxlZFVwZGF0ZURhdGFGcm9tRGIQv70D", + "EiMKHUJlYWNvblNob3BOb3RGb3VuZFNvbGRSZWNvcmRzEMC9AxIhChtCZWFj", + "b25TaG9wUHJvZmlsZURvY0lzRW1wdHkQwb0DEiIKHEJlYWNvblNob3BTb2xk", + "UHJpY2VFeGNlcHRpb24Qwr0DEiEKG0JlYWNvblNob3BOb3RGb3VuZFNvbGRQ", + "cmljZRDDvQMSJAoeQmVhY29uU2hvcEZhaWxlZFRvRmluZE9yVXBkYXRlEMS9", + "AxIbChVCZWFjb25TaG9wRGJFeGNlcHRpb24Qxb0DEiAKGkJlYWNvblNob3BP", + "dmVyU2VsbGluZ1ByaWNlEMa9AxIiChxCZWFjb25TaG9wT3ZlclJlbnRhbFNh", + "ZmVUaW1lEMe9AxIjCh1CZWFjb25TaG9wRGVhY3RpdmVJdGVtRm9yU2VsbBDI", + "vQMSGgoUVWdxSW52YWxpZFRhc2tBY3Rpb24Q4dQDEhsKFVVncVRhc2tBY3Rp", + "b25EaXNhYmxlZBDi1AMSGgoUVWdxSW52YWxpZERpYWxvZ1R5cGUQ49QDEh8K", + "GVVncUludmFsaWREaWFsb2dDb25kaXRpb24Q5NQDEhgKElVncVZhbGlkYXRp", + "b25FcnJvchDl1AMSEwoNVWdxTnVsbEVudGl0eRDm1AMSGQoTVWdxU3RhdGVD", + "aGFuZ2VFcnJvchDn1AMSGwoVVWdxTm90RW5vdWdoUXVlc3RTbG90EOjUAxIV", + "Cg9VZ3FOb3RBbGxvd0VkaXQQ6dQDEhoKFFVncURpYWxvZ0lzTm90SW5UYXNr", + "EOrUAxIVCg9VZ3FSZXF1aXJlSW1hZ2UQ69QDEhYKEFVncVJlcXVpcmVCZWFj", + "b24Q7NQDEhkKE1VncUJlYWNvbklucHV0RXJyb3IQ7dQDEhoKFFVncUdhbWVE", + "QkFjY2Vzc0Vycm9yEO7UAxIVCg9VZ3FOb3RPd25VZ2NOcGMQ79QDEh0KF1Vn", + "cUFscmVhZHlFeGlzdHNBY2NvdW50EPDUAxIYChJVZ3FTZXJ2ZXJFeGNlcHRp", + "b24Q8dQDEh4KGFVncUludmFsaWRXZWJQb3J0YWxUb2tlbhDy1AMSFQoPVWdx", + "SW52YWxpZFRva2VuEPPUAxIXChFVZ3FSZXF1aXJlQWNjb3VudBD01AMSHgoY", + "VWdxTm90RW5vdWdoQ3JlYXRvclBvaW50EPXUAxISCgxVZ3FTbG90TGltaXQQ", + "9tQDEh8KGVVncUV4Y2VlZFRyYW5zYWN0aW9uUmV0cnkQ99QDEhgKElVncU1l", + "dGF2ZXJzZU9ubGluZRD41AMSFAoOVWdxQXV0aFJlbW92ZWQQ+dQDEhsKFVVn", + "cUludmFsaWRQcmVzZXRJbWFnZRD61AMSGgoUVWdxTm90RW5vdWdoQ3VycmVu", + "Y3kQ+9QDEhYKEFVncUN1cnJlbmN5RXJyb3IQ/NQDEiIKHFVncUFscmVhZHlF", + "eGlzdHNSZXNlcnZlR3JhZGUQ/dQDEhsKFVVncUludmFsaWRSZXNlcnZlVGlt", + "ZRD+1AMSGQoTVWdxU2FtZUdyYWRlUmVzZXJ2ZRD/1AMSGgoUVWdxQWxyZWFk", + "eUJvb2ttYXJrZWQQydwDEhYKEFVncU5vdEJvb2ttYXJrZWQQytwDEhUKD1Vn", + "cUFscmVhZHlMaWtlZBDL3AMSEQoLVWdxTm90TGlrZWQQzNwDEhgKElVncUFs", + "cmVhZHlSZXBvcnRlZBDN3AMSFAoOVWdxTm90T3duUXVlc3QQztwDEhUKD1Vn", + "cUludmFsaWRTdGF0ZRDP3AMSHQoXTmZ0Rm9yT3duZXJBbGxHZXRGYWlsZWQQ", + "seQDEhcKEU1xUmVzcG9uc2VUaW1lb3V0EJnsAxIZChNJbnRlcm5hbFNlcnZl", + "ckVycm9yEPGiBBIOCghSZGJFcnJvchDyogQSEQoLRHluYW1vRXJyb3IQ86IE", + "EhQKDkludmFsaWRSZXF1ZXN0EPuiBBIWChBQbGFuZXRJZE5vdEZvdW5kENmq", + "BBIjCh1QbGFuZXRTZWNyZXRLZXlEb2VzTm90TWF0Y2hlZBDaqgQSFgoQSW52", + "YWxpZFBsYW5ldEp3dBDbqgQSFgoQRXhwaXJlZFBsYW5ldEp3dBDcqgQSIAoa", + "TWV0YXZlcnNlQ2xpZW50T25Db25uZWN0ZWQQoawEEhQKDkludmFsaWRVc2Vy", + "Snd0EKKsBBIUCg5FeHBpcmVkVXNlckp3dBCjrAQSFQoPQWNjb3VudE5vdEZv", + "dW5kEIWtBBISCgxVc2VyTm90Rm91bmQQhq0EEh0KF0V4Y2hhbmdlT3JkZXJJ", + "ZE5vdEZvdW5kEMGyBBIqCiRFeGNoYW5nZVRvdGFsT3JkZXJEYWlseUxpbWl0", + "RXhjZWVkZWQQwrIEEikKI0V4Y2hhbmdlVXNlck9yZGVyRGFpbHlMaW1pdEV4", + "Y2VlZGVkEMOyBBIVCg9Cb3RQbGF5ZXJJc051bGwQ0ds6EhwKFkNsaWVudFRv", + "TG9naW5SZXNJc051bGwQ0ts6EiAKGkNsaWVudFRvTG9naW5NZXNzYWdlSXNO", + "dWxsENPbOhIbChVDbGllbnRUb0dhbWVSZXNJc051bGwQ1Ns6Eh8KGUNsaWVu", + "dFRvR2FtZU1lc3NhZ2VJc051bGwQ1ds6EhsKFUNvbm5lY3RlZFRvU2VydmVy", + "RmFpbBDW2zoSGgoUU2NlbmFyaW9uUGFyYW1Jc051bGwQ19s6EiEKGkJhdHRs", + "ZVJvb21Db250ZW50c1R5cGVPbmx5EMGxnwUSHgoXQmF0dGxlSW5zdGFuY2VU", + "eXBlRXJyb3IQwrGfBRIlCh5CYXR0bGVJbnN0YW5jZUluZm9BbHJlYWR5RXhp", + "c3QQw7GfBRIhChpCYXR0bGVJbnN0YW5jZUluZm9Ob3RFeGlzdBDEsZ8FEiQK", + "HUJhdHRsZUluc3RhbmNlSm9pblBsYXllckVycm9yEMWxnwUSLQomQmF0dGxl", + "SW5zdGFuY2VVc2FibGVTcGF3blBvaW50Tm90RXhpc3QQxrGfBRIdChZCYXR0", + "bGVJbnN0YW5jZUluQWN0aXZlEMexnwUSJAodQmF0dGxlSW5zdGFuY2VOb3RF", + "eGlzdEFuY2hvcnMQyLGfBRIlCh5CYXR0bGVJbnN0YW5jZUFkZFBvZENvbWJh", + "dEZhaWwQybGfBRInCiBCYXR0bGVJbnN0YW5jZU9iamVjdE1ldGFOb3RFeGlz", + "dBDKsZ8FEjAKKUJhdHRsZUluc3RhbmNlT2JqZWN0SW50ZXJhY3Rpb25Ob3R5", + "ZXRUaW1lEMuxnwUSIwocQmF0dGxlSW5zdGFuY2VPYmplY3ROb3RFeGlzdBDM", + "sZ8FEisKJEJhdHRsZUluc3RhbmNlUG9kQ29tYmF0QWxyZWFkeU9jY3VweRDN", + "sZ8FEi8KKEJhdHRsZUluc3RhbmNlT2JqZWN0SW50ZXJhY3Rpb25Ob3RBY3Rp", + "dmUQz7GfBRIrCiRCYXR0bGVJbnN0YW5jZU1ldGFDb25maWdOb3RFeGlzdERh", + "dGEQ0LGfBRIrCiRCYXR0bGVJbnN0YW5jZU1ldGFSZXdhcmROb3RFeGlzdERh", + "dGEQ0bGfBRIzCixCYXR0bGVJbnN0YW5jZVBpY2t1cFBvZEdlbmVyYXRlZFRp", + "bWVOb3RFeGlzdBDSsZ8FEioKI0JhdHRsZUluc3RhbmNlUGlja3VwUG9kTm90", + "RXhpc3REYXRhENOxnwUSJwogQmF0dGxlSW5zdGFuY2VOb3RFeGlzdFBsYXll", + "ckluZm8Q1LGfBRIkCh1CYXR0bGVJbnN0YW5jZUludGVyYWN0aW9uRmFpbBDV", + "sZ8FEjEKKkJhdHRsZUluc3RhbmNlUGlja3VwUG9kUmV3YXJkQWxsb2NhdGVF", + "cnJvchDWsZ8FEiYKH0JhdHRsZUluc3RhbmNlTm90RXhpc3RFdmVudEluZm8Q", + "17GfBRIgChlCYXR0bGVJbnN0YW5jZUNsb3NpbmdUaW1lENixnwUSIgobQmF0", + "dGxlSW5zdGFuY2VTZXFQYXJzZUVycm9yENmxnwUSIwocQmF0dGxlSW5zdGFu", + "Y2VFdmVudElkSW52YWxpZBDasZ8FEiIKG0dhbWVNb2RlSm9pbkhhbmRsZXJO", + "b3RFeGlzdBCksp8FEiIKG0dhbWVNb2RlSW5pdEhhbmRsZXJOb3RFeGlzdBCl", + "sp8FEikKIkdhbWVNb2RlSm9pblN1Y2Nlc3NIYW5kbGVyTm90RXhpc3QQprKf", + "BRIZChJHYW1lTW9kZUNyZWF0ZUZhaWwQp7KfBRIaChNHYW1lTW9kZUNsYXNz", + "SXNOdWxsEKiynwUSGwoUR2FtZU1vZGVBbHJlYWR5RXhpc3QQqbKfBRIgChlH", + "YW1lTW9kZUludmFsaWRBbmNob3JHdWlkEKqynwUSHAoVR2FtZU1vZGVJbnZh", + "bGlkQWN0aW9uEKuynwUSMwosR2FtZU1vZGVQcmVwYXJhdGlvbkZvckxlYXZp", + "bmdIYW5kbGVyTm90RXhpc3QQrLKfBRIjChxHYW1lTW9kZUxlYXZlSGFuZGxl", + "ck5vdEV4aXN0EK2ynwUSHwoYR2FtZU1vZGVSdW5SYW5rVXNlckV4aXN0EK6y", + "nwUSFwoQTWF0Y2hDb21tb25FcnJvchCBttwFEhgKEU1hdGNoVXNlck5vdEZv", + "dW5kEIucnDkSHwoYTWF0Y2hVc2VyUmVzZXJ2ZWRBbHJlYWR5EIycnDkSGwoU", + "TWF0Y2hVc2VyTm90UmVzZXJ2ZWQQjZycORIdChZNYXRjaFVzZXJHcm91cE5v", + "dEZvdW5kEI6cnDkSGQoSTWF0Y2hUYXNrRXhjZXB0aW9uEJWcnDkSHAoVTWF0", + "Y2hTZXJ2ZXJOb1Jlc3BvbnNlEOa93AUSEQoKTWF0Y2hFcnJvchDnvdwFEioK", + "I1NlcnZlck1ldHJpY3NUcmlnZ2VySGFuZGxlck5vdEZvdW5kEP261y8SDAoI", + "RHVwTG9naW4QARIKCgZNb3ZpbmcQAhILCgdEYkVycm9yEAMSDAoIS2lja0Zh", + "aWwQBBIWChJOb3RDb3JyZWN0UGFzc3dvcmQQBRIQCgxOb3RGb3VuZFVzZXIQ", + "BhIQCgxOb0dhbWVTZXJ2ZXIQBxIQCgxMb2dpblBlbmRpbmcQCBISCg5Ob3RJ", + "bXBsZW1lbnRlZBAJEh0KGU5vdEV4aXN0U2VsZWN0ZWRDaGFyYWN0ZXIQChIV", + "ChFOb3RFeGlzdENoYXJhY3RlchALEhQKEFNlcnZlckxvZ2ljRXJyb3IQDBIR", + "Cg1Ob1Blcm1pc3Npb25zEA4SDQoJUmVkaXNGYWlsEA8SDgoJTG9naW5GYWls", + "EOgHEhMKDkR1cGxpY2F0ZWRVc2VyEOkHEhEKDEludmFsaWRUb2tlbhDqBxIV", + "ChBOb3RDb3JyZWN0U2VydmVyEOsHEg8KCkluc3BlY3Rpb24Q7AcSDgoJQmxh", + "Y2tMaXN0EO0HEg8KClNlcnZlckZ1bGwQ7gcSEwoOTm90Rm91bmRTZXJ2ZXIQ", + "7wcSEgoNTm90Rm91bmRUYWJsZRDwBxIPCgpUYWJsZUVycm9yEPEHEhUKEElu", + "dmFsaWRDb25kaXRpb24QzAgSEwoOQ2hhckNyZWF0ZUZhaWwQ0A8SEwoOQ2hh", + "clNlbGVjdEZhaWwQtBASEwoOQ3JlYXRlUm9vbUZhaWwQuBcSEQoMSm9pblJv", + "b21GYWlsEJwYEhUKEEpvaW5JbnN0YW5jZUZhaWwQgBkSGwoWTm90RXhpc3RJ", + "bnN0YW5jZVRpY2tldBCBGRIWChFMZWF2ZUluc3RhbmNlRmFpbBDkGRIZChRO", + "b3RFeGlzdEluc3RhbmNlUm9vbRDIGhIbChZOb3RDb3JyZWN0SW5zdGFuY2VS", + "b29tEMkaEg8KClBvc0lzRW1wdHkQyhoSFAoPRW50ZXJNeUhvbWVGYWlsENgd", + "EhQKD0xlYXZlTXlIb21lRmFpbBC8HhIXChJFeGNoYW5nZU15SG9tZUZhaWwQ", + "oB8SFwoSTm90Rm91bmRNeUhvbWVEYXRhEKEfEhMKDk5vdE15SG9tZU93bmVy", + "EKIfEhYKEUFscmVhZHlIYXZlTXlIb21lEKMfEhsKFkV4Y2hhbmdlTXlIb21l", + "UHJvcEZhaWwQhCASGQoURXhjaGFuZ2VCdWlsZGluZ0ZhaWwQzCESHwoaRXhj", + "aGFuZ2VCdWlsZGluZ0xGUHJvcEZhaWwQsCISGQoURXhjaGFuZ2VJbnN0YW5j", + "ZUZhaWwQlCMSIQocRXhjaGFuZ2VTb2NpYWxBY3Rpb25TbG90RmFpbBD4IxId", + "ChhOb3RGb3VuZFNvY2lhbEFjdGlvbkRhdGEQ+iMSGgoVTm90SW5Tb2NpYWxB", + "Y3Rpb25TbG90EPsjEhwKF0FscmVhZHlIYXZlU29jaWFsQWN0aW9uEPwjEhkK", + "FE5vdEZvdW5kQnVpbGRpbmdEYXRhEKwkEhYKEU5vdEZvdW5kRmxvb3JJbmZv", + "EK0kEhYKEU5vdEZvdW5kSW5kdW5EYXRhEK8kEhkKFEVudGVyRml0dGluZ1Jv", + "b21GYWlsENwkEhoKFU5vdEVudGV0ZWRGaXR0aW5nUm9vbRDdJBIQCgtNYWtl", + "RmFpbE90cBDeJBIdChhOb3RFeGlzdFJvb21JbmZvRm9yRW50ZXIQ3yQSHwoa", + "Tm90RXhpc3RHYW1lU2VydmVyRm9yRW50ZXIQ4CQSFQoQUG9zaXRpb25TYXZl", + "RmFpbBDhJBIcChdFeGNoYW5nZUVtb3Rpb25TbG90RmFpbBDAJRIaChVFbW90", + "aW9uU2xvdE91dE9mUmFuZ2UQwSUSHAoXTm90Rm91bmRBbmNob3JHdWlkSW5N", + "YXAQoyYSFwoSTm90Rm91bmRBbmNob3JHdWlkEKQmEg8KClByb3BJc1VzZWQQ", + "piYSFAoPUHJvcFR5cGVpc1dyb25nEKcmEhgKE05vdEZvdW5kRW1vdGlvbkRh", + "dGEQuCYSFQoQTm90SW5FbW90aW9uU2xvdBC5JhITCg5DcmVhdGVJdGVtRmFp", + "bBCEJxIQCgtBZGRJdGVtRmFpbBCFJxITCg5EZWxldGVJdGVtRmFpbBCGJxIS", + "Cg1Ob01vcmVBZGRJdGVtEIcnEhEKDE5vdEZvdW5kSXRlbRCIJxISCg1Ob3RF", + "bm91Z2hJdGVtEIknEhUKEEludmFsaWRTbG90SW5kZXgQiicSFwoSRHVwbGlj", + "YXRlZEl0ZW1HdWlkEIsnEhgKE05vdEZvdW5kSXRlbVRhYmxlSWQQjCcSFAoP", + "Tm90U2VsZWN0ZWRDaGFyEI0nEhAKC05vdEZvdW5kTWFwEI4nEhEKDE5vdEVt", + "cHR5U2xvdBCPJxIOCglFbXB0eVNsb3QQkCcSGAoTTm90Rm91bmRCdWZmVGFi", + "bGVJZBCRJxIRCgxOb3RGb3VuZEJ1ZmYQkicSHAoXTm90RXhpc3RGb3JlY2Vk", + "TW92ZUluZm8QkycSFwoSRHVwbGljYXRlZE5pY2tOYW1lEJUnEhcKEkFscmVh", + "ZHlTZXROaWNrTmFtZRCWJxIZChREaXNhbGxvd2VkQ2hhcmFjdGVycxCXJxIT", + "Cg5EYlVwZGF0ZUZhaWxlZBCYJxIUCg9JbnZhbGlkQXJndW1lbnQQmScSFAoP", + "TWFpbFN5c3RlbUVycm9yEKYnEhIKDUludmFsaWRUYXJnZXQQpycSEQoMTm90", + "Rm91bmRNYWlsEKgnEhQKD0VtcHR5SXRlbUluTWFpbBCpJxIWChFNYWlsU2Vu", + "ZENvdW50T3ZlchCqJxIUCg9DaGFuZ2VkTmlja05hbWUQqycSFgoRU3RhdGVD", + "aGFuZ2VGYWlsZWQQsCcSEwoOTm90Rm91bmRUYXJnZXQQuicSFgoRQmxvY2tl", + "ZEZyb21UYXJnZXQQuycSEQoMTG9nT2ZmVGFyZ2V0ELwnEhMKDkNhbnRTZW5k", + "VG9TZWxmEL0nEhQKD0J1ZmZUeXBlSXNXcm9uZxDOJxIZChRSZWdpc3RlclRv", + "b2xTbG90RmFpbBDYJxIbChZEZXJlZ2lzdGVyVG9vbFNsb3RGYWlsENknEhcK", + "ElRvb2xTbG90T3V0T2ZSYW5nZRDaJxIVChBOb3RGb3VuZFRvb2xTbG90ENsn", + "EhIKDUVtcHR5VG9vbFNsb3QQ3CcSHQoYRm9sZGVyTmFtZUV4Y2VlZGVkTGVu", + "Z3RoEOInEhsKFkZvbGRlck5hbWVBbHJlYWR5RXhpc3QQ4ycSFwoSRm9sZGVy", + "TmFtZU5vdEV4aXN0EOQnEiIKHUZvbGRlck5hbWVBbHJlYWR5TWF4SG9sZENv", + "dW50EOUnEhYKEUZvbGRlckNvdW50RXhjZWVkEOYnEhUKEEZvbGRlckNyZWF0", + "ZUZhaWwQ5ycSHgoZRm9sZGVyUmVOYW1lQ2Fubm90RGVmYXVsdBDoJxIaChVG", + "b2xkZXJPZGVydHlwZUludmFsaWQQ6ScSHgoZRnJpZW5kUmVxdWVzdE5vdEV4", + "aXN0SW5mbxDsJxIdChhGcmllbmRSZXF1ZXN0QWxyZWFkeVNlbmQQ7ScSIAob", + "RnJpZW5kUmVxdWVzdEFscmVhZHlSZWNlaXZlEO4nEiAKG0ZyaWVuZFJlcXVl", + "c3RDYW50U2VuZFRvU2VsZhDvJxImCiFGcmllbmRSZXF1ZXN0Tm90RXhpc3RS", + "ZWNlaXZlZEluZm8Q8CcSHwoaRnJpZW5kUmVxdWVzdEFscmVhZHlGcmllbmQQ", + "8ScSEAoLSW52YWxpZFR5cGUQ8icSGQoUSW52YWxpZEF0dHJpYnV0ZVNsb3QQ", + "8ycSJQogRnJpZW5kUmVxdWVzdENhbm5vdFNlbmRCbG9ja1VzZXIQ/ycSHgoZ", + "QWRkRnJpZW5kQWxyZWFkeUJsb2NrVXNlchCAKBIfChpBZGRGcmllbmROb3RF", + "eGlzdENoYXJhY3RlchCBKBIbChZBZGRGcmllbmRBbHJlYWR5RnJpZW5kEIIo", + "EiIKHUFkZEZyaWVuZEFscmVhZHlDYW5jZWxSZXF1ZXN0EIMoEhwKF0FkZEZy", + "aWVuZEFscmVhZHlFeHBpcmVkEIQoEhcKEkZyaWVuZEluZm9Ob3RFeGlzdBCK", + "KBIlCiBGcmllbmRJbmZvTXlDb3VudEFscmVhZHlNYXhDb3VudBCLKBIpCiRG", + "cmllbmRJbmZvT3RoZXJzQ291bnRBbHJlYWR5TWF4Q291bnQQjCgSFgoRRnJp", + "ZW5kSW5mb09mZmxpbmUQjSgSGwoWRnJpZW5kSW5mb0FscmVhZHlFeGlzdBCO", + "KBIhChxGcmllbmRJbnZpdGVNeVBvc0lzTm90TXlIb21lEJQoEiEKHEZyaWVu", + "ZEludml0ZURvbnREaXN0dXJiU3RhdGUQlSgSIQocRnJpZW5kSW52aXRlRXhw", + "aXJlVGltZVJlbWFpbhCWKBIjCh5GcmllbmRJbnZpdGVXYWl0aW5nT3RoZXJJ", + "bnZpdGUQlygSHgoZRnJpZW5kSW52aXRlQWxyZWFkeUV4cGlyZRCYKBIfChpG", + "cmllbmRLaWNrTXlQb3NJc05vdE15SG9tZRCZKBIdChhGcmllbmRLaWNrTWVt", + "YmVyTm90RXhpc3QQmigSHAoXRnJpZW5kSXNJbkFub3RoZXJNeWhvbWUQmygS", + "FgoRQmxvY2tVc2VyTWF4Q291bnQQnigSGgoVQmxvY2tVc2VyQWxyZWFkeUJs", + "b2NrEJ8oEh4KGUJsb2NrVXNlckNhbm5vdFNlbmRNYWlsVG8QoCgSEwoOQmxv", + "Y2tlZEJ5T3RoZXIQoSgSHQoYQmxvY2tVc2VyQ2Fubm90V2hpc3BlclRvEKIo", + "EhMKDkJsb2NrSW5mb0VtcHR5EKMoEh8KGkJsb2NrVXNlckNhbm5vdEludml0", + "ZVBhcnR5EKQoEiMKHkNhcnRTZWxsVHlwZU1pc3NNYXRjaFdpdGhUYWJsZRDQ", + "KBIYChNDYXJ0RnVsbFN0YWNrb2ZJdGVtENEoEg8KCkNhcnRpc0Z1bGwQ0igS", + "EAoLQmFuTmlja05hbWUQ0ygSGQoUQ3VycmVuY3lOb3RGb3VuZERhdGEQmCoS", + "FgoRQ3VycmVuY3lOb3RFbm91Z2gQmSoSGQoUQ3VycmVuY3lJbnZhbGlkVmFs", + "dWUQmioSFAoPU3RhcnRCdWZmRmFpbGVkEPwqEhMKDlN0b3BCdWZmRmFpbGVk", + "EP0qEhUKEE5vdEZvdW5kTmlja05hbWUQ4CsSIAobTm90Rm91bmRUYXR0b29B", + "dHRyaWJ1dGVEYXRhEKgtEhMKDk5vdEZvdW5kU2hvcElkEIwuEhgKE1RpbWVP", + "dmVyRm9yUHVyY2hhc2UQjS4SFwoSTm90RW5vdWdoQXR0cmlidXRlEI4uEhIK", + "DUludmFsaWRHZW5kZXIQjy4SEAoLSXNFcXVpcEl0ZW0QkC4SEAoLSW52YWxp", + "ZEl0ZW0QkS4SFgoRQWxyZWFkeVJlZ2lzdGVyZWQQki4SFQoQTm90Rm91bmRT", + "aG9wSXRlbRCTLhIWChFOb3RFbm91Z2hTaG9wSXRlbRCULhIVChBJbnZhbGlk", + "SXRlbUNvdW50EJUuEhIKDUludmVudG9yeUZ1bGwQli4SFgoRTm90Rm91bmRQ", + "cm9kdWN0SWQQly4SHQoYUmFuZG9tQm94SXRlbURhdGFJbnZhbGlkENQvEhYK", + "EU5vdEV4aXN0R2FjaGFEYXRhENUvQi8KK2NvbS5jYWxpdmVyc2UuYWRtaW4u", + "ZG9tYWluLlJhYmJpdE1xLm1lc3NhZ2VQAWIGcHJvdG8z")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ServerErrorCode), }, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Result), global::Result.Parser, new[]{ "ErrorCode", "ResultString" }, null, null, null, null) + })); + } + #endregion + +} +#region Enums +/// +/// 나중에 Error 카테고리별 범위 구성을 설정 해야 한다. - kangms +/// +public enum ServerErrorCode { + [pbr::OriginalName("Success")] Success = 0, + /// + ///============================================================================================= + /// 결과 코드 관련 오류 + ///============================================================================================= + /// + [pbr::OriginalName("ResultCodeNotSet")] ResultCodeNotSet = -1, + /// + ///============================================================================================= + /// 시스템 오류 : 10000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TryCatchException")] TryCatchException = 10001, + /// + /// DotNet 예외가 발생 했습니다. + /// + [pbr::OriginalName("DotNetException")] DotNetException = 10002, + /// + /// ProudNet 예외가 발생 했습니다. + /// + [pbr::OriginalName("ProudNetException")] ProudNetException = 10003, + /// + /// RabbitMQ 예외가 발생 했습니다. + /// + [pbr::OriginalName("RabbitMqException")] RabbitMqException = 10004, + /// + /// DynamoDB 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbException")] DynamoDbException = 10005, + /// + /// DynamoDB Transact 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbTransactException")] DynamoDbTransactException = 10006, + /// + /// Redis 예외가 발생 했습니다. + /// + [pbr::OriginalName("RedisException")] RedisException = 10007, + /// + /// Meta 스키마 및 데이터 예외가 발생 했습니다. + /// + [pbr::OriginalName("MetaInfoException")] MetaInfoException = 10008, + /// + /// MySqlDB 예외가 발생 했습니다. + /// + [pbr::OriginalName("MySqlDbException")] MySqlDbException = 10009, + /// + /// MongoDb 예외가 발생 했습니다. + /// + [pbr::OriginalName("MongoDbException")] MongoDbException = 10010, + /// + ///============================================================================================= + /// NLog 관련 오류 : 10030 ~ + ///============================================================================================= + /// + [pbr::OriginalName("NlogWithAwsCloudWatchSetupFailed")] NlogWithAwsCloudWatchSetupFailed = 10031, + /// + /// NLog 객체가 초기화되지 않은 상태 입니다. + /// + [pbr::OriginalName("NlogNotInitialized")] NlogNotInitialized = 10032, + /// + ///============================================================================================= + /// 비즈니스 로그 관련 오류 : 10050 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LogActionIsNull")] LogActionIsNull = 10051, + /// + /// LogAppender 객체가 Null 입니다. + /// + [pbr::OriginalName("LogAppenderIsNull")] LogAppenderIsNull = 10052, + /// + /// LogFormatter 객체가 Null 입니다. + /// + [pbr::OriginalName("LogFormatterIsNull")] LogFormatterIsNull = 10053, + /// + /// LogActionType 오류 입니다. + /// + [pbr::OriginalName("LogActionTypeInvalid")] LogActionTypeInvalid = 10054, + /// + ///============================================================================================= + /// 네트워크 오류 : 10100 ~ + ///============================================================================================= + /// ProudNet 오류 + /// + [pbr::OriginalName("RmiHostIsNull")] RmiHostIsNull = 10101, + /// + /// Rmi Host 핸들러에 바인딩을 실패 했습니다. + /// + [pbr::OriginalName("RmiHostHandlerBindFailed")] RmiHostHandlerBindFailed = 10102, + /// + /// Stub 핸들러에 바인딩을 실패 했습니다. + /// + [pbr::OriginalName("SubHandlerBindFailed")] SubHandlerBindFailed = 10103, + /// + /// Proxy 연결을 실패 했습니다. + /// + [pbr::OriginalName("ProxyAttachFailed")] ProxyAttachFailed = 10104, + /// + /// 패킷 최대 사이즈 설정 실패. + /// + [pbr::OriginalName("SetMessageMaxLengthFailed")] SetMessageMaxLengthFailed = 10105, + /// + /// 네트워크 모듈 오류 + /// + [pbr::OriginalName("PacketRecvHandlerRegisterFailed")] PacketRecvHandlerRegisterFailed = 10151, + /// + /// 패킷 송신 핸들러에 등록을 실패 했습니다. + /// + [pbr::OriginalName("PacketSendHandlerRegisterFailed")] PacketSendHandlerRegisterFailed = 10152, + /// + /// 패킷 오류 + /// + [pbr::OriginalName("PacketRecvInvalid")] PacketRecvInvalid = 10153, + /// + /// 수신 패킷 핸들러를 찾지 못했습니다. + /// + [pbr::OriginalName("RacketRecvHandlerNotFound")] RacketRecvHandlerNotFound = 10154, + /// + /// 대용량 패킷이 모두 수신되지 않았습니다. + /// + [pbr::OriginalName("LargePacketNotAllReceived")] LargePacketNotAllReceived = 10155, + /// + /// 대용량 패킷이 수신 대기 시간이 오래 지났다. + /// + [pbr::OriginalName("LargePacketRecvTimeOver")] LargePacketRecvTimeOver = 10156, + /// + /// 대용량 패킷이 수신 타입이 유효하지 않다. + /// + [pbr::OriginalName("LargePacketProcessTypeInvalid")] LargePacketProcessTypeInvalid = 10157, + /// + /// 대용량 패킷이 데이터가 존재하지 않는다. + /// + [pbr::OriginalName("LargePacketDataIsNull")] LargePacketDataIsNull = 10158, + /// + /// 대용량 패킷 Exception + /// + [pbr::OriginalName("LargePacketException")] LargePacketException = 10159, + /// + ///============================================================================================= + /// DB 오류 : 10200 ~ + ///============================================================================================= + /// DB 공통 오류 + /// + [pbr::OriginalName("DbQueryTypeInvalid")] DbQueryTypeInvalid = 10201, + /// + /// DynamoDB 오류 + /// + [pbr::OriginalName("DynamoDbTransactionCanceledException")] DynamoDbTransactionCanceledException = 10211, + /// + /// DynamoDB AmazonDynamoDBException 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbAmazonDynamoDbException")] DynamoDbAmazonDynamoDbException = 10212, + /// + /// DynamoDB AmazonServiceException 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbAmazonServiceException")] DynamoDbAmazonServiceException = 10213, + /// + /// DynamoDB Config 파일 로딩을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbConfigLoadFailed")] DynamoDbConfigLoadFailed = 10214, + /// + /// DynamoDB 연결을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbConnectFailed")] DynamoDbConnectFailed = 10215, + /// + /// DynamoDB Table 생성을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbTableCreateFailed")] DynamoDbTableCreateFailed = 10216, + /// + /// DynamoDB Table과 연결이 되어있지 않습니다. + /// + [pbr::OriginalName("DynamoDbTableNotConnected")] DynamoDbTableNotConnected = 10217, + /// + /// DynamoDB Query를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbQueryFailed")] DynamoDbQueryFailed = 10218, + /// + /// DynamoDB Item 저장 크기를 초과 했습니다. + /// + [pbr::OriginalName("DynamoDbItemSizeExceeded")] DynamoDbItemSizeExceeded = 10219, + /// + /// DynamoDB Query와 일치하는 Attribute가 아닙니다. + /// + [pbr::OriginalName("DynamoDbQueryNoMatchAttribute")] DynamoDbQueryNoMatchAttribute = 10220, + /// + /// DynamoDB TransactionConflictException 예외가 발생 했습니다. + /// + [pbr::OriginalName("DynamoDbTransactionConflictException")] DynamoDbTransactionConflictException = 10221, + /// + /// DynamoDB Expression 오류 입니다. + /// + [pbr::OriginalName("DynamoDbExpressionError")] DynamoDbExpressionError = 10222, + /// + /// DynamoDB PrimaryKey 를 찾을 수 없습니다. + /// + [pbr::OriginalName("DynamoDbPrimaryKeyNotFound")] DynamoDbPrimaryKeyNotFound = 10223, + /// + /// DynamoDB Table Name 오류 입니다. + /// + [pbr::OriginalName("DynamoDbTableNameInvalid")] DynamoDbTableNameInvalid = 10224, + /// + /// DynamoDB 연결 객체가 Null 입니다. + /// + [pbr::OriginalName("DynamoDbConnectorIsNull")] DynamoDbConnectorIsNull = 10225, + /// + /// DynamoDB Table 이름이 중복 되었습니다. + /// + [pbr::OriginalName("DynamoDbTableNameDuplicated")] DynamoDbTableNameDuplicated = 10226, + /// + /// DynamoDB 쿼리 오류 + /// + [pbr::OriginalName("DynamoDbQueryException")] DynamoDbQueryException = 10231, + /// + /// DynamoDbQuery Request를 가지고 있지 않습니다. + /// + [pbr::OriginalName("DynamoDbQueryNoRequested")] DynamoDbQueryNoRequested = 10232, + /// + /// DynamoDbQuery Document를 찾을 수 없습니다. + /// + [pbr::OriginalName("DynamoDbQueryNotFoundDocumentQuery")] DynamoDbQueryNotFoundDocumentQuery = 10233, + /// + /// DynamoDbQuery ExceptionNotifier를 찾을 수 없습니다. + /// + [pbr::OriginalName("DynamoDbQueryExceptionNotifierNotFound")] DynamoDbQueryExceptionNotifierNotFound = 10234, + /// + /// DynamoDB Document 오류 + /// + [pbr::OriginalName("DynamoDbDocumentIsNullInQueryContext")] DynamoDbDocumentIsNullInQueryContext = 10241, + /// + /// DynamoDbDocument내의 정보에 오류가 있습니다. + /// + [pbr::OriginalName("DynamoDbDocumentIsInvalid")] DynamoDbDocumentIsInvalid = 10242, + /// + /// DynamoDbDocumentQueryContext내의 Type 정보 오류 입니다. + /// + [pbr::OriginalName("DynamoDbDocumentQueryContextTypeInvalid")] DynamoDbDocumentQueryContextTypeInvalid = 10243, + /// + /// DynamoDbDocument를 Doc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocumentCopyFailedToDoc")] DynamoDbDocumentCopyFailedToDoc = 10244, + /// + /// DynamoDbDocument Upsert를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocumentUpsertFailed")] DynamoDbDocumentUpsertFailed = 10245, + /// + /// DynamoDB ItemRequest 오류 + /// + [pbr::OriginalName("DynamoDbItemRequestIsInvalid")] DynamoDbItemRequestIsInvalid = 10251, + /// + /// DynamoDbItemRequestQueryContext내의 Type 정보 오류 입니다. + /// + [pbr::OriginalName("DynamoDbItemRequestQueryContextTypeInvalid")] DynamoDbItemRequestQueryContextTypeInvalid = 10252, + /// + /// DynamoDbItemRequest내에 UpdateExpression 정보가 비어 있다. + /// + [pbr::OriginalName("DynamoDbItemRequestUpdateExpressionEmpty")] DynamoDbItemRequestUpdateExpressionEmpty = 10253, + /// + /// DynamoDB Custom Doc 오류 + /// + [pbr::OriginalName("DynamoDbDocPkInvalid")] DynamoDbDocPkInvalid = 10261, + /// + /// DynamoDbDoc의 SK 값이 오류 입니다. + /// + [pbr::OriginalName("DynamoDbDocSkInvalid")] DynamoDbDocSkInvalid = 10262, + /// + /// DynamoDbDoc의 AttribType이 중복 되었습니다. + /// + [pbr::OriginalName("DynamoDbDocAttribTypeDuplicated")] DynamoDbDocAttribTypeDuplicated = 10263, + /// + /// Doc를 DynamoDbDocument에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyFailedToDocument")] DynamoDbDocCopyFailedToDocument = 10264, + /// + /// DynamoDbDocument를 Doc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyFailedFromDocument")] DynamoDbDocCopyFailedFromDocument = 10265, + /// + /// DynamoDbDocType이 일치하지 않습니다. + /// + [pbr::OriginalName("DynamoDbDocTypeNotMatch")] DynamoDbDocTypeNotMatch = 10266, + /// + /// DynamoDbRequest 오류 입니다. + /// + [pbr::OriginalName("DynamoDbRequestInvalid")] DynamoDbRequestInvalid = 10267, + /// + /// DynamoDbDoc AttributeState 플래그를 선택하지 않았습니다. + /// + [pbr::OriginalName("DynamoDbDocAttributeStateNotSet")] DynamoDbDocAttributeStateNotSet = 10268, + /// + /// DynamoDbDoc AttribWrapper 복사를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocAttribWrapperCopyFailed")] DynamoDbDocAttribWrapperCopyFailed = 10269, + /// + /// DynamoDbDoc Attribute 가져오기를 실패 했습니다. + /// + [pbr::OriginalName("DynamoDbDocAttributeGettingFailed")] DynamoDbDocAttributeGettingFailed = 10270, + /// + /// DynamoDbDoc LinkPKSK 값 오류 입니다. + /// + [pbr::OriginalName("DynamoDbDocLinkPkSkInvalid")] DynamoDbDocLinkPkSkInvalid = 10271, + /// + /// DynamoDbDoc AttribWrapper가 Null 입니다. + /// + [pbr::OriginalName("DynamoDbDocAttribWrapperIsNull")] DynamoDbDocAttribWrapperIsNull = 10272, + /// + /// MySql 오류 + /// + [pbr::OriginalName("MySqlConnectionCreateFailed")] MySqlConnectionCreateFailed = 10281, + /// + /// MySqlConnection Open을 실패 했습니다. + /// + [pbr::OriginalName("MySqlConnectionOpenFailed")] MySqlConnectionOpenFailed = 10282, + /// + /// MySql 쿼리 오류 + /// + [pbr::OriginalName("MySqlDbQueryException")] MySqlDbQueryException = 10283, + /// + /// MongoDb 오류 + /// + [pbr::OriginalName("MongoDbInitAndVefifyDbFailed")] MongoDbInitAndVefifyDbFailed = 10291, + /// + ///============================================================================================= + /// Cache 오류 : 10300 ~ + ///============================================================================================= + /// Redis 오류 + /// + [pbr::OriginalName("RedisServerConnectFailed")] RedisServerConnectFailed = 10301, + /// + /// Redis Strings 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisStringsWriteFailed")] RedisStringsWriteFailed = 10302, + /// + /// Redis Strings 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisStringsReadFailed")] RedisStringsReadFailed = 10303, + /// + /// Redis Sets 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSetsWriteFailed")] RedisSetsWriteFailed = 10304, + /// + /// Redis Sets 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSetsReadFailed")] RedisSetsReadFailed = 10305, + /// + /// Redis SortedSets 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSortedSetsWriteFailed")] RedisSortedSetsWriteFailed = 10306, + /// + /// Redis SortedSets 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisSortedSetsReadFailed")] RedisSortedSetsReadFailed = 10307, + /// + /// Redis Hashes 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisHashesWriteFailed")] RedisHashesWriteFailed = 10308, + /// + /// Redis Hashes 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisHashesReadFailed")] RedisHashesReadFailed = 10309, + /// + /// Redis Lists 자료구조 쓰기를 실패 했습니다. + /// + [pbr::OriginalName("RedisListsWriteFailed")] RedisListsWriteFailed = 10310, + /// + /// Redis Lists 자료구조 읽기를 실패 했습니다. + /// + [pbr::OriginalName("RedisListsReadFailed")] RedisListsReadFailed = 10311, + /// + /// Redis Request의 Key 값이 없습니다. + /// + [pbr::OriginalName("RedisRequestKeyIsEmpty")] RedisRequestKeyIsEmpty = 10312, + /// + /// Redis에서 LoginCache 정보 조회를 실패했습니다. + /// + [pbr::OriginalName("RedisLoginCacheGetFailed")] RedisLoginCacheGetFailed = 10313, + /// + /// Redis에 LoginCache 정보를 저장을 실패했습니다. + /// + [pbr::OriginalName("RedisLoginCacheSetFailed")] RedisLoginCacheSetFailed = 10314, + /// + /// RedisPrivateCache가 중복 등록 되었습니다. + /// + [pbr::OriginalName("RedisPrivateCacheDuplicated")] RedisPrivateCacheDuplicated = 10315, + /// + /// RedisGlobalSharedCache가 중복 등록 되었습니다. + /// + [pbr::OriginalName("RedisGlobalSharedCacheDuplicated")] RedisGlobalSharedCacheDuplicated = 10316, + /// + /// RedisLoginCache Owner UserGuid가 일치하지 않습니다. + /// + [pbr::OriginalName("RedisLoginCacheOwnerUserGuidNotMatch")] RedisLoginCacheOwnerUserGuidNotMatch = 10317, + /// + /// Redis PartyCache 읽기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyCacheGetFailed")] RedisGlobalPartyCacheGetFailed = 10318, + /// + /// Redis PartyCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyCacheWriteFailed")] RedisGlobalPartyCacheWriteFailed = 10319, + /// + /// Redis PartyMemberCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyMemberCacheWriteFailed")] RedisGlobalPartyMemberCacheWriteFailed = 10320, + /// + /// Redis PartyServerCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyServerCacheWriteFailed")] RedisGlobalPartyServerCacheWriteFailed = 10321, + /// + /// Redis PartyInvitePartySendCache 쓰기를 실패했습니다. + /// + [pbr::OriginalName("RedisGlobalPartyInvitePartySendCacheWriteFailed")] RedisGlobalPartyInvitePartySendCacheWriteFailed = 10322, + /// + /// Redis에서 InstanceRoomInfoCache 정보 조회를 실패했습니다. + /// + [pbr::OriginalName("RedisInstanceRoomInfoCacheGetFailed")] RedisInstanceRoomInfoCacheGetFailed = 10323, + /// + /// Redis 에서 누적 랭킹 데이터 저정에 실패했습니다. + /// + [pbr::OriginalName("RedisUgcNpcTotalRankCacheWriteFailed")] RedisUgcNpcTotalRankCacheWriteFailed = 10324, + /// + /// RedisRequestHandler를 찾을 수 없습니다. + /// + [pbr::OriginalName("RedisRequestHandlerNotFound")] RedisRequestHandlerNotFound = 10325, + /// + ///============================================================================================= + /// Message Queue 오류 : 10400 ~ + ///============================================================================================= + /// RabbitMQ 오류 + /// + [pbr::OriginalName("RabbitMqConsumerStartFailed")] RabbitMqConsumerStartFailed = 10401, + /// + /// RabbitMQ 연결을 실패 했습니다. + /// + [pbr::OriginalName("RabbitMqConnectFailed")] RabbitMqConnectFailed = 10402, + /// + /// RabbitMQ 메시지 시간이 오래 됐습니다. + /// + [pbr::OriginalName("RabbitMessageTimeOld")] RabbitMessageTimeOld = 10403, + /// + /// RabbitMQ 채널 생성을 실패 했습니다. + /// + [pbr::OriginalName("RabbitMqChannelCreateFailed")] RabbitMqChannelCreateFailed = 10404, + /// + ///============================================================================================= + /// S3 오류 : 10500 ~ + ///============================================================================================= + /// S3 오류 + /// + [pbr::OriginalName("S3ClientCreateFailed")] S3ClientCreateFailed = 10501, + /// + /// S3 Bucket 생성을 실패 했습니다. + /// + [pbr::OriginalName("S3BucketCreateFailed")] S3BucketCreateFailed = 10502, + /// + /// S3 File Upload를 실패 했습니다. + /// + [pbr::OriginalName("S3FileUploadFailed")] S3FileUploadFailed = 10503, + /// + /// S3 File Delete에 실패 했습니다. + /// + [pbr::OriginalName("S3FileDeleteFailed")] S3FileDeleteFailed = 10504, + /// + /// S3 File Get에 실패 했습니다. + /// + [pbr::OriginalName("S3FileGetFailed")] S3FileGetFailed = 10505, + /// + ///============================================================================================= + /// Meta 스키마 및 데이터 기반 오류 : 10550 ~ + ///============================================================================================= + /// 스키마 오류 + /// 데이터 오류 + /// + [pbr::OriginalName("MetaDataLoadFailed")] MetaDataLoadFailed = 10551, + /// + /// Meta 데이터 오류 입니다. + /// + [pbr::OriginalName("InvalidMetaData")] InvalidMetaData = 10552, + /// + /// Meta Id 오류 입니다. + /// + [pbr::OriginalName("MetaIdInvalid")] MetaIdInvalid = 10553, + /// + ///============================================================================================= + /// 기타 라이브러리 오류 : 10610 ~ + ///============================================================================================= + /// Json 오류 + /// + [pbr::OriginalName("JsonTypeInvalid")] JsonTypeInvalid = 10611, + /// + /// JsonConvert Deserialize 오류 입니다. + /// + [pbr::OriginalName("JsonConvertDeserializeFailed")] JsonConvertDeserializeFailed = 10612, + /// + ///============================================================================================= + /// 서버 공통 오류 : 10700 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ServerConfigFileNotFound")] ServerConfigFileNotFound = 10701, + /// + /// 서버 타입 오류 입니다. + /// + [pbr::OriginalName("ServerTypeInvalid")] ServerTypeInvalid = 10702, + /// + /// 해당 리슨포트로 이미 실행중인 프로세스가 있습니다. + /// + [pbr::OriginalName("AlreadyRunningServerWithListenPort")] AlreadyRunningServerWithListenPort = 10703, + /// + /// 캐시 스토리지를 찾을 수 없습니다. + /// + [pbr::OriginalName("NotFoundCacheStorage")] NotFoundCacheStorage = 10704, + /// + /// 함수 파라메터중에 Null 값이 있어서 오류 입니다. + /// + [pbr::OriginalName("FunctionParamNull")] FunctionParamNull = 10705, + /// + /// 함수 파라메터 오류 입니다. + /// + [pbr::OriginalName("FunctionInvalidParam")] FunctionInvalidParam = 10706, + /// + /// 클라이언트 리슨 포트 오류 입니다. + /// + [pbr::OriginalName("ClientListenPortInvalid")] ClientListenPortInvalid = 10707, + /// + /// Interface 를 Override 하지 않았습니다. + /// + [pbr::OriginalName("NotOverrideInterface")] NotOverrideInterface = 10708, + /// + /// 서버 실행후 대기를 실패 했습니다. + /// + [pbr::OriginalName("ServerOnRunningFailed")] ServerOnRunningFailed = 10709, + /// + /// 서비스 타입 오류 입니다. + /// + [pbr::OriginalName("ServiceTypeInvalid")] ServiceTypeInvalid = 10710, + /// + /// 함수를 구현하지 않았습니다. + /// + [pbr::OriginalName("FunctionNotImplemented")] FunctionNotImplemented = 10711, + /// + /// Interface를 상속받아 구현하지 않았습니다. + /// + [pbr::OriginalName("ClassDoesNotImplementInterfaceInheritance")] ClassDoesNotImplementInterfaceInheritance = 10712, + /// + /// 정책 타입이 중복 되었습니다. + /// + [pbr::OriginalName("RuleTypeDuplicated")] RuleTypeDuplicated = 10713, + /// + /// ClassType 형변환은 null 입니다. + /// + [pbr::OriginalName("ClassTypeCastIsNull")] ClassTypeCastIsNull = 10714, + /// + /// 이미 등록된 PeriodicTask 입니다. + /// + [pbr::OriginalName("PeriodicTaskAlreadyRegistered")] PeriodicTaskAlreadyRegistered = 10715, + /// + /// 이미 등록된 EntityTicker 입니다. + /// + [pbr::OriginalName("EntityTickerAlreadyRegistered")] EntityTickerAlreadyRegistered = 10716, + /// + /// EntityTicker를 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityTickerNotFound")] EntityTickerNotFound = 10717, + /// + /// EntityBase를 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityBaseNotFound")] EntityBaseNotFound = 10718, + /// + /// 부합하는 서버가 없습니다. + /// + [pbr::OriginalName("ValidServerNotFound")] ValidServerNotFound = 10719, + /// + /// 해당 서버에 유저가 가득찼습니다. + /// + [pbr::OriginalName("TargetServerUserCountExceed")] TargetServerUserCountExceed = 10720, + /// + /// 해당 유저를 찾을 수 없습니다. + /// + [pbr::OriginalName("TargetUserNotFound")] TargetUserNotFound = 10721, + /// + /// 대상이 접속중이지 않습니다. + /// + [pbr::OriginalName("TargetUserNotLogIn")] TargetUserNotLogIn = 10722, + /// + /// 맵에서 찾을 수 없습니다. + /// + [pbr::OriginalName("NotExistMap")] NotExistMap = 10723, + /// + /// 조건에 맞지 않아 입장 예약을 실패했습니다. + /// + [pbr::OriginalName("FailedToReserveEnterCondition")] FailedToReserveEnterCondition = 10724, + /// + /// 입장 예약에 실패했습니다. + /// + [pbr::OriginalName("FailedToReservationEnter")] FailedToReservationEnter = 10725, + /// + /// OwnerEntityType 오류 입니다. + /// + [pbr::OriginalName("OwnerEntityTypeInvalid")] OwnerEntityTypeInvalid = 10726, + /// + /// OwnerEntity 정보를 채울 수 없습니다. + /// + [pbr::OriginalName("OwnerEntityCannotFillup")] OwnerEntityCannotFillup = 10727, + /// + /// Owner Guid 오류 입니다. + /// + [pbr::OriginalName("OwnerGuidInvalid")] OwnerGuidInvalid = 10728, + /// + /// DailyTimeEvent 등록 오류입니다. + /// + [pbr::OriginalName("DailyTimeEventAdditionFailed")] DailyTimeEventAdditionFailed = 10729, + /// + /// 프로그램 VersionPath 토큰을 찾을 수 없습니다. + /// + [pbr::OriginalName("ProgramVersionPathTokenNotFound")] ProgramVersionPathTokenNotFound = 10730, + /// + /// 현재 처리중 입니다. + /// + [pbr::OriginalName("CurrentlyProcessingState")] CurrentlyProcessingState = 10731, + /// + /// ServerUrlType 오류 입니다. + /// + [pbr::OriginalName("ServerUrlTypeInvalid")] ServerUrlTypeInvalid = 10732, + /// + /// ServerUrlType 이미 등록되어 있습니다. + /// + [pbr::OriginalName("ServerUrlTypeAlreadyRegistered")] ServerUrlTypeAlreadyRegistered = 10733, + /// + /// 서버 Offline 모드가 활성화 상태 입니다. + /// + [pbr::OriginalName("ServerOfflineModeEnable")] ServerOfflineModeEnable = 10734, + /// + /// Owner 정보 오류 입니다. + /// + [pbr::OriginalName("OwnerInvalid")] OwnerInvalid = 10735, + /// + /// 이미 등록된 Module 입니다. + /// + [pbr::OriginalName("ModuleAlreadyRegistred")] ModuleAlreadyRegistred = 10736, + /// + /// 이미 추가된 초기화용 Module 입니다. + /// + [pbr::OriginalName("ModuleAlreadyAddInitializeModule")] ModuleAlreadyAddInitializeModule = 10737, + /// + /// Moudle을 찾을 수 없습니다. + /// + [pbr::OriginalName("ModuleNotFound")] ModuleNotFound = 10738, + /// + /// 프로그램 Version 읽기를 실패 했습니다. + /// + [pbr::OriginalName("ProgramVersionLoadFailed")] ProgramVersionLoadFailed = 10739, + /// + /// 서버가 이미 실행중 입니다. + /// + [pbr::OriginalName("ServerAlreadyRunning")] ServerAlreadyRunning = 10740, + /// + ///============================================================================================= + /// 데이터 변환 및 복사 오류 : 10850 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MetaDataCopyToDynamoDbDocFailed")] MetaDataCopyToDynamoDbDocFailed = 10850, + /// + /// Meta 데이터를 EntityAttribute에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("MetaDataCopyToEntityAttributeFailed")] MetaDataCopyToEntityAttributeFailed = 10851, + /// + /// DynamoDbDoc를 Cache에 복사하는 것을 실패 헀습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyToCacheFailed")] DynamoDbDocCopyToCacheFailed = 10852, + /// + /// DynamoDbDoc를 EntityAttribute에 복사하는 것을 실패 헀습니다. + /// + [pbr::OriginalName("DynamoDbDocCopyToEntityAttributeFailed")] DynamoDbDocCopyToEntityAttributeFailed = 10853, + /// + /// Cache를 EntityAttrib에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("CacheCopyToEntityAttributeFailed")] CacheCopyToEntityAttributeFailed = 10854, + /// + /// Cache를 DynamoDbDoc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("CacheCopyToDynamoDbDocFailed")] CacheCopyToDynamoDbDocFailed = 10855, + /// + /// EntityAttribute를 Cache에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCopyToCacheFailed")] EntityAttributeCopyToCacheFailed = 10856, + /// + /// EntityAttribute를 DynamoDbDoc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCopyToDynamoDbDocFailed")] EntityAttributeCopyToDynamoDbDocFailed = 10857, + /// + /// EntityAttribute를 EntityAttributeTransactor에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCopyToEntityAttributeTransactorFailed")] EntityAttributeCopyToEntityAttributeTransactorFailed = 10858, + /// + /// EntityAttributeTransactor를 EntityAttribute에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeTransactorCopyToEntityAttributeFailed")] EntityAttributeTransactorCopyToEntityAttributeFailed = 10859, + /// + /// EntityAttributeTransactor를 DynamoDbDoc에 복사하는 것을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeTransactorCopyToDynamoDbDocFailed")] EntityAttributeTransactorCopyToDynamoDbDocFailed = 10860, + /// + /// Attrib를 찾을 수 없습니다. + /// + [pbr::OriginalName("AttribNotFound")] AttribNotFound = 10861, + /// + /// Attrib Path 구성을 실패 했습니다. + /// + [pbr::OriginalName("AttribPathMakeFailed")] AttribPathMakeFailed = 10862, + /// + /// EntityAttribute Casting을 실패 했습니다. + /// + [pbr::OriginalName("EntityAttributeCastFailed")] EntityAttributeCastFailed = 10863, + /// + /// 문자열을 Enum으로 변환하는 것을 실패 했습니다. + /// + [pbr::OriginalName("StringConvertToEnumFailed")] StringConvertToEnumFailed = 10864, + /// + ///============================================================================================= + /// 프로그램 버전 오류 : 10900 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MetaSchemaVersionNotMatch")] MetaSchemaVersionNotMatch = 10901, + /// + /// 메타 데이터 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("MetaDataVersionNotMatch")] MetaDataVersionNotMatch = 10902, + /// + /// 패킷 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("PacketVersionNotMatch")] PacketVersionNotMatch = 10903, + /// + /// 클라이언트 로직 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("ClientLogicVersionNotMatch")] ClientLogicVersionNotMatch = 10904, + /// + /// 리소스 버전이 일치하지 않습니다. + /// + [pbr::OriginalName("ResourceVersionNotMatch")] ResourceVersionNotMatch = 10905, + /// + /// ClientProgramVersion 정보 null 입니다. + /// + [pbr::OriginalName("ClientProgramVersionIsNull")] ClientProgramVersionIsNull = 10906, + /// + ///============================================================================================= + /// 계정 인증 오류 : 11000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TestIdNotAllow")] TestIdNotAllow = 11001, + /// + /// Bot 계정은 허용되지 않습니다. + /// + [pbr::OriginalName("BotdNotAllow")] BotdNotAllow = 11002, + /// + /// 계정 id 길이가 짧습니다. + /// + [pbr::OriginalName("AccountIdLengthShort")] AccountIdLengthShort = 11003, + /// + /// 통합인증Db에서 Id를 찾을 수 없습니다. + /// + [pbr::OriginalName("AccountIdNotFoundInSsoAccountDb")] AccountIdNotFoundInSsoAccountDb = 11004, + /// + /// 테스트 계정으로 Meta 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("MetaDataNotFoundByTestUserId")] MetaDataNotFoundByTestUserId = 11005, + /// + /// 계정 비밀번호가 일치하지 않습니다. + /// + [pbr::OriginalName("AccountPasswordNotMatch")] AccountPasswordNotMatch = 11006, + /// + /// UserData 정보를 AccountAttr 정보로 변환을 실패 했습니다. + /// + [pbr::OriginalName("UserDataConvertToAccountAttrFailed")] UserDataConvertToAccountAttrFailed = 11007, + /// + /// AccountBaseAttrib 정보를 DB에 추가를 실패 했습니다. + /// + [pbr::OriginalName("AccountBaseAttribInsertDbFailed")] AccountBaseAttribInsertDbFailed = 11008, + /// + /// 접속 가능한 서버가 없습니다. + /// + [pbr::OriginalName("NoServerConnectable")] NoServerConnectable = 11009, + /// + /// 접속 제재 처리된 계정입니다. + /// + [pbr::OriginalName("BlockedAccount")] BlockedAccount = 11010, + /// + /// 통합계정인증과 런처 로그인을 허용하지 않습니다. + /// + [pbr::OriginalName("SsoAccountAuthWithLauncherLoginNotAllow")] SsoAccountAuthWithLauncherLoginNotAllow = 11011, + /// + /// 클라이언트 단독 로그인을 허용하지 않습니다. + /// + [pbr::OriginalName("ClientStandaloneLoginNotAllow")] ClientStandaloneLoginNotAllow = 11012, + /// + /// 접속 허용이 되지 않는 PlatformType 입니다. + /// + [pbr::OriginalName("PlatformTypeNotAllow")] PlatformTypeNotAllow = 11013, + /// + /// 통합계정DB에서 계정 정보를 읽지 못했습니다. + /// + [pbr::OriginalName("AccountCanNotReadFromSsoAccountDb")] AccountCanNotReadFromSsoAccountDb = 11014, + /// + /// 통합계정인증 JWT 체크를 실패 했습니다. + /// + [pbr::OriginalName("SsoAccountAuthJwtCheckFailed")] SsoAccountAuthJwtCheckFailed = 11015, + /// + /// 통합계정인증 JWT 안에 UserId Key 정보가 없습니다. + /// + [pbr::OriginalName("UserIdKeyNotFoundInSsoAccountAuthJwt")] UserIdKeyNotFoundInSsoAccountAuthJwt = 11016, + /// + /// 통합계정인증 JWT 안에 UserId Value 정보가 없습니다. + /// + [pbr::OriginalName("UserIdValueEmptyInSsoAccountAuthJwt")] UserIdValueEmptyInSsoAccountAuthJwt = 11017, + /// + /// 통합계정인증 JWT 안에 AccountType Key 정보가 없습니다. + /// + [pbr::OriginalName("AccountTypeKeyNotFoundInSsoAccountAuthJwt")] AccountTypeKeyNotFoundInSsoAccountAuthJwt = 11018, + /// + /// 통합계정인증 JWT 안에 AccountType Value 정보가 허용된 AccountType이 아닙니다. + /// + [pbr::OriginalName("AccountTypeValueNotAllowInSsoAccountAuthJwt")] AccountTypeValueNotAllowInSsoAccountAuthJwt = 11019, + /// + /// 메타버스Db에서 AccountBaseDoc를 찾을 수 없습니다. + /// + [pbr::OriginalName("AccountBaseDocNotFoundInMetaverseDb")] AccountBaseDocNotFoundInMetaverseDb = 11020, + /// + /// 통합계정인증 JWT 안에 AssessToken Key 정보가 없습니다. + /// + [pbr::OriginalName("AccessTokenKeyNotAllowInSsoAccountAuthJwt")] AccessTokenKeyNotAllowInSsoAccountAuthJwt = 11021, + /// + /// 통합인증Db의 AccessToken과 일치하지 않습니다. + /// + [pbr::OriginalName("AccessTokenNotMatchInSsoAccountDb")] AccessTokenNotMatchInSsoAccountDb = 11022, + /// + /// 현재의 AccountType은 허용되지 않습니다. + /// + [pbr::OriginalName("AccountTypeNotAllow")] AccountTypeNotAllow = 11023, + /// + /// AccountBaseDoc가 로드되지 않았습니다. + /// + [pbr::OriginalName("AccountBaseDocNotLoad")] AccountBaseDocNotLoad = 11024, + /// + /// 권한이 부족합니다. + /// + [pbr::OriginalName("NotEnoughAuthority")] NotEnoughAuthority = 11054, + /// + /// AccountBaseDoc이 null 입니다. + /// + [pbr::OriginalName("AccountBaseDocIsNull")] AccountBaseDocIsNull = 11055, + /// + /// Account Id 오류 입니다. + /// + [pbr::OriginalName("AccountIdInvalid")] AccountIdInvalid = 11056, + /// + /// Account에 UserGuid가 없습니다. + /// + [pbr::OriginalName("AccountWithoutUserGuid")] AccountWithoutUserGuid = 11057, + /// + /// 통합계정인증 JWT 이 기간만료 입니다. + /// + [pbr::OriginalName("SsoAccountAuthJwtTokenExpired")] SsoAccountAuthJwtTokenExpired = 11058, + /// + /// 통합계정인증 JWT 예외가 발생 했습니다. + /// + [pbr::OriginalName("SsoAccountAuthJwtException")] SsoAccountAuthJwtException = 11059, + /// + ///============================================================================================= + /// 엔티티 트랜잭션 관련 오류 : 11200 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TransactionRunnerAlreadyRunning")] TransactionRunnerAlreadyRunning = 11201, + /// + /// TransactionRunner를 찾을 수 없습니다. + /// + [pbr::OriginalName("TransactionRunnerNotFound")] TransactionRunnerNotFound = 11202, + /// + ///============================================================================================= + /// 엔티티 속성 관련 오류 : 11300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EntityGuidInvalid")] EntityGuidInvalid = 11301, + /// + /// EntityAttrib 중복 등록 되었습니다. + /// + [pbr::OriginalName("EntityAttribDuplicated")] EntityAttribDuplicated = 11302, + /// + /// EntityAttribute가 null 입니다. + /// + [pbr::OriginalName("EntityAttributeIsNull")] EntityAttributeIsNull = 11303, + /// + /// EntityAttribute 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityAttributeNotFound")] EntityAttributeNotFound = 11304, + /// + /// EntityAttribute 상태 오류 입니다. + /// + [pbr::OriginalName("EntityAttributeStateInvalid")] EntityAttributeStateInvalid = 11305, + /// + /// EntityType 오류 입니다. + /// + [pbr::OriginalName("EntityTypeInvalid")] EntityTypeInvalid = 11306, + /// + /// Entity가 Map에 연결되어 있습니다. + /// + [pbr::OriginalName("EntityLinkedToMap")] EntityLinkedToMap = 11307, + /// + /// Entity가 Map에 연결되어 있지 않습니다. + /// + [pbr::OriginalName("EntityNotLinkedToMap")] EntityNotLinkedToMap = 11308, + /// + /// 현재 dence 상태가 아닙니다. dance end 패킷이 날라올때 처리 + /// + [pbr::OriginalName("EntityStateNotDancing")] EntityStateNotDancing = 11309, + /// + ///============================================================================================= + /// 엔티티 얙션 관련 오류 : 11400 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EntityActionDuplicated")] EntityActionDuplicated = 11401, + /// + /// EntityAction을 찾을 수 없습니다. + /// + [pbr::OriginalName("EntityActionNotFound")] EntityActionNotFound = 11402, + /// + ///============================================================================================= + /// 엔티티 상태 관련 오류 : 11500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EntityBaseHfsmInitFailed")] EntityBaseHfsmInitFailed = 11501, + /// + ///============================================================================================= + /// 글로벌 엔티티 오류 : 11600 ~ + ///============================================================================================= + /// + [pbr::OriginalName("RedisGlobalEntityDuplicated")] RedisGlobalEntityDuplicated = 11601, + /// + ///============================================================================================= + /// 접속 서버 변경 관련 오류 : 11700 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UserIsSwitchingServer")] UserIsSwitchingServer = 11701, + /// + /// 유저가 다른 서버로 접속을 변경하고 있지 않습니다. + /// + [pbr::OriginalName("UserIsNotSwitchingServer")] UserIsNotSwitchingServer = 11702, + /// + /// 접속 서버 변경 Otp 값이 일치하지 않습니다. + /// + [pbr::OriginalName("ServerSwitchingOtpNotMatch")] ServerSwitchingOtpNotMatch = 11703, + /// + /// 접속된 서버는 목적지 서버가 아닙니다. + /// + [pbr::OriginalName("ConnectedServerIsNotDestServer")] ConnectedServerIsNotDestServer = 11704, + /// + /// 예약된 유저가 아닙니다. + /// + [pbr::OriginalName("InvalidReservationUser")] InvalidReservationUser = 11705, + /// + /// 복귀 유저가 아닙니다. + /// + [pbr::OriginalName("InvalidReturnUser")] InvalidReturnUser = 11706, + /// + ///============================================================================================= + /// 유저 관련 오류 : 12000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UserNicknameNotAllowWithSpecialChars")] UserNicknameNotAllowWithSpecialChars = 12001, + /// + /// 유저 닉네임은 한글로 최소2 에서 최대8 글자까지 허용 합니다. + /// + [pbr::OriginalName("UserNicknameAllowedMin2ToMax8WithKorean")] UserNicknameAllowedMin2ToMax8WithKorean = 12002, + /// + /// 유저 닉네임은 영어로 최소4 에서 최대16 글자까지 허용 합니다. + /// + [pbr::OriginalName("UserNicknameAllowedMin4ToMax16WithEnglish")] UserNicknameAllowedMin4ToMax16WithEnglish = 12003, + /// + /// 유저 닉네임은 첫번째 글자에 숫자를 허용하지 않습니다. + /// + [pbr::OriginalName("UserNicknameNotAllowedNumberAtFirstChars")] UserNicknameNotAllowedNumberAtFirstChars = 12004, + /// + /// 유저 닉네임으로 허용되지 않는 문자 입니다. + /// + [pbr::OriginalName("UserNicknameNotAllowChars")] UserNicknameNotAllowChars = 12005, + /// + /// 유저 닉네임으로 한글 초성체를 허용하지 않습니다. + /// + [pbr::OriginalName("UserNicknameNotAllowWithInitialismKorean")] UserNicknameNotAllowWithInitialismKorean = 12006, + /// + /// 유저 닉네임이 금지어에 해당 합니다. + /// + [pbr::OriginalName("UserNicknameBan")] UserNicknameBan = 12007, + /// + /// 유저 중복 로그인 입니다. + /// + [pbr::OriginalName("UserDuplicatedLogin")] UserDuplicatedLogin = 12008, + /// + /// 유저가 로그인되어 있지 않습니다. + /// + [pbr::OriginalName("UserNotLogin")] UserNotLogin = 12009, + /// + /// 유저 생성을 위한 DynamoDbDoc가 중복 등록 되었습니다. + /// + [pbr::OriginalName("UserCreationForDynamoDbDocDuplicated")] UserCreationForDynamoDbDocDuplicated = 12010, + /// + /// UserGuid를 참조하는 모든 Attribute에 Guid를 적용하는 것을 실패했습니다. + /// + [pbr::OriginalName("UserGuidApplyToRefAttributeAllFailed")] UserGuidApplyToRefAttributeAllFailed = 12011, + /// + /// TestUserPrepareCreate 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("TestUserPrepareCreateNotCompleted")] TestUserPrepareCreateNotCompleted = 12012, + /// + /// DefaultUserPrepareCreate 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("DefaultUserPrepareCreateNotCompleted")] DefaultUserPrepareCreateNotCompleted = 12013, + /// + /// UserPrepareLoad 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("UserPrepareLoadNotCompleted")] UserPrepareLoadNotCompleted = 12014, + /// + /// 유저 닉네임이 생성되어 있지 않습니다. + /// + [pbr::OriginalName("UserNicknameNotCreated")] UserNicknameNotCreated = 12015, + /// + /// 유저 닉네임을 이미 생성 했습니다. + /// + [pbr::OriginalName("UserNicknameAlreadyCreated")] UserNicknameAlreadyCreated = 12016, + /// + /// 유저 생성 절차가 완료되지 않았습니다. + /// + [pbr::OriginalName("UserCreateStepNotCompleted")] UserCreateStepNotCompleted = 12017, + /// + /// 유저 생성이 완료 되었습니다. + /// + [pbr::OriginalName("UserCreateCompleted")] UserCreateCompleted = 12018, + /// + /// 유저 서브키 바인딩을 실패 했습니다. + /// + [pbr::OriginalName("UserSubKeyBindToFailed")] UserSubKeyBindToFailed = 12019, + /// + /// 유저 서브키 변경을 실패 했습니다. + /// + [pbr::OriginalName("UserSubKeyReplaceFailed")] UserSubKeyReplaceFailed = 12020, + /// + /// 유저 Guid 오류 입니다. + /// + [pbr::OriginalName("UserGuidInvalid")] UserGuidInvalid = 12021, + /// + /// UserNicknameDoc이 null 입니다. + /// + [pbr::OriginalName("UserNicknameDocIsNull")] UserNicknameDocIsNull = 12022, + /// + /// UserDoc이 null 입니다. + /// + [pbr::OriginalName("UserDocIsNull")] UserDocIsNull = 12023, + /// + /// UserGuid가 이미 등록되어 있습니다. + /// + [pbr::OriginalName("UserGuidAlreadyAdded")] UserGuidAlreadyAdded = 12024, + /// + /// User 닉네임이 중복 되었습니다. + /// + [pbr::OriginalName("UserNicknameDuplicated")] UserNicknameDuplicated = 12025, + /// + /// 유저 닉네임은 최소2 에서 최대12 글자까지 허용 합니다. + /// + [pbr::OriginalName("UserNicknameAllowedMin2ToMax12")] UserNicknameAllowedMin2ToMax12 = 12026, + /// + /// UserNickname 검색 페이지가 잘못되었습니다. + /// + [pbr::OriginalName("UserNicknameSearchPageWrong")] UserNicknameSearchPageWrong = 12027, + /// + /// 유저 닉네임이 없습니다. + /// + [pbr::OriginalName("UserNicknameEmpty")] UserNicknameEmpty = 12028, + /// + /// UserContentsSettingDoc이 null 입니다. + /// + [pbr::OriginalName("UserContentsSettingDocIsNull")] UserContentsSettingDocIsNull = 12029, + /// + /// 유저 MoneyDoc이 없습니다. + /// + [pbr::OriginalName("UserMoneyDocEmpty")] UserMoneyDocEmpty = 12030, + /// + ///============================================================================================= + /// 유저 신고하기 관련 오류 : 12100 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UserReportInvalidTitleLength")] UserReportInvalidTitleLength = 12101, + /// + /// 유저 신고하기 의 내용 길이 오류입니다. + /// + [pbr::OriginalName("UserReportInvalidContentLength")] UserReportInvalidContentLength = 12102, + /// + /// UserReport에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("UserReportDocException")] UserReportDocException = 12103, + /// + ///============================================================================================= + /// 캐릭터 관련 오류 : 13000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TestCharacterPrepareCreateNotCompleted")] TestCharacterPrepareCreateNotCompleted = 13001, + /// + /// DefaultCharacterPrepareCreate 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("DefaultCharacterPrepareCreateNotCompleted")] DefaultCharacterPrepareCreateNotCompleted = 13012, + /// + /// CharacterPrepareLoad 단계가 완료되지 않았습니다. + /// + [pbr::OriginalName("CharacterPrepareLoadNotCompleted")] CharacterPrepareLoadNotCompleted = 13013, + /// + /// CharacterBaseDoc 로딩중에 중복된 캐릭터가 발견되었습니다. + /// + [pbr::OriginalName("CharacterBaseDocLoadDuplicatedCharacter")] CharacterBaseDocLoadDuplicatedCharacter = 13014, + /// + /// 선택된 캐릭터가 없습니다. + /// + [pbr::OriginalName("CharacterNotSelected")] CharacterNotSelected = 13015, + /// + /// 캐릭터를 찾지 못했습니다. + /// + [pbr::OriginalName("CharacterNotFound")] CharacterNotFound = 13016, + /// + /// CharacterBaseDoc 읽지 않았습니다. + /// + [pbr::OriginalName("CharacterBaseDocNoRead")] CharacterBaseDocNoRead = 13017, + /// + /// 캐릭터 커스터마이징이 완료가 되지 않았습니다. + /// + [pbr::OriginalName("CharacterCustomizingNotCompleted")] CharacterCustomizingNotCompleted = 13018, + /// + /// 캐릭터 생성 절차가 완료되지 않았습니다. + /// + [pbr::OriginalName("CharacterCreateStepNotCompleted")] CharacterCreateStepNotCompleted = 13019, + /// + /// 캐릭터 커스터마이징이 이미 완료 되었습니다. + /// + [pbr::OriginalName("CharacterCustomizingAlreadyCompleted")] CharacterCustomizingAlreadyCompleted = 13020, + /// + /// 캐릭터 준비 단계에서 캐릭터 생성이 되지 않습니다. + /// + [pbr::OriginalName("CharacterPrepareNotCreated")] CharacterPrepareNotCreated = 13021, + /// + /// 캐릭터 생성이 완료 되었습니다. + /// + [pbr::OriginalName("CharacterCreateCompleted")] CharacterCreateCompleted = 13022, + /// + /// CharacterBaseDoc이 null 입니다. + /// + [pbr::OriginalName("CharacterBaseDocIsNull")] CharacterBaseDocIsNull = 13023, + /// + ///============================================================================================= + /// 능력치 관련 오류 : 13300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("AbilityNotEnough")] AbilityNotEnough = 13301, + /// + ///============================================================================================= + /// 아이템 관련 오류 : 14000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ItemMetaDataNotFound")] ItemMetaDataNotFound = 14001, + /// + /// 아이템 Guid 값 오류 입니다. + /// + [pbr::OriginalName("ItemGuidInvalid")] ItemGuidInvalid = 14002, + /// + /// ItemDoc 객체가 null 입니다. + /// + [pbr::OriginalName("ItemDocIsNull")] ItemDocIsNull = 14003, + /// + /// 아이템 DefaultAttribute를 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemDefaultAttributeNotFoundInMeta")] ItemDefaultAttributeNotFoundInMeta = 14004, + /// + /// 아이템 Level Enchant를 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemLevelEnchantNotFoundInMeta")] ItemLevelEnchantNotFoundInMeta = 14005, + /// + /// 아이템 Enchant를 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemEnchantNotFoundInMeta")] ItemEnchantNotFoundInMeta = 14006, + /// + /// 아이템 AttributeRandomGroup을 관련 메타 데이터에서 찾지 못했습니다. + /// + [pbr::OriginalName("ItemAttributeRandomGroupNotFoundInMeta")] ItemAttributeRandomGroupNotFoundInMeta = 14007, + /// + /// 아이템 AttributeRandomGroup의 TotalWeight 오류 입니다. + /// + [pbr::OriginalName("ItemAttributeRandomGroupTotalWeightInvalid")] ItemAttributeRandomGroupTotalWeightInvalid = 14008, + /// + /// 아이템을 찾지 못했습니다. + /// + [pbr::OriginalName("ItemNotFound")] ItemNotFound = 14009, + /// + /// 의상 아이템 LargeType 오류 입니다. + /// + [pbr::OriginalName("ItemClothInvalidLargeType")] ItemClothInvalidLargeType = 14010, + /// + /// 의상 아이템 SmallType 오류 입니다. + /// + [pbr::OriginalName("ItemClothInvalidSmallType")] ItemClothInvalidSmallType = 14011, + /// + /// 아이템 스택 개수 오류 입니다. + /// + [pbr::OriginalName("ItemStackCountInvalid")] ItemStackCountInvalid = 14012, + /// + /// 아이템 최대 보유 갯수를 초과 했습니다. + /// + [pbr::OriginalName("ItemMaxCountExceed")] ItemMaxCountExceed = 14013, + /// + /// ItemDoc 로딩중에 중복된 아이템이 발견되었습니다. + /// + [pbr::OriginalName("ItemDocLoadDuplicatedItem")] ItemDocLoadDuplicatedItem = 14014, + /// + /// ClothSlotType 오류 입니다. + /// + [pbr::OriginalName("ClothSlotTypeInvalid")] ClothSlotTypeInvalid = 14015, + /// + /// 아이템 스택 개수가 부족 합니다. + /// + [pbr::OriginalName("ItemStackCountNotEnough")] ItemStackCountNotEnough = 14016, + /// + /// 아이템 보유 개수가 부족 합니다. + /// + [pbr::OriginalName("ItemCountNotEnough")] ItemCountNotEnough = 14017, + /// + /// ItemType(LargeType, SmallType) 오류입니다. + /// + [pbr::OriginalName("ItemInvalidItemType")] ItemInvalidItemType = 14018, + /// + /// 아이템 Tool 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("ItemToolMetaDataNotFound")] ItemToolMetaDataNotFound = 14019, + /// + /// 아이템 Tool을 찾지 못했습니다. + /// + [pbr::OriginalName("ItemToolNotFound")] ItemToolNotFound = 14020, + /// + /// 아이템 Tool이 활성화 상태가 아닙니다. + /// + [pbr::OriginalName("ItemToolNotActivateState")] ItemToolNotActivateState = 14021, + /// + /// ToolActionDoc이 null 입니다. + /// + [pbr::OriginalName("ToolActionDocIsNull")] ToolActionDocIsNull = 14022, + /// + /// ToolAction이 이미 비활성화 상태 입니다. + /// + [pbr::OriginalName("ToolActionAlreadyUnactivateState")] ToolActionAlreadyUnactivateState = 14023, + /// + /// ToolAction이 이미 활성화 상태 입니다. + /// + [pbr::OriginalName("ToolActionAlreadyActivateState")] ToolActionAlreadyActivateState = 14024, + /// + /// 아이템 Tattoo가 없습니다. + /// + [pbr::OriginalName("ItemTattooNotFound")] ItemTattooNotFound = 14025, + /// + /// 아이템 Attribute Enchant 메타가 없습니다. + /// + [pbr::OriginalName("ItemAttributeEnchantMetaNotFound")] ItemAttributeEnchantMetaNotFound = 14026, + /// + /// 아이템 Attribute Change가 선택되지 않았습니다. + /// + [pbr::OriginalName("ItemAttributeChangeNotSelected")] ItemAttributeChangeNotSelected = 14027, + /// + /// 아이템 문자열을 정수로 변환하는데 오류가 발생했습니다. + /// + [pbr::OriginalName("ItemParsingFromStringToIntErorr")] ItemParsingFromStringToIntErorr = 14028, + /// + /// 아이템 개수 문자열을 정수로 변환하는데 오류가 발생했습니다. + /// + [pbr::OriginalName("ItemValueParsingFromStringToIntErorr")] ItemValueParsingFromStringToIntErorr = 14029, + /// + /// ItemFirstPurchaseHistoryDoc이 null 입니다. + /// + [pbr::OriginalName("ItemFirstPurchaseHistoryDocIsNull")] ItemFirstPurchaseHistoryDocIsNull = 14030, + /// + /// ItemFirstPurchaseHistoryDoc 로딩중에 중복된 아이템이 발견되었습니다. + /// + [pbr::OriginalName("ItemFirstPurchaseHistoryDocLoadDuplicatedItem")] ItemFirstPurchaseHistoryDocLoadDuplicatedItem = 14031, + /// + /// ItemFirstPurchaseHistory가 이미 존재합니다. + /// + [pbr::OriginalName("ItemFirstPurchaseHistoryAlreadyExist")] ItemFirstPurchaseHistoryAlreadyExist = 14032, + /// + /// 아이템 첫 구매 할인 아이템 개수가 잘못되었습니다. + /// + [pbr::OriginalName("ItemFirstPurchaseDiscountItemCountWrong")] ItemFirstPurchaseDiscountItemCountWrong = 14033, + /// + /// 아이템 AttributeIdType 오류 입니다. + /// + [pbr::OriginalName("ItemAttributeIdTypeInvalid")] ItemAttributeIdTypeInvalid = 14034, + /// + /// 아이템 할당 오류 입니다. + /// + [pbr::OriginalName("ItemAllocFailed")] ItemAllocFailed = 14035, + /// + /// 아이템 Guid 중복 오류 입니다. + /// + [pbr::OriginalName("ItemGuidDuplicated")] ItemGuidDuplicated = 14036, + /// + /// 아이템 레벨이 현재 최대 입니다. + /// + [pbr::OriginalName("ItemLevelCurrentMax")] ItemLevelCurrentMax = 14037, + /// + /// 보관할 수 없는 아이템 입니다. + /// + [pbr::OriginalName("ItemCanNotBeStored")] ItemCanNotBeStored = 14038, + /// + ///============================================================================================= + /// 아이템 액션 관련 오류 : 14101 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ItemUseFunctionNotFound")] ItemUseFunctionNotFound = 14101, + /// + /// 퀘스트 쿨타임 초기화 아이템 사용시 : 퀘스트 메일이 가득차서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseQuestMailCountMax")] ItemUseQuestMailCountMax = 14102, + /// + /// 퀘스트 쿨타임 초기화 아이템 사용시 : 이미 수행중이거나, 퀘스트메일이 존재해서 할당가능한 퀘스트가 없어서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseNotExistAssignableQuest")] ItemUseNotExistAssignableQuest = 14103, + /// + /// 퀘스트 할당 아이템 사용시 : 해당 퀘스트는 이미 진행중이어서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseAlreadyHasQuest")] ItemUseAlreadyHasQuest = 14104, + /// + /// 퀘스트 할당 아이템 사용시 : 해당 퀘스트메일은 이미 존재해서 아이템 사용 불가. + /// + [pbr::OriginalName("ItemUseAlreadyHasQuestMail")] ItemUseAlreadyHasQuestMail = 14105, + /// + ///============================================================================================= + /// 인벤토리 관련 오류 : 15000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BagRuleItemLargeTypeDuplicated")] BagRuleItemLargeTypeDuplicated = 15001, + /// + /// ToolEquipRule에 ItemLargeType이 중복 되었습니다. + /// + [pbr::OriginalName("ToolEquipRuleItemLargeTypeDuplicated")] ToolEquipRuleItemLargeTypeDuplicated = 15002, + /// + /// ClosthEquipRule에 ItemLargeType이 중복 되었습니다. + /// + [pbr::OriginalName("ClothEquipRuleItemLargeTypeDuplicated")] ClothEquipRuleItemLargeTypeDuplicated = 15003, + /// + /// TattooEquipRule에 ItemLargeType이 중복 되었습니다. + /// + [pbr::OriginalName("TattooEquipRuleItemLargeTypeDuplicated")] TattooEquipRuleItemLargeTypeDuplicated = 15004, + /// + /// InventoryRule을 찾을 수 없습니다. + /// + [pbr::OriginalName("InventoryRuleNotFound")] InventoryRuleNotFound = 15005, + /// + /// EquipInven을 찾을 수 없습니다. + /// + [pbr::OriginalName("EquipInvenNotFound")] EquipInvenNotFound = 15006, + /// + /// 이미 장착된 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyEquiped")] SlotsAlreadyEquiped = 15007, + /// + /// 이미 장착 해제된 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyUnequiped")] SlotsAlreadyUnequiped = 15008, + /// + /// 가방에 아이템이 가득 찼습니다. + /// + [pbr::OriginalName("BagIsItemFull")] BagIsItemFull = 15009, + /// + /// 가방에 아이템이 비어 있습니다. + /// + [pbr::OriginalName("BagIsItemEmpty")] BagIsItemEmpty = 15010, + /// + /// 가방에 DeltaItem이 중복 되었습니다. + /// + [pbr::OriginalName("BagDeltaItemDupliated")] BagDeltaItemDupliated = 15011, + /// + /// 가방에서 아이템을 찾을 수 없습니다. + /// + [pbr::OriginalName("BagItemNotFound")] BagItemNotFound = 15012, + /// + /// ClothEquipRule에서 ClothSlotType을 찾을 수 없습니다. + /// + [pbr::OriginalName("ClothEquipRuleClothSlotTypeNotFound")] ClothEquipRuleClothSlotTypeNotFound = 15013, + /// + /// ToolEquipRule에서 ToolSlotType를 찾을 수 없습니다. + /// + [pbr::OriginalName("ToolEquipRuleToolSlotTypeNotFound")] ToolEquipRuleToolSlotTypeNotFound = 15014, + /// + /// TattooEquipRule에서 TattooSlotType을 찾을 수 없습니다. + /// + [pbr::OriginalName("TattooEquipRuleTattooSlotTypeNotFound")] TattooEquipRuleTattooSlotTypeNotFound = 15015, + /// + /// BagTabType 추가를 실패 했습니다. + /// + [pbr::OriginalName("BagTabTypeAddFailed")] BagTabTypeAddFailed = 15016, + /// + /// BagTabType 오류 입니다. + /// + [pbr::OriginalName("BagTabTypeInvalid")] BagTabTypeInvalid = 15017, + /// + /// BagTabType을 찾을 수 없습니다. + /// + [pbr::OriginalName("BagTabTypeNotFound")] BagTabTypeNotFound = 15018, + /// + /// BagTabCount Merge를 실패 했습니다. + /// + [pbr::OriginalName("BagTabCountMergeFailed")] BagTabCountMergeFailed = 15019, + /// + /// Inventory EntityType 오류 입니다. + /// + [pbr::OriginalName("InventoryEntityTypeInvalid")] InventoryEntityTypeInvalid = 15020, + /// + /// InvenEquipType 오류 입니다. + /// + [pbr::OriginalName("InvenEquipTypeInvalid")] InvenEquipTypeInvalid = 15021, + /// + /// 가방에 예약된 아이템이 가득 찼습니다. + /// + [pbr::OriginalName("BagIsReservedItemFull")] BagIsReservedItemFull = 15022, + /// + /// 가방에 예약된 아이템이 없습니다. + /// + [pbr::OriginalName("BagIsReservedItemEmpty")] BagIsReservedItemEmpty = 15023, + /// + /// 장착 슬롯이 일치하지 않습니다. + /// + [pbr::OriginalName("EquipSlotNotMatch")] EquipSlotNotMatch = 15024, + /// + /// 장착 슬롯 범위를 벗어났습니다. + /// + [pbr::OriginalName("EquipSlotOutOfRange")] EquipSlotOutOfRange = 15025, + /// + /// 이미 예약된 장착 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyReservedEquip")] SlotsAlreadyReservedEquip = 15026, + /// + /// 이미 예약된 장착 해제 Slots 입니다. + /// + [pbr::OriginalName("SlotsAlreadyReservedUnequip")] SlotsAlreadyReservedUnequip = 15027, + /// + /// SlotType을 찾을 수 없습니다. + /// + [pbr::OriginalName("SlotTypeNotFound")] SlotTypeNotFound = 15028, + /// + ///============================================================================================= + /// UgcNpc 관련 오류 : 15101 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UgcNpcMetaGuidAlreadyAdded")] UgcNpcMetaGuidAlreadyAdded = 15101, + /// + /// UgcNpcDoc이 null 입니다. + /// + [pbr::OriginalName("UgcNpcDocIsNull")] UgcNpcDocIsNull = 15102, + /// + /// UgcNpc 생성 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcMaxCountExceed")] UgcNpcMaxCountExceed = 15103, + /// + /// UgcNpc 의상 아이템이 부족 합니다. + /// + [pbr::OriginalName("UgcNpcClothItemNotEnough")] UgcNpcClothItemNotEnough = 15104, + /// + /// UgcNpcDoc 중복 로딩 되었습니다. + /// + [pbr::OriginalName("UgcNpcDocLoadDuplicatedUgcNpc")] UgcNpcDocLoadDuplicatedUgcNpc = 15105, + /// + /// UgcNpc 캐릭터 설명 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcDescriptionLengthExceed")] UgcNpcDescriptionLengthExceed = 15106, + /// + /// UgcNpc 세계관 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcWordScenarioLengthExceed")] UgcNpcWordScenarioLengthExceed = 15107, + /// + /// UgcNpc 인사말 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcGreetingLengthExceed")] UgcNpcGreetingLengthExceed = 15108, + /// + /// UgcNpc 타투 아이템이 부족 합니다. + /// + [pbr::OriginalName("UgcNpcTattooItemNotEnough")] UgcNpcTattooItemNotEnough = 15109, + /// + /// UgcNpc 자주 사용하는 소셜 액션 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcHabitSocialActionCountExceed")] UgcNpcHabitSocialActionCountExceed = 15110, + /// + /// UgcNpc 대화중 기본 소셜 액션 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcDialogueSocialActionCountExceed")] UgcNpcDialogueSocialActionCountExceed = 15111, + /// + /// UgcNpc 닉네임이 중복 되었습니다. + /// + [pbr::OriginalName("UgcNpcNicknameDuplicated")] UgcNpcNicknameDuplicated = 15112, + /// + /// UgcNpc를 찾을 수 없습니다. + /// + [pbr::OriginalName("UgcNpcNotFound")] UgcNpcNotFound = 15113, + /// + /// UgcNpc 자기소개 문자열 길이를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcIntroductionLengthExceed")] UgcNpcIntroductionLengthExceed = 15114, + /// + /// UgcNpc Tag 개수를 초과 했습니다. + /// + [pbr::OriginalName("UgcNpcMaxTagExceed")] UgcNpcMaxTagExceed = 15115, + /// + /// UgcNpc 닉네임이 없습니다. + /// + [pbr::OriginalName("UgcNpcNicknameEmpty")] UgcNpcNicknameEmpty = 15116, + /// + /// UgcNpc가 이미 게임존에 등록되어 있습니다. + /// + [pbr::OriginalName("UgcNpcAlreadyRegisteredInGameZone")] UgcNpcAlreadyRegisteredInGameZone = 15117, + /// + /// UgcNpc가 게임존에 등록되어 있지 않습니다. + /// + [pbr::OriginalName("UgcNpcNotRegisteredInGameZone")] UgcNpcNotRegisteredInGameZone = 15118, + /// + /// UgcNpcLikeSelecteeCount를 찾을 수 없습니다. + /// + [pbr::OriginalName("UgcNpcLikeSelecteeCountNotFound")] UgcNpcLikeSelecteeCountNotFound = 15119, + /// + /// UgcNpcLikeSelectedFlag를 찾을 수 없습니다. + /// + [pbr::OriginalName("UgcNpcLikeSelectedFlagNotFound")] UgcNpcLikeSelectedFlagNotFound = 15120, + /// + /// UgcNpc가 마이홈 Ugc에 중복 배치 되었습니다. + /// + [pbr::OriginalName("UgcNpcDuplicateInMyhomeUgc")] UgcNpcDuplicateInMyhomeUgc = 15121, + /// + /// UgcNpc가 배치된 상태 입니다. + /// + [pbr::OriginalName("UgcNpcLocatedState")] UgcNpcLocatedState = 15122, + /// + /// UgcNpc 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("UgcNpcMetaDataNotFound")] UgcNpcMetaDataNotFound = 15123, + /// + /// Beacon 앱 프로필 업로드 쿨타임 입니다. + /// + [pbr::OriginalName("BeaconAppProfileUploadCoolTime")] BeaconAppProfileUploadCoolTime = 15124, + /// + /// Beacon Body 아이템 오류 입니다. + /// + [pbr::OriginalName("BeaconBodyItemInvalid")] BeaconBodyItemInvalid = 15125, + /// + /// UgcNpc가 BeaconShopItem을 가지고 있습니다. + /// + [pbr::OriginalName("UgcNpcHasBeaconShopItem")] UgcNpcHasBeaconShopItem = 15126, + /// + ///============================================================================================= + /// UgcNpc Ranking 관련 오류 : 15201 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UgcNpcRankEntityIsNotFound")] UgcNpcRankEntityIsNotFound = 15201, + /// + /// ugc npc ranking 조회 범위를 초과했습니다. + /// + [pbr::OriginalName("UgcNpcRankOutOfRange")] UgcNpcRankOutOfRange = 15202, + /// + ///============================================================================================= + /// Farming Effect 관련 오류 : 15301 ~ + ///============================================================================================= + /// + [pbr::OriginalName("FarmingEffectDocLinkPkSkNotSet")] FarmingEffectDocLinkPkSkNotSet = 15301, + /// + /// FarmingEffect가 이미 게임존에 등록되어 있습니다. + /// + [pbr::OriginalName("FarmingEffectAlreadyRegisteredInGameZone")] FarmingEffectAlreadyRegisteredInGameZone = 15302, + /// + /// 이미 진행중인 Farming 입니다. + /// + [pbr::OriginalName("FarmingAlready")] FarmingAlready = 15303, + /// + /// 나는 Farming 중입니다. + /// + [pbr::OriginalName("FarmingByMe")] FarmingByMe = 15304, + /// + /// FarmingPropMeta 데이터를 찾을 수 없습니다. + /// + [pbr::OriginalName("FarmingPropMetaDataNotFound")] FarmingPropMetaDataNotFound = 15305, + /// + /// Farming 시도 횟수 오류 입니다. + /// + [pbr::OriginalName("FarmingTryCountInvalid")] FarmingTryCountInvalid = 15306, + /// + /// Farming 상태가 아닙니다. + /// + [pbr::OriginalName("FarmingNotState")] FarmingNotState = 15307, + /// + /// Farming 소유자 일치 하지 않습니다. + /// + [pbr::OriginalName("FarmingOwnerNotMatch")] FarmingOwnerNotMatch = 15308, + /// + /// FarmingSummonedEntityType 오류 입니다. + /// + [pbr::OriginalName("FarmingSummonedEntityTypeInvalid")] FarmingSummonedEntityTypeInvalid = 15309, + /// + /// FarmingEffect가 게임존에 존재하지 않습니다. + /// + [pbr::OriginalName("FarmingEffectNotExistInGameZone")] FarmingEffectNotExistInGameZone = 15310, + /// + /// Farming 상태 입니다. + /// + [pbr::OriginalName("FarimgState")] FarimgState = 15311, + /// + /// Farming StandBy 상태가 아닙니다. + /// + [pbr::OriginalName("FarmingStandByNotState")] FarmingStandByNotState = 15312, + /// + /// Farming Anchor를 찾을 수 없습니다. + /// + [pbr::OriginalName("FarmingAnchorNotFound")] FarmingAnchorNotFound = 15313, + /// + /// Farming 관련 Beacon Db 정보 무결성 오류 입니다. + /// + [pbr::OriginalName("FarmingBeaconDbInfoIntegrityError")] FarmingBeaconDbInfoIntegrityError = 15314, + /// + ///============================================================================================= + /// Gacha 관련 오류 : 15401 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GachaMetaDataNotFound")] GachaMetaDataNotFound = 15401, + /// + /// Gacha 보상 정보가 없습니다. + /// + [pbr::OriginalName("GachaRewardEmpty")] GachaRewardEmpty = 15402, + /// + ///============================================================================================= + /// Master 관련 오류 : 15800 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MasterNotFound")] MasterNotFound = 15801, + /// + /// Master 관계 설정이 없다. + /// + [pbr::OriginalName("MasterNotRelated")] MasterNotRelated = 15802, + /// + ///============================================================================================= + /// 게임존 관련 오류 : 15900 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GameZoneNotJoin")] GameZoneNotJoin = 15901, + /// + /// Map 객체가 null 입니다. + /// + [pbr::OriginalName("MapIsNull")] MapIsNull = 15902, + /// + /// LOCATION_UNIQUE_ID 값 오류 입니다. + /// + [pbr::OriginalName("LocationUniqueIdInvalid")] LocationUniqueIdInvalid = 15903, + /// + /// Prop이 점유중 입니다. + /// + [pbr::OriginalName("PropIsOccupied")] PropIsOccupied = 15904, + /// + /// Prop을 점유중이 아닙니다. + /// + [pbr::OriginalName("PropIsNotOccupied")] PropIsNotOccupied = 15905, + /// + ///============================================================================================= + /// 친구 관련 오류 : 16000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("FriendDocIsNull")] FriendDocIsNull = 16001, + /// + /// FriendFolderDoc이 null 입니다. + /// + [pbr::OriginalName("FriendFolderDocIsNull")] FriendFolderDocIsNull = 16002, + /// + ///============================================================================================= + /// 소셜 액션 관련 오류 : 17000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("SocialActionMetaDataNotFound")] SocialActionMetaDataNotFound = 17001, + /// + /// 소셜 액션을 찾지 못했습니다. + /// + [pbr::OriginalName("SocialActionNotFound")] SocialActionNotFound = 17002, + /// + /// SocialActionDoc 로딩중에 중복된 소셜 액션이 발견되었습니다. + /// + [pbr::OriginalName("SocialActionDocLoadDuplicatedSocialAction")] SocialActionDocLoadDuplicatedSocialAction = 17003, + /// + /// 소셜 액션이 이미 존재합니다. + /// + [pbr::OriginalName("SocialActionAlreadyExist")] SocialActionAlreadyExist = 17004, + /// + /// 소셜 액션 슬롯의 범위를 벗어났습니다. + /// + [pbr::OriginalName("SocialActionSlotOutOfRange")] SocialActionSlotOutOfRange = 17005, + /// + /// 소셜 액션이 슬롯에 존재하지 않습니다. + /// + [pbr::OriginalName("SocialActionNotOnSlot")] SocialActionNotOnSlot = 17006, + /// + /// SocialActionDoc이 null 입니다. + /// + [pbr::OriginalName("SocialActionDocIsNull")] SocialActionDocIsNull = 17007, + /// + ///============================================================================================= + /// 채널 관련 오류 : 18000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ChannelMoveSameChannel")] ChannelMoveSameChannel = 18001, + /// + /// 채널 이동 쿨타임이 지나지 않았습니다. + /// + [pbr::OriginalName("ChannelInvalidMoveCoolTime")] ChannelInvalidMoveCoolTime = 18002, + /// + /// 채널서버가 아닙니다. ( Indun 서버에서 이동요청시 발생 ) + /// + [pbr::OriginalName("NotChannelServer")] NotChannelServer = 18003, + /// + ///============================================================================================= + /// 던전 관련 오류 : 19000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("OwnedRoomDocIsNull")] OwnedRoomDocIsNull = 19001, + /// + /// RoomDoc이 null 입니다. + /// + [pbr::OriginalName("RoomDocIsNull")] RoomDocIsNull = 19002, + /// + /// Room이 마이홈이 아닙니다. + /// + [pbr::OriginalName("RoomIsNotMyHome")] RoomIsNotMyHome = 19003, + /// + /// MapRange의 범위를 벗어난 CellPos 입니다. + /// + [pbr::OriginalName("MapRangeOutOfCellPos")] MapRangeOutOfCellPos = 19004, + /// + /// MapGrid의 범위를 벗어 났습니다. + /// + [pbr::OriginalName("MapGridBoundOutOfRange")] MapGridBoundOutOfRange = 19005, + /// + /// Map내에서 Grid를 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridNotFoundGrid")] MapGridNotFoundGrid = 19006, + /// + /// MapGrid내에서 Cell을 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridNotFoundCell")] MapGridNotFoundCell = 19007, + /// + /// MapGrid내의 Cell에서 플레이어를 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridCellNotFoundPlayer")] MapGridCellNotFoundPlayer = 19008, + /// + /// MapGrid내의 Cell에서 UgcNpc를 찾지 못했습니다. + /// + [pbr::OriginalName("MapGridCellNotFoundUgcNpc")] MapGridCellNotFoundUgcNpc = 19009, + /// + ///============================================================================================= + /// 메일 관련 오류 : 20000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MailNotFound")] MailNotFound = 20001, + /// + /// 이미 아이템을 받았습니다. + /// + [pbr::OriginalName("MailAlreadyTaken")] MailAlreadyTaken = 20002, + /// + /// 메일 MailType 오류 입니다. + /// + [pbr::OriginalName("MailInvalidMailType")] MailInvalidMailType = 20003, + /// + /// 보낼 수 있는 메일의 갯수를 초과 했습니다. + /// + [pbr::OriginalName("MailMaxSendCountExceed")] MailMaxSendCountExceed = 20004, + /// + /// 메일 Doc이 null 입니다. + /// + [pbr::OriginalName("MailDocIsNull")] MailDocIsNull = 20005, + /// + /// 메일 블락한 유저에게 보낼 수 없습니다. + /// + [pbr::OriginalName("MailBlockUserCannotSend")] MailBlockUserCannotSend = 20006, + /// + /// 타겟의 받을 수 있는 메일의 갯수를 초과 했습니다. + /// + [pbr::OriginalName("MailMaxTargetReceivedCountExceed")] MailMaxTargetReceivedCountExceed = 20007, + /// + /// 나에게 메일을 보낼 수 없습니다. + /// + [pbr::OriginalName("MailCantSendSelf")] MailCantSendSelf = 20008, + /// + /// 아이템이 있는 상태의 메일은 삭제할 수 없습니다. + /// + [pbr::OriginalName("MailCantDeleteIfItemExists")] MailCantDeleteIfItemExists = 20009, + /// + /// MailDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("MailDocException")] MailDocException = 20010, + /// + ///============================================================================================= + /// 메일 프로필 관련 오류 : 20300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MailProfileDocIsNull")] MailProfileDocIsNull = 20301, + /// + /// MailProfileDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("MailProfileDocException")] MailProfileDocException = 20302, + /// + ///============================================================================================= + /// 시스템 메일 관련 오류 : 20500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("SystemMailDocIsNull")] SystemMailDocIsNull = 20501, + /// + ///============================================================================================= + /// 파티 관련 오류 : 21000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("PartyCannotSetGuid")] PartyCannotSetGuid = 21001, + /// + /// Party Member 정보 생성 실패입니다. + /// + [pbr::OriginalName("PartyFailedMakePartyMember")] PartyFailedMakePartyMember = 21002, + /// + /// 파티 인원이 초과되었습니다. + /// + [pbr::OriginalName("PartyIsFull")] PartyIsFull = 21003, + /// + /// 이미 초대를 보냈습니다. + /// + [pbr::OriginalName("AlreadyInviteParty")] AlreadyInviteParty = 21004, + /// + /// 초대 정보를 확인할 수 없습니다. + /// + [pbr::OriginalName("NotFoundPartyInvite")] NotFoundPartyInvite = 21005, + /// + /// 파티 정보를 확인할 수 없습니다. + /// + [pbr::OriginalName("NotFoundParty")] NotFoundParty = 21006, + /// + /// 파티 상태가 아닙니다. + /// + [pbr::OriginalName("NotParty")] NotParty = 21007, + /// + /// 파티 리더가 아닙니다. + /// + [pbr::OriginalName("NotPartyLeader")] NotPartyLeader = 21008, + /// + /// 파티에 입장 중입니다. + /// + [pbr::OriginalName("JoiningParty")] JoiningParty = 21009, + /// + /// 파티원이 아닙니다. + /// + [pbr::OriginalName("NotPartyMember")] NotPartyMember = 21010, + /// + /// 이미 소환된 상태입니다. + /// + [pbr::OriginalName("AlreadySummon")] AlreadySummon = 21011, + /// + /// 파티 리더의 서버 허용인원이 초과되었습니다. + /// + [pbr::OriginalName("PartyLeaderServerIsFull")] PartyLeaderServerIsFull = 21012, + /// + /// 초대할 유저가 콘서트에 있습니다. + /// + [pbr::OriginalName("InviteMemberIsConcert")] InviteMemberIsConcert = 21013, + /// + /// 파티 초대 발송에 실패했습니다. + /// + [pbr::OriginalName("FailToSendInviteMember")] FailToSendInviteMember = 21014, + /// + /// 이미 파티에 소속되어 있습니다. + /// + [pbr::OriginalName("AlreadyPartyMember")] AlreadyPartyMember = 21015, + /// + /// 소환할 수 없는 서버에 있습니다. + /// + [pbr::OriginalName("InvalidSummonServerType")] InvalidSummonServerType = 21016, + /// + /// 소환 대상의 거리가 짧습니다. + /// + [pbr::OriginalName("SummonUserLimitDistance")] SummonUserLimitDistance = 21017, + /// + /// 소환 대상자가 아닙니다. + /// + [pbr::OriginalName("InvalidSummonMember")] InvalidSummonMember = 21018, + /// + /// 파티 리더가 logout 상태입니다. + /// + [pbr::OriginalName("PartyLeaderLoggedOut")] PartyLeaderLoggedOut = 21019, + /// + /// 진행 중인 Vote 가 있습니다. + /// + [pbr::OriginalName("AlreadyStartPartyVote")] AlreadyStartPartyVote = 21020, + /// + /// 진행 중인 Vote 가 없습니다. + /// + [pbr::OriginalName("NoStartPartyVote")] NoStartPartyVote = 21021, + /// + /// 투표 가능 시간이 지났습니다. + /// + [pbr::OriginalName("AlreadyPassPartyVoteTime")] AlreadyPassPartyVoteTime = 21022, + /// + /// 투표 시작 가능 시간이 지나지 않았습니다. + /// + [pbr::OriginalName("InvalidPartyVoteTime")] InvalidPartyVoteTime = 21023, + /// + /// 이미 투표를 하였습니다. + /// + [pbr::OriginalName("AlreadyReplyPartyVote")] AlreadyReplyPartyVote = 21024, + /// + /// 파티 인스턴스 가 없습니다. + /// + [pbr::OriginalName("EmptyPartyInstanceId")] EmptyPartyInstanceId = 21025, + /// + /// 초대할 수 없는 장소입니다. + /// + [pbr::OriginalName("InvalidInvitePlace")] InvalidInvitePlace = 21026, + /// + /// 초대 유저의 정보가 잘못되었습니다. + /// + [pbr::OriginalName("InvitePartyInvalidUsers")] InvitePartyInvalidUsers = 21027, + /// + /// 파티원 소환에 실패했습니다. + /// + [pbr::OriginalName("SummonPartyMemberFail")] SummonPartyMemberFail = 21028, + /// + /// 입력 글자수 최대 길이가 잘못되었습니다. + /// + [pbr::OriginalName("InvalidPartyStringLength")] InvalidPartyStringLength = 21029, + /// + /// 파티명에 금칙어가 있습니다. + /// + [pbr::OriginalName("IncludeBanWordFromPartyName")] IncludeBanWordFromPartyName = 21030, + /// + /// 파티원 정보가 없습니다. + /// + [pbr::OriginalName("JoiningPartyMemberInfoIsNull")] JoiningPartyMemberInfoIsNull = 21031, + /// + /// 서로 다른 월드에 있습니다. + /// + [pbr::OriginalName("InvalidSummonWorldServer")] InvalidSummonWorldServer = 21032, + [pbr::OriginalName("NotExistPartyInstance")] NotExistPartyInstance = 21999, + /// + ///============================================================================================= + /// 버프 관련 오류 : 22000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BuffMetaDataNotFound")] BuffMetaDataNotFound = 22001, + /// + /// 등록되지 않은 버프 카테고리 입니다. + /// + [pbr::OriginalName("BuffNotRegistryCategory")] BuffNotRegistryCategory = 22002, + /// + /// 버프를 찾지 못했습니다. + /// + [pbr::OriginalName("BuffNotFound")] BuffNotFound = 22003, + /// + /// 버프 BuffCategoryType 오류 입니다. + /// + [pbr::OriginalName("BuffInvalidBuffCategoryType")] BuffInvalidBuffCategoryType = 22004, + /// + /// BuffCache 로딩중에 중복된 버프가 발견되었습니다. + /// + [pbr::OriginalName("BuffCacheLoadDuplicatedBuff")] BuffCacheLoadDuplicatedBuff = 22005, + /// + /// 버프 AttributeType 오류 입니다. + /// + [pbr::OriginalName("BuffInvalidAttributeType")] BuffInvalidAttributeType = 22006, + /// + ///============================================================================================= + /// 퀘스트 관련 오류 : 23000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("QuestAssingDataNotExist")] QuestAssingDataNotExist = 23000, + /// + /// 퀘스트 메일이 없습니다. + /// + [pbr::OriginalName("QuestMailNotExist")] QuestMailNotExist = 23001, + /// + /// 이미 완료된 퀘스트 + /// + [pbr::OriginalName("QuestAlreadyEnded")] QuestAlreadyEnded = 23002, + /// + /// 할당 가능한 퀘스트 수 맥스 + /// + [pbr::OriginalName("QuestCountMax")] QuestCountMax = 23003, + /// + /// 타입별로 할당 가능한 퀘스트 수 맥스 + /// + [pbr::OriginalName("QuestTypeAssignCountMax")] QuestTypeAssignCountMax = 23004, + /// + /// 퀘스트 타입 오류 + /// + [pbr::OriginalName("QuestInvalidType")] QuestInvalidType = 23005, + /// + /// 퀘스트 값 오류 + /// + [pbr::OriginalName("QuestInvalidValue")] QuestInvalidValue = 23006, + /// + /// 퀘스트 아이디 없음 + /// + [pbr::OriginalName("QuestIdNotFound")] QuestIdNotFound = 23007, + /// + /// 잘못된 태스트 진행 번호 + /// + [pbr::OriginalName("QuestInvalidTaskNum")] QuestInvalidTaskNum = 23008, + /// + /// 퀘스트 거절은 일반 퀘스트만 가능 + /// + [pbr::OriginalName("QuestRefuseOnlyNormal")] QuestRefuseOnlyNormal = 23009, + /// + /// 포기하려는 퀘스트 정보 존재하지 않습니다. + /// + [pbr::OriginalName("QuestAbadonNotExistQuest")] QuestAbadonNotExistQuest = 23010, + /// + /// 이미 달성한 퀘스트 + /// + [pbr::OriginalName("QuestAlreadyComplete")] QuestAlreadyComplete = 23011, + /// + /// 퀘스트 미달성 + /// + [pbr::OriginalName("QuestNotComplete")] QuestNotComplete = 23012, + /// + /// 퀘스트 포기는 일반 퀘스트만 가능 + /// + [pbr::OriginalName("QuestAbandonOnlyNormal")] QuestAbandonOnlyNormal = 23013, + /// + /// QuestMailDoc이 null 입니다. + /// + [pbr::OriginalName("QuestMailDocIsNull")] QuestMailDocIsNull = 23014, + /// + /// QuestDoc이 null 입니다. + /// + [pbr::OriginalName("QuestDocIsNull")] QuestDocIsNull = 23015, + /// + /// EndQuestDoc이 null 입니다. + /// + [pbr::OriginalName("EndQuestDocIsNull")] EndQuestDocIsNull = 23016, + /// + /// QuestMetaBase가 구현되지 않았습니다. + /// + [pbr::OriginalName("QuestMetaBaseNotImplement")] QuestMetaBaseNotImplement = 23017, + /// + /// Quest notify 레디스에 등록 실패 + /// + [pbr::OriginalName("QuestNotifyRedisRegistFail")] QuestNotifyRedisRegistFail = 23018, + /// + /// Quest를 이미 보유중입니다. + /// + [pbr::OriginalName("QuestAlreadyExist")] QuestAlreadyExist = 23019, + /// + ///============================================================================================= + /// 월드 관련 오류 : 24000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("WorldMetaDataNotFound")] WorldMetaDataNotFound = 24001, + /// + /// 월드 입장 아이템 수량이 부족합니다. + /// + [pbr::OriginalName("LackOfWorldEnterItem")] LackOfWorldEnterItem = 24002, + /// + /// 월드 맵트리 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("WorldMapTreeDataNotFound")] WorldMapTreeDataNotFound = 24003, + /// + /// 월드 맵트리 하위 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("WorldMapTreeChildLandNotFound")] WorldMapTreeChildLandNotFound = 24004, + /// + /// 룸 맵 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("RoomMapDataNotFound")] RoomMapDataNotFound = 24005, + /// + ///============================================================================================= + /// 랜드 관련 오류 : 24500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LandMetaDataNotFound")] LandMetaDataNotFound = 24501, + /// + /// 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("LandNotFound")] LandNotFound = 24502, + /// + /// LandDoc 로딩중에 중복된 랜드가 발견되었습니다. + /// + [pbr::OriginalName("LandDocLoadDuplicatedLand")] LandDocLoadDuplicatedLand = 24503, + /// + /// LandDoc이 null 입니다. + /// + [pbr::OriginalName("LandDocIsNull")] LandDocIsNull = 24504, + /// + /// 소유 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("OwnedLandNotFound")] OwnedLandNotFound = 24505, + /// + /// OwnedLandDoc 로딩중에 중복된 랜드가 발견되었습니다. + /// + [pbr::OriginalName("OwnedLandDocLoadDuplicatedOwnedLand")] OwnedLandDocLoadDuplicatedOwnedLand = 24506, + /// + /// OwnedLandDoc이 null 입니다. + /// + [pbr::OriginalName("OwnedLandDocIsNull")] OwnedLandDocIsNull = 24507, + /// + /// 랜드 맵트리 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("LandMapTreeDataNotFound")] LandMapTreeDataNotFound = 24508, + /// + /// 랜드 맵트리 하위 빌딩을 찾지 못했습니다. + /// + [pbr::OriginalName("LandMapTreeChildBuildingNotFound")] LandMapTreeChildBuildingNotFound = 24509, + /// + /// 랜드 빌딩이 비어 있지 않습니다. + /// + [pbr::OriginalName("LandBuildingIsNotEmpty")] LandBuildingIsNotEmpty = 24510, + /// + /// 랜드 맵 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("LandMapDataNotFound")] LandMapDataNotFound = 24511, + /// + /// 랜드 오너가 존재합니다. + /// + [pbr::OriginalName("LandExistOwner")] LandExistOwner = 24512, + /// + /// 랜드 EditorType 이 USER 가 아닙니다. + /// + [pbr::OriginalName("LandEditorIsNotUser")] LandEditorIsNotUser = 24513, + /// + /// 랜드 오너가 아닙니다. + /// + [pbr::OriginalName("LandOwnerIsNotMatch")] LandOwnerIsNotMatch = 24514, + /// + ///============================================================================================= + /// 빌딩 관련 오류 : 25000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BuildingMetaDataNotFound")] BuildingMetaDataNotFound = 25001, + /// + /// 빌딩을 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingNotFound")] BuildingNotFound = 25002, + /// + /// BuildingDoc 로딩중에 중복된 빌딩이 발견되었습니다. + /// + [pbr::OriginalName("BuildingDocLoadDuplicatedBuilding")] BuildingDocLoadDuplicatedBuilding = 25003, + /// + /// BuildingDoc이 null 입니다. + /// + [pbr::OriginalName("BuildingDocIsNull")] BuildingDocIsNull = 25004, + /// + /// 소유 빌딩을 찾지 못했습니다. + /// + [pbr::OriginalName("OwnedBuildingNotFound")] OwnedBuildingNotFound = 25005, + /// + /// OwnedBuildingDoc 로딩중에 중복된 빌딩이 발견되었습니다. + /// + [pbr::OriginalName("OwnedBuildingDocLoadDuplicatedOwnedBuilding")] OwnedBuildingDocLoadDuplicatedOwnedBuilding = 25006, + /// + /// OwnedBuildingDoc이 null 입니다. + /// + [pbr::OriginalName("OwnedBuildingDocIsNull")] OwnedBuildingDocIsNull = 25007, + /// + /// 빌딩 맵트리 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapTreeDataNotFound")] BuildingMapTreeDataNotFound = 25008, + /// + /// 빌딩 맵트리 하위 룸을 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapTreeChildRoomNotFound")] BuildingMapTreeChildRoomNotFound = 25009, + /// + /// 빌딩 층이 비어 있지 않습니다. + /// + [pbr::OriginalName("BuildingFloorIsNotEmpty")] BuildingFloorIsNotEmpty = 25010, + /// + /// 빌딩 맵 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapDataNotFound")] BuildingMapDataNotFound = 25011, + /// + /// 빌딩 오너가 존재합니다. + /// + [pbr::OriginalName("BuildingExistOwner")] BuildingExistOwner = 25012, + /// + /// 빌딩 맵트리 상위 랜드를 찾지 못했습니다. + /// + [pbr::OriginalName("BuildingMapTreeParentLandNotFound")] BuildingMapTreeParentLandNotFound = 20513, + /// + /// 빌딩 오너가 아닙니다. + /// + [pbr::OriginalName("BuildingOwnerIsNotMatch")] BuildingOwnerIsNotMatch = 25014, + /// + ///============================================================================================= + /// 마이홈 관련 오류 : 25500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MyHomeMetaDataNotFound")] MyHomeMetaDataNotFound = 25501, + /// + /// 마이홈을 찾지 못했습니다. + /// + [pbr::OriginalName("MyHomeNotFound")] MyHomeNotFound = 25502, + /// + /// MyHomeDoc 로딩중에 중복된 마이홈이 발견되었습니다. + /// + [pbr::OriginalName("MyHomeDocLoadDuplicatedMyHome")] MyHomeDocLoadDuplicatedMyHome = 25503, + /// + /// 마이홈이 이미 존재합니다. + /// + [pbr::OriginalName("MyHomeAlreadyExist")] MyHomeAlreadyExist = 25504, + /// + /// MyHomeDoc이 null 입니다. + /// + [pbr::OriginalName("MyHomeDocIsNull")] MyHomeDocIsNull = 25505, + /// + /// 마이홈이 내 것이 아닙니다. + /// + [pbr::OriginalName("MyHomeIsNotMine")] MyHomeIsNotMine = 25506, + /// + /// 제작중일때는 마이홈을 변경할수 없습니다. + /// + [pbr::OriginalName("MyHomeCantExchangeWhenCrafting")] MyHomeCantExchangeWhenCrafting = 25507, + /// + /// EditableRoom 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("EditableRoomMetaDataNotFound")] EditableRoomMetaDataNotFound = 25508, + /// + /// EditableFramework 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("EditableFrameworkMetaDataNotFound")] EditableFrameworkMetaDataNotFound = 25509, + /// + /// 사용 가능한 InteriorPoint를 초과 하였습니다. + /// + [pbr::OriginalName("InteriorPointExceed")] InteriorPointExceed = 25510, + /// + /// 마이홈 슬롯이 부족 합니다. + /// + [pbr::OriginalName("MyhomeNotEnoughSlot")] MyhomeNotEnoughSlot = 25511, + /// + /// 마이홈이 선택되어 있습니다. + /// + [pbr::OriginalName("MyhomeIsSelected")] MyhomeIsSelected = 25512, + /// + /// 마이홈 이름 문자열 길이가 짧습니다 + /// + [pbr::OriginalName("MyhomeNameLengthShort")] MyhomeNameLengthShort = 25513, + /// + /// 마이홈 이름 문자열 길이가 깁니다. + /// + [pbr::OriginalName("MyhomeNameLengthLong")] MyhomeNameLengthLong = 25514, + /// + /// 마이홈 이름이 중복 되었습니다. + /// + [pbr::OriginalName("MyhomeNameDuplicated")] MyhomeNameDuplicated = 25515, + /// + /// 마이홈 인터폰이 없습니다. + /// + [pbr::OriginalName("MyhomeInterphoneNotExist")] MyhomeInterphoneNotExist = 25516, + /// + /// 마이홈 시작 지점이 없습니다. + /// + [pbr::OriginalName("MyhomeStartPointNotExist")] MyhomeStartPointNotExist = 25517, + /// + /// 마이홈 인터폰이 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("MyhomeInterphoneExceed")] MyhomeInterphoneExceed = 25518, + /// + /// 마이홈 시작 지점이 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("MyhomeStartPointExceed")] MyhomeStartPointExceed = 25519, + /// + /// 의상 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CraftingClothesExceed")] CraftingClothesExceed = 25520, + /// + /// 요리 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CraftingCookingExceed")] CraftingCookingExceed = 25521, + /// + /// 가구 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CraftingFurnitureExceed")] CraftingFurnitureExceed = 25522, + /// + /// 앵커가 마이홈에 배치되어 있지 않습니다. + /// + [pbr::OriginalName("AnchorIsNotInMyhome")] AnchorIsNotInMyhome = 25523, + /// + /// 제작 진행중인 제작대를 제거 할 수 없습니다. + /// + [pbr::OriginalName("DoNotRemoveProcessCraftingAnchor")] DoNotRemoveProcessCraftingAnchor = 25524, + /// + /// 앵커 Guid가 중복 되었습니다. + /// + [pbr::OriginalName("AnchorGuidDuplicate")] AnchorGuidDuplicate = 25525, + /// + /// 마이홈이 편집 중 입니다. + /// + [pbr::OriginalName("MyhomeIsEditting")] MyhomeIsEditting = 25526, + /// + /// 마이홈이 렌탈 중 입니다. + /// + [pbr::OriginalName("MyhomeIsOnRental")] MyhomeIsOnRental = 25527, + /// + /// 마이홈 Ugc Info 파일을 찾지 못했습니다. + /// + [pbr::OriginalName("MyhomeUgcInfoFileNotFoune")] MyhomeUgcInfoFileNotFoune = 25528, + /// + /// EditableRoom SizeType 오류 입니다. + /// + [pbr::OriginalName("EditableRoomSizeTypeInvalid")] EditableRoomSizeTypeInvalid = 25529, + /// + /// 제작대 허용 갯수를 초과 하였습니다. + /// + [pbr::OriginalName("CrafterCountExceed")] CrafterCountExceed = 25530, + /// + ///============================================================================================= + /// 미니맵 마커 관련 오류 : 26000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MinimapMarkerNotFound")] MinimapMarkerNotFound = 26001, + /// + /// MinimapMarkerDoc 로딩중에 중복된 미니맵 마커가 발견되었습니다. + /// + [pbr::OriginalName("MinimapMarkerDocLoadDuplicatedMinimapMarker")] MinimapMarkerDocLoadDuplicatedMinimapMarker = 26002, + /// + /// MinimapMarkerDoc이 null 입니다. + /// + [pbr::OriginalName("MinimapMarkerDocIsNull")] MinimapMarkerDocIsNull = 26003, + /// + ///============================================================================================= + /// 카트 관련 오류 : 26500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CartMetaDataNotFound")] CartMetaDataNotFound = 26501, + /// + /// 카트 용량이 가득 찼습니다. + /// + [pbr::OriginalName("CartMaxCountExceed")] CartMaxCountExceed = 26502, + /// + /// 카트에 아이템 스텍이 가득 찼습니다. + /// + [pbr::OriginalName("CartStackCountInvalid")] CartStackCountInvalid = 26503, + /// + /// 카트의 아이템 갯수가 요청값보다 낮습니다. + /// + [pbr::OriginalName("CartStackCountNotEnough")] CartStackCountNotEnough = 26504, + /// + /// 카트에서 아이템을 찾지 못했습니다. + /// + [pbr::OriginalName("CartItemNotFound")] CartItemNotFound = 26505, + /// + /// 등록되지 않은 재화타입 입니다. + /// + [pbr::OriginalName("CartNotRegistryCurrencyType")] CartNotRegistryCurrencyType = 26506, + /// + /// 카트 CurrencyType 오류 입니다. + /// + [pbr::OriginalName("CartInvalidCurrencyType")] CartInvalidCurrencyType = 26507, + /// + /// CartDoc이 null 입니다. + /// + [pbr::OriginalName("CartDocIsNull")] CartDocIsNull = 26508, + /// + /// CartDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CartDocException")] CartDocException = 26509, + /// + ///============================================================================================= + /// 채팅 관련 오류 : 27000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ChatSendSelfFailed")] ChatSendSelfFailed = 27001, + /// + /// 채팅 ChatType 오류 입니다. + /// + [pbr::OriginalName("ChatInvalidChatType")] ChatInvalidChatType = 27002, + /// + /// 귓속말을 블락한 유저에게 보낼 수 없습니다. + /// + [pbr::OriginalName("ChatBlockUserCannotWhisper")] ChatBlockUserCannotWhisper = 27003, + /// + /// 채팅 메시지 길이를 초과했습니다. + /// + [pbr::OriginalName("ChatInvalidMessageLength")] ChatInvalidMessageLength = 27004, + /// + /// 금칙어가 포함되어 있습니다. + /// + [pbr::OriginalName("ChatIncludeBanWord")] ChatIncludeBanWord = 27005, + /// + /// NoticeChatDoc이 null 입니다. + /// + [pbr::OriginalName("NoticeChatDocIsNull")] NoticeChatDocIsNull = 27501, + /// + ///============================================================================================= + /// 탈출 관련 오류 : 28000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("EscapePositionNotAvailableTime")] EscapePositionNotAvailableTime = 28001, + /// + /// EscapePositionDoc이 null 입니다. + /// + [pbr::OriginalName("EscapePositionDocIsNull")] EscapePositionDocIsNull = 28002, + /// + ///============================================================================================= + /// 블록 유저 관련 오류 : 28500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BlockUserDocIsNull")] BlockUserDocIsNull = 28501, + /// + ///============================================================================================= + /// 캐릭터 프로필 관련 오류 : 29000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CharacterProfileDocIsNull")] CharacterProfileDocIsNull = 29001, + /// + ///============================================================================================= + /// 커스텀 디파인 Data 관련 오류 : 29300 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CustomDefinedDataDocIsNull")] CustomDefinedDataDocIsNull = 29301, + /// + ///============================================================================================= + /// 커스텀 디파인 UI 관련 오류 : 29500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CustomDefinedUiDocIsNull")] CustomDefinedUiDocIsNull = 29501, + /// + ///============================================================================================= + /// 게임 옵션 관련 오류 : 30000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GameOptionDocIsNull")] GameOptionDocIsNull = 30001, + /// + /// GameOptionDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("GameOptionDocException")] GameOptionDocException = 30002, + /// + ///============================================================================================= + /// 레벨 관련 오류 : 30500 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LevelDocIsNull")] LevelDocIsNull = 30501, + /// + ///============================================================================================= + /// 위치 관련 오류 : 31000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LocationDocIsNull")] LocationDocIsNull = 31001, + /// + /// 사용 할 수 없는 장소 입니다. + /// + [pbr::OriginalName("NotUsablePlace")] NotUsablePlace = 31002, + /// + /// Redis에 LocationCache 정보 저장을 실패했습니다. + /// + [pbr::OriginalName("RedisLocationCacheSetFailed")] RedisLocationCacheSetFailed = 31003, + /// + ///============================================================================================= + /// 재화 관련 오류 : 32000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("MoneyDocIsNull")] MoneyDocIsNull = 32001, + /// + /// CurrencyControl이 초기화되지 않았습니다. + /// + [pbr::OriginalName("MoneyControlNotInitialize")] MoneyControlNotInitialize = 32002, + /// + /// 금전이 부족합니다. + /// + [pbr::OriginalName("MoneyNotEnough")] MoneyNotEnough = 32003, + /// + /// 금전 최대 보유량을 초과 했습니다. + /// + [pbr::OriginalName("MoneyMaxCountExceeded")] MoneyMaxCountExceeded = 32004, + /// + /// 재화 메타 데이터를 찾을 수 없습니다. + /// + [pbr::OriginalName("CurrencyMetaDataNotFound")] CurrencyMetaDataNotFound = 32005, + /// + ///============================================================================================= + /// 상점 관련 오류 : 33000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ShopProductTradingMeterDocIsNull")] ShopProductTradingMeterDocIsNull = 33001, + /// + /// MyHome 에 설치된 Item 입니다. + /// + [pbr::OriginalName("ShopIsMyHomeItem")] ShopIsMyHomeItem = 33002, + /// + /// ShopProduct 에 Shop Buy Type 이 잘못되었습니다. + /// + [pbr::OriginalName("InvalidShopBuyType")] InvalidShopBuyType = 33003, + /// + /// 리뉴얼 할 수 없는 상점입니다. + /// + [pbr::OriginalName("ShopProductCannotRenwal")] ShopProductCannotRenwal = 33004, + /// + /// 자동 갱신시간 전 후 1분 동안은 갱신 할 수 없다. + /// + [pbr::OriginalName("ShopProductNotRenwalTime")] ShopProductNotRenwalTime = 33005, + /// + /// 갱신 가능한 횟수를 이미 다 사용했습니다. + /// + [pbr::OriginalName("ShopProductRenewalCountAlreadyMax")] ShopProductRenewalCountAlreadyMax = 33006, + /// + /// 재판매할 수 없는 상품입니다. + /// + [pbr::OriginalName("ShopItemCannotSell")] ShopItemCannotSell = 33007, + /// + ///============================================================================================= + /// 보상 관련 오류 : 34000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("RewardInfoNotExist")] RewardInfoNotExist = 34000, + /// + /// 보상 타입 오류 + /// + [pbr::OriginalName("RewardInvalidType")] RewardInvalidType = 34001, + /// + /// 보상 타입 값 오류 + /// + [pbr::OriginalName("RewardInvalidTypeValue")] RewardInvalidTypeValue = 34002, + /// + /// + [pbr::OriginalName("NotRequireAttributeValue")] NotRequireAttributeValue = 34003, + /// + /// 보상 그룹 아이디 문자열을 정수로 변환하는데 오류가 발생했습니다. + /// + [pbr::OriginalName("RewardGroupIdParsingFromStringToIntErorr")] RewardGroupIdParsingFromStringToIntErorr = 34004, + /// + ///============================================================================================= + /// Claime 관련 오류 : 35000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ClaimInvalidType")] ClaimInvalidType = 35000, + /// + ///클레임 멤버십 없음 + /// + [pbr::OriginalName("ClaimMembershipNotExist")] ClaimMembershipNotExist = 35001, + /// + ///클레임 정보 없음 + /// + [pbr::OriginalName("ClaimInfoNotExist")] ClaimInfoNotExist = 35002, + /// + ///클레임 보상시간이 안됐다 + /// + [pbr::OriginalName("ClaimRewardNotEnoughTime")] ClaimRewardNotEnoughTime = 35003, + /// + ///클레임 보상 이벤트 종료 + /// + [pbr::OriginalName("ClaimRewardEventEnd")] ClaimRewardEventEnd = 35004, + /// + ///ClaimDoc 존재하지 않음 + /// + [pbr::OriginalName("ClaimDocIsNull")] ClaimDocIsNull = 35005, + /// + ///============================================================================================= + /// 제작 관련 오류 : 36000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("CraftRecipeDocIsNull")] CraftRecipeDocIsNull = 36001, + /// + /// CraftHelpDoc이 null 입니다. + /// + [pbr::OriginalName("CraftHelpDocIsNull")] CraftHelpDocIsNull = 36002, + /// + /// CraftDoc이 null 입니다. + /// + [pbr::OriginalName("CraftDocIsNull")] CraftDocIsNull = 36003, + /// + /// Crafting 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("CraftingMetaDataNotFound")] CraftingMetaDataNotFound = 36004, + /// + /// 제작이 완료되지 않았습니다. + /// + [pbr::OriginalName("CraftingNotFinish")] CraftingNotFinish = 36005, + /// + /// 제작중이지 않은 제작대 입니다. + /// + [pbr::OriginalName("CraftingNotCraftingAnchor")] CraftingNotCraftingAnchor = 36006, + /// + /// 제작대가 이미 사용중입니다. + /// + [pbr::OriginalName("CraftingAlreadyCrafting")] CraftingAlreadyCrafting = 36007, + /// + /// 제작대 프랍이 배치되어 있지 않습니다. + /// + [pbr::OriginalName("CraftingAnchorIsNotPlaced")] CraftingAnchorIsNotPlaced = 36008, + /// + /// 제작대 레시피가 등록되어 있지 않습니다. + /// + [pbr::OriginalName("CraftingRecipeIsNotRegister")] CraftingRecipeIsNotRegister = 36009, + /// + /// 레시피와 해당하지 않는 제작대 입니다. + /// + [pbr::OriginalName("CraftingAnchorIsNotMatchWithRecipe")] CraftingAnchorIsNotMatchWithRecipe = 36010, + /// + /// 도움 줄수 있는 횟수가 초과했습니다. + /// + [pbr::OriginalName("CraftingHelpCountOver")] CraftingHelpCountOver = 36011, + /// + /// 같은 유저에게 도움줄 횟수가 초과했습니다. + /// + [pbr::OriginalName("CraftingHelpSameUserCountOver")] CraftingHelpSameUserCountOver = 36012, + /// + /// 도움 받을수 있는 횟수가 초과했습니다. + /// + [pbr::OriginalName("CraftingHelpReceivedCountOver")] CraftingHelpReceivedCountOver = 36013, + /// + /// CraftHelpDoc이 비어있습니다. + /// + [pbr::OriginalName("CraftHelpDocIsEmpty")] CraftHelpDocIsEmpty = 36014, + /// + /// CraftDoc이 비어있습니다. + /// + [pbr::OriginalName("CraftDocIsEmpty")] CraftDocIsEmpty = 36015, + /// + /// 이미 제작이 완료되어 도움을 줄수 없습니다. + /// + [pbr::OriginalName("CraftingAlreadyFinish")] CraftingAlreadyFinish = 36016, + /// + /// 제작대 레시피가 이미 등록되어 있습니다. + /// + [pbr::OriginalName("CraftingRecipeIsAlreadyRegister")] CraftingRecipeIsAlreadyRegister = 36017, + /// + /// CraftDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CraftDocException")] CraftDocException = 36018, + /// + /// CraftHelpDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CraftHelpDocException")] CraftHelpDocException = 36019, + /// + /// CraftRecipeDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("CraftRecipeDocException")] CraftRecipeDocException = 36020, + /// + /// 잘못된 제작 갯수의 요청입니다. + /// + [pbr::OriginalName("CraftInvalidRequestCount")] CraftInvalidRequestCount = 36021, + /// + ///============================================================================================= + /// UGQ 관련 오류 : 37000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("UgqApiServerRequestFailed")] UgqApiServerRequestFailed = 37001, + /// + /// API Server와의 통신 후 객체 변환 중 오류발생 + /// + [pbr::OriginalName("UgqApiServerConvertToObjectFailed")] UgqApiServerConvertToObjectFailed = 37002, + /// + /// API Server검색 카테고리가 유효하지 않습니다. + /// + [pbr::OriginalName("UgqApiServerInvaildSearchCategoryType")] UgqApiServerInvaildSearchCategoryType = 37003, + /// + /// ugc 신고하기의 내용 길이가 맞지 않습니다. + /// + [pbr::OriginalName("UgqReportInvalidTextLength")] UgqReportInvalidTextLength = 37004, + /// + /// UGQ 메타 정보가 없습니다. + /// + [pbr::OriginalName("UgqQuestMetaNotExist")] UgqQuestMetaNotExist = 37005, + /// + /// UGQ 재화를 차감하기 위한 request 요청이 실패 + /// + [pbr::OriginalName("UgqBeginCreatorPointFail")] UgqBeginCreatorPointFail = 37006, + /// + /// shutdown 된 Ugq Revision 입니다. + /// + [pbr::OriginalName("UgqQuestShutdowned")] UgqQuestShutdowned = 37007, + /// + /// 이미 Test Complete 한 단계입니다. + /// + [pbr::OriginalName("UgqTestQuestAlreadyCompleted")] UgqTestQuestAlreadyCompleted = 37008, + /// + /// 아이템 사용해서 UGQ를 재 할당 받으려고 했으나, 해당 퀘스트에 대해서 완료한 기록이 없어서 에러 + /// + [pbr::OriginalName("UgqReassignUsingItemErrorCauseQuestNotComplete")] UgqReassignUsingItemErrorCauseQuestNotComplete = 37009, + /// + /// 아이템 사용해서 UGQ를 재 할당 받으려고 했으나, 이미 진행중인 퀘스트 존재 + /// + [pbr::OriginalName("UgqReassignUsingItemErrorCauseQuestAlreadyExist")] UgqReassignUsingItemErrorCauseQuestAlreadyExist = 37010, + /// + /// 아이템 사용해서 UGQ를 재 할당 받으려고 했으나, 새로운 리비전이 업데이트 되었음 + /// + [pbr::OriginalName("UgqReassignUsingItemErrorCauseNewRevisionUpdated")] UgqReassignUsingItemErrorCauseNewRevisionUpdated = 37011, + /// + /// UGQ데이터의 리비전 정보가 유효하지 않습니다. + /// + [pbr::OriginalName("UgqQuestDataInvalidRevision")] UgqQuestDataInvalidRevision = 37012, + /// + /// UGQ state가 유효하지 않아 포기할수 없다. + /// + [pbr::OriginalName("UgqAbortCannotCauseInvalidState")] UgqAbortCannotCauseInvalidState = 37013, + /// + /// UGQ Meta 생성 클래스가 존재 하지 않습니다. + /// + [pbr::OriginalName("UgqMetaGeneratorNotExist")] UgqMetaGeneratorNotExist = 37014, + /// + /// UGQ Revision이 업데이트가 되었습니다. + /// + [pbr::OriginalName("UgqQuestDataRevisionUpdated")] UgqQuestDataRevisionUpdated = 37015, + /// + /// UGQ 최신 리비전은 요청한 리비전보다 작을 수 없습니다. + /// + [pbr::OriginalName("UgqRevisionCannotSmallerThanRequestedRevision")] UgqRevisionCannotSmallerThanRequestedRevision = 37016, + /// + /// UGQ 리비전의 상태가 Live가 아닙니다. + /// + [pbr::OriginalName("UgqRevisionStateNotLive")] UgqRevisionStateNotLive = 37017, + /// + /// UGQ 리비전의 상태가 Live 혹은 Test 상태인경우만 ugq 데이터 호출이 가능 + /// + [pbr::OriginalName("UgqRevisionStateOnlyLivenAndTest")] UgqRevisionStateOnlyLivenAndTest = 37018, + /// + /// api 서버를 못찾을 경우 + /// + [pbr::OriginalName("UgqApiServerHttpRequestException")] UgqApiServerHttpRequestException = 37019, + /// + /// 리비전이 변경됐습니다. + /// + [pbr::OriginalName("UgqQuestRevisionChanged")] UgqQuestRevisionChanged = 37020, + /// + /// 본인 소유의 ugq는 본인이 받을수 없습니다. + /// + [pbr::OriginalName("UgqAssignCannotOwnedQuest")] UgqAssignCannotOwnedQuest = 37021, + /// + /// 이미 이전 리비전의 퀘스트를 소유중입니다. + /// + [pbr::OriginalName("UgqAlreadyOwnedOldRevisionQuest")] UgqAlreadyOwnedOldRevisionQuest = 37022, + /// + ///============================================================================================= + /// 시즌 패스 관련 오류 : 38000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("SeasonPassDocIsNull")] SeasonPassDocIsNull = 38001, + /// + /// SeasonPass 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("SeasonPassMetaDataNotFound")] SeasonPassMetaDataNotFound = 38002, + /// + /// SeasonPassReward 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("SeasonPassRewardMetaDataNotFound")] SeasonPassRewardMetaDataNotFound = 38003, + /// + /// 최대 등급에 도달해 더이상 경험치 획득할수 없습니다. + /// + [pbr::OriginalName("SeasonPassMaxGrade")] SeasonPassMaxGrade = 38004, + /// + /// 시즌 패스 기간이 아닙니다. + /// + [pbr::OriginalName("SeasonPassNotAblePeriod")] SeasonPassNotAblePeriod = 38005, + /// + /// 유료 시즌 패스를 이미 구입 했습니다. + /// + [pbr::OriginalName("SeasonPassAlreadyBuyCharged")] SeasonPassAlreadyBuyCharged = 38006, + /// + /// 이미 수령한 보상입니다. + /// + [pbr::OriginalName("SeasonPassAlreadyTakenReward")] SeasonPassAlreadyTakenReward = 38007, + /// + /// 해당 등급에 도달하지 못한 보상입니다. + /// + [pbr::OriginalName("SeasonPassNotEnoughGrade")] SeasonPassNotEnoughGrade = 38008, + /// + /// 해당 보상은 유료 시즌 패스가 필요합니다. + /// + [pbr::OriginalName("SeasonPassNeedChargedPass")] SeasonPassNeedChargedPass = 38009, + /// + /// 경험치 획득이 양수가 아닙니다. + /// + [pbr::OriginalName("SeasonPassInvalidExp")] SeasonPassInvalidExp = 38010, + /// + /// SeasonPassDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("SeasonPassDocException")] SeasonPassDocException = 38011, + /// + ///============================================================================================= + /// 랜드 경매 관련 오류 : 39000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LandAuctionRedisCacheSetFailed")] LandAuctionRedisCacheSetFailed = 39001, + /// + /// LandAuction 객체를 찾을 수 없습니다. + /// + [pbr::OriginalName("LandAuctionNotFound")] LandAuctionNotFound = 39002, + /// + /// LandAuction의 Auction Number 오류 입니다. + /// + [pbr::OriginalName("LandAuctionInvalidAuctionNumber")] LandAuctionInvalidAuctionNumber = 39003, + /// + /// LandAuction 입찰 금전 타입 오류 입니다. + /// + [pbr::OriginalName("LandAuctionBidCurrencyTypeInvalid")] LandAuctionBidCurrencyTypeInvalid = 30004, + /// + /// LandAuction 입찰금이 부족 합니다. + /// + [pbr::OriginalName("LandAuctionBidPriceNotEnough")] LandAuctionBidPriceNotEnough = 39005, + /// + /// LandAuction 캐시 정보가 Redis에 없습니다. + /// + [pbr::OriginalName("LandAuctionEmptyCacheInRedis")] LandAuctionEmptyCacheInRedis = 39006, + /// + /// LandAuctionRegistryDoc에 입력되어 있는 정보 오류 입니다. + /// + [pbr::OriginalName("LandAuctionRegistryInfoInvalid")] LandAuctionRegistryInfoInvalid = 39007, + /// + /// LandAuction가 시작 상태가 아닙니다. + /// + [pbr::OriginalName("LandAuctionNotStarted")] LandAuctionNotStarted = 39008, + /// + /// LandAuction 관련 Cache 와 DB 정보가 일치하지 않습니다. + /// + [pbr::OriginalName("LandAuctionMismatchBetweenCacheAndDb")] LandAuctionMismatchBetweenCacheAndDb = 39009, + /// + /// LandAuction가 이미 시작되었습니다. + /// + [pbr::OriginalName("LandAuctionAlreadyStarted")] LandAuctionAlreadyStarted = 39010, + /// + /// LandAuction 관련 Land 메타 정보에 LandItem 설정이 안되어 있습니다. + /// + [pbr::OriginalName("LandAuctionLandItemNotSet")] LandAuctionLandItemNotSet = 39011, + /// + /// LandAuction 관련 Land 메타 정보에 EditorType 오류 입니다. + /// + [pbr::OriginalName("LandAuctionEditorTypeInvalid")] LandAuctionEditorTypeInvalid = 39012, + /// + /// LandAuction 관련 이미 종료된 랜드 경매 입니다. + /// + [pbr::OriginalName("LandAuctionAlreadyEnded")] LandAuctionAlreadyEnded = 39013, + /// + /// LandAuction 관련 HighestBidUserAttrib 오류 입니다. + /// + [pbr::OriginalName("LandAuctionHighestBidUserAttribError")] LandAuctionHighestBidUserAttribError = 39014, + /// + /// LandAuction 관련 RefundBidPriceAttribError 오류 입니다. + /// + [pbr::OriginalName("LandAuctionRefundBidPriceAttribError")] LandAuctionRefundBidPriceAttribError = 39015, + /// + /// LandAuction 관련 현재 입찰자 RefundBidPriceAttribError 오류 입니다. + /// + [pbr::OriginalName("LandAuctionBidderRefundBidPriceAttribError")] LandAuctionBidderRefundBidPriceAttribError = 39016, + /// + ///============================================================================================= + /// Meta 데이터 오류 : 40000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GameConfigMetaDataNotFound")] GameConfigMetaDataNotFound = 40001, + /// + /// AttributeDefineition 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("AttributeDefineitionMetaDataNotFound")] AttributeDefineitionMetaDataNotFound = 40002, + /// + /// Requirement 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("RequirementMetaDataNotFound")] RequirementMetaDataNotFound = 40003, + /// + /// SystemMail 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("SystemMailMetaDataNotFound")] SystemMailMetaDataNotFound = 40004, + /// + /// Interior 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("InteriorMetaDataNotFound")] InteriorMetaDataNotFound = 40005, + /// + /// PropGroup 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("PropGroupMetaDataNotFound")] PropGroupMetaDataNotFound = 40006, + /// + ///============================================================================================= + /// Ai Chat 서버 오류 : 41000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("AiChatServerStart")] AiChatServerStart = 41001, + /// + /// AI Chat 서버 요청이 실패했습니다. + /// + [pbr::OriginalName("AiChatServerReqFailed")] AiChatServerReqFailed = 41002, + /// + /// Request가 잘못되었습니다. + /// + [pbr::OriginalName("AiChatServerBadrequest")] AiChatServerBadrequest = 41003, + /// + /// + [pbr::OriginalName("AiChatServerForbidden")] AiChatServerForbidden = 41004, + /// + /// 인증되지 않은 사용자입니다. + /// + [pbr::OriginalName("AiChatServerUnauthorized")] AiChatServerUnauthorized = 41005, + /// + /// 사용자를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerUserNotFound")] AiChatServerUserNotFound = 41006, + /// + /// 캐릭터를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerCharacterNotFound")] AiChatServerCharacterNotFound = 41007, + /// + /// 리액션를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerReactionNotFound")] AiChatServerReactionNotFound = 41008, + /// + /// 예시문구를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerExampleDialongNotFound")] AiChatServerExampleDialongNotFound = 41009, + /// + /// 세션을 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerSessionNotFound")] AiChatServerSessionNotFound = 41010, + /// + /// 모델을 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerModelNotFound")] AiChatServerModelNotFound = 41011, + /// + /// 포인트가 충분하지 않습니다. + /// + [pbr::OriginalName("AiChatServerNotEnoughPoint")] AiChatServerNotEnoughPoint = 41012, + /// + /// 인자가 잘못되었습니다. + /// + [pbr::OriginalName("AiChatServerInvalidParameters")] AiChatServerInvalidParameters = 41013, + /// + /// 세션 리미트를 초과하였습니다. + /// + [pbr::OriginalName("AiChatServerSessionLimitExceeded")] AiChatServerSessionLimitExceeded = 41014, + /// + /// 서명이 잘못되었습니다. + /// + [pbr::OriginalName("AiChatServerInvalidSignature")] AiChatServerInvalidSignature = 41015, + /// + /// 유저가 이미 존재합니다. + /// + [pbr::OriginalName("AiChatServerUserAlreadyExists")] AiChatServerUserAlreadyExists = 41016, + /// + /// 삭제에 실패했습니다. + /// + [pbr::OriginalName("AiChatServerRemoveFailed")] AiChatServerRemoveFailed = 41017, + /// + /// 중복된 Guid입니다. + /// + [pbr::OriginalName("AiChatServerDuplicateGuid")] AiChatServerDuplicateGuid = 41018, + /// + /// 중복된 Id입니다. + /// + [pbr::OriginalName("AiChatServerDuplicateId")] AiChatServerDuplicateId = 41019, + /// + /// 채팅을 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerChatNotFound")] AiChatServerChatNotFound = 41020, + /// + /// JWT 사용 기간이 만료 되었습니다. + /// + [pbr::OriginalName("AiChatServerTokenExpired")] AiChatServerTokenExpired = 41021, + /// + /// 서버 내부 오류입니다. + /// + [pbr::OriginalName("AiChatServerInternalServer")] AiChatServerInternalServer = 41022, + /// + /// 잘못된 메시지입니다. + /// + [pbr::OriginalName("AiChatServerInvalidMessage")] AiChatServerInvalidMessage = 41023, + /// + /// 유저만 사용 가능한 API 입니다. 현재 JWT 의 role 이 user가 아닙니다. + /// + [pbr::OriginalName("AiChatServerUserOnlyFeature")] AiChatServerUserOnlyFeature = 41024, + /// + /// 해당 API에 접근 할 권한이 없습니다. 해당 리소스의 소유자 또는 운영자만 접근 가능합니다. + /// + [pbr::OriginalName("AiChatServerOwnershipError")] AiChatServerOwnershipError = 41025, + /// + /// 오더를 찾지 못했습니다. + /// + [pbr::OriginalName("AiChatServerChargeOrderNotFoundError")] AiChatServerChargeOrderNotFoundError = 41026, + /// + /// 클라이언트용 Ai Chat Error Code EndPoint + /// + [pbr::OriginalName("AiChatServerEnd")] AiChatServerEnd = 41100, + /// + /// 포인트 충전 재시도 + /// + [pbr::OriginalName("AiChatServerRetryChargePoint")] AiChatServerRetryChargePoint = 41101, + /// + /// Aichat 비활성화 상태입니다. + /// + [pbr::OriginalName("AiChatServerInactive")] AiChatServerInactive = 41102, + /// + /// AiChatDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("AiChatDocException")] AiChatDocException = 41103, + /// + ///============================================================================================= + /// Npc State 관련 오류 + ///============================================================================================= + /// + [pbr::OriginalName("NpcIsBusy")] NpcIsBusy = 42001, + /// + ///============================================================================================= + /// 칼리움 컨버터 관련 오류 : 43000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LackOfDailyCalium")] LackOfDailyCalium = 43001, + /// + /// 전체 변환 제공 Calium 이 부족합니다. + /// + [pbr::OriginalName("LackOfTotalCalium")] LackOfTotalCalium = 43002, + /// + /// Calium 변환에 필요한 재화가 부족합니다. + /// + [pbr::OriginalName("LackOfCommissionCurrency")] LackOfCommissionCurrency = 43003, + /// + /// 인벤토리에 요청한 변환 Material 수량이 부족합니다. + /// + [pbr::OriginalName("LackOfCommissionMaterials")] LackOfCommissionMaterials = 43004, + /// + /// 입력한 SlotId 가 잘못되었습니다. + /// + [pbr::OriginalName("InvalidMaterialSlotId")] InvalidMaterialSlotId = 43005, + /// + /// Calium Data 로딩에 실패했습니다. + /// + [pbr::OriginalName("FailToLoadCalium")] FailToLoadCalium = 43006, + /// + /// Calium Data 저장에 실패했습니다. + /// + [pbr::OriginalName("FailToSaveCaliumDynamo")] FailToSaveCaliumDynamo = 43007, + /// + ///============================================================================================= + /// 칼리움 교환소 관련 오류 : 43050 ~ + ///============================================================================================= + /// + [pbr::OriginalName("LackOfConvertCalium")] LackOfConvertCalium = 43051, + /// + ///============================================================================================= + /// Web3 관련 오류 : 43100 ~ + ///============================================================================================= + /// + [pbr::OriginalName("GetFailEchoSystemResponse")] GetFailEchoSystemResponse = 43100, + /// + /// EchoSystem 데이터 응답 오류 ( HttpError ) + /// + [pbr::OriginalName("FailToGetEchoSystemHttpError")] FailToGetEchoSystemHttpError = 43101, + /// + /// EchoSystem 데이터 응답 오류 ( 응답값 : null ) + /// + [pbr::OriginalName("FailToGetEchoSystemMessageNull")] FailToGetEchoSystemMessageNull = 43102, + /// + /// EchoSystem 데이터 응답 오류 ( Http 예외 발생 ) + /// + [pbr::OriginalName("FailToGetEchoSystemException")] FailToGetEchoSystemException = 43103, + /// + /// EchoSystem 데이터 전송 오류 + /// + [pbr::OriginalName("FailToSendEchoSystem")] FailToSendEchoSystem = 43104, + /// + /// EchoSystem Rollup Data 획득 실패 + /// + [pbr::OriginalName("FailToGetEchoSystemRollUp")] FailToGetEchoSystemRollUp = 43105, + /// + ///============================================================================================= + /// 서비스 관련 오류 + ///============================================================================================= + /// + [pbr::OriginalName("AccountLoginBlockEnable")] AccountLoginBlockEnable = 50001, + /// + ///============================================================================================= + /// 인스턴스룸 관련 오류 : 51000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("InstanceRoomException")] InstanceRoomException = 51001, + /// + /// 인스턴스 룸 추가 데이터 저장 오류입니다. + /// + [pbr::OriginalName("InstanceRoomCannotWriteExtraInfo")] InstanceRoomCannotWriteExtraInfo = 51002, + /// + /// 인스턴스 룸을 위한 시즌패스 구매를 하지 않았습니다. + /// + [pbr::OriginalName("InstanceRoomNotChargedSeasonPass")] InstanceRoomNotChargedSeasonPass = 51003, + /// + /// 인스턴스 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("InstanceMetaDataNotFound")] InstanceMetaDataNotFound = 51004, + /// + /// 인스턴스 메타 데이타 OverLimit 가 잘못되었습니다. + /// + [pbr::OriginalName("InstanceMetaDataOverLimitWrong")] InstanceMetaDataOverLimitWrong = 51005, + /// + /// AccessType 오류 입니다. + /// + [pbr::OriginalName("InstanceAccessTypeInvalid")] InstanceAccessTypeInvalid = 51006, + /// + /// 인스턴스 입장에 필요한 아이템이 충분하지 않습니다. + /// + [pbr::OriginalName("InstanceAccessItemNotEnough")] InstanceAccessItemNotEnough = 51007, + /// + /// 인스턴스 입장에 필요한 시즌패스를 구매 하지 않았습니다. + /// + [pbr::OriginalName("InstanceAccessSeasonPassNotCharged")] InstanceAccessSeasonPassNotCharged = 51008, + /// + /// 인스턴스 룸이 가득 찼습니다. + /// + [pbr::OriginalName("InstanceRoomIsFull")] InstanceRoomIsFull = 51009, + /// + /// 인스턴스 룸 Id 가 중복 되었습니다. + /// + [pbr::OriginalName("InstanceRoomIdDuplicated")] InstanceRoomIdDuplicated = 51010, + /// + /// 인스턴스 룸이 레디스에 존재하지 않습니다. + /// + [pbr::OriginalName("InstanceRoomNotExistAtRedis")] InstanceRoomNotExistAtRedis = 51011, + /// + ///============================================================================================= + /// Task Reservation 관련 오류 : 52000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TaskReservationDocIsNull")] TaskReservationDocIsNull = 52001, + /// + ///============================================================================================= + /// Billing 관련 오류. Web api : 53000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BillingGetPurchaseInfoFailed")] BillingGetPurchaseInfoFailed = 53001, + /// + /// billing 상태 업데이트 실패 입니다. + /// + [pbr::OriginalName("BillingUpdateStateFailed")] BillingUpdateStateFailed = 53002, + /// + /// billing 확인되지 않은 상태 입니다. + /// + [pbr::OriginalName("BillingInvalidStateType")] BillingInvalidStateType = 53003, + /// + /// billing productMetaId의 타입 변환에 실패했습니다. + /// + [pbr::OriginalName("BillingFailedParseProductMetaIdType")] BillingFailedParseProductMetaIdType = 53004, + /// + /// billing 정의되어 있지 않은 stateType입니다. + /// + [pbr::OriginalName("BillingStateTypeInvalid")] BillingStateTypeInvalid = 53005, + /// + /// billing 변환 할수 없는 stateType입니다. + /// + [pbr::OriginalName("BillingStateTypeCantBeChanged")] BillingStateTypeCantBeChanged = 53006, + /// + /// billing 환불 처리중 입니다. + /// + [pbr::OriginalName("BillingStateTypeRefund")] BillingStateTypeRefund = 53007, + /// + /// billing 환불 처리가 완료된 상품입니다. + /// + [pbr::OriginalName("BillingStateTypeRefundComplete")] BillingStateTypeRefundComplete = 53008, + /// + ///============================================================================================= + /// 이동 관련 오류 : 54000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("TaxiMetaDataNotFound")] TaxiMetaDataNotFound = 54001, + /// + /// TaxiType 오류 입니다. + /// + [pbr::OriginalName("TaxiTypeInvalid")] TaxiTypeInvalid = 54002, + /// + /// 워프 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("WarpMetaDataNotFound")] WarpMetaDataNotFound = 54003, + /// + /// WarpTyoe 오류 입니다. + /// + [pbr::OriginalName("WarpTypeInvalid")] WarpTypeInvalid = 54004, + /// + ///============================================================================================= + /// 렌탈 관련 오류 : 55000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("RentalNotFound")] RentalNotFound = 55001, + /// + /// RentalDoc 로딩중에 중복된 미니맵 마커가 발견되었습니다. + /// + [pbr::OriginalName("RentalDocLoadDuplicatedRental")] RentalDocLoadDuplicatedRental = 55002, + /// + /// RentalDoc이 null 입니다. + /// + [pbr::OriginalName("RentalDocIsNull")] RentalDocIsNull = 55003, + /// + /// 렌탈이 불가능한 랜드 입니다. + /// + [pbr::OriginalName("RentalNotAvailableLand")] RentalNotAvailableLand = 55004, + /// + /// 렌탈 주소가 유효하지 않습니다. + /// + [pbr::OriginalName("RentalAddressInvalid")] RentalAddressInvalid = 55005, + /// + /// 렌탈 주소가 비어 있지 않습니다. + /// + [pbr::OriginalName("RentalAddressIsNotEmpty")] RentalAddressIsNotEmpty = 55006, + /// + /// 렌탈비용 메타 데이터를 찾지 못했습니다. + /// + [pbr::OriginalName("RentalfeeMetaDataNotFound")] RentalfeeMetaDataNotFound = 55007, + /// + /// 렌탈 계약 정보가 변경 되었습니다. + /// + [pbr::OriginalName("RentalContractInfoUpdate")] RentalContractInfoUpdate = 55008, + /// + /// 렌탈비용이 허용치 보다 낮습니다. + /// + [pbr::OriginalName("RentalCurrencyAmountIsTooLow")] RentalCurrencyAmountIsTooLow = 56009, + /// + /// 렌탈 재화 종류 오류 입니다. + /// + [pbr::OriginalName("RentalCurrencyTypeIsWrong")] RentalCurrencyTypeIsWrong = 56010, + /// + ///============================================================================================= + /// Package 관련 오류 : 56000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("PackageLastOrderRecodeDocException")] PackageLastOrderRecodeDocException = 56001, + /// + /// PackageRepeatDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("PackageRepeatDocException")] PackageRepeatDocException = 56002, + /// + ///============================================================================================= + /// BeaconShop 관련 오류 : 57000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("BeaconShopException")] BeaconShopException = 57001, + /// + /// BeaconShop 클라이언트에서 보낸 argument값이 잘못되었습니다. + /// + [pbr::OriginalName("BeaconShopInvalidArgument")] BeaconShopInvalidArgument = 57002, + /// + /// Beacon이 랜탈마이홈에 배치되지 않았습니다. + /// + [pbr::OriginalName("BeaconShopBeaconIsNotInRentalHome")] BeaconShopBeaconIsNotInRentalHome = 57003, + /// + /// BeaconShop에 등록할 아이템을 찾지 못했습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundItem")] BeaconShopNotFoundItem = 57004, + /// + /// BeaconShop에 등록할 아이템 갯수가 부족합니다. + /// + [pbr::OriginalName("BeaconShopNotEnoughItem")] BeaconShopNotEnoughItem = 57005, + /// + /// BeaconShop에 등록할 수 없는 아이템입니다. + /// + [pbr::OriginalName("BeaconShopInvalidItemForSell")] BeaconShopInvalidItemForSell = 57006, + /// + /// BeaconShop에 등록할 아이템의 메타를 찾을수 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundMetaData")] BeaconShopNotFoundMetaData = 57007, + /// + /// BeaconShop에 판매가격이 최소값보다 낮습니다. + /// + [pbr::OriginalName("BeaconShopLowSellingPrice")] BeaconShopLowSellingPrice = 57008, + /// + /// BeaconShop에 등록 수수료가 부족합니다. + /// + [pbr::OriginalName("BeaconShopNotEnoughRegisterFee")] BeaconShopNotEnoughRegisterFee = 57009, + /// + /// BeaconShop에 등록된 슬롯이 가득 찼습니다. + /// + [pbr::OriginalName("BeaconShopSlotIsFull")] BeaconShopSlotIsFull = 57010, + /// + /// BeaconShop에 하루 등록 횟수를 넘었습니다. + /// + [pbr::OriginalName("BeaconShopOverOneDayRegisterLimit")] BeaconShopOverOneDayRegisterLimit = 57011, + /// + /// BeaconShop 생성에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedToCreate")] BeaconShopFailedToCreate = 57012, + /// + /// BeaconShop 게시판에 등록을 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedRegisterBoard")] BeaconShopFailedRegisterBoard = 57013, + /// + /// BeaconShop 게시판에서 지우지 못했습니다. + /// + [pbr::OriginalName("BeaconShopFailedDeleteBoard")] BeaconShopFailedDeleteBoard = 57014, + /// + /// BeaconShop 게시판에 해당 물품이 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundItemFromBoard")] BeaconShopNotFoundItemFromBoard = 57015, + /// + /// BeaconShopProfileDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopProfileException")] BeaconShopProfileException = 57016, + /// + /// BeaconShop 등록에 필요한 골드가 부족합니다. + /// + [pbr::OriginalName("BeaconShopNotEnoughRegisterGold")] BeaconShopNotEnoughRegisterGold = 57017, + /// + /// BeaconShop 구매시 Item Amount가 부족합니다. + /// + [pbr::OriginalName("BeaconShopLackOfItemAmount")] BeaconShopLackOfItemAmount = 57018, + /// + /// BeaconShop 게시판에 아이템 목록 가져오기에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedGetBoardItem")] BeaconShopFailedGetBoardItem = 57019, + /// + /// BeaconShopSoldRecordDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopSoldRecordException")] BeaconShopSoldRecordException = 57020, + /// + /// BeaconShop BeaconInventory 불러오기에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedReloadBeaconShopInven")] BeaconShopFailedReloadBeaconShopInven = 57021, + /// + /// BeaconShop 새로운 정보가 갱신되었습니다. + /// + [pbr::OriginalName("BeaconShopUpdateNewData")] BeaconShopUpdateNewData = 57022, + /// + /// BeaconShop db 불러오기에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedUpdateDataFromDb")] BeaconShopFailedUpdateDataFromDb = 57023, + /// + /// BeaconShop 판매 내역이 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundSoldRecords")] BeaconShopNotFoundSoldRecords = 57024, + /// + /// BeaconShopProfileDoc이 없습니다. + /// + [pbr::OriginalName("BeaconShopProfileDocIsEmpty")] BeaconShopProfileDocIsEmpty = 57025, + /// + /// BeaconShopSoldPriceDoc에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopSoldPriceException")] BeaconShopSoldPriceException = 57026, + /// + /// BeaconShop 정산할 내용이 없습니다. + /// + [pbr::OriginalName("BeaconShopNotFoundSoldPrice")] BeaconShopNotFoundSoldPrice = 57027, + /// + /// BeaconShop db에서 찾지 못하거나 update에 실패했습니다. + /// + [pbr::OriginalName("BeaconShopFailedToFindOrUpdate")] BeaconShopFailedToFindOrUpdate = 57028, + /// + /// BeaconShop db에서 Exception이 발생했습니다. + /// + [pbr::OriginalName("BeaconShopDbException")] BeaconShopDbException = 57029, + /// + /// BeaconShop에 판매가격이 최대값보다 높습니다. + /// + [pbr::OriginalName("BeaconShopOverSellingPrice")] BeaconShopOverSellingPrice = 57030, + /// + /// BeaconShop이 있는 마이홈 랜탈 기간이 곧 만료됩니다. + /// + [pbr::OriginalName("BeaconShopOverRentalSafeTime")] BeaconShopOverRentalSafeTime = 57031, + /// + /// BeaconShop에 판매 아이템의 판매가 비활성화 되었습니다. + /// + [pbr::OriginalName("BeaconShopDeactiveItemForSell")] BeaconShopDeactiveItemForSell = 57032, + /// + /// 없는 task + /// + [pbr::OriginalName("UgqInvalidTaskAction")] UgqInvalidTaskAction = 60001, + /// + /// disable 된 task + /// + [pbr::OriginalName("UgqTaskActionDisabled")] UgqTaskActionDisabled = 60002, + /// + /// 없는 dialog type + /// + [pbr::OriginalName("UgqInvalidDialogType")] UgqInvalidDialogType = 60003, + /// + /// 없는 dialog 조건 + /// + [pbr::OriginalName("UgqInvalidDialogCondition")] UgqInvalidDialogCondition = 60004, + /// + /// Validation 에러 + /// + [pbr::OriginalName("UgqValidationError")] UgqValidationError = 60005, + /// + /// 엔티티 없음 + /// + [pbr::OriginalName("UgqNullEntity")] UgqNullEntity = 60006, + /// + /// 상태 변경 실패 + /// + [pbr::OriginalName("UgqStateChangeError")] UgqStateChangeError = 60007, + /// + /// 퀘스트 슬롯 부족 + /// + [pbr::OriginalName("UgqNotEnoughQuestSlot")] UgqNotEnoughQuestSlot = 60008, + /// + /// 편집 불가 상태 + /// + [pbr::OriginalName("UgqNotAllowEdit")] UgqNotAllowEdit = 60009, + /// + /// Dialog가 Task에 없음 + /// + [pbr::OriginalName("UgqDialogIsNotInTask")] UgqDialogIsNotInTask = 60010, + /// + /// 이미지 입력 없음 + /// + [pbr::OriginalName("UgqRequireImage")] UgqRequireImage = 60011, + /// + /// 비컨 입력 필요 + /// + [pbr::OriginalName("UgqRequireBeacon")] UgqRequireBeacon = 60012, + /// + /// 하나의 비컨만 입력해야 함 + /// + [pbr::OriginalName("UgqBeaconInputError")] UgqBeaconInputError = 60013, + /// + /// 게임 DB 접근 에러 + /// + [pbr::OriginalName("UgqGameDBAccessError")] UgqGameDbaccessError = 60014, + /// + /// 내 소유 UgcNpc가 아님 + /// + [pbr::OriginalName("UgqNotOwnUgcNpc")] UgqNotOwnUgcNpc = 60015, + /// + /// 이미 계정이 존재함 + /// + [pbr::OriginalName("UgqAlreadyExistsAccount")] UgqAlreadyExistsAccount = 60016, + /// + /// 예외 발생 + /// + [pbr::OriginalName("UgqServerException")] UgqServerException = 60017, + /// + /// 유효하지 않은 웹포탈 인증 토큰 + /// + [pbr::OriginalName("UgqInvalidWebPortalToken")] UgqInvalidWebPortalToken = 60018, + /// + /// 유효하지 않은 토큰 + /// + [pbr::OriginalName("UgqInvalidToken")] UgqInvalidToken = 60019, + /// + /// 메타버스 계정이 필요함 + /// + [pbr::OriginalName("UgqRequireAccount")] UgqRequireAccount = 60020, + /// + /// 포인트 부족함 + /// + [pbr::OriginalName("UgqNotEnoughCreatorPoint")] UgqNotEnoughCreatorPoint = 60021, + /// + /// 최대 슬롯 수 제한 + /// + [pbr::OriginalName("UgqSlotLimit")] UgqSlotLimit = 60022, + /// + /// 트랜잭션 재시도 횟수 초과 + /// + [pbr::OriginalName("UgqExceedTransactionRetry")] UgqExceedTransactionRetry = 60023, + /// + /// 메타버스에 온라인 상태임 + /// + [pbr::OriginalName("UgqMetaverseOnline")] UgqMetaverseOnline = 60024, + /// + /// 인증 상태가 제거됨 + /// + [pbr::OriginalName("UgqAuthRemoved")] UgqAuthRemoved = 60025, + /// + /// 잘못된 프리셋 이미지 + /// + [pbr::OriginalName("UgqInvalidPresetImage")] UgqInvalidPresetImage = 60026, + /// + /// 재화 부족 + /// + [pbr::OriginalName("UgqNotEnoughCurrency")] UgqNotEnoughCurrency = 60027, + /// + /// 재화 사용 오류 발생 + /// + [pbr::OriginalName("UgqCurrencyError")] UgqCurrencyError = 60028, + /// + /// 이미 등급 변경 예약이 존재함 + /// + [pbr::OriginalName("UgqAlreadyExistsReserveGrade")] UgqAlreadyExistsReserveGrade = 60029, + /// + /// 유효하지 않은 예약 시간 + /// + [pbr::OriginalName("UgqInvalidReserveTime")] UgqInvalidReserveTime = 60030, + /// + /// 같은 등급으로 변경이 불가능. + /// + [pbr::OriginalName("UgqSameGradeReserve")] UgqSameGradeReserve = 60031, + /// + ///============================================================================================= + /// UGQ 관련 오류. InGame Api 오류 + ///============================================================================================= + /// + [pbr::OriginalName("UgqAlreadyBookmarked")] UgqAlreadyBookmarked = 61001, + /// + /// 북마크 안되어 있음 + /// + [pbr::OriginalName("UgqNotBookmarked")] UgqNotBookmarked = 61002, + /// + /// 이미 종아요 + /// + [pbr::OriginalName("UgqAlreadyLiked")] UgqAlreadyLiked = 61003, + /// + /// 좋아요 안되어 있음 + /// + [pbr::OriginalName("UgqNotLiked")] UgqNotLiked = 61004, + /// + /// 이미 신고함 + /// + [pbr::OriginalName("UgqAlreadyReported")] UgqAlreadyReported = 61005, + /// + /// 내 퀘스트가 아님 + /// + [pbr::OriginalName("UgqNotOwnQuest")] UgqNotOwnQuest = 61006, + /// + /// 잘못된 상태 + /// + [pbr::OriginalName("UgqInvalidState")] UgqInvalidState = 61007, + /// + ///============================================================================================= + /// NFT 관련 오류 : 62000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("NftForOwnerAllGetFailed")] NftForOwnerAllGetFailed = 62001, + /// + ///=========================================== + /// RabbitMqRpc + ///=========================================== + /// + [pbr::OriginalName("MqResponseTimeout")] MqResponseTimeout = 63001, + /// + ///============================================================================================= + /// Broker Api Server 오류: 70000 ~ + ///============================================================================================= + /// + [pbr::OriginalName("InternalServerError")] InternalServerError = 70001, + /// + /// Rdb에 장애가 발생했습니다. + /// + [pbr::OriginalName("RdbError")] RdbError = 70002, + /// + /// Dynamo에 장애가 발생했습니다. + /// + [pbr::OriginalName("DynamoError")] DynamoError = 70003, + /// + /// Api request 형식에 오류가 있습니다. + /// + [pbr::OriginalName("InvalidRequest")] InvalidRequest = 70011, + /// + /// 플래닛 id가 존재하지 않습니다. + /// + [pbr::OriginalName("PlanetIdNotFound")] PlanetIdNotFound = 71001, + /// + /// 해당 플래닛의 SecretKey가 일치하지 않습니다. + /// + [pbr::OriginalName("PlanetSecretKeyDoesNotMatched")] PlanetSecretKeyDoesNotMatched = 71002, + /// + /// 플래닛 인증 토큰에 오류가 있습니다. + /// + [pbr::OriginalName("InvalidPlanetJwt")] InvalidPlanetJwt = 71003, + /// + /// 플래닛 인증 토큰 유효기간이 만료됐습니다. + /// + [pbr::OriginalName("ExpiredPlanetJwt")] ExpiredPlanetJwt = 71004, + /// + /// 메타버스에 유저의 클라이언트 로그인되어 있습니다. 메타버스 앱을 정상 로그아웃 후 다시 시도해 주세요. + /// + [pbr::OriginalName("MetaverseClientOnConnected")] MetaverseClientOnConnected = 71201, + /// + /// 유저 인증 토큰에 오류가 있습니다. + /// + [pbr::OriginalName("InvalidUserJwt")] InvalidUserJwt = 71202, + /// + /// 유저 인증 토큰 유효기간이 만료됐습니다. + /// + [pbr::OriginalName("ExpiredUserJwt")] ExpiredUserJwt = 71203, + /// + /// 계정을 찾을 수 없습니다. + /// + [pbr::OriginalName("AccountNotFound")] AccountNotFound = 71301, + /// + /// 유저를 찾을 수 없습니다. + /// + [pbr::OriginalName("UserNotFound")] UserNotFound = 71302, + /// + /// 교환 주문 아이디가 존재하지 않습니다. + /// + [pbr::OriginalName("ExchangeOrderIdNotFound")] ExchangeOrderIdNotFound = 72001, + /// + /// 총 교환 주문 수량이 초과되었습니다. + /// + [pbr::OriginalName("ExchangeTotalOrderDailyLimitExceeded")] ExchangeTotalOrderDailyLimitExceeded = 72002, + /// + /// 유저 교환 주문 수량이 초과되었습니다. + /// + [pbr::OriginalName("ExchangeUserOrderDailyLimitExceeded")] ExchangeUserOrderDailyLimitExceeded = 72003, + /// + ///============================================================================================= + /// Bot 관련 오류. + ///============================================================================================= + /// + [pbr::OriginalName("BotPlayerIsNull")] BotPlayerIsNull = 962001, + /// + /// ClientToLoginRes가 null 입니다. + /// + [pbr::OriginalName("ClientToLoginResIsNull")] ClientToLoginResIsNull = 962002, + /// + /// ClientToLoginMessage가 null 입니다. + /// + [pbr::OriginalName("ClientToLoginMessageIsNull")] ClientToLoginMessageIsNull = 962003, + /// + /// ClientToGameRes가 null 입니다. + /// + [pbr::OriginalName("ClientToGameResIsNull")] ClientToGameResIsNull = 962004, + /// + /// ClientToGameMessage가 null 입니다. + /// + [pbr::OriginalName("ClientToGameMessageIsNull")] ClientToGameMessageIsNull = 962005, + /// + /// Server 연결 실패 + /// + [pbr::OriginalName("ConnectedToServerFail")] ConnectedToServerFail = 962006, + /// + /// 시나리오에 param 설정이 필요합니다. + /// + [pbr::OriginalName("ScenarionParamIsNull")] ScenarionParamIsNull = 962007, + /// + ///============================================================================================= + /// 전투 관련 오류. 삭제 가능성 있어서 뒷번호로 배정 + /// running mode도 아래에 배정 + ///============================================================================================= + /// + [pbr::OriginalName("BattleRoomContentsTypeOnly")] BattleRoomContentsTypeOnly = 11000001, + /// + /// 배틀인스턴스 타입이 잘못 됐습니다. + /// + [pbr::OriginalName("BattleInstanceTypeError")] BattleInstanceTypeError = 11000002, + /// + /// 이미 존재하는 인스턴스 입니다. + /// + [pbr::OriginalName("BattleInstanceInfoAlreadyExist")] BattleInstanceInfoAlreadyExist = 11000003, + /// + /// 존재하지 않는 인스턴스 입니다. + /// + [pbr::OriginalName("BattleInstanceInfoNotExist")] BattleInstanceInfoNotExist = 11000004, + /// + /// 플에이어 객체 + /// + [pbr::OriginalName("BattleInstanceJoinPlayerError")] BattleInstanceJoinPlayerError = 11000005, + /// + /// 사용 가능한 스폰 포인트가 없습니다. + /// + [pbr::OriginalName("BattleInstanceUsableSpawnPointNotExist")] BattleInstanceUsableSpawnPointNotExist = 11000006, + /// + /// 배틀 인스턴스 비활성화 상태 입니다. + /// + [pbr::OriginalName("BattleInstanceInActive")] BattleInstanceInActive = 11000007, + /// + /// 배틀 인스턴스 앵커 정보가 없습니다. + /// + [pbr::OriginalName("BattleInstanceNotExistAnchors")] BattleInstanceNotExistAnchors = 11000008, + /// + /// 배틀 인스턴스 pod 추가에 실패하였습니다. + /// + [pbr::OriginalName("BattleInstanceAddPodCombatFail")] BattleInstanceAddPodCombatFail = 11000009, + /// + /// 배틀 인스턴스 오브젝트 메타가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceObjectMetaNotExist")] BattleInstanceObjectMetaNotExist = 11000010, + /// + /// 배틀 인스턴스 오브젝트 상호 작용 가능한 시간이 아직 안됐습니다. + /// + [pbr::OriginalName("BattleInstanceObjectInteractionNotyetTime")] BattleInstanceObjectInteractionNotyetTime = 11000011, + /// + /// 배틀 인스턴스 오브젝트 정보가 없습니다. + /// + [pbr::OriginalName("BattleInstanceObjectNotExist")] BattleInstanceObjectNotExist = 11000012, + /// + /// pod combat이 이미 점유중 + /// + [pbr::OriginalName("BattleInstancePodCombatAlreadyOccupy")] BattleInstancePodCombatAlreadyOccupy = 11000013, + /// + /// 배틀 오브젝트가 활성화 되지 않았습니다. + /// + [pbr::OriginalName("BattleInstanceObjectInteractionNotActive")] BattleInstanceObjectInteractionNotActive = 11000015, + /// + /// Config Meta에 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceMetaConfigNotExistData")] BattleInstanceMetaConfigNotExistData = 11000016, + /// + /// Reward Meta에 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceMetaRewardNotExistData")] BattleInstanceMetaRewardNotExistData = 11000017, + /// + /// 픽업 포드 생성시간 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstancePickupPodGeneratedTimeNotExist")] BattleInstancePickupPodGeneratedTimeNotExist = 11000018, + /// + /// 픽업 포드 데이터가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstancePickupPodNotExistData")] BattleInstancePickupPodNotExistData = 11000019, + /// + /// 인스턴스에 유저정보가 존재하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceNotExistPlayerInfo")] BattleInstanceNotExistPlayerInfo = 11000020, + /// + /// 배틀 오브젝트 상호작용에 실패 하였습니다. + /// + [pbr::OriginalName("BattleInstanceInteractionFail")] BattleInstanceInteractionFail = 11000021, + /// + /// 픽업포드 리워드 생성에 실패 하였습니다. + /// + [pbr::OriginalName("BattleInstancePickupPodRewardAllocateError")] BattleInstancePickupPodRewardAllocateError = 11000022, + /// + /// 배틀 이벤트 정보가 없습니다. + /// + [pbr::OriginalName("BattleInstanceNotExistEventInfo")] BattleInstanceNotExistEventInfo = 11000023, + /// + /// 배틀 이벤트, 라운드가 거의다 끝나가는 시간입니다. + /// + [pbr::OriginalName("BattleInstanceClosingTime")] BattleInstanceClosingTime = 11000024, + /// + /// 배틀 인스턴스 시퀀스 파싱 에러 + /// + [pbr::OriginalName("BattleInstanceSeqParseError")] BattleInstanceSeqParseError = 11000025, + /// + /// 전투 이벤트 아이디가 유효하지 않습니다. + /// + [pbr::OriginalName("BattleInstanceEventIdInvalid")] BattleInstanceEventIdInvalid = 11000026, + /// + /// 게임모드 조인 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeJoinHandlerNotExist")] GameModeJoinHandlerNotExist = 11000100, + /// + /// 게임모드 Init 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeInitHandlerNotExist")] GameModeInitHandlerNotExist = 11000101, + /// + /// 게임모드 조인 성공 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeJoinSuccessHandlerNotExist")] GameModeJoinSuccessHandlerNotExist = 11000102, + /// + /// 게임모드 생성 실패 + /// + [pbr::OriginalName("GameModeCreateFail")] GameModeCreateFail = 11000103, + /// + /// 게임모드가 null + /// + [pbr::OriginalName("GameModeClassIsNull")] GameModeClassIsNull = 11000104, + /// + /// 게임모드가 존재 + /// + [pbr::OriginalName("GameModeAlreadyExist")] GameModeAlreadyExist = 11000105, + /// + /// 유효하지 않은 AnchorGuid 입니다. + /// + [pbr::OriginalName("GameModeInvalidAnchorGuid")] GameModeInvalidAnchorGuid = 11000106, + /// + /// 유효하지 않은 Action 입니다. + /// + [pbr::OriginalName("GameModeInvalidAction")] GameModeInvalidAction = 11000107, + /// + /// 게임모드 나가기준비 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModePreparationForLeavingHandlerNotExist")] GameModePreparationForLeavingHandlerNotExist = 11000108, + /// + /// 게임모드 나가기 핸들러가 없습니다. + /// + [pbr::OriginalName("GameModeLeaveHandlerNotExist")] GameModeLeaveHandlerNotExist = 11000109, + /// + /// 게임모드 레이스 랭크가 존재 + /// + [pbr::OriginalName("GameModeRunRankUserExist")] GameModeRunRankUserExist = 11000110, + [pbr::OriginalName("MatchCommonError")] MatchCommonError = 12000001, + [pbr::OriginalName("MatchUserNotFound")] MatchUserNotFound = 120000011, + [pbr::OriginalName("MatchUserReservedAlready")] MatchUserReservedAlready = 120000012, + [pbr::OriginalName("MatchUserNotReserved")] MatchUserNotReserved = 120000013, + [pbr::OriginalName("MatchUserGroupNotFound")] MatchUserGroupNotFound = 120000014, + [pbr::OriginalName("MatchTaskException")] MatchTaskException = 120000021, + [pbr::OriginalName("MatchServerNoResponse")] MatchServerNoResponse = 12000998, + [pbr::OriginalName("MatchError")] MatchError = 12000999, + /// + ///============================================================================================= + /// Server Metrics 오류: 99999100 ~ + ///============================================================================================= + /// + [pbr::OriginalName("ServerMetricsTriggerHandlerNotFound")] ServerMetricsTriggerHandlerNotFound = 99999101, + [pbr::OriginalName("DupLogin")] DupLogin = 1, + [pbr::OriginalName("Moving")] Moving = 2, + [pbr::OriginalName("DbError")] DbError = 3, + [pbr::OriginalName("KickFail")] KickFail = 4, + [pbr::OriginalName("NotCorrectPassword")] NotCorrectPassword = 5, + [pbr::OriginalName("NotFoundUser")] NotFoundUser = 6, + [pbr::OriginalName("NoGameServer")] NoGameServer = 7, + [pbr::OriginalName("LoginPending")] LoginPending = 8, + [pbr::OriginalName("NotImplemented")] NotImplemented = 9, + [pbr::OriginalName("NotExistSelectedCharacter")] NotExistSelectedCharacter = 10, + [pbr::OriginalName("NotExistCharacter")] NotExistCharacter = 11, + [pbr::OriginalName("ServerLogicError")] ServerLogicError = 12, + [pbr::OriginalName("NoPermissions")] NoPermissions = 14, + [pbr::OriginalName("RedisFail")] RedisFail = 15, + [pbr::OriginalName("LoginFail")] LoginFail = 1000, + [pbr::OriginalName("DuplicatedUser")] DuplicatedUser = 1001, + [pbr::OriginalName("InvalidToken")] InvalidToken = 1002, + [pbr::OriginalName("NotCorrectServer")] NotCorrectServer = 1003, + [pbr::OriginalName("Inspection")] Inspection = 1004, + [pbr::OriginalName("BlackList")] BlackList = 1005, + [pbr::OriginalName("ServerFull")] ServerFull = 1006, + [pbr::OriginalName("NotFoundServer")] NotFoundServer = 1007, + [pbr::OriginalName("NotFoundTable")] NotFoundTable = 1008, + [pbr::OriginalName("TableError")] TableError = 1009, + [pbr::OriginalName("InvalidCondition")] InvalidCondition = 1100, + [pbr::OriginalName("CharCreateFail")] CharCreateFail = 2000, + [pbr::OriginalName("CharSelectFail")] CharSelectFail = 2100, + [pbr::OriginalName("CreateRoomFail")] CreateRoomFail = 3000, + [pbr::OriginalName("JoinRoomFail")] JoinRoomFail = 3100, + [pbr::OriginalName("JoinInstanceFail")] JoinInstanceFail = 3200, + [pbr::OriginalName("NotExistInstanceTicket")] NotExistInstanceTicket = 3201, + [pbr::OriginalName("LeaveInstanceFail")] LeaveInstanceFail = 3300, + [pbr::OriginalName("NotExistInstanceRoom")] NotExistInstanceRoom = 3400, + [pbr::OriginalName("NotCorrectInstanceRoom")] NotCorrectInstanceRoom = 3401, + [pbr::OriginalName("PosIsEmpty")] PosIsEmpty = 3402, + [pbr::OriginalName("EnterMyHomeFail")] EnterMyHomeFail = 3800, + [pbr::OriginalName("LeaveMyHomeFail")] LeaveMyHomeFail = 3900, + [pbr::OriginalName("ExchangeMyHomeFail")] ExchangeMyHomeFail = 4000, + [pbr::OriginalName("NotFoundMyHomeData")] NotFoundMyHomeData = 4001, + [pbr::OriginalName("NotMyHomeOwner")] NotMyHomeOwner = 4002, + [pbr::OriginalName("AlreadyHaveMyHome")] AlreadyHaveMyHome = 4003, + [pbr::OriginalName("ExchangeMyHomePropFail")] ExchangeMyHomePropFail = 4100, + [pbr::OriginalName("ExchangeBuildingFail")] ExchangeBuildingFail = 4300, + [pbr::OriginalName("ExchangeBuildingLFPropFail")] ExchangeBuildingLfpropFail = 4400, + [pbr::OriginalName("ExchangeInstanceFail")] ExchangeInstanceFail = 4500, + [pbr::OriginalName("ExchangeSocialActionSlotFail")] ExchangeSocialActionSlotFail = 4600, + [pbr::OriginalName("NotFoundSocialActionData")] NotFoundSocialActionData = 4602, + [pbr::OriginalName("NotInSocialActionSlot")] NotInSocialActionSlot = 4603, + [pbr::OriginalName("AlreadyHaveSocialAction")] AlreadyHaveSocialAction = 4604, + [pbr::OriginalName("NotFoundBuildingData")] NotFoundBuildingData = 4652, + [pbr::OriginalName("NotFoundFloorInfo")] NotFoundFloorInfo = 4653, + [pbr::OriginalName("NotFoundIndunData")] NotFoundIndunData = 4655, + [pbr::OriginalName("EnterFittingRoomFail")] EnterFittingRoomFail = 4700, + [pbr::OriginalName("NotEntetedFittingRoom")] NotEntetedFittingRoom = 4701, + [pbr::OriginalName("MakeFailOtp")] MakeFailOtp = 4702, + [pbr::OriginalName("NotExistRoomInfoForEnter")] NotExistRoomInfoForEnter = 4703, + [pbr::OriginalName("NotExistGameServerForEnter")] NotExistGameServerForEnter = 4704, + [pbr::OriginalName("PositionSaveFail")] PositionSaveFail = 4705, + [pbr::OriginalName("ExchangeEmotionSlotFail")] ExchangeEmotionSlotFail = 4800, + [pbr::OriginalName("EmotionSlotOutOfRange")] EmotionSlotOutOfRange = 4801, + [pbr::OriginalName("NotFoundAnchorGuidInMap")] NotFoundAnchorGuidInMap = 4899, + [pbr::OriginalName("NotFoundAnchorGuid")] NotFoundAnchorGuid = 4900, + [pbr::OriginalName("PropIsUsed")] PropIsUsed = 4902, + [pbr::OriginalName("PropTypeisWrong")] PropTypeisWrong = 4903, + [pbr::OriginalName("NotFoundEmotionData")] NotFoundEmotionData = 4920, + [pbr::OriginalName("NotInEmotionSlot")] NotInEmotionSlot = 4921, + [pbr::OriginalName("CreateItemFail")] CreateItemFail = 4996, + [pbr::OriginalName("AddItemFail")] AddItemFail = 4997, + [pbr::OriginalName("DeleteItemFail")] DeleteItemFail = 4998, + [pbr::OriginalName("NoMoreAddItem")] NoMoreAddItem = 4999, + [pbr::OriginalName("NotFoundItem")] NotFoundItem = 5000, + [pbr::OriginalName("NotEnoughItem")] NotEnoughItem = 5001, + [pbr::OriginalName("InvalidSlotIndex")] InvalidSlotIndex = 5002, + [pbr::OriginalName("DuplicatedItemGuid")] DuplicatedItemGuid = 5003, + [pbr::OriginalName("NotFoundItemTableId")] NotFoundItemTableId = 5004, + [pbr::OriginalName("NotSelectedChar")] NotSelectedChar = 5005, + [pbr::OriginalName("NotFoundMap")] NotFoundMap = 5006, + [pbr::OriginalName("NotEmptySlot")] NotEmptySlot = 5007, + [pbr::OriginalName("EmptySlot")] EmptySlot = 5008, + [pbr::OriginalName("NotFoundBuffTableId")] NotFoundBuffTableId = 5009, + [pbr::OriginalName("NotFoundBuff")] NotFoundBuff = 5010, + [pbr::OriginalName("NotExistForecedMoveInfo")] NotExistForecedMoveInfo = 5011, + [pbr::OriginalName("DuplicatedNickName")] DuplicatedNickName = 5013, + [pbr::OriginalName("AlreadySetNickName")] AlreadySetNickName = 5014, + [pbr::OriginalName("DisallowedCharacters")] DisallowedCharacters = 5015, + [pbr::OriginalName("DbUpdateFailed")] DbUpdateFailed = 5016, + [pbr::OriginalName("InvalidArgument")] InvalidArgument = 5017, + [pbr::OriginalName("MailSystemError")] MailSystemError = 5030, + [pbr::OriginalName("InvalidTarget")] InvalidTarget = 5031, + [pbr::OriginalName("NotFoundMail")] NotFoundMail = 5032, + [pbr::OriginalName("EmptyItemInMail")] EmptyItemInMail = 5033, + [pbr::OriginalName("MailSendCountOver")] MailSendCountOver = 5034, + [pbr::OriginalName("ChangedNickName")] ChangedNickName = 5035, + [pbr::OriginalName("StateChangeFailed")] StateChangeFailed = 5040, + [pbr::OriginalName("NotFoundTarget")] NotFoundTarget = 5050, + [pbr::OriginalName("BlockedFromTarget")] BlockedFromTarget = 5051, + [pbr::OriginalName("LogOffTarget")] LogOffTarget = 5052, + [pbr::OriginalName("CantSendToSelf")] CantSendToSelf = 5053, + [pbr::OriginalName("BuffTypeIsWrong")] BuffTypeIsWrong = 5070, + [pbr::OriginalName("RegisterToolSlotFail")] RegisterToolSlotFail = 5080, + [pbr::OriginalName("DeregisterToolSlotFail")] DeregisterToolSlotFail = 5081, + [pbr::OriginalName("ToolSlotOutOfRange")] ToolSlotOutOfRange = 5082, + [pbr::OriginalName("NotFoundToolSlot")] NotFoundToolSlot = 5083, + [pbr::OriginalName("EmptyToolSlot")] EmptyToolSlot = 5084, + [pbr::OriginalName("FolderNameExceededLength")] FolderNameExceededLength = 5090, + [pbr::OriginalName("FolderNameAlreadyExist")] FolderNameAlreadyExist = 5091, + [pbr::OriginalName("FolderNameNotExist")] FolderNameNotExist = 5092, + [pbr::OriginalName("FolderNameAlreadyMaxHoldCount")] FolderNameAlreadyMaxHoldCount = 5093, + [pbr::OriginalName("FolderCountExceed")] FolderCountExceed = 5094, + [pbr::OriginalName("FolderCreateFail")] FolderCreateFail = 5095, + [pbr::OriginalName("FolderReNameCannotDefault")] FolderReNameCannotDefault = 5096, + [pbr::OriginalName("FolderOdertypeInvalid")] FolderOdertypeInvalid = 5097, + [pbr::OriginalName("FriendRequestNotExistInfo")] FriendRequestNotExistInfo = 5100, + [pbr::OriginalName("FriendRequestAlreadySend")] FriendRequestAlreadySend = 5101, + [pbr::OriginalName("FriendRequestAlreadyReceive")] FriendRequestAlreadyReceive = 5102, + [pbr::OriginalName("FriendRequestCantSendToSelf")] FriendRequestCantSendToSelf = 5103, + [pbr::OriginalName("FriendRequestNotExistReceivedInfo")] FriendRequestNotExistReceivedInfo = 5104, + [pbr::OriginalName("FriendRequestAlreadyFriend")] FriendRequestAlreadyFriend = 5105, + [pbr::OriginalName("InvalidType")] InvalidType = 5106, + [pbr::OriginalName("InvalidAttributeSlot")] InvalidAttributeSlot = 5107, + [pbr::OriginalName("FriendRequestCannotSendBlockUser")] FriendRequestCannotSendBlockUser = 5119, + [pbr::OriginalName("AddFriendAlreadyBlockUser")] AddFriendAlreadyBlockUser = 5120, + [pbr::OriginalName("AddFriendNotExistCharacter")] AddFriendNotExistCharacter = 5121, + [pbr::OriginalName("AddFriendAlreadyFriend")] AddFriendAlreadyFriend = 5122, + [pbr::OriginalName("AddFriendAlreadyCancelRequest")] AddFriendAlreadyCancelRequest = 5123, + [pbr::OriginalName("AddFriendAlreadyExpired")] AddFriendAlreadyExpired = 5124, + [pbr::OriginalName("FriendInfoNotExist")] FriendInfoNotExist = 5130, + [pbr::OriginalName("FriendInfoMyCountAlreadyMaxCount")] FriendInfoMyCountAlreadyMaxCount = 5131, + [pbr::OriginalName("FriendInfoOthersCountAlreadyMaxCount")] FriendInfoOthersCountAlreadyMaxCount = 5132, + [pbr::OriginalName("FriendInfoOffline")] FriendInfoOffline = 5133, + [pbr::OriginalName("FriendInfoAlreadyExist")] FriendInfoAlreadyExist = 5134, + [pbr::OriginalName("FriendInviteMyPosIsNotMyHome")] FriendInviteMyPosIsNotMyHome = 5140, + [pbr::OriginalName("FriendInviteDontDisturbState")] FriendInviteDontDisturbState = 5141, + [pbr::OriginalName("FriendInviteExpireTimeRemain")] FriendInviteExpireTimeRemain = 5142, + [pbr::OriginalName("FriendInviteWaitingOtherInvite")] FriendInviteWaitingOtherInvite = 5143, + [pbr::OriginalName("FriendInviteAlreadyExpire")] FriendInviteAlreadyExpire = 5144, + [pbr::OriginalName("FriendKickMyPosIsNotMyHome")] FriendKickMyPosIsNotMyHome = 5145, + [pbr::OriginalName("FriendKickMemberNotExist")] FriendKickMemberNotExist = 5146, + [pbr::OriginalName("FriendIsInAnotherMyhome")] FriendIsInAnotherMyhome = 5147, + [pbr::OriginalName("BlockUserMaxCount")] BlockUserMaxCount = 5150, + [pbr::OriginalName("BlockUserAlreadyBlock")] BlockUserAlreadyBlock = 5151, + [pbr::OriginalName("BlockUserCannotSendMailTo")] BlockUserCannotSendMailTo = 5152, + [pbr::OriginalName("BlockedByOther")] BlockedByOther = 5153, + [pbr::OriginalName("BlockUserCannotWhisperTo")] BlockUserCannotWhisperTo = 5154, + [pbr::OriginalName("BlockInfoEmpty")] BlockInfoEmpty = 5155, + [pbr::OriginalName("BlockUserCannotInviteParty")] BlockUserCannotInviteParty = 5156, + [pbr::OriginalName("CartSellTypeMissMatchWithTable")] CartSellTypeMissMatchWithTable = 5200, + [pbr::OriginalName("CartFullStackofItem")] CartFullStackofItem = 5201, + [pbr::OriginalName("CartisFull")] CartisFull = 5202, + [pbr::OriginalName("BanNickName")] BanNickName = 5203, + [pbr::OriginalName("CurrencyNotFoundData")] CurrencyNotFoundData = 5400, + [pbr::OriginalName("CurrencyNotEnough")] CurrencyNotEnough = 5401, + [pbr::OriginalName("CurrencyInvalidValue")] CurrencyInvalidValue = 5402, + [pbr::OriginalName("StartBuffFailed")] StartBuffFailed = 5500, + [pbr::OriginalName("StopBuffFailed")] StopBuffFailed = 5501, + [pbr::OriginalName("NotFoundNickName")] NotFoundNickName = 5600, + [pbr::OriginalName("NotFoundTattooAttributeData")] NotFoundTattooAttributeData = 5800, + [pbr::OriginalName("NotFoundShopId")] NotFoundShopId = 5900, + [pbr::OriginalName("TimeOverForPurchase")] TimeOverForPurchase = 5901, + [pbr::OriginalName("NotEnoughAttribute")] NotEnoughAttribute = 5902, + [pbr::OriginalName("InvalidGender")] InvalidGender = 5903, + [pbr::OriginalName("IsEquipItem")] IsEquipItem = 5904, + [pbr::OriginalName("InvalidItem")] InvalidItem = 5905, + [pbr::OriginalName("AlreadyRegistered")] AlreadyRegistered = 5906, + [pbr::OriginalName("NotFoundShopItem")] NotFoundShopItem = 5907, + [pbr::OriginalName("NotEnoughShopItem")] NotEnoughShopItem = 5908, + [pbr::OriginalName("InvalidItemCount")] InvalidItemCount = 5909, + [pbr::OriginalName("InventoryFull")] InventoryFull = 5910, + [pbr::OriginalName("NotFoundProductId")] NotFoundProductId = 5911, + [pbr::OriginalName("RandomBoxItemDataInvalid")] RandomBoxItemDataInvalid = 6100, + [pbr::OriginalName("NotExistGachaData")] NotExistGachaData = 6101, +} + +#endregion + +#region Messages +/// +/// 각종 결과 전달용 정보 - kangms +/// +[global::System.SerializableAttribute] +public sealed partial class Result : pb::IMessage +#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage +#endif +{ + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Result()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::DefineResultReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Result() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Result(Result other) : this() { + errorCode_ = other.errorCode_; + resultString_ = other.resultString_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Result Clone() { + return new Result(this); + } + + /// Field number for the "errorCode" field. + public const int ErrorCodeFieldNumber = 1; + private global::ServerErrorCode errorCode_ = global::ServerErrorCode.Success; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::ServerErrorCode ErrorCode { + get { return errorCode_; } + set { + errorCode_ = value; + } + } + + /// Field number for the "resultString" field. + public const int ResultStringFieldNumber = 2; + private string resultString_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string ResultString { + get { return resultString_; } + set { + resultString_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as Result); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(Result other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (ErrorCode != other.ErrorCode) return false; + if (ResultString != other.ResultString) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (ErrorCode != global::ServerErrorCode.Success) hash ^= ErrorCode.GetHashCode(); + if (ResultString.Length != 0) hash ^= ResultString.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (ErrorCode != global::ServerErrorCode.Success) { + output.WriteRawTag(8); + output.WriteEnum((int) ErrorCode); + } + if (ResultString.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ResultString); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (ErrorCode != global::ServerErrorCode.Success) { + output.WriteRawTag(8); + output.WriteEnum((int) ErrorCode); + } + if (ResultString.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ResultString); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (ErrorCode != global::ServerErrorCode.Success) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ErrorCode); + } + if (ResultString.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ResultString); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(Result other) { + if (other == null) { + return; + } + if (other.ErrorCode != global::ServerErrorCode.Success) { + ErrorCode = other.ErrorCode; + } + if (other.ResultString.Length != 0) { + ResultString = other.ResultString; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + ErrorCode = (global::ServerErrorCode) input.ReadEnum(); + break; + } + case 18: { + ResultString = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + ErrorCode = (global::ServerErrorCode) input.ReadEnum(); + break; + } + case 18: { + ResultString = input.ReadString(); + break; + } + } + } + } + #endif + +} + +#endregion + + +#endregion Designer generated code diff --git a/Protocol/pidl_build.sh b/Protocol/pidl_build.sh new file mode 100644 index 0000000..300e59f --- /dev/null +++ b/Protocol/pidl_build.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# PIDL 실행 파일과 출력 경로 설정 +PIDL_EXE="../../ThirdPartyPackages/ProudNet/1.9.58941/ProudNet/util/PIDL.exe" +PIDL_OUT="../Protocol/out-RMI/" + +# 출력 디렉토리의 기존 파일들 삭제 +if [ -d "$PIDL_OUT" ]; then + rm -rf "$PIDL_OUT"/* + if [ $? -ne 0 ]; then + echo "Failed to DEL from RMI out path !!! - Path : [$PIDL_OUT] !!!" + exit $? + fi +else + # 디렉토리가 없으면 생성 + mkdir -p "$PIDL_OUT" +fi + +# mono를 사용하여 PIDL.exe 실행 +mono "$PIDL_EXE" -cs ./PIDL/ClientToServer.PIDL -outdir "$PIDL_OUT" + +# mono 실행 결과 확인 +if [ $? -ne 0 ]; then + echo "ERROR !!! - PIDL Generation Failed - ErrorCode : $?" + exit $? +fi + +# PIDL 파일들을 대상 디렉토리로 복사 +cp ./PIDL/*.PIDL ../../../Common/MS2Packet/PIDL/ + +# 복사 결과 확인 +if [ $? -ne 0 ]; then + echo "ERROR !!! - Failed to copy PIDL files - ErrorCode : $?" + exit $? +fi + +# 성공적으로 완료 +echo "PIDL Generation completed successfully" +exit 0 diff --git a/Protocol/proto/ClientToGame.proto b/Protocol/proto/ClientToGame.proto index 244b12a..caec31b 100644 --- a/Protocol/proto/ClientToGame.proto +++ b/Protocol/proto/ClientToGame.proto @@ -1,4 +1,4 @@ -syntax = "proto3"; +syntax = "proto3"; import "google/protobuf/timestamp.proto"; import "Define_Common.proto"; @@ -11,7 +11,7 @@ import "Game_Define.proto"; message ClientToGameReq { //============================================================================================= - // REQ Ŷ + // REQ 패킷 정의 //============================================================================================= message LoginReq { @@ -23,7 +23,7 @@ message ClientToGameReq } message ChatReq { - ChatType type =1; // 1 2 3 ü 4 ӼӸ + ChatType type =1; // 1 지역 2 서버 3 전체 4 귓속말 string message = 2; string toNickName = 3; } @@ -125,7 +125,7 @@ message ClientToGameReq int32 socialActionSlot = 1; int32 socialActionId = 2; } - + message ExchangeEmotionSlotReq { int32 emotionSlot = 1; int32 emotionId = 2; @@ -139,16 +139,16 @@ message ClientToGameReq message UseRandomBoxItemReq { string itemGuid = 1; } - + message RemoveItemReq { string itemGuid = 1; int32 count = 2; } - // ϱ - message EquipClothItemReq + // 복장 장착하기 + message EquipClothItemReq { - uint32 cloth_slot = 1; + uint32 cloth_slot = 1; /* slot index cloth_avatar = 5; cloth_headwear = 6; @@ -165,10 +165,10 @@ message ClientToGameReq */ string itemGuid = 2; } - - - // 廩 - message UnequipClothItemReq + + + // 복장빼기 + message UnequipClothItemReq { uint32 cloth_slot = 1; /* slot index @@ -186,22 +186,22 @@ message ClientToGameReq cloth_socks = 16; */ } - + message WarpReq { int32 warpId = 1; } - + message ClothInfoSaveReq { ClothInfo clothInfo = 2; } - + message AvatarInfoSaveReq { AvatarInfo avatarInfo = 1; ClothInfo clothInfo = 2; } - + message EnterFittingRoomReq { } @@ -211,7 +211,7 @@ message ClientToGameReq message UseMountPropReq { string anchorGuid = 1; } - + message EndUseMountPropReq { string anchorGuid = 1; } @@ -235,20 +235,20 @@ message ClientToGameReq message StopEmotionReq { } - + message StartBuffReq { int32 buff_id = 1; } - + message StopBuffReq { int32 buff_id = 1; } - + message BuyItemReq{ - - int32 itemId = 1; + + int32 itemId = 1; int32 count = 2; } @@ -318,22 +318,22 @@ message ClientToGameReq } message RegisterToolSlotReq { - int32 slotIndex = 1; + int32 slotIndex = 1; string itemGuid = 2; } - + message DeregisterToolSlotReq { int32 slotIndex = 1; string itemGuid = 2; } - + message ActivateToolItemReq { int32 slotIndex = 1; } - + message DeactivateToolItemReq { } - + message UseToolItemReq { int32 equipedToolItemStep = 1; int32 equipedToolItemRandomState = 2; @@ -348,12 +348,13 @@ message ClientToGameReq } message ChangeStateReq { - int32 state = 1; // 0 : online 1 : sleep 2 : don't disturb + int32 state = 1; // 0 : online 1 : sleep 2 : don't disturb } message TaxiReq { - int32 taxiId = 1; + int32 departureTaxiId = 1; + int32 arrivalTaxiId = 2; } message GetCartReq @@ -371,10 +372,10 @@ message ClientToGameReq { repeated CartItemInfo itemInfos = 1; } - message GetFriendListReq + message GetFriendListReq { } - message GetFriendFolderListReq + message GetFriendFolderListReq { } message GetReceivedFriendRequestListReq { @@ -383,12 +384,12 @@ message ClientToGameReq } message GetBlockListReq { } - - message ConfirmNewFriendReq + + message ConfirmNewFriendReq { repeated string guids = 1; } - + message DeleteFriendReq { string guid = 1; } @@ -423,11 +424,11 @@ message ClientToGameReq message ReleaseFriendFolderReq { string folderName = 1; } - message DeleteFriendFolderReq + message DeleteFriendFolderReq { string folderName = 1; } - message MoveFriendFolderUserReq + message MoveFriendFolderUserReq { string guid = 1; string folderName = 2; @@ -440,7 +441,7 @@ message ClientToGameReq { repeated string guids = 1; } - + message RegisterTattooReq { string itemGuid = 1; @@ -464,7 +465,7 @@ message ClientToGameReq { int32 slotIndex = 1; int32 isVisible = 2; - } + } message ReNameFriendFolderReq { string folderName = 1; string newFolderName = 2; @@ -537,23 +538,23 @@ message ClientToGameReq message GetClaimInfoReq { } - + message ClaimRewardReq { int32 claimId = 1; int32 claimType = 2; } message GetRewardReq { - + } message GetQuestMailReq { } message ReadQuestMailReq { - int64 composedQuestId = 1; + int64 composedQuestId = 1; } message QuestRewardReq { - int64 composedQuestId = 1; + int64 composedQuestId = 1; } message GetCurrencyInfoReq { @@ -645,9 +646,9 @@ message ClientToGameReq message IntroCompleteReq { } - message C2GS_REQ_ESCAPE_POSITION + message C2GS_REQ_ESCAPE_POSITION { - + } message C2GS_REQ_NPC_DIALOGUE @@ -656,7 +657,7 @@ message ClientToGameReq string dialogue = 2; string dialogueResult = 3; } - + message C2GS_REQ_CUSTOM_DEFINED_UI_UPDATE { map toUpdateUiDatas = 1; @@ -696,36 +697,36 @@ message ClientToGameReq string anchorGuid = 1; } - // UGC NPC û + // UGC NPC 생성 요청 message C2GS_REQ_UGC_NPC_CREATION { - string bodyItemGuid = 1; // Ҹ Body ITEM_GUID - repeated string materialItemGuids = 2; // ITEM_GUID + string bodyItemGuid = 1; // 주 소모 Body ITEM_GUID + repeated string materialItemGuids = 2; // 보조 재료로 사용될 ITEM_GUID - string title = 3; // ŸƲ - string nickname = 4; // Ugc Npc г - - string greeting = 5; // λ縻 - string introduction = 6; // ڱҰ - - string description = 7; // ij - string worldScenario = 8; // - - int32 defaultSocialActionId = 9; // ⺻ SocialAction Meta Id - repeated int32 habitSocialActionIds = 10; // ϴ SocialAction Meta Id - repeated int32 dialogueSocialActionIds = 11; // ȭ ⺻ SocialAction Meta Id + string title = 3; // 타이틀 + string nickname = 4; // Ugc Npc 닉네임 + + string greeting = 5; // 인사말 + string introduction = 6; // 자기소개 + + string description = 7; // 캐릭터 설명 + string worldScenario = 8; // 세계관 + + int32 defaultSocialActionId = 9; // 기본 SocialAction Meta Id + repeated int32 habitSocialActionIds = 10; // 자주 사용하는 SocialAction Meta Id 목록 + repeated int32 dialogueSocialActionIds = 11; // 대화중 기본 SocialAction Meta Id 목록 // - map tattooSlotVisibles = 12; // Ÿ ̱:true, Ⱥ̱:false - - repeated int32 hashTagMetaIds = 20; // ؽ± (˻), BeaconTagData.xlsx + map tattooSlotVisibles = 12; // 타투 슬롯 보이기:true, 안보이기:false + + repeated int32 hashTagMetaIds = 20; // 해시태그 목록 (검색용), BeaconTagData.xlsx 참조 } - message C2GS_REQ_PAGED_UGQ_FROM_BOARD //忡 ؽƮ ˻ UGQs + message C2GS_REQ_PAGED_UGQ_FROM_BOARD //보드에서 텍스트 검색없이 보여지는 UGQs { - UgqSearchCategoryType searchCategoryType = 1; - UgqSortType sortType = 2; //ֽż, ƿ , ϸũ - int32 pageNumber = 3; // ȣ + UgqSearchCategoryType searchCategoryType = 1; + UgqSortType sortType = 2; //최신순, 좋아요 많은순, 북마크 많은순 + int32 pageNumber = 3; //페이지 번호 } message C2GS_REQ_UGQ_DETAIL_FROM_BOARD @@ -734,13 +735,13 @@ message ClientToGameReq int64 composedQuestId = 1; } - message C2GS_REQ_SEARCHED_UGQ_FROM_BOARD //忡 ؽƮ ˻ ã UGQs + message C2GS_REQ_SEARCHED_UGQ_FROM_BOARD //보드에서 텍스트 검색으로 찾는 UGQs { - UgqSearchCategoryType searchCategoryType = 1; + UgqSearchCategoryType searchCategoryType = 1; UgqSearchType searchType = 2; UgqSortType sortType = 3; - string searchText = 4; //UGQ ˻ ڿ - int32 pageNumber = 5; // ȣ + string searchText = 4; //UGQ 검색 문자열 + int32 pageNumber = 5; //페이지 번호 } message C2GS_REQ_UGQ_SPOTLIGHT_FROM_BOARD @@ -773,7 +774,7 @@ message ClientToGameReq { //UgqQuestId ugqQuestId = 1; int64 composedQuestId = 1; - string reportText = 2; //Ű + string reportText = 2; //신고 상세 내역 } message C2GS_REQ_UGQ_ASSIGN { @@ -790,7 +791,7 @@ message ClientToGameReq { int64 composedQuestId = 1; } - + message C2GS_REQ_UGQ_TEST_ASSIGN { //UgqQuestId ugqQuestId = 1; @@ -816,14 +817,14 @@ message ClientToGameReq { } - // NPC ߰ȴٸ Ŷ Ѵ. - kangms - // C2GS_REQ_UGQ_FIND_NPC -> EntityType üũ - // <- GS2C_NTF_XXX_XXX_INFO : ش EntityType ȸ - // <- GS2C_ACK_UGQ_FIND_NPC : / ַ !!! + // 추후 NPC 종류가 추가된다면 다음과 같은 패킷 구조로 개선 한다. - kangms + // C2GS_REQ_UGQ_FIND_NPC -> EntityType 체크 + // <- GS2C_NTF_XXX_XXX_INFO : 해당 EntityType 정보 조회 및 통지 + // <- GS2C_ACK_UGQ_FIND_NPC : 성공/실패 여부 위주로 전달 !!! message C2GS_REQ_UGQ_FIND_NPC { - string ownerGuid = 1; //beacon guid - string npcGuid = 2; + string ownerGuid = 1; //beacon 소유주의 guid + string npcGuid = 2; } message C2GS_REQ_UGQ_SIMPLE_INFO { @@ -831,34 +832,34 @@ message ClientToGameReq int64 composedQuestId = 1; } message C2GS_REQ_AICHAT_AUTH - { + { } - // н + //유료 시즌 패스 구매 message C2GS_REQ_BUY_CHARGED_SEASON_PASS { } - // н + //시즌 패스 보상 수령 message C2GS_REQ_TAKE_SEASON_PASS_REWARD { - int32 RewardGrade = 1; // Grade + int32 RewardGrade = 1; //보상 받을 리워드 Grade } message C2GS_REQ_SEARCH_NICKNAME { string searchString = 1; - int32 pageNum = 2; // 0: г ˻ 1~: ˻ г + int32 pageNum = 2; // 0: 닉네임 검색 1~: 검색된 닉네임 결과 페이지 } - // UGC NPC ġ û : C2GS_REQ_NPC_TOUCH Ʒ Ŷ Ѵ. - kangms + // UGC NPC 터치 요청 : C2GS_REQ_NPC_TOUCH 적용후 아래 패킷 삭제 한다. - kangms message C2GS_REQ_UGC_NPC_TOUCH { string toTouchUgcNpcMetaGuid = 1; - } - - // UGC NPC Like û + } + + // UGC NPC Like 요청 message C2GS_REQ_UGC_NPC_LIKE { - string npcGuid = 1; //beacon guid - string ownerGuid = 2; //beacon guid + string npcGuid = 1; //beacon의 guid + string ownerGuid = 2; //beacon 소유주의 guid } message C2GS_REQ_SELECT_ADVERTISEMENT @@ -884,93 +885,94 @@ message ClientToGameReq string myhomeGuid = 1; string newMyhomeName = 2; } - - // UGC NPC α û + + // UGC NPC 인기 순위 요청 message C2GS_REQ_UGC_NPC_RANK { - UgcNpcRankType type = 1; // ȸ rank Type ( Like, ȭ, UGQ ) - UgcNpcRankState state = 2; // ȸ rank ( / Ʈ ) + UgcNpcRankType type = 1; // 조회할 rank Type ( Like, 대화, UGQ ) + UgcNpcRankState state = 2; // 조회할 rank 상태 ( 누적 / 트랜드 ) } - // Npc ȸ û + // Npc 정보를 조회 요청 message C2GS_REQ_NPC_INFO { - EntityType touchedNpcType = 1; // EntityType ( EntityType.Beacon, ߰ ִ !!! ) - string npcUniqueId = 2; // npc ĺŰ, ( EntityType.Beacon ϰ UgcNpcMetaGuid or EntityInstanceGuid ) - string ownerUserGuid = 3; // npc Guid ( EntityType.Beacon ϰ UserGuid, ڰ : 0 ) + EntityType touchedNpcType = 1; // EntityType 참조 ( EntityType.Beacon, 추후 추가될 수 있다 !!! ) + string npcUniqueId = 2; // npc 식별키, ( EntityType.Beacon 일경우 UgcNpcMetaGuid or EntityInstanceGuid ) + string ownerUserGuid = 3; // npc 소유자 Guid ( EntityType.Beacon 일경우 UserGuid, 소유자가 없는 경우: 0 ) } - // C2GS_REQ_NPC_INFO Ŷ ó - // GS2C_ACK_NPC_INFO Ŷ ۽ ϱ Ŷ Ǿ Ѵ. + // C2GS_REQ_NPC_INFO 패킷 처리결과가 성공일 경우 + // GS2C_ACK_NPC_INFO 패킷 송신전에 하기의 패킷이 먼저 통지 되어야 한다. // GS2C_NTF_BEACON_DETAIL_INFO - // Npc ʵ忡 ġ û + // Npc를 필드에서 터치 요청 message C2GS_REQ_NPC_TOUCH { - EntityType touchedNpcType = 5; // EntityType ( EntityType.Beacon, EntityType.FarmingProp ߰ ִ !!! ) - string entityInstantGuid = 6; // ġ Npc Instant Guid, GS2C_NTF_NPC_IN_SIGHT.UgcNpcEntity.entityInstantGuid ޵ Guid !!! + EntityType touchedNpcType = 5; // EntityType 참조 ( EntityType.Beacon, EntityType.FarmingProp 추후 추가될 수 있다 !!! ) + string entityInstantGuid = 6; // 터치한 Npc Instant Guid, GS2C_NTF_NPC_IN_SIGHT.UgcNpcEntity.entityInstantGuid 전달된 Guid !!! } - // C2GS_REQ_NPC_TOUCH Ŷ ó - // GS2C_ACK_NPC_TOUCH Ŷ ۽ ϱ Ŷ Ǿ Ѵ. + // C2GS_REQ_NPC_TOUCH 패킷 처리결과가 성공일 경우 + // GS2C_ACK_NPC_TOUCH 패킷 송신전에 하기의 패킷이 먼저 통지 되어야 한다. // GS2C_NTF_BEACON_INTERACTION_INFO message C2GS_REQ_CALIUM_CONVERTER { } - message C2S_REQ_LARGE_PACKET + message C2GS_REQ_LARGE_PACKET { string uid = 1; int32 totalPacketCount = 2; int32 packetIndex = 3; bytes data = 4; + LargePacketProcess processType = 5; } - + message C2GS_REQ_CONVERT_MATERIAL_TO_CALIUM { - // : Ҹ ׸ + // : 소모할 마테리얼 정보 map useMaterials = 1; } - - // Ĺ û + + // 파밍 시작 요청 message C2GS_REQ_FARMING_START { - string farmingAnchorMetaId = 1; // MapMetaData ִ Ĺ Anchor Id + string farmingAnchorMetaId = 1; // MapMetaData에 있는 파밍 Anchor Id - int32 farmingActionTryCount = 2; // Ĺ õ Ƚ - - FarmingSummonedEntityType toUseEntityType = 4; // Ĺֽ ƼƼ - string toUseEnityGuid = 5; // Ĺֽ ƼƼ Guid, FarmingSummonedEntityType.User : UserGuid, FarmingSummonedEntityType.Beacon : UgcNpcMetaGuid, + int32 farmingActionTryCount = 2; // 파밍 실행 시도 횟수 + + FarmingSummonedEntityType toUseEntityType = 4; // 파밍시 선택할 엔티티의 종류 + string toUseEnityGuid = 5; // 파밍시 사용할 엔티티의 Guid, FarmingSummonedEntityType.User : UserGuid, FarmingSummonedEntityType.Beacon : UgcNpcMetaGuid, } - // Ĺ û + // 파밍 취소 요청 message C2GS_REQ_FARMING_CANCEL { - string farmingAnchorMetaId = 1; // MapMetaData ִ Ĺ Anchor Id + string farmingAnchorMetaId = 1; // MapMetaData에 있는 파밍 Anchor Id } - // UGC NPC û + // UGC NPC 편집 요청 message C2GS_REQ_UGC_NPC_EDIT { - string toEditUgcNpcMetaGuid = 1; // UgcNpc Ÿ Guid + string toEditUgcNpcMetaGuid = 1; // 편집할 UgcNpc 메타 Guid - string title = 2; // ŸƲ - string greeting = 3; // λ縻 - string introduction = 4; // ڱҰ - string description = 5; // ij - string worldScenario = 6; // + string title = 2; // 타이틀 + string greeting = 3; // 인사말 + string introduction = 4; // 자기소개 + string description = 5; // 캐릭터 설명 + string worldScenario = 6; // 세계관 - int32 defaultSocialActionId = 11; // ⺻ SocialAction Meta Id - repeated int32 habitSocialActionIds = 12; // ϴ SocialAction Meta Id - repeated int32 dialogueSocialActionIds = 13; // ȭ ⺻ SocialAction Meta Id + int32 defaultSocialActionId = 11; // 기본 SocialAction Meta Id + repeated int32 habitSocialActionIds = 12; // 자주 사용하는 SocialAction Meta Id 목록 + repeated int32 dialogueSocialActionIds = 13; // 대화중 기본 SocialAction Meta Id 목록 + + repeated int32 hashTagMetaIds = 21; // 해시태그 목록 (검색용), BeaconTagData.xlsx 참조 + + repeated string materialItemGuids = 31; // 보조 재료로 사용될 ITEM_GUID, (UgcNpc에 새로 장착될 것들과 장착을 유지할 것들, 주의: 여기에 없는 것들은 모두 제거 대상 !!!) - repeated int32 hashTagMetaIds = 21; // ؽ± (˻), BeaconTagData.xlsx - - repeated string materialItemGuids = 31; // ITEM_GUID, (UgcNpc ͵ ͵, : ⿡ ͵ !!!) - // - map tattooSlotVisibles = 41; // Ÿ ̱:true, Ⱥ̱:false + map tattooSlotVisibles = 41; // 타투 슬롯 보이기:true, 안보이기:false } message C2GS_REQ_ENTER_MYHOME_EDIT_ROOM @@ -978,24 +980,24 @@ message ClientToGameReq string myhomeGuid = 1; } - // ij Ŀ͸¡ û + // 캐릭터 외형 커스터마이징 요청 message C2GS_REQ_CHARACTER_APPEARANCE_CUSTOMIZE { - string toChangeCharacterGuid = 1; // UserGuid !!! - AppearanceCustomization toApplyAppearCustomize = 2; // , Ŷ AvatarInfo.Init ʴ´ !!! + string toChangeCharacterGuid = 1; // 현재는 UserGuid 와 동일 !!! + AppearanceCustomization toApplyAppearCustomize = 2; // 적용할 외형 정보, 이 패킷에서는 AvatarInfo.Init 정보를 서버는 참조 및 변경하지 않는다 !!! } - // Ŀ͸¡ û + // 비컨 외형 커스터마이징 요청 message C2GS_REQ_BEACON_APPEARANCE_CUSTOMIZE { - string toChangeUgcNpcMetaGuid = 1; // UgcNpc Ÿ Guid - AppearanceCustomization toApplyAppearCustomize = 2; // , Ŷ AvatarInfo.Init ʴ´ !!! + string toChangeUgcNpcMetaGuid = 1; // 변경할 UgcNpc 메타 Guid + AppearanceCustomization toApplyAppearCustomize = 2; // 적용할 외형 정보, 이 패킷에서는 AvatarInfo.Init 정보를 서버는 참조 및 변경하지 않는다 !!! } - // Ű û + // 보유중인 비컨을 매각 요청 message C2GS_REQ_BEACON_SELL { - string toSellUgcNpcMetaGuid = 1; // Ű UgcNpc Ÿ Guid + string toSellUgcNpcMetaGuid = 1; // 매각할 UgcNpc 메타 Guid } message C2GS_REQ_RENTAL_LAND_INFOS @@ -1028,12 +1030,12 @@ message ClientToGameReq CurrencyType rentalCurrencyType = 10; double rentalCurrencyAmount = 11; } - + message C2GS_REQ_CHANGE_SCREEN_PAGE { - BoolType isIncrease = 1; // BoolType_True : / BoolType_False : - BoolType isCustom = 2; // true : , false : ̻ - int32 customPage = 3; // Number + BoolType isIncrease = 1; // BoolType_True : 증가 / BoolType_False : 감소 + BoolType isCustom = 2; // true : 설정 페이지 사용 , false : 설정 페이지 미사용 + int32 customPage = 3; // 설정 페이지 Number } message C2GS_REQ_RENEWAL_SHOP_PRODUCTS { @@ -1047,7 +1049,7 @@ message ClientToGameReq message C2GS_REQ_ENTITY_STATE_DANCE_END{ } - + message C2GS_REQ_BEACON_APP_PROFILE_UPLOAD_ADDRESS { string beaconGuid = 1; @@ -1073,7 +1075,7 @@ message ClientToGameReq message C2GS_REQ_GAIN_LAND_PROFIT { int32 landMetaId = 1; - int32 floor = 2; // 0 : ȹ, 1~ : õ ȹ + int32 floor = 2; // 0 : 모든 층 수익 획득, 1~ : 선택된 층 수익 획득 } message C2GS_REQ_LAND_RENTAL_HISTORY @@ -1086,31 +1088,31 @@ message ClientToGameReq int32 landMetaId = 1; } - // ߻ - // üũ û + // 랜드 선택할 때 발생 + // 랜드 경매 체크 요청 message C2GS_REQ_LAND_AUCTION_CHECK { - int32 landMetaId = 1; // üũ LandData Meta Id + int32 landMetaId = 1; // 체크 대상 LandData Meta Id } - // û + // 랜드 경매 입찰 요청 message C2GS_REQ_LAND_AUCTION_BID { - int32 landMetaId = 1; // LandData Meta Id - int32 auctionNumber = 2; // ȣ - - LandAuctionBidType bidType = 5; // , Blind ְ ƾ Ѵ !!! - - CurrencyType currencyType = 11; // ȭ - double bidPrice = 12; // , GameConfigData.LandAuctionAskPriceLow, LandAuctionAskPriceMid, LandAuctionAskPriceHigh + int32 landMetaId = 1; // 경매 대상 LandData Meta Id + int32 auctionNumber = 2; // 경매 번호 + + LandAuctionBidType bidType = 5; // 입찰의 종류, Blind일 경우 최고 입찰가가 보다 높아야 한다 !!! + + CurrencyType currencyType = 11; // 선택한 재화 종류 + double bidPrice = 12; // 설정한 입찰가, GameConfigData.LandAuctionAskPriceLow, LandAuctionAskPriceMid, LandAuctionAskPriceHigh 참조 } - // û + // 랜드 경매 모든 스케쥴 요청 message C2GS_REQ_LAND_AUCTION_SCHEDULE_ALL - { + { } - // 丮 û + // 랜드 경매 모든 히스토리 요청 message C2GS_REQ_LAND_AUCTION_HISTORY_ALL { } @@ -1118,17 +1120,17 @@ message ClientToGameReq message C2GS_REQ_CALIUM_EXCHANGER_INFO { } - + message C2GS_REQ_CONVERT_EXCHANGER_CALIUM { - double sapphire = 1; // û ̾ + double sapphire = 1; // 요청 사파이어 수 } message C2GS_REQ_LAND_RENTAL_iNFO { int32 landMetaId = 1; } - + message C2GS_REQ_SWITCHING_PROP_STATE { } @@ -1137,8 +1139,8 @@ message ClientToGameReq { int32 idx = 1; } - - // + + //비컨 개인 상점 아이템 등록 message C2GS_REQ_BEACON_SHOP_REGISTER_ITEM { string itemGuid = 1; @@ -1147,14 +1149,14 @@ message ClientToGameReq string beaconGuid = 4; } - // ȸ + //비컨 개인 상점 아이템 회수 message C2GS_REQ_BEACON_SHOP_RETURN_ITEM { string itemGuid = 1; string beaconGuid = 2; } - - // + + //비컨 개인 상점 아이템 구매 message C2GS_REQ_BEACON_SHOP_PURCHASE_ITEM { string itemGuid = 1; @@ -1163,44 +1165,74 @@ message ClientToGameReq string beaconOwnerGuid = 4; } - // Ǹ + //비컨 개인 상점 아이템 판매 내역 message C2GS_REQ_BEACON_SHOP_GET_SOLD_RECORDS { string beaconGuid = 1; } - // + //비컨 개인 상점 아이템 정산 message C2GS_REQ_BEACON_SHOP_RECEIVE_PAYMENT_FOR_SALES { string beaconGuid = 1; } - // ȸ + //비컨 개인 상점 조회 message C2GS_REQ_BEACON_SHOP_SEARCH_ITEM { - int32 itemMetaid = 1; + repeated int32 itemMetaids = 1; + int32 page = 2; + repeated SortInfo sortInfo = 3; } - // ޾ƿ + //비컨 아이템 정보 받아오기 message C2GS_REQ_BEACON_SHOP_GET_ITEM_INFOS { string beaconGuid = 1; string beaconOwnerGuid = 2; } + message C2GS_REQ_BEACON_SHOP_RECENT_REGISTER_ITEMS + { + } + + message C2GS_REQ_MOVE_TO_BEACON + { + string beaconGuid = 1; + } + + message C2GS_REQ_CONTENTS_MOVE + { + int32 contentsMenuId = 1; + } + + message C2GS_REQ_RANKING_INFO + { + string rankingGuid = 1; + } + + message C2GS_REQ_USE_COUPON + { + string couponNumber = 1; + } + + message C2GS_REQ_DESTROY_WEAPON_OBJECT { + string anchorGuid = 1; + } + //============================================================================================= - // Ʋ Ŷ + // 배틀 관련 패킷 설정 시작 message C2GS_REQ_JOIN_BATTLE_INSTANCE { - BattleInstanceType instanceType = 1; //Normal : νϽ, Event : ð + BattleInstanceType instanceType = 1; //Normal : 상시 인스턴스, Event : 시간 제한 있음 int32 landId = 2; int32 buildingId = 3; int32 floor = 4; int32 eventId = 5; google.protobuf.Timestamp packetCreateTime = 6; } - + message C2GS_REQ_LEAVE_BATTLE_INSTANCE { google.protobuf.Timestamp packetCreateTime = 1; @@ -1222,28 +1254,85 @@ message ClientToGameReq string anchorGuid = 1; google.protobuf.Timestamp packetCreateTime = 2; } - + message C2GS_REQ_PREPARATION_FOR_LEAVING_BATTLE_INSTANCE { google.protobuf.Timestamp packetCreateTime = 1; } - - //Ʋ Ŷ + + //배틀 관련 패킷 설정 종료 // ============================================================================================ //============================================================================================= - // GameMode - + // GameMode 설정 + message C2GS_REQ_GAME_OBJECT_INTERACTION { string anchorGuid = 1; google.protobuf.Timestamp packetCreateTime = 2; } - // GameMode + + message C2GS_REQ_GAME_PLAYER_DEATH + { + string killerUserGuid = 1; + string deadUserGuid = 2; + GameModeDeadType deadType = 3; + google.protobuf.Timestamp packetCreateTime = 4; + } + + message C2GS_REQ_GAME_PLAYER_RESPAWN + { + google.protobuf.Timestamp packetCreateTime = 1; + } + + message C2GS_REQ_PREPARATION_FOR_LEAVING_GAME_INSTANCE + { + google.protobuf.Timestamp packetCreateTime = 1; + } + + message C2GS_REQ_LEAVE_GAME_INSTANCE + { + google.protobuf.Timestamp packetCreateTime = 1; + } + + message C2GS_REQ_GAME_MODE_LOAD_COMPLETE + { + } + + + // GameMode 종료 //============================================================================================= - + + // ============================================================================================ + // Match 관련 메시지 설정 + // ============================================================================================ + // 매칭 예약 요청 + message C2GS_REQ_MATCH_RESERVE + { + int32 gameModeId = 1; // 게임 모드 id + int32 eventId = 2; + string regionName = 3; // 리전 + int32 regionPingMs = 4; // 해당 리전의 핑 측정 결과 밀리세컨드 + int32 instanceId = 5; // 유저가 맵을 선택하는 경우 맵의 instanceId, 0인 경우 게임 모드에 따라 랜덤 또는 무시 + } + + // 매칭 취소 요청 + message C2GS_REQ_MATCH_CANCEL + { + int32 gameModeId = 1; // 게임 모드 id - 서버에서 이미 알고 있지만, 확인 차원 + } //============================================================================================= - // REQ Ŷ + + //============================================================================================= + // WorldEvent 월드이벤트 req + //============================================================================================= + message C2GS_REQ_WORLD_EVENT_CONTRIBUTION + { + int32 worldEventId = 1; // 월드 이벤트 id + } + + //============================================================================================= + // REQ 패킷 설정 //============================================================================================= oneof msg { LoginReq loginReq = 1; @@ -1434,10 +1523,10 @@ message ClientToGameReq C2GS_REQ_NPC_INFO reqNpcInfo = 193; C2GS_REQ_NPC_TOUCH reqNpcTouch = 194; - + C2GS_REQ_CALIUM_CONVERTER reqCaliumConverter = 195; C2GS_REQ_CONVERT_MATERIAL_TO_CALIUM reqConvertMaterialToCalium = 196; - C2S_REQ_LARGE_PACKET reqLargePacket = 197; + C2GS_REQ_LARGE_PACKET reqLargePacket = 197; C2GS_REQ_FARMING_START reqFarmingStart = 198; C2GS_REQ_FARMING_CANCEL reqFarmingCancel = 199; @@ -1453,7 +1542,7 @@ message ClientToGameReq C2GS_REQ_RENTAL_FLOOR_INFOS reqRentalFloorInfos = 206; C2GS_REQ_LAND_INFO reqLandInfo = 207; C2GS_REQ_RENT_FLOOR reqRentFloor = 208; - + C2GS_REQ_CHANGE_SCREEN_PAGE reqChangeScreenPage = 209; C2GS_REQ_RENEWAL_SHOP_PRODUCTS reqRenewalShopProducts = 210; C2GS_REQ_ENTITY_STATE_DANCE_START reqEntityStateDanceStart = 211; @@ -1484,9 +1573,19 @@ message ClientToGameReq C2GS_REQ_BEACON_SHOP_RECEIVE_PAYMENT_FOR_SALES reqBeaconShopReceivePaymentForSales = 232; C2GS_REQ_BEACON_SHOP_SEARCH_ITEM reqBeaconShopSearchItem = 233; C2GS_REQ_BEACON_SHOP_GET_ITEM_INFOS reqBeaconShopGetItemInfos = 234; + C2GS_REQ_BEACON_SHOP_RECENT_REGISTER_ITEMS reqBeaconShopRecentRegisterItems = 235; + C2GS_REQ_MOVE_TO_BEACON reqMoveToBeacon = 236; + C2GS_REQ_CONTENTS_MOVE reqContentsMove = 237; + C2GS_REQ_RANKING_INFO reqRankingInfo = 238; + C2GS_REQ_USE_COUPON reqUseCoupon = 239; + C2GS_REQ_DESTROY_WEAPON_OBJECT reqDestroyWeaponObject = 240; + + //============================================================================================= + // WorldEvent 월드이벤트 관련 패킷 정의 + C2GS_REQ_WORLD_EVENT_CONTRIBUTION reqWorldEventContribution= 241; //============================================================================================= - // Battle Ŷ - Pilot ̱ 100000 + // Battle 관련 패킷 정의 - Pilot 이기 때문에 100000번 부터 시작 C2GS_REQ_JOIN_BATTLE_INSTANCE reqJoinBattleInstance = 100000; C2GS_REQ_LEAVE_BATTLE_INSTANCE reqLeaveBattleInstance = 100001; C2GS_REQ_BATTLE_PLAYER_DEATH reqBattlePlayerDeath = 100002; @@ -1494,11 +1593,21 @@ message ClientToGameReq C2GS_REQ_BATTLE_OBJECT_INTERACTION reqBattleObjectInteraction = 100004; C2GS_REQ_PREPARATION_FOR_LEAVING_BATTLE_INSTANCE reqPreparationForLeavingInstance = 100005; + //============================================================================================= - // GameMode Ŷ - ϴ ⿡ ϰ ߿ ø Ѵ. + // GameMode 관련 패킷 정의 - 일단 여기에 정의하고 개발 마무리 시점에 나중에 위로 올리던가 한다. C2GS_REQ_GAME_OBJECT_INTERACTION reqGameObjectInteraction = 100010; - - + C2GS_REQ_GAME_PLAYER_DEATH reqGamePlayerDeath = 100011; + C2GS_REQ_GAME_PLAYER_RESPAWN reqGamePlayerRespawn = 100012; + C2GS_REQ_PREPARATION_FOR_LEAVING_GAME_INSTANCE reqPreparationForLeavingGameInstance = 100013; + C2GS_REQ_LEAVE_GAME_INSTANCE reqLeaveGameInstance = 100014; + C2GS_REQ_GAME_MODE_LOAD_COMPLETE reqGameModeLoadComplete = 100015; + + + //==================================================================================== + // GameMatching 관련 패킷 정의 + C2GS_REQ_MATCH_RESERVE reqMatchReserve = 110001; + C2GS_REQ_MATCH_CANCEL reqMatchCancel = 110002; } } @@ -1506,7 +1615,7 @@ message ClientToGameReq message ClientToGameRes { //============================================================================================= - // ACK Ŷ + // ACK 패킷 정의 //============================================================================================= message LoginRes { @@ -1528,7 +1637,7 @@ message ClientToGameRes message CreateCharRes { GameCharacter charData = 1; } - + message SelectCharRes { int32 Id = 1; GameCharacter charData = 2; @@ -1544,19 +1653,19 @@ message ClientToGameRes message ListCharRes { repeated GameCharacter charList = 1; } - // ä÷ + // 채팅룸 만들기 message CreateChatRoomRes { string chatServerAddr = 1; int32 chatServerPort = 2; int64 roomId = 3; } - // ä÷ + // 채팅룸 조인 message JoinChatRoomRes { string chatServerAddr = 1; int32 chatServerPort = 2; int64 roomId = 3; } - // ä÷ + // 채팅룸 나가기 message LeaveChatRoomRes { } @@ -1579,7 +1688,7 @@ message ClientToGameRes message CancelInstanceEntryQueueRes { } - + message EnterMyHomeRes { string instanceServerAddr = 1; int32 instanceServerPort = 2; @@ -1597,7 +1706,7 @@ message ClientToGameRes message ExchagneMyHomeRes { MyHomeInfo myhomeInfo = 1; } - + message ExchagneMyHomePropRes { string anchorGuid = 1; string itmeGuid = 2; @@ -1605,7 +1714,7 @@ message ClientToGameRes message ExchangeLandPropRes { } - + message ExchangeBuildingRes { } @@ -1620,7 +1729,7 @@ message ClientToGameRes message ExchangeEmotionSlotRes{ } - + message UseItemRes { string itemGuid = 1; int32 count = 2; @@ -1631,7 +1740,7 @@ message ClientToGameRes message UseRandomBoxItemRes { CommonResult commonResult = 1; } - + message WarpRes { oneof place @@ -1648,30 +1757,30 @@ message ClientToGameRes ClothInfo clothInfo = 2; Inventory inventory = 3; } - + message ClothInfoSaveRes { ClothInfo clothInfo = 1; } - + message AddItemRes{ - - int32 index = 1; // 1 etc 2 Costume 3 ׸ + + int32 index = 1; // 1 etc 2 Costume 3 인테리어 repeated Item item = 2; } - + message RemoveItemRes{ - - string itemGuid = 1; // 1 etc 2 Costume 3 ׸ + + string itemGuid = 1; // 1 etc 2 Costume 3 인테리어 int32 count = 2; } - + message RemoveItemsRes{ - - int32 index = 1; // 1 etc 2 Costume 3 ׸ + + int32 index = 1; // 1 etc 2 Costume 3 인테리어 repeated Item item = 2; } - + message EnterFittingRoomRes { string instanceServerAddr = 1; @@ -1701,10 +1810,10 @@ message ClientToGameRes message NewLeaveFittingRoomRes { Pos pos = 1; } - + message EquipClothItemRes { - uint32 cloth_slot = 1; + uint32 cloth_slot = 1; /* slot index cloth_avatar = 1; cloth_headwear = 2; @@ -1746,18 +1855,18 @@ message ClientToGameRes message StopSocialActionRes { } - + message UseEmotionRes { int32 emotionId = 1; } message StopEmotionRes { } - + message StartBuffRes { } - + message StopBuffRes { } @@ -1772,7 +1881,7 @@ message ClientToGameRes ServerConnectInfo ServerConnectInfo = 1; Pos pos = 2; } - } + } message EnterCESConcertRes { } @@ -1802,7 +1911,7 @@ message ClientToGameRes } message GetItemsMailRes { - CommonResult commonResult = 1; // ȭ , Game_Define.CommonResult + CommonResult commonResult = 1; // 각종 변화된 결과 정보들, Game_Define.CommonResult 참조 } message DeleteMailRes { @@ -1814,7 +1923,7 @@ message ClientToGameRes message MoveChannelRes { ServerConnectInfo gameServerConnectInfo = 1; - int32 possibleRemainingTime = 2; // ä̵ ð (s) + int32 possibleRemainingTime = 2; // 채널이동 남은 시간 (s) } message ExchangeMannequinDisplayItemRes{ @@ -1880,55 +1989,55 @@ message ClientToGameRes CharInfo currencyInfo = 2; } - message GetFriendListRes + message GetFriendListRes { repeated FriendInfo friendList = 1; } - message GetFriendFolderListRes + message GetFriendFolderListRes { int32 folderOrderType = 1; repeated FriendFolder friendFolderList = 2; } - message GetReceivedFriendRequestListRes + message GetReceivedFriendRequestListRes { repeated FriendRequestInfo requestList = 1; } - message GetSendedFriendRequestListRes + message GetSendedFriendRequestListRes { repeated FriendRequestInfo requestList = 1; } - message GetBlockListRes + message GetBlockListRes { repeated BlockInfo blockList = 1; } - message ConfirmNewFriendRes + message ConfirmNewFriendRes { } - message DeleteFriendRes + message DeleteFriendRes { } - message ChangeFriendOrderRes + message ChangeFriendOrderRes { } - message SendFriendRequestRes + message SendFriendRequestRes { FriendRequestInfo friendRequestInfo = 1; } - message ReplyReceivedFriendRequestRes + message ReplyReceivedFriendRequestRes { string guid = 1; } - message BlockUserRes + message BlockUserRes { string nickName = 1; string guid = 2; } - message InviteFriendToMyhomeRes + message InviteFriendToMyhomeRes { repeated string successGuids = 1; repeated FriendErrorMember failInfos = 2; } - message ReplyInviteToMyhomeRes + message ReplyInviteToMyhomeRes { } @@ -1938,26 +2047,26 @@ message ClientToGameRes repeated FriendErrorMember failInfos = 2; } - message CreateFriendFolderRes + message CreateFriendFolderRes { FriendFolder friendFolder = 1; } - message ChangeFriendFolderNameRes + message ChangeFriendFolderNameRes { } - message HoldFriendFolderRes + message HoldFriendFolderRes { FriendFolder friendFolder = 1; } - message ReleaseFriendFolderRes + message ReleaseFriendFolderRes { FriendFolder friendFolder = 1; } - message DeleteFriendFolderRes + message DeleteFriendFolderRes { } - message MoveFriendFolderUserRes + message MoveFriendFolderUserRes { } message ConfirmNewReceivedFriendRequestRes @@ -2051,9 +2160,9 @@ message ClientToGameRes int64 composedQuestId = 1; int32 replacedRewardGroupId = 2; CommonResult commonResult = 3; - + //repeated Item rewardItems = 3; - + } message QuestTaskUpdateRes { @@ -2089,18 +2198,18 @@ message ClientToGameRes int32 claimId = 1; CommonResult commonResult = 2; } - + message GetCurrencyInfoRes { CharInfo currencyInfo = 1; } message UserReportRes { - } + } message ReplySummonPartyMemberRes { ServerConnectInfo gameServerConnectInfo = 1; } - + message RegisterMinimapMarkerRes{ } @@ -2151,6 +2260,7 @@ message ClientToGameRes message RePurchaseItemRes { repeated Item items = 1; CharInfo currencyInfo = 2; + string repurchaseItemGuid = 3; } message SellItemRes { string ItemGuid = 1; @@ -2175,7 +2285,7 @@ message ClientToGameRes message IntroCompleteRes { } - + message GS2C_ACK_ESCAPE_POSITION { int64 remainTime = 1; @@ -2188,7 +2298,7 @@ message ClientToGameRes string dialogue = 2; string dialogueResult = 3; } - + message GS2C_ACK_CUSTOM_DEFINED_UI_UPDATE { map updatedUiDatas = 1; @@ -2230,31 +2340,31 @@ message ClientToGameRes string anchorGuid = 2; string beaconGuid = 3; } - - // UGC NPC û + + // UGC NPC 생성 요청에 대한 응답 message GS2C_ACK_UGC_NPC_CREATION { string ugcNpcMetaGuid = 1; // Ugc Npc Meta Id (GUID) - - int32 bodyItemMetaId = 2; // ItemData.xlsx - string title = 3; // ŸƲ - string nickname = 4; // Ugc Npc г - - string greeting = 5; // λ縻 - string introduction = 6; // ڱҰ - string description = 7; // ij - string worldScenario = 8; // - - int32 defaultSocialActionId = 9; // ⺻ SocialAction Meta Id - repeated int32 habitSocialActionIds = 10; // ϴ SocialAction Meta Id - repeated int32 dialogueSocialActionIds = 11; // ȭ ⺻ SocialAction Meta Id + int32 bodyItemMetaId = 2; // ItemData.xlsx 참조 + string title = 3; // 타이틀 + string nickname = 4; // Ugc Npc 닉네임 - map tattooSlotInfos = 12; // Ÿ - - repeated int32 hashTagMetaIds = 20; // ؽ± (˻), BeaconTagData.xlsx + string greeting = 5; // 인사말 + string introduction = 6; // 자기소개 - CommonResult commonResult = 21; // ȭ , Game_Define.CommonResult + string description = 7; // 캐릭터 설명 + string worldScenario = 8; // 세계관 + + int32 defaultSocialActionId = 9; // 기본 SocialAction Meta Id + repeated int32 habitSocialActionIds = 10; // 자주 사용하는 SocialAction Meta Id 목록 + repeated int32 dialogueSocialActionIds = 11; // 대화중 기본 SocialAction Meta Id 목록 + + map tattooSlotInfos = 12; // 타투 관련 정보 목록 + + repeated int32 hashTagMetaIds = 20; // 해시태그 목록 (검색용), BeaconTagData.xlsx 참조 + + CommonResult commonResult = 21; // 각종 변화된 결과 정보들, Game_Define.CommonResult 참조 } message GS2C_ACK_PAGED_UGQ_FROM_BOARD @@ -2277,7 +2387,7 @@ message ClientToGameRes message GS2C_ACK_UGQ_FROM_NPC { - + } message GS2C_ACK_UGQ_REGISTER_BOOKMARK { @@ -2298,7 +2408,7 @@ message ClientToGameRes { repeated QuestInfo quests = 1; repeated QuestMetaInfo questMetaInfos = 2; - repeated UgqGameQuestDataForClient ugqGameQuestDataForClients = 3;//ȭ , ŸƲ + repeated UgqGameQuestDataForClient ugqGameQuestDataForClients = 3;//대화 정보, 타이틀 정보 CommonResult commonResult = 4; } message GS2C_ACK_UGQ_REASSIGN @@ -2308,7 +2418,7 @@ message ClientToGameRes repeated UgqGameQuestDataForClient ugqGameQuestDataForClients = 3; int64 deletedComposedQuestId = 4; } - + message GS2C_ACK_UGQ_ABORT { int64 composedQuestId = 1; @@ -2317,7 +2427,7 @@ message ClientToGameRes { repeated QuestInfo quests = 1; repeated QuestMetaInfo questMetaInfos = 2; - repeated UgqGameQuestDataForClient ugqGameQuestDataForClients = 3;//ȭ , ŸƲ + repeated UgqGameQuestDataForClient ugqGameQuestDataForClients = 3;//대화 정보, 타이틀 정보 } message GS2C_ACK_UGQ_TEST_ABORT { @@ -2354,7 +2464,7 @@ message ClientToGameRes message GS2C_ACK_TAKE_SEASON_PASS_REWARD { CommonResult commonResult = 1; - int32 takenRewardGrade = 2; // н + int32 takenRewardGrade = 2; // 시즌패스의 보상 받은 보상 등급 } message GS2C_ACK_SEARCH_NICKNAME @@ -2363,15 +2473,15 @@ message ClientToGameRes int32 totalPage = 2; int32 currentPage = 3; repeated string nicknames = 4; - } - - // UGC NPC ġ û : C2GS_REQ_NPC_TOUCH Ʒ Ŷ Ѵ. - kangms + } + + // UGC NPC 터치 요청 결과 : C2GS_REQ_NPC_TOUCH 적용후 아래 패킷 삭제 한다. - kangms message GS2C_ACK_UGC_NPC_TOUCH { string ugcNpcMetaGuid = 1; // Ugc Npc Meta Id (GUID) - } + } - // UGC NPC Like û + // UGC NPC Like 요청 결과 message GS2C_ACK_UGC_NPC_LIKE { string npcGuid = 1; @@ -2383,7 +2493,7 @@ message ClientToGameRes { int32 index = 1; } - + message GS2C_ACK_SAVE_MYHOME_UGC { string myhomeGuid = 1; @@ -2405,97 +2515,98 @@ message ClientToGameRes string myhomeGuid = 1; string newMyhomeName = 2; } - - message S2C_REQ_LARGE_PACKET + + message GS2C_ACK_LARGE_PACKET { string uid = 1; int32 totalPacketCount = 2; int32 packetIndex = 3; bytes data = 4; + LargePacketProcess processType = 5; } - // UGC NPC Rank û + // UGC NPC Rank 요청 결과 message GS2C_ACK_UGC_NPC_RANK { - UgcNpcRankType type = 1; // ȸ rank Type ( Like, ȭ, UGQ ) - UgcNpcRankState state = 2; // ȸ rank ( / Ʈ ) - repeated UgcNpcRank ugcNpcRank = 3; // Rank Ʈ + UgcNpcRankType type = 1; // 조회된 rank Type ( Like, 대화, UGQ ) + UgcNpcRankState state = 2; // 조회된 rank 상태 ( 누적 / 트랜드 ) + repeated UgcNpcRank ugcNpcRank = 3; // Rank 정보 리스트 } - // Npc ȸ û + // Npc 정보를 조회 요청 결과 message GS2C_ACK_NPC_INFO - { + { string debugString = 1; - - string npcUniqueId = 5; // npc ĺŰ, ( EntityType.Beacon ϰ UgcNpcMetaGuid ) - string ownerUserGuid = 6; // npc Guid ( EntityType.Beacon ϰ UserGuid, ڰ : 0 ) + + string npcUniqueId = 5; // npc 식별키, ( EntityType.Beacon 일경우 UgcNpcMetaGuid ) + string ownerUserGuid = 6; // npc 소유자 Guid ( EntityType.Beacon 일경우 UserGuid, 소유자가 없는 경우: 0 ) } - // Npc ʵ忡 ġ û + // Npc를 필드에서 터치 요청 결과 message GS2C_ACK_NPC_TOUCH { string debugString = 1; - EntityType touchedNpcType = 5; // EntityType ( EntityType.Beacon, ߰ ִ !!! ) - string entityInstantGuid = 6; // ġ Npc Instant Guid, GS2C_NTF_NPC_IN_SIGHT.UgcNpcEntity.entityInstantGuid ޵ Guid !!! + EntityType touchedNpcType = 5; // EntityType 참조 ( EntityType.Beacon, 추후 추가될 수 있다 !!! ) + string entityInstantGuid = 6; // 터치한 Npc Instant Guid, GS2C_NTF_NPC_IN_SIGHT.UgcNpcEntity.entityInstantGuid 전달된 Guid !!! } - + message GS2C_ACK_CALIUM_CONVERTER { - double calium = 1; // Calium - float caliumForUser = 2; // 1 Calium ( 1 ) - float caliumEfficiency = 3; // ȯ ȿ + double calium = 1; // 총 Calium 수량 + float caliumForUser = 2; // 1일 제공 Calium 수량 ( 1인 기준 ) + float caliumEfficiency = 3; // 변환 에너지 효율 } - + message GS2C_ACK_CONVERT_MATERIAL_TO_CALIUM { - double totalCalium = 1; // Calium - float caliumForUser = 2; // 1 Calium ( 1 ) - CharInfo currencyInfo = 3; // ȭ - repeated Item delItem = 4; // Ҹ MATERIAL + double totalCalium = 1; // 변경된 총 Calium 수량 + float caliumForUser = 2; // 변경된 1일 제공 Calium 수량 ( 1인 기준 ) + CharInfo currencyInfo = 3; // 변경된 재화 수량 + repeated Item delItem = 4; // 소모된 MATERIAL 수량 } - // Ĺ û + // 파밍 시작 요청 결과 message GS2C_ACK_FARMING_START { - string farmingAnchorMetaId = 1; // MapMetaData ִ Ĺ Anchor Id + string farmingAnchorMetaId = 1; // MapMetaData에 있는 파밍 Anchor Id - FarmingSummary farmingSummary = 2; // Ĺ - - CommonResult commonResult = 3; // ֿ Delta + FarmingSummary farmingSummary = 2; // 파밍 요약 정보 + + CommonResult commonResult = 3; // 주요 결과 Delta 정보 } - // Ĺ û + // 파밍 취소 요청 결과 message GS2C_ACK_FARMING_CANCEL { - string farmingAnchorMetaId = 1; // MapMetaData ִ Ĺ Anchor Id + string farmingAnchorMetaId = 1; // MapMetaData에 있는 파밍 Anchor Id - FarmingSummary farmingSummary = 2; // Ĺ + FarmingSummary farmingSummary = 2; // 파밍 요약 정보 } - // UGC NPC û + // UGC NPC 편집 요청에 대한 응답 message GS2C_ACK_UGC_NPC_EDIT { - string editedUgcNpcMetaGuid = 1; // Ugc Npc Meta Id (GUID) + string editedUgcNpcMetaGuid = 1; // 편집된 Ugc Npc Meta Id (GUID) - string title = 3; // ŸƲ - string greeting = 5; // λ縻 - string introduction = 6; // ڱҰ + string title = 3; // 타이틀 + string greeting = 5; // 인사말 + string introduction = 6; // 자기소개 - string description = 7; // ij - string worldScenario = 8; // - - int32 defaultSocialActionId = 11; // ⺻ SocialAction Meta Id - repeated int32 habitSocialActionIds = 12; // ϴ SocialAction Meta Id - repeated int32 dialogueSocialActionIds = 13; // ȭ ⺻ SocialAction Meta Id + string description = 7; // 캐릭터 설명 + string worldScenario = 8; // 세계관 - repeated int32 hashTagMetaIds = 21; // ؽ± (˻), BeaconTagData.xlsx + int32 defaultSocialActionId = 11; // 기본 SocialAction Meta Id + repeated int32 habitSocialActionIds = 12; // 자주 사용하는 SocialAction Meta Id 목록 + repeated int32 dialogueSocialActionIds = 13; // 대화중 기본 SocialAction Meta Id 목록 - map tattooSlotInfos = 31; // Ÿ - - CommonResult commonResult = 41; // ȭ , Game_Define.CommonResult + repeated int32 hashTagMetaIds = 21; // 해시태그 목록 (검색용), BeaconTagData.xlsx 참조 + + map tattooSlotInfos = 31; // 타투 관련 정보 목록 + + CommonResult commonResult = 41; // 각종 변화된 결과 정보들, Game_Define.CommonResult 참조 } message GS2C_ACK_ENTER_MYHOME_EDIT_ROOM @@ -2506,27 +2617,27 @@ message ClientToGameRes string roomId = 4; } - // ij Ŀ͸¡ + // 캐릭터 외형 커스터마이징 결과 message GS2C_ACK_CHARACTER_APPEARANCE_CUSTOMIZE { - string toChangeCharacterGuid = 1; // UserGuid !!! - AppearanceCustomization appearCustomize = 2; // - CommonResult commonResult = 5; // ... + string toChangeCharacterGuid = 1; // 현재는 UserGuid 와 동일 !!! + AppearanceCustomization appearCustomize = 2; // 성공시 적용된 외형 정보 + CommonResult commonResult = 5; // 변경된 각종 정보들... } - // Ŀ͸¡ + // 비컨 외형 커스터마이징 결과 message GS2C_ACK_BEACON_APPEARANCE_CUSTOMIZE { - string toChangeUgcNpcMetaGuid = 1; // UgcNpc Ÿ Guid - AppearanceCustomization appearCustomize = 2; // - CommonResult commonResult = 5; // ... + string toChangeUgcNpcMetaGuid = 1; // 변경할 UgcNpc 메타 Guid + AppearanceCustomization appearCustomize = 2; // 성공시 적용된 외형 정보 + CommonResult commonResult = 5; // 변경된 각종 정보들... } - // Ű û + // 비컨 매각 요청 결과 message GS2C_ACK_BEACON_SELL { - string toSellUgcNpcMetaGuid = 1; // Ű UgcNpc Ÿ Guid - CommonResult commonResult = 5; // ... + string toSellUgcNpcMetaGuid = 1; // 매각할 UgcNpc 메타 Guid + CommonResult commonResult = 5; // 변경된 각종 정보들... } message GS2C_ACK_RENTAL_LAND_INFOS @@ -2548,17 +2659,17 @@ message ClientToGameRes { CommonResult commonResult = 1; } - + message GS2C_ACK_CHANGE_SCREEN_PAGE { - int32 screenPageNo = 1; // Screen Page ȣ + int32 screenPageNo = 1; // 변경된 Screen Page 번호 } message GS2C_ACK_RENEWAL_SHOP_PRODUCTS { ShopPacketInfo ProductInfo = 1; CommonResult commonResult = 4; } - + message GS2C_ACK_ENTITY_STATE_DANCE_START { uint32 metaId = 1; } @@ -2569,10 +2680,10 @@ message ClientToGameRes { string uploadAddress = 1; } - + message GS2C_ACK_MODIFY_LAND_INFO { - + } message GS2C_ACK_LAND_PROFIT_REPORT @@ -2595,34 +2706,34 @@ message ClientToGameRes { repeated BuildingProfitHistoryInfo historyInfos = 1; } - - // üũ û + + // 랜드 경매 체크 요청 결과 message GS2C_ACK_LAND_AUCTION_CHECK { LandAuctionInfo landAutionInfo = 1; - - LandAuctionBidType bidType = 6; // , LandAuctionState.Started LanduActionBidType.None ƴϴ !!! - - CurrencyType currencyType = 11; // ȭ (Ϲ ְ or ε ) - double bidPrice = 12; // (Ϲ ְ or ε ) + + LandAuctionBidType bidType = 6; // 입찰 상태, LandAuctionState.Started 상태일 경우 LanduActionBidType.None이 아니다 !!! + + CurrencyType currencyType = 11; // 직전 나의 입찰한 재화 종류 (일반 입찰은 최고가인 경우 or 블라인드 입찰에 참여한 경우) + double bidPrice = 12; // 직전 나의 입찰가 (일반 입찰은 최고가인 경우 or 블라인드 입찰에 참여한 경우) } - // û + // 랜드 경매 입찰 요청 결과 message GS2C_ACK_LAND_AUCTION_BID { - int32 landMetaId = 1; // LandData Meta Id - - LandAuctionBidType bidType = 5; // , Blind ְ ƾ Ѵ !!! - CommonResult commonResult = 11; // ȭ , Game_Define.CommonResult + int32 landMetaId = 1; // 경매 대상 LandData Meta Id + + LandAuctionBidType bidType = 5; // 입찰의 종류, Blind일 경우 최고 입찰가가 보다 높아야 한다 !!! + CommonResult commonResult = 11; // 각종 변화된 결과 정보들, Game_Define.CommonResult 참조 } - // û + // 랜드 경매 모든 요청 결과 message GS2C_ACK_LAND_AUCTION_SCHEDULE_ALL { repeated LandAuctionSummary landAuctionsSummaries = 1; } - // 丮 + // 랜드 경매 히스토리 message GS2C_ACK_LAND_AUCTION_HISTORY_ALL { repeated LandAuctionCompact landAuctionCompacts = 1; @@ -2634,10 +2745,10 @@ message ClientToGameRes double FixedExchangeRate = 2; google.protobuf.Timestamp nextEpochTime = 3; } - + message GS2C_ACK_CONVERT_EXCHANGER_CALIUM { - CharInfo currencyInfo = 1; // ȭ + CharInfo currencyInfo = 1; // 변경된 재화 수량 } message GS2C_ACK_LAND_RENTAL_INFO @@ -2654,7 +2765,7 @@ message ClientToGameRes { int32 idx = 1; } - + message GS2C_ACK_BEACON_SHOP_REGISTER_ITEM { string itemGuid = 1; @@ -2691,6 +2802,8 @@ message ClientToGameRes message GS2C_ACK_BEACON_SHOP_SEARCH_ITEM { repeated BeaconShopItemBoardInfo beaconShopItemBoardInfos = 1; + int32 currentPage = 2; + int32 totalPage = 3; } message GS2C_ACK_BEACON_SHOP_GET_ITEM_INFOS @@ -2700,14 +2813,64 @@ message ClientToGameRes int32 numOfReceiptNotReceived = 3; } - //============================================================================================= - // Ʋ Ŷ + message GS2C_ACK_BEACON_SHOP_RECENT_REGISTER_ITEMS + { + repeated BeaconShopItemBoardInfo recentRegisterBeaconShopItemBoardInfos = 1; + } - message GS2C_ACK_JOIN_BATTLE_INSTANCE + message GS2C_ACK_MOVE_TO_BEACON + { + ServerConnectInfo serverConnectInfo = 1; + CommonResult commonResult = 2; + } + + message GS2C_ACK_CONTENTS_MOVE + { + oneof place + { + ServerConnectInfo gameServerConnectInfo = 1; + ServerConnectInfo InstanceServerConnectInfo = 2; + Pos pos = 3; + } + CommonResult commonResult = 4; + } + + //============================================================================================= + // WorldEvent 월드이벤트 ack + //============================================================================================= + message GS2C_ACK_WORLD_EVENT_CONTRIBUTION + { + int32 worldEventId = 1; // 월드 이벤트 id + int32 contributionPoint = 2; // 기여도 포인트 + int32 contributionPointMax = 3; // 기여도 포인트 최대 + } + + message GS2C_ACK_RANKING_INFO + { + string rankingGuid = 1; + google.protobuf.Timestamp nextRefreshTime = 2; + repeated RankInfo rankInfos = 3; + } + + message GS2C_ACK_USE_COUPON + { + + } + + message GS2C_ACK_DESTROY_WEAPON_OBJECT + { + string anchorGuid = 1; + CommonResult commonResult = 2; + } + + //============================================================================================= + // 배틀 관련 패킷 설정 시작 + + message GS2C_ACK_JOIN_BATTLE_INSTANCE { ServerConnectInfo InstanceServerConnectInfo = 1; } - message GS2C_ACK_LEAVE_BATTLE_INSTANCE + message GS2C_ACK_LEAVE_BATTLE_INSTANCE { string gameServerAddr = 1; int32 gameServerPort = 2; @@ -2715,7 +2878,7 @@ message ClientToGameRes } message GS2C_ACK_BATTLE_PLAYER_DEATH { - + } message GS2C_ACK_BATTLE_PLAYER_RESPAWN { @@ -2735,23 +2898,61 @@ message ClientToGameRes google.protobuf.Timestamp packetProcessStartTime = 1; google.protobuf.Timestamp beforSendAckTime = 2; } - //Ʋ Ŷ + //배틀 관련 패킷 설정 종료 // ============================================================================================ - + // ============================================================================================ - //Ӹ Ŷ + //게임모드 관련 패킷 설정 시작 message GS2C_ACK_GAME_OBJECT_INTERACTION { string anchorGuid = 1; } - //Ӹ Ŷ + + message GS2C_ACK_GAME_PLAYER_DEATH + { + + } + + message GS2C_ACK_GAME_PLAYER_RESPAWN + { + Pos pos = 1; + } + + message GS2C_ACK_PREPARATION_FOR_LEAVING_GAME_INSTANCE + {} + + message GS2C_ACK_LEAVE_GAME_INSTANCE + { + string gameServerAddr = 1; + int32 gameServerPort = 2; + string otp = 3; + } + + message GS2C_ACK_GAME_MODE_LOAD_COMPLETE + {} + + + //게임모드 관련 패킷 설정 종료 // ============================================================================================ - - - + + //============================================================ + // Match + // 매칭 예약 응답 + message GS2C_ACK_MATCH_RESERVE + { + MatchStatusInfo matchStatus = 1; + } + + // 매칭 취소 응답 + message GS2C_ACK_MATCH_CANCEL + { + } + //============================================================ + + //============================================================================================= - // ACK Ŷ + // ACK 패킷 설정 //============================================================================================= ServerErrorCode errorCode = 1; oneof msg { @@ -2946,10 +3147,10 @@ message ClientToGameRes GS2C_ACK_NPC_INFO ackNpcInfo = 195; GS2C_ACK_NPC_TOUCH ackNpcTouch = 196; GS2C_ACK_CALIUM_CONVERTER ackCaliumConverter = 197; - GS2C_ACK_CONVERT_MATERIAL_TO_CALIUM ackConvertMaterialToCalium = 198; + GS2C_ACK_CONVERT_MATERIAL_TO_CALIUM ackConvertMaterialToCalium = 198; GS2C_ACK_FARMING_START ackFarmingStart = 199; GS2C_ACK_FARMING_CANCEL ackFarmingCancel = 200; - S2C_REQ_LARGE_PACKET ackLargePacket = 201; + GS2C_ACK_LARGE_PACKET ackLargePacket = 201; GS2C_ACK_UGC_NPC_EDIT ackUgcNpcEdit = 202; GS2C_ACK_ENTER_MYHOME_EDIT_ROOM ackEnterMyhomeEditRoom = 203; @@ -2962,7 +3163,7 @@ message ClientToGameRes GS2C_ACK_RENTAL_FLOOR_INFOS ackRentalFloorInfos = 208; GS2C_ACK_LAND_INFO ackLandInfo = 209; GS2C_ACK_RENT_FLOOR ackRentFloor = 210; - + GS2C_ACK_CHANGE_SCREEN_PAGE ackChangeScreenPage = 211; GS2C_ACK_RENEWAL_SHOP_PRODUCTS ackRenewalShopProducts = 212; GS2C_ACK_ENTITY_STATE_DANCE_START ackEntityStateDanceStart = 213; @@ -2994,9 +3195,16 @@ message ClientToGameRes GS2C_ACK_BEACON_SHOP_RECEIVE_PAYMENT_FOR_SALES ackBeaconShopReceivePaymentForSales = 234; GS2C_ACK_BEACON_SHOP_SEARCH_ITEM ackBeaconShopSearchItem = 235; GS2C_ACK_BEACON_SHOP_GET_ITEM_INFOS ackBeaconShopGetItemInfos = 236; + GS2C_ACK_BEACON_SHOP_RECENT_REGISTER_ITEMS ackBeaconShopRecentRegisterItems = 237; + GS2C_ACK_MOVE_TO_BEACON ackMoveToBeacon = 238; + GS2C_ACK_CONTENTS_MOVE ackContentsMove = 239; + GS2C_ACK_WORLD_EVENT_CONTRIBUTION ackWorldEventContribution = 240; + GS2C_ACK_RANKING_INFO ackRankingInfo = 241; + GS2C_ACK_USE_COUPON ackUseCoupon = 242; + GS2C_ACK_DESTROY_WEAPON_OBJECT ackDestroyWeaponObject = 243; //============================================================================================= - // Battle Ŷ - Pilot ̱ 100000 + // Battle 관련 패킷 정의 - Pilot 이기 때문에 100000번 부터 시작 GS2C_ACK_JOIN_BATTLE_INSTANCE ackJoinBattleInstance = 100000; GS2C_ACK_LEAVE_BATTLE_INSTANCE ackLeaveBattleInstance = 100001; GS2C_ACK_BATTLE_PLAYER_DEATH ackBattlePlayerDeath = 100002; @@ -3005,8 +3213,20 @@ message ClientToGameRes GS2C_ACK_PREPARATION_FOR_LEAVING_BATTLE_INSTANCE ackPreparationForLeavingInstance = 100005; //============================================================================================= - // GameMode Ŷ - ϴ ⿡ ϰ ߿ ø Ѵ. + // GameMode 관련 패킷 정의 - 일단 여기에 정의하고 개발 마무리 시점에 나중에 위로 올리던가 한다. GS2C_ACK_GAME_OBJECT_INTERACTION ackGameObjectInteraction = 100010; + GS2C_ACK_GAME_PLAYER_DEATH ackGamePlayerDeath = 100011; + GS2C_ACK_GAME_PLAYER_RESPAWN ackGamePlayerRespawn = 100012; + GS2C_ACK_PREPARATION_FOR_LEAVING_GAME_INSTANCE ackPreparationForLeavingGameInstance = 100013; + GS2C_ACK_LEAVE_GAME_INSTANCE ackLeaveGameInstance = 100014; + GS2C_ACK_GAME_MODE_LOAD_COMPLETE ackGameModeLoadComplete = 100015; + + //===================================================================== + // 게임 매칭 + GS2C_ACK_MATCH_RESERVE ackMatchReserve = 110001; + GS2C_ACK_MATCH_CANCEL ackMatchCancel = 110002; + //===================================================================== + } } @@ -3015,8 +3235,8 @@ message ClientToGameRes message ClientToGameMessage { //============================================================================================= - // NTF Ŷ - //============================================================================================= + // NTF 패킷 정의 + //==================================================`=========================================== message Chat { ChatType type = 1; @@ -3027,7 +3247,7 @@ message ClientToGameMessage } //============================================================================================= - // Actor ġ Sight + // Actor 위치 및 Sight 관련 //============================================================================================= message SetLocation { @@ -3039,31 +3259,31 @@ message ClientToGameMessage Pos pos = 2; } - // ü Ŷ => GC2C_NTF_PLAYER_IN_SIGHT + // 삭제 및 교체 패킷 => GC2C_NTF_PLAYER_IN_SIGHT message ActorInSight { repeated GameActor actorList = 1; } - // ü Ŷ => GC2C_NTF_PLAYER_MODIFY + // 삭제 및 교체 패킷 => GC2C_NTF_PLAYER_MODIFY message ActorModify { string actorGuid = 1; AvatarInfo avatarInfo = 2; ClothInfoOfAnotherUser clothInfo = 3; } - // ü Ŷ => GC2C_NTF_PLAYER_OUT_OF_SIGHT + // 삭제 및 교체 패킷 => GC2C_NTF_PLAYER_OUT_OF_SIGHT message ActorOutOfSight { repeated string actorGuid = 1; } - + //============================================================================================= - // Player Signt + // Player Signt 관련 //============================================================================================= message GS2C_NTF_PLAYER_IN_SIGHT { repeated GameActor toAddPlayerEntities = 1; } - + message GS2C_NTF_PLAYER_MODIFY { string toModifyPlayerGuid = 1; AvatarInfo avatarInfo = 2; @@ -3074,27 +3294,27 @@ message ClientToGameMessage message GS2C_NTF_PLAYER_OUT_OF_SIGHT { repeated string toOutOfPlayerGuids = 1; - repeated string debugInfos = 2; // ʿ Ȱ + repeated string debugInfos = 2; // 디버깅 필요시 활용 } //============================================================================================= - // NPC Signt + // NPC Signt 관련 //============================================================================================= message GS2C_NTF_NPC_IN_SIGHT { - repeated UgcNpcEntity toAddUgcNpcEntities = 1; //߰ UgcNpcEntity + repeated UgcNpcEntity toAddUgcNpcEntities = 1; //추가된 UgcNpcEntity 목록 } message GS2C_NTF_NPC_MODIFY { - UgcNpcEntity toModifyUgcNpcEntity = 1; // UgcNpcEntity + UgcNpcEntity toModifyUgcNpcEntity = 1; //수정된 UgcNpcEntity } message GS2C_NTF_NPC_OUT_OF_SIGHT { repeated string toOutOfEntityInstantGuids = 1; - repeated string debugInfos = 2; // ʿ Ȱ + repeated string debugInfos = 2; // 디버깅 필요시 활용 } //============================================================================================= - // Instance Room + // Instance Room 관련 //============================================================================================= message InstanceRoomMember { @@ -3158,7 +3378,7 @@ message ClientToGameMessage repeated int32 ownedList = 1; repeated SlotInfo equipList = 2; } - + message OwnedEmotionNoti { repeated int32 ownedList = 1; repeated SlotInfo equipList = 2; @@ -3198,13 +3418,13 @@ message ClientToGameMessage string actorGuid = 1; Buff buf = 2; } - + message StopBuffNoti { string actorGuid = 1; Buff buf = 2; } - + message ConcurrentUsersCountNoti { int32 count = 1; @@ -3232,7 +3452,7 @@ message ClientToGameMessage message PropInSight { repeated PropInfo propList = 1; } - + message PropModify { repeated PropInfo propList = 1; } @@ -3287,22 +3507,22 @@ message ClientToGameMessage repeated CartItemInfo itemList = 1; } - message FriendListNoti + message FriendListNoti { repeated FriendInfo friendList = 1; } - message FriendFolderListNoti + message FriendFolderListNoti { int32 folderOrderType = 1; repeated FriendFolder friendFolderList = 2; } - message InviteFriendToMyhomeNoti + message InviteFriendToMyhomeNoti { } - message ReplyInviteToMyhomeNoti + message ReplyInviteToMyhomeNoti { } - message BlockListNoti + message BlockListNoti { repeated BlockInfo BlockList = 1; } @@ -3323,7 +3543,7 @@ message ClientToGameMessage message ToFiendNotiBase { string senderId = 1; - string senderGuid = 2; + string senderGuid = 2; string senderNickName = 3; int32 senderState = 4; int32 senderMapId = 5; @@ -3332,27 +3552,27 @@ message ClientToGameMessage string receiverId = 8; } - message FriendLoginNoti + message FriendLoginNoti { ToFiendNotiBase baseInfo = 1; UserLocationInfo locationInfo = 2; } - message FriendLogoutNoti + message FriendLogoutNoti { ToFiendNotiBase baseInfo = 1; } - message FriendStateNoti + message FriendStateNoti { ToFiendNotiBase baseInfo = 1; UserLocationInfo locationInfo = 2; } - message ChangeTattooNoti + message ChangeTattooNoti { string accountGuid = 1; repeated TattooSlotInfo tattooInfoList = 2; } - message ReceiveInviteMyHomeNoti + message ReceiveInviteMyHomeNoti { ToFiendNotiBase baseInfo = 1; string receiverId = 2; @@ -3371,7 +3591,7 @@ message ClientToGameMessage ServerConnectInfo serverInfo = 1; } - message ReplyInviteMyhomeNoti + message ReplyInviteMyhomeNoti { int32 acceptOrRefuse = 1; string receiverId = 2; @@ -3428,7 +3648,7 @@ message ClientToGameMessage repeated QuestEndInfo endQuests = 1; } - message GS2C_NTF_QUEST_ASSIGN_META_INFO + message GS2C_NTF_QUEST_ASSIGN_META_INFO { repeated QuestAssignMetaInfo questAssignMeteInfos = 1; repeated QuestTaskMetaInfo questTaskMetaInfos = 2; @@ -3445,7 +3665,7 @@ message ClientToGameMessage ServerErrorCode errorCode = 1; string inviteUserGuid = 2; } - + message InvitePartyNoti { string inviteHostUserGuid = 1; string inviteHostUserNickname = 2; @@ -3456,7 +3676,7 @@ message ClientToGameMessage string inviteUserNickname = 2; BoolType result = 3; } - + message JoinPartyInfoNoti { string partyName = 1; string partyLeaderNickname = 2; @@ -3498,7 +3718,7 @@ message ClientToGameMessage message BanPartyNoti { } - + message ChangeNickNameNoti { string Guid = 1; string NickName = 2; @@ -3527,7 +3747,7 @@ message ClientToGameMessage message PartyVoteResultNoti { string voteTitle = 1; - + int32 resultTrue = 2; int32 resultFalse = 3; int32 abstain = 4; @@ -3585,7 +3805,7 @@ message ClientToGameMessage google.protobuf.Timestamp nextRefreshTime = 2; } - + message GS2C_NTF_UGQ_STATE_CHANGED { int64 composedQuestId = 1; @@ -3600,7 +3820,7 @@ message ClientToGameMessage message GS2C_NTF_HOST_FRIEND_LEAVED_HOME { - + } message GS2C_NTF_FRIEND_LEAVING_HOME @@ -3612,7 +3832,7 @@ message ClientToGameMessage message GS2C_NTF_CANCEL_SUMMON_PARTY_MEMBER { repeated Item items = 1; } - + message GS2C_NTF_CUSTOM_DEFINED_UI_ALL { map uiDatas = 1; } @@ -3625,7 +3845,7 @@ message ClientToGameMessage { repeated uint32 itemMetaids = 1; } - + message GS2C_NTF_SHOP_PRODUCT_RESET { int32 shopId = 1; @@ -3636,132 +3856,130 @@ message ClientToGameMessage string ownerGuid = 2; } - // Ugc Npc : ε + // 보유 Ugc Npc 정보 목록 통지 : 유저 로딩시점에 통지 message GS2C_NTF_UGC_NPC_ALL_LOAD { - // - map hasUgcNpcSummaries = 1; // UgcNpcSummary Ÿ - // - map hasUgcNpcItems = 2; // UgcNpcItem , Game_Define.UgcNpcItem + // + map hasUgcNpcSummaries = 1; // 보유 UgcNpcSummary 메타 정보 + // + map hasUgcNpcItems = 2; // 보유 UgcNpcItem들의 정보, Game_Define.UgcNpcItem } - // Ugc Npc + // 보유 Ugc Npc 삭제 통지 message GS2C_NTF_UGC_NPC_DELETION { - string deletedUgcNpcMetaGuid = 1; // Ugc Npc Meta Guid - CommonResult commonResult = 11; // ȭ , Game_Define.CommonResult + string deletedUgcNpcMetaGuid = 1; // 삭제된 Ugc Npc Meta Guid + CommonResult commonResult = 11; // 각종 변화된 결과 정보들, Game_Define.CommonResult 참조 } message GS2C_NTF_CRAFT_HELP_UPDATE { string anchor_guid = 1; google.protobuf.Timestamp craftFinishTime = 2; } - + message GS2C_NTF_SEASON_PASS_INFO { - uint32 metaId = 1; // SeasonPass Meta Id - uint32 exp = 2; // SeasonPass ġ - int32 grade = 3; // SeasonPass ִ - repeated int32 takenRewards = 4; // ް - BoolType isChargedPass = 5; // ߴ + uint32 metaId = 1; // SeasonPass의 Meta Id + uint32 exp = 2; // SeasonPass의 경험치 + int32 grade = 3; // SeasonPass의 보상 받을수 있는 등급 + repeated int32 takenRewards = 4; // 받은 보상 등급값 + BoolType isChargedPass = 5; // 유료 결재 했는지 } - // Ŷ ú ŸӾƿ - message S2C_NTF_LARGE_PACKET_TIMEOUT + // 패킷 리시브 에러 통지 + message GS2C_NTF_LARGE_PACKET_PROCESS_ERROR { - string uid = 1; - google.protobuf.Timestamp lastRecvTime = 2; - google.protobuf.Timestamp lastCheckTime = 3; + ServerErrorCode errorCode = 1; } message GS2C_NTF_MYHOME_UGC_INFO { - string myhomeGuid = 1; + string myhomeGuid = 1; MyhomeUgcInfo myhomeUgcInfo = 2; } - // EntityType.Beacon + // EntityType.Beacon 상세 정보 message GS2C_NTF_BEACON_DETAIL_INFO { string ugcNpcMetaGuid = 1; // Ugc Npc Meta Id (GUID) - string ownerUserGuid = 2; // Ugc Npc Meta UserGuid - - UgcNpcSummary ugcNpcSummary = 5; // Ugc Npc Meta - - int64 likeCount = 11; // ƿ - int64 dialogCount = 12; // ȭ + string ownerUserGuid = 2; // Ugc Npc Meta 정보의 소유자의 UserGuid + + UgcNpcSummary ugcNpcSummary = 5; // Ugc Npc Meta 요약 정보 + + int64 likeCount = 11; // 좋아요 누적 개수 + int64 dialogCount = 12; // 대화 누적 개수 } - // EntityType.Beacon ȣۿ + // EntityType.Beacon 상호작용 정보 message GS2C_NTF_BEACON_INTERACTION_INFO { - string entityInstantGuid = 1; // ġ Npc Instant Guid, GS2C_NTF_NPC_IN_SIGHT.UgcNpcEntity.entityInstantGuid ޵ Guid !!! + string entityInstantGuid = 1; // 터치한 Npc Instant Guid, GS2C_NTF_NPC_IN_SIGHT.UgcNpcEntity.entityInstantGuid 전달된 Guid !!! - UgcNpcInteraction ugcNpcInteraction = 5; // Ugc Npc ȣۿ + UgcNpcInteraction ugcNpcInteraction = 5; // Ugc Npc 상호작용 정보 } - - // ugc npc rank Ƽ + + // ugc npc rank 갱신 노티 message GS2C_NTF_UGC_NPC_RANK_REFRESH { } - // owner ˸ + // 제작 owner에게 도움 받은 알림 message GS2C_NTF_CRAFT_HELPED { int32 helpedCount = 1; string helpUserNickName = 2; } - // Ĺ : ε + // 진행중인 파밍 목록 통지 : 유저 로딩시점에 통지 message GS2C_NTF_FARMING_ALL_LOAD { - repeated FarmingSummary progressFarmingSummaries = 1; // Ĺ + repeated FarmingSummary progressFarmingSummaries = 1; // 진행중인 파밍 목록 } - // Ĺ : þ߱dz ִ ÷̾鿡 . + // 파밍 시작 통지 : 시야권내에 있는 플레이어들에게 보낸다. message GS2C_NTF_FARMING_START { - string farmingAnchorMetaId = 1; // MapMetaData ִ Ĺ Anchor Id - FarmingSummary farmingSummary = 2; // Ĺ + string farmingAnchorMetaId = 1; // MapMetaData에 있는 파밍 Anchor Id + FarmingSummary farmingSummary = 2; // 파밍 요약 정보 } - // Ĺ : þ߱dz ִ ÷̾鿡 . + // 파밍 취소 통지 : 시야권내에 있는 플레이어들에게 보낸다. message GS2C_NTF_FARMING_CANCEL { - string farmingAnchorMetaId = 1; // MapMetaData ִ Ĺ Anchor Id - FarmingSummary farmingSummary = 2; // Ĺ + string farmingAnchorMetaId = 1; // MapMetaData에 있는 파밍 Anchor Id + FarmingSummary farmingSummary = 2; // 파밍 요약 정보 } - // Ĺ + // 파밍 종료 통지 message GS2C_NTF_FARMING_END { - string farmingAnchorMetaId = 1; // MapMetaData ִ Ĺ Anchor Id + string farmingAnchorMetaId = 1; // MapMetaData에 있는 파밍 Anchor Id - FarmingSummary farmingSummary = 2; // Ĺ + FarmingSummary farmingSummary = 2; // 파밍 요약 정보 } - // ƮƮ - message GS2C_NTF_ATTRIBUTE_UPDATE + // 어트리뷰트 갱신 + message GS2C_NTF_ATTRIBUTE_UPDATE { - OwnerEntityType ownerEntityType = 1; // ƼƼ - string ownerGuid = 2; // ƼƼ Guid + OwnerEntityType ownerEntityType = 1; // 소유자 엔티티의 종류 + string ownerGuid = 2; // 소유자 엔티티의 Guid AbilityInfo attributeInfo = 3; // AbilityInfo } - // ʰ ˸ + // 보유 금전량 초과 알림 통지 message GS2C_NTF_CURRENCY_MAX_ALERT { - CurrencyType currencyType = 1; // ʰ ȭ - double maxMoney = 2; // ش ȭ ִ + CurrencyType currencyType = 1; // 초과한 재화 종류 + double maxMoney = 2; // 해당 재화 종류의 최대 금전량 } - // Beam (cave_duck ) ˸ + // Beam충전 (cave_duck에 충전) 알림 message GS2C_NTF_BEAM_CHARGE { } - // Ȩ Ȩ ˸ + // 마이홈 오너 마이홈 편집 알림 message GS2C_NTF_MYHOME_HOST_ENTER_EDIT_ROOM { } @@ -3786,13 +4004,13 @@ message ClientToGameMessage repeated ModifyOwnedRentalInfo modifyOwnedRentalInfos = 1; } - // Ѵ. + // 비컨의 상태 변경시 통지 한다. message GS2C_NTF_BEACON_COMPACT_UPDATE { string ugcNpcMetaGuid = 1; // Ugc Npc Meta Id (GUID) - string ownerUserGuid = 2; // Ugc Npc Meta UserGuid - - UgcNpcCompact ugcNpcCompact = 5; // Ugc Npc + string ownerUserGuid = 2; // Ugc Npc Meta 정보의 소유자의 UserGuid + + UgcNpcCompact ugcNpcCompact = 5; // Ugc Npc 간소한 요약 정보 } message GS2C_NTF_QUEST_REWARD @@ -3805,40 +4023,75 @@ message ClientToGameMessage repeated SwitchingPropState switchingPropState = 1; } - // ÷̾ αν ߻ - // + // 플레이어 게임 서버 로그인시 발생 + // 랜드 경매 목록 통지 message GS2C_NTF_LAND_AUCTION_ALL_LOAD { - repeated LandAuctionSummary landAuctionsSummaries = 1; // ( ִ !!!) + repeated LandAuctionSummary landAuctionsSummaries = 1; // 랜드 경매 목록 (랜드는 있으나 랜드 경매 정보는 없을 수 있다 !!!) } - // ߻ - // + // 랜드 경매 낙찰시 발생 + // 랜드 경매 낙찰 통지 message GS2C_NTF_LAND_AUCTION_WINNING_BID { - string winningUserNickname = 1; // г - int32 landMetaId = 5; // LandData Meta Id + string winningUserNickname = 1; // 낙찰 받은 유저의 닉네임 + int32 landMetaId = 5; // 낙찰 받은 LandData Meta Id } - // ְ + // 랜드 경매 최고 입찰자 변경 통지 message GS2C_NTF_LAND_AUCTION_HIGHEST_BIDDER_CHANGE { - int32 landMetaId = 1; // LandData Meta Id - - CurrencyType currencyType = 5; // ȭ - double highestBidPrice = 6; // ְ - string highestBidUserGuid = 7; // ְ ĺŰ - string highestBidUserNickname = 8; // ְ г + int32 landMetaId = 1; // 경매 대상 LandData Meta Id + + CurrencyType currencyType = 5; // 입찰한 재화 종류 + double highestBidPrice = 6; // 최고 입찰가 + string highestBidUserGuid = 7; // 최고가 입찰자 식별키 + string highestBidUserNickname = 8; // 최고가 입찰자 닉네임 } + message GS2C_NTF_BANNER_INFOS + { + repeated BannerInfo bannerInfos = 1; + } + message GS2C_NTF_RANKING_SCHEDULE_INFOS + { + repeated RankingScheduleInfo rankingScheduleInfos = 1; + } + + //============================================================================================= + // WorldEvent 월드이벤트 NTF + //============================================================================================= + message GS2C_NTF_WORLD_EVENT_SCHEDULE + { + repeated WordEventSchedule schedules = 1; // 월드이벤트 스케줄 목록 + } + + //================================================================================================= + // GameMode 관련 Notify //================================================================================================= - // GameMode Notify - //================================================================================================= - message GS2C_NTF_GAME_STATE_UPDATE_NOTI + message GS2C_NTF_GAME_MODE_STATE_UPDATE { GameModeState currentState = 1; - google.protobuf.Timestamp nextUpdatableTime = 2; //ð · ȯǴ ð ° ٲ𽺵 ִ.ʿ ó + google.protobuf.Timestamp nextUpdatableTime = 2; //시간이 지남에 따라 다음 상태로 변환되는 시간 그전에 상태가 바뀔스도 있다.필요없으면 삭제 처리 + StateUpdateReasonType updateReason = 3; + } + + message GS2C_NTF_GAME_MODE_OVERALL_INFO + { + google.protobuf.Timestamp gameCreateTime = 1; + google.protobuf.Timestamp serverTime = 2; + } + + message GS2C_NTF_GAME_MODE_PLAY_REGULATION + { + repeated MatchRestriction matchRestriction = 1; + repeated GameModePlayPenalty gamePlayPenalty = 2; + } + + message GS2C_NTF_GAME_MODE_OBJECT_INFO + { + repeated GameModeObjectInfo gameModeObjectInfos = 1; } message GS2C_NTF_POD_COMBAT_STATE @@ -3858,6 +4111,30 @@ message ClientToGameMessage Pos pos = 2; } + message GS2C_NTF_GAME_PLAYER_RESPAWN + { + string respawnUserGuid = 1; + Pos pos = 2; + } + + message GS2C_NTF_RUN_RACE_RESULT_SUMMARY + { + int32 totalPlayUserCount = 1; + repeated RunRaceResult raceResults = 2; + repeated GameModeRewardResult raceRewardResults = 3; + repeated GameModeRewardResultWithRank raceRewardResultsWithRank = 4; + } + + + + + + message GS2C_NTF_GAME_USER_KICK + { + GameModeKickReason reason = 1; + } + + message GS2C_NTF_BATTLE_OBJECT_STATE_INFO { repeated BattleObjectInfo battleObjectInfos = 1; @@ -3872,7 +4149,7 @@ message ClientToGameMessage message GS2C_NTF_BATTLE_INSTANCE_DESTROYED { } - + message GS2C_NTF_BATTLE_INSTANCE_REWARD { string acquireUserGuid = 1; @@ -3883,28 +4160,33 @@ message ClientToGameMessage { repeated BattleEventInfo battleEvent = 1; } - + message GS2C_NTF_P2P_HOST_UPDATE { - string hostUserGuid = 1; - } - - message GS2C_NTF_BATTLE_INSTANCE_STATE - { - google.protobuf.Timestamp createTime = 1; //ش νϽ ð(̺Ʈ ð ) - - // ߿ oneofmessage ٸ 嵵 ߰ - BattleRoundStateType roundStateType = 2; - google.protobuf.Timestamp roundStateStartTime = 3; - int32 currentRound = 4; - int32 rewardedStep = 5; // - int32 chargedStep = 6; // + string hostUserGuid = 1; } - // ߰ or + message GS2C_NTF_READY_POS + { + repeated GameModePlayerPositionInfo gameModePlayerPositionInfos = 1; + } + + message GS2C_NTF_BATTLE_INSTANCE_STATE + { + google.protobuf.Timestamp createTime = 1; //해당 인스턴스 생성 시간(이벤트 생성시간 기준) + + GameModeState roundStateType = 2; + google.protobuf.Timestamp roundStateStartTime = 3; + int32 currentRound = 4; + int32 rewardedStep = 5; //보상 받은 스텝 + int32 chargedStep = 6; //충전된 스텝 + google.protobuf.Timestamp nextUpdatableTime = 7; + } + + // 랜드 경매 추가 or 갱신 통지 message GS2C_NTF_LAND_AUCTION_SUMMARY { - LandAuctionSummary landAuctionSummary = 1; + LandAuctionSummary landAuctionSummary = 1; } message GS2C_NTF_PREPARATION_FOR_LEAVING_BATTLE_INSTANCE @@ -3912,23 +4194,48 @@ message ClientToGameMessage string leavingUserGuid = 1; } - // û ϵ ϴ Ŷ - ش UI ѵΰ + //비컨 개인 상점 정보 재요청 하도록 하는 패킷 - 해당 비컨의 UI를 켜두고 있을경우 message GS2C_NTF_BEACON_SHOP_REFRESH { string beaconGuid = 1; BoolType hasBeaconShopItem = 2; } - // + // 아이템 삭제 통지 message GS2C_NTF_ITEM_DELETE { string deletedItemGuid = 1; int32 deletedCount = 2; } + //========================================================== + // Match + + // 매칭 상태 통지 + message GS2C_NTF_MATCH_STATUS + { + MatchStatusInfo matchStatusInfo = 1; + } + + // 매칭 결과 통지 + message GS2C_NTF_MATCH_RESULT + { + MatchStatusType matchStatus = 1; + ServerConnectInfo instanceServerConnectInfo = 2; // 커넥션 정보 - 인스턴스 id 포함 + int32 gameModeId = 3; // 게임 모드 아이디 + } + + // 매칭 리전 핑 정보 - 게임 서버 접속 시 전송됨 + message GS2C_NTF_MATCH_REGION_INFO + { + repeated MatchRegionInfo MatchRegionInfos = 1; + } + + //========================================================== + //============================================================================================= - // NTF Ŷ + // NTF 패킷 설정 //============================================================================================= oneof msg { @@ -4039,7 +4346,7 @@ message ClientToGameMessage GS2C_NTF_CRAFT_RECIPES ntfCraftRecipesInfo = 107; GS2C_NTF_ITEM_FIRST_PURCHASE_HISTORY ntfItemFirstPurchaseHistory = 108; GS2C_NTF_SHOP_PRODUCT_RESET ntfShopProductChange = 109; - + GS2C_NTF_UGC_NPC_ALL_LOAD ntfUgcNpcAllLoad = 110; GS2C_NTF_CRAFT_INFO ntfCraftInfos = 111; GS2C_NTF_CRAFT_HELP_UPDATE ntfCraftHelpUpdate = 112; @@ -4064,7 +4371,7 @@ message ClientToGameMessage GS2C_NTF_UGC_NPC_RANK_REFRESH ntfUgcNpcRankRefresh = 125; GS2C_NTF_CRAFT_HELPED ntfCraftHelped = 126; GS2C_NTF_FARMING_END ntfFarmingEnd = 127; - S2C_NTF_LARGE_PACKET_TIMEOUT ntfLargePacketTimeout = 128; + GS2C_NTF_LARGE_PACKET_PROCESS_ERROR ntfLargePacketProcessError = 128; GS2C_NTF_ATTRIBUTE_UPDATE ntfAttributeUpdate = 129; @@ -4102,9 +4409,11 @@ message ClientToGameMessage GS2C_NTF_BEACON_SHOP_REFRESH ntfBeaconShopRefresh = 150; GS2C_NTF_ITEM_DELETE ntfItemDelete = 151; + GS2C_NTF_BANNER_INFOS ntfBannerInfos = 152; + GS2C_NTF_RANKING_SCHEDULE_INFOS ntfRankingScheduleInfos = 153; + + GS2C_NTF_WORLD_EVENT_SCHEDULE ntfWorldEventSchedule = 161; - - //Battle GS2C_NTF_POD_COMBAT_STATE ntfPodCombatState = 100000; GS2C_NTF_BATTLE_PLAYER_DEATH ntfBattlePlayerDeath = 100001; @@ -4119,7 +4428,19 @@ message ClientToGameMessage GS2C_NTF_P2P_HOST_UPDATE ntfP2PHostUpdate = 100010; //GameMode - GS2C_NTF_GAME_STATE_UPDATE_NOTI ntfGameStateUpdate = 110000; + GS2C_NTF_GAME_MODE_STATE_UPDATE ntfGameModeStateUpdate = 110000; + GS2C_NTF_GAME_MODE_OVERALL_INFO ntfGameModeOverallInfo = 110001; + GS2C_NTF_GAME_MODE_OBJECT_INFO ntfGameModeObjectInfo = 110002; + GS2C_NTF_GAME_PLAYER_RESPAWN ntfGamePlayerRespawn = 110003; + GS2C_NTF_RUN_RACE_RESULT_SUMMARY ntfRunRaceResultSummary = 110004; + GS2C_NTF_GAME_USER_KICK ntfGameUserKick = 110005; + GS2C_NTF_READY_POS ntfReadyPos = 110006; + GS2C_NTF_GAME_MODE_PLAY_REGULATION ntfGameModePlayRegulation = 110007; + + //Match + GS2C_NTF_MATCH_STATUS ntfMatchStatus = 120001; + GS2C_NTF_MATCH_RESULT ntfMatchResult = 120002; + GS2C_NTF_MATCH_REGION_INFO ntfMatchRegionInfo = 120003; } } @@ -4130,4 +4451,4 @@ message ClientToGame ClientToGameRes response = 2; ClientToGameMessage message = 3; } -} \ No newline at end of file +} diff --git a/Protocol/proto/Define_Common.proto b/Protocol/proto/Define_Common.proto index 2b1cc63..081d065 100644 --- a/Protocol/proto/Define_Common.proto +++ b/Protocol/proto/Define_Common.proto @@ -6,19 +6,19 @@ option java_multiple_files = true; option java_package = "com.caliverse.admin.domain.RabbitMq.message"; //============================================================================================= -// ڵ带 ۼ Ѵ. -// -// ŸԵ ۼ Ѵ. (: , , ġ, ) +// 공통 정의 코드를 작성 한다. +// +// 공통적 성격의 정의 타입들을 작성 한다. (예: 계정, 유저, 위치, 비게임적인 정보 등등) // - kangms -// enum Ÿ ̽ !!! -// enum Ÿ԰ Ǹ Compile ߻ Ѵ. !!! -// message Ͽ Complie dz ڵ忡 -// xxxxx.Types.xxxx ߰ ڵ Ų. -// ׷ Prefix enum ߰ ߴ. +// enum 타입 관련 이슈 !!! +// enum 타입간 내부 정의명이 동일할 경우 Compile 에러가 발생 한다. !!! +// message 로 정의하여 도메인을 줄 경우 Complie 은 되나 생성된 코드에 +// xxxxx.Types.xxxx 인위적인 추가 도메인이 생겨 코드 가독성을 저해 시킨다. +// 그래서 차악으로 Prefix 로 동일한 enum 명을 추가 했다. //============================================================================================= -// +// 모듈 종류 enum ModuleId { ModuleId_None = 0; @@ -30,20 +30,20 @@ enum ModuleId ModuleId_RabbitMqConnector = 5; ModuleId_S3Connector = 6; ModuleId_ProudNetListener = 7; - + } // bool enum enum BoolType { BoolType_None = 0; - + BoolType_True = 1; BoolType_False = 2; } -// +// 계정 종류 enum AccountType { AccountType_None = 0; @@ -52,7 +52,7 @@ enum AccountType AccountType_Apple = 2; } -// +// 서비스의 종류 enum ServiceCategory { ServiceCategory_None = 0; @@ -60,7 +60,7 @@ enum ServiceCategory ServiceCategory_Caliverse = 1; } -// Ÿ +// 서비스 버전 타입 enum ServiceType { ServiceType_None = 0; @@ -71,45 +71,47 @@ enum ServiceType ServiceType_Live = 4; } -// URL Ÿ +// 서버 URL 타입 enum ServerUrlType { ServerUrlType_None = 0; - ServerUrlType_BillingApiServerUrl = 1; // Api URL - ServerUrlType_ChatAiApiServerUrl = 2; // Chat Ai Api URL - ServerUrlType_MyhomeEditGuideUrl = 3; // MyHome Api URL () - ServerUrlType_WebLinkUrlSeasonPass = 4; // WebLink Api URL () - ServerUrlType_CaliumConverterWebGuide = 5; // Į Api URL () - ServerUrlType_S3ResourceImageUrl = 6; // ̹ ҽ URL - ServerUrlType_RentalGuideURL = 7; // Ż ̵ URL () - ServerUrlType_LandAuctionWebGuide = 8; // ̵ URL () - ServerUrlType_LandManageGuideURL = 9; // ̵ URL () - ServerUrlType_Calium_Exchange_Web1 = 10; // Į ȯ ũ (ȯ UI ߾ ) () - ServerUrlType_Calium_Exchange_Web2 = 11; // Į ȯ ũ (ȯ UI ϴ) () - ServerUrlType_WebLinkURLCurrency = 12; // ȭ ư ũ ּ () - ServerUrlType_WebLinkURLSeasonPass1 = 13; // н 1 ũ ּ () - ServerUrlType_WebLinkURLSeasonPass2 = 14; // н 2 ũ ּ () - ServerUrlType_WebLinkURLSeasonPass3 = 15; // н 3 ũ ּ () - ServerUrlType_WebLinkURLSeasonPass4 = 16; // н 4 ũ ּ () - ServerUrlType_WebLinkURLSeasonPass5 = 17; // н 5 ũ ּ () + ServerUrlType_BillingApiServerUrl = 1; // 결제 Api 서버 URL + ServerUrlType_ChatAiApiServerUrl = 2; // Chat Ai Api 서버 URL + ServerUrlType_MyhomeEditGuideUrl = 3; // MyHome Api 서버 URL (언어별) + ServerUrlType_WebLinkUrlSeasonPass = 4; // WebLink Api 서버 URL (언어별) + ServerUrlType_CaliumConverterWebGuide = 5; // 칼리움 컨버터 Api 서버 URL (언어별) + ServerUrlType_S3ResourceImageUrl = 6; // 이미지 리소스 URL + ServerUrlType_RentalGuideURL = 7; // 렌탈 가이드 URL (언어별) + ServerUrlType_LandAuctionWebGuide = 8; // 랜드 경매 가이드 페이지 URL (언어별) + ServerUrlType_LandManageGuideURL = 9; // 랜드 관리 가이드 페이지 URL (언어별) + ServerUrlType_Calium_Exchange_Web1 = 10; // 칼리움 교환소 웹 페이지 링크 (교환소 UI 중앙 상단) (언어별) + ServerUrlType_Calium_Exchange_Web2 = 11; // 칼리움 교환소 웹 페이지 링크 (교환소 UI 우측 하단) (언어별) + ServerUrlType_WebLinkURLCurrency = 12; // 재화 구매 버튼 웹페이지 링크 주소 (언어별) + ServerUrlType_WebLinkURLSeasonPass1 = 13; // 시즌패스 1 웹페이지 링크 주소 (언어별) + ServerUrlType_WebLinkURLSeasonPass2 = 14; // 시즌패스 2 웹페이지 링크 주소 (언어별) + ServerUrlType_WebLinkURLSeasonPass3 = 15; // 시즌패스 3 웹페이지 링크 주소 (언어별) + ServerUrlType_WebLinkURLSeasonPass4 = 16; // 시즌패스 4 웹페이지 링크 주소 (언어별) + ServerUrlType_WebLinkURLSeasonPass5 = 17; // 시즌패스 5 웹페이지 링크 주소 (언어별) + ServerUrlType_WebLinkURLSeasonPass6 = 18; // 시즌패스 6 웹페이지 링크 주소 (언어별) + ServerUrlType_WebLinkURLSeasonPass7 = 19; // 시즌패스 7 웹페이지 링크 주소 (언어별) } -//  URL +// 언어별 서버 URL message ServerUrlWithLanguage { - LanguageType langType = 1; - string targetUrl = 2; + LanguageType langType = 1; + string targetUrl = 2; } -// URL +// 서버 URL message ServerUrl { - ServerUrlType serverUrlType = 1; // URL URL Ÿ - repeated ServerUrlWithLanguage serverUrlWithLanguages = 2; //  URL + ServerUrlType serverUrlType = 1; // 접속할 URL의 서버 URL 타입 + repeated ServerUrlWithLanguage serverUrlWithLanguages = 2; // 언어별 서버 URL 목록 } -// +// 서버 종류 enum ServerType { ServerType_None = 0; @@ -125,9 +127,10 @@ enum ServerType ServerType_UgqAdmin = 9; ServerType_UgqIngame = 10; ServerType_BrokerApi = 11; + ServerType_Match = 12; } -// ϸ +// 오토 스케일링 서버 종류 enum AutoScaleServerType { AutoScaleServerType_None = 0; @@ -138,7 +141,7 @@ enum AutoScaleServerType AutoScaleServerType_Chat = 4; } -// +// 게임 서버 종류 enum GameServerType { GameServerType_None = 0; @@ -148,14 +151,14 @@ enum GameServerType } -// ̽ +// 디바이스 종류 enum DeviceType { DeviceType_None = 0; // Desktop : IBM PC compatible DeviceType_WindowsPC = 1; - + // Mobile : Apple DeviceType_IPhone = 5; DeviceType_Mac = 6; @@ -167,7 +170,7 @@ enum DeviceType DeviceType_Oculus = 15; } -// Os +// Os 종류 enum OsType { OsType_None = 0; @@ -177,7 +180,7 @@ enum OsType OsType_Ios = 3; } -// ÷ +// 플랫폼 종류 enum PlatformType { PlatformType_None = 0; @@ -188,7 +191,7 @@ enum PlatformType PlatformType_Apple = 4; } -// Account +// Account 생성 종류 enum AccountCreationType { AccountCreationType_None = 0; @@ -198,7 +201,7 @@ enum AccountCreationType AccountCreationType_Bot = 3; } -// +// 컨텐츠 종류 enum ContentsType { ContentsType_None = 0; @@ -207,7 +210,7 @@ enum ContentsType ContentsType_DressRoom = 2; ContentsType_Concert = 3; ContentsType_Movie = 4; - ContentsType_Instance = 5; // ⺻ νϽ, Ư + ContentsType_Instance = 5; // 기본 인스턴스, 특별한 로직이 없는 경우 ContentsType_Meeting = 6; ContentsType_BeaconCreateRoom = 7; ContentsType_BeaconEditRoom = 8; @@ -216,12 +219,13 @@ enum ContentsType ContentsType_BeaconCustomizeRoom = 11; ContentsType_BattleRoom = 12; ContentsType_ArcadeRunning = 13; + ContentsType_GameRoom = 14; } -// ij +// 캐릭터 종족 종류 enum CharRace { - CharRace_None = 0; + CharRace_None = 0; CharRace_Latino = 1; CharRace_Caucasian = 2; @@ -231,7 +235,7 @@ enum CharRace CharRace_Pacificislander = 6; } -// +// 계정 관리 레벨 종류 enum AuthAdminLevelType { AuthAdminLevelType_None = 0; @@ -241,83 +245,83 @@ enum AuthAdminLevelType AuthAdminLevelType_Developer = 3; } -// ǥ ISO 639-1 ڵ - kangms +// 세계 표준 ISO 639-1 코드 기준 - kangms enum LanguageType { - LanguageType_None = 0; + LanguageType_None = 0; - LanguageType_ko = 1; // ѱ(⺻) - LanguageType_en = 2; // - //LanguageType_th = 3; // ± - LanguageType_ja = 4; // Ϻ - //LanguageType_zh = 5; // ߱ - //LanguageType_fr = 6; // - //LanguageType_de = 7; // Ͼ - //LanguageType_es = 8; // ξ - //LanguageType_ru = 9; // þƾ - //LanguageType_ar = 10; // ƶ + LanguageType_ko = 1; // 한국어(기본값) + LanguageType_en = 2; // 영어 + //LanguageType_th = 3; // 태국 + LanguageType_ja = 4; // 일본어 + //LanguageType_zh = 5; // 중국어 + //LanguageType_fr = 6; // 프랑스어 + //LanguageType_de = 7; // 독일어 + //LanguageType_es = 8; // 스페인어 + //LanguageType_ru = 9; // 러시아어 + //LanguageType_ar = 10; // 아랍어 } -// ǰ +// 상품 종류 enum ProductType { ProductType_None = 0; - - ProductType_Currency = 1; // ȭ - ProductType_Item = 2; // + + ProductType_Currency = 1; // 재화 + ProductType_Item = 2; // 아이템 } -// α +// 로그인 방식의 종류 enum LoginMethodType { LoginMethodType_None = 0; - LoginMethodType_ClientStandalone = 1; // Ŭ̾Ʈ ܵ α - LoginMethodType_SsoAccountAuthWithLauncher = 2; // հ Բ ó α + LoginMethodType_ClientStandalone = 1; // 클라이언트 단독 로그인 + LoginMethodType_SsoAccountAuthWithLauncher = 2; // 통합계정인증과 함께 런처 로그인 } -// α ǹ +// 로그인 실패 의미의 종류 enum LoginFailureReasonType { LoginFailureReasonType_None = 0; - LoginFailureReasonType_ProcessingException = 1; // ó߿ ܰ ߻ ߽ϴ. - LoginFailureReasonType_AuthenticationFailed = 2; // Դϴ. - LoginFailureReasonType_UserValidCheckFailed = 3; // ŷڼ üũ Դϴ. + LoginFailureReasonType_ProcessingException = 1; // 처리중에 예외가 발생 했습니다. + LoginFailureReasonType_AuthenticationFailed = 2; // 인증 실패 입니다. + LoginFailureReasonType_UserValidCheckFailed = 3; // 유저 신뢰성 체크 실패 입니다. } -// α׾ƿ ǹ +// 로그아웃의 의미 enum LogoutReasonType { LogoutReasonType_None = 0; - LogoutReasonType_ExitToService = 1; // - LogoutReasonType_EnterToGame = 2; // - LogoutReasonType_GoToGame = 3; // ̵ ϱ + LogoutReasonType_ExitToService = 1; // 서비스 종료 + LogoutReasonType_EnterToGame = 2; // 게임 서버 입장을 위해 + LogoutReasonType_GoToGame = 3; // 게임 서버로 이동 하기 위해 LogoutReasonType_DuplicatedLogin = 4; } -// +// 계정 제재 종류 enum AccountSactionType { AccountSactionType_None = 0; - AccountSactionType_BadBhavior = 1; // ų - AccountSactionType_InvapproprivateName = 2; // Ұ ̸ - AccountSactionType_CashTransaction = 3; // ij Ʈ - AccountSactionType_GameInterference = 4; // - AccountSactionType_ServiceInterference = 5; //  - AccountSactionType_AccountImpersonation = 6; // - AccountSactionType_BugAbuse = 7; // /¡ - AccountSactionType_IllegalProgram = 8; // α׷ ҹ - AccountSactionType_PersonalInfo_Leak = 9; // - AccountSactionType_AdminImpersonation = 10; //  Ī + AccountSactionType_BadBhavior = 1; // 비매너 행위 + AccountSactionType_InvapproprivateName = 2; // 불건전 이름 사용 + AccountSactionType_CashTransaction = 3; // 캐시 트랜잭션 + AccountSactionType_GameInterference = 4; // 게임 진행 방해 + AccountSactionType_ServiceInterference = 5; // 운영서비스 방해 + AccountSactionType_AccountImpersonation = 6; // 계정 도용 + AccountSactionType_BugAbuse = 7; // 버그/어뷰징 + AccountSactionType_IllegalProgram = 8; // 프로그램 불법사용 + AccountSactionType_PersonalInfo_Leak = 9; // 개인정보 유출 + AccountSactionType_AdminImpersonation = 10; // 운영자 사칭 } -// ä +// 채널 서버 예약 종류 enum ServerMoveType { ServerMoveType_None = 0; @@ -329,47 +333,47 @@ enum ServerMoveType message ChannelInfo { - int32 channel = 1; - int32 trafficlevel = 2; + int32 channel = 1; + int32 trafficlevel = 2; } message ServerConnectInfo { - string serverAddr = 1; - int32 serverPort = 2; - string otp = 3; - string roomId = 4; - Pos pos = 5; + string serverAddr = 1; + int32 serverPort = 2; + string otp = 3; + string roomId = 4; + Pos pos = 5; - oneof instanceType - { - int32 instanceId = 6; - MyHomeInfo myhomeInfo = 7; - } + oneof instanceType + { + int32 instanceId = 6; + MyHomeInfo myhomeInfo = 7; + } } message MyHomeInfo { - string myhomeGuid = 1; - string myhomeName = 2; - MyhomeUgcInfo myhomeUgcInfo = 3; + string myhomeGuid = 1; + string myhomeName = 2; + MyhomeUgcInfo myhomeUgcInfo = 3; } message MyhomeUgcInfo { - int32 roomType = 1; + int32 roomType = 1; int32 version = 2; - repeated UgcFrameworkInfo frameworkInfos = 3; - repeated UgcAnchorInfo anchorInfos = 4; + repeated UgcFrameworkInfo frameworkInfos = 3; + repeated UgcAnchorInfo anchorInfos = 4; repeated CrafterBeaconPos crafterBeaconPos = 5; } message UgcFrameworkInfo { - int32 interiorItemId = 1; - int32 floor = 2; - Coordinate coordinate = 3; - Rotation rotation = 4; + int32 interiorItemId = 1; + int32 floor = 2; + Coordinate coordinate = 3; + Rotation rotation = 4; int32 materialId = 5; repeated UgcFrameworkMaterialInfo UgcFrameworkMaterialInfos = 6; } @@ -377,28 +381,28 @@ message UgcFrameworkInfo message UgcFrameworkMaterialInfo { string type = 1; - int32 materialId = 2; - Color color_mask_r = 3; - Color color_mask_g = 4; - Color color_mask_b = 5; + int32 materialId = 2; + Color color_mask_r = 3; + Color color_mask_g = 4; + Color color_mask_b = 5; } message Color { - float r = 1; - float g = 2; - float b = 3; - float a = 4; -} + float r = 1; + float g = 2; + float b = 3; + float a = 4; +} message UgcAnchorInfo { - string anchorGuid = 1; - string anchorType = 2; - int32 tableId = 3; - string entityGuid = 4; - Coordinate coordinate = 5; - Rotation rotation = 6; + string anchorGuid = 1; + string anchorType = 2; + int32 tableId = 3; + string entityGuid = 4; + Coordinate coordinate = 5; + Rotation rotation = 6; } message CrafterBeaconPos @@ -409,46 +413,46 @@ message CrafterBeaconPos message Coordinate { - float x = 1; - float y = 2; - float z = 3; + float x = 1; + float y = 2; + float z = 3; } message Rotation { - float Pitch = 1; - float Yaw = 2; - float Roll = 3; + float Pitch = 1; + float Yaw = 2; + float Roll = 3; } -// ڿ +// 문자열 기반의 프로필 목록 message StringProfile { map stringProfile = 1; } -// ġ +// 유저의 접속 및 위치 message UserLocationInfo { - int32 isChannel = 1; // 1:äμ, 0:νϽ - int32 id = 2; - int32 channelNumber = 3; + int32 isChannel = 1; // 1:채널서버, 0:인스턴스 서버 + int32 id = 2; + int32 channelNumber = 3; } -// ÷̾ +// 플레이어 상태 종류 enum PlayerStateType { PlayerStateType_None = 0; - PlayerStateType_Online = 1; // ¶ - PlayerStateType_Sleep = 2; // ڸ - PlayerStateType_DontDistrub = 3; // ر - PlayerStateType_Offline = 4; // - PlayerStateType_Dormant = 5; // ޸ - PlayerStateType_LeaveMember = 6; // ȸ Ż + PlayerStateType_Online = 1; // 온라인 + PlayerStateType_Sleep = 2; // 자리비움 + PlayerStateType_DontDistrub = 3; // 방해금지 + PlayerStateType_Offline = 4; // 오프라인 + PlayerStateType_Dormant = 5; // 휴면 계정 + PlayerStateType_LeaveMember = 6; // 회원 탈퇴 } -// ƼƼ ġ +// 엔티티의 위치 정보 message Pos { float x = 1; @@ -457,54 +461,54 @@ message Pos int32 angle = 4; } -// ȭ : ȭ ȭ +// 양의 변화 정보 : 재화의 변화 enum AmountDeltaType { AmountDeltaType_None = 0; - AmountDeltaType_Acquire = 1; // ȹ ( ) - AmountDeltaType_Consume = 2; // Ҹ ( ) - AmountDeltaType_Merge = 3; // ( :ȹ, :Ҹ) + AmountDeltaType_Acquire = 1; // 획득 (양의 정수) + AmountDeltaType_Consume = 2; // 소모 (양의 정수) + AmountDeltaType_Merge = 3; // 병합 (양의 정수:획득, 음의 정수:소모) } -// ȭ : ȭ +// 개수의 변화 정보 : 아이템의 변화 enum CountDeltaType { CountDeltaType_None = 0; - CountDeltaType_New = 1; // ű - CountDeltaType_Update = 2; // - CountDeltaType_Acquire = 3; // - CountDeltaType_Consume = 4; // - CountDeltaType_Delete = 5; // + CountDeltaType_New = 1; // 신규 + CountDeltaType_Update = 2; // 수정 + CountDeltaType_Acquire = 3; // 증가 + CountDeltaType_Consume = 4; // 감소 + CountDeltaType_Delete = 5; // 삭제 } -// ȭ +// 재화 종류 enum CurrencyType { CurrencyType_None = 0; - CurrencyType_Gold = 1; // ΰ ⺻ ȭ - CurrencyType_Sapphire = 2; // ΰ ȭ () BlueCali ) - CurrencyType_Calium = 3; // ׷̵ , /Ÿ 湮 () RedCali ) - CurrencyType_Beam = 4; // ׷̵ , ŷ () BlackCali ) - CurrencyType_Ruby = 5; // ű ߰ ȭ + CurrencyType_Gold = 1; // 인게임 전용 기본 재화 + CurrencyType_Sapphire = 2; // 인게임 전용 상위 재화 (구) BlueCali ) + CurrencyType_Calium = 3; // 랜드 업그레이드 보상, 오프/메타버스 방문 및 결재 (구) RedCali ) + CurrencyType_Beam = 4; // 랜드 업그레이드 희귀 보상, 콘텐츠 랭킹 보상 (구) BlackCali ) + CurrencyType_Ruby = 5; // 신규 추가 재화 } -// +// 금전량 message Money -{ - double amount = 1; +{ + double amount = 1; } -// ȭ +// 금전의 변화량 message MoneyDeltaAmount -{ - AmountDeltaType deltaType = 1; +{ + AmountDeltaType deltaType = 1; double amount = 2; } -// α׷ +// 프로그램 버전 종류 enum ProgramVersionType { ProgramVersionType_None = 0; @@ -518,64 +522,194 @@ enum ProgramVersionType ProgramVersionType_LogicVersion = 7; } -// Ƽ ׼ Ÿ +// 파티 멤버 액션 타입 enum PartyMemberActionType { PartyMemberActionType_None = 0; - PartyMemberActionType_Invite = 1; // ʴ - PartyMemberActionType_InviteAccept = 2; // ʴ - PartyMemberActionType_InviteReject = 3; // ʴ - PartyMemberActionType_Summon = 4; // ȯ - PartyMemberActionType_SummonAccept = 5; // ȯ - PartyMemberActionType_SummonReject = 6; // ȯ - PartyMemberActionType_PartyInstance_Join = 7; // Ƽ νϽ - PartyMemberActionType_PartyInstance_Leave = 8; // Ƽ νϽ - PartyMemberActionType_PartyLeader = 9; // Ƽ Ӹ - PartyMemberActionType_JoinParty = 10; // Ƽ - PartyMemberActionType_LeaveParty = 11; // Ƽ Ż - PartyMemberActionType_BanParty = 12; // Ƽ ߹ + PartyMemberActionType_Invite = 1; // 초대 + PartyMemberActionType_InviteAccept = 2; // 초대 수락 + PartyMemberActionType_InviteReject = 3; // 초대 거절 + PartyMemberActionType_Summon = 4; // 소환 + PartyMemberActionType_SummonAccept = 5; // 소환 수락 + PartyMemberActionType_SummonReject = 6; // 소환 거절 + PartyMemberActionType_PartyInstance_Join = 7; // 파티 인스턴스 입장 + PartyMemberActionType_PartyInstance_Leave = 8; // 파티 인스턴스 퇴장 + PartyMemberActionType_PartyLeader = 9; // 파티 리더 임명 + PartyMemberActionType_JoinParty = 10; // 파티 가입 + PartyMemberActionType_LeaveParty = 11; // 파티 탈퇴 + PartyMemberActionType_BanParty = 12; // 파티 추방 } enum UserBlockPolicyType { UserBlockPolicyType_None = 0; - - UserBlockPolicyType_Access_Restrictions = 1; // - UserBlockPolicyType_Chatting_Restrictions = 2; //ä + + UserBlockPolicyType_Access_Restrictions = 1; //접근 제한 + UserBlockPolicyType_Chatting_Restrictions = 2; //채팅 제한 } enum UserBlockReasonType { UserBlockReasonType_None = 0; - - UserBlockReasonType_Bad_Behavior = 1; //ų - UserBlockReasonType_Inappropriate_Name = 2; //Ұ ̸ - UserBlockReasonType_Cash_Transaction = 3; //ݰŷ - UserBlockReasonType_Game_Interference = 4; // - UserBlockReasonType_Service_Interference = 5; // - UserBlockReasonType_Account_Impersonation = 6; // - UserBlockReasonType_Bug_Abuse = 7; ///¡ - UserBlockReasonType_Illegal_Program = 8; //ҹα׷ - UserBlockReasonType_Personal_Info_Leak = 9; // - UserBlockReasonType_Asmin_Impersonation = 10; // Ī + + UserBlockReasonType_Bad_Behavior = 1; //비매너 행위 + UserBlockReasonType_Inappropriate_Name = 2; //불건전 이름 사용 + UserBlockReasonType_Cash_Transaction = 3; //현금거래 행위 + UserBlockReasonType_Game_Interference = 4; //게임 진행 방해 + UserBlockReasonType_Service_Interference = 5; //운영서비스 방해 + UserBlockReasonType_Account_Impersonation = 6; //계정도용 + UserBlockReasonType_Bug_Abuse = 7; //버그/어뷰징 + UserBlockReasonType_Illegal_Program = 8; //불법프로그램 사용 + UserBlockReasonType_Personal_Info_Leak = 9; //개인정보 유출 + UserBlockReasonType_Asmin_Impersonation = 10; //운영자 사칭 } -// ƼƼ ˸ Ʈ +// 엔티티 알림 트리거 종류 enum EntityAlertTriggerType { EntityAlertTriggerType_None = 0; - EntityAlertTriggerType_ItemExpireWarningBefore = 1; // Ⱓ ˸ - EntityAlertTriggerType_ItemExpire = 2; // Ⱓ ˸ + EntityAlertTriggerType_ItemExpireWarningBefore = 1; //아이템 기간만료 삭제전 경고 알림 + EntityAlertTriggerType_ItemExpire = 2; //아이템 기간만료 삭제 알림 } -// ƼƼ ˸ +// 엔티티 알림 방법 종류 enum EntityAlertMethodType { EntityAlertMethodType_None = 0; - - EntityAlertMethodType_Mail = 1; // ˸ + EntityAlertMethodType_Mail = 1; //우편으로 알림 +} + +// 매칭 상태 +enum MatchStatusType { + MatchStatusType_NONE = 0; + MatchStatusType_RESERVED = 1; // 매칭 예약 상태 + MatchStatusType_PROGRESS = 2; // 매칭 진행 중 + MatchStatusType_SUCCESS = 3; // 매칭 성공 + MatchStatusType_CANCEL = 4; // 매칭 취소 + MatchStatusType_TIMEOUT = 5; // 매칭 실패 - 대기시간 초과 + MatchStatusType_FAIL = 6; // 매칭 실패 - 이유 미정 + MatchStatusType_RESERVE_FAILED = 7; // 매칭 예약 실패 + MatchStatusType_CANCEL_FAILED = 8; // 매칭 취소 실패 + + MatchStatusType_JOIN_ROOM_CHECKED = 9; // 매칭 룸 입장 확인 - 실제로 게임 룸에 입장한 이벤트를 받은 상태 + MatchStatusType_QUIT_ROOM = 10; // 매칭 룸 퇴장 + MatchStatusType_CANCEL_BY_DISCONNECT = 11; // 매칭 취소 (접종) + MatchStatusType_JOIN_ROOM_INVALID = 12; // 룸에 입장 하지 않음 +} + +// 매칭 취소 타입 +enum MatchCancelType { + MatchCancelType_NONE = 0; + MatchCancelType_NORMAL = 1; + MatchCancelType_DISCONNECTED = 2; +} + +// 게임 매치 유저 정보 +message MatchUserInfo { + string userGuid = 1; + string serverName = 2; + string matchGroupId = 3; + int32 instanceId = 4; + string nickname = 5; + MatchStatusType status = 6; + google.protobuf.Timestamp startTime = 7; + // TODO: Need PartyInfo, MMR +} + +// 게엄 매칭 상태 정보 +message MatchStatusInfo { + MatchStatusType status = 1; // + int32 matchStep = 2; // 매칭 단계 + int32 waitTimeSec = 3; // 예상 대기시간 + int32 waitTimeMaxSec = 4; // 최대 대기시간 +} + +// 리전 핑 체크를 정보 +message MatchRegionInfo { + string Name = 1; + string TextStringMetaId = 2; + string PingUrl = 3; +} + +message MatchGameInfo { + int32 gameModeId = 1; + int32 eventId = 2; + int32 region =3; +} + +enum BannerType +{ + BannerType_None = 0; + BannerType_MainMenu = 1; +} + +enum SortType +{ + SortType_None = 0; + + SortType_Ascending = 1; + SortType_Descending = 2; +} + +enum SortColumnType +{ + SortColumnType_None = 0; + + SortColumnType_Price = 1; + SortColumnType_ItemId = 2; +} + +enum ScoreType +{ + ScoreType_None = 0; + + ScoreType_Score = 1; + ScoreType_Time = 2; +} + +enum ScoreModifyType +{ + ScoreModifyType_None = 0; + + ScoreModifyType_Overwrite = 1; + ScoreModifyType_Increase = 2; +} + +enum RankingType +{ + RankingType_None = 0; + + RankingType_Pioneer = 1; + RankingType_GameMode = 2; + RankingType_WorldEvent = 3; +} + +enum EventType +{ + EventType_None = 0; + + EventType_Contribution = 1; + EventType_PersonalCrafting = 2; +} + +enum RankingScheduleStateType +{ + RankingScheduleStateType_None = 0; + + RankingScheduleStateType_Waiting = 1; + RankingScheduleStateType_Started = 2; + RankingScheduleStateType_Ended = 3; +} + +enum IntervalType +{ + IntervalType_None = 0; + + IntervalType_Refresh = 1; + IntervalType_Snapshot = 2; + IntervalType_Initialization = 3; } diff --git a/Protocol/proto/Define_Result.proto b/Protocol/proto/Define_Result.proto index 250b9c6..4bcea31 100644 --- a/Protocol/proto/Define_Result.proto +++ b/Protocol/proto/Define_Result.proto @@ -28,7 +28,7 @@ enum ServerErrorCode MongoDbException = 10010; // MongoDb 예외가 발생 했습니다. //============================================================================================= - // NLog 관련 오류 : 10030 ~ + // NLog 관련 오류 : 10030 ~ //============================================================================================= NlogWithAwsCloudWatchSetupFailed = 10031; // NLog와 AWS CloudWatch 설정을 실패 했습니다. NlogNotInitialized = 10032; // NLog 객체가 초기화되지 않은 상태 입니다. @@ -40,7 +40,7 @@ enum ServerErrorCode LogAppenderIsNull = 10052; // LogAppender 객체가 Null 입니다. LogFormatterIsNull = 10053; // LogFormatter 객체가 Null 입니다. LogActionTypeInvalid = 10054; // LogActionType 오류 입니다. - + //============================================================================================= // 네트워크 오류 : 10100 ~ @@ -59,6 +59,9 @@ enum ServerErrorCode RacketRecvHandlerNotFound = 10154; // 수신 패킷 핸들러를 찾지 못했습니다. LargePacketNotAllReceived = 10155; // 대용량 패킷이 모두 수신되지 않았습니다. LargePacketRecvTimeOver = 10156; // 대용량 패킷이 수신 대기 시간이 오래 지났다. + LargePacketProcessTypeInvalid = 10157; // 대용량 패킷이 수신 타입이 유효하지 않다. + LargePacketDataIsNull = 10158; // 대용량 패킷이 데이터가 존재하지 않는다. + LargePacketException = 10159; // 대용량 패킷 Exception //============================================================================================= // DB 오류 : 10200 ~ @@ -70,7 +73,7 @@ enum ServerErrorCode DynamoDbTransactionCanceledException = 10211; // DynamoDB TransactionCanceledException 예외가 발생 했습니다. DynamoDbAmazonDynamoDbException = 10212; // DynamoDB AmazonDynamoDBException 예외가 발생 했습니다. DynamoDbAmazonServiceException = 10213; // DynamoDB AmazonServiceException 예외가 발생 했습니다. - DynamoDbConfigLoadFailed = 10214; // DynamoDB Config 파일 로딩을 실패 했습니다. + DynamoDbConfigLoadFailed = 10214; // DynamoDB Config 파일 로딩을 실패 했습니다. DynamoDbConnectFailed = 10215; // DynamoDB 연결을 실패 했습니다. DynamoDbTableCreateFailed = 10216; // DynamoDB Table 생성을 실패 했습니다. DynamoDbTableNotConnected = 10217; // DynamoDB Table과 연결이 되어있지 않습니다. @@ -99,6 +102,7 @@ enum ServerErrorCode DynamoDbItemRequestIsInvalid = 10251; // DynamoDbItemRequest내의 정보에 오류가 있습니다. DynamoDbItemRequestQueryContextTypeInvalid = 10252; // DynamoDbItemRequestQueryContext내의 Type 정보 오류 입니다. DynamoDbItemRequestUpdateExpressionEmpty = 10253; // DynamoDbItemRequest내에 UpdateExpression 정보가 비어 있다. + DynamoDbItemRequestConditionFail = 10254; // DynamoDb ItemRequest 조건이 실패했습니다. // DynamoDB Custom Doc 오류 DynamoDbDocPkInvalid = 10261; // DynamoDbDoc의 PK 값이 오류 입니다. @@ -110,7 +114,7 @@ enum ServerErrorCode DynamoDbRequestInvalid = 10267; // DynamoDbRequest 오류 입니다. DynamoDbDocAttributeStateNotSet = 10268; // DynamoDbDoc AttributeState 플래그를 선택하지 않았습니다. DynamoDbDocAttribWrapperCopyFailed = 10269; // DynamoDbDoc AttribWrapper 복사를 실패 했습니다. - DynamoDbDocAttributeGettingFailed = 10270; // DynamoDbDoc Attribute 가져오기를 실패 했습니다. + DynamoDbDocAttributeGettingFailed = 10270; // DynamoDbDoc Attribute 가져오기를 실패 했습니다. DynamoDbDocLinkPkSkInvalid = 10271; // DynamoDbDoc LinkPKSK 값 오류 입니다. DynamoDbDocAttribWrapperIsNull = 10272; // DynamoDbDoc AttribWrapper가 Null 입니다. @@ -169,7 +173,7 @@ enum ServerErrorCode //============================================================================================= // S3 오류 S3ClientCreateFailed = 10501; // S3 Client 생성을 실패 했습니다. - S3BucketCreateFailed = 10502; // S3 Bucket 생성을 실패 했습니다. + S3BucketCreateFailed = 10502; // S3 Bucket 생성을 실패 했습니다. S3FileUploadFailed = 10503; // S3 File Upload를 실패 했습니다. S3FileDeleteFailed = 10504; // S3 File Delete에 실패 했습니다. S3FileGetFailed = 10505; // S3 File Get에 실패 했습니다. @@ -235,6 +239,9 @@ enum ServerErrorCode ModuleAlreadyAddInitializeModule = 10737; // 이미 추가된 초기화용 Module 입니다. ModuleNotFound = 10738; // Moudle을 찾을 수 없습니다. ProgramVersionLoadFailed = 10739; // 프로그램 Version 읽기를 실패 했습니다. + ServerAlreadyRunning = 10740; // 서버가 이미 실행중 입니다. + PreconditionFailed = 10741; // 사전 조건 준비 오류 + //============================================================================================= // 데이터 변환 및 복사 오류 : 10850 ~ @@ -253,8 +260,8 @@ enum ServerErrorCode AttribNotFound = 10861; // Attrib를 찾을 수 없습니다. AttribPathMakeFailed = 10862; // Attrib Path 구성을 실패 했습니다. EntityAttributeCastFailed = 10863; // EntityAttribute Casting을 실패 했습니다. - StringConvertToEnumFailed = 10864; // 문자열을 Enum으로 변환하는 것을 실패 했습니다. - + StringConvertToEnumFailed = 10864; // 문자열을 Enum으로 변환하는 것을 실패 했습니다. + //============================================================================================= // 프로그램 버전 오류 : 10900 ~ @@ -305,7 +312,7 @@ enum ServerErrorCode //============================================================================================= // 엔티티 트랜잭션 관련 오류 : 11200 ~ //============================================================================================= - TransactionRunnerAlreadyRunning = 11201; // TransactionRunner가 이미 실행중 입니다. + TransactionRunnerAlreadyRunning = 11201; // TransactionRunner가 이미 실행중 입니다. TransactionRunnerNotFound = 11202; // TransactionRunner를 찾을 수 없습니다. @@ -322,7 +329,7 @@ enum ServerErrorCode EntityTypeInvalid = 11306; // EntityType 오류 입니다. EntityLinkedToMap = 11307; // Entity가 Map에 연결되어 있습니다. EntityNotLinkedToMap = 11308; // Entity가 Map에 연결되어 있지 않습니다. - EntityStateNotDancing = 11309; //현재 dence 상태가 아닙니다. dance end 패킷이 날라올때 처리 + EntityStateNotDancing = 11309; // 현재 dence 상태가 아닙니다. dance end 패킷이 날라올때 처리 //============================================================================================= @@ -337,16 +344,17 @@ enum ServerErrorCode // 엔티티 상태 관련 오류 : 11500 ~ //============================================================================================= EntityBaseHfsmInitFailed = 11501; // 엔티티 기반 HFSM 초기화 오류 입니다. + EntityStateTypeInvalid = 11502; // EntityState 오류 입니다. //============================================================================================= // 글로벌 엔티티 오류 : 11600 ~ //============================================================================================= - RedisGlobalEntityDuplicated = 11601; // 글로벌 엔티티가 중복 되었습니다. + RedisGlobalEntityDuplicated = 11601; // 글로벌 엔티티가 중복 되었습니다. + - //============================================================================================= // 접속 서버 변경 관련 오류 : 11700 ~ //============================================================================================= @@ -404,7 +412,7 @@ enum ServerErrorCode //============================================================================================= // 캐릭터 관련 오류 : 13000 ~ //============================================================================================= - TestCharacterPrepareCreateNotCompleted = 13001; // TestCharacterPrepareCreate 단계가 완료되지 않았습니다. + TestCharacterPrepareCreateNotCompleted = 13001; // TestCharacterPrepareCreate 단계가 완료되지 않았습니다. DefaultCharacterPrepareCreateNotCompleted = 13012; // DefaultCharacterPrepareCreate 단계가 완료되지 않았습니다. CharacterPrepareLoadNotCompleted = 13013; // CharacterPrepareLoad 단계가 완료되지 않았습니다. CharacterBaseDocLoadDuplicatedCharacter = 13014; // CharacterBaseDoc 로딩중에 중복된 캐릭터가 발견되었습니다. @@ -464,7 +472,7 @@ enum ServerErrorCode ItemFirstPurchaseDiscountItemCountWrong = 14033; // 아이템 첫 구매 할인 아이템 개수가 잘못되었습니다. ItemAttributeIdTypeInvalid = 14034; // 아이템 AttributeIdType 오류 입니다. ItemAllocFailed = 14035; // 아이템 할당 오류 입니다. - ItemGuidDuplicated = 14036; // 아이템 Guid 중복 오류 입니다. + ItemGuidDuplicated = 14036; // 아이템 Guid 중복 오류 입니다. ItemLevelCurrentMax = 14037; // 아이템 레벨이 현재 최대 입니다. ItemCanNotBeStored = 14038; // 보관할 수 없는 아이템 입니다. @@ -477,7 +485,7 @@ enum ServerErrorCode ItemUseNotExistAssignableQuest = 14103; // 퀘스트 쿨타임 초기화 아이템 사용시 : 이미 수행중이거나, 퀘스트메일이 존재해서 할당가능한 퀘스트가 없어서 아이템 사용 불가. ItemUseAlreadyHasQuest = 14104; // 퀘스트 할당 아이템 사용시 : 해당 퀘스트는 이미 진행중이어서 아이템 사용 불가. ItemUseAlreadyHasQuestMail = 14105; // 퀘스트 할당 아이템 사용시 : 해당 퀘스트메일은 이미 존재해서 아이템 사용 불가. - + //============================================================================================= // 인벤토리 관련 오류 : 15000 ~ @@ -541,7 +549,7 @@ enum ServerErrorCode UgcNpcMetaDataNotFound = 15123; // UgcNpc 메타 데이터를 찾지 못했습니다. BeaconAppProfileUploadCoolTime = 15124; // Beacon 앱 프로필 업로드 쿨타임 입니다. BeaconBodyItemInvalid = 15125; // Beacon Body 아이템 오류 입니다. - UgcNpcHasBeaconShopItem = 15126; // UgcNpc가 BeaconShopItem을 가지고 있습니다. + UgcNpcHasBeaconShopItem = 15126; // UgcNpc가 BeaconShopItem을 가지고 있습니다. //============================================================================================= @@ -590,6 +598,12 @@ enum ServerErrorCode GameZoneNotJoin = 15901; // 게임존에 입장된 상태가 아닙니다. MapIsNull = 15902; // Map 객체가 null 입니다. LocationUniqueIdInvalid = 15903; // LOCATION_UNIQUE_ID 값 오류 입니다. + PropIsOccupied = 15904; // Prop이 점유중 입니다. + PropIsNotOccupied = 15905; // Prop을 점유중이 아닙니다. + SameMapAsBeacon = 15906; // Beacon과 같은 Map에 있습니다. + PropIsNotRewardPropOrGroupProp = 15907; // Prop이 RewardProp 또는 GroupProp 이 아닙니다. + PropStateIsNotActivation = 15908; // Prop이 Activation 상태가 아닙니다. + RewardPropMetaDataNotFound = 15910; // RewardProp 메타 데이터를 찾지 못했습니다. //============================================================================================= @@ -622,7 +636,7 @@ enum ServerErrorCode //============================================================================================= // 던전 관련 오류 : 19000 ~ //============================================================================================= - OwnedRoomDocIsNull = 19001; // + OwnedRoomDocIsNull = 19001; // RoomDocIsNull = 19002; // RoomDoc이 null 입니다. RoomIsNotMyHome = 19003; // Room이 마이홈이 아닙니다. MapRangeOutOfCellPos = 19004; // MapRange의 범위를 벗어난 CellPos 입니다. @@ -694,7 +708,7 @@ enum ServerErrorCode IncludeBanWordFromPartyName = 21030; // 파티명에 금칙어가 있습니다. JoiningPartyMemberInfoIsNull = 21031; // 파티원 정보가 없습니다. InvalidSummonWorldServer = 21032; // 서로 다른 월드에 있습니다. - + NotExistPartyInstance = 21999; //============================================================================================= @@ -795,11 +809,11 @@ enum ServerErrorCode MyhomeNameLengthShort = 25513; // 마이홈 이름 문자열 길이가 짧습니다 MyhomeNameLengthLong = 25514; // 마이홈 이름 문자열 길이가 깁니다. MyhomeNameDuplicated = 25515; // 마이홈 이름이 중복 되었습니다. - MyhomeInterphoneNotExist = 25516; // 마이홈 인터폰이 없습니다. - MyhomeStartPointNotExist = 25517; // 마이홈 시작 지점이 없습니다. + MyhomeInterphoneNotExist = 25516; // 마이홈 인터폰이 없습니다. + MyhomeStartPointNotExist = 25517; // 마이홈 시작 지점이 없습니다. MyhomeInterphoneExceed = 25518; // 마이홈 인터폰이 허용 갯수를 초과 하였습니다. MyhomeStartPointExceed = 25519; // 마이홈 시작 지점이 허용 갯수를 초과 하였습니다. - CraftingClothesExceed = 25520; // 의상 제작대 허용 갯수를 초과 하였습니다. + CraftingClothesExceed = 25520; // 의상 제작대 허용 갯수를 초과 하였습니다. CraftingCookingExceed = 25521; // 요리 제작대 허용 갯수를 초과 하였습니다. CraftingFurnitureExceed = 25522; // 가구 제작대 허용 갯수를 초과 하였습니다. AnchorIsNotInMyhome = 25523; // 앵커가 마이홈에 배치되어 있지 않습니다. @@ -831,6 +845,7 @@ enum ServerErrorCode CartDocIsNull = 26508; // CartDoc이 null 입니다. CartDocException = 26509; // CartDoc에서 Exception이 발생했습니다. + //============================================================================================= // 채팅 관련 오류 : 27000 ~ //============================================================================================= @@ -847,7 +862,7 @@ enum ServerErrorCode //============================================================================================= EscapePositionNotAvailableTime = 28001; // 비상 탈출 쿨타임 시간입니다. EscapePositionDocIsNull = 28002; // EscapePositionDoc이 null 입니다. - + //============================================================================================= // 블록 유저 관련 오류 : 28500 ~ //============================================================================================= @@ -894,7 +909,7 @@ enum ServerErrorCode MoneyNotEnough = 32003; // 금전이 부족합니다. MoneyMaxCountExceeded = 32004; // 금전 최대 보유량을 초과 했습니다. CurrencyMetaDataNotFound = 32005; // 재화 메타 데이터를 찾을 수 없습니다. - + //============================================================================================= // 상점 관련 오류 : 33000 ~ @@ -914,9 +929,9 @@ enum ServerErrorCode RewardInfoNotExist = 34000; // 보상 정보 없음 RewardInvalidType = 34001; // 보상 타입 오류 RewardInvalidTypeValue = 34002; // 보상 타입 값 오류 - NotRequireAttributeValue = 34003; // + NotRequireAttributeValue = 34003; // RewardGroupIdParsingFromStringToIntErorr = 34004; // 보상 그룹 아이디 문자열을 정수로 변환하는데 오류가 발생했습니다. - + //============================================================================================= // Claime 관련 오류 : 35000 ~ @@ -952,6 +967,7 @@ enum ServerErrorCode CraftHelpDocException = 36019; // CraftHelpDoc에서 Exception이 발생했습니다. CraftRecipeDocException = 36020; // CraftRecipeDoc에서 Exception이 발생했습니다. CraftInvalidRequestCount = 36021; // 잘못된 제작 갯수의 요청입니다. + CraftingMetaDataDisabled = 36022; // 비활성 된 메타에 대한 요청입니다. //============================================================================================= // UGQ 관련 오류 : 37000 ~ @@ -969,7 +985,7 @@ enum ServerErrorCode UgqReassignUsingItemErrorCauseNewRevisionUpdated = 37011; // 아이템 사용해서 UGQ를 재 할당 받으려고 했으나, 새로운 리비전이 업데이트 되었음 UgqQuestDataInvalidRevision = 37012; // UGQ데이터의 리비전 정보가 유효하지 않습니다. UgqAbortCannotCauseInvalidState = 37013; // UGQ state가 유효하지 않아 포기할수 없다. - UgqMetaGeneratorNotExist = 37014; // UGQ Meta 생성 클래스가 존재 하지 않습니다. + UgqMetaGeneratorNotExist = 37014; // UGQ Meta 생성 클래스가 존재 하지 않습니다. UgqQuestDataRevisionUpdated = 37015; // UGQ Revision이 업데이트가 되었습니다. UgqRevisionCannotSmallerThanRequestedRevision = 37016; // UGQ 최신 리비전은 요청한 리비전보다 작을 수 없습니다. UgqRevisionStateNotLive = 37017; // UGQ 리비전의 상태가 Live가 아닙니다. @@ -1033,7 +1049,7 @@ enum ServerErrorCode AiChatServerStart = 41001; // 클라이언트용 Ai Chat Error Code StartPoint AiChatServerReqFailed = 41002; // AI Chat 서버 요청이 실패했습니다. AiChatServerBadrequest = 41003; // Request가 잘못되었습니다. - AiChatServerForbidden = 41004; // + AiChatServerForbidden = 41004; // AiChatServerUnauthorized = 41005; // 인증되지 않은 사용자입니다. AiChatServerUserNotFound = 41006; // 사용자를 찾지 못했습니다. AiChatServerCharacterNotFound = 41007; // 캐릭터를 찾지 못했습니다. @@ -1081,7 +1097,7 @@ enum ServerErrorCode // 칼리움 교환소 관련 오류 : 43050 ~ //============================================================================================= LackOfConvertCalium = 43051; // 변환된 칼리움 수가 최소치보다 작습니다. - + //============================================================================================= // Web3 관련 오류 : 43100 ~ //============================================================================================= @@ -1091,7 +1107,7 @@ enum ServerErrorCode FailToGetEchoSystemException = 43103; // EchoSystem 데이터 응답 오류 ( Http 예외 발생 ) FailToSendEchoSystem = 43104; // EchoSystem 데이터 전송 오류 FailToGetEchoSystemRollUp = 43105; // EchoSystem Rollup Data 획득 실패 - + //============================================================================================= // 서비스 관련 오류 //============================================================================================= @@ -1111,7 +1127,7 @@ enum ServerErrorCode InstanceRoomIsFull = 51009; // 인스턴스 룸이 가득 찼습니다. InstanceRoomIdDuplicated = 51010; // 인스턴스 룸 Id 가 중복 되었습니다. InstanceRoomNotExistAtRedis = 51011; // 인스턴스 룸이 레디스에 존재하지 않습니다. - + //============================================================================================= // Task Reservation 관련 오류 : 52000 ~ //============================================================================================= @@ -1136,12 +1152,14 @@ enum ServerErrorCode TaxiTypeInvalid = 54002; // TaxiType 오류 입니다. WarpMetaDataNotFound = 54003; // 워프 메타 데이터를 찾지 못했습니다. WarpTypeInvalid = 54004; // WarpTyoe 오류 입니다. + ContentsMunuMetaDataNotFound = 54005; // 컨텐츠 메뉴 메타 데이터를 찾지 못했습니다. + ContentsMunuConnectionTypeInvalid = 54006; // ContentsMunuConnectionType 오류 입니다. //============================================================================================= // 렌탈 관련 오류 : 55000 ~ //============================================================================================= RentalNotFound = 55001; // 렌탈을 찾지 못했습니다. - RentalDocLoadDuplicatedRental = 55002; // RentalDoc 로딩중에 중복된 미니맵 마커가 발견되었습니다. + RentalDocLoadDuplicatedRental = 55002; // RentalDoc 로딩중에 중복된 Rental이 발견되었습니다. RentalDocIsNull = 55003; // RentalDoc이 null 입니다. RentalNotAvailableLand = 55004; // 렌탈이 불가능한 랜드 입니다. RentalAddressInvalid = 55005; // 렌탈 주소가 유효하지 않습니다. @@ -1193,6 +1211,27 @@ enum ServerErrorCode BeaconShopOverSellingPrice = 57030; // BeaconShop에 판매가격이 최대값보다 높습니다. BeaconShopOverRentalSafeTime = 57031; // BeaconShop이 있는 마이홈 랜탈 기간이 곧 만료됩니다. BeaconShopDeactiveItemForSell = 57032; // BeaconShop에 판매 아이템의 판매가 비활성화 되었습니다. + BeaconShopFailedRecentBoardItem = 57033; // BeaconShop 최근 아이템 목록 가져오기에 실패했습니다. + + //============================================================================================= + // 배너 관련 오류 : 58000 ~ + //============================================================================================= + BannerNotFound = 58001; // 렌탈을 찾지 못했습니다. + BannerDocLoadDuplicatedBanner = 58002; // BannerDoc 로딩중에 중복된 배너가 발견되었습니다. + BannerDocIsNull = 58003; // BannerDoc이 null 입니다. + + //============================================================================================= + // 랭킹 관련 오류 : 58500 ~ + //============================================================================================= + RankingScheduleDocLoadDuplicatedRankingSchedule = 58501; // RankingScheduleDoc 로딩중에 중복된 RankingSchedule이 발견되었습니다. + RankingScheduleNotFound = 58502; // RankingSchedule을 찾지 못했습니다. + RankingDocLoadDuplicatedRanking = 58503; // RankingDoc 로딩중에 중복된 Ranking이 발견되었습니다. + RankingNotFound = 58504; // Ranking을 찾지 못했습니다. + RankerDocLoadDuplicatedRanker = 58505; // RankerDoc 로딩중에 중복된 Ranker가 발견되었습니다. + RankerNotFound = 58506; // Ranker를 찾지 못했습니다. + RankDocLoadDuplicatedRank = 58507; // RankDoc 로딩중에 중복된 Ranker가 발견되었습니다. + RankNotFound = 58508; // Rank를 찾지 못했습니다. + RankingMetaDataNotFound = 58509; // Ranking 메타 데이터를 찾지 못했습니다. //============================================================================================= // UGQ 관련 오류. Web Api 오류 @@ -1248,10 +1287,14 @@ enum ServerErrorCode //============================================================================================= - // NFT 관련 오류 : 62000 ~ + // NFT 관련 오류 : 62000 ~ //============================================================================================= NftForOwnerAllGetFailed = 62001; // NFT 소유자 정보를 조회를 실패 했습니다. + //=========================================== + // RabbitMqRpc + //=========================================== + MqResponseTimeout = 63001; //============================================================================================= // Broker Api Server 오류: 70000 ~ @@ -1272,15 +1315,29 @@ enum ServerErrorCode ExpiredUserJwt = 71203; // 유저 인증 토큰 유효기간이 만료됐습니다. AccountNotFound = 71301; // 계정을 찾을 수 없습니다. UserNotFound = 71302; // 유저를 찾을 수 없습니다. - + ExchangeOrderIdNotFound = 72001; // 교환 주문 아이디가 존재하지 않습니다. ExchangeTotalOrderDailyLimitExceeded = 72002; // 총 교환 주문 수량이 초과되었습니다. ExchangeUserOrderDailyLimitExceeded = 72003; // 유저 교환 주문 수량이 초과되었습니다. + //================================ + // Match Error + //================================ + MatchServerException = 73001; // 매칭 과정에서 서버 예외 발생 - 매칭 창 다음 - 매칭 상태가 좋지 않습니다. 잠시 후에 다시 시도해 주세요. + MatchUserNotFound = 73002; // 매칭 취소 실패 - 매칭 예약 유저가 아님 (동시성 이슈, 매칭 창 닫음) + MatchInvalidGameModeId = 73003; // 오픈되지 않았거나 테이블에 없는 게임 모드 아이디로 요청 + MatchInvalidRegion = 73004; // 매칭 지역명 오류 + MatchInvalidMetaData = 73005; // 매칭 관련 메타데이터 오류 + MatchCantCausePlayPenaltyTime = 73006; // 매칭 예약 시, 플레이 페널티가 적용 중이라 매칭 불가 + MatchCantCauseDailyJoinLimit = 73007; // 매칭 예약 시, 일일 입장 제한, 일일 매칭 횟수 제한으로 매칭 불가 + + MatchCancelFail = 73008; // 매칭 취소 실패 - 매칭이 성공한 상태에서 취소가 호출된 경우 (동시성 이슈, 매칭 창을 닫기 않고 매칭 성공/실패를 수신할 때까지 대기) + + MatchReserveFail = 73009; // 매칭 예약 실패 - 매칭이 가능하지 않은 모든 상황에서 매칭이 호출된 경우 (매칭이 성공했으나 게임 입장 중에 강제 종료하고 게임을 나간 경우 매칭 서버에서 아직 타이밍 이슈로 매칭 예약이 불가능한 상태 일 수 있음) - //============================================================================================= + //============================================================================================= // Bot 관련 오류. //============================================================================================= BotPlayerIsNull = 962001; // BotPalyer가 null 입니다. @@ -1304,9 +1361,9 @@ enum ServerErrorCode BattleInstanceTypeError = 11000002; // 배틀인스턴스 타입이 잘못 됐습니다. BattleInstanceInfoAlreadyExist = 11000003; // 이미 존재하는 인스턴스 입니다. BattleInstanceInfoNotExist = 11000004; // 존재하지 않는 인스턴스 입니다. - BattleInstanceJoinPlayerError = 11000005; // 플에이어 객체 + BattleInstanceJoinPlayerError = 11000005; // 플에이어 객체 BattleInstanceUsableSpawnPointNotExist = 11000006; // 사용 가능한 스폰 포인트가 없습니다. - BattleInstanceInActive = 11000007; // 배틀 인스턴스 비활성화 상태 입니다. + BattleInstanceInActive = 11000007; // 배틀 인스턴스 비활성화 상태 입니다. BattleInstanceNotExistAnchors = 11000008; // 배틀 인스턴스 앵커 정보가 없습니다. BattleInstanceAddPodCombatFail = 11000009; // 배틀 인스턴스 pod 추가에 실패하였습니다. BattleInstanceObjectMetaNotExist = 11000010; // 배틀 인스턴스 오브젝트 메타가 존재하지 않습니다. @@ -1322,9 +1379,10 @@ enum ServerErrorCode BattleInstanceInteractionFail = 11000021; // 배틀 오브젝트 상호작용에 실패 하였습니다. BattleInstancePickupPodRewardAllocateError= 11000022; // 픽업포드 리워드 생성에 실패 하였습니다. BattleInstanceNotExistEventInfo = 11000023; // 배틀 이벤트 정보가 없습니다. - BattleInstanceClosingTime = 11000024; // 배틀 이벤트, 라운드가 거의다 끝나가는 시간입니다. - BattleInstanceSeqParseError = 11000025; // 배틀 인스턴스 시퀀스 파싱 에러 - + BattleInstanceClosingTime = 11000024; // 배틀 이벤트, 라운드가 거의다 끝나가는 시간입니다. + BattleInstanceSeqParseError = 11000025; // 배틀 인스턴스 시퀀스 파싱 에러 + BattleInstanceEventIdInvalid = 11000026; // 전투 이벤트 아이디가 유효하지 않습니다. + GameModeJoinHandlerNotExist = 11000100; // 게임모드 조인 핸들러가 없습니다. GameModeInitHandlerNotExist = 11000101; // 게임모드 Init 핸들러가 없습니다. GameModeJoinSuccessHandlerNotExist = 11000102; // 게임모드 조인 성공 핸들러가 없습니다. @@ -1332,37 +1390,41 @@ enum ServerErrorCode GameModeClassIsNull = 11000104; // 게임모드가 null GameModeAlreadyExist = 11000105; // 게임모드가 존재 GameModeInvalidAnchorGuid = 11000106; // 유효하지 않은 AnchorGuid 입니다. - - - - + GameModeInvalidAction = 11000107; // 유효하지 않은 Action 입니다. + GameModePreparationForLeavingHandlerNotExist = 11000108; // 게임모드 나가기준비 핸들러가 없습니다. + GameModeLeaveHandlerNotExist = 11000109; // 게임모드 나가기 핸들러가 없습니다. + GameModeRunRankUserExist = 11000110; // 게임모드 레이스 랭크가 존재 + GameModeNotEnoughPlayableUserCount = 11000111; // 게임모드 플레이 가능한 유저가 모자라다 + GameModePlayRegulationDocException = 11000112; // 게임모드 PlayerRegulationDocException + GameModeTeamIdParseError = 11000113; // 게임모드 TeamId parse 중 에러 발생 + GameModeNotExistMeta = 11000114; // 게임모드 Meta 데이터 없음 //============================================================================================= // Server Metrics 오류: 99999100 ~ //============================================================================================= ServerMetricsTriggerHandlerNotFound = 99999101; //ServerMetrics TriggerHandler를 찾을 수 없습니다. - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + // 하기 정의는 추후 재조정 한다. kangms @@ -1377,9 +1439,9 @@ enum ServerErrorCode NotImplemented = 9; NotExistSelectedCharacter = 10; - + NotExistCharacter = 11; - + ServerLogicError = 12; NoPermissions = 14; @@ -1462,13 +1524,12 @@ enum ServerErrorCode NotFoundAnchorGuidInMap = 4899; NotFoundAnchorGuid = 4900; - PropIsOccupied = 4901; PropIsUsed = 4902; PropTypeisWrong = 4903; NotFoundEmotionData = 4920; NotInEmotionSlot = 4921; - + CreateItemFail = 4996; AddItemFail = 4997; DeleteItemFail = 4998; @@ -1478,18 +1539,18 @@ enum ServerErrorCode InvalidSlotIndex = 5002; DuplicatedItemGuid = 5003; NotFoundItemTableId = 5004; - + NotSelectedChar = 5005; NotFoundMap = 5006; - + NotEmptySlot = 5007; EmptySlot = 5008; - + NotFoundBuffTableId = 5009; NotFoundBuff = 5010; NotExistForecedMoveInfo = 5011; - + DuplicatedNickName = 5013; AlreadySetNickName = 5014; DisallowedCharacters = 5015; @@ -1517,7 +1578,7 @@ enum ServerErrorCode ToolSlotOutOfRange = 5082; NotFoundToolSlot = 5083; EmptyToolSlot = 5084; - + FolderNameExceededLength = 5090; FolderNameAlreadyExist = 5091; FolderNameNotExist = 5092; @@ -1548,7 +1609,7 @@ enum ServerErrorCode FriendInfoOthersCountAlreadyMaxCount = 5132; FriendInfoOffline = 5133; FriendInfoAlreadyExist = 5134; - + FriendInviteMyPosIsNotMyHome = 5140; FriendInviteDontDisturbState = 5141; FriendInviteExpireTimeRemain = 5142; @@ -1557,7 +1618,7 @@ enum ServerErrorCode FriendKickMyPosIsNotMyHome = 5145; FriendKickMemberNotExist = 5146; FriendIsInAnotherMyhome = 5147; - + BlockUserMaxCount = 5150; BlockUserAlreadyBlock = 5151; BlockUserCannotSendMailTo = 5152; @@ -1570,8 +1631,8 @@ enum ServerErrorCode CartFullStackofItem = 5201; CartisFull = 5202; BanNickName = 5203; - - + + CurrencyNotFoundData = 5400; CurrencyNotEnough = 5401; @@ -1597,7 +1658,7 @@ enum ServerErrorCode InventoryFull = 5910; NotFoundProductId = 5911; - + RandomBoxItemDataInvalid = 6100; NotExistGachaData = 6101; @@ -1609,4 +1670,4 @@ message Result { ServerErrorCode errorCode = 1; string resultString = 2; -} \ No newline at end of file +} diff --git a/Protocol/proto/Game_Define.proto b/Protocol/proto/Game_Define.proto index 71da0c8..d71c136 100644 --- a/Protocol/proto/Game_Define.proto +++ b/Protocol/proto/Game_Define.proto @@ -125,6 +125,8 @@ enum EntityType EntityType_GameModeTPSTeamDeathMatch = 90402; EntityType_GameModeRunAdventure = 90411; EntityType_GameModeRunRace = 90412; + EntityType_GameModeRunPractice = 90413; + EntityType_GameModeRunHonor = 90414; EntityType_GameObject = 905; EntityType_GameObjectWeapon = 90501; EntityType_GameObjectBuff = 90502; @@ -132,7 +134,7 @@ enum EntityType EntityType_GameObjectCombatPod = 90504; EntityType_GameObjectPickupPod = 90505; EntityType_GameObjectSavePoint = 90506; - + // ۷ι Ƽ EntityType_Party = 10; // Ƽ ƼƼ @@ -170,11 +172,15 @@ enum EntityType EntityType_LargePacketCheckTicker = 1415; EntityType_BuildingUpdateTicker = 1416; EntityType_CaliumEventTicker = 1417; - EntityType_BattleEventCheckTicker = 1418; - EntityType_BattleInstanceStateCheckTicker = 1419; - EntityType_LandAuctionReservationConfigureTicker = 1420; - EntityType_LandAuctionCheckTicker = 1421; - EntityType_GameModeLifeCycleTicker = 1422; + EntityType_BattleEventCheckTicker = 1418; + EntityType_BattleInstanceStateCheckTicker = 1419; + EntityType_LandAuctionReservationConfigureTicker = 1420; + EntityType_LandAuctionCheckTicker = 1421; + EntityType_GameModeLifeCycleTicker = 1422; + EntityType_RankingScheduleUpdateTicker = 1423; + EntityType_RankingUpdateTicker = 1424; + EntityType_MatchServerListTicker = 1431; + EntityType_GameEventCheckTicker = 1432; // EntityType_BlockUser = 15; @@ -199,6 +205,10 @@ enum EntityType EntityType_NoticeChat = 210201; // н owner EntityType_SeasonPassManager = 2103; + // owner + EntityType_BannerManager = 2104; + // + EntityType_Banner = 210401; // īƮ EntityType_Cart = 22; @@ -240,11 +250,27 @@ enum EntityType // ŷ ݾ EntityType_BeaconShopSoldPrice = 3102; + // ŷ + EntityType_RankingSchedule = 32; + + // ŷ + EntityType_Ranking = 33; + // Ŀ + EntityType_Ranker = 3201; + + EntityType_EventScore = 34; + EntityType_WorldEventScore = 3401; + EntityType_PlanetUser = 41; - // UI + EntityType_GameEvent = 51; + EntityType_GameEventCollector = 5101; + + // UI EntityType_CustomDefinedUi = 99; + + // ˻ : ƼƼ ˻ !!! EntityType_All = 1000000000; } @@ -1759,6 +1785,42 @@ enum ProfitHistoryType ProfitHistoryType_Gain = 2; } +message BannerInfo +{ + int32 bannerId = 1; + BannerType bannerType = 2; + google.protobuf.Timestamp startTime = 3; + google.protobuf.Timestamp endTime = 4; + int32 orderId = 5; + map imageUrl = 6; // Key : LanguageType + map linkAddress = 7; // Key : LanguageType +} + +message SortInfo +{ + SortColumnType sortColumnType = 1; + SortType sortType = 2; +} + +message RankingScheduleInfo +{ + string rankingGuid = 1; + int32 rankingMetaId = 2; + int32 eventActionScoreGroupId = 3; + google.protobuf.Timestamp startTime = 4; + google.protobuf.Timestamp endTime = 5; +} + +message RankInfo +{ + string rankerGuid = 1; + EntityType rankerEntityType = 2; + string rankerName = 3; + int32 rankNum = 4; + int32 prevRankNum = 5; + int32 score = 6; + ScoreType scoreType = 7; +} //================================================================================================= // - kangms @@ -1902,6 +1964,25 @@ message BeaconShopSoldRecordInfo double GivenPrice = 8; } +enum LargePacketProcess +{ + LargePacketProcess_None = 0; + LargePacketProcess_Start = 1; + LargePacketProcess_Processing = 2; + LargePacketProcess_End = 3; +} + +//  ̺Ʈ +message WordEventSchedule { + int32 WorldEventId = 1; // ̺Ʈ ̵ + google.protobuf.Timestamp startTime = 2; // ð + google.protobuf.Timestamp endTime = 3; // ð + int32 GlobalContributionPointMax = 4; // ̺Ʈ ⿩ Ʈ ִ밪 + int32 GlobalEventActionScoreGroupId = 5; // EventActionScoreData ̺ GroupId (̺Ʈ ൿ ) + int32 PersonalEventActionScoreGroupId = 6; // EventActionScoreData ̺ GroupId (̺Ʈ ൿ ) +} + + //================================================================================================= // - khlee //================================================================================================= @@ -1929,14 +2010,15 @@ enum PodCombatStateType PodCombatStateType_DeActive = 4; } -enum BattleRoundStateType -{ - BattleRoundStateType_None = 0; - BattleRoundStateType_Rounding = 1; - BattleRoundStateType_RoundWait = 2; - BattleRoundStateType_RoundEndAll = 3; - BattleRoundStateType_Destroyed = 4; -} +//enum BattleRoundStateType +//{ +// BattleRoundStateType_None = 0; +// +// BattleRoundStateType_Rounding = 1; +// BattleRoundStateType_RoundWait = 2; +// BattleRoundStateType_RoundEndAll = 3; +// BattleRoundStateType_Destroyed = 4; +//} message BattlePodCombatState { @@ -1958,34 +2040,171 @@ message BattleObjectInfo message BattleEventInfo { int32 eventId = 1; - int32 instanceId = 2; - google.protobuf.Timestamp startTime = 3; - int32 configDataId = 4; //BattleFFAConfigData ID - int32 rewardGroupId = 5; //BattleFFARewardData ID - int32 hotTime = 6; // ?? - int32 roundCount = 7; + int32 gameModeId = 2; + int32 instanceId = 3;// + google.protobuf.Timestamp startTime = 4; + google.protobuf.Timestamp endTime = 5; + int32 configDataId = 6; // + int32 rewardGroupId = 7; // + int32 hotTime = 8; // + int32 roundCount = 9;// } //================================================================================================= // - khlee //================================================================================================= + +message GameModeObjectInfo +{ + string anchorGuid = 1; + BoolType isActive = 2; +} + +message GameModePlayerPositionInfo +{ + string respawnUserGuid = 1; + Pos pos = 2; + string anchorGuid = 3; +} + enum GameModeState { GameModeState_None = 0; - //common - GameModeState_Destroyed = 1; - - //TPS FFA - GameModeState_Rounding = 11; - GameModeState_RoundWait = 12; - GameModeState_RoundEndAll = 13; - + GameModeState_Loading = 1; + GameModeState_Created = 2; + GameModeState_Destroyed = 3; - GameModeState_Ready = 21; - GameModeState_Start = 22; - GameModeState_End = 23; + GameModeState_LoadingWait = 11; + GameModeState_Wait = 12; + GameModeState_Ready = 13; + GameModeState_Play = 14; + GameModeState_Result = 15; + GameModeState_RewardSummary = 16; + GameModeState_End = 17; + + + //TPS FFA FFA + GameModeState_Rounding = 31; + GameModeState_RoundWait = 32; + GameModeState_RoundEndAll = 33; + GameModeState_EventEnd = 34; } +enum GameModePlayerState +{ + GameModePlayerState_None = 0; + GameModePlayerState_Join = 1; + GameModePlayerState_MatchReserve = 2; + GameModePlayerState_LoadComplete = 3; + GameModePlayerState_Wait = 4; +}//̰ ó + +enum GameModeKickReason +{ + GameModeKickReason_None = 0; + GameModeKickReason_LoadingTimeExpired = 1; +} + + +enum GameModeDeadType +{ + GameModeDeadType_None = 0; + GameModeDeadType_ByOthers = 1; + GameModeDeadType_Fall = 2; +} + +enum GameModeRewardType +{ + GameModeRewardType_None = 0; + GameModeRewardType_GetPickUpPod = 1; + GameModeRewardType_ObtainedByOwningForTime = 2; + GameModeRewardType_FinishRankReward = 3; + GameModeRewardType_MinRespawnBonusReward = 4; + GameModeRewardType_UnfinishRankReward = 5; +} + +enum StateUpdateReasonType +{ + StateUpdateReasonType_None = 0; + StateUpdateReasonType_NormalFlow = 1; + StateUpdateReasonType_NotEnoughPlayableUser = 2; + StateUpdateReasonType_EventTimeEnd = 3; + StateUpdateReasonType_UserLoadComplete = 4; + StateUpdateReasonType_LoadingWaitTimeExceeded = 5; +} + +message MatchRestriction +{ + string gameModeType = 1; + int32 matchCount = 2; + google.protobuf.Timestamp nextRefreshTime = 3; +} + +message GameModePlayPenalty +{ + string gameModeType = 1; + int32 penaltyCount = 2; + google.protobuf.Timestamp penaltyStartTime = 3; + google.protobuf.Timestamp penaltyEndTime = 4; +} + +message GameModeRewardResult +{ + string UserGuid = 1; + GameModeRewardType RewardType = 2; + CommonResult CommonResult = 3; +} + +message GameModeRewardResultWithRank +{ + int32 rank = 1; + GameModeRewardResult rewardResult = 2; +} + +message RunRaceResult +{ + string userGuid = 1; + int32 rank = 2; + int32 respawnCount = 3; + int32 completionTime = 4; // ̹ + int32 prevPersonalBestTime = 5; // ְ + bool isNewBest = 6; // ְ +} + +message MatchTeamInfo +{ + string TeamId = 1; + repeated string UserGuids = 2; +} + +message MatchRoomInfo +{ + string RoomId = 1; + repeated MatchTeamInfo Teams = 2; + repeated MatchTeamMetaInfo MatchTeamMetaInfos = 3; +} + +message MatchTeamMetaInfo +{ + string TeamId = 1; + int32 MinMemberCount = 2; + int32 MaxMemberCount = 3; +} + +message MatchRoomMetaInfo +{ + +} + +enum MatchGameStateType +{ + MatchGameStateType_None = 0; + MatchGameStateType_Idle = 1; + MatchGameStateType_Ready = 2; + MatchGameStateType_Start = 3; + MatchGameStateType_Finish = 4; + MatchGameStateType_Destroy = 5; +} diff --git a/Protocol/proto/ServerMessage.proto b/Protocol/proto/ServerMessage.proto index 34478fa..5e8f6bf 100644 --- a/Protocol/proto/ServerMessage.proto +++ b/Protocol/proto/ServerMessage.proto @@ -113,8 +113,8 @@ message ServerMessage message ToFiendNotiBase { string senderId = 1; - string senderGuid = 2; - string senderNickName = 3; + string senderGuid = 2; + string senderNickName = 3; int32 senderState = 4; int32 senderMapId = 5; string receiverId = 6; @@ -125,8 +125,8 @@ message ServerMessage message InviteMyHomeBase { string senderId = 1; - string senderGuid = 2; - string senderNickName = 3; + string senderGuid = 2; + string senderNickName = 3; //int32 senderState = 4; //int32 senderMapId = 5; string receiverId = 4; @@ -134,12 +134,12 @@ message ServerMessage string receiverNickName = 6; } - message LoginNotiToFriend + message LoginNotiToFriend { ToFiendNotiBase baseInfo = 1; UserLocationInfo locationInfo = 2; } - message LogoutNotiToFriend + message LogoutNotiToFriend { ToFiendNotiBase baseInfo = 1; } @@ -150,7 +150,7 @@ message ServerMessage UserLocationInfo locationInfo = 2; } - message ReceiveInviteMyHomeNoti + message ReceiveInviteMyHomeNoti { InviteMyHomeBase baseInfo = 1; string inviterMyHomeId = 2; @@ -159,14 +159,14 @@ message ServerMessage string uniqueKey = 5; } - message ReplyInviteMyhomeNoti + message ReplyInviteMyhomeNoti { int32 acceptOrRefuse = 1; string receiverId = 2; string replyUserGuid = 3; } - message KickFromFriendsHomeNoti + message KickFromFriendsHomeNoti { string kickerGuid = 1; string kickerId = 2; @@ -217,35 +217,35 @@ message ServerMessage message KickedFromFriendsMyHomeNoti { } - + message GS2GS_REQ_RESERVATION_ENTER_TO_SERVER { ServerMoveType moveType = 1; - + string requestServerName = 2; string requestUserGuid = 3; string summonPartyGuid = 4; } - + message GS2GS_ACK_RESERVATION_ENTER_TO_SERVER { Result result = 1; - + string reservationUserGuid = 2; string reservationServerName = 3; } - + message GS2GS_REQ_RESERVATION_CANCEL_TO_SERVER { string requestServerName = 1; string requestUserGuid = 2; } - + message GS2GS_ACK_RESERVATION_CANCEL_TO_SERVER { string requestUserGuid = 1; } - + message GS2GS_NTF_RETURN_USER_LOGOUT { string returnUserGuid = 1; @@ -257,13 +257,13 @@ message ServerMessage string nickName = 2; string receiverId = 3; } - + message GS2C_NTF_PARTY_INFO { string partyGuid = 1; repeated string partyMemberGuids = 2; } - + message GS2C_NTF_PARTY_CHAT { string partyGuid = 1; @@ -271,21 +271,21 @@ message ServerMessage string partySenderNickname = 3; string partySendMessage = 4; } - + message GS2C_NTF_PARTY_INVITE_RESULT { ServerErrorCode errorCode = 1; - + string invitePartyGuid = 2; string inviteHostGuid = 3; string inviteUserGuid = 4; } - + message GS2C_NTF_DESTROY_PARTY { string destroyPartyGuid = 1; } - + message InvitePartyNoti { string inviteUserGuid = 1; string invitePartyLeaderGuid = 2; @@ -341,7 +341,7 @@ message ServerMessage } message ExchangePartyMemberMarkNoti { - string partyGuid = 1; + string partyGuid = 1; string memberUserGuid = 2; int32 markId = 3; } @@ -384,7 +384,7 @@ message ServerMessage message PartyVoteResultNoti { string partyGuid = 1; - + string voteTitle = 2; int32 resultTrue = 3; int32 resultFalse = 4; @@ -416,7 +416,7 @@ message ServerMessage string partyGuid = 1; string memberUserGuid = 2; } - + message GS2GS_NTF_DELETE_PARTY_INVITE_SEND { string partyGuid = 1; string inviteUserGuid = 2; @@ -436,7 +436,7 @@ message ServerMessage string myhomeGuid = 2; MyHomeInfo myhomeInfo = 3; } - + message GS2GS_NTF_UGC_NPC_RANK_REFRESH { } @@ -444,8 +444,8 @@ message ServerMessage string roomId = 1; string exceptUserGuid = 2; } - - message MOS2GS_NTF_USER_KICK + + message MOS2GS_NTF_USER_KICK { string userGuid = 1; LogoutReasonType logoutReasonType = 2; @@ -471,17 +471,17 @@ message ServerMessage message GS2MQS_NTF_FARMING_END { string userGuid = 1; - FarmingSummary farmingSummary = 5; // Ĺ - BoolType isApplyDb = 6; // Db Ʈ + FarmingSummary farmingSummary = 5; // 파밍 요약 정보 + BoolType isApplyDb = 6; // Db 업데이트 적용 여부 } message GS2MQS_NTF_BEACON_COMPACT_SYNC { string userGuid = 1; - UgcNpcCompact ugcNpcCompact = 5; // UgcNpc - string locatedInstanceGuid = 6; // ġ instance Guid + UgcNpcCompact ugcNpcCompact = 5; // UgcNpc 간소한 요약 정보 + string locatedInstanceGuid = 6; // 배치된 instance Guid } - + message GS2GS_NTF_RENT_FLOOR { string exceptServerName = 1; @@ -489,28 +489,28 @@ message ServerMessage int32 instanceMetaId = 3; } - message GS2GS_NTF_MODIFY_FLOOR_LINKED_INFOS + message GS2GS_NTF_MODIFY_FLOOR_LINKED_INFOS { string exceptServerName = 1; repeated ModifyFloorLinkedInfo modifyFloorLinkedInfos = 2; } - + message OS2GS_REQ_CREATE_CALIUM_CONTENT_STORAGE { string requestServerName = 1; - + string contentId = 2; double calium = 3; } - + message OS2GS_ACK_CREATE_CALIUM_CONTENT_STORAGE { Result result = 1; } - + message GS2GS_NTF_CHANGE_CALIUM_STORAGE_INFO { - + } message GS2GS_NTF_MODIFY_LAND_INFO @@ -532,33 +532,33 @@ message ServerMessage map floorProfits = 3; } - // to Other ϱ Delivery Packet ó ߰ - message GS2GS_NTF_LAND_AUCTION_HIGHEST_BIDDER_CHANGE + // 서버 to Other 서버의 접속중인 유저에게 통지하기 위한 Delivery 전용 Packet 처리 기능 추가 + message GS2GS_NTF_LAND_AUCTION_HIGHEST_BIDDER_CHANGE { - string receiverUserGuid = 1; // Ŷ ĺŰ - BoolType hasReceivedRefundMail = 2; // ȯޱ - int32 landMetaId = 3; // LandData Meta Id - - CurrencyType currencyType = 5; // ȭ - double highestBidPrice = 6; // ְ - string highestBidUserGuid = 7; // ְ ĺŰ - string highestBidUserNickname = 8; // ְ г + string receiverUserGuid = 1; // 패킷 수신 받을 유저 식별키 + BoolType hasReceivedRefundMail = 2; // 환급금 메일 수신 여부 + int32 landMetaId = 3; // 경매 대상 LandData Meta Id + + CurrencyType currencyType = 5; // 입찰한 재화 종류 + double highestBidPrice = 6; // 최고 입찰가 + string highestBidUserGuid = 7; // 최고가 입찰자 식별키 + string highestBidUserNickname = 8; // 최고가 입찰자 닉네임 } message GS2GS_NTF_LAND_AUCTION_WINNING_BID { - string winningUserGuid = 1; // ĺŰ - string winningUserNickname = 2; // г + string winningUserGuid = 1; // 낙찰자의 유저 식별키 + string winningUserNickname = 2; // 낙찰 받은 유저의 닉네임 - int32 landMetaId = 5; // LandData Meta Id - repeated int32 buildingMetaIds = 6; // 忡 ġ BuildingData Meta Id - BoolType isNewRecvMail = 7; // ο + int32 landMetaId = 5; // 낙찰 받은 LandData Meta Id + repeated int32 buildingMetaIds = 6; // 낙찰 받은 랜드에 배치된 BuildingData Meta Id 목록 + BoolType isNewRecvMail = 7; // 새로운 수신 메일 설정 } message GS2GS_NTF_LAND_AUCTION_RESERVATION { - repeated int32 toAddActivitings = 1; // Ȱȭ Ͽ ߰ Meta Id - } + repeated int32 toAddActivitings = 1; // 활성화 목록에 추가할 랜드 Meta Id + } message GS2GS_NTF_ADD_BUILDING_PROFIT_HISTORY { @@ -592,6 +592,128 @@ message ServerMessage BoolType hasBeaconShopItem = 3; } + message MOS2GS_NTF_UPDATE_BANNER + { + } + + message MOS2GS_NTF_QUEST_TASK_FORCE_COMPLETE + { + int32 reqId = 1; + string accountId = 2; + string questKey = 3; + int32 taskId = 4; + } + + message MOS2GS_NTF_UPDATE_RANKING_SCHEDULE + { + } + + message GS2GS_NTF_MODIFY_RANKING_INFO + { + string rankingGuid = 1; + } + + message GS2GS_NTF_REFRESH_RANK + { + string rankingGuid = 1; + } + + message MOS2GS_REQ_UPDATE_RANKING_INTERVAL + { + string rankingGuid = 1; + IntervalType IntervalType = 2; + } + + //============================================================== + // Game Match + + // 매칭 상태 노티 + message GS2C_NTF_MATCH_STATUS + { + MatchStatusInfo matchStatusInfo = 1; + } + + message GS2MS_REQ_MATCH_RESERVE + { + string traceId = 1; + MatchUserInfo matchUserInfo = 2; + } + + message MS2GS_ACK_MATCH_RESERVE + { + string traceId = 1; + ServerErrorCode errorCode = 2; + MatchUserInfo matchPlayerInfo = 3; + MatchStatusInfo matchStatusInfo = 4; + } + + message GS2MS_REQ_MATCH_CANCEL + { + string traceId = 1; + string serverName = 2; + string userGuid = 3; + MatchCancelType matchCancelType = 4; + } + + message MS2GS_ACK_MATCH_CANCEL + { + string traceId = 1; + ServerErrorCode errorCode = 2; + string userGuid = 3; + MatchCancelType matchCancelType = 4; + } + + message MS2GS_NTF_MATCH_STATUS + { + string userGuid = 1; + // 현재 매칭 상태 + MatchStatusInfo matchStatusInfo = 2; + } + + message MS2GS_NTF_MATCH_RESULT + { + MatchStatusType matchStatus = 1; + MatchUserInfo matchUserInfo = 2; + string matchRoomKey = 3; + string matchUpTeamId = 4; + } + + // 게임 중 유저 퇴장 처리를 Match 서버에 알림 + message GS2MS_NTF_MATCH_GAME_QUIT + { + string roomId = 1; + string userGuid = 2; + } + + // 게임에 실제로 멤버가 입장하면 호출 + message GS2MS_NTF_MATCH_GAME_JOIN + { + string roomId = 1; + string userGuid = 2; + } + + message GS2MS_NTF_MATCH_CHANGE_GAME_STATE + { + string roomId = 1; + MatchGameStateType state = 2; + } + + message MS2GS_NTF_MATCH_GAME_JOIN_RESERVE + { + string userGuid = 1; + string roomId = 2; + string teamId = 3; + } + + // 매칭 관련 환경 테스트 환경용 MQ CMD + message GS2MS_NTF_MATCH_CHEAT_CMD + { + repeated string args = 1; + } + + + //================================================================== + google.protobuf.Timestamp messageTime = 1; string messageSender = 2; oneof msg { @@ -681,6 +803,29 @@ message ServerMessage GS2GS_NTF_UPDATE_SOLD_RECORD ntfUpdateSoldRecord = 89; GS2GS_NTF_UPDATE_BEACON_SHOP_ITEM ntfUpdateBeaconShopItem = 90; + + MOS2GS_NTF_UPDATE_BANNER ntfUpdateBanner = 91; + MOS2GS_NTF_QUEST_TASK_FORCE_COMPLETE ntfQuestTaskForceComplete = 92; + MOS2GS_NTF_UPDATE_RANKING_SCHEDULE ntfUpdateRankingSchedule = 93; + GS2GS_NTF_MODIFY_RANKING_INFO ntfModifyRankingInfo = 94; + GS2GS_NTF_REFRESH_RANK ntfRefreshRank = 95; + MOS2GS_REQ_UPDATE_RANKING_INTERVAL reqUpdateRankingInterval = 96; + + //======================================================== + // Game Matching + GS2MS_REQ_MATCH_RESERVE reqMatchReserve = 110001; + MS2GS_ACK_MATCH_RESERVE ackMatchReserve = 110002; + GS2MS_REQ_MATCH_CANCEL reqMatchCancel = 110003; + MS2GS_ACK_MATCH_CANCEL ackMatchCancel = 110004; + MS2GS_NTF_MATCH_STATUS ntfMatchStatus = 110005; + MS2GS_NTF_MATCH_RESULT ntfMatchResult = 110006; + + GS2MS_NTF_MATCH_CHANGE_GAME_STATE ntfMatchChangeGameState = 110011; + GS2MS_NTF_MATCH_GAME_QUIT ntfMatchGameQuit = 110012; + GS2MS_NTF_MATCH_GAME_JOIN ntfMatchGameJoin = 110013; + MS2GS_NTF_MATCH_GAME_JOIN_RESERVE ntfMatchGameJoinReserve = 110014; + + GS2MS_NTF_MATCH_CHEAT_CMD ntfMatchCheatCmd = 110019; } } diff --git a/Protocol/proto_build.bat b/Protocol/proto_build.bat index f97687d..e4c8ed1 100644 --- a/Protocol/proto_build.bat +++ b/Protocol/proto_build.bat @@ -5,7 +5,7 @@ cd /d %~dp0proto rem =============================================================================================== rem CSharp 코드 생성 및 출력 rem =============================================================================================== -set protoc_exe=..\..\..\ThirdPartyPackages\protobuf\3.21.12\bin\protoc.exe +set protoc_exe=..\..\..\ThirdPartyPackages\protobuf\31.1-win64\bin\protoc.exe set proto_out=..\out-Proto\ del %proto_out% /q /f diff --git a/Protocol/proto_build.sh b/Protocol/proto_build.sh new file mode 100644 index 0000000..e052808 --- /dev/null +++ b/Protocol/proto_build.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash + +# 스크립트가 위치한 디렉토리로 이동 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR/proto" + +# =============================================================================================== +# CSharp 코드 생성 및 출력 +# =============================================================================================== + +# protoc 실행 파일과 출력 경로 설정 +PROTOC_EXE="../../../ThirdPartyPackages/protobuf/31.1-linux-x86_64/protoc" +PROTO_OUT="../out-Proto/" + +# Linux에서는 protoc 실행 파일 확장자가 없을 수 있음 +if [ ! -f "$PROTOC_EXE" ] && [ -f "${PROTOC_EXE}.exe" ]; then + # .exe 파일이 있다면 mono로 실행 + PROTOC_EXE="${PROTOC_EXE}.exe" + USE_MONO=true +else + USE_MONO=false +fi + +# 출력 디렉토리의 기존 파일들 삭제 +if [ -d "$PROTO_OUT" ]; then + rm -rf "$PROTO_OUT"/* + if [ $? -ne 0 ]; then + echo "Failed to DEL from Proto out path !!! - Path : [$PROTO_OUT] !!!" + exit $? + fi +else + # 디렉토리가 없으면 생성 + mkdir -p "$PROTO_OUT" +fi + +# protoc 실행 함수 +run_protoc() { + if [ "$USE_MONO" = true ]; then + mono "$PROTOC_EXE" "$@" + else + "$PROTOC_EXE" "$@" + fi +} + +# 컴파일할 proto 파일들 목록 +PROTO_FILES=( + "Define_Common.proto" + "Define_Meta.proto" + "Define_Result.proto" + "Define_ProgramVersion.proto" + "Auth_Protocol.proto" + "Game_Define.proto" + "Game_Protocol_Define.proto" + "Game_Protocol.proto" + "Community_Protocol.proto" + "ClientToLogin.proto" + "ClientToGame.proto" + "ClientToChat.proto" + "ServerMessage.proto" +) + +# 각 proto 파일에 대해 C# 코드 생성 +for proto_file in "${PROTO_FILES[@]}"; do + if [ -f "$proto_file" ]; then + echo "Generating C# code from $proto_file..." + run_protoc --csharp_opt=serializable --csharp_out="$PROTO_OUT" "$proto_file" + + if [ $? -ne 0 ]; then + echo "ERROR !!! - Failed to generate C# code from $proto_file - ErrorCode : $?" + exit $? + fi + else + echo "Warning: Proto file $proto_file not found, skipping..." + fi +done + +# 상위 디렉토리로 이동 +cd .. + +# proto 파일들을 대상 디렉토리로 복사 +TARGET_PROTO_DIR="../../../Common/MS2Packet/proto" + +# 대상 디렉토리가 없으면 생성 +if [ ! -d "$TARGET_PROTO_DIR" ]; then + mkdir -p "$TARGET_PROTO_DIR" +fi + +echo "Copying proto files to $TARGET_PROTO_DIR..." +cp proto/*.proto "$TARGET_PROTO_DIR/" + +if [ $? -ne 0 ]; then + echo "ERROR !!! - Failed to copy proto files - ErrorCode : $?" + exit $? +fi + +# 성공적으로 완료 +echo "ProtoBuf Generation completed successfully" +exit 0 diff --git a/Server-Build.sln b/Server-Build.sln index 69eb1e8..2762e6f 100644 --- a/Server-Build.sln +++ b/Server-Build.sln @@ -61,8 +61,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServerControlAgent", "..\To EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServerControlCenter", "..\Tools\ControlCenter\ServerControlCenter\ServerControlCenter.csproj", "{CADF7BE9-9067-436F-A025-7560883F11C7}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServerControlTool", "..\Tools\ControlCenter\ServerControlTool\ServerControlTool.csproj", "{6EC33358-0FD4-4971-80AB-E27BD4604B33}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServerControlPatch", "..\Tools\ControlCenter\ServerControlPatch\ServerControlPatch.csproj", "{9212468C-B601-442B-B972-D111DD5F1F9E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCenter.SFTP", "..\Tools\ControlCenter\ControlCenter.SFTP\ControlCenter.SFTP.csproj", "{AB26C1CE-B0FB-4063-B052-AB3F6C816CF1}" @@ -86,6 +84,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCenter.NamedPipeHost EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServerBase", "ServerBase\ServerBase.csproj", "{C8F1EDE5-4CBB-4FEB-96DA-62C91E62CD87}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatchServer", "MatchServer\MatchServer.csproj", "{F54B6DD2-24B9-454A-8F79-01D6A098590E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -177,12 +177,6 @@ Global {CADF7BE9-9067-436F-A025-7560883F11C7}.Release|Any CPU.Build.0 = Release|Any CPU {CADF7BE9-9067-436F-A025-7560883F11C7}.Shipping|Any CPU.ActiveCfg = Shipping|Any CPU {CADF7BE9-9067-436F-A025-7560883F11C7}.Shipping|Any CPU.Build.0 = Shipping|Any CPU - {6EC33358-0FD4-4971-80AB-E27BD4604B33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6EC33358-0FD4-4971-80AB-E27BD4604B33}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6EC33358-0FD4-4971-80AB-E27BD4604B33}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6EC33358-0FD4-4971-80AB-E27BD4604B33}.Release|Any CPU.Build.0 = Release|Any CPU - {6EC33358-0FD4-4971-80AB-E27BD4604B33}.Shipping|Any CPU.ActiveCfg = Shipping|Any CPU - {6EC33358-0FD4-4971-80AB-E27BD4604B33}.Shipping|Any CPU.Build.0 = Shipping|Any CPU {9212468C-B601-442B-B972-D111DD5F1F9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9212468C-B601-442B-B972-D111DD5F1F9E}.Debug|Any CPU.Build.0 = Debug|Any CPU {9212468C-B601-442B-B972-D111DD5F1F9E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -193,8 +187,8 @@ Global {AB26C1CE-B0FB-4063-B052-AB3F6C816CF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {AB26C1CE-B0FB-4063-B052-AB3F6C816CF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {AB26C1CE-B0FB-4063-B052-AB3F6C816CF1}.Release|Any CPU.Build.0 = Release|Any CPU - {AB26C1CE-B0FB-4063-B052-AB3F6C816CF1}.Shipping|Any CPU.ActiveCfg = Shipping|Any CPU - {AB26C1CE-B0FB-4063-B052-AB3F6C816CF1}.Shipping|Any CPU.Build.0 = Shipping|Any CPU + {AB26C1CE-B0FB-4063-B052-AB3F6C816CF1}.Shipping|Any CPU.ActiveCfg = Release|Any CPU + {AB26C1CE-B0FB-4063-B052-AB3F6C816CF1}.Shipping|Any CPU.Build.0 = Release|Any CPU {9528EE34-6B6E-42B8-8423-67E20040C365}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9528EE34-6B6E-42B8-8423-67E20040C365}.Debug|Any CPU.Build.0 = Debug|Any CPU {9528EE34-6B6E-42B8-8423-67E20040C365}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -217,14 +211,14 @@ Global {AEC33405-74E8-42E5-975E-159AF1FEF4D7}.Debug|Any CPU.Build.0 = Debug|Any CPU {AEC33405-74E8-42E5-975E-159AF1FEF4D7}.Release|Any CPU.ActiveCfg = Release|Any CPU {AEC33405-74E8-42E5-975E-159AF1FEF4D7}.Release|Any CPU.Build.0 = Release|Any CPU - {AEC33405-74E8-42E5-975E-159AF1FEF4D7}.Shipping|Any CPU.ActiveCfg = Debug|Any CPU - {AEC33405-74E8-42E5-975E-159AF1FEF4D7}.Shipping|Any CPU.Build.0 = Debug|Any CPU + {AEC33405-74E8-42E5-975E-159AF1FEF4D7}.Shipping|Any CPU.ActiveCfg = Release|Any CPU + {AEC33405-74E8-42E5-975E-159AF1FEF4D7}.Shipping|Any CPU.Build.0 = Release|Any CPU {F207DEF6-0A41-4190-B2A0-854E8EB9CF66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F207DEF6-0A41-4190-B2A0-854E8EB9CF66}.Debug|Any CPU.Build.0 = Debug|Any CPU {F207DEF6-0A41-4190-B2A0-854E8EB9CF66}.Release|Any CPU.ActiveCfg = Release|Any CPU {F207DEF6-0A41-4190-B2A0-854E8EB9CF66}.Release|Any CPU.Build.0 = Release|Any CPU - {F207DEF6-0A41-4190-B2A0-854E8EB9CF66}.Shipping|Any CPU.ActiveCfg = Debug|Any CPU - {F207DEF6-0A41-4190-B2A0-854E8EB9CF66}.Shipping|Any CPU.Build.0 = Debug|Any CPU + {F207DEF6-0A41-4190-B2A0-854E8EB9CF66}.Shipping|Any CPU.ActiveCfg = Release|Any CPU + {F207DEF6-0A41-4190-B2A0-854E8EB9CF66}.Shipping|Any CPU.Build.0 = Release|Any CPU {0EEF137A-5F3B-4AE9-8433-CFDCC57448B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0EEF137A-5F3B-4AE9-8433-CFDCC57448B8}.Debug|Any CPU.Build.0 = Debug|Any CPU {0EEF137A-5F3B-4AE9-8433-CFDCC57448B8}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -237,6 +231,12 @@ Global {C8F1EDE5-4CBB-4FEB-96DA-62C91E62CD87}.Release|Any CPU.Build.0 = Release|Any CPU {C8F1EDE5-4CBB-4FEB-96DA-62C91E62CD87}.Shipping|Any CPU.ActiveCfg = Shipping|Any CPU {C8F1EDE5-4CBB-4FEB-96DA-62C91E62CD87}.Shipping|Any CPU.Build.0 = Shipping|Any CPU + {F54B6DD2-24B9-454A-8F79-01D6A098590E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F54B6DD2-24B9-454A-8F79-01D6A098590E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F54B6DD2-24B9-454A-8F79-01D6A098590E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F54B6DD2-24B9-454A-8F79-01D6A098590E}.Release|Any CPU.Build.0 = Release|Any CPU + {F54B6DD2-24B9-454A-8F79-01D6A098590E}.Shipping|Any CPU.ActiveCfg = Shipping|Any CPU + {F54B6DD2-24B9-454A-8F79-01D6A098590E}.Shipping|Any CPU.Build.0 = Shipping|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -258,7 +258,6 @@ Global {D2E556DA-585F-47F2-A1D7-5A513AAA7B79} = {E1682B4B-7FBC-4E9B-A118-7E7B5D087E2C} {0C596B72-DD05-4AAA-BFDB-BB21504BA703} = {5029441A-EB8A-41C3-B5FE-792BEA9EA3DD} {CADF7BE9-9067-436F-A025-7560883F11C7} = {5029441A-EB8A-41C3-B5FE-792BEA9EA3DD} - {6EC33358-0FD4-4971-80AB-E27BD4604B33} = {5029441A-EB8A-41C3-B5FE-792BEA9EA3DD} {9212468C-B601-442B-B972-D111DD5F1F9E} = {5029441A-EB8A-41C3-B5FE-792BEA9EA3DD} {AB26C1CE-B0FB-4063-B052-AB3F6C816CF1} = {E1682B4B-7FBC-4E9B-A118-7E7B5D087E2C} {24D69F44-2006-4188-951F-3AE60039BE99} = {F804B499-8B97-41FB-A91B-ADD5F0F72341} @@ -269,6 +268,7 @@ Global {F207DEF6-0A41-4190-B2A0-854E8EB9CF66} = {E1682B4B-7FBC-4E9B-A118-7E7B5D087E2C} {0EEF137A-5F3B-4AE9-8433-CFDCC57448B8} = {E1682B4B-7FBC-4E9B-A118-7E7B5D087E2C} {C8F1EDE5-4CBB-4FEB-96DA-62C91E62CD87} = {30BFCEC9-AA70-49EA-9187-35C952BCA520} + {F54B6DD2-24B9-454A-8F79-01D6A098590E} = {F804B499-8B97-41FB-A91B-ADD5F0F72341} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution VisualSVNWorkingCopyRoot = . diff --git a/ServerBase/0. Base/BaseGetSet.cs b/ServerBase/0. Base/BaseGetSet.cs index 331073d..6312e02 100644 --- a/ServerBase/0. Base/BaseGetSet.cs +++ b/ServerBase/0. Base/BaseGetSet.cs @@ -49,7 +49,7 @@ using Microsoft.Extensions.Configuration; namespace ServerBase; //============================================================================================= -// +// //============================================================================================= public partial class Initializers { @@ -61,7 +61,7 @@ public partial class Initializers //============================================================================================= -// +// //============================================================================================= public partial class ServerConfig { @@ -158,6 +158,8 @@ public partial class ServerConfig public LoadBalancingRule LoadBalancingRule { get; set; } = new(); + public MatchRegionInfo[] MatchRegionInfos { get; private set; } = []; + public bool isLoadedConfig() => m_is_loaded_config; } @@ -178,7 +180,7 @@ public class MongoDbConf } //============================================================================================= -// +// //============================================================================================= public class AWSConf { @@ -220,7 +222,7 @@ public static partial class ServerConfigHelper } //============================================================================================= -// +// //============================================================================================= public abstract partial class SessionBase : ISession, IInitializer { @@ -240,11 +242,10 @@ public abstract partial class SessionBase : ISession, IInitializer public int getPacketChunkSize() => m_packet_chunk_size; - public LargePacketHandler getLargePacketHandler() => m_large_packet_handler; } //============================================================================================= -// +// //============================================================================================= public abstract partial class ListenSessionBase : SessionBase { @@ -253,16 +254,16 @@ public abstract partial class ListenSessionBase : SessionBase public ConcurrentDictionary getEntityWithSessions() => m_entity_with_sessions; public NetServer getNetServer() - { + { var net_server = getRmiHost() as NetServer; NullReferenceCheckHelper.throwIfNull(net_server, () => $"net_server is null !!!"); - + return net_server; } } //============================================================================================= -// +// //============================================================================================= public abstract partial class ConnectToServerSessionBase : SessionBase { @@ -282,7 +283,7 @@ public abstract partial class ConnectToServerSessionBase : SessionBase } //============================================================================================= -// +// //============================================================================================= public abstract partial class ServerLogicBase : IServerLogic { @@ -336,7 +337,7 @@ public abstract partial class ServerLogicBase : IServerLogic } //============================================================================================= -// +// //============================================================================================= public abstract partial class EntityBase : IActor, IInitializer, IWithTaskSerializer { @@ -364,7 +365,7 @@ public abstract partial class EntityBase : IActor, IInitializer, IWithTaskSerial } //============================================================================================= -// +// //============================================================================================= public abstract partial class EntityAttributeBase : IInitializer { @@ -403,14 +404,14 @@ public abstract partial class EntityAttributeBase : IInitializer public void setEntityOfOwnerEntityType(OwnerEntityType ownerEntityType, EntityBase entityOfOwnerEntityType) { m_owner_entity_type = ownerEntityType; - m_entity_of_owner_entity_type = entityOfOwnerEntityType; + m_entity_of_owner_entity_type = entityOfOwnerEntityType; } public EntityBase getOwner() => m_owner; } //============================================================================================= -// +// //============================================================================================= public abstract partial class EntityAttributeTransactorBase : IEntityAttributeTransactor @@ -430,7 +431,7 @@ public abstract partial class EntityAttributeTransactorBase : } //============================================================================================= -// +// //============================================================================================= public abstract partial class EntityActionBase : IInitializer { @@ -438,7 +439,7 @@ public abstract partial class EntityActionBase : IInitializer } //============================================================================================= -// +// //============================================================================================= public abstract partial class EntityHFSMBase : HFSMBase, IWithEntityOwner { @@ -446,7 +447,7 @@ public abstract partial class EntityHFSMBase : HFSMBase where TPrimaryKey : notnull @@ -481,14 +482,14 @@ public abstract partial class UserManagerBase } //============================================================================================= -// +// //============================================================================================= public abstract partial class RedisRequestBase : IInitializer { public RedisConnector getRedisConnector() => m_redis_connector; public IDatabase getDatabase() - { + { var db = m_redis_connector.getDatabase(); NullReferenceCheckHelper.throwIfNull(db, () => $"db is null !!!"); return db; @@ -507,7 +508,7 @@ public abstract partial class RedisRequestBase : IInitializer } //============================================================================================= -// +// //============================================================================================= public abstract partial class RedisRequestPrivateBase : RedisRequestBase { @@ -515,7 +516,7 @@ public abstract partial class RedisRequestPrivateBase : RedisRequestBase } //============================================================================================= -// +// //============================================================================================= public abstract partial class RedisRequestSharedBase : RedisRequestBase { @@ -528,7 +529,7 @@ public abstract partial class RedisRequestSharedBase : RedisRequestBase //============================================================================================= -// +// //============================================================================================= public partial class DynamoDbDocumentQueryContext : IQueryContext { @@ -540,7 +541,7 @@ public partial class DynamoDbDocumentQueryContext : IQueryContext } //============================================================================================= -// +// //============================================================================================= public partial class DynamoDbItemRequestQueryContext : IQueryContext { @@ -552,7 +553,7 @@ public partial class DynamoDbItemRequestQueryContext : IQueryContext } //============================================================================================= -// +// //============================================================================================= public abstract partial class DynamoDbDocBase : IRowData { @@ -602,7 +603,7 @@ public abstract partial class DynamoDbDocBase : IRowData } //============================================================================================= -// +// //============================================================================================= public abstract partial class AttribBase { @@ -610,7 +611,7 @@ public abstract partial class AttribBase } //============================================================================================= -// +// //============================================================================================= public partial class AttribWrapper : IAttribWrapper @@ -624,7 +625,7 @@ public partial class AttribWrapper : IAttribWrapper } //============================================================================================= -// +// //============================================================================================= public partial class MySqlDbConnector { @@ -636,7 +637,7 @@ public partial class MySqlDbConnector } //============================================================================================= -// +// //============================================================================================= public partial class LogAction { @@ -646,7 +647,7 @@ public partial class LogAction } //============================================================================================= -// +// //============================================================================================= public abstract partial class ILogInvoker { diff --git a/ServerBase/1. Define/DatabaseDefine.cs b/ServerBase/1. Define/DatabaseDefine.cs index eb164cd..64fb405 100644 --- a/ServerBase/1. Define/DatabaseDefine.cs +++ b/ServerBase/1. Define/DatabaseDefine.cs @@ -10,6 +10,9 @@ using DYNAMO_DB_TABLE_NAME = System.String; namespace ServerBase; + +// HANDOVER: 메타버스 DynamoDB 메인 테이블명 정의 + public static class DynamoDbDefine { public static class TableNames diff --git a/ServerBase/BusinessLog/BusinessLogger.cs b/ServerBase/BusinessLog/BusinessLogger.cs index 4ddb45c..a76cb1f 100644 --- a/ServerBase/BusinessLog/BusinessLogger.cs +++ b/ServerBase/BusinessLog/BusinessLogger.cs @@ -5,6 +5,9 @@ using ServerCore; using ServerBase; namespace ServerBase; +// HANDOVER: 비지니스 로그 처리 최상위 클래스 이다. +// IAppender, IFormatter, ILogInvoker 를 활용하여 collectLog(), collectLogs() 통해서 비즈니스 로그를 작성 한다. + //======================================================================================================== // 비즈니스 로그 처리자 // diff --git a/ServerBase/Cache/CacheBase.cs b/ServerBase/Cache/CacheBase.cs index 4e5643b..42a5f74 100644 --- a/ServerBase/Cache/CacheBase.cs +++ b/ServerBase/Cache/CacheBase.cs @@ -13,6 +13,8 @@ using ServerCore; using ServerBase; namespace ServerBase; +// HANDOVER: 캐시용 Base 클래스 이다. + public abstract class CacheBase { public DateTime CreatedDateTime { get; set; } diff --git a/ServerBase/Cache/ServerMetrics/ServerMetricsCacheRequest.cs b/ServerBase/Cache/ServerMetrics/ServerMetricsCacheRequest.cs index 6cb260d..fb398e6 100644 --- a/ServerBase/Cache/ServerMetrics/ServerMetricsCacheRequest.cs +++ b/ServerBase/Cache/ServerMetrics/ServerMetricsCacheRequest.cs @@ -16,6 +16,10 @@ using SERVER_METRICS_TRIGGER_TYPE = System.UInt32; namespace ServerBase; + +// HANDOVER: RedisRequestWithLambdaBase 기반으로 서버 지표를 처리하는 클래스 이다. + + public class ServerInfo : IComparable { public ServerInfo() @@ -64,6 +68,7 @@ public class ServerInfo : IComparable } } + public class ServerMetricsCacheRequest : RedisRequestWithLambdaBase { public class CacheServerKey diff --git a/ServerBase/Config/ServerConfig.cs b/ServerBase/Config/ServerConfig.cs index f66c41b..8249d4e 100644 --- a/ServerBase/Config/ServerConfig.cs +++ b/ServerBase/Config/ServerConfig.cs @@ -119,7 +119,7 @@ public class NftRule //========================================================================================= public class AIChatConfig { - public string BaseAddress { get; set; } = "https://caliverse-dev.caveduck.io"; + public string BaseAddress { get; set; } = "https://ai-dev-api.caliverse.io"; public string PrivateKey { get; set; } = "/EbpXsuA4Wi3M9NV11dQj1mtw8FGB42txTUBVfyS2BrdLq7JEwGRFDtmzdDQNMMfByFEX8qJhkpbPZnepTbcCg=="; } @@ -291,7 +291,7 @@ public partial class ServerConfig setRegionId(AWS.Region); } - // Cloud Watch Log + // Cloud Watch Log if (aws["CloudWatchLog"] is JObject cloud_watch_log) { AWS.CloudWatchLog = new CloudWatchLogConf @@ -533,7 +533,20 @@ public partial class ServerConfig LoadBalancingRule.ToEtcServerRule.MaxRate = to_etc_server_rule["MaxRate"]?.Value() ?? LoadBalancingRule.ToEtcServerRule.MaxRate; LoadBalancingRule.ToEtcServerRule.UserLanguageBased = to_etc_server_rule["UserLanguageBased"]?.Value() ?? LoadBalancingRule.ToEtcServerRule.UserLanguageBased; } + } + var load_match_regions = loadedJosn["MatchRegions"]; + if (null != load_match_regions) + { + // 리스트 형식으로 가져온다. + var regions = load_match_regions.ToObject(); + if (null != regions) + { + MatchRegionInfos = regions; + } + } + + MetaverseBroker = loadBrokerConfig(loadedJosn); System.IO.Directory.CreateDirectory(LogDir); System.IO.Directory.CreateDirectory(DumpDir); @@ -543,6 +556,25 @@ public partial class ServerConfig } + private MetaverseBrokerConfig? loadBrokerConfig(JObject loadedJosn) + { + var jToken = loadedJosn["MetaverseBroker"]; + if (jToken != null) + { + return new MetaverseBrokerConfig + { + JwtSecretKey = jToken["JwtSecretKey"]?.Value() ?? string.Empty, + ExpireMinutes = jToken["ExpireMinutes"]?.Value() ?? 1440, + Issuer = jToken["Issuer"]?.Value() ?? string.Empty, + Audience = jToken["Audience"]?.Value() ?? string.Empty, + MetaverseBrokerDb = jToken["MetaverseBrokerDb"]?.Value() ?? string.Empty, + MetaverseBrokerDbLocal = jToken["MetaverseBrokerDbLocal"]?.Value() ?? string.Empty, + SsoAccountDb = jToken["SsoAccountDb"]?.Value() ?? string.Empty + }; + } + return null; + } + public Dictionary getVersionPaths() { return m_program_versions; diff --git a/ServerBase/Config/ServerConfigMetaverseBroker.cs b/ServerBase/Config/ServerConfigMetaverseBroker.cs index 7cdf860..e13ef5e 100644 --- a/ServerBase/Config/ServerConfigMetaverseBroker.cs +++ b/ServerBase/Config/ServerConfigMetaverseBroker.cs @@ -21,31 +21,31 @@ public partial class ServerConfig public class ServerConfigMetaverseBroker : ServerConfig { - public override async Task parseConfig(JObject loadedJosn) - { - var result = await base.parseConfig(loadedJosn); - if (result.isSuccess()) - { - var r = loadedJosn.ContainsKey("MetaverseBroker"); - var jToken = loadedJosn["MetaverseBroker"]; - if (jToken != null) - { - MetaverseBroker = new MetaverseBrokerConfig - { - JwtSecretKey = jToken["JwtSecretKey"]?.Value() ?? string.Empty, - ExpireMinutes = jToken["ExpireMinutes"]?.Value() ?? 1440, - Issuer = jToken["Issuer"]?.Value() ?? string.Empty, - Audience = jToken["Audience"]?.Value() ?? string.Empty, - MetaverseBrokerDb = jToken["MetaverseBrokerDb"]?.Value() ?? string.Empty, - MetaverseBrokerDbLocal = jToken["MetaverseBrokerDbLocal"]?.Value() ?? string.Empty, - SsoAccountDb = jToken["SsoAccountDb"]?.Value() ?? string.Empty - }; - return result; - } - } - - result.setFail(ServerErrorCode.ServerConfigFileNotFound, "server config error : metaverse_broker not found"); - - return await Task.FromResult(result); - } -} \ No newline at end of file + // public override async Task parseConfig(JObject loadedJosn) + // { + // var result = await base.parseConfig(loadedJosn); + // if (result.isSuccess()) + // { + // var r = loadedJosn.ContainsKey("MetaverseBroker"); + // var jToken = loadedJosn["MetaverseBroker"]; + // if (jToken != null) + // { + // MetaverseBroker = new MetaverseBrokerConfig + // { + // JwtSecretKey = jToken["JwtSecretKey"]?.Value() ?? string.Empty, + // ExpireMinutes = jToken["ExpireMinutes"]?.Value() ?? 1440, + // Issuer = jToken["Issuer"]?.Value() ?? string.Empty, + // Audience = jToken["Audience"]?.Value() ?? string.Empty, + // MetaverseBrokerDb = jToken["MetaverseBrokerDb"]?.Value() ?? string.Empty, + // MetaverseBrokerDbLocal = jToken["MetaverseBrokerDbLocal"]?.Value() ?? string.Empty, + // SsoAccountDb = jToken["SsoAccountDb"]?.Value() ?? string.Empty + // }; + // return result; + // } + // } + // + // result.setFail(ServerErrorCode.ServerConfigFileNotFound, "server config error : metaverse_broker not found"); + // + // return await Task.FromResult(result); + // } +} diff --git a/ServerBase/DB/DynamoDb/AttribBase.cs b/ServerBase/DB/DynamoDb/AttribBase.cs index bfb29ab..7ea15cc 100644 --- a/ServerBase/DB/DynamoDb/AttribBase.cs +++ b/ServerBase/DB/DynamoDb/AttribBase.cs @@ -14,9 +14,11 @@ using Newtonsoft.Json.Linq; namespace ServerBase; +// HANDOVER: DB에 정보를 읽고, 쓸때 직렬화 시켜주는 Base 클래스 이다. + public abstract partial class AttribBase { - // AttribType은 DynamoDb Document 정보를 Read시 식별키로 사용되기 때문에 고유하게 정의되어야 한다 !!! - kangms + // AttribType은 DynamoDb Document 정보를 Read시 식별키로 사용되기 때문에 고유하게 정의되어야 한다 !!! - kangms [JsonProperty("attrib_type")] public readonly string AttribType; diff --git a/ServerBase/DB/DynamoDb/DynamoDbClient.cs b/ServerBase/DB/DynamoDb/DynamoDbClient.cs index 20f89a6..1c26bad 100644 --- a/ServerBase/DB/DynamoDb/DynamoDbClient.cs +++ b/ServerBase/DB/DynamoDb/DynamoDbClient.cs @@ -23,6 +23,8 @@ using DYNAMO_DB_TABLE_FULL_NAME = System.String; namespace ServerBase; +// HANDOVER: DynamoDbConnectorBase의 실체적 구현 클래스 이며, ModuleContext를 통해 각종 설정을 시도 한다. + public partial class DynamoDbClient : DynamoDbConnectorBase, IModule, IInitializer { diff --git a/ServerBase/DB/DynamoDb/DynamoDbDocBase.cs b/ServerBase/DB/DynamoDb/DynamoDbDocBase.cs index 07f3c22..f5e9130 100644 --- a/ServerBase/DB/DynamoDb/DynamoDbDocBase.cs +++ b/ServerBase/DB/DynamoDb/DynamoDbDocBase.cs @@ -19,7 +19,7 @@ using Newtonsoft.Json.Linq; using Google.Protobuf.WellKnownTypes; -using ServerCore; using ServerBase; +using ServerCore; @@ -29,6 +29,8 @@ using DYNAMO_DB_TABLE_NAME = System.String; namespace ServerBase; +// HANDOVER: DynamoDb Document SDK Wrapper 클래스 이다. + //============================================================================================= // DynamoDb Document 기반 시간 설정 // diff --git a/ServerBase/DB/DynamoDb/DynamoDbItemRequestBase.cs b/ServerBase/DB/DynamoDb/DynamoDbItemRequestBase.cs index 8a1988a..9706e57 100644 --- a/ServerBase/DB/DynamoDb/DynamoDbItemRequestBase.cs +++ b/ServerBase/DB/DynamoDb/DynamoDbItemRequestBase.cs @@ -13,6 +13,10 @@ using ServerCore; using ServerBase; namespace ServerBase; + +// HANDOVER: DynamoDb ItemRequest SDK Wrapper 클래스 이다. + + //============================================================================================= // DynamoDb ItemRequest 기반 Query Context // diff --git a/ServerBase/DB/DynamoDb/DynamoDbQueryExceptionNotifier.cs b/ServerBase/DB/DynamoDb/DynamoDbQueryExceptionNotifier.cs index b268b57..887c42a 100644 --- a/ServerBase/DB/DynamoDb/DynamoDbQueryExceptionNotifier.cs +++ b/ServerBase/DB/DynamoDb/DynamoDbQueryExceptionNotifier.cs @@ -11,6 +11,9 @@ using ServerCore; using ServerBase; namespace ServerBase; + +// HANDOVER: DynamoDb 관련 에외 처리를 통지 받는 클래스 이다. + public class DynamoDbQueryExceptionNotifier { public const string Success = "None"; // 쿼리 성공 diff --git a/ServerBase/DB/MongoDb/MongoDbConnector.cs b/ServerBase/DB/MongoDb/MongoDbConnector.cs index f4985ba..f47dc55 100644 --- a/ServerBase/DB/MongoDb/MongoDbConnector.cs +++ b/ServerBase/DB/MongoDb/MongoDbConnector.cs @@ -6,6 +6,10 @@ using ServerCore; namespace ServerBase; + +// HANDOVER: MongoDbConnectorBase 실체적 구현 클래스 이며, ModuleContext를 통해 각종 설정을 시도 한다. + + public partial class MongoDbConnector : MongoDbConnectorBase, IModule, IInitializer { private readonly ModuleContext m_module_context; diff --git a/ServerBase/DB/MySqlDb/MySqlDbConnector.cs b/ServerBase/DB/MySqlDb/MySqlDbConnector.cs index 5a68c5f..4db0810 100644 --- a/ServerBase/DB/MySqlDb/MySqlDbConnector.cs +++ b/ServerBase/DB/MySqlDb/MySqlDbConnector.cs @@ -16,6 +16,8 @@ using ServerCore; using ServerBase; namespace ServerBase; +// HANDOVER: MySqlConnection 관련 Wrapper 클래스 이다. + public partial class MySqlDbConnector : IDisposable { // MySql 접속 재시도 간격 시간 밀리초) diff --git a/ServerBase/DB/QueryBatch.cs b/ServerBase/DB/QueryBatch.cs index 79c684a..433e00c 100644 --- a/ServerBase/DB/QueryBatch.cs +++ b/ServerBase/DB/QueryBatch.cs @@ -26,6 +26,11 @@ using LOG_ACTION_TYPE = System.String; namespace ServerBase; + +// HANDOVER: 복합 DB 쿼리를 등록 및 실행 시켜주고, 예외처리 수신 받게 해주는 Base 클래스 이다. + + + //============================================================================================== // 배치 쿼리를 전담 처리해 주는 추상 클래스 이다. //============================================================================================== diff --git a/ServerBase/DB/QueryExecutorBase.cs b/ServerBase/DB/QueryExecutorBase.cs index 7e02f6d..c719140 100644 --- a/ServerBase/DB/QueryExecutorBase.cs +++ b/ServerBase/DB/QueryExecutorBase.cs @@ -14,6 +14,10 @@ using ServerCore; using ServerBase; namespace ServerBase; + +// HANDOVER: DB 쿼리를 실행해 주는 Base 클래스 이다. + + //============================================================================================== // 쿼리 내용을 작성하는 기반 클래스 //============================================================================================== diff --git a/ServerBase/DB/QueryRunnerWithDocument.cs b/ServerBase/DB/QueryRunnerWithDocument.cs index b5e3313..36b732c 100644 --- a/ServerBase/DB/QueryRunnerWithDocument.cs +++ b/ServerBase/DB/QueryRunnerWithDocument.cs @@ -18,6 +18,10 @@ using ServerCore; using ServerBase; namespace ServerBase; + +// HANDOVER: DynamoDB Document 기반 쿼리 실행 클래스 이다. + + //============================================================================================= // Amazon.DynamoDBv2.DocumentModel 기반 쿼리를 실행해 주는 클래스 이다. // diff --git a/ServerBase/DB/QueryRunnerWithItemRequest.cs b/ServerBase/DB/QueryRunnerWithItemRequest.cs index 340b8c4..1c2797a 100644 --- a/ServerBase/DB/QueryRunnerWithItemRequest.cs +++ b/ServerBase/DB/QueryRunnerWithItemRequest.cs @@ -17,6 +17,10 @@ using ServerCore; using ServerBase; namespace ServerBase; + +// HANDOVER: DynamoDB ItemRequest 기반 쿼리 실행 클래스 이다. + + //============================================================================================= // Amazon.DynamoDBv2.Model 기반 쿼리를 실행해 주는 클래스 이다. // diff --git a/ServerBase/Entity/Action/EntityActionBase.cs b/ServerBase/Entity/Action/EntityActionBase.cs index 4c8c643..d95d1f9 100644 --- a/ServerBase/Entity/Action/EntityActionBase.cs +++ b/ServerBase/Entity/Action/EntityActionBase.cs @@ -20,6 +20,9 @@ using ITEM_GUID = System.String; namespace ServerBase; +// HANDOVER: EntityBase 클래스의 행위를 위한 Base 클래스 이다. +// EntityBase의 본질적인 행위를 추상화하는 구조를 제공 한다. + public abstract partial class EntityActionBase : IInitializer { private EntityBase m_owner; diff --git a/ServerBase/Entity/Aggregation/EntityAggregator.cs b/ServerBase/Entity/Aggregation/EntityAggregator.cs index b441d7c..0126883 100644 --- a/ServerBase/Entity/Aggregation/EntityAggregator.cs +++ b/ServerBase/Entity/Aggregation/EntityAggregator.cs @@ -7,11 +7,12 @@ using System.Text; using System.Threading.Tasks; -using ServerCore; using ServerBase; +using ServerCore; namespace ServerBase; +// HANDOVER: EntityBase와 연관된 정보의 카운트를 관리 한다. public class EntityAggregator where TKey : ITuple diff --git a/ServerBase/Entity/Attribute/EntityAttributeBase.cs b/ServerBase/Entity/Attribute/EntityAttributeBase.cs index 08a71fd..b76718e 100644 --- a/ServerBase/Entity/Attribute/EntityAttributeBase.cs +++ b/ServerBase/Entity/Attribute/EntityAttributeBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -27,6 +27,8 @@ using ITEM_GUID = System.String; namespace ServerBase; +// HANDOVER: EntityBase 클래스의 속성 정보의 Base 클래스 이다. +// Cache 와 DB 정보의 읽기, 쓰기에 대한 직렬화를 재정의 한다. public abstract partial class EntityAttributeBase : IInitializer { @@ -151,14 +153,14 @@ public abstract partial class EntityAttributeBase : IInitializer public abstract EntityAttributeBase onCloned(); - + protected void deepCopyFromBase(EntityAttributeBase other) { m_attribute_state.deepCopy(other.getAttributeState()); } - + public virtual DynamoDbDocBase? onCreateDocBase() { @@ -172,13 +174,37 @@ public abstract partial class EntityAttributeBase : IInitializer { var result = new Result(); - var err_msg = $"Not implementation EntityAttribute.toDocBase() !!! : {this.getTypeName()} - {getOwner().toBasicString()}"; + var err_msg = $"Not implementation EntityAttribute.toDocBase() !!! : {this.getTypeName()} - {getOwner().toBasicString()}"; result.setFail(ServerErrorCode.NotImplemented, err_msg); Log.getLogger().warn(result.toBasicString()); return await Task.FromResult<(Result, DynamoDbDocBase?)>((result, null)); } + /// + /// Attribute의 데이터를 바탕으로 DocBase를 생성하고 관리하는 헬퍼 메서드입니다. + /// toDocBase 메서드 내부의 반복적인 코드를 줄여줍니다. + /// @heon + /// + protected TDoc getOrCreatePendingDoc(Func docFactory) + where TDoc : DynamoDbDocBase + where TAttrib : AttribBase + { + if (getTryPendingDocBase() is not TDoc tryPendingDoc) + { + tryPendingDoc = docFactory(); + + var originDoc = getOriginDocBase(); + if (null != originDoc) + { + tryPendingDoc.copyTimestampsFromOriginDocBase(originDoc); + } + + setTryPendingDocBase(tryPendingDoc); + } + return tryPendingDoc; + } + public virtual async Task fillupDocBase(TDoc copyToDocBase) where TDoc : DynamoDbDocBase { diff --git a/ServerBase/Entity/Delta/EntityDeltaRecorder.cs b/ServerBase/Entity/Delta/EntityDeltaRecorder.cs index 4cbb1a1..6a9479c 100644 --- a/ServerBase/Entity/Delta/EntityDeltaRecorder.cs +++ b/ServerBase/Entity/Delta/EntityDeltaRecorder.cs @@ -12,6 +12,9 @@ using Newtonsoft.Json.Linq; namespace ServerBase; + +// HANDOVER: EntityBase와 연관된 정보중 Delta 성격의 정보를 보관해 주는 클래스 이다. + public class EntityRecorder { private readonly EntityBase m_owner; diff --git a/ServerBase/Entity/EntityBase.cs b/ServerBase/Entity/EntityBase.cs index 00ce314..69e96d6 100644 --- a/ServerBase/Entity/EntityBase.cs +++ b/ServerBase/Entity/EntityBase.cs @@ -35,6 +35,9 @@ using ITEM_GUID = System.String; namespace ServerBase; +// HANDOVER: Entity 기반 구조의 Base 클래스 이다. +// EntityAttributeBase, EntityActionBase 를 소유할 수 있다. + /*=================================================================================================================================================== // Entity 정보 관리 흐름도 diff --git a/ServerBase/Entity/State/EntityHFSMBase.cs b/ServerBase/Entity/State/EntityHFSMBase.cs index 0e2d98e..4ccb054 100644 --- a/ServerBase/Entity/State/EntityHFSMBase.cs +++ b/ServerBase/Entity/State/EntityHFSMBase.cs @@ -24,6 +24,8 @@ using ITEM_GUID = System.String; namespace ServerBase; +// HANDOVER: EntityBase의 HFSM 기반 상태 구조의 Base 클래스 이다. + public abstract partial class EntityHFSMBase : HFSMBase, IWithEntityOwner { private readonly EntityBase m_owner; diff --git a/ServerBase/Entity/State/EntityStateBase.cs b/ServerBase/Entity/State/EntityStateBase.cs index d492978..c464547 100644 --- a/ServerBase/Entity/State/EntityStateBase.cs +++ b/ServerBase/Entity/State/EntityStateBase.cs @@ -21,6 +21,9 @@ using ITEM_GUID = System.String; namespace ServerBase; + +// HANDOVER: EntityBase의 단순 상태 구조의 Base 클래스 이다. + public abstract partial class EntityStateBase { private readonly EntityBase m_owner; diff --git a/ServerBase/Entity/Task/EntityTicker.cs b/ServerBase/Entity/Task/EntityTicker.cs index 6a0d6a2..a2307d7 100644 --- a/ServerBase/Entity/Task/EntityTicker.cs +++ b/ServerBase/Entity/Task/EntityTicker.cs @@ -10,6 +10,9 @@ using ServerCore; using ServerBase; namespace ServerBase; + +// HANDOVER: EntityBase의 독립적인 처리 보장해주는 시간 기반 갱신 클래스 이다. + public abstract class EntityTicker : EntityBase, ITaskTicker { private double m_tick_interval_milliseconds; diff --git a/ServerBase/Entity/Transaction/EntityTransactionRunnerWithScopLock.cs b/ServerBase/Entity/Transaction/EntityTransactionRunnerWithScopLock.cs index dedcc7c..8818912 100644 --- a/ServerBase/Entity/Transaction/EntityTransactionRunnerWithScopLock.cs +++ b/ServerBase/Entity/Transaction/EntityTransactionRunnerWithScopLock.cs @@ -16,6 +16,9 @@ using MASTER_GUID = System.String; namespace ServerBase; + +// HANDOVER: EntityTransactionRunner + Scope 기반 동기화를 보장해주는 클래스 이다. + public class EntityTransactionRunnerWithScopLock : IDisposable { private readonly EntityBase m_owner; diff --git a/ServerBase/Entity/Transaction/TransactionRunner.cs b/ServerBase/Entity/Transaction/TransactionRunner.cs index 7ff307c..080c3b9 100644 --- a/ServerBase/Entity/Transaction/TransactionRunner.cs +++ b/ServerBase/Entity/Transaction/TransactionRunner.cs @@ -38,6 +38,12 @@ using ITEM_GUID = System.String; namespace ServerBase; + +// HANDOVER: EntityBase가 발생한 이벤트로 순차적으로 발생한 정보의 변화들에 대해 완료될 때 캐싱해 주고, +// 예외 발생시 이전 정보로 유지해 주는 Transaction 기반 Task 실행 클래스 이다. +// EntityBase의 속성 정보를 위한 EntityAttributeBase와 DB 처리를 위한 QueryBatchBase와 연동 된다. + + public class ReservedSlotOnInven { public class ReservedSlot diff --git a/ServerBase/Etc/ModuleContext.cs b/ServerBase/Etc/ModuleContext.cs index 7d4ad00..6f1f52b 100644 --- a/ServerBase/Etc/ModuleContext.cs +++ b/ServerBase/Etc/ModuleContext.cs @@ -12,6 +12,10 @@ using SORT_ORDER_NO = System.Int32; namespace ServerBase; + +// HANDOVER: ServerLogicApp에 등록하여 모듈을 사용할 경우 ModuleContext 정보를 아규먼트 넘겨 해당 모듈을 설정 한다. + + public class ModuleContext { private readonly MODULE_ID m_module_id; diff --git a/ServerBase/Etc/ResultValue.cs b/ServerBase/Etc/ResultValue.cs index db6ea7a..07342f2 100644 --- a/ServerBase/Etc/ResultValue.cs +++ b/ServerBase/Etc/ResultValue.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; -using ServerCore; using ServerBase; +using ServerCore; namespace ServerBase; diff --git a/ServerBase/Event/SimpleEventTriggerBase.cs b/ServerBase/Event/SimpleEventTriggerBase.cs index d89aafd..e4344a1 100644 --- a/ServerBase/Event/SimpleEventTriggerBase.cs +++ b/ServerBase/Event/SimpleEventTriggerBase.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; -using ServerCore; using ServerBase; +using ServerCore; namespace ServerBase; diff --git a/ServerBase/Helper/DataCopyHelper.cs b/ServerBase/Helper/DataCopyHelper.cs index cb43fa4..cf62287 100644 --- a/ServerBase/Helper/DataCopyHelper.cs +++ b/ServerBase/Helper/DataCopyHelper.cs @@ -10,7 +10,7 @@ using Grpc.Core; using Microsoft.AspNetCore.Mvc.TagHelpers; -using ServerCore; using ServerBase; +using ServerCore; namespace ServerBase; diff --git a/ServerBase/Helper/DynamoDbClientHelper.cs b/ServerBase/Helper/DynamoDbClientHelper.cs index 2dfd063..554f21b 100644 --- a/ServerBase/Helper/DynamoDbClientHelper.cs +++ b/ServerBase/Helper/DynamoDbClientHelper.cs @@ -1409,6 +1409,24 @@ public static class DynamoDbClientHelper return path_parts.Distinct().ToDictionary(part => "#" + part, part => part); } + public static Dictionary toExpressionAttributeNamesFromJson(string jsonString, Dictionary targetLeafKeys) + { + var expression_attribute_names = new Dictionary(); + + foreach (var target_leaf_key in targetLeafKeys.Values) + { + (var is_success, var fillup_key_paths) = maekKeyPathsFromJson(jsonString, target_leaf_key); + var path_parts = fillup_key_paths.Split('.'); + + foreach (var part in path_parts) + { + expression_attribute_names.TryAdd("#" + part, part); + } + } + + return expression_attribute_names; + } + //========================================================================================= // JsonString => AttributeExpression 정보로 변환해 준다. //========================================================================================= @@ -1429,6 +1447,22 @@ public static class DynamoDbClientHelper return (false, string.Empty); } + public static (bool, Dictionary) toAttributeExpressionsFromJson(string jsonString, Dictionary targetLeafKeys) + { + var attribute_expressions = new Dictionary(); + + foreach (var (property_name, target_leaf_key) in targetLeafKeys) + { + var (is_success, attribute_expression) = toAttributeExpressionFromJson(jsonString, target_leaf_key); + if (!is_success) + return (false, attribute_expressions); + + attribute_expressions.TryAdd(property_name, attribute_expression); + } + + return (true, attribute_expressions); + } + //========================================================================================= // JsonString => AttributePath 정보로 변환해 준다. (AttributePath : Path.Path.Path ...) //========================================================================================= diff --git a/ServerBase/Helper/DynamoDbDocBaseHelper.cs b/ServerBase/Helper/DynamoDbDocBaseHelper.cs index ea0a0f9..c37dd49 100644 --- a/ServerBase/Helper/DynamoDbDocBaseHelper.cs +++ b/ServerBase/Helper/DynamoDbDocBaseHelper.cs @@ -1163,7 +1163,16 @@ public static class DynamoDBDocBaseHelper return (result, null); } NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!!"); - var query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(make_primary_key.PK, make_primary_key.SK); + + QueryOperationConfig? query_config; + if (true == make_primary_key.SK.isEmptySK()) + { + query_config = dynamoDbClient.makeQueryConfigForReadByPKOnly(make_primary_key.PK); + } + else + { + query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(make_primary_key.PK, make_primary_key.SK); + } (result, var found_base_doc) = await dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig(query_config, eventTid:eventTid); if (result.isFail()) @@ -1195,7 +1204,16 @@ public static class DynamoDBDocBaseHelper return (result, null); } NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!!"); - var query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(make_primary_key.PK, make_primary_key.SK); + + QueryOperationConfig? query_config; + if (true == make_primary_key.SK.isEmptySK()) + { + query_config = dynamoDbClient.makeQueryConfigForReadByPKOnly(make_primary_key.PK); + } + else + { + query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(make_primary_key.PK, make_primary_key.SK); + } (result, var found_base_doc_list) = await dynamoDbClient.simpleQueryDocTypesWithQueryOperationConfig(query_config, eventTid:eventTid); if (result.isFail()) diff --git a/ServerBase/Helper/HttpClientHelper.cs b/ServerBase/Helper/HttpClientHelper.cs index 4e318f1..6bae660 100644 --- a/ServerBase/Helper/HttpClientHelper.cs +++ b/ServerBase/Helper/HttpClientHelper.cs @@ -18,11 +18,11 @@ public static class HttpClientHelper , string mediaType , string userAgentName , string userAgentVersion - , short timeOutSec = 5 ) + , short timeOutSec = 15 ) { validateUserAgentArgs(mediaType, userAgentName, userAgentVersion); - Log.getLogger().debug($"Request Message To WebServer. url : {url}, httpMethod : {httpMethod}, jwt : {jwt}, bodyJson : {bodyJson}"); + Log.getLogger().debug($"Request Message to WebServer. url:{url}, httpMethod:{httpMethod}, jwt:{jwt}, bodyJson:{bodyJson}"); var requestMessage = new HttpRequestMessage(new HttpMethod(httpMethod), url); @@ -45,7 +45,7 @@ public static class HttpClientHelper var resMsg = await SharedHttpClient.It.sendAsync(requestMessage, cts.Token); if (resMsg == null) { - Log.getLogger().error($"resMsg is null, sendAsync() returned null - url:{url}"); + Log.getLogger().error($"resMsg is null, sendAsync() returned null : requestMsg:{requestMessage} - url:{url}"); return (false, string.Empty); } diff --git a/ServerBase/Helper/ProgramVersionHelper.cs b/ServerBase/Helper/ProgramVersionHelper.cs index 3f9d3e3..83e7e9b 100644 --- a/ServerBase/Helper/ProgramVersionHelper.cs +++ b/ServerBase/Helper/ProgramVersionHelper.cs @@ -8,11 +8,14 @@ using System.Threading.Tasks; using NLog.LayoutRenderers.Wrappers; -using ServerCore; using ServerBase; +using ServerCore; namespace ServerBase; + +// HANDOVER: ServerProgramVersion & ClientProgramVersion 을 관리 한다. + public static class ProgramVersionHelper { public static ServerErrorCode checkClientProgramVersion( ServerConfig config diff --git a/ServerBase/Helper/ResultHelper.cs b/ServerBase/Helper/ResultHelper.cs index c262416..93fb724 100644 --- a/ServerBase/Helper/ResultHelper.cs +++ b/ServerBase/Helper/ResultHelper.cs @@ -89,6 +89,20 @@ public static class ResultHelper result.set(errorCode, resultString); } + /// + /// setFail과 동일하지만, result를 리턴한다. + /// Fail()처리와 동시에 Result를 리턴할 수 있다. + /// + /// + /// + /// + /// + public static Result Fail(this Result result, ServerErrorCode errorCode, string resultString = "") + { + result.set(errorCode, resultString); + return result; + } + public static string getResultString(this Result result) { ArgumentNullReferenceCheckHelper.throwIfNull(result, () => "Result is null !!!"); @@ -136,4 +150,4 @@ public static class ResultHelper return $"ErrorInfo: errCode:{result.getErrorCode()}, errDesc:{result.getResultString()}"; } -} \ No newline at end of file +} diff --git a/ServerBase/Helper/ServerConfigHelper.cs b/ServerBase/Helper/ServerConfigHelper.cs index 907647f..c33d1b5 100644 --- a/ServerBase/Helper/ServerConfigHelper.cs +++ b/ServerBase/Helper/ServerConfigHelper.cs @@ -14,7 +14,7 @@ using Axion.Collections.Concurrent; using Amazon.DynamoDBv2.Model; -using ServerCore; using ServerBase; +using ServerCore; using DYNAMO_DB_TABLE_NAME = System.String; diff --git a/ServerBase/Helper/ServerHelper.cs b/ServerBase/Helper/ServerHelper.cs index 1651cc2..79cf821 100644 --- a/ServerBase/Helper/ServerHelper.cs +++ b/ServerBase/Helper/ServerHelper.cs @@ -6,7 +6,7 @@ using System.Text; using System.Threading.Tasks; -using ServerCore; using ServerBase; +using ServerCore; namespace ServerBase; @@ -25,6 +25,8 @@ public static class ServerHelper case ServerType.Chat: case ServerType.Auth: case ServerType.Manager: + case ServerType.Match: + case ServerType.BrokerApi: break; default: diff --git a/ServerBase/Helper/TransactionRunnerHelper.cs b/ServerBase/Helper/TransactionRunnerHelper.cs index 32ba1c6..e6f3877 100644 --- a/ServerBase/Helper/TransactionRunnerHelper.cs +++ b/ServerBase/Helper/TransactionRunnerHelper.cs @@ -7,7 +7,7 @@ using System.Text; using System.Threading.Tasks; -using ServerCore; using ServerBase; +using ServerCore; using TRANSACTION_NAME = System.String; @@ -16,6 +16,9 @@ using TRANSACTION_GUID = System.String; namespace ServerBase; + +// HANDOVER: Task를 TransactionRunner로 실행시키고 안전하게 소멸 시킨다. 중복 실행 방지해야 할 경우 TRANSACTION_GUID 설정하면 된다. + public class TransactionRunnerHelper { public static async Task runTransactionRunnerWithLogic( EntityBase owner diff --git a/ServerBase/Logic/ServerLogicBase.cs b/ServerBase/Logic/ServerLogicBase.cs index a43bed0..5643012 100644 --- a/ServerBase/Logic/ServerLogicBase.cs +++ b/ServerBase/Logic/ServerLogicBase.cs @@ -16,6 +16,7 @@ using StackExchange.Redis; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.DocumentModel; using Amazon.DynamoDBv2.Model; +using ControlCenter.NamedPipe.Model; using Microsoft.Extensions.Options; using MongoDB.Driver; using Google.Protobuf; @@ -28,11 +29,16 @@ using ServerControlCenter; using MODULE_ID = System.UInt32; +using Microsoft.AspNetCore.Http.HttpResults; +using static ServerControlCenter.SCC2T_ACK_LOAD_SERVER_LOG.Types; namespace ServerBase; + +// HANDOVER: 서버 애플리케이션을 위한 Base 클래스 + public abstract partial class ServerLogicBase : IServerLogic { private ServerProgramVersion m_server_program_version = new(); @@ -40,6 +46,8 @@ public abstract partial class ServerLogicBase : IServerLogic private string m_server_type = string.Empty; private string m_server_name = string.Empty; + private Mutex? m_logic_mutex = null; + private ServerConfig? m_server_config = null; private IConfigurationRoot? m_configuration_root = null; @@ -79,6 +87,24 @@ public abstract partial class ServerLogicBase : IServerLogic ServerLogicApp.setServerLogicApp(this); } + private Result createLogicMutex() + { + var result = new Result(); + + var mutex_name = getServerName(); + var mutex = new Mutex(true, mutex_name, out bool created_new); + if (false == created_new) + { + var err_msg = $"Failed to new Mutex(), Already running Server !!! : mutexName:{mutex_name} - {toBasicString()}"; + result.setFail(ServerErrorCode.ServerAlreadyRunning, err_msg); + Log.getLogger().error(result.getResultString()); + return result; + } + + m_logic_mutex = mutex; + return result; + } + public async Task tryCreateAndRegisterModule(Func<(IModule?, Result)> toCreateAction, bool initializeImmediately = false) { var err_msg = string.Empty; @@ -242,16 +268,6 @@ public abstract partial class ServerLogicBase : IServerLogic Log.getLogger().info($"Log.initLog() Start !!! : {toBasicString()}"); - var with_configuration = this as IWithConfiguration; - if (null != with_configuration) - { - result = with_configuration.mergeConfiguration(getConfiguration()); - if (result.isFail()) - { - return result; - } - } - var config = getServerConfig(); result = await config.tryLoadConfig(); if (result.isFail()) @@ -261,10 +277,10 @@ public abstract partial class ServerLogicBase : IServerLogic DotNetDumpHelper.init(config.DumpDir, onGetDumpFilename()); - result = onCreateServerName(); + result = createLogicMutex(); if (result.isFail()) { - err_msg = $"Failed to onCreateServerName() !!! : {result.toBasicString()}"; + err_msg = $"Failed to createLogicMutex() !!! : {result.toBasicString()}"; Log.getLogger().fatal(err_msg); return result; } @@ -670,15 +686,35 @@ public abstract partial class ServerLogicBase : IServerLogic //============================================================================================= // 서버 로직을 시작 한다. !!! //============================================================================================= - public virtual async Task onRunServer() + public virtual async Task onRunServer(NamedPipeClientOptionBuilder namedPipeBuilder) { var result = new Result(); var err_msg = string.Empty; setServerStopEvent(); - Log.initLog(getServerName(), "Developer", onNLogConfigurationChanged); - + var with_configuration = this as IWithConfiguration; + if (null != with_configuration) + { + result = with_configuration.mergeConfiguration(getConfiguration()); + if (result.isFail()) + { + return result; + } + } + + result = onCreateServerName(); + if (result.isFail()) + { + err_msg = $"Failed to onCreateServerName() !!! : {result.toBasicString()}"; + Log.getLogger().fatal(err_msg); + return result; + } + + var server_config = getServerConfig(); + Log.initLog(getServerType(), "Developer", onNLogConfigurationChanged); + Log.setIPnPort(server_config.toClientListenIP(), server_config.toClientListenPort()); + onInitBusinessLog(); result = await onInit(); @@ -690,6 +726,17 @@ public abstract partial class ServerLogicBase : IServerLogic setThreadPool(); + namedPipeBuilder.setEnablePipe(m_server_config?.ControlAgentEnable ?? false); + namedPipeBuilder.setMaxClientConnection(m_server_config?.DefaultMaxUser ?? 0); + var named_option = namedPipeBuilder.build(); + if (null == named_option) + { + result.setFail(ServerErrorCode.ServerOnRunningFailed, "namedPipeBuilder.build() failed"); + Log.getLogger().error(result.toBasicString()); + return result; + } + await NamedPipeMonitor.StartNamedPipeService(named_option).ConfigureAwait(false); + result = await startModuleAll(); if (result.isFail()) { @@ -720,9 +767,13 @@ public abstract partial class ServerLogicBase : IServerLogic return result; } + await NamedPipeMonitor.ChangeServerStatus(ServerStatus.Running); + var running_task = onRunning(); if (running_task == null) { + await NamedPipeMonitor.SetStopReason(StopReason.CannotRunning); + err_msg = $"Failed to ServiceLogicBase.onRunning() !!! - {toBasicString()}"; result.setFail(ServerErrorCode.ServerOnRunningFailed, err_msg); Log.getLogger().error(result.toBasicString()); diff --git a/ServerBase/Manager/ServerMetricsManager.cs b/ServerBase/Manager/ServerMetricsManager.cs index 81140db..ebf1178 100644 --- a/ServerBase/Manager/ServerMetricsManager.cs +++ b/ServerBase/Manager/ServerMetricsManager.cs @@ -14,6 +14,8 @@ using ServerCore; namespace ServerBase; +// HANDOVER: 서버 지표 정보를 관리 클래스 이다. + public class ServerMetricsManager : Singleton { public static readonly int KEEP_SERVER_UPDATE_INTERVAL_MSEC = (10 * 1000); diff --git a/ServerBase/MessageQueue/RabbitMqConnector.cs b/ServerBase/MessageQueue/RabbitMqConnector.cs index 8b18e28..9ada9a2 100644 --- a/ServerBase/MessageQueue/RabbitMqConnector.cs +++ b/ServerBase/MessageQueue/RabbitMqConnector.cs @@ -25,6 +25,8 @@ using ServerCore; namespace ServerBase; +// HANDOVER: RabbitMQConnectorBase 실체적 구현 클래스 이며, ModuleContext를 통해 각종 설정을 시도 한다. + public abstract partial class RabbitMqConnector : RabbitMQConnectorBase, IRabbitMqSession, IModule, IWithPacketNamespaceVerifier { public delegate Task FnServerMessageRecvFromConsumer(BasicDeliverEventArgs ea, IMessage message); @@ -125,7 +127,7 @@ public abstract partial class RabbitMqConnector : RabbitMQConnectorBase, IRabbit { return false; } - + var result = await m_packet_receiver.registerRecvHandlerAll(); if (false == result) { @@ -141,7 +143,14 @@ public abstract partial class RabbitMqConnector : RabbitMQConnectorBase, IRabbit public async Task onRecvProtocol(BasicDeliverEventArgs ea, T recvProtocol) where T : Google.Protobuf.IMessage { - Log.getLogger().info($"receive:{recvProtocol.ToString()} - {toBasicString()}"); + if (recvProtocol is ServerMessage serverMessage) + { + NullReferenceCheckHelper.throwIfNull(serverMessage, () => $"serverMessage is null !!!"); + Log.getLogger().info($"[MQ receive] [{serverMessage.MsgCase.ToString()}] msg:{serverMessage.ToString()} - {toBasicString()}"); + } + else { + Log.getLogger().info($"[MQ receive] msg:{recvProtocol.ToString()} - {toBasicString()}"); + } Stopwatch? stopwatch = null; var event_tid = string.Empty; @@ -187,6 +196,9 @@ public abstract partial class RabbitMqConnector : RabbitMQConnectorBase, IRabbit Log.getLogger().error($"Failed to check Valid !!! : {fillupPacketCommand.toBasicString()}"); } + // mq에서 수신한 메시지가 순차적으로 처리됨 + // 메시지 하나의 처리가 길어질 경우, 처리에 병목이 발생할 수 있음 + // todo 디스패치될 객체를 분리해서 await 처리를 하도록 수정해야할 수도 ... result = await found_handler.onProcessPacket(this, recvProtocol); if (result.isFail()) { @@ -220,13 +232,13 @@ public abstract partial class RabbitMqConnector : RabbitMQConnectorBase, IRabbit Log.getLogger().error($"consumer parsing error bodyStr : {bodyStr}, stackTrace : {e.StackTrace}"); return; } - + if(message == null) { Log.getLogger().error($"message is null, bodyStr : {bodyStr}"); return; } - + if(m_fn_server_message_recv_from_consumer == null) { Log.getLogger().error("_fnServerMessageRecvFromConsumer is null"); @@ -279,9 +291,9 @@ public abstract partial class RabbitMqConnector : RabbitMQConnectorBase, IRabbit if(null == con) { Log.getLogger().error("GetConnection return null"); - return; + return; } - + using (var channel = con.CreateModel()) { Stopwatch? stopwatch = null; @@ -313,7 +325,7 @@ public abstract partial class RabbitMqConnector : RabbitMQConnectorBase, IRabbit basicProperties: null, body: body ); - Log.getLogger().info($"send to MQS !!!, msg:{messageJson} - receiver:{to}"); + Log.getLogger().info($"[MQ Send to {to}] [{message.MsgCase.ToString()}] msg:{messageJson} - receiver:{to}"); if (null != stopwatch) { diff --git a/ServerBase/Meta/MetaAssets/ContentLoader.cs b/ServerBase/Meta/MetaAssets/ContentLoader.cs deleted file mode 100644 index 5fa7c71..0000000 --- a/ServerBase/Meta/MetaAssets/ContentLoader.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - - -namespace ServerBase; - -public static class ContentLoader -{ - public static T? loadFile(string dataDir, string fileName) where T : ContentTableBase - { - try - { - string exactPath = Path.GetFullPath(dataDir); - - string data = File.ReadAllText(Path.Combine(dataDir, fileName)); - return Newtonsoft.Json.JsonConvert.DeserializeObject(data); - } - catch (Exception ex) - { - throw new Exception($"content load fail. dataDir: {dataDir}, fileName: {fileName}", ex); - } - } - - public static T? loadMultipleFiles(string dataDir, string filePattern) where T : ContentTableBase - { - var files = Directory.GetFiles(dataDir, filePattern, SearchOption.TopDirectoryOnly); - - List tables = new List(); - foreach (string file in files) - { - string data = File.ReadAllText(file); - T? json = Newtonsoft.Json.JsonConvert.DeserializeObject(data); - - if (json != null) - tables.Add(json); - } - - T? oneTable = null; - foreach (var table in tables) - { - if (oneTable == null) - oneTable = table; - else - oneTable.merge(table); - } - - return oneTable; - } -} diff --git a/ServerBase/Meta/MetaAssets/ContentTableBase.cs b/ServerBase/Meta/MetaAssets/ContentTableBase.cs deleted file mode 100644 index 27527d6..0000000 --- a/ServerBase/Meta/MetaAssets/ContentTableBase.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - - -namespace ServerBase; - -public class ContentTableBase -{ - public virtual void merge(T table) { } -} diff --git a/ServerBase/Meta/MetaValidation/MetaValidator.cs b/ServerBase/Meta/MetaValidation/MetaValidator.cs index c608bce..909068a 100644 --- a/ServerBase/Meta/MetaValidation/MetaValidator.cs +++ b/ServerBase/Meta/MetaValidation/MetaValidator.cs @@ -1,20 +1,32 @@ using System.Reflection; -using ServerCore; using ServerBase; +using ServerCore; namespace ServerBase; + +// HANDOVER: Ÿ Ἲ θ üũ ִ Ŭ ̴. + + public class MetaValidator { public void validate(IReadOnlyList list, ValidatorErrorCollection errors) { - var thisAssembly = Assembly.GetExecutingAssembly(); - var methods = thisAssembly.GetTypes() - .SelectMany(x => x.GetMethods(BindingFlags.Public | BindingFlags.Static)) - .Where(x => x.GetCustomAttributes().Any()) - .Where(x => x.GetParameters()?[0]?.ParameterType == typeof(T)) + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + var methods = assemblies + .SelectMany(asm => asm.GetTypes()) + .SelectMany(type => type.GetMethods(BindingFlags.Public | BindingFlags.Static)) + .Where(method => method.GetCustomAttributes().Any()) + .Where(method => method.GetParameters()?.Length >= 2) + .Where(method => + { + var parameters = method.GetParameters(); + return parameters[0].ParameterType == typeof(T) && + parameters[1].ParameterType == typeof(ValidatorErrorCollection); + }) .ToList(); if (methods.Count == 0) diff --git a/ServerBase/Monitor/Monitor.cs b/ServerBase/Monitor/Monitor.cs index eb0f685..89e36f9 100644 --- a/ServerBase/Monitor/Monitor.cs +++ b/ServerBase/Monitor/Monitor.cs @@ -6,6 +6,10 @@ using ServerCore; namespace ServerBase; + +// HANDOVER: 서버 모니터링을 위한 정보 관리 클래스 이다. + + public class Monitor : Singleton { // 접속중인 유저수 카운터 diff --git a/ServerBase/Monitor/NamedPipe/NamedPipeMonitor.cs b/ServerBase/Monitor/NamedPipe/NamedPipeMonitor.cs index 4940be4..eeab383 100644 --- a/ServerBase/Monitor/NamedPipe/NamedPipeMonitor.cs +++ b/ServerBase/Monitor/NamedPipe/NamedPipeMonitor.cs @@ -3,6 +3,7 @@ using System.Reflection; using ControlCenter.NamedPipe; +using ControlCenter.NamedPipe.Model; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -15,6 +16,10 @@ using ServerControlCenter; namespace ServerBase; + +// HANDOVER: 서버 모니터링을 위한 NamedPipe 연동 클래스 이다. + + public static class NamedPipeMonitor { public static async Task ChangeServerStatus(ServerStatus status) => @@ -37,19 +42,19 @@ public static class NamedPipeMonitor public static NamedPipeResultCode setCommonDetail(string key, Value value) => NamedPipeClientHelper.Instance.SetDetail(key, value); - + public static NamedPipeResultCode setCommonDetails(Dictionary list) => NamedPipeClientHelper.Instance.SetDetails(list); - - public static NamedPipeResultCode setContentDetail(string key, Value value) => - NamedPipeClientHelper.Instance.SetContentDetail(key, value); - - public static NamedPipeResultCode setContentDetails(Dictionary list) => + + public static NamedPipeResultCode setContentDetail(string key, Value value) => + NamedPipeClientHelper.Instance.SetContentDetail(key, value); + + public static NamedPipeResultCode setContentDetails(Dictionary list) => NamedPipeClientHelper.Instance.SetContentDetails(list); public static NamedPipeResultCode SetCurrentCapacity(int capacity) => NamedPipeClientHelper.Instance.SetCurrentCapacity(capacity); - + public static async Task SendMessageAsync(T message, string message_id) where T : IMessage => await NamedPipeClientHelper.Instance.SendMessageAsync(message, message_id); @@ -58,6 +63,13 @@ public static class NamedPipeMonitor Log.getLogger().debug($"NamedPipeClientHelper : StopNamedPipeService Call !!!"); await NamedPipeClientHelper.Instance.StopPipeClient(); } + + public static async Task StartNamedPipeService(NamedPipeClientOption options) + { + Log.getLogger().debug($"Start NamedPipe Client - Name[Agent_{options.m_ip}.{options.m_port}]"); + await NamedPipeClientHelper.Instance.StartNamedPipeClientAsync(options); + } + public static async Task StartNamedPipeService(bool enable_named_pipe, ServerType server_type, int port, ServiceCategory serviceCategory, int max_connection_count, int? channel_no = null, int? world_id = null) { var ip = NetworkHelper.getEthernetLocalIPv4(); @@ -72,12 +84,13 @@ public static class NamedPipeMonitor ServerType.UgqAdmin => ServerControlCenter.ServerType.UgqAdmin, ServerType.UgqIngame => ServerControlCenter.ServerType.UgqIngame, ServerType.BrokerApi => ServerControlCenter.ServerType.BrokerApi, + ServerType.Match => ServerControlCenter.ServerType.Match, _ => ServerControlCenter.ServerType.None }; if (type == ServerControlCenter.ServerType.None) return; - - var named_pipe_options = new NamedPipeClientHelper.NamedPipeClientOption + + var named_pipe_options = new NamedPipeClientOption { m_enable_pipe = enable_named_pipe, m_service_category = serviceCategory.ToString(), @@ -85,15 +98,15 @@ public static class NamedPipeMonitor m_channel_no = channel_no, m_world_id = world_id, m_max_client_connection = max_connection_count, - m_assemblies = GetAssemblies().ToList(), - m_max_retry_count = 5, + m_assemblies = GetAssemblies().ToList(), + m_max_retry_count = 5, m_retry_delay_time_ms = 5_000 }; - + Log.getLogger().debug($"Start NamedPipe Client - Name[Agent_{named_pipe_options.m_ip}.{named_pipe_options.m_port}]"); await NamedPipeClientHelper.Instance.StartNamedPipeClientAsync(named_pipe_options); } - private static IEnumerable GetAssemblies() + private static IEnumerable GetAssemblies() => new List(AppDomain.CurrentDomain.GetAssemblies()); -} \ No newline at end of file +} diff --git a/ServerBase/Network/Handler/LargePacketHandler.cs b/ServerBase/Network/Handler/LargePacketHandler.cs deleted file mode 100644 index 42ad021..0000000 --- a/ServerBase/Network/Handler/LargePacketHandler.cs +++ /dev/null @@ -1,251 +0,0 @@ -using System.Collections.Concurrent; - - -using Google.Protobuf; -using NeoSmart.AsyncLock; -using Nettention.Proud; - - -using ServerCore; using ServerBase; - - -namespace ServerBase; - -public class LargePacketHandler -{ - //private ConcurrentDictionary<(Player, string), SortedSet> m_large_packets = new(); - private ConcurrentDictionary> m_large_packets = new(); // 하나의 패킷만 처리 - private static AsyncLock _lock = new(); - - private SessionBase? m_network_session { get; set; } - - public void onInit(SessionBase networkSession) - { - m_network_session = networkSession; - var func = checkLargePacket; - _ = PeriodicTaskHelper.runTask(func, TimeSpan.FromMilliseconds(Constant.LARGE_PACKET_CHECK_INTERVAL_MSEC)); - } - - public Result collectPacket(IEntityWithSession ssesion, string packetUid, Int32 totalPacketCount, Int32 packetIdx, ByteString datas) - { - Log.getLogger().debug($"LargePacketCollect packetUid : {packetUid}, totalPacketCount : {totalPacketCount}, packetIdx : {packetIdx}, datasize : {datas.Length}"); - - var collected_packet = new CollectedPacketInfo(packetUid, totalPacketCount, packetIdx, datas); - //var user_guid = player.getUserGuid(); - - var result = addCollectedPacket(ssesion, collected_packet); - return result; - } - - - private Result addCollectedPacket(IEntityWithSession ssesion, CollectedPacketInfo packet) - { - using (_lock.Lock()) - { - if (false == m_large_packets.TryGetValue(ssesion, out var collected_packets)) - { - collected_packets = new SortedSet(); - collected_packets.Add(packet); - m_large_packets.TryAdd(ssesion, collected_packets); - } - else - { - collected_packets.Add(packet); - } - - } - Log.getLogger().debug($"addCollectedPacket done"); - return new Result(); - } - - private Result removeCollectedPacket(List sessionIds) - { - - if (sessionIds.Count == 0) return new(); - - var result = new Result(); - using (_lock.Lock()) - { - foreach (var session_id in sessionIds) - { - m_large_packets.TryRemove(session_id, out var _); - } - } - return result; - } - - public Task checkLargePacket() - { - var result = new Result(); - List delete_users = new(); - foreach (var packets in m_large_packets) - { - var session = packets.Key; - var packet_set = packets.Value; - - var isRecvResult = validCheck(session, packet_set); - if (isRecvResult.isSuccess()) - { - if (false == processPacket(session, packet_set)) - { - Log.getLogger().error("processPacket error!!"); - delete_users.Add(session); - break; - } - - delete_users.Add(session); - continue; - } - - if (isRecvResult.getErrorCode() == ServerErrorCode.LargePacketNotAllReceived) continue; - if (isRecvResult.getErrorCode() != ServerErrorCode.LargePacketRecvTimeOver) - { - send_GS2C_NTF_LARGE_PACKET_TIMEOUT(session, isRecvResult, packet_set); - delete_users.Add(session); - continue; - } - } - - if (delete_users.Count > 0) - { - removeCollectedPacket(delete_users); - } - - return Task.FromResult(result); - } - - private void send_GS2C_NTF_LARGE_PACKET_TIMEOUT(IEntityWithSession session, Result result, SortedSet packetSet) - { - var last_recv_time = packetSet.OrderByDescending(p => p.m_last_recv_time).FirstOrDefault(); - NullReferenceCheckHelper.throwIfNull(last_recv_time, () => $"Last Receive time is null !! - player:{session.toBasicString()}"); - - // var uid = packetSet.Select(p => p.m_packet_uid).FirstOrDefault() ?? string.Empty; - - ClientToGame msg = new(); - msg.Message = new(); - msg.Message.NtfLargePacketTimeout = new(); - msg.Message.NtfLargePacketTimeout.Uid = last_recv_time.m_packet_uid; - msg.Message.NtfLargePacketTimeout.LastCheckTime = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(last_recv_time.m_last_recv_time); - msg.Message.NtfLargePacketTimeout.LastCheckTime = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.UtcNow); - } - - private bool processPacket(IEntityWithSession session, SortedSet packets) - { - var network_session = getNetworkSession(); - - using (var stream = new MemoryStream()) - { - foreach (var packet in packets) - { - if (packet.m_datas == null) - { - Log.getLogger().warn("Warning: packet.m_datas is null"); - continue; - } - packet.m_datas.WriteTo(stream); - } - - if (stream.Length == 0) - { - Log.getLogger().warn("Warning: stream is empty after writing packets."); - return false; - } - - try - { - stream.Position = 0; - var req = ClientToGame.Parser.ParseFrom(stream); - - var func = network_session.onCallProtocolHandler(session, req); - return true; - - } - catch (Exception e) - { - Log.getLogger().error($"Exception !!!, Failed to perform !!!, in LargePacketHandler.processPacket() : exception:{e} - {session.toBasicString()}"); - return false; - } - } - } - - private Result validCheck(IEntityWithSession session, SortedSet packets) - { - var err_msg = string.Empty; - var result = new Result(); - - var sorted_packet_idxs = packets.OrderBy(p => p.m_packet_idx).ToList(); - var total_packet_count = packets.Select(p => p.m_total_packet_count).FirstOrDefault(); - - //아직 패킷이 다 안들어온거다. - if (sorted_packet_idxs.Count < total_packet_count) - { - err_msg = $"Not all packets were received !!! : recvPacketCount:{sorted_packet_idxs.Count} < totalPacketCount:{total_packet_count} - {session.toBasicString()}"; - result.setFail(ServerErrorCode.LargePacketNotAllReceived, err_msg); - return result; - } - - var last_recv_time = packets.OrderByDescending(p => p.m_last_recv_time).FirstOrDefault(); - NullReferenceCheckHelper.throwIfNull(last_recv_time, () => $"last receive time is null !!! - {session.toBasicString()}"); - - var diff = DateTimeHelper.Current - last_recv_time.m_last_recv_time; - if (diff.TotalSeconds > Constant.LARGE_PACKET_CHECK_WAIT_SEC) - { - err_msg = $"Packet receive timeout exceeded !!! : waitingSec:{diff.TotalSeconds} > WaitMaxSec:{Constant.LARGE_PACKET_CHECK_WAIT_SEC}, lastRecvTime:{last_recv_time} - {session.toBasicString()}"; - result.setFail(ServerErrorCode.LargePacketRecvTimeOver, err_msg); - Log.getLogger().error(result.toBasicString()); - return result; - } - - return result; - } - - public SessionBase getNetworkSession() - { - NullReferenceCheckHelper.throwIfNull(m_network_session, () => $"m_network_session is null !!!"); - return m_network_session; - } -} - - -public class CollectedPacketInfo : IComparable -{ - public string m_packet_uid { get; } = string.Empty; - public Int32 m_total_packet_count { get; } = 0; - public Int32 m_packet_idx { get; } = 0; - public ByteString? m_datas { get; } = null; - public DateTime m_last_recv_time { get; } = DateTime.Now; - public CollectedPacketInfo(string packetUid, Int32 totalPacketCount, Int32 packetIdx, ByteString datas) - { - m_packet_uid = packetUid; - m_total_packet_count = totalPacketCount; - m_packet_idx = packetIdx; - m_datas = datas; - } - - public Int32 CompareTo(CollectedPacketInfo? other) - { - if (other == null) return 1; - return this.m_packet_idx.CompareTo(other.m_packet_idx); - } - - public override bool Equals(object? obj) - { - if (obj == null || GetType() != obj.GetType()) - { - return false; - } - - CollectedPacketInfo other = (CollectedPacketInfo)obj; - //return m_packet_uid == other.m_packet_uid && - // m_total_packet_count == other.m_total_packet_count && - // m_packet_idx == other.m_packet_idx && - // m_last_recv_time == other.m_last_recv_time; - return m_packet_idx == other.m_packet_idx; - } - - public override int GetHashCode() - { - return HashCode.Combine(m_packet_uid, m_total_packet_count, m_packet_idx, m_last_recv_time); - } -} - diff --git a/ServerBase/Network/Packet/PacketHandler.cs b/ServerBase/Network/Packet/PacketHandler.cs index 7c7f556..1885c8c 100644 --- a/ServerBase/Network/Packet/PacketHandler.cs +++ b/ServerBase/Network/Packet/PacketHandler.cs @@ -14,11 +14,14 @@ using ServerCore; namespace ServerBase; +// HANDOVER: 패킷 수신/송신 핸들러 클래스 이다. + + //============================================================================================= // 패킷 응답/통지 수신 처리자 연결을 위한 AttributeUsage 등록 // // author : kangms -// +// //============================================================================================= [AttributeUsage(AttributeTargets.Class)] @@ -76,7 +79,7 @@ public class PacketHandlerAttribute : Attribute // 패킷 요청 Handler // // author : kangms -// +// //=========================================================================================== public abstract class PacketSendHandler @@ -97,7 +100,7 @@ public abstract class PacketSendHandler // 패킷 수신 Handler // // author : kangms -// +// //=========================================================================================== public abstract class PacketRecvHandler @@ -127,7 +130,7 @@ public abstract class PacketRecvHandler // 패킷 응답 Handler // // author : kangms -// +// //=========================================================================================== public abstract class PacketAckHandler : PacketRecvHandler @@ -141,7 +144,7 @@ public abstract class PacketAckHandler : PacketRecvHandler // 패킷 통지 Handler // // author : kangms -// +// //=========================================================================================== public abstract class PacketNtfHandler : PacketRecvHandler @@ -157,14 +160,14 @@ public abstract class PacketNtfHandler : PacketRecvHandler // p2p 패킷 수신 Handler // // author : khlee -// +// //=========================================================================================== public abstract class P2PPacketRecvHandler { public P2PPacketRecvHandler() { - + } public virtual async Task onCheckValid(ISession session, Google.Protobuf.IMessage recvMessage) { diff --git a/ServerBase/Network/Packet/PacketReceiver.cs b/ServerBase/Network/Packet/PacketReceiver.cs index 44ed10d..7d81431 100644 --- a/ServerBase/Network/Packet/PacketReceiver.cs +++ b/ServerBase/Network/Packet/PacketReceiver.cs @@ -22,6 +22,9 @@ using ServerCore; using ServerBase; namespace ServerBase; +// HANDOVER: 패킷 수신 처리 클래스 이다. + + //============================================================================================= // 패킷 수신 관리자 // diff --git a/ServerBase/Network/Packet/PacketSender.cs b/ServerBase/Network/Packet/PacketSender.cs index 0925f9b..69dd95d 100644 --- a/ServerBase/Network/Packet/PacketSender.cs +++ b/ServerBase/Network/Packet/PacketSender.cs @@ -15,6 +15,9 @@ using ServerCore; using ServerBase; namespace ServerBase; +// HANDOVER: 패킷 송신 처리 클래스 이다. + + //============================================================================================= // 패킷 송신 관리자 // diff --git a/ServerBase/ProudNet/Session/SessionBase.cs b/ServerBase/ProudNet/Session/SessionBase.cs index 836be7c..7100854 100644 --- a/ServerBase/ProudNet/Session/SessionBase.cs +++ b/ServerBase/ProudNet/Session/SessionBase.cs @@ -23,6 +23,9 @@ using SESSION_ID = System.Int32; namespace ServerBase; +// HANDOVER: ProudNet 기반 네트워크 Base 클래스 이다. + + //============================================================================================= // 네트워크 세션 기반 클래스 이다. // @@ -51,8 +54,6 @@ public abstract partial class SessionBase : ISession, IInitializer private int m_large_packet_check_size = Constant.LARGE_PACKET_CHECK_DEFAULT_SIZE; private int m_packet_chunk_size = Constant.PACKET_CHUNK_DEFAULT_SIZE; - private LargePacketHandler m_large_packet_handler = new(); - public enum ConnectionState { None = 0, @@ -252,15 +253,9 @@ public abstract partial class SessionBase : ISession, IInitializer ArgumentNullReferenceCheckHelper.throwIfNull(session, () => $"Session is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(recvProtocol, () => $"recvProtocol is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(packetCommand, () => $"packetCommand is null !!!"); - - System.Type command_type = packetCommand.getCommandType(); - switch (command_type) - { - case var _ when command_type == typeof(ClientToGameReq.Types.C2S_REQ_LARGE_PACKET): - return async () => await onCallLargeSizeProtocolHandler(session, recvProtocol); - default: - return async () => await onCallProtocolHandler(session, recvProtocol); - } + + return async () => await onCallProtocolHandler(session, recvProtocol); + } public async Task onCallProtocolHandler(IEntityWithSession session, T recvProtocol) @@ -359,46 +354,6 @@ public abstract partial class SessionBase : ISession, IInitializer return result; } - public async Task onCallLargeSizeProtocolHandler(IEntityWithSession session, T recvProtocol) - where T : Google.Protobuf.IMessage - { - await Task.CompletedTask; - - var result = new Result(); - var err_msg = string.Empty; - - try - { - var network_session = m_large_packet_handler.getNetworkSession(); - NullReferenceCheckHelper.throwIfNull(network_session, () => $"network_session is null !!! - {session.toBasicString()}"); - - ArgumentNullReferenceCheckHelper.throwIfNull(session, fnLog: () => $"session is null !!!"); - ArgumentNullReferenceCheckHelper.throwIfNull(recvProtocol, fnLog: () => $"recvProtocol is null !!! - {session.toBasicString()}"); - - var recv_msg = recvProtocol as ClientToGame; - NullReferenceCheckHelper.throwIfNull(recv_msg, fnLog: () => $"recv_msg is null !!! - {session.toBasicString()}"); - - var request = recv_msg.Request.ReqLargePacket; - NullReferenceCheckHelper.throwIfNull(request, fnLog: () => $"request is null !!! - {session.toBasicString()}"); - - string packet_uid = request.Uid; - Int32 packet_index = request.PacketIndex; - Int32 total_packet_count = request.TotalPacketCount; - - m_large_packet_handler.collectPacket(session, packet_uid, total_packet_count, packet_index, request.Data); - } - catch(Exception e) - { - var error_coce = ServerErrorCode.TryCatchException; - err_msg = $"Exception !!!, Failed to perform in onCallLargeSizeProtocolHandler() !!! : errorCode:{error_coce}, exception:{e} - PacketCommand:{recvProtocol?.toBasicString()}, {session?.toBasicString()}"; - result.setFail(error_coce, err_msg); - Log.getLogger().error(result.toBasicString()); - } - - return result; - - } - #region 패킷 송신용 함수들 public bool onSendPacket(IEntityWithSession session, T msg) where T : class, IMessage @@ -537,8 +492,12 @@ public abstract partial class SessionBase : ISession, IInitializer cToG.Response.AckLargePacket.Uid = otp_encoding_by_base32; cToG.Response.AckLargePacket.Data = chunkByteString; cToG.Response.AckLargePacket.TotalPacketCount = numberOfChunks; + + if (i == 0) cToG.Response.AckLargePacket.ProcessType = LargePacketProcess.Start; + else if (i == numberOfChunks - 1) cToG.Response.AckLargePacket.ProcessType = LargePacketProcess.End; + else cToG.Response.AckLargePacket.ProcessType = LargePacketProcess.Processing; + var chunk_msg = cToG as IMessage; - msgs.Add((T)chunk_msg); } } @@ -595,6 +554,8 @@ public abstract partial class SessionBase : ISession, IInitializer #endregion 로그용 문자열 함수들 } +// HANDOVER: SessionBase 서버용 실체적 구현 클래스 이다. + //============================================================================================= // 리슨 역할을 하는 세션 기반 클래스 이다. (서버 역할용) // @@ -607,8 +568,6 @@ public abstract partial class ListenSessionBase : SessionBase private NetworkAddress m_listen_address = new(); - private LargePacketHandler m_large_packet_handler = new(); - // SESSION_ID 는 Pround.HostID 와 같다 !!! private readonly ConcurrentDictionary m_entity_with_sessions = new(); @@ -787,6 +746,7 @@ public abstract partial class ListenSessionBase : SessionBase #endregion 재정의 해야 하는 함수들 } +// HANDOVER: SessionBase 클라이언트용 실체적 구현 클래스 이다. //=========================================================================================== // 서버로 연결할 세션 기반 클래스 이다. (클라이언트 역할용) diff --git a/ServerBase/Redis/RedisConnector.cs b/ServerBase/Redis/RedisConnector.cs index bbe1afd..6eb9e9f 100644 --- a/ServerBase/Redis/RedisConnector.cs +++ b/ServerBase/Redis/RedisConnector.cs @@ -15,6 +15,10 @@ using SORT_ORDER_NO = System.Int32; namespace ServerBase; + +// HANDOVER: RedisConnectorBase 실체적 구현 클래스 이며, ModuleContext를 통해 각종 설정을 시도 한다. + + public partial class RedisConnector : RedisConnectorBase, IModule, IInitializer { private readonly ModuleContext m_module_context; diff --git a/ServerBase/Redis/RedisRequestBase.cs b/ServerBase/Redis/RedisRequestBase.cs index 2cabe25..50682ef 100644 --- a/ServerBase/Redis/RedisRequestBase.cs +++ b/ServerBase/Redis/RedisRequestBase.cs @@ -13,6 +13,9 @@ using ServerCore; using ServerBase; namespace ServerBase; + +// HANDOVER: Redis 요청 기반 Base 클래스 이다. + public abstract partial class RedisRequestBase : IInitializer { private readonly RedisConnector m_redis_connector; diff --git a/ServerBase/Redis/RedisRequestPrivateBase.cs b/ServerBase/Redis/RedisRequestPrivateBase.cs index 23256a3..963bd60 100644 --- a/ServerBase/Redis/RedisRequestPrivateBase.cs +++ b/ServerBase/Redis/RedisRequestPrivateBase.cs @@ -11,6 +11,9 @@ using ServerCore; using ServerBase; namespace ServerBase; + +// HANDOVER: Redis 요청시 엔티티의 공유되지 않는 Base 클래스 이다. + public abstract partial class RedisRequestPrivateBase : RedisRequestBase { private readonly IActor m_owner; diff --git a/ServerBase/Redis/RedisRequestSharedBase.cs b/ServerBase/Redis/RedisRequestSharedBase.cs index eb015ea..46b3607 100644 --- a/ServerBase/Redis/RedisRequestSharedBase.cs +++ b/ServerBase/Redis/RedisRequestSharedBase.cs @@ -10,6 +10,8 @@ using ServerCore; using ServerBase; namespace ServerBase; +// HANDOVER: Redis 요청시 엔티티간의 공유용 Base 클래스 이다. + public abstract partial class RedisRequestSharedBase : RedisRequestBase { private readonly string m_shared_key; diff --git a/ServerBase/Redis/RedisRequestWithLambdaBase.cs b/ServerBase/Redis/RedisRequestWithLambdaBase.cs index 50f3b28..03a1bfd 100644 --- a/ServerBase/Redis/RedisRequestWithLambdaBase.cs +++ b/ServerBase/Redis/RedisRequestWithLambdaBase.cs @@ -9,7 +9,7 @@ using System.Collections.Concurrent; using StackExchange.Redis; -using ServerCore; using ServerBase; +using ServerCore; using REDIS_REQUEST_HANDLER_TYPE = System.UInt32; @@ -22,6 +22,8 @@ using REDIS_KEY = System.String; namespace ServerBase; +// HANDOVER: Redis 요청시 Lambda를 함께 실행시킬 수 있는 Base 클래스 이다. + public class RedisKeyFormat { public REDIS_KEY_FORMAT_TYPE KeyType { get; set; } diff --git a/ServerBase/Redis/RedisWithLuaScriptExecutor.cs b/ServerBase/Redis/RedisWithLuaScriptExecutor.cs index 2974f40..99c1df7 100644 --- a/ServerBase/Redis/RedisWithLuaScriptExecutor.cs +++ b/ServerBase/Redis/RedisWithLuaScriptExecutor.cs @@ -11,6 +11,8 @@ using ServerCore; namespace ServerBase; +// HANDOVER: Redis 요청시 Lua 스크립트를 함께 실행시킬 수 있는 Base 클래스 이다. + public partial class RedisWithLuaScriptExecutor : RedisWithLuaScriptExecutorBase, IModule { private readonly ModuleContext m_module_context; diff --git a/ServerBase/S3/S3Connector.cs b/ServerBase/S3/S3Connector.cs index 8310f41..931bbfc 100644 --- a/ServerBase/S3/S3Connector.cs +++ b/ServerBase/S3/S3Connector.cs @@ -15,6 +15,8 @@ using SORT_ORDER_NO = System.Int32; namespace ServerBase; +// HANDOVER: S3ConnectorBase 실체적 구현 클래스 이며, ModuleContext를 통해 각종 설정을 시도 한다. + public partial class S3Connector : S3ConnectorBase, IModule, IInitializer { private readonly ModuleContext m_module_context; diff --git a/ServerBase/ServerBase.csproj b/ServerBase/ServerBase.csproj index 2c2e5b9..db413e9 100644 --- a/ServerBase/ServerBase.csproj +++ b/ServerBase/ServerBase.csproj @@ -1,50 +1,43 @@ - - - - net8.0 - enable - enable - true - true - Debug;Release;Shipping - true - - true - $(MSBuildProjectName) - - - - full - $(DefineConstants); - True - - - - full - $(DefineConstants); - True - - - - full - $(DefineConstants); - true - True - - - - - - - - - - - ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetClient.dll - - - ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetServer.dll - - - - + + + net8.0 + enable + enable + true + true + Debug;Release;Shipping + true + + true + $(MSBuildProjectName) + + + full + $(DefineConstants); + True + + + full + $(DefineConstants); + True + + + full + $(DefineConstants); + true + True + + + + + + + + + ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetClient.dll + + + ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetServer.dll + + + \ No newline at end of file diff --git a/ServerBase/ServerMetrics/ServerMetricsHandler.cs b/ServerBase/ServerMetrics/ServerMetricsHandler.cs index 5c0c901..0ff6324 100644 --- a/ServerBase/ServerMetrics/ServerMetricsHandler.cs +++ b/ServerBase/ServerMetrics/ServerMetricsHandler.cs @@ -28,6 +28,9 @@ using System.Drawing; namespace ServerBase; +// HANDOVER: 서버 지표 정보 관련 로직을 처리하는 클래스 이다. + + /*================================================================================================= * Server Metrics Key @@ -36,11 +39,11 @@ namespace ServerBase; serverinfo:Keys:{ChannelKey}:{WorldMetaId} MEMBER serverinfo:{Channel}:{WorldId}:{ChannelNo} - + + Etc serverinfo:Keys:{ServerTypeKey} MEMBER - serverinfo:{ServerType}:{IP}_{Port} + serverinfo:{ServerType}:{IP}_{Port} =================================================================================================*/ @@ -74,6 +77,7 @@ public class ServerMetricsHandler : ServerMetricsCacheRequest if (ServerType.Login == serverType) { return "LoginKey"; } else if (ServerType.Channel == serverType) { return "ChannelKey"; } else if (ServerType.Indun == serverType) { return "IndunKey"; } + else if (ServerType.Match == serverType) { return "MatchKey"; } else { throw new TypeAccessException($"Invalid ServerType : {serverType}"); } } @@ -677,7 +681,7 @@ public class ServerMetricsHandler : ServerMetricsCacheRequest { for (var i = 0; i < world_meta_ids.Count; i++) { - var world_meta_id = (WORLD_META_ID)world_meta_ids[i]; + var world_meta_id = (WORLD_META_ID)world_meta_ids[i]; var redis_key = buildRedisKeyByServerInfo(server_type, world_meta_id); var to_replace_params = new Dictionary>(); diff --git a/ServerBase/Ticker/TimeEventForMinuteTicker.cs b/ServerBase/Ticker/TimeEventForMinuteTicker.cs index 3671e18..1944b20 100644 --- a/ServerBase/Ticker/TimeEventForMinuteTicker.cs +++ b/ServerBase/Ticker/TimeEventForMinuteTicker.cs @@ -4,6 +4,10 @@ using System.Runtime.CompilerServices; namespace ServerBase; + +// HANDOVER: 분단위 DailyTimeEventManager 호출 Ticker 클래스 이다. + + public class TimeEventForMinuteTicker : EntityTicker { public TimeEventForMinuteTicker(double onTickIntervalMilliseconds, CancellationTokenSource? cts) diff --git a/ServerBase/User/UserManagerBase.cs b/ServerBase/User/UserManagerBase.cs index dad7000..7a24f71 100644 --- a/ServerBase/User/UserManagerBase.cs +++ b/ServerBase/User/UserManagerBase.cs @@ -27,6 +27,9 @@ using ITEM_GUID = System.String; namespace ServerBase; + +// HANDOVER: MultiIndexDictionary 기반의 User 목록 관리 클래스 이다. + public abstract partial class UserManagerBase where TPrimaryKey : notnull where TSubKey : notnull @@ -125,7 +128,7 @@ public abstract partial class UserManagerBase { foundUser = null; - if (false == m_users.tryGetValue(primaryKey, out var found_user)) + if (false == m_users.tryGetPrimaryValue(primaryKey, out var found_user)) { return false; } @@ -138,7 +141,7 @@ public abstract partial class UserManagerBase { foundUser = null; - if (false == m_users.tryGetValue(subKey, out var found_user)) + if (false == m_users.tryGetSubValue(subKey, out var found_user)) { return false; } diff --git a/ServerCommon/1. Define/BusinessLog/Battle/BattleObjectInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Battle/BattleObjectInteractionLogInfo.cs index 9b53579..0a090d6 100644 --- a/ServerCommon/1. Define/BusinessLog/Battle/BattleObjectInteractionLogInfo.cs +++ b/ServerCommon/1. Define/BusinessLog/Battle/BattleObjectInteractionLogInfo.cs @@ -23,7 +23,7 @@ public class BattleObjectInteractionLogInfo : ILogInvoker.IInfo public string m_room_id { get; set; } = string.Empty; [JsonProperty("battle_object_type")] - public EBattleObjectType m_battle_object_type { get; set; } = EBattleObjectType.None; + public EGameModeObjectType m_battle_object_type { get; set; } = EGameModeObjectType.None; [JsonProperty("pod_type")] public ECombatPodType m_pod_type { get; set; } = ECombatPodType.None; diff --git a/ServerCommon/1. Define/BusinessLog/Battle/BattleObjectStateUpdateLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Battle/BattleObjectStateUpdateLogInfo.cs index c77bea2..d1d1f79 100644 --- a/ServerCommon/1. Define/BusinessLog/Battle/BattleObjectStateUpdateLogInfo.cs +++ b/ServerCommon/1. Define/BusinessLog/Battle/BattleObjectStateUpdateLogInfo.cs @@ -17,7 +17,7 @@ public class BattleObjectLogInfo public string m_anchor_guid { get; set; } = string.Empty; [JsonProperty("battle_object_type")] - public EBattleObjectType m_battle_object_type { get; set; } = EBattleObjectType.None; + public EGameModeObjectType m_battle_object_type { get; set; } = EGameModeObjectType.None; [JsonProperty("active_time")] public DateTime m_active_time { get; set; } = DateTimeHelper.Current; diff --git a/ServerCommon/1. Define/BusinessLog/Battle/BattlePodCombatRewardLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Battle/BattlePodCombatRewardLogInfo.cs index 2024d0c..47c6567 100644 --- a/ServerCommon/1. Define/BusinessLog/Battle/BattlePodCombatRewardLogInfo.cs +++ b/ServerCommon/1. Define/BusinessLog/Battle/BattlePodCombatRewardLogInfo.cs @@ -13,7 +13,7 @@ public class BattlePodCombatRewardLogInfo : ILogInvoker.IInfo public int m_round { get; set; } = 0; [JsonProperty("round_state")] - public BattleRoundStateType m_round_state { get; set; } = BattleRoundStateType.None; + public GameModeState m_round_state { get; set; } = GameModeState.None; [JsonProperty("rewarded_user_guid")] public string m_rewarded_user_guid { get; set; } = string.Empty; @@ -31,7 +31,7 @@ public class BattlePodCombatRewardLogInfo : ILogInvoker.IInfo public List m_rewards { get; set; } = new(); - public BattlePodCombatRewardLogInfo(ILogInvoker parent, string roomId, int round, BattleRoundStateType roundSate, string rewardUserGuid, string rewardUserNickname + public BattlePodCombatRewardLogInfo(ILogInvoker parent, string roomId, int round, GameModeState roundSate, string rewardUserGuid, string rewardUserNickname , int chargedStep, int rewardedStep, List rewards) : base(parent) { m_room_id = roomId; diff --git a/ServerCommon/1. Define/BusinessLog/Battle/BattleRespawnLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Battle/BattleRespawnLogInfo.cs index 6fb0101..904d3f0 100644 --- a/ServerCommon/1. Define/BusinessLog/Battle/BattleRespawnLogInfo.cs +++ b/ServerCommon/1. Define/BusinessLog/Battle/BattleRespawnLogInfo.cs @@ -9,7 +9,7 @@ public class BattleRespawnLogInfo : ILogInvoker.IInfo [JsonProperty("round")] public int m_round { get; set; } = 0; - [JsonProperty("round_state")] public BattleRoundStateType m_round_state { get; set; } = BattleRoundStateType.None; + [JsonProperty("round_state")] public GameModeState m_round_state { get; set; } = GameModeState.None; [JsonProperty("user_guid")] public string m_user_guid { get; set; } = string.Empty; [JsonProperty("user_nickname")] public string m_user_nickname { get; set; } = string.Empty; @@ -19,7 +19,7 @@ public class BattleRespawnLogInfo : ILogInvoker.IInfo public DateTime m_next_available_respawn_time_at_this_anchor { get; set; } = DateTimeHelper.Current; - public BattleRespawnLogInfo(ILogInvoker parent, string roomId, int round, BattleRoundStateType roundState, string userGuid, string userNickname, string respawnAnchorGuid, DateTime nextAvailableRespawnTimeAtThisAnchor) + public BattleRespawnLogInfo(ILogInvoker parent, string roomId, int round, GameModeState roundState, string userGuid, string userNickname, string respawnAnchorGuid, DateTime nextAvailableRespawnTimeAtThisAnchor) : base(parent) { m_room_id = roomId; diff --git a/ServerCommon/1. Define/BusinessLog/Battle/BattleRoundingUpdateLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Battle/BattleRoundingUpdateLogInfo.cs index bb991ac..83bc280 100644 --- a/ServerCommon/1. Define/BusinessLog/Battle/BattleRoundingUpdateLogInfo.cs +++ b/ServerCommon/1. Define/BusinessLog/Battle/BattleRoundingUpdateLogInfo.cs @@ -30,31 +30,19 @@ public class BattleRoundingUpdateLogInfo : ILogInvoker.IInfo public int m_ended_round { get; set; } = 0; [JsonProperty("round_state")] - public BattleRoundStateType m_round_state { get; set; } = BattleRoundStateType.None; + public GameModeState m_round_state { get; set; } = GameModeState.None; [JsonProperty("exist_users")] public List m_exist_users { get; set; } = new(); - public BattleRoundingUpdateLogInfo(ILogInvoker parent, string roomId, int endedRound, BattleRoundStateType roundSate, List users) : base(parent) + public BattleRoundingUpdateLogInfo(ILogInvoker parent, string roomId, int endedRound, GameModeState roundSate, List users) : base(parent) { m_room_id = roomId; m_ended_round = endedRound; m_round_state = roundSate; m_exist_users.AddRange(users); } - - // public BattleRoundingUpdateLogInfo(string roomId, int endedRound, BattleRoundStateType roundSate, List users, - // string rewardUserGuid, string rewardUserNickname, List rewards) - // { - // m_room_id = roomId; - // m_ended_round = endedRound; - // m_round_state = roundSate; - // m_exist_users.AddRange(users); - // m_rewarded_user_guid = rewardUserGuid; - // m_rewarded_user_nickname = rewardUserNickname; - // m_rewards.AddRange(rewards); - // } } \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/Battle/BattleSnapshotLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Battle/BattleSnapshotLogInfo.cs index ce91d92..3e32d4c 100644 --- a/ServerCommon/1. Define/BusinessLog/Battle/BattleSnapshotLogInfo.cs +++ b/ServerCommon/1. Define/BusinessLog/Battle/BattleSnapshotLogInfo.cs @@ -18,8 +18,8 @@ public class BattleSnapshotLogInfo : ILogInvoker.IInfo [JsonProperty("event_start_time")] public DateTime m_battle_instance_event_start_time { get; set; } = DateTimeHelper.Current; - [JsonProperty("reward_group_id")] - public Int32 m_pod_combat_reward_group_id { get; set; } = 1; + // [JsonProperty("reward_group_id")] + // public Int32 m_pod_combat_reward_group_id { get; set; } = 1; [JsonProperty("ffa_id")] public Int32 m_pod_combat_ffa_id { get; set; } = 1; @@ -52,7 +52,7 @@ public class BattleSnapshotLogInfo : ILogInvoker.IInfo [JsonProperty("buffs")] public ConcurrentDictionary m_buffs {get; set;} = new (); - [JsonProperty("state")] public BattleRoundStateType m_round_state_type { get; set; } = BattleRoundStateType.Rounding; + [JsonProperty("state")] public GameModeState m_game_mode_state { get; set; } = GameModeState.Rounding; [JsonProperty("round")] public int m_current_round { get; set; } = 1; [JsonProperty("state_start_time")] public DateTime m_current_state_start_time { get; set; } = DateTimeHelper.Current; diff --git a/ServerCommon/1. Define/BusinessLog/Domain/GameModePlayerRegulationLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Domain/GameModePlayerRegulationLogInfo.cs new file mode 100644 index 0000000..a72368b --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/Domain/GameModePlayerRegulationLogInfo.cs @@ -0,0 +1,32 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class GameModePlayerRegulationLogInfo : ILogInvoker.IInfo +{ + [JsonProperty] + + public Dictionary m_match_restriction { get; set; } = new(); + + [JsonProperty] public Dictionary m_play_penalty { get; set; } = new(); + + public GameModePlayerRegulationLogInfo() : base() + { + } + + public GameModePlayerRegulationLogInfo(ILogInvoker parent, GameModePlayerRegulationLogInfo logParam): base(parent) + { + if (null != logParam) + { + setPositionInfo(logParam); + } + } + + public void setPositionInfo(GameModePlayerRegulationLogInfo logInfo) + { + + } + +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/Domain/MatchLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Domain/MatchLogInfo.cs new file mode 100644 index 0000000..06d8fa1 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/Domain/MatchLogInfo.cs @@ -0,0 +1,77 @@ +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +//======================================== +// 매칭 서버의 로그에서 사용함 +//======================================== + +/// +/// 매칭 요청 로그 +/// +public class MatchServerUserLog : ILogInvoker.IInfo +{ + [JsonProperty] + public int GameModeId { get; init; } + + [JsonProperty] + public string Region { get; init; } = string.Empty; + + [JsonProperty] + public string Event { get; init; } = string.Empty; + + [JsonProperty] + public string RoomId { get; init; } = string.Empty; + + [JsonProperty] + public string TeamId { get; init; } = string.Empty; + + [JsonProperty] + public required string UserGuid { get; init; } + + [JsonProperty] + public required string Nickname { get; init; } + + [JsonProperty] + public required MatchStatusType MatchStatus { get; init; } + + public MatchServerUserLog(ILogInvoker parent): base(parent) + { + } +} + +/// +/// 매치 룸 상태 로그 +/// +public class MatchRoomLog : ILogInvoker.IInfo +{ + [JsonProperty] + public int GameModeId { get; init; } + + [JsonProperty] + public string Region { get; init; } = string.Empty; + + [JsonProperty] + public string Event { get; init; } = string.Empty; + + [JsonProperty] public required string RoomId { get; init; } + [JsonProperty] public required MatchRoomLogType LogState { get; init; } + [JsonProperty] public required MatchGameStateType GameState { get; init; } + [JsonProperty] public required IEnumerable Users { get; init; } + public MatchRoomLog(ILogInvoker parent): base(parent) + { + } +} + +public enum MatchRoomLogType +{ + Create, + Update, + JoinReserve, + JoinConfirm, + Quit, + Start, + End, + Destroy, +} diff --git a/ServerCommon/1. Define/BusinessLog/Domain/MatchUserLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Domain/MatchUserLogInfo.cs new file mode 100644 index 0000000..ca70a84 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/Domain/MatchUserLogInfo.cs @@ -0,0 +1,33 @@ +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +//======================================== +// 매칭의 게임 서버 유저 로그에서 사용함 +//======================================== + +public class MatchPoolLog +{ + [JsonProperty] public int GameModeId { get; set; } + [JsonProperty] public int EventId { get; set; } + [JsonProperty] public required string Region { get; set; } + [JsonProperty] public required string MatchGroupId { get; set; } +} + +public class MatchStepInfo +{ + [JsonProperty] public int MatchStep { get; set; } + [JsonProperty] public int WaitSeconds { get; set; } +} + +public class MatchUserLog : ILogInvoker.IInfo +{ + [JsonProperty] public required MatchPoolLog MatchPool { get; init; } + [JsonProperty] public string RoomId { get; init; } = string.Empty; + [JsonProperty] public string TeamId { get; init; } = string.Empty; + [JsonProperty] public required MatchStatusType MatchStatus { get; init; } + [JsonProperty] public ServerErrorCode ErrorCode { get; init; } = ServerErrorCode.Success; + [JsonProperty] public ServerConnectInfo? ConnectInfo { get; init; } + public MatchUserLog(ILogInvoker parent) : base(parent) {} +} diff --git a/ServerCommon/1. Define/BusinessLog/Domain/RankerLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Domain/RankerLogInfo.cs new file mode 100644 index 0000000..e55ca6a --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/Domain/RankerLogInfo.cs @@ -0,0 +1,55 @@ +using Newtonsoft.Json; +using ServerBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon.BusinessLogDomain +{ + public class RankerLogInfo : ILogInvoker.IInfo + { + [JsonProperty] + public string RankIngGuid { get; set; } = string.Empty; + [JsonProperty] + public string RankerGuid { get; set; } = string.Empty; + [JsonProperty] + public EntityType RankerEntityType { get; set; } = EntityType.None; + [JsonProperty] + public int ModifyScore { get; set; } + [JsonProperty] + public ScoreModifyType ScoreModifyType { get; set; } = ScoreModifyType.None; + + public RankerLogInfo() + : base() + { } + + public RankerLogInfo(ILogInvoker parent, RankerLogInfo logParam) + : base(parent) + { + if (null != logParam) + { + setRankingInfo(logParam); + } + } + + public void setRankingInfo(RankerLogInfo logInfo) + { + RankIngGuid = logInfo.RankIngGuid; + RankerGuid = logInfo.RankerGuid; + RankerEntityType = logInfo.RankerEntityType; + ModifyScore = logInfo.ModifyScore; + ScoreModifyType = logInfo.ScoreModifyType; + } + + public void setLogProperty(string rankingGuid, string rankerGuid, EntityType rankerEntityType, int modifyScore, ScoreModifyType scoreModifyType) + { + RankIngGuid = rankingGuid; + RankerGuid = rankerGuid; + RankerEntityType = RankerEntityType; + ModifyScore = modifyScore; + ScoreModifyType = scoreModifyType; + } + } +} diff --git a/ServerCommon/1. Define/BusinessLog/Domain/RankingEventActionScoreLog.cs b/ServerCommon/1. Define/BusinessLog/Domain/RankingEventActionScoreLog.cs new file mode 100644 index 0000000..22cc686 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/Domain/RankingEventActionScoreLog.cs @@ -0,0 +1,86 @@ +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class RankingEventActionScoreLog : ILogInvoker.IInfo +{ + /// + /// 랭킹 스케줄 guid + /// + [JsonProperty] public required string RankingScheduleGuid { get; init; } + /// + /// 유저 행동이 정의된 EventActionScoreData의 index + /// + [JsonProperty] public required int EventActionScoreMetaId { get; init; } + /// + /// 랭킹 칻테고리 + /// + [JsonProperty] public required string RankType { get; init; } + /// + /// 월드 이벤트 분류 + /// + [JsonProperty] public required string WorldEventType { get; init; } + /// + /// 이벤트 도메인 + /// + [JsonProperty] public required string EventTarget { get; init; } + /// + /// 이벤트 행동 + /// + [JsonProperty] public required string EventAction { get; init; } + /// + /// 이벤트 조건 퀘스트아이디, 아이템아이디 등 + /// + [JsonProperty] public required string EventCondition1 { get; init; } + [JsonProperty] public required string EventCondition2 { get; init; } + [JsonProperty] public required string EventCondition3 { get; init; } + /// + /// 획득 점수 + /// + [JsonProperty] public required int Score { get; init; } + public RankingEventActionScoreLog(ILogInvoker parent) : base(parent) {} +} + +public class WorldEventActionScoreLog : ILogInvoker.IInfo +{ + /// + /// 월드 이벤트 스케줄 id + /// + [JsonProperty] public required int WorldEventScheduleId { get; init; } + /// + /// 유저 행동이 정의된 EventActionScoreData의 index + /// + [JsonProperty] public required int EventActionScoreMetaId { get; init; } + /// + /// 랭킹 칻테고리 + /// + [JsonProperty] public required string RankType { get; init; } + /// + /// 월드 이벤트 분류 + /// + [JsonProperty] public required string WorldEventType { get; init; } + /// + /// 이벤트 도메인 + /// + [JsonProperty] public required string EventTarget { get; init; } + /// + /// 이벤트 행동 + /// + [JsonProperty] public required string EventAction { get; init; } + /// + /// 이벤트 조건 퀘스트아이디, 아이템아이디 등 + /// + [JsonProperty] public required string EventCondition1 { get; init; } + [JsonProperty] public required string EventCondition2 { get; init; } + [JsonProperty] public required string EventCondition3 { get; init; } + /// + /// 획득 점수 + /// + [JsonProperty] public required int Score { get; init; } + /// + /// 총 누적 점수 + /// + [JsonProperty] public required long ScoreTotal { get; init; } + public WorldEventActionScoreLog(ILogInvoker parent) : base(parent) {} +} diff --git a/ServerCommon/1. Define/BusinessLog/Domain/RankingLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Domain/RankingLogInfo.cs new file mode 100644 index 0000000..e16b622 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/Domain/RankingLogInfo.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using ServerBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon.BusinessLogDomain +{ + public class RankingLogInfo : ILogInvoker.IInfo + { + [JsonProperty] + public string RankingGuid { get; set; } = string.Empty; + [JsonProperty] + public int RankingMetaId { get; set; } + + + public RankingLogInfo() + : base() + { } + + public RankingLogInfo(ILogInvoker parent, RankingLogInfo logParam) + : base(parent) + { + if (null != logParam) + { + setRankingInfo(logParam); + } + } + + public void setRankingInfo(RankingLogInfo logInfo) + { + RankingGuid = logInfo.RankingGuid; + RankingMetaId = logInfo.RankingMetaId; + } + + public void setLogProperty(string rankingGuid, int rankingMetaId) + { + RankingGuid = rankingGuid; + RankingMetaId = rankingMetaId; + } + } +} diff --git a/ServerCommon/1. Define/BusinessLog/Domain/TaxiLogInfo.cs b/ServerCommon/1. Define/BusinessLog/Domain/TaxiLogInfo.cs index 355d951..196c8a1 100644 --- a/ServerCommon/1. Define/BusinessLog/Domain/TaxiLogInfo.cs +++ b/ServerCommon/1. Define/BusinessLog/Domain/TaxiLogInfo.cs @@ -16,20 +16,12 @@ namespace ServerCommon.BusinessLogDomain; public class TaxiLogInfo : ILogInvoker.IInfo { [JsonProperty] - public int TaxiMID { get; set; } + public int DepartureTaxiMID { get; set; } [JsonProperty] - public string TaxiType { get; set; } = string.Empty; // TaxiType + public int ArrivalTaxiMID { get; set; } [JsonProperty] public int Cost { get; set; } - [JsonProperty] - public int UnloadingWorldId { get; set; } - [JsonProperty] - public int UnloadingLandId { get; set; } - [JsonProperty] - public int UnloadingFloor { get; set; } - [JsonProperty] - public Pos UnloadingPos { get; set; } = new Pos(); - + public TaxiLogInfo() : base() { } @@ -45,12 +37,8 @@ public class TaxiLogInfo : ILogInvoker.IInfo public void setTaxiInfo(TaxiLogInfo logInfo) { - TaxiMID = logInfo.TaxiMID; - TaxiType = logInfo.TaxiType; + DepartureTaxiMID = logInfo.DepartureTaxiMID; + ArrivalTaxiMID = logInfo.ArrivalTaxiMID; Cost = logInfo.Cost; - UnloadingWorldId = logInfo.UnloadingWorldId; - UnloadingLandId = logInfo.UnloadingLandId; - UnloadingFloor = logInfo.UnloadingFloor; - UnloadingPos = logInfo.UnloadingPos; } } diff --git a/ServerCommon/1. Define/BusinessLog/Enum/LogEnum.cs b/ServerCommon/1. Define/BusinessLog/Enum/LogEnum.cs index f9da61e..3743030 100644 --- a/ServerCommon/1. Define/BusinessLog/Enum/LogEnum.cs +++ b/ServerCommon/1. Define/BusinessLog/Enum/LogEnum.cs @@ -64,10 +64,16 @@ namespace ServerCommon Land, [BusinessLogEnum("전투")] Battle, + [BusinessLogEnum("게임모드")] + GameMode, [BusinessLogEnum("BrokerApi")] BrokerApi, [BusinessLogEnum("비컨상점")] BeaconShop, + [BusinessLogEnum("매칭")] + Match, + [BusinessLogEnum("랭킹")] + Ranking, }//LogCategoryType @@ -107,12 +113,20 @@ namespace ServerCommon BattleInstance, [BusinessLogEnum("배틀 오브젝트")] BattleObject, + [BusinessLogEnum("게임 모드 오브젝트")] + GameModeObject, [BusinessLogEnum("전투 중 죽음")] BattleDead, + [BusinessLogEnum("러닝")] + RunRace, + [BusinessLogEnum("러닝 연습모드")] + RunPractice, [BusinessLogEnum("랜탈")] Rental, [BusinessLogEnum("비컨 상점")] BeaconShop, + [BusinessLogEnum("랭커")] + Ranker, }//LogSubCategoryType @@ -269,12 +283,44 @@ namespace ServerCommon BattleRound, [BusinessLogEnum("전투 스냅샷")] BattleSnapshot, + [BusinessLogEnum("게임 오브젝트 인터렉션")] + GameObjectInteraction, + [BusinessLogEnum("러닝 완주 보상")] + RunRaceFinishReward, + [BusinessLogEnum("러닝 리스폰 보상")] + RunRaceRespawnReward, + [BusinessLogEnum("러닝 미완주 보상")] + RunRaceUnFinishReward, + [BusinessLogEnum("러닝 체크포인트 어뷰징")] + RunRaceCheckPointAbusing, + [BusinessLogEnum("러닝 리워드 결과 요약")] + RunRaceRewardSummary, + [BusinessLogEnum("러닝 염습모드 결과 요약")] + RunPracticePlaySummary, + [BusinessLogEnum("게임모드 패널티")] + GameModePenalty, [BusinessLogEnum("비컨 상점")] BeaconShop, [BusinessLogEnum("비컨 상점 영수증")] BeaconShopSoldRecord, [BusinessLogEnum("비컨 상점 정산금")] BeaconShopSoldPrice, + [BusinessLogEnum("게임모드 규정")] + GameModePlayRegulation, + [BusinessLogEnum("매칭 유저")] + MatchUser, + [BusinessLogEnum("매칭 서버 유저")] + MatchServerUser, + [BusinessLogEnum("매칭 룸")] + MatchRoom, + [BusinessLogEnum("이벤트 액션 스코어")] + RankingEventActionScore, + [BusinessLogEnum("월드 이벤트 액션 스코어")] + WorldEventActionScore, + [BusinessLogEnum("랭킹")] + Ranking, + [BusinessLogEnum("랭커")] + Ranker, //====================================================== // BrokerApi @@ -706,6 +752,23 @@ namespace ServerCommon LandEnd, #endregion + //======================================================================================================== + // 랭킹 + //======================================================================================================== + #region + RankingBegin = 7800, + + [BusinessLogEnum("랭킹 시작", LogCategoryType.Ranking, LogSubCategoryType.None, "Game")] + RankingStart, + [BusinessLogEnum("랭킹 종료", LogCategoryType.Ranking, LogSubCategoryType.None, "Game")] + RankingFinish, + [BusinessLogEnum("랭킹 점수 변경", LogCategoryType.Ranking, LogSubCategoryType.Ranker, "Game")] + RankingScoreUpdate, + + //<== 위에추가 + RankingEnd, + #endregion + //======================================================================================================== // Calium (칼리움) //======================================================================================================== @@ -819,6 +882,10 @@ namespace ServerCommon DeleteMyhome, [BusinessLogEnum("마이홈 이름 변경", LogCategoryType.Contents, LogSubCategoryType.None, "Game")] RenameMyhome, + [BusinessLogEnum("비컨으로 이동", LogCategoryType.Contents, LogSubCategoryType.None, "Game")] + MoveToBeacon, + [BusinessLogEnum("메인 메뉴 컨텐츠 이동", LogCategoryType.Contents, LogSubCategoryType.None, "Game")] + ContentsMove, @@ -860,6 +927,8 @@ namespace ServerCommon [BusinessLogEnum("빌딩 층 임대", LogCategoryType.Contents, LogSubCategoryType.Rental, "Game")] RentFloor, + [BusinessLogEnum("필드 총격 오프젝트 파괴", LogCategoryType.Contents, LogSubCategoryType.RewardProp, "Game")] + DestroyWeaponObject, //<== 위에추가 ContentsEnd, @@ -1003,7 +1072,11 @@ namespace ServerCommon CheatCommandBeaconShopItemTimeChange, [BusinessLogEnum("치트로 인한 비컨 상점 일일 횟수 초기화", LogCategoryType.Contents, LogSubCategoryType.BeaconShop, "Game")] CheatCommandDailyLimitInit, + [BusinessLogEnum("운영툴 명령으로 인한 퀘스트 Task 강제 완료", LogCategoryType.Contents, LogSubCategoryType.QuestMain, "Game")] + AdminToolQuestTaskForceComplete, + [BusinessLogEnum("치트로 인한 제작에 필요한 아이템 등록", LogCategoryType.Craft, LogSubCategoryType.None, "Game")] + CheatCommandAddCraftItems, //<== 위에추가 GmCommandEnd, @@ -1058,9 +1131,36 @@ namespace ServerCommon igmApiEnd, #endregion + //===================== + // 매칭 + //===================== + [BusinessLogEnum("매칭 예약")] + MatchReserve, + [BusinessLogEnum("매칭 취소")] + MatchCancel, + [BusinessLogEnum("매칭 결과")] + MatchResult, + [BusinessLogEnum("매칭 룸 입장")] + MatchRoomUserJoin, + [BusinessLogEnum("매칭 룸 퇴장")] + MatchRoomUserQuit, + [BusinessLogEnum("매칭 룸 생성")] + MatchRoomCreate, + [BusinessLogEnum("매칭 룸 업데이트")] + MatchRoomUpdate, + [BusinessLogEnum("매칭 룸 삭제")] + MatchRoomDestroy, + + //=================== + // 이벤트 액션 스코어 + //=================== + [BusinessLogEnum("랭킹 이벤트 액션 스코어 처리")] + RankingEventActionScore, + [BusinessLogEnum("월드 이벤트 액션 스코어 처리")] + WorldEventActionScore, //======================================================================================================== - // Battle + // Battle FFA //======================================================================================================== #region BattleBegin = 100001, @@ -1089,6 +1189,81 @@ namespace ServerCommon BattleEnd, #endregion + //======================================================================================================== + // Run Race + //======================================================================================================== + #region + RunRaceBegin = 100101, + + [BusinessLogEnum("레이스 완주 보상", LogCategoryType.GameMode, LogSubCategoryType.RunRace, "Game")] + RunRaceFinishReward, + [BusinessLogEnum("레이스 리스폰 보상", LogCategoryType.GameMode, LogSubCategoryType.RunRace, "Game")] + RunRaceRespawnReward, + [BusinessLogEnum("레이스 미완주 보상", LogCategoryType.GameMode, LogSubCategoryType.RunRace, "Game")] + RunRaceUnFinishReward, + [BusinessLogEnum("체크포인트 어뷰징", LogCategoryType.GameMode, LogSubCategoryType.RunRace, "Game")] + RunRaceCheckPointAbusing, + [BusinessLogEnum("결과 요약", LogCategoryType.GameMode, LogSubCategoryType.RunRace, "Game")] + RunRaceResultSummary, + + //<== 위에추가 + RunRace, + #endregion + + //======================================================================================================== + // Run Practice + //======================================================================================================== + #region + RunPracticeBegin = 100151, + + [BusinessLogEnum("레이스 연습 플레이 요약", LogCategoryType.GameMode, LogSubCategoryType.RunPractice, "Game")] + RunPracticePlaySummary, + + //<== 위에추가 + RunPractice, + #endregion + + // //======================================================================================================== + // // Run Single + // //======================================================================================================== + // #region + // RunSingleBegin = 100161, + // + // [BusinessLogEnum("런 싱글 플레이 요약", LogCategoryType.GameMode, LogSubCategoryType.RunPractice, "Game")] + // RunSinglePlaySummary, + // RunSingleCompletionReward, + // + // //<== 위에추가 + // RunSingleEnd, + // #endregion + + + //======================================================================================================== + // Battle FFA + //======================================================================================================== + #region + GameModeBegin = 100201, + + [BusinessLogEnum("게임모드 오브젝트 상호작용", LogCategoryType.GameMode, LogSubCategoryType.GameModeObject, "Game")] + GameModeObjectInteraction, + [BusinessLogEnum("게임모드 오브젝트 상태 변경", LogCategoryType.GameMode, LogSubCategoryType.BattleObject, "Game")] + GameModeObjectStateUpdate, + [BusinessLogEnum("게임모드 유저 데드", LogCategoryType.GameMode, LogSubCategoryType.BattleInstance, "Game")] + GameModeUserDead, + [BusinessLogEnum("게임모드 리스폰", LogCategoryType.GameMode, LogSubCategoryType.BattleInstance, "Game")] + GameModeUserRespawn, + [BusinessLogEnum("게임모드 규정", LogCategoryType.GameMode, LogSubCategoryType.GameOption, "Game")] + UpdateGameModePlayerRegulation, + [BusinessLogEnum("게임모드 패널티", LogCategoryType.GameMode, LogSubCategoryType.GameOption, "Game")] + GameModePenalty, + [BusinessLogEnum("GameMode 매치카운트 증가", LogCategoryType.GameMode, LogSubCategoryType.None, "Game")] + GameModeAddMatchCount, + [BusinessLogEnum("GameMode 개인 최고 기록 저장", LogCategoryType.GameMode, LogSubCategoryType.None, "Game")] + GameModePersonalBestRecord, + + //<== 위에추가 + GameModeEnd, + #endregion }//LogActionType diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/GameModeMatchRegulationLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/GameModeMatchRegulationLogInfo.cs new file mode 100644 index 0000000..61a41c1 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/GameModeMatchRegulationLogInfo.cs @@ -0,0 +1,32 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class GameModeMatchRegulationLogInfo : ILogInvoker.IInfo +{ + [JsonProperty] + public Dictionary m_match_restriction { get; set; } = new(); + [JsonProperty] + public Dictionary m_play_penalty { get; set; } = new(); + public GameModeMatchRegulationLogInfo() : base() + { + + } + + public void setLogInfo(GameModeMatchRegulationLogInfo logInfo) + { + m_match_restriction = logInfo.m_match_restriction; + m_play_penalty = logInfo.m_play_penalty; + } + + public GameModeMatchRegulationLogInfo(ILogInvoker parent, GameModeMatchRegulationLogInfo logInfo) + : base(parent) + { + if (null != logInfo) + { + setLogInfo(logInfo); + } + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/Battle/BattleInstancesObjects.cs b/ServerCommon/1. Define/BusinessLog/GameMode/GameModeObject.cs similarity index 80% rename from ServerCommon/1. Define/BusinessLog/Battle/BattleInstancesObjects.cs rename to ServerCommon/1. Define/BusinessLog/GameMode/GameModeObject.cs index 5ecf4bc..045d421 100644 --- a/ServerCommon/1. Define/BusinessLog/Battle/BattleInstancesObjects.cs +++ b/ServerCommon/1. Define/BusinessLog/GameMode/GameModeObject.cs @@ -8,20 +8,20 @@ namespace ServerCommon; //파일럿이라 무시하고 이쪽에 넣는다... -public class BattleInstancesObject +public class GameModeObject { - public EBattleObjectType m_type = EBattleObjectType.None; + public EGameModeObjectType m_type = EGameModeObjectType.None; public string m_anchor_guid { get; set; } = string.Empty; public DateTime m_active_time { get; set; } = DateTimeHelper.Current; public bool m_is_active { get; set; } = true; - public BattleInstancesObject(EBattleObjectType type, string guid) + public GameModeObject(EGameModeObjectType type, string guid) { m_type = type; m_anchor_guid = guid; } - public BattleInstancesObject(EBattleObjectType type, string guid, bool isActive) + public GameModeObject(EGameModeObjectType type, string guid, bool isActive) { m_type = type; m_anchor_guid = guid; @@ -29,26 +29,32 @@ public class BattleInstancesObject } } -public class BattleObjectPodStorage : BattleInstancesObject +public class BattleObjectPodStorage : GameModeObject { public Int32 m_reward_cnt { get; set; } = 0; - public BattleObjectPodStorage(string anchorGuid) : base(EBattleObjectType.Pod_Combat, anchorGuid) + public BattleObjectPodStorage(string anchorGuid) : base(EGameModeObjectType.Pod_Combat, anchorGuid) { } } -public class BattleObjectEmpty : BattleInstancesObject +public class BattleObjectEmpty : GameModeObject { - public BattleObjectEmpty() : base(EBattleObjectType.None, string.Empty) + public BattleObjectEmpty() : base(EGameModeObjectType.None, string.Empty) { } - + } +public class GameObjectEmpty : GameModeObject +{ + public GameObjectEmpty() : base(EGameModeObjectType.None, string.Empty) + { + } +} -public class BattleObjectPodCombat : BattleInstancesObject +public class BattleObjectPodCombat : GameModeObject { private ECombatPodType m_combat_pod_type { get; } = ECombatPodType.Pod; public string m_source_storage_anchor_guid { get; set; } = string.Empty; //pod 발생된 스토리지의 anchor guid @@ -59,11 +65,11 @@ public class BattleObjectPodCombat : BattleInstancesObject public string m_current_occupier_guid { get; set; } = string.Empty; public DateTime m_occupied_time { get; set; } = DateTimeHelper.Current; //이게 상태에 따라 획득한 시간으로도 쓴다. - public BattleObjectPodCombat(): base(EBattleObjectType.Pod_Combat, string.Empty, false) + public BattleObjectPodCombat(): base(EGameModeObjectType.Pod_Combat, string.Empty, false) { } - public BattleObjectPodCombat(string anchorGuid, string sourceAnchorGuid, DateTime startTime) : base(EBattleObjectType.Pod_Combat, anchorGuid, false) + public BattleObjectPodCombat(string anchorGuid, string sourceAnchorGuid, DateTime startTime) : base(EGameModeObjectType.Pod_Combat, anchorGuid, false) { m_source_storage_anchor_guid = sourceAnchorGuid; m_state = PodCombatStateType.Active; @@ -103,10 +109,10 @@ public class BattleObjectPodCombat : BattleInstancesObject } } -public class BattleObjectPickupPod : BattleInstancesObject +public class BattleObjectPickupPod : GameModeObject { //public bool m_has_reward = false; - public BattleObjectPickupPod(string anchorGuid) : base(EBattleObjectType.Pod_Box, anchorGuid) + public BattleObjectPickupPod(string anchorGuid) : base(EGameModeObjectType.Pod_Box, anchorGuid) { } @@ -125,10 +131,10 @@ public class BattleObjectPickupPod : BattleInstancesObject } } -public class BattleObjectWeapon : BattleInstancesObject +public class BattleObjectWeapon : GameModeObject { //뭐가 필요할까? - public BattleObjectWeapon(string anchorGuid) : base(EBattleObjectType.Weapon, anchorGuid) + public BattleObjectWeapon(string anchorGuid) : base(EGameModeObjectType.Weapon, anchorGuid) { } @@ -142,10 +148,10 @@ public class BattleObjectWeapon : BattleInstancesObject } } -public class BattleObjectBuff : BattleInstancesObject +public class BattleObjectBuff : GameModeObject { //뭐가 필요할까? - public BattleObjectBuff(string anchorGuid) : base(EBattleObjectType.Buff, anchorGuid) + public BattleObjectBuff(string anchorGuid) : base(EGameModeObjectType.Buff, anchorGuid) { } diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/GameModePenaltyLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/GameModePenaltyLogInfo.cs new file mode 100644 index 0000000..7b901ed --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/GameModePenaltyLogInfo.cs @@ -0,0 +1,42 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class GameModePenaltyLogInfo : ILogInvoker.IInfo +{ + [JsonProperty("game_mode_id")] + public int m_game_mode_id { get; private set; } = 0; + + [JsonProperty("game_mode_type")] + public GameModeType m_game_mode_type { get; private set; } = GameModeType.None; + + [JsonProperty("room_id")] + public string m_room_id { get; private set; } = string.Empty; + + [JsonProperty("match_restriction")] + public Dictionary m_match_restriction { get; set; } = new(); + + [JsonProperty("play_penalty")] + public Dictionary m_play_penalty { get; set; } = new(); + + public GameModePenaltyLogInfo(int gameModeId, GameModeType gameModeType, string roomId, + Dictionary matchRestriction, Dictionary playPenalties) + { + m_game_mode_id = gameModeId; + m_game_mode_type = gameModeType; + m_room_id = roomId; + m_match_restriction = matchRestriction; + m_play_penalty = playPenalties; + } + + public GameModePenaltyLogInfo(ILogInvoker parent, GameModePenaltyLogInfo logInfo) + { + m_game_mode_id = logInfo.m_game_mode_id; + m_game_mode_type = logInfo.m_game_mode_type; + m_room_id = logInfo.m_room_id; + m_match_restriction = logInfo.m_match_restriction; + m_play_penalty = logInfo.m_play_penalty; + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/GameObjectInteractionLogInfoBase.cs b/ServerCommon/1. Define/BusinessLog/GameMode/GameObjectInteractionLogInfoBase.cs new file mode 100644 index 0000000..57ecf6e --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/GameObjectInteractionLogInfoBase.cs @@ -0,0 +1,79 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace ServerCommon.BusinessLogDomain; + +public abstract class GameObjectInteractionLogInfoBase : ILogInvoker.IInfo, IGameObjectInteractionLogInfo +{ + [JsonProperty("room_id")] + public string m_room_id { get; set; } = string.Empty; + + [JsonProperty("game_mode_type")] + private GameModeType m_game_mode_type = GameModeType.None; + + [JsonProperty("game_object_type")] + private EGameModeObjectType m_game_object_type = EGameModeObjectType.None; + + [JsonProperty("interaction_user_guid")] + public string m_interaction_user_guid { get; set; } = string.Empty; + + [JsonProperty("interaction_user_nickname")] + public string m_interaction_user_nickname { get; set; } = string.Empty; + + [JsonProperty("interaction_anchor_guid")] + public string m_interaction_anchor_guid { get; set; } = string.Empty; + + [JsonProperty("table_id")] public int m_table_id { get; set; } = 0; + + [JsonProperty("next_active_time")] public DateTime m_object_next_active_time { get; set; } = DateTimeHelper.Current; + + [JsonProperty("is_active")] + public bool m_is_active = false; + + [JsonProperty("reward")] + public List m_rewards = new(); + + public GameObjectInteractionLogInfoBase(GameModeType gameModeType, EGameModeObjectType gameModeObjectType, + string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId) : base() + { + m_game_object_type = gameModeObjectType; + m_game_mode_type = gameModeType; + m_interaction_anchor_guid = interactionAnchorGuid; + m_interaction_user_nickname = userNickname; + m_room_id = roomId; + m_interaction_user_guid = userGuid; + m_table_id = tableId; + } + + public GameObjectInteractionLogInfoBase() + { + } + + public GameObjectInteractionLogInfoBase(ILogInvoker parent, GameObjectInteractionLogInfoBase logInfo, GameModeType gameModeType, EGameModeObjectType gameModeObjectType) + : base(parent) + { + m_game_object_type = gameModeObjectType; + m_game_mode_type = gameModeType; + if (null != logInfo) + { + setLogInfo(logInfo); + } + } + + protected void setLogInfo(GameObjectInteractionLogInfoBase logInfoBase) + { + m_interaction_anchor_guid = logInfoBase.m_interaction_anchor_guid; + m_interaction_user_nickname = logInfoBase.m_interaction_user_nickname; + m_room_id = logInfoBase.m_room_id; + m_interaction_user_guid = logInfoBase.m_interaction_user_guid; + m_object_next_active_time = logInfoBase.m_object_next_active_time; + m_is_active = logInfoBase.m_is_active; + m_rewards = logInfoBase.m_rewards; + m_table_id = logInfoBase.m_table_id; + + setLogInfoDetail(logInfoBase); + } + protected abstract void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase); +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/GameObjectInteractionLogInfoEmpty.cs b/ServerCommon/1. Define/BusinessLog/GameMode/GameObjectInteractionLogInfoEmpty.cs new file mode 100644 index 0000000..9895128 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/GameObjectInteractionLogInfoEmpty.cs @@ -0,0 +1,13 @@ +using ServerCommon.BusinessLogDomain; + +namespace ServerCommon._1._Define.BusinessLog.GameMode; + +public class GameObjectInteractionLogInfoEmpty : GameObjectInteractionLogInfoBase +{ + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + return; + } + + +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/IGameObjectInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/IGameObjectInteractionLogInfo.cs new file mode 100644 index 0000000..0e03197 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/IGameObjectInteractionLogInfo.cs @@ -0,0 +1,6 @@ +namespace ServerCommon.BusinessLogDomain; + +public interface IGameObjectInteractionLogInfo +{ + +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/GameModeRunPracticePlayLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/GameModeRunPracticePlayLogInfo.cs new file mode 100644 index 0000000..bc8765f --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/GameModeRunPracticePlayLogInfo.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace ServerCommon; + +public class GameModeRunPracticePlayLogInfo : ILogInvoker.IInfo +{ + [JsonProperty("room_id")] + public string m_room_id { get; set; } = string.Empty; + + [JsonProperty("user_guid")] + public string m_user_guid { get; set; } = string.Empty; + + [JsonProperty("start_time")] + public DateTime m_start_time { get; set; } = DateTimeHelper.Current; + + [JsonProperty("end_time")] + public DateTime m_end_time { get; set; } = DateTimeHelper.Current; + + [JsonProperty("respawn_count")] + public Int32 m_respawn_count { get; set; } = 0; + + public GameModeRunPracticePlayLogInfo() + { + } + + public GameModeRunPracticePlayLogInfo(ILogInvoker parent, GameModeRunPracticePlayLogInfo logInfo) + { + m_room_id = logInfo.m_room_id; + m_user_guid = logInfo.m_user_guid; + m_start_time = logInfo.m_start_time; + m_end_time = logInfo.m_end_time; + m_respawn_count = logInfo.m_respawn_count; + } + +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeBuffInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeBuffInteractionLogInfo.cs new file mode 100644 index 0000000..4f82611 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeBuffInteractionLogInfo.cs @@ -0,0 +1,23 @@ +using MetaAssets; +using ServerBase; +using ServerCommon.BusinessLogDomain; + +namespace ServerCommon; + +public class RunPracticeBuffInteractionLogInfo : GameObjectInteractionLogInfoBase +{ + public RunPracticeBuffInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId) + : base(GameModeType.RUN_PRACTICE, EGameModeObjectType.Buff, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + + } + + public RunPracticeBuffInteractionLogInfo(ILogInvoker parent, RunPracticeBuffInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.RUN_PRACTICE, EGameModeObjectType.Buff) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeCheckPointInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeCheckPointInteractionLogInfo.cs new file mode 100644 index 0000000..4376993 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeCheckPointInteractionLogInfo.cs @@ -0,0 +1,24 @@ +using MetaAssets; +using ServerBase; +using ServerCommon.BusinessLogDomain; + +namespace ServerCommon; + +public class RunPracticeCheckPointInteractionLogInfo : GameObjectInteractionLogInfoBase +{ + + public RunPracticeCheckPointInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId) + : base(GameModeType.RUN_PRACTICE, EGameModeObjectType.CheckPoint, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + } + + public RunPracticeCheckPointInteractionLogInfo(ILogInvoker parent, RunPracticeCheckPointInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.RUN_PRACTICE, EGameModeObjectType.CheckPoint) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeGoalLineInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeGoalLineInteractionLogInfo.cs new file mode 100644 index 0000000..33f2b7a --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunPractice/RunPracticeGoalLineInteractionLogInfo.cs @@ -0,0 +1,23 @@ +using MetaAssets; +using ServerBase; +using ServerCommon.BusinessLogDomain; + +namespace ServerCommon; + +public class RunPracticeGoalLineInteractionLogInfo : GameObjectInteractionLogInfoBase +{ + public RunPracticeGoalLineInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId) + : base(GameModeType.RUN_PRACTICE, EGameModeObjectType.Goal, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + + } + + public RunPracticeGoalLineInteractionLogInfo(ILogInvoker parent, RunPracticeGoalLineInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.RUN_PRACTICE, EGameModeObjectType.Goal) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/GameModeRunRaceCheckPointAbusingLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/GameModeRunRaceCheckPointAbusingLogInfo.cs new file mode 100644 index 0000000..ee7dddb --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/GameModeRunRaceCheckPointAbusingLogInfo.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace ServerCommon.BusinessLogDomain; + +public class GameModeRunRaceCheckPointAbusingLogInfo : ILogInvoker.IInfo +{ + [JsonProperty("room_id")] + public string m_room_id { get; private set; } = string.Empty; + [JsonProperty("run_race_start_time")] + public DateTime m_start_time { get; private set; } = DateTimeHelper.Current; + [JsonProperty("interact_time")] + public DateTime m_interact_time { get; private set; } = DateTimeHelper.Current; + [JsonProperty("check_point_time")] + public int m_check_point_time { get; private set; } = 0; + [JsonProperty("check_point_id")] + public int m_check_point_id { get; private set; } = 0; + + public GameModeRunRaceCheckPointAbusingLogInfo(string roomId, DateTime startTime, DateTime interactTime, int checkPointTime, int checkPointId) : base() + { + m_room_id = roomId; + m_start_time = startTime; + m_interact_time = interactTime; + m_check_point_time = checkPointTime; + m_check_point_id = checkPointId; + } + + public GameModeRunRaceCheckPointAbusingLogInfo(ILogInvoker parent, GameModeRunRaceCheckPointAbusingLogInfo logInfo) + { + m_room_id = logInfo.m_room_id; + m_start_time = logInfo.m_start_time; + m_interact_time = logInfo.m_interact_time; + m_check_point_time = logInfo.m_check_point_time; + m_check_point_id = logInfo.m_check_point_id; + } + +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/GameModeRunRaceRewardSummayLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/GameModeRunRaceRewardSummayLogInfo.cs new file mode 100644 index 0000000..50b454f --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/GameModeRunRaceRewardSummayLogInfo.cs @@ -0,0 +1,91 @@ +using System.Collections.Concurrent; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace ServerCommon.BusinessLogDomain; + +using USER_GUID = System.String; +using RANK = System.Int32; +using RESPAWN_COUNT = System.Int32; +using COMPLETION_TIME = System.DateTime; +using ANCHOR_GUID = System.String; +using TABLE_ID = System.Int32; +public class GameModeRunRaceRewardSummayLogInfo : ILogInvoker.IInfo +{ + [JsonProperty("room_id")] + public string m_room_id { get; set; } = string.Empty; + + [JsonProperty("start_play_user")] + public List m_start_play_users = new(); + + [JsonProperty("end_play_user")] + public List m_end_play_users = new(); + + [JsonProperty("run_race_start_time")] + public DateTime m_start_time { get; set; } = DateTimeHelper.Current; + + [JsonProperty("run_race_end_time")] + public DateTime m_end_time { get; set; } = DateTimeHelper.Current; + + + [JsonProperty("rank_rewards")] + public Dictionary)> m_finished_rewards_with_rank = new(); + + [JsonProperty("rewpawn_rewards")] + public Dictionary> m_respawn_rewards = new(); + + [JsonProperty("unfinished_rewards")] + public Dictionary> m_unfinished_rewards = new(); + + [JsonProperty("ranks")] + public Queue m_ranks = new(); + + [JsonProperty("respawns")] + public Dictionary m_respawns = new(); + + [JsonProperty("check_points")] + public Dictionary> m_check_points = new(); + + [JsonProperty("completion_time")] + public Dictionary m_completion_time = new(); + + + + public GameModeRunRaceRewardSummayLogInfo() + { + } + + public GameModeRunRaceRewardSummayLogInfo(ILogInvoker parent, GameModeRunRaceRewardSummayLogInfo logInfo) + { + m_room_id = logInfo.m_room_id; + m_start_time = logInfo.m_start_time; + m_end_time = logInfo.m_end_time; + m_start_play_users.AddRange(logInfo.m_start_play_users); + m_check_points = logInfo.m_check_points; + m_completion_time = logInfo.m_completion_time; + m_end_play_users.AddRange(logInfo.m_end_play_users); + m_finished_rewards_with_rank = logInfo.m_finished_rewards_with_rank; + m_ranks = logInfo.m_ranks; + m_respawns = logInfo.m_respawns; + m_respawn_rewards = logInfo.m_respawn_rewards; + m_unfinished_rewards = logInfo.m_unfinished_rewards; + } +} + +public class RunRaceCheckPointInteractionInfo +{ + public string m_user_guid { get; set; } = string.Empty; + public string m_anchor_guid { get; set; } = string.Empty; + public int m_table_id { get; set; } = 0; + public DateTime m_interaction_time { get; set; } = DateTime.MinValue; + + public RunRaceCheckPointInteractionInfo(string userGuid, string anchorGuid, int tableId, DateTime interactionTime) + { + m_user_guid = userGuid; + m_anchor_guid = anchorGuid; + m_table_id = tableId; + m_interaction_time = interactionTime; + } + +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceBuffInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceBuffInteractionLogInfo.cs new file mode 100644 index 0000000..99bbdcb --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceBuffInteractionLogInfo.cs @@ -0,0 +1,24 @@ +using MetaAssets; +using ServerBase; +using ServerCommon.BusinessLogDomain; + +namespace ServerCommon; + +public class RunRaceBuffInteractionLogInfo : GameObjectInteractionLogInfoBase +{ + public RunRaceBuffInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId) + : base(GameModeType.RUN_RACE, EGameModeObjectType.Buff, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + + } + + public RunRaceBuffInteractionLogInfo(ILogInvoker parent, RunRaceBuffInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.RUN_RACE, EGameModeObjectType.Buff) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + } + +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceCheckPointInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceCheckPointInteractionLogInfo.cs new file mode 100644 index 0000000..ea297c8 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceCheckPointInteractionLogInfo.cs @@ -0,0 +1,26 @@ +using MetaAssets; +using ServerBase; +using ServerCommon.BusinessLogDomain; + +namespace ServerCommon; + +public class RunRaceCheckPointInteractionLogInfo : GameObjectInteractionLogInfoBase +{ + + + public RunRaceCheckPointInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId) + : base(GameModeType.RUN_RACE, EGameModeObjectType.CheckPoint, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + + } + + public RunRaceCheckPointInteractionLogInfo(ILogInvoker parent, RunRaceCheckPointInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.RUN_RACE, EGameModeObjectType.CheckPoint) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + } + +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceFinishRewardLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceFinishRewardLogInfo.cs new file mode 100644 index 0000000..72a94c8 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceFinishRewardLogInfo.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; + +public class RunRaceFinishRewardLogInfo : ILogInvoker.IInfo +{ + [JsonProperty("room_id")] + public string m_room_id { get; set; } = string.Empty; + + [JsonProperty("user_guid")] + public string m_user_guid { get; set; } = string.Empty; + + [JsonProperty("rank")] + public int m_rank { get; set; } = 0; + + [JsonProperty("rewards")] + public List m_rewards { get; set; } = new(); + + + public RunRaceFinishRewardLogInfo(ILogInvoker parent, string roomId, string userGuid, int rank, List rewards) + { + m_room_id = roomId; + m_user_guid = userGuid; + m_rank = rank; + m_rewards = rewards; + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceGoalLineInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceGoalLineInteractionLogInfo.cs new file mode 100644 index 0000000..4a6604e --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceGoalLineInteractionLogInfo.cs @@ -0,0 +1,26 @@ +using MetaAssets; +using ServerBase; +using ServerCommon.BusinessLogDomain; + +namespace ServerCommon; + +public class RunRaceGoalLineInteractionLogInfo : GameObjectInteractionLogInfoBase +{ + + + public RunRaceGoalLineInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId) + : base(GameModeType.RUN_RACE, EGameModeObjectType.Goal, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + + } + + public RunRaceGoalLineInteractionLogInfo(ILogInvoker parent, RunRaceGoalLineInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.RUN_RACE, EGameModeObjectType.Goal) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + } + +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceRespawnRewardLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceRespawnRewardLogInfo.cs new file mode 100644 index 0000000..78d47c8 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceRespawnRewardLogInfo.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; + +public class RunRaceRespawnRewardLogInfo : ILogInvoker.IInfo +{ + [JsonProperty("room_id")] + public string m_room_id { get; set; } = string.Empty; + + [JsonProperty("user_guid")] + public string m_user_guid { get; set; } = string.Empty; + + [JsonProperty("respawn_count")] + public int m_respawn_count { get; set; } = 0; + + [JsonProperty("rewards")] + public List m_rewards { get; set; } = new(); + + + public RunRaceRespawnRewardLogInfo(ILogInvoker parent, string roomId, string userGuid, int respawnCount, List rewards) + { + m_room_id = roomId; + m_user_guid = userGuid; + m_respawn_count = respawnCount; + m_rewards = rewards; + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceUnFinishRewardLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceUnFinishRewardLogInfo.cs new file mode 100644 index 0000000..c032b70 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/RunRace/RunRaceUnFinishRewardLogInfo.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; + +public class RunRaceUnFinishRewardLogInfo: ILogInvoker.IInfo +{ + [JsonProperty("room_id")] + public string m_room_id { get; set; } = string.Empty; + + [JsonProperty("user_guid")] + public string m_user_guid { get; set; } = string.Empty; + + [JsonProperty("rewards")] + public List m_rewards { get; set; } = new(); + + + public RunRaceUnFinishRewardLogInfo(ILogInvoker parent, string roomId, string userGuid, List rewards) + { + m_room_id = roomId; + m_user_guid = userGuid; + m_rewards = rewards; + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaBuffInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaBuffInteractionLogInfo.cs new file mode 100644 index 0000000..6ca11aa --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaBuffInteractionLogInfo.cs @@ -0,0 +1,21 @@ +using MetaAssets; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class TpsFfaBuffInteractionLogInfo : GameObjectInteractionLogInfoBase +{ + public TpsFfaBuffInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId = 0) + : base(GameModeType.TPS_FFA, EGameModeObjectType.Buff, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + } + + public TpsFfaBuffInteractionLogInfo(ILogInvoker parent, TpsFfaBuffInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.TPS_FFA, EGameModeObjectType.Buff) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaCombatPodInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaCombatPodInteractionLogInfo.cs new file mode 100644 index 0000000..a219110 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaCombatPodInteractionLogInfo.cs @@ -0,0 +1,29 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class TpsFfaCombatPodInteractionLogInfo : GameObjectInteractionLogInfoBase +{ + [JsonProperty("pod_type")] + public ECombatPodType m_pod_type = ECombatPodType.None; + [JsonProperty("pod_combat_pos")] + public Pos m_pod_combat_pos { get; set; } = new(); + [JsonProperty("pod_combat_state")] + public PodCombatStateType m_pod_combat_state { get; set; } = PodCombatStateType.None; + public TpsFfaCombatPodInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId = 0) + : base(GameModeType.TPS_FFA, EGameModeObjectType.Pod_Combat, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + + } + + public TpsFfaCombatPodInteractionLogInfo(ILogInvoker parent, TpsFfaCombatPodInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.TPS_FFA, EGameModeObjectType.Pod_Combat) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaPickupPodInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaPickupPodInteractionLogInfo.cs new file mode 100644 index 0000000..f8c5371 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaPickupPodInteractionLogInfo.cs @@ -0,0 +1,24 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class TpsFfaPickupPodInteractionLogInfo: GameObjectInteractionLogInfoBase +{ + public TpsFfaPickupPodInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId = 0) + : base(GameModeType.TPS_FFA, EGameModeObjectType.Pod_Box, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + + } + + public TpsFfaPickupPodInteractionLogInfo(ILogInvoker parent, TpsFfaPickupPodInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.TPS_FFA, EGameModeObjectType.Pod_Box) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + return; + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaPodStorageInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaPodStorageInteractionLogInfo.cs new file mode 100644 index 0000000..f97f3f7 --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaPodStorageInteractionLogInfo.cs @@ -0,0 +1,44 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class TpsFfaPodStorageInteractionLogInfo : GameObjectInteractionLogInfoBase +{ + [JsonProperty("has_pod_combat")] + public bool m_has_pod_combat = false; + + [JsonProperty("pod_type")] + public ECombatPodType m_pod_type = ECombatPodType.None; + + [JsonProperty("pod_combat_pos")] + public Pos m_pod_combat_pos { get; set; } = new(); + + [JsonProperty("pod_combat_state")] + public PodCombatStateType m_pod_combat_state { get; set; } = PodCombatStateType.None; + + public TpsFfaPodStorageInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId = 0) + : base(GameModeType.TPS_FFA, EGameModeObjectType.Pod_Combat, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + + } + + public TpsFfaPodStorageInteractionLogInfo(ILogInvoker parent, TpsFfaPodStorageInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.TPS_FFA, EGameModeObjectType.Pod_Combat) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + var log_info = logInfoBase as TpsFfaPodStorageInteractionLogInfo; + + if (log_info != null) + { + m_has_pod_combat = log_info.m_has_pod_combat; + m_pod_combat_pos = log_info.m_pod_combat_pos; + m_pod_combat_state = log_info.m_pod_combat_state; + m_pod_type = log_info.m_pod_type; + } + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaWeaponInteractionLogInfo.cs b/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaWeaponInteractionLogInfo.cs new file mode 100644 index 0000000..192d43e --- /dev/null +++ b/ServerCommon/1. Define/BusinessLog/GameMode/TpsFfa/TpsFfaWeaponInteractionLogInfo.cs @@ -0,0 +1,22 @@ +using MetaAssets; +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class TpsFfaWeaponInteractionLogInfo : GameObjectInteractionLogInfoBase +{ + public TpsFfaWeaponInteractionLogInfo(string roomId, string userGuid, string userNickname, string interactionAnchorGuid, int tableId = 0) + : base(GameModeType.TPS_FFA, EGameModeObjectType.Weapon, roomId, userGuid, userNickname, interactionAnchorGuid, tableId) + { + + } + + public TpsFfaWeaponInteractionLogInfo(ILogInvoker parent, TpsFfaWeaponInteractionLogInfo logInfo) + : base(parent, logInfo, GameModeType.TPS_FFA, EGameModeObjectType.Weapon) + { + } + + protected override void setLogInfoDetail(GameObjectInteractionLogInfoBase logInfoBase) + { + } +} \ No newline at end of file diff --git a/ServerCommon/1. Define/Constant.cs b/ServerCommon/1. Define/Constant.cs index 56c94d8..e585798 100644 --- a/ServerCommon/1. Define/Constant.cs +++ b/ServerCommon/1. Define/Constant.cs @@ -55,14 +55,14 @@ public static class Constant public static readonly int NOTICE_CHAT_UPDATE_TIME = 1000; - public static readonly int KEEP_INSTANCE_ROOM_TIME = (30 * 1000); + public static readonly int INSTANCE_ROOM_CACHE_REFRESH_INTERVAL_MSEC = (30 * 1000); public static readonly int KEEP_LAST_POSITION_TIME = (60 * 1000); public static readonly string BOT_CLIENT = "BotClient"; public static readonly int SEND_MAX_GRID_USER = 100; public static readonly int CHARACTER_RADIUS = 40; - public static readonly double WATIQUEUE_PERIOD_SECOND = 1; + public static readonly double WATIQUEUE_PERIOD_SECOND = 1; public static readonly string FRIEND_FOLDER_DEFUALT_NAME = "";//기본 폴더는 빈값으로; @@ -77,7 +77,7 @@ public static class Constant public static readonly int KEEP_PARTY_TIME = (5 * 60 * 1000); public static readonly int PARTY_INSTANCE_CREATE_ITEM_ID = 12080001; public static readonly int PARTY_SUMMON_LIMIT_DISTANCE = 5; - + public static readonly int UPDATE_SESSION_TIME = 1; //seconds public static readonly int DRESS_ROOM_INSTANCE_META_ID = 10002; @@ -94,18 +94,23 @@ public static class Constant public static readonly string PREFIX_EDIT_ROOM_INSTANCE_ROOM_ID = "editroom"; public static readonly string PREFIX_PARTY_INSTANCE_ROOM_ID = "party"; + public static readonly string PREFIX_GAME_INSTANCE_ROOM_ID = "game"; + public static readonly string POSTFIX_ONLY_ONE_INSTANCE_ROOM_ID = "onlyone"; public static readonly int NORMAL_QUEST_CHECK_INTERVAL_MSEC = 1_000; public static readonly int LARGE_PACKET_CHECK_INTERVAL_MSEC = 300; public static readonly int LARGE_PACKET_CHECK_WAIT_SEC = 10; //라지 패킷 대기시간 10초 - + public static readonly bool IS_LARGE_PACKET_PROTOCOL_ACTIVE = true; //false면 라지 패킷 미사용 //이 값은 GameConfig같은 걸로 빼서 클라랑 같이 써야될수 있다. public static readonly Int32 MAX_PACKET_SIZE = 256_000; - + public static readonly int SYSTEM_MAIL_CHECK_INTERVAL_MSEC = 60_000; public static readonly int P2P_PACKET_DATA_CHECK_INTERVAL_MSEC = 30_000; + public static readonly int MATCH_SERVER_LIST_INTERVAL_MSEC = 10_000; + public static readonly string MATCH_ROOM_PREFIX = "game"; + public static readonly int MYHOME_WARP_ID = 15; public static readonly int DRESSROOM_WARP_ID = 16; public static readonly int EDITROOM_WARP_ID = 170018; @@ -122,7 +127,7 @@ public static class Constant public static readonly string UGQ_INPUT_DATA_TYPE_NAME_ATTRIBUTE = "UGQInput_Dialog_Attribute"; public static readonly string UGQ_INPUT_DATA_TYPE_NAME_EMOTE = "UGQInput_Dialog_Emote"; public static readonly string UGQ_INPUT_DATA_TYPE_NAME_ITEM = "UGQInput_Dialog_Item"; - + public static readonly int DEFAULT_SOCIAL_ACTION_WEIGHT = 1; public static readonly int HABIT_SOCIAL_ACTION_WEIGHT = 2; @@ -143,4 +148,11 @@ public static class Constant public static readonly int LAND_AUCTION_RESERVATION_CONFIGURE_INTERVAL_MSEC = 3 * 60_000; public static readonly int LAND_AUCTION_CHECK_INTERVAL_MSEC = 1 * 60_000; + + public static readonly int RANKING_SCHESULE_CHECK_INTERVAL_MSEC = 60_000; + + public static readonly int RANKING_CHECK_INTERVAL_MSEC = 60_000; + + // 게임 이벤트 업데이트 체크 간격 + public static readonly int GAME_EVENT_CHECK_INTERVAL = 30_000;// 30초 } diff --git a/ServerCommon/1. Define/DatabaseDefine.cs b/ServerCommon/1. Define/DatabaseDefine.cs index a38c978..4bfa75c 100644 --- a/ServerCommon/1. Define/DatabaseDefine.cs +++ b/ServerCommon/1. Define/DatabaseDefine.cs @@ -14,6 +14,8 @@ public static class DynamoDbDefine { } +// HANDOVER: 메타버스 DynamoDB 메인 테이블에 저장되는 스키마 정의 + /*=================================================================================================================================================================================================================================================== * Dynamo DB DocType 모델 정의 (DynamoDB DocType Model Define) diff --git a/ServerCommon/1. Define/TypeDefine.cs b/ServerCommon/1. Define/TypeDefine.cs index 39894c8..6428646 100644 --- a/ServerCommon/1. Define/TypeDefine.cs +++ b/ServerCommon/1. Define/TypeDefine.cs @@ -422,24 +422,7 @@ public enum StageMoveType } -public enum GameGenreType -{ - None = 0, - - TPS = 1, - Running = 2, - //Dance = 3, -} -public enum GameModeType -{ - None = 0, - - TPS_FFA = 1, - TPS_TDM = 2, - RUN_ADV = 3, - RUN_RACE = 4, - //DANCE_ = 5, -} + //============================================================================================= // Broker Api 에서 사용하는 아이템 교환 Type diff --git a/ServerCommon/BusinessLog/LogActor/RankingActorLog.cs b/ServerCommon/BusinessLog/LogActor/RankingActorLog.cs new file mode 100644 index 0000000..be0f8fc --- /dev/null +++ b/ServerCommon/BusinessLog/LogActor/RankingActorLog.cs @@ -0,0 +1,51 @@ +using Newtonsoft.Json; +using ServerBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using WORLD_ID = System.UInt32; + +namespace ServerCommon +{ + public class RankingActorLog : ILogActor + { + [JsonProperty] + public string RegionId { get; private set; } = string.Empty; + [JsonProperty] + public WORLD_ID WorldId { get; private set; } = 0; + [JsonProperty] + public ServerType ServerType { get; private set; } = ServerType.None; + [JsonProperty] + public string RankingGuid { get; private set; } = string.Empty; + [JsonProperty] + public int RankingMetaId { get; private set; } = 0; + + public void initLogInfo(string regionId, WORLD_ID worldId, ServerType serverType, string rankingGuid, int rankingMetaId) + { + RegionId = regionId; + WorldId = worldId; + ServerType = serverType; + RankingGuid = rankingGuid; + RankingMetaId = rankingMetaId; + } + + public void setLogInfo(RankingActorLog logInfo) + { + RegionId = logInfo.RegionId; + WorldId = logInfo.WorldId; + ServerType = logInfo.ServerType; + RankingGuid = logInfo.RankingGuid; + RankingMetaId = logInfo.RankingMetaId; + } + + public RankingActorLog() { } + + public RankingActorLog(RankingActorLog logInfo) + { + setLogInfo(logInfo); + } + } +} diff --git a/ServerCommon/BusinessLog/LogHelper/BusinessLogInvoker.cs b/ServerCommon/BusinessLog/LogHelper/BusinessLogInvoker.cs new file mode 100644 index 0000000..cee48ee --- /dev/null +++ b/ServerCommon/BusinessLog/LogHelper/BusinessLogInvoker.cs @@ -0,0 +1,61 @@ +using Newtonsoft.Json; +using ServerBase; +using ServerCommon; + +namespace ServerCommon.BusinessLogDomain; + +/// +/// 비즈니스 로그 생성을 간단하게 하기 위해서 만든 추상 클래스 +/// +public abstract class LogInfoWithInvoker : ILogInvoker.IInfo +{ + protected LogInfoWithInvoker(ILogInvoker invoker) : base(invoker) + { + } +} + +/// +/// 비즈니스 로그 생성을 간단하게 하기 위해서 만든 LogInvoker +/// +public class BusinessLogInvoker : ILogInvokerEx +{ + private readonly ILogInvoker.IInfo _logData; + + public BusinessLogInvoker(LogDomainType logDomainType, Func creator) : + base(logDomainType) + { + _logData = creator(this); + } + + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(_logData); + } + + public override bool hasLog() + { + return true; + } + + /// + /// 로그 데이터를 json 스트링으로 변환해서 반환한다. + /// + /// + public override string ToString() + { + try + { + var str = JsonConvert.SerializeObject(_logData, Formatting.Indented); + return str; + // return System.Text.Json.JsonSerializer.Serialize(_logData, new System.Text.Json.JsonSerializerOptions + // { + // WriteIndented = false, + // PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase + // }); + } + catch (Exception ex) + { + return $"{{\"error\":\"Failed to serialize log data: {ex.Message}\"}}"; + } + } +} diff --git a/ServerCommon/BusinessLog/LogInvoker/BeaconCreateBusinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/BeaconCreateBusinessLog.cs index fb1683f..a0d9fd2 100644 --- a/ServerCommon/BusinessLog/LogInvoker/BeaconCreateBusinessLog.cs +++ b/ServerCommon/BusinessLog/LogInvoker/BeaconCreateBusinessLog.cs @@ -6,7 +6,6 @@ using System.Text; using System.Threading.Tasks; -using ServerCore; using ServerBase; diff --git a/ServerCommon/BusinessLog/LogInvoker/CaliumBusinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/CaliumBusinessLog.cs index 16dd658..e3f1900 100644 --- a/ServerCommon/BusinessLog/LogInvoker/CaliumBusinessLog.cs +++ b/ServerCommon/BusinessLog/LogInvoker/CaliumBusinessLog.cs @@ -24,7 +24,7 @@ public class CaliumBusinessLog : ILogInvokerEx public override bool hasLog() => true; - protected override void fillup(ref BusinessLog.LogBody body) + protected override void fillup(ref ServerBase.BusinessLog.LogBody body) { body.append(new CaliumConverterLogData(this, _converterLogData)); } diff --git a/ServerCommon/BusinessLog/LogInvoker/GameModeMatchRegulationBusinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/GameModeMatchRegulationBusinessLog.cs new file mode 100644 index 0000000..499de7a --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/GameModeMatchRegulationBusinessLog.cs @@ -0,0 +1,23 @@ +using ServerBase; +namespace ServerCommon.BusinessLogDomain; + + +public class GameModeMatchRegulationBusinessLog : ILogInvokerEx +{ + private GameModeMatchRegulationLogInfo m_log_data; + + public GameModeMatchRegulationBusinessLog(GameModeMatchRegulationLogInfo logInfo) : base(LogDomainType.GameModePlayRegulation) + { + m_log_data = logInfo; + } + + public override bool hasLog() + { + return true; + } + + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(new GameModeMatchRegulationLogInfo(this, m_log_data)); + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/GameModePlayerRegulationBusinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/GameModePlayerRegulationBusinessLog.cs new file mode 100644 index 0000000..dcc4d78 --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/GameModePlayerRegulationBusinessLog.cs @@ -0,0 +1,24 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class GameModePlayerRegulationBusinessLog : ILogInvokerEx +{ + private GameModePlayerRegulationLogInfo m_log_data; + + public GameModePlayerRegulationBusinessLog(GameModePlayerRegulationLogInfo logData) + :base(LogDomainType.GameModePlayRegulation) + { + m_log_data = logData; + } + + public override bool hasLog() + { + return true; + } + + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(new GameModePlayerRegulationLogInfo(this, m_log_data)); + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/GameObjectInteractionBusinessLogBase.cs b/ServerCommon/BusinessLog/LogInvoker/GameObjectInteractionBusinessLogBase.cs new file mode 100644 index 0000000..c2ad31c --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/GameObjectInteractionBusinessLogBase.cs @@ -0,0 +1,22 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class GameObjectInteractionBusinessLogBase : ILogInvokerEx +{ + protected IGameObjectInteractionLogInfo m_info; + + public GameObjectInteractionBusinessLogBase(IGameObjectInteractionLogInfo info) : base(LogDomainType.GameObjectInteraction) + { + m_info = info; + } + + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + //하위에서 구현해야된다. + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/RankerBusinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/RankerBusinessLog.cs new file mode 100644 index 0000000..bac9d24 --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/RankerBusinessLog.cs @@ -0,0 +1,31 @@ +using ServerBase; +using System; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon.BusinessLogDomain +{ + public class RankerBusinessLog : ILogInvokerEx + { + private RankerLogInfo m_data_to_log; + + public RankerBusinessLog(RankerLogInfo logParam) + : base(LogDomainType.Ranking) + { + m_data_to_log = new RankerLogInfo(this, logParam); + } + + public override bool hasLog() + { + return true; + } + + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(m_data_to_log); + } + } +} diff --git a/ServerCommon/BusinessLog/LogInvoker/RankingBusinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/RankingBusinessLog.cs new file mode 100644 index 0000000..d482fff --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/RankingBusinessLog.cs @@ -0,0 +1,30 @@ +using ServerBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon.BusinessLogDomain +{ + public class RankingBusinessLog : ILogInvokerEx + { + private RankingLogInfo m_data_to_log; + + public RankingBusinessLog(RankingLogInfo logParam) + : base(LogDomainType.Ranking) + { + m_data_to_log = new RankingLogInfo(this, logParam); + } + + public override bool hasLog() + { + return true; + } + + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(m_data_to_log); + } + } +} diff --git a/ServerCommon/BusinessLog/LogInvoker/RunPracticeBuffInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/RunPracticeBuffInteractionBuisinessLog.cs new file mode 100644 index 0000000..b5ef8d8 --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/RunPracticeBuffInteractionBuisinessLog.cs @@ -0,0 +1,22 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class RunPracticeBuffInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + public RunPracticeBuffInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var log_info = m_info as RunPracticeBuffInteractionLogInfo; + if (log_info is not null) + { + body.append(new RunPracticeBuffInteractionLogInfo(this, log_info)); + } + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/RunPracticeCheckPointInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/RunPracticeCheckPointInteractionBuisinessLog.cs new file mode 100644 index 0000000..b48855d --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/RunPracticeCheckPointInteractionBuisinessLog.cs @@ -0,0 +1,22 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class RunPracticeCheckPointInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + public RunPracticeCheckPointInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var log_info = m_info as RunPracticeCheckPointInteractionLogInfo; + if (log_info is not null) + { + body.append(new RunPracticeCheckPointInteractionLogInfo(this, log_info)); + } + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/RunPracticeGoalLineInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/RunPracticeGoalLineInteractionBuisinessLog.cs new file mode 100644 index 0000000..bd96ca6 --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/RunPracticeGoalLineInteractionBuisinessLog.cs @@ -0,0 +1,24 @@ + + +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class RunPracticeGoalLineInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + public RunPracticeGoalLineInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var log_info = m_info as RunPracticeGoalLineInteractionLogInfo; + if (log_info is not null) + { + body.append(new RunPracticeGoalLineInteractionLogInfo(this, log_info)); + } + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/RunPracticePlayBusinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/RunPracticePlayBusinessLog.cs new file mode 100644 index 0000000..7a2f0b0 --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/RunPracticePlayBusinessLog.cs @@ -0,0 +1,22 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class RunPracticePlayBusinessLog : ILogInvokerEx +{ + private GameModeRunPracticePlayLogInfo m_info; + public RunPracticePlayBusinessLog(GameModeRunPracticePlayLogInfo logInfo) : base(LogDomainType.RunPracticePlaySummary) + { + m_info = logInfo; + } + + protected override void fillup(ref BusinessLog.LogBody body) + { + body.append(new GameModeRunPracticePlayLogInfo(this, m_info)); + } + + public override bool hasLog() + { + return true; + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/RunRaceBuffInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/RunRaceBuffInteractionBuisinessLog.cs new file mode 100644 index 0000000..546d4ea --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/RunRaceBuffInteractionBuisinessLog.cs @@ -0,0 +1,22 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class RunRaceBuffInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + public RunRaceBuffInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var log_info = m_info as RunRaceBuffInteractionLogInfo; + if (log_info is not null) + { + body.append(new RunRaceBuffInteractionLogInfo(this, log_info)); + } + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/RunRaceCheckPointInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/RunRaceCheckPointInteractionBuisinessLog.cs new file mode 100644 index 0000000..0a85258 --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/RunRaceCheckPointInteractionBuisinessLog.cs @@ -0,0 +1,22 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class RunRaceCheckPointInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + public RunRaceCheckPointInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var log_info = m_info as RunRaceCheckPointInteractionLogInfo; + if (log_info is not null) + { + body.append(new RunRaceCheckPointInteractionLogInfo(this, log_info)); + } + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/RunRaceGoalLineInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/RunRaceGoalLineInteractionBuisinessLog.cs new file mode 100644 index 0000000..1dca2dd --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/RunRaceGoalLineInteractionBuisinessLog.cs @@ -0,0 +1,22 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class RunRaceGoalLineInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + public RunRaceGoalLineInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var log_info = m_info as RunRaceGoalLineInteractionLogInfo; + if (log_info is not null) + { + body.append(new RunRaceGoalLineInteractionLogInfo(this, log_info)); + } + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/TpsFfaBuffInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/TpsFfaBuffInteractionBuisinessLog.cs new file mode 100644 index 0000000..6c72515 --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/TpsFfaBuffInteractionBuisinessLog.cs @@ -0,0 +1,25 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class TpsFfaBuffInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + + public TpsFfaBuffInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var tpf_ffa_buff_log_info = m_info as TpsFfaBuffInteractionLogInfo; + if (tpf_ffa_buff_log_info is not null) + { + body.append(new TpsFfaBuffInteractionLogInfo(this, tpf_ffa_buff_log_info)); + } + } + + +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/TpsFfaCombatPodInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/TpsFfaCombatPodInteractionBuisinessLog.cs new file mode 100644 index 0000000..b112d34 --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/TpsFfaCombatPodInteractionBuisinessLog.cs @@ -0,0 +1,23 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class TpsFfaCombatPodInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + + public TpsFfaCombatPodInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var log_info = m_info as TpsFfaCombatPodInteractionLogInfo; + if (log_info is not null) + { + body.append(new TpsFfaCombatPodInteractionLogInfo(this, log_info)); + } + } +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/TpsFfaPickupPodInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/TpsFfaPickupPodInteractionBuisinessLog.cs new file mode 100644 index 0000000..0bf3a64 --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/TpsFfaPickupPodInteractionBuisinessLog.cs @@ -0,0 +1,25 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class TpsFfaPickupPodInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + + public TpsFfaPickupPodInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var log_info = m_info as TpsFfaPickupPodInteractionLogInfo; + if (log_info is not null) + { + body.append(new TpsFfaPickupPodInteractionLogInfo(this, log_info)); + } + } + + +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/TpsFfaPodStorageInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/TpsFfaPodStorageInteractionBuisinessLog.cs new file mode 100644 index 0000000..a39dfff --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/TpsFfaPodStorageInteractionBuisinessLog.cs @@ -0,0 +1,25 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class TpsFfaPodStorageInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + + public TpsFfaPodStorageInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var log_info = m_info as TpsFfaPodStorageInteractionLogInfo; + if (log_info is not null) + { + body.append(new TpsFfaPodStorageInteractionLogInfo(this, log_info)); + } + } + + +} \ No newline at end of file diff --git a/ServerCommon/BusinessLog/LogInvoker/TpsFfaWeaponInteractionBuisinessLog.cs b/ServerCommon/BusinessLog/LogInvoker/TpsFfaWeaponInteractionBuisinessLog.cs new file mode 100644 index 0000000..2524c8a --- /dev/null +++ b/ServerCommon/BusinessLog/LogInvoker/TpsFfaWeaponInteractionBuisinessLog.cs @@ -0,0 +1,23 @@ +using ServerBase; + +namespace ServerCommon.BusinessLogDomain; + +public class TpsFfaWeaponInteractionBuisinessLog : GameObjectInteractionBusinessLogBase +{ + + public TpsFfaWeaponInteractionBuisinessLog(IGameObjectInteractionLogInfo info) : base(info) + { + } + public override bool hasLog() + { + return true; + } + protected override void fillup(ref BusinessLog.LogBody body) + { + var tpf_ffa_weapon_log_info = m_info as TpsFfaWeaponInteractionLogInfo; + if (tpf_ffa_weapon_log_info is not null) + { + body.append(new TpsFfaWeaponInteractionLogInfo(this, tpf_ffa_weapon_log_info)); + } + } +} \ No newline at end of file diff --git a/ServerCommon/Cache/LocationCacheRequest.cs b/ServerCommon/Cache/LocationCacheRequest.cs index 52c408a..5ae5eac 100644 --- a/ServerCommon/Cache/LocationCacheRequest.cs +++ b/ServerCommon/Cache/LocationCacheRequest.cs @@ -19,9 +19,12 @@ namespace ServerCommon public class LocationCache : CacheBase, ICopyCacheFromEntityAttribute { public ChannelServerLocation LastestChannelServerLocation = new(); + public List ReturnIndunLocations = new(); public IndunLocation EnterIndunLocation = new(); + public Timestamp MoveChannelTime = DateTimeHelper.MinTime.ToTimestamp(); + public bool MoveToAccountStartPosAtLogin = false; public LocationCache() : base() @@ -45,9 +48,12 @@ namespace ServerCommon // Attribute => Cache //===================================================================================== LastestChannelServerLocation = location_attribute.LastestChannelServerLocation.clone(); + ReturnIndunLocations = location_attribute.ReturnIndunLocations.Select(x => x.clone()).ToList(); EnterIndunLocation = location_attribute.EnterIndunLocation.clone(); + MoveChannelTime = location_attribute.MoveChannelTime; + MoveToAccountStartPosAtLogin = location_attribute.MoveToAccountStartPosAtLogin; return true; } @@ -71,10 +77,7 @@ namespace ServerCommon public LocationCacheRequest(UserBase owner, RedisConnector redisConnector) : base(owner, redisConnector) { - var user_attribute = owner.getEntityAttribute(); - NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user_attribute is null !!! - {owner.toBasicString()}"); - - m_user_guid = user_attribute.UserGuid; + m_user_guid = owner.getUserGuid(); } public override string toBasicString() diff --git a/ServerCommon/Cache/RankingCacheRequest.cs b/ServerCommon/Cache/RankingCacheRequest.cs new file mode 100644 index 0000000..1b6e446 --- /dev/null +++ b/ServerCommon/Cache/RankingCacheRequest.cs @@ -0,0 +1,230 @@ +using ServerBase; +using ServerCore; +using StackExchange.Redis; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankingCacheRequest : RedisRequestSharedBase + { + string m_rank_key = string.Empty; + + public RankingCacheRequest(string rankingGuid, RedisConnector redisConnector) + : base(rankingGuid, redisConnector) + { + m_rank_key = rankingGuid; + } + + protected override string onMakeKey() + { + return $"Ranking:{m_rank_key}"; + } + + public override string toBasicString() + { + return $"Ranking:{m_rank_key}"; + } + + public async Task increaseRankScore(string rankerGuid, int deltaScore) + { + var result = new Result(); + var err_msg = string.Empty; + + try + { + result = await onPrepareRequest(); + if (result.isFail()) + { + err_msg = $"Failed to onPrepareRequest() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var database = getDatabase(); + await database.SortedSetIncrementAsync(getKey(), rankerGuid, deltaScore); + + return result; + } + catch (Exception e) + { + err_msg = $"Failed to SortedSetIncrementAsync() in Redis !!! : message{e}"; + result.setFail(ServerErrorCode.TryCatchException, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + } + + public async Task decreaseRankScore(string rankerGuid, int deltaScore) + { + var result = new Result(); + var err_msg = string.Empty; + + try + { + result = await onPrepareRequest(); + if (result.isFail()) + { + err_msg = $"Failed to onPrepareRequest() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var database = getDatabase(); + await database.SortedSetDecrementAsync(getKey(), rankerGuid, deltaScore); + + return result; + } + catch (Exception e) + { + err_msg = $"Failed to SortedSetDecrementAsync() in Redis !!! : message{e}"; + result.setFail(ServerErrorCode.TryCatchException, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + } + + public async Task setRankScore(string rankerGuid, double score) + { + var result = new Result(); + var err_msg = string.Empty; + + try + { + result = await onPrepareRequest(); + if (result.isFail()) + { + err_msg = $"Failed to onPrepareRequest() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var database = getDatabase(); + await database.SortedSetAddAsync(getKey(), rankerGuid, score); + + return result; + } + catch (Exception e) + { + err_msg = $"Failed to SortedSetAddAsync() in Redis !!! : message{e}"; + result.setFail(ServerErrorCode.TryCatchException, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + } + + public async Task<(Result, List<(int, string, int)>)> getRedisRanks(int startRankNum, int endRankNum, SortType sortType) + { + var result = new Result(); + var err_msg = string.Empty; + + var ranks = new List<(int, string, int)>(); + + try + { + result = await onPrepareRequest(); + if (result.isFail()) + { + err_msg = $"Failed to onPrepareRequest() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, ranks); + } + + var database = getDatabase(); + var sorted_set_entries = await database.SortedSetRangeByRankWithScoresAsync(getKey(), startRankNum, endRankNum, sortType == SortType.Ascending ? Order.Ascending : Order.Descending); + + var rank_num = startRankNum + 1; + foreach (var sorted_set_entry in sorted_set_entries) + { + ranks.Add((rank_num, sorted_set_entry.Element.ToString(), (int)sorted_set_entry.Score)); + ++rank_num; + } + + return (result, ranks); + } + catch (Exception e) + { + err_msg = $"Failed to SortedSetRangeByRankWithScoresAsync() in Redis !!! : message{e}"; + result.setFail(ServerErrorCode.TryCatchException, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, ranks); + } + } + + public async Task<(Result, int)> getRedisRank(string rankerguid, SortType sortType) + { + var result = new Result(); + var err_msg = string.Empty; + + var rank = 0; + + try + { + result = await onPrepareRequest(); + if (result.isFail()) + { + err_msg = $"Failed to onPrepareRequest() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return (result, rank); + } + + var database = getDatabase(); + var redis_rank = await database.SortedSetRankAsync(getKey(), rankerguid, sortType == SortType.Ascending ? Order.Ascending : Order.Descending); + rank = (int)redis_rank + 1; + + return (result, rank); + } + catch (Exception e) + { + err_msg = $"Failed to SortedSetRankAsync() in Redis !!! : message{e}"; + result.setFail(ServerErrorCode.TryCatchException, err_msg); + Log.getLogger().error(result.toBasicString()); + + return (result, rank); + } + } + + public async Task removeRank(string rankerGuid) + { + var result = new Result(); + var err_msg = string.Empty; + + try + { + result = await onPrepareRequest(); + if (result.isFail()) + { + err_msg = $"Failed to onPrepareRequest() !!! : {result.toBasicString()}"; + Log.getLogger().error(err_msg); + + return result; + } + + var database = getDatabase(); + await database.SortedSetRemoveAsync(getKey(), rankerGuid); + + return result; + } + catch (Exception e) + { + err_msg = $"Failed to SortedSetAddAsync() in Redis !!! : message{e}"; + result.setFail(ServerErrorCode.TryCatchException, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + } + } +} diff --git a/ServerCommon/Contents/GameEvent/GameEvent.cs b/ServerCommon/Contents/GameEvent/GameEvent.cs new file mode 100644 index 0000000..7414198 --- /dev/null +++ b/ServerCommon/Contents/GameEvent/GameEvent.cs @@ -0,0 +1,21 @@ +using ServerCore; + +namespace ServerCommon; + +public class GameEvent +{ + public int EventId { get; set; } = 0; + public bool IsActive { get; set; } + public DateTime ReadyTime { get; set; } = DateTimeHelper.Current; + public bool IsSendNoti { get; set; } = false; + public int GameModeId { get; set; } = 1; + public DateTime StartTime { get; set; } = DateTimeHelper.Current; + public DateTime EndTime { get; set; } = DateTimeHelper.Current; + public int FfaHotTime { get; set; } = 1; + + public override string ToString() + { + return + $"GameEvent - EventId: {EventId}, GameModeId: {GameModeId}, StartTime: {StartTime}, EndTime: {EndTime}, FfaHotTime: {FfaHotTime}"; + } +} diff --git a/ServerCommon/Contents/GameEvent/GameEventDatabaseLoader.cs b/ServerCommon/Contents/GameEvent/GameEventDatabaseLoader.cs new file mode 100644 index 0000000..d11fbcf --- /dev/null +++ b/ServerCommon/Contents/GameEvent/GameEventDatabaseLoader.cs @@ -0,0 +1,70 @@ +using ServerBase; +using ServerCore; + +namespace ServerCommon; + +/// +/// 데이터베이스에서 게임 이벤트 정보를 로드하는 클래스 +/// 데이터베이스 연동 로직을 분리하여 단일 책임 원칙을 준수합니다. +/// +public class GameEventDatabaseLoader +{ + /// DynamoDB 클라이언트 - 데이터베이스 연동용 + private readonly DynamoDbClient _dbClient; + + /// 로거 - 시스템 로그 기록용 + private readonly NLog.Logger _logger; + + /// + /// EventDatabaseLoader 생성자 + /// + /// DynamoDB 클라이언트 인스턴스 + /// dynamoDbClient가 null인 경우 + public GameEventDatabaseLoader(DynamoDbClient dynamoDbClient) + { + _dbClient = dynamoDbClient ?? throw new ArgumentNullException(nameof(dynamoDbClient)); + _logger = Log.getLogger(); + } + + /// + /// 데이터베이스에서 게임 이벤트 속성들을 비동기적으로 로드합니다. + /// DynamoDB에서 BattleEvent 관련 모든 문서를 조회하여 BattleEventAttrib 객체로 변환합니다. + /// + /// 로드된 이벤트 속성 리스트와 결과 + public async Task<(Result result, List eventAttribs)> LoadEventsFromDatabaseAsync() + { + // _logger.Info("Starting to load events from database"); + + // DynamoDB 쿼리 설정 생성 + var doc = new GameEventDoc(); + var queryConfig = _dbClient.makeQueryConfigForReadByPKOnly(doc.getPK()); + + // 실제 데이터베이스 쿼리 실행 + var (result, readDocs) = await _dbClient.simpleQueryDocTypesWithQueryOperationConfig(queryConfig); + + // 쿼리 실패 시 에러 반환 + if (result.isFail()) + { + _logger.Error($"Failed to load events from database: {result.ResultString}"); + return (result, new List()); + } + + // GameEventDoc을 BattleEventAttrib로 변환 + var eventAttribs = new List(); + foreach (var readDoc in readDocs) + { + var eventAttrib = readDoc.getAttrib(); + if (eventAttrib != null) + { + eventAttribs.Add(eventAttrib); + } + else + { + _logger.Warn($"Failed to convert document to BattleEventAttrib: {readDoc}"); + } + } + + // _logger.Info($"Successfully loaded {eventAttribs.Count} events from database"); + return (new Result(), eventAttribs); + } +} diff --git a/ServerCommon/Contents/GameEvent/GameEventManager.cs b/ServerCommon/Contents/GameEvent/GameEventManager.cs new file mode 100644 index 0000000..ded41b8 --- /dev/null +++ b/ServerCommon/Contents/GameEvent/GameEventManager.cs @@ -0,0 +1,562 @@ +using System.Collections.Concurrent; +using MetaAssets; +using ServerBase; +using ServerCore; + +namespace ServerCommon; + +/// +/// 게임 이벤트 관리를 위한 클래스 (개선된 버전) +/// 운영툴의 게임 이벤트 설정을 캐시하고 관리합니다. +/// +/// 주요 기능: +/// - 데이터베이스에서 이벤트 정보 로드 및 캐싱 +/// - 이벤트 시간 유효성 검증 +/// - 이벤트 변경사항 자동 감지 및 업데이트 +/// - 스레드 안전한 이벤트 캐시 관리 +/// +public class GameEventManager +{ + /// DynamoDB 클라이언트 - 데이터베이스 연동용 + private readonly DynamoDbClient _dbClient; + + /// 로거 - 시스템 로그 기록용 + private readonly NLog.Logger _logger; + + /// 게임 이벤트 캐시 - 스레드 안전한 딕셔너리 + private readonly ConcurrentDictionary _eventCache = new(); + private readonly List _eventDocCache = new(); + + /// 이벤트 시간 계산 담당 클래스 + private readonly EventTimeCalculator _timeCalculator; + + /// 이벤트 유효성 검증 담당 클래스 + private readonly EventValidator _validator; + + /// + /// 게임 이벤트 적용 여부를 결정하는 플래그 + /// false일 경우 모든 이벤트 시간 검증을 통과시킵니다. + /// 개발/테스트 환경에서 이벤트 시간 제약을 무시하고 싶을 때 사용합니다. + /// + public bool ApplyGameEvent { get; set; } = true; + + /// + /// 게임 이벤트 업데이트 시 호출되는 콜백 함수 + /// 이벤트가 추가/수정/삭제될 때마다 호출되어 다른 시스템에 변경사항을 알립니다. + /// + public Action>? OnUpdateGameEvent { get; set; } + + /// + /// GameEventManager2 생성자 + /// 필요한 의존성들을 주입받고 내부 서비스 클래스들을 초기화합니다. + /// + /// DynamoDB 클라이언트 인스턴스 + /// dynamoDbClient가 null인 경우 + public GameEventManager(DynamoDbClient dynamoDbClient) + { + // 의존성 검증 - null 체크를 통해 런타임 오류 방지 + _dbClient = dynamoDbClient ?? throw new ArgumentNullException(nameof(dynamoDbClient)); + _logger = Log.getLogger(); + + // 내부 서비스 클래스들 초기화 + _timeCalculator = new EventTimeCalculator(); + _validator = new EventValidator(); + + _logger.Info("GameEventManager2 initialized successfully"); + } + + /// + /// 지정된 이벤트 ID로 게임 이벤트를 조회합니다. + /// 캐시에서 빠르게 조회하므로 성능상 오버헤드가 거의 없습니다. + /// + /// 조회할 이벤트 ID + /// 찾은 게임 이벤트 객체, 없으면 null + public GameEvent? GetGameEvent(int eventId) + { + return _eventCache.GetValueOrDefault(eventId); + } + + /// + /// 지정된 이벤트 ID의 현재 시간 유효성을 검사합니다. + /// 이벤트 시간 범위와 버퍼를 고려하여 현재 참여 가능한지 판단합니다. + /// + /// 검사할 이벤트 ID + /// 이벤트 시간 오차 방지를 위한 버퍼 초 (기본값: 60초) + /// 기준 시간 (기본값: 현재 시간) + /// 유효한 시간이면 true, 그렇지 않으면 false + public bool IsValidGameEventTime(int eventId, int bufferSeconds = 60, DateTime? now = null) + { + // 게임 이벤트가 비활성화된 경우 항상 유효하다고 반환 (개발/테스트 모드) + if (!ApplyGameEvent) return true; + + // 기준 시간 설정 - null인 경우 현재 시간 사용 + var currentTime = now ?? DateTimeHelper.Current; + + // 캐시에서 이벤트를 찾아 시간 유효성 검증 + return _eventCache.TryGetValue(eventId, out var gameEvent) && + _validator.IsEventTimeValid(gameEvent, bufferSeconds, currentTime); + } + + /// + /// 데이터베이스에서 이벤트 정보를 새로고침합니다. + /// 주기적으로 호출되어 운영툴에서 변경된 이벤트 설정을 반영합니다. + /// + /// 작업 결과 - 성공/실패 정보 포함 + public async Task RefreshEvent() + { + // 1. 데이터베이스에서 최신 이벤트 문서들을 로드 + var (result, eventDocs) = await LoadEventsFromDatabaseAsync(); + + if (result.isFail()) return result; + + await UpdateEventCacheAsync(eventDocs); + _logger.Info($"Event refresh completed successfully. Total events: {eventDocs.Count}"); + return new Result(); // 성공 + } + + /// + /// 현재 캐시된 모든 이벤트 정보를 문자열로 반환합니다. + /// 디버깅 및 모니터링 목적으로 사용됩니다. + /// + /// 이벤트 정보 문자열 (없으면 "No active events") + public override string ToString() + { + return _eventCache.Count == 0 + ? "No active events" + : string.Join("\n", _eventCache.Values.Select(x => x.ToString())); + } + + /// + /// 데이터베이스에서 게임 이벤트 문서들을 비동기적으로 로드합니다. + /// DynamoDB에서 BattleEvent 관련 모든 문서를 조회합니다. + /// + /// 로드된 이벤트 문서 리스트 + /// 데이터베이스 쿼리 실패 시 + private async Task<(Result, List)> LoadEventsFromDatabaseAsync() + { + // DynamoDB 쿼리 설정 생성 + var doc = new GameEventDoc(); + var queryConfig = _dbClient.makeQueryConfigForReadByPKOnly(doc.getPK()); + + // 실제 데이터베이스 쿼리 실행 + var (result, readDocList) = await _dbClient.simpleQueryDocTypesWithQueryOperationConfig(queryConfig); + + // 쿼리 실패 시 예외 발생 + if (result.isFail()) + { + return (result, new()); + } + return (result, readDocList); + } + + /// + /// 로드된 이벤트 문서들로 이벤트 캐시를 업데이트합니다. + /// 신규 이벤트 추가, 기존 이벤트 수정, 삭제된 이벤트 제거를 처리합니다. + /// + /// 데이터베이스에서 로드된 이벤트 문서 리스트 + private async Task UpdateEventCacheAsync(List eventDocs) + { + await Task.CompletedTask; // 비동기 시그니처 유지 (향후 확장 가능성) + + var now = DateTimeHelper.Current; + + // 현재 DB에 존재하는 이벤트 ID들을 추적 (삭제된 이벤트 감지용) + var incomingEventIds = new HashSet(eventDocs.Count); + var updatedEventIds = new List(); // 실제 변경된 이벤트 ID들 + + List newEventAttribs = new(); + + // 각 이벤트 문서를 처리하여 캐시 업데이트 + foreach (var doc in eventDocs) + { + var eventAttrib = doc.getAttrib(); + if (eventAttrib == null) continue; // 잘못된 문서 스킵 + + incomingEventIds.Add(eventAttrib.m_event_id); + newEventAttribs.Add(eventAttrib); + + // 이벤트 처리 후 실제 변경이 있었는지 확인 + if (ProcessEventDocument(eventAttrib, now)) + { + updatedEventIds.Add(eventAttrib.m_event_id); + } + } + + // DB에서 제거된 이벤트들을 캐시에서도 제거 + RemoveObsoleteEvents(incomingEventIds); + + // _eventDocCache 업데이트 + _eventDocCache.Clear(); + _eventDocCache.AddRange(newEventAttribs); + + // 변경사항이 있는 경우 콜백 호출 + if (updatedEventIds.Count > 0) + { + OnUpdateGameEvent?.Invoke(_eventCache.Values); + // _logger.Info($"[GameEventManager] Updated event IDs: [{string.Join(", ", updatedEventIds)}]"); + } + + // _logger.Info($"Event cache updated. Current event count: {_eventCache.Count}"); + } + + /// + /// 개별 이벤트 문서를 처리하여 캐시에 추가하거나 업데이트합니다. + /// 이벤트 시간 계산, 변경사항 감지, 캐시 업데이트를 담당합니다. + /// + /// 처리할 이벤트 속성 + /// 현재 시간 + /// 실제로 이벤트가 변경되었으면 true, 그렇지 않으면 false + private bool ProcessEventDocument(BattleEventAttrib attrib, DateTime now) + { + try + { + // 1. 이벤트 시간 범위 계산 + var eventTimes = _timeCalculator.CalculateEventTimes(attrib, now); + if (eventTimes.Count == 0) return false; // 유효한 시간이 없으면 스킵 + + // 2. 첫 번째 유효한 시간으로 GameEvent 객체 생성 + var (startTime, endTime) = eventTimes[0]; + var newGameEvent = EventFactory.Create(attrib, startTime, endTime); + + // 3. 기존 이벤트와 비교하여 변경사항 확인 (IsEventAttributeChanged 사용) + var existingEvent = _eventCache.GetValueOrDefault(attrib.m_event_id); + bool hasChanges = false; + + if (existingEvent == null) + { + hasChanges = true; + } + else + { + // _eventDocCache에서 기존 BattleEventAttrib를 찾아서 비교 + var existingAttrib = _eventDocCache.FirstOrDefault(x => x.m_event_id == attrib.m_event_id); + if (existingAttrib != null) + { + hasChanges = EventChangeDetector.IsEventAttributeChanged(existingAttrib, attrib); + } + // else + // { + // // 기존 캐시에 없는 경우 GameEvent 객체 비교 사용 + // hasChanges = EventChangeDetector.HasEventChanged(existingEvent, newGameEvent); + // } + } + + // 4. 변경사항이 있는 경우 캐시 업데이트 + if (hasChanges) + { + // 스레드 안전하게 캐시 업데이트 (추가 또는 교체) + _eventCache.AddOrUpdate(attrib.m_event_id, newGameEvent, (key, oldValue) => newGameEvent); + + // var action = existingEvent == null ? "Added new" : "Updated existing"; + // _logger.Info($"[GameEventManager] {action} event: EventID {attrib.m_event_id}"); + return true; + } + + return false; // 변경사항 없음 + } + catch (Exception ex) + { + // 개별 이벤트 처리 실패 시 로그 기록 후 계속 진행 + _logger.Error($"Error processing event document for EventID {attrib.m_event_id}: {ex.Message}"); + return false; + } + } + + /// + /// 데이터베이스에서 제거되었거나 비활성화된 이벤트들을 캐시에서 제거합니다. + /// 메모리 누수 방지 및 정확한 이벤트 상태 유지를 위해 필요합니다. + /// + /// 현재 DB에 존재하는 이벤트 ID들 + private void RemoveObsoleteEvents(HashSet incomingEventIds) + { + var removedEventIds = new List(); + + // 현재 캐시의 스냅샷을 생성하여 반복 중 수정 방지 + var cachedEventIds = _eventCache.Keys.ToArray(); + + // 각 캐시된 이벤트가 여전히 활성 상태인지 확인 + foreach (var cachedEventId in cachedEventIds) + { + if (!incomingEventIds.Contains(cachedEventId)) + { + // 스레드 안전하게 캐시에서 제거 + if (_eventCache.TryRemove(cachedEventId, out _)) + { + removedEventIds.Add(cachedEventId); + } + } + } + + // 제거된 이벤트가 있는 경우 로그 기록 + if (removedEventIds.Count > 0) + { + _logger.Info($"[GameEventManager] Removed inactive events: [{string.Join(", ", removedEventIds)}]"); + } + } +} + +/// +/// 이벤트 시간 계산을 담당하는 내부 클래스 +/// 다양한 이벤트 주기 타입(일일, 주간, 월간)에 따라 정확한 이벤트 시간을 계산합니다. +/// +internal class EventTimeCalculator +{ + /// 이벤트 시간 계산 시 미래로 몇 일까지 고려할지 + private const int EVENT_TIME_LOOKAHEAD_DAYS = 1; + + /// 이벤트 시간 계산 시 과거로 몇 일까지 고려할지 + private const int EVENT_TIME_LOOKBACK_DAYS = -1; + + /// + /// 이벤트 속성과 현재 시간을 기반으로 이벤트 시간들을 계산합니다. + /// 이벤트 주기 타입에 따라 적절한 계산 메서드를 호출합니다. + /// + /// 이벤트 속성 (주기, 시작시간, 지속시간 등) + /// 현재 시간 + /// 계산된 시작시간과 종료시간 튜플 리스트 + public List<(DateTime startTime, DateTime endTime)> CalculateEventTimes(BattleEventAttrib attrib, DateTime now) + { + var today = now.Date; // 시간 부분을 제거한 오늘 날짜 + + // 이벤트 주기 타입에 따른 분기 처리 (C# 8.0 switch expression 사용) + return attrib.m_period switch + { + OncePeriodRangeType.NONE or OncePeriodRangeType.Daily => + CalculateDailyEventTimes(attrib, today, now), + OncePeriodRangeType.Weekly => + CalculateWeeklyEventTimes(attrib, today, now), + OncePeriodRangeType.Monthly => + CalculateMonthlyEventTimes(attrib, today, now), + _ => new List<(DateTime, DateTime)>() // 지원되지 않는 타입 + }; + } + + /// + /// 일일 이벤트의 시간들을 계산합니다. + /// 전날, 오늘, 내일의 이벤트 중 현재 시점에서 유효한 첫 번째 이벤트를 반환합니다. + /// + /// 이벤트 속성 + /// 오늘 날짜 (시간 제거됨) + /// 현재 시간 + /// 유효한 일일 이벤트 시간 리스트 + private List<(DateTime, DateTime)> CalculateDailyEventTimes(BattleEventAttrib attrib, DateTime today, DateTime now) + { + var times = new List<(DateTime, DateTime)>(3); // 최대 3개 (전날, 오늘, 내일) + + // 전날부터 내일까지의 이벤트 시간을 확인 + for (int dayOffset = EVENT_TIME_LOOKBACK_DAYS; dayOffset <= EVENT_TIME_LOOKAHEAD_DAYS; dayOffset++) + { + var targetDay = today.AddDays(dayOffset); + var startTime = CreateEventDateTime(targetDay, attrib.m_start_hour, attrib.m_start_min); + var endTime = startTime.AddMinutes(attrib.m_open_duration_minutes); + + // 이벤트 종료시간이 현재 시간보다 미래인 경우 (아직 유효한 이벤트) + if (endTime > now) + { + times.Add((startTime, endTime)); + break; // 첫 번째 유효한 이벤트만 반환 (일일 이벤트는 하나만 활성) + } + } + + return times; + } + + /// + /// 주간 이벤트의 시간들을 계산합니다. + /// 지정된 요일들에 대해 다음 발생 시점을 계산하여 반환합니다. + /// + /// 이벤트 속성 (요일 리스트 포함) + /// 오늘 날짜 + /// 현재 시간 + /// 유효한 주간 이벤트 시간 리스트 + private List<(DateTime, DateTime)> CalculateWeeklyEventTimes(BattleEventAttrib attrib, DateTime today, DateTime now) + { + var times = new List<(DateTime, DateTime)>(attrib.m_day_of_week_type.Count); + + // 설정된 각 요일에 대해 다음 이벤트 시간 계산 + foreach (var dayOfWeek in attrib.m_day_of_week_type) + { + // 오늘부터 해당 요일까지 남은 일수 계산 (0~6) + var daysUntilNext = ((int)dayOfWeek - (int)today.DayOfWeek + 7) % 7; + var eventDay = today.AddDays(daysUntilNext); + var startTime = CreateEventDateTime(eventDay, attrib.m_start_hour, attrib.m_start_min); + var endTime = startTime.AddMinutes(attrib.m_open_duration_minutes); + + // 이벤트가 유효한 시간 범위 내에 있는지 확인 + if (IsEventTimeInValidRange(startTime, now, attrib.m_end_date)) + { + times.Add((startTime, endTime)); + } + } + + return times; + } + + /// + /// 월간 이벤트의 시간들을 계산합니다. + /// 매월 지정된 날짜와 시간에 발생하는 이벤트를 계산합니다. + /// + /// 이벤트 속성 (시작 날짜 포함) + /// 오늘 날짜 + /// 현재 시간 + /// 유효한 월간 이벤트 시간 리스트 + private List<(DateTime, DateTime)> CalculateMonthlyEventTimes(BattleEventAttrib attrib, DateTime today, DateTime now) + { + var times = new List<(DateTime, DateTime)>(1); // 월간 이벤트는 보통 하나 + + // 이번 달의 지정된 날짜로 이벤트 시간 생성 + var monthlyStartTime = new DateTime( + today.Year, + today.Month, + attrib.m_start_day.Day, // 설정된 시작 날짜의 일(day) 부분 사용 + attrib.m_start_hour, + attrib.m_start_min, + 0 // 초는 0으로 고정 + ); + + var endTime = monthlyStartTime.AddMinutes(attrib.m_open_duration_minutes); + + // 이벤트가 유효한 시간 범위 내에 있는지 확인 + if (IsEventTimeInValidRange(monthlyStartTime, now, attrib.m_end_date)) + { + times.Add((monthlyStartTime, endTime)); + } + + return times; + } + + /// + /// 날짜에 시간을 추가하여 완전한 DateTime을 생성합니다. + /// 가독성과 재사용성을 위한 유틸리티 메서드입니다. + /// + /// 기본 날짜 + /// 추가할 시간 (0-23) + /// 추가할 분 (0-59) + /// 시간이 추가된 DateTime + private static DateTime CreateEventDateTime(DateTime date, int hour, int minute) + { + return date.AddHours(hour).AddMinutes(minute); + } + + /// + /// 이벤트 시작 시간이 유효한 범위 내에 있는지 확인합니다. + /// 현재 시간 이후이면서 이벤트 종료 날짜 이전이어야 합니다. + /// + /// 확인할 이벤트 시작 시간 + /// 현재 시간 + /// 이벤트 종료 날짜 + /// 유효한 범위 내에 있으면 true + private static bool IsEventTimeInValidRange(DateTime startTime, DateTime now, DateTime endDate) + { + return startTime >= now && endDate > now; + } +} + +/// +/// 이벤트 유효성 검증을 담당하는 내부 클래스 +/// 현재 시간이 이벤트 활성 시간 범위 내에 있는지 검증합니다. +/// +internal class EventValidator +{ + /// + /// 이벤트 시간 유효성을 검사합니다. + /// 버퍼 시간을 고려하여 이벤트 시작 전후와 종료 전후의 여유시간을 둡니다. + /// + /// 검사할 게임 이벤트 + /// 시간 오차 방지를 위한 버퍼 초 (네트워크 지연 등 고려) + /// 기준 시간 + /// 유효한 시간 범위 내에 있으면 true + public bool IsEventTimeValid(GameEvent gameEvent, int bufferSeconds, DateTime now) + { + // 버퍼를 적용한 실제 유효 시간 범위 계산 + var startWithBuffer = gameEvent.StartTime.AddSeconds(-bufferSeconds); // 시작 시간보다 일찍 참여 허용 + var endWithBuffer = gameEvent.EndTime.AddSeconds(bufferSeconds); // 종료 시간보다 늦게까지 참여 허용 + + // 현재 시간이 버퍼가 적용된 유효 범위 내에 있는지 확인 + return startWithBuffer <= now && now <= endWithBuffer; + } +} + +/// +/// GameEvent 생성을 담당하는 내부 정적 클래스 +/// 팩토리 패턴을 적용하여 GameEvent 객체 생성을 중앙화하고 일관성을 보장합니다. +/// +internal static class EventFactory +{ + /// 이벤트 준비 시간 - 시작 시간보다 몇 분 전에 준비 상태로 설정할지 + private const int READY_TIME_MINUTES_BEFORE = 5; + + /// + /// 이벤트 속성을 기반으로 GameEvent 객체를 생성합니다. + /// 모든 필수 필드를 적절한 값으로 초기화합니다. + /// + /// DB에서 로드한 이벤트 속성 + /// 계산된 이벤트 시작 시간 + /// 계산된 이벤트 종료 시간 + /// 완전히 초기화된 GameEvent 객체 + public static GameEvent Create(BattleEventAttrib attrib, DateTime startTime, DateTime endTime) + { + return new GameEvent + { + // 기본 식별 정보 + EventId = attrib.m_event_id, + IsActive = attrib.m_is_active, + GameModeId = attrib.m_game_mode_id, + + // 시간 관련 정보 + StartTime = startTime, + EndTime = endTime, + ReadyTime = startTime.AddMinutes(-READY_TIME_MINUTES_BEFORE), // 이벤트 시작 5분 전 준비 + + // 추가 설정 + IsSendNoti = false, // 알림 전송 여부 (기본값: 미전송) + FfaHotTime = attrib.m_ffa_hot_time // FFA 핫타임 설정 + }; + } +} + +/// +/// 이벤트 변경사항 감지를 담당하는 내부 정적 클래스 +/// 기존 이벤트와 새로운 이벤트를 비교하여 실제 변경이 있었는지 확인합니다. +/// +internal static class EventChangeDetector +{ + /// + /// 두 GameEvent 객체를 비교하여 중요한 필드에 변경사항이 있는지 확인합니다. + /// 캐시 업데이트 필요성을 판단하는 데 사용됩니다. + /// + /// 기존에 캐시된 이벤트 + /// 새로 생성된 이벤트 + /// 변경사항이 있으면 true + public static bool HasEventChanged(GameEvent existing, GameEvent updated) + { + // 주요 필드들만 비교 (성능상 이유로 모든 필드를 비교하지 않음) + return existing.IsActive != updated.IsActive || // 활성 상태 변경 + existing.GameModeId != updated.GameModeId || // 게임 모드 변경 + existing.StartTime != updated.StartTime || // 시작 시간 변경 + existing.EndTime != updated.EndTime || // 종료 시간 변경 + existing.FfaHotTime != updated.FfaHotTime; + } + + /// + /// 기존 GameEvent와 새로운 BattleEventAttrib를 비교하여 변경사항을 확인합니다. + /// DB에서 로드한 속성과 캐시된 이벤트를 직접 비교할 때 사용됩니다. + /// + /// 기존 캐시된 이벤트 + /// DB에서 새로 로드한 이벤트 속성 + /// 변경사항이 있으면 true + public static bool IsEventAttributeChanged(BattleEventAttrib oldAttrib, BattleEventAttrib newAttrib) + { + // 직접적으로 비교 가능한 필드들을 모두 비교한다. + return oldAttrib.m_is_active != newAttrib.m_is_active || + oldAttrib.m_game_mode_id != newAttrib.m_game_mode_id || + oldAttrib.m_start_hour != newAttrib.m_start_hour || + oldAttrib.m_start_min != newAttrib.m_start_min || + oldAttrib.m_open_duration_minutes != newAttrib.m_open_duration_minutes || + oldAttrib.m_end_date != newAttrib.m_end_date || + oldAttrib.m_period != newAttrib.m_period || + oldAttrib.m_day_of_week_type != newAttrib.m_day_of_week_type || + oldAttrib.m_start_day != newAttrib.m_start_day || + oldAttrib.m_ffa_hot_time != newAttrib.m_ffa_hot_time; + } +} diff --git a/ServerCommon/Contents/GameEvent/GameEventManagerOld.cs b/ServerCommon/Contents/GameEvent/GameEventManagerOld.cs new file mode 100644 index 0000000..a7c5b8c --- /dev/null +++ b/ServerCommon/Contents/GameEvent/GameEventManagerOld.cs @@ -0,0 +1,292 @@ +using System.Collections.Concurrent; +using Amazon.DynamoDBv2.DocumentModel; +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace ServerCommon; + +/// +/// 운영툴의 게임 이벤트를 설정을 캐시한다. +/// +public class GameEventManagerOld +{ + private readonly DynamoDbClient _dynamoDbClient; + private readonly NLog.Logger _logger; + private readonly ConcurrentDictionary _gameEvents = new(); + private Func>? OnUpdateGameEvent { get; set; } + public bool ApplyGameEvent { get; set; } = false; + + public GameEventManagerOld(DynamoDbClient dynamoDbClient) + { + _dynamoDbClient = dynamoDbClient; + _logger = Log.getLogger(); + } + + public GameEvent? GetGameEvent(int eventId) + { + return _gameEvents.GetValueOrDefault(eventId); + } + + public bool IsValidGameEventTime(int eventId, DateTime now = default) + { + if (!ApplyGameEvent) { return true;} + + if (_gameEvents.TryGetValue(eventId, out GameEvent? gameEvent)) + { + return gameEvent.StartTime <= now && now >= gameEvent.EndTime; + } + return false; + } + + public async Task RefreshEvent() + { + var result = new Result(); + (result, List docs) = await LoadFromDb(); + if (result.isFail()) return result; + + if (docs.Count == 0) + { + _logger.info("Not exist Game Event From DB"); + return result; + } + + //실제로 작동하는 battle event로 만들기 + await UpdateBattleEvent(docs); + + return result; + } + + private async Task<(Result, List)> LoadFromDb() + { + var doc = new GameEventDoc(); + QueryOperationConfig queryConfig = _dynamoDbClient.makeQueryConfigForReadByPKOnly(doc.getPK()); + (Result result, List readDocList) = await _dynamoDbClient.simpleQueryDocTypesWithQueryOperationConfig(queryConfig); + if (result.isFail()) + { + return (result, []); + } + return (result, readDocList); + } + + private async Task UpdateBattleEvent(List docs) + { + await Task.CompletedTask; + DateTime now = DateTimeHelper.Current; + + // + + //지난 이벤트 삭제 + DeleteOldEvents(now); + + foreach (GameEventDoc readDoc in docs) + { + AddOrUpdateEvent(readDoc); + } + + OnUpdateGameEvent?.Invoke(); + } + + private void AddOrUpdateEvent(GameEventDoc doc) + { + var attrib = doc.getAttrib(); + if (attrib is null) + { + _logger.Error("BattleEventDoc BattleEventAttrib is null !!!!"); + return; + } + //비활성화 된 이벤트는 추가 하지 않는다. + if (attrib.m_is_active == false) return; + + //이미 존재하는 이벤트는 추가 하지 않는다. + int eventId = attrib.m_event_id; + bool isExist = _gameEvents.ContainsKey(eventId); + if (isExist) return; + + //end_date가 지난 이벤트는 추가하지 않는다. + DateTime now = DateTimeHelper.Current; + DateTime today = now.Date; + DateTime startDay = DateTime.SpecifyKind(attrib.m_start_day, DateTimeKind.Utc); + if (attrib.m_end_date < now) return; + + DateTime nextDay = today.AddDays(1); + DateTime beforeDay = today.AddDays(-1); + + + //end_time이 지난 이벤트도 추가하지 않는다. + DateTime beforeStartTime = MakeGameEventStartTime(attrib, startDay, beforeDay); + DateTime beforeEndTime = beforeStartTime.AddMinutes(attrib.m_open_duration_minutes); + if (now <= beforeEndTime) + { + AddEvent(attrib, beforeStartTime, beforeEndTime, now, beforeDay); + return; + }; + + DateTime todayStartTime = MakeGameEventStartTime(attrib, startDay, today); + DateTime todayEndTime = todayStartTime.AddMinutes(attrib.m_open_duration_minutes); + if (now < todayEndTime) + { + AddEvent(attrib, todayStartTime, todayEndTime, now, today); + return; + } + + DateTime nextStartTime = MakeGameEventStartTime(attrib, startDay, nextDay); + DateTime nextEndTime = nextStartTime.AddMinutes(attrib.m_open_duration_minutes); + if (now < nextEndTime) + { + AddEvent(attrib, nextStartTime, nextEndTime, now, nextDay); + } + } + + private void AddEvent(BattleEventAttrib attrib, DateTime startTime, DateTime endTime, DateTime now, DateTime today) + { + List newEvents = []; + OncePeriodRangeType period = attrib.m_period; + switch (period) + { + case OncePeriodRangeType.NONE: + case OncePeriodRangeType.Daily: + newEvents.AddRange(MakeDailyGameEvent(startTime, endTime, attrib)); + break; + case OncePeriodRangeType.Weekly: + newEvents.AddRange(MakeWeeklyGameEvent(today, now, attrib)); + break; + case OncePeriodRangeType.Monthly: + newEvents.AddRange(MakeMonthlyGameEvent(today, now, attrib)); + break; + case OncePeriodRangeType.Nolimit: + Log.getLogger($"There is not used Type in Game Event !!!! eventId : {attrib.m_event_id}"); + break; + } + + foreach (var newEvent in newEvents) + { + _gameEvents.TryAdd(attrib.m_event_id, newEvent); + } + } + + private void DeleteOldEvents(DateTime now) + { + ConcurrentDictionary events = _gameEvents; + + //오래된 이벤트 id 취합 및 삭제 + List deletedEventIds = []; + foreach (GameEvent battleEvents in events.Values.ToList()) + { + int eventId = battleEvents.EventId; + DateTime endTime = battleEvents.EndTime; + if (endTime < now) + { + deletedEventIds.Add(eventId); + } + } + + if (deletedEventIds.Count > 0) + { + _logger.Info($"deleted event ids : {JsonConvert.SerializeObject(deletedEventIds)}"); + RemoveGameEvents(deletedEventIds); + } + + return; + + void RemoveGameEvents(List removeEventIds) + { + foreach (int eventId in removeEventIds) + { + if (false == _gameEvents.TryRemove(eventId, out GameEvent? ev)) + { + _logger.Warn($"remove battle event fali event_id : {eventId}"); + } + } + } + } + + private DateTime MakeGameEventStartTime(BattleEventAttrib attrib, DateTime startDay, DateTime today) + { + switch (attrib.m_period) + { + case OncePeriodRangeType.Daily: + case OncePeriodRangeType.Weekly: + case OncePeriodRangeType.Monthly: + return today.AddHours(attrib.m_start_hour).AddMinutes(attrib.m_start_min); + case OncePeriodRangeType.Nolimit: + _logger.Warn($"There is not used Type in Battle System Event !!!! eventId : {JsonConvert.SerializeObject(attrib)}"); + return DateTimeHelper.MinTime; + case OncePeriodRangeType.NONE: + default: + break; + } + return startDay.AddHours(attrib.m_start_hour).AddMinutes(attrib.m_start_min); + } + + private List MakeDailyGameEvent(DateTime startTime, DateTime endTime, BattleEventAttrib attrib) + { + List events = []; + GameEvent battleEvent = MakeGameEvent(attrib, startTime, endTime); + + if(_gameEvents.ContainsKey(battleEvent.EventId)) return events; + events.Add(battleEvent); + return events; + } + + private List MakeWeeklyGameEvent(DateTime today, DateTime now, BattleEventAttrib attrib) + { + List events = []; + foreach (DayOfWeekType dayOfWeek in attrib.m_day_of_week_type) + { + int daysUntilNext = ((int)dayOfWeek - (int)today.DayOfWeek + 7) % 7; + DateTime eventTime = today.AddDays(daysUntilNext).Date + .AddHours(attrib.m_start_hour) + .AddMinutes(attrib.m_start_min); + if (eventTime >= now) + { + if (attrib.m_end_date <= now) return events; + GameEvent ev = MakeGameEvent(attrib, eventTime, eventTime.AddMinutes(attrib.m_open_duration_minutes)); + if (_gameEvents.ContainsKey(ev.EventId)) continue; + events.Add(ev); + } + } + return events; + } + + private List MakeMonthlyGameEvent(DateTime today, DateTime now, BattleEventAttrib attrib) + { + List events = []; + var monthlyTime = new DateTime( + today.Year, today.Month, attrib.m_start_day.Day, + attrib.m_start_hour, attrib.m_start_min, 0 + ); + + if (monthlyTime >= now) + { + if (attrib.m_end_date <= now) return events; + GameEvent ev = MakeGameEvent(attrib, monthlyTime, monthlyTime.AddMinutes(attrib.m_open_duration_minutes)); + if (_gameEvents.ContainsKey(ev.EventId)) return events; + events.Add(ev); + } + + return events; + } + + private GameEvent MakeGameEvent(BattleEventAttrib attrib, DateTime startTime, DateTime endTime) + { + var gameEvent = new GameEvent + { + EventId = attrib.m_event_id, + GameModeId = attrib.m_game_mode_id, + StartTime = startTime, + EndTime = endTime, + ReadyTime = startTime.AddMinutes(-5), + IsSendNoti = false, + FfaHotTime = attrib.m_ffa_hot_time, + }; + + return gameEvent; + } + + public override string ToString() + { + return string.Join("\n", _gameEvents.Values.Select(x => x.ToString())); + } +} diff --git a/ServerCommon/Contents/GameMode/GameModeRewardConditionBase.cs b/ServerCommon/Contents/GameMode/GameModeRewardConditionBase.cs new file mode 100644 index 0000000..bd07541 --- /dev/null +++ b/ServerCommon/Contents/GameMode/GameModeRewardConditionBase.cs @@ -0,0 +1,23 @@ +using MetaAssets; + +namespace ServerCommon.Contents.GameMode; + +public class GameModeRewardConditionBase : IGameModeRewardCondition +{ + public GameModeType m_game_mode_type { get; } = GameModeType.None; + public int m_condition_group_id { get; } = 0; + public int m_reward_group_id { get; } = 0; + + public GameModeRewardConditionBase(GameModeType gameModeType, int conditionGroupId, int rewardGroupId) + { + m_game_mode_type = gameModeType; + m_condition_group_id = conditionGroupId; + m_reward_group_id = rewardGroupId; + } + + public GameModeRewardConditionBase() + { + + } + +} \ No newline at end of file diff --git a/ServerCommon/Contents/GameMode/IGameModeRewardCondition.cs b/ServerCommon/Contents/GameMode/IGameModeRewardCondition.cs new file mode 100644 index 0000000..2e25d0b --- /dev/null +++ b/ServerCommon/Contents/GameMode/IGameModeRewardCondition.cs @@ -0,0 +1,6 @@ +namespace ServerCommon.Contents.GameMode; + +public interface IGameModeRewardCondition +{ + +} \ No newline at end of file diff --git a/ServerCommon/Contents/GameMode/RunRaceMinimumRespawnRewardCondition.cs b/ServerCommon/Contents/GameMode/RunRaceMinimumRespawnRewardCondition.cs new file mode 100644 index 0000000..3e2b75d --- /dev/null +++ b/ServerCommon/Contents/GameMode/RunRaceMinimumRespawnRewardCondition.cs @@ -0,0 +1,15 @@ +using MetaAssets; + +namespace ServerCommon.Contents.GameMode; + +public class RunRaceMinimumRespawnRewardCondition : GameModeRewardConditionBase +{ + + public int m_player_count { get; } = 0; + public int m_minimum_respawn { get; } = 100; + public RunRaceMinimumRespawnRewardCondition(int playerCount, int minimumRespawn, int conditionGroupId, int rewardGroupId) : base(GameModeType.RUN_RACE, conditionGroupId, rewardGroupId) + { + m_player_count = playerCount; + m_minimum_respawn = minimumRespawn; + } +} \ No newline at end of file diff --git a/ServerCommon/Contents/GameMode/RunRaceRankRewardCondition.cs b/ServerCommon/Contents/GameMode/RunRaceRankRewardCondition.cs new file mode 100644 index 0000000..7c60f94 --- /dev/null +++ b/ServerCommon/Contents/GameMode/RunRaceRankRewardCondition.cs @@ -0,0 +1,15 @@ +using MetaAssets; + +namespace ServerCommon.Contents.GameMode; + +public class RunRaceRankRewardCondition: GameModeRewardConditionBase +{ + + public int m_player_count { get; } = 0; + public int m_rank { get; } = 100; + public RunRaceRankRewardCondition(int playerCount, int rank, int conditionGroupId, int rewardGroupId) : base(GameModeType.RUN_RACE, conditionGroupId, rewardGroupId) + { + m_player_count = playerCount; + m_rank = rank; + } +} \ No newline at end of file diff --git a/ServerCommon/Contents/GameMode/RunRaceUnfinishRankRewardCondition.cs b/ServerCommon/Contents/GameMode/RunRaceUnfinishRankRewardCondition.cs new file mode 100644 index 0000000..7055b7f --- /dev/null +++ b/ServerCommon/Contents/GameMode/RunRaceUnfinishRankRewardCondition.cs @@ -0,0 +1,14 @@ +using MetaAssets; + +namespace ServerCommon.Contents.GameMode; + +public class RunRaceUnfinishRankRewardCondition : GameModeRewardConditionBase +{ + public int m_player_count { get; } = 0; + public int m_last_save_checkpoint { get; } = 100; + public RunRaceUnfinishRankRewardCondition(int playerCount, int lastSaveCheckpoint, int conditionGroupId, int rewardGroupId) : base(GameModeType.RUN_RACE, conditionGroupId, rewardGroupId) + { + m_player_count = playerCount; + m_last_save_checkpoint = lastSaveCheckpoint; + } +} \ No newline at end of file diff --git a/ServerCommon/Contents/GameMode/TpsFfaPickupPodRewardCondition.cs b/ServerCommon/Contents/GameMode/TpsFfaPickupPodRewardCondition.cs new file mode 100644 index 0000000..64d45f1 --- /dev/null +++ b/ServerCommon/Contents/GameMode/TpsFfaPickupPodRewardCondition.cs @@ -0,0 +1,11 @@ +using MetaAssets; + +namespace ServerCommon.Contents.GameMode; + +public class TpsFfaPickupPodRewardCondition : GameModeRewardConditionBase +{ + public TpsFfaPickupPodRewardCondition(int conditionGroupId, int rewardGroupId) : base(GameModeType.TPS_FFA, conditionGroupId, rewardGroupId) + { + + } +} \ No newline at end of file diff --git a/ServerCommon/Contents/GameMode/TpsFfaPodStorageRewardCondition.cs b/ServerCommon/Contents/GameMode/TpsFfaPodStorageRewardCondition.cs new file mode 100644 index 0000000..e3f0559 --- /dev/null +++ b/ServerCommon/Contents/GameMode/TpsFfaPodStorageRewardCondition.cs @@ -0,0 +1,16 @@ +using MetaAssets; + +namespace ServerCommon.Contents.GameMode; + +public class TpsFfaPodStorageRewardCondition : GameModeRewardConditionBase +{ + public int m_charge_level { get; } = 1; + public int m_charge_time { get; } = 60; + + public TpsFfaPodStorageRewardCondition(int chargeLevel, int chargeTime, int conditionGroupId, int rewardGroupId) : base(GameModeType.TPS_FFA, conditionGroupId, rewardGroupId) + { + m_charge_level = chargeLevel; + m_charge_time = chargeTime; + } + +} \ No newline at end of file diff --git a/ServerCommon/Contents/GameModeSettings/GameModeMatchSettingsLoader.cs b/ServerCommon/Contents/GameModeSettings/GameModeMatchSettingsLoader.cs new file mode 100644 index 0000000..8f8fd78 --- /dev/null +++ b/ServerCommon/Contents/GameModeSettings/GameModeMatchSettingsLoader.cs @@ -0,0 +1,55 @@ +using ServerBase; + +namespace ServerCommon.Contents.GameModeSettings; + +public class GameModeMatchSettingsLoader +{ + private readonly DynamoDbClient _dynamoDbClient; + + public GameModeMatchSettingsLoader(IServerLogic serverLogic, DynamoDbClient dynamoDbClient) + { + _dynamoDbClient = dynamoDbClient; + } + + public async Task<(Result, List)> LoadGameModeMatchFromDb() + { + var dynamoDbClient = _dynamoDbClient; + + var doc = new GameModeMatchSettingsDoc(); + var queryConfig = dynamoDbClient.makeQueryConfigForReadByPKOnly(doc.getPK()); + (Result result, List readDocList) = await dynamoDbClient.simpleQueryDocTypesWithQueryOperationConfig(queryConfig); + if (result.isFail()) + { + return (result, new()); + } + return (result, readDocList); + } + + public async Task<(Result, List)> LoadGameModeFromDb() + { + var dynamoDbClient = _dynamoDbClient; + + var doc = new GameModeMatchSettingsDoc(); + var queryConfig = dynamoDbClient.makeQueryConfigForReadByPKOnly(doc.getPK()); + (Result result, List readDocList) = await dynamoDbClient.simpleQueryDocTypesWithQueryOperationConfig(queryConfig); + if (result.isFail()) + { + return (result, new()); + } + return (result, readDocList); + } + + public async Task<(Result, List)> LoadGameModeTeamFromDb() + { + var dynamoDbClient = _dynamoDbClient; + + var doc = new GameModeMatchSettingsDoc(); + var queryConfig = dynamoDbClient.makeQueryConfigForReadByPKOnly(doc.getPK()); + (Result result, List readDocList) = await dynamoDbClient.simpleQueryDocTypesWithQueryOperationConfig(queryConfig); + if (result.isFail()) + { + return (result, new()); + } + return (result, readDocList); + } +} diff --git a/ServerCommon/Contents/Match/GameRoomInfo.cs b/ServerCommon/Contents/Match/GameRoomInfo.cs new file mode 100644 index 0000000..17be994 --- /dev/null +++ b/ServerCommon/Contents/Match/GameRoomInfo.cs @@ -0,0 +1,40 @@ +namespace ServerCommon; + +/// +/// 인던에서 생성된 게임 룸의 게임의 정보 +/// 정보 전달용 오브젝트 Dto +/// +public class GameRoomInfo +{ + public class TeamInfo + { + /// + /// idx는 0부터 시작 + /// + public required int Idx { get; init; } + public string Id + { + get { return $"{Idx + 1:D3}"; } + init { } + } + + public required string UserGuid { get; init; } + public string NickName { get; init; } = string.Empty; + } + + public required string Id { get; init; } + public required string PolicyVersion { get; init; } + public required int GameModeId { get; init; } + public required int GameEventId { get; init; } + public required string MatchGroupId { get; init; } + public required int InstanceMetaId { get; init; } + public required MatchTeamPolicy[] TeamPolicies { get; init; } + public required int MinTeamCount { get; init; } + public required int MaxTeamCount { get; init; } + public required TeamInfo[] Teams { get; init; } + + // 이벤트 + public required int HotTimeEvent { get; init; } + public required DateTime? EventStartTime { get; set; } + public required DateTime? EventEndTime { get; set; } +} diff --git a/ServerCommon/Contents/Match/MagleveHasher.cs b/ServerCommon/Contents/Match/MagleveHasher.cs new file mode 100644 index 0000000..7223adb --- /dev/null +++ b/ServerCommon/Contents/Match/MagleveHasher.cs @@ -0,0 +1,177 @@ +using System.Security.Cryptography; +using System.Text; +using System.Threading; + +namespace ServerCommon; + +public class MaglevHasher +{ + // 계수는 상황에 맞게 조정가능 (예: 100~500 사이 추천) + private const int m_lookup_table_factor = 100; + + // 내부 상태를 나타내는 불변 클래스 - 스레드 안전 + private class HashState + { + public readonly int LookupTableSize; + public readonly string[] Backends; + public readonly int[] LookupTable; + + public HashState(string[] backends, int lookupTableSize, int[] lookupTable) + { + Backends = backends; + LookupTableSize = lookupTableSize; + LookupTable = lookupTable; + } + } + + // volatile 키워드로 원자적 참조 교체 보장 + private volatile HashState? m_state = null; + + public MaglevHasher(string[] backends) + { + updateBackends(backends); + } + + // 해시 함수 + private static int computeStableHash(string input, int size) + { + using var sha256 = SHA256.Create(); + var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(input)); + int value = BitConverter.ToInt32(hash, 0) & 0x7FFFFFFF; + return value % size; + } + + // Maglev 해시 테이블 초기화 + private static int[] populateLookupTable(string[] backends, int lookupTableSize) + { + int n = backends.Length; + int[] lookup_table = new int[lookupTableSize]; + + for (int i = 0; i < lookupTableSize; i++) + { + lookup_table[i] = -1; + } + + var offsets = new int[n]; + var skips = new int[n]; + + for (int i = 0; i < n; i++) + { + offsets[i] = computeStableHash(backends[i] + "-1", lookupTableSize); + skips[i] = computeStableHash(backends[i] + "-2", lookupTableSize - 1) + 1; + } + + var next = new int[n]; + for (int i = 0; i < n; i++) + { + next[i] = offsets[i]; + } + + // 테이블의 모든 칸이 다 채워질 때까지 반복 + int remaining = lookupTableSize; + while (remaining > 0) + { + for (int i = 0; i < n && remaining > 0; i++) + { + int entry = next[i]; + while (lookup_table[entry] >= 0) + { + entry = (entry + skips[i]) % lookupTableSize; + next[i] = entry; + } + + lookup_table[entry] = i; + next[i] = (entry + skips[i]) % lookupTableSize; + remaining--; + } + } + + return lookup_table; + } + + public string getBackend(string key) + { + // 현재 상태의 로컬 참조 획득 (원자적 읽기 작업) + HashState? current_state = m_state; + + if (current_state == null || current_state.Backends.Length == 0) + { + return string.Empty; + } + + int index = computeStableHash(key, current_state.LookupTableSize); + int backend_index = current_state.LookupTable[index]; + return current_state.Backends[backend_index]; + } + + public void updateBackends(string[] newBackends) + { + if (newBackends.Length == 0) + { + return; + } + + // 현재 백엔드와 새 백엔드가 동일한지 비교 + if (m_state != null && areBackendsEqual(m_state.Backends, newBackends)) + { + return; // 백엔드가 동일하면 업데이트하지 않음 + } + + // 백엔드 배열 방어적 복사 + string[] backends_copy = newBackends.ToArray(); + int n = backends_copy.Length; + + // lookup_table_size 계산 + int estimated_size = n * m_lookup_table_factor; + int lookup_table_size = PrimeHelper.findNearestPrimeGreaterOrEqual(estimated_size); + + // 새 룩업 테이블 생성 및 초기화 + int[] lookup_table = populateLookupTable(backends_copy, lookup_table_size); + + // 새 상태 객체 생성 + HashState new_state = new HashState(backends_copy, lookup_table_size, lookup_table); + + // 원자적 참조 교체 (volatile 필드) + m_state = new_state; + } + + // 두 백엔드 배열이 동일한지 확인하는 헬퍼 메서드 + private static bool areBackendsEqual(string[] current, string[] newBackends) + { + if (current.Length != newBackends.Length) + { + return false; + } + + return !current.Where((t, i) => t != newBackends[i]).Any(); + } + + public string[] GetBackends() + { + return m_state?.Backends ?? []; + } +} + +// 소수를 찾기 위한 헬퍼 클래스 +public static class PrimeHelper +{ + public static int findNearestPrimeGreaterOrEqual(int n) + { + if (n < 2) return 2; + while (!isPrime(n)) n++; + return n; + } + + private static bool isPrime(int x) + { + if (x <= 1) return false; + if (x == 2) return true; + if (x % 2 == 0) return false; + int limit = (int)Math.Sqrt(x); + for (int i = 3; i <= limit; i += 2) + { + if (x % i == 0) return false; + } + return true; + } +} diff --git a/ServerCommon/Contents/Match/MatchCheatCmd.cs b/ServerCommon/Contents/Match/MatchCheatCmd.cs new file mode 100644 index 0000000..c929cff --- /dev/null +++ b/ServerCommon/Contents/Match/MatchCheatCmd.cs @@ -0,0 +1,37 @@ +namespace ServerCommon; + +public static class MatchCheatCmd +{ + public enum CmdType + { + MatchDelay = 1, + MatchReset, + CancelForce + } + + public interface ICmd + { + public CmdType Type { get; } + } + + // 매칭 유보한다. + public class MatchDelay: ICmd + { + public CmdType Type => CmdType.MatchDelay; + public string UserGuid { get; set; } = string.Empty; + public int DelaySec { get; set; } = 60 * 5; // 5분 + } + + // 매칭 풀 초기화 + public class Reset : ICmd + { + public CmdType Type => CmdType.MatchReset; + // 999이면 모두 초기화 + public int GameModeId { get; set; } = 0; + } + + public class CancelForce : ICmd + { + public CmdType Type => CmdType.CancelForce; + } +} diff --git a/ServerCommon/Contents/Match/MatchGuard.cs b/ServerCommon/Contents/Match/MatchGuard.cs new file mode 100644 index 0000000..2b669d0 --- /dev/null +++ b/ServerCommon/Contents/Match/MatchGuard.cs @@ -0,0 +1,60 @@ +using System.Diagnostics.CodeAnalysis; +using ServerBase; + +namespace ServerCommon; + +public class MatchGuard +{ + public static void NotNull([NotNull] object? target, Func? fnLog = null) + { + if (target is null) + { + throw new ResultException(ServerErrorCode.ServerLogicError, + $"{nameof(target)} is null => {fnLog?.Invoke()}"); + } + } + + public static void InvalidMetaData([NotNull] object? target, string resultString) + { + if (target is null) + { + throw new ResultException(ServerErrorCode.MatchInvalidMetaData, + $"{nameof(target)} is null => {resultString}"); + } + } + + public static void InvalidGameModeId(bool condition, int gameModeId) + { + if (condition is false) + { + throw new ResultException(ServerErrorCode.MatchInvalidGameModeId, $"GameModeId{gameModeId} not found"); + } + } + + public static void InvalidMatchGroupId(string matchGroupId) + { + if (string.IsNullOrEmpty(matchGroupId) || string.IsNullOrWhiteSpace(matchGroupId)) + { + throw new ResultException(ServerErrorCode.MatchInvalidGameModeId, + $"matchGroupId:{matchGroupId} => IsNullOrEmpty or IsNullOrWhiteSpace"); + } + } + + public static void InvalidMatchEventTime(int eventId, string userGuid) + { + throw new ResultException(ServerErrorCode.MatchReserveFail, $"Event({eventId}) Time over => user_guid:{userGuid}"); + } + + // public static void ShouldBeTrue(bool condition, string resultString) + // { + // throw new ServerException(ServerErrorCode.MatchInvalidGameModeId, $"GameModeId{gameModeId} not found"); + // } + + public static void ResultFailException(Result result) + { + if (result.isFail()) + { + throw new ResultException(result); + } + } +} diff --git a/ServerCommon/Contents/Match/MatchPolicy.cs b/ServerCommon/Contents/Match/MatchPolicy.cs new file mode 100644 index 0000000..f219b61 --- /dev/null +++ b/ServerCommon/Contents/Match/MatchPolicy.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; + +namespace ServerCommon; +/// +/// 매치매이킹 옵션 +/// +public class MatchPolicy +{ + public required string Version { get; init; } + public required int GameModeId { get; init; } + public required int GameEventId { get; init; } + // public required int InstanceMetaId { get; init; } + /// + /// 난입 가능 여부 + /// + public required bool IsJoinAnytime { get; init; } + public required int MinTeamCount { get; init; } + public required int MaxTeamCount { get; init; } + // 팀별 인원 옵션 + public required MatchTeamPolicy[] TeamPolicies { get; init; } + // 최대 입장 대기 시간 + public required TimeSpan MaxStartWaitTime { get; init; } + // 입장 제한 남은 시간 => 난입: 이벤트 + // public required TimeSpan EntranceClosingTime { get; init; } + public required TimeSpan JoinInMaxTime { get; init; } + public required TimeSpan MatchWaitTime { get; init; } + public required int MatchRetryCount { get; init; } + public int MaxTotalMemberCount => TeamPolicies.Sum(x => x.MaxMemberCount) * MaxTeamCount; + public bool IsSingleGame => MaxTeamCount <= 1; + + public int MatchLoopIntervalMs{ get; init; } = 10000; +} + +public record MatchTeamPolicy(int MinMemberCount, int MaxMemberCount); diff --git a/ServerCommon/Contents/Match/MatchPoolInfo.cs b/ServerCommon/Contents/Match/MatchPoolInfo.cs new file mode 100644 index 0000000..687f216 --- /dev/null +++ b/ServerCommon/Contents/Match/MatchPoolInfo.cs @@ -0,0 +1,47 @@ +namespace ServerCommon; + +public record MatchPoolInfo +{ + public const string PREFIX = "game"; + public int GameModeId { get; init; } + public string Region { get; init;} + public string EventId { get; init;} + public int GameEventId { get; init;} + + // 캐시된 문자열 표현 + private string? _cachedString; + + public MatchPoolInfo(int gameModeId, string region, int gameEventId) + { + var eventStartDt = DateTime.UtcNow.Date; + GameModeId = gameModeId; + Region = region; + GameEventId = gameEventId; + EventId = $"{eventStartDt:yyyyMMdd}_{gameEventId}"; + } + + private MatchPoolInfo(int gameModeId, string region, int gameEventId, string eventId) + { + GameModeId = gameModeId; + Region = region; + GameEventId = gameEventId; + EventId = eventId; + } + + public string MatchGroupId => ToString(); + + public override string ToString() + { + return _cachedString ??= $"{PREFIX}-{GameModeId}-{EventId}-{Region}"; + } + + public static MatchPoolInfo ConvertByMatchGroupId(string matchGameId) + { + var parts = matchGameId.Split('-'); + var gameModeId = int.Parse(parts[1]); + var eventId = parts[2]; + var gameEventId = int.Parse(eventId.Split('_').Last()); + var region = parts[3]; + return new MatchPoolInfo(gameModeId, region, gameEventId, eventId); + } +} diff --git a/ServerCommon/Contents/Match/MatchReserveUser.cs b/ServerCommon/Contents/Match/MatchReserveUser.cs new file mode 100644 index 0000000..8607b88 --- /dev/null +++ b/ServerCommon/Contents/Match/MatchReserveUser.cs @@ -0,0 +1,65 @@ +using Google.Protobuf.WellKnownTypes; + +namespace ServerCommon; +public class MatchReserveUser : IEquatable +{ + public required MatchUserInfo UserInfo { get; init; } + public string UserGuid => UserInfo.UserGuid; + public DateTime MatchStartTime { get; set; } = DateTime.UtcNow; + public TimeSpan WaitElapsed => DateTime.UtcNow - MatchStartTime; + public TimeSpan RematchElapsed { get; set; } = TimeSpan.FromMinutes(5); + // 재매칭까지 남은 시간 + public TimeSpan RematchRemain => RematchElapsed - WaitElapsed; + public int MatchTryCount { get; set; } = 0; + public int MatchStep => MatchTryCount + 1; + public string RoomId { get; set; } = string.Empty; + public string TeamId { get; set; } = string.Empty; + + public DateTime RoomJoinTime { get; set; } = DateTime.MaxValue.AddYears(-1); + + public bool HasTimeout(TimeSpan maxWaitTime) + { + return WaitElapsed >= maxWaitTime; + } + + public bool Equals(MatchReserveUser? other) + { + if (other is null) return false; + return UserGuid == other.UserGuid; + } + + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals(obj as MatchReserveUser); + } + + public override int GetHashCode() + { + return UserInfo.UserGuid.GetHashCode(); + } + + public static bool operator ==(MatchReserveUser? left, MatchReserveUser? right) + { + if (ReferenceEquals(left, right)) return true; + if (left is null || right is null) return false; + return left.Equals(right); + } + + public static bool operator !=(MatchReserveUser? left, MatchReserveUser? right) + { + return !(left == right); + } +} + +// public class MatchCancelUser { +// public MatchUserInfo UserInfo { get; set; } = null!; +// public string TraceId { get; set; } = string.Empty; +// } + +// public class MatchCancelResult { +// public MatchCancelUser User { get; set; } = null!; +// public bool IsSuccess { get; set; } = false; +// } diff --git a/ServerCommon/Contents/Match/MatchRoom.cs b/ServerCommon/Contents/Match/MatchRoom.cs new file mode 100644 index 0000000..8311801 --- /dev/null +++ b/ServerCommon/Contents/Match/MatchRoom.cs @@ -0,0 +1,172 @@ +using System.Text; +using Google.Protobuf.WellKnownTypes; +using Org.BouncyCastle.Asn1.Cms; + +namespace ServerCommon; + +public class MatchRoom +{ + private readonly string _id; + private readonly List _teams; + public string Id => _id; + public IEnumerable Teams => _teams; + public DateTime ReservedTime { get; set; } + public DateTime CreatedTime { get; private set; } + public DateTime StartedTime { get; set; } + public DateTime EndTime { get; set; } + public MatchPoolInfo MatchPoolInfo { get; init; } + public string MatchGroupId => MatchPoolInfo.MatchGroupId; + public MatchGameStateType GameState { get; set; } = MatchGameStateType.None; + public InstanceRoomInfo RoomInfo { get; private set; } = null!; + public string ServerName { get; private set; } = string.Empty; + public MatchPolicy MatchPolicy { get; private set; } + public int InstanceMetaId { get; private set; } + public EPlaceType InstanceMetaType { get; private set; } = EPlaceType.GameRoom; + + public MatchRoom(string id, MatchPoolInfo matchPoolInfo, MatchPolicy matchPolicy, int instanceMetaId) + { + _id = id; + MatchPoolInfo = matchPoolInfo; + MatchPolicy = matchPolicy; + _teams = Enumerable.Range(0, matchPolicy.MaxTeamCount) + .Select(teamIdx => + new MatchTeam(teamIdx, + matchPolicy.TeamPolicies[teamIdx])) + .ToList(); + ReservedTime = DateTime.UtcNow; + CreatedTime = DateTime.UtcNow; + EndTime = DateTime.MaxValue; + // 랜덤으로 결정되어야 한다. + InstanceMetaId = instanceMetaId; + } + + public void StartOnIndun(InstanceRoomInfo roomInfo, string serverName) + { + RoomInfo = roomInfo; + ServerName = serverName; + CreatedTime = DateTime.UtcNow.ToUniversalTime(); + } + + public void SetInstanceMetaId(int instanceMetaId) + { + InstanceMetaId = instanceMetaId; + } + + public void SetMatchPolicy(MatchPolicy matchPolicy) + { + MatchPolicy = matchPolicy; + } + + public bool IsFull() + { + return Teams.All(team => team.IsFull()); + } + + public bool IsEmpty() + { + return Teams.All(team => team.IsEmpty()); + } + + public bool CanEnter() + { + var createdTime = CreatedTime; + + DateTime canJoinLimitTime; + + // 난입 게임모드 처리 + if (MatchPolicy.IsJoinAnytime) + { + // 난입 게임은 시작 시간 부터 최대 입장 가능 시간까지 입장 가능 + canJoinLimitTime = createdTime + MatchPolicy.JoinInMaxTime; + } + else + { + // 비난입 게임모드 처리 + // 난입 게임이 아니라면 생성 시간 + 시작 대기 시간 (MatchPolicy.MaxStartWaitTime) 까지 입장 가능 + canJoinLimitTime = createdTime + MatchPolicy.MaxStartWaitTime; + } + + return DateTime.UtcNow < canJoinLimitTime; + } + + public bool CanStart() + { + // 최소 시작팀 수 만큼이 시작 가능하면 시작 가능으로 판단함 + return Teams.Count(team => team.CanStart()) >= MatchPolicy.MinTeamCount; + } + + public MatchReserveUser[] GetTotalMembers() + { + return Teams.SelectMany(team => team.Members).ToArray(); + } + + /// + /// 팀간 균형을 이루도록 멤버를 추가한다. + /// + /// + /// + public bool AddMemberOnTeamBalance(MatchReserveUser member) + { + var team = Teams.Where(team => !team.IsFull()).MinBy(team => team.Members.Count); + if (team == null) { return false; } + + var addSuccess = team.AddMember(member); + if (addSuccess) + { + member.RoomId = Id; + member.TeamId = team.Id; + } + + return addSuccess; + } + + public bool RemoveMember(string userGuid) + { + var team = Teams.FirstOrDefault(team => team.Members.Any(member => member.UserInfo.UserGuid == userGuid)); + if (team is null) + { + return false; + } + + team.Members.RemoveAll(member => member.UserInfo.UserGuid == userGuid); + return true; + } + + public override string ToString() + { + var createdTimeStr = CreatedTime.ToString("yyyy-MM-dd HH:mm:ss UTC") ?? "Not Created"; + var startedTimeStr = StartedTime.ToString("yyyy-MM-dd HH:mm:ss UTC") ?? "Not Started"; + var endTimeStr = EndTime.ToString("yyyy-MM-dd HH:mm:ss UTC") ?? "Not Ended"; + + var totalMembers = GetTotalMembers().Length; + + // 각 팀의 멤버 목록 생성 + var teamDetails = Teams.Select(team => + { + if (team.Members.Count == 0) + { + return ""; + } + var memberList = team.Members.Select(member => + $"{member.UserInfo.UserGuid}"); + return $"Team{team.Id}({team.Members.Count}): [{string.Join("\n", memberList)}]"; + }); + + var teamInfo = string.Join("\n", teamDetails); + + var sb = new StringBuilder(); + sb.Append($"MatchRoom [Id: {Id}, ") + .Append($"Server: {ServerName}, ") + .Append($"GroupId: {MatchGroupId}, ") + .Append($"GameState: {GameState}, ") + .Append($"Created: {createdTimeStr}, ") + .Append($"Started: {startedTimeStr}, ") + .Append($"Ended: {endTimeStr}] ") + .AppendLine() + .Append($"Members: {totalMembers}, ") + .AppendLine() + .Append($"[Teams] {teamInfo}"); + return sb.ToString(); + } + +} diff --git a/ServerCommon/Contents/Match/MatchRouter.cs b/ServerCommon/Contents/Match/MatchRouter.cs new file mode 100644 index 0000000..280684d --- /dev/null +++ b/ServerCommon/Contents/Match/MatchRouter.cs @@ -0,0 +1,45 @@ + +using ServerCore; + +namespace ServerCommon; +public class MatchRouter +{ + private readonly MaglevHasher m_hasher = new MaglevHasher([]); + // private readonly List m_backends = []; + public string getBackend(string matchGroupId) + { + return m_hasher.getBackend(matchGroupId); + } + + public string[] GetBackends() + { + return m_hasher.GetBackends(); + } + + // public void addBackend(string backend) + // { + // NullReferenceCheckHelper.throwIfNull(string.IsNullOrEmpty(backend), () => $"backend is null or empty !!!"); + // m_backends.Add(backend); + // } + // + // public void addBackends(string[] backends) + // { + // ConditionValidCheckHelper.throwIfFalse(backends.Length > 0, $"backends is empty !!!"); + // m_backends.AddRange(backends); + // m_hasher.updateBackends(m_backends.ToArray()); + // } + // + // public Result removeBackend(string backend) + // { + // var result = new Result(); + // NullReferenceCheckHelper.throwIfNull(string.IsNullOrEmpty(backend), () => $"backend is null or empty !!!"); + // m_backends.Remove(backend); + // return result; + // } + + public void updateBackends(string[] backends) + { + ConditionValidCheckHelper.throwIfFalse(backends.Length > 0, $"backends is empty !!!"); + m_hasher.updateBackends(backends); + } +} diff --git a/ServerCommon/Contents/Match/MatchTeam.cs b/ServerCommon/Contents/Match/MatchTeam.cs new file mode 100644 index 0000000..5d27746 --- /dev/null +++ b/ServerCommon/Contents/Match/MatchTeam.cs @@ -0,0 +1,46 @@ +namespace ServerCommon; + +public class MatchTeam +{ + public int Idx { get; init; } + public string Id => $"{Idx+1:D3}"; + public List Members { get; init; } + public int MinMemberCount { get; init; } + private int MaxMemberCount { get; init; } + + public MatchTeam(int idx, MatchTeamPolicy teamPolicy, List members = null!) + { + Idx = idx; + MinMemberCount = teamPolicy.MinMemberCount; + MaxMemberCount = teamPolicy.MaxMemberCount; + Members = members?.ToList() ?? []; + } + + public bool AddMember(MatchReserveUser member) + { + if (Members.Count >= MaxMemberCount) return false; + Members.Add(member); + return true; + } + + public bool RemoveMember(string userGuid) + { + return Members.RemoveAll(x => x.UserInfo.UserGuid == userGuid) > 0; + } + + public bool IsEmpty() + { + return Members.Count == 0; + } + + public bool IsFull() + { + return Members.Count >= MaxMemberCount; + } + + public bool CanStart() + { + return Members.Count >= MinMemberCount; + } +} + diff --git a/ServerCommon/Contents/Match/RabbitMqRpc.cs b/ServerCommon/Contents/Match/RabbitMqRpc.cs new file mode 100644 index 0000000..bfcb576 --- /dev/null +++ b/ServerCommon/Contents/Match/RabbitMqRpc.cs @@ -0,0 +1,106 @@ +using System.Collections.Concurrent; +using ServerCore; +using ServerBase; + +namespace ServerCommon; + +using Google.Protobuf; + +public class RabbitMqRpc +{ + private readonly RabbitMqConnector m_mq; + private readonly ConcurrentDictionary> m_pending_messages = new(); + + public RabbitMqRpc(RabbitMQConnectorBase mq) + { + m_mq = (RabbitMqConnector)mq; + NullReferenceCheckHelper.throwIfNull(m_mq, () => $"RabbitMqConnector is null !!!"); + } + + // 일정 시간 응답이 없으면 에러 처리하는 요청 생성 + public async Task<(Result, IMessage?)> reqMessageWaitOnAck(string to, IMessage message, string traceId, + CancellationToken cancellationToken = default) + { + var result = new Result(); + var tcs = new TaskCompletionSource<(Result, IMessage)>(TaskCreationOptions.RunContinuationsAsynchronously); + m_pending_messages.TryAdd(traceId, tcs); + + sendMessage(to, message); + + // 타임아웃 처리 + using var timeout_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + timeout_cts.CancelAfter(TimeSpan.FromSeconds(15)); // todo: 설정으로 처리: 15초 타임아웃 + + var completed_task = await Task.WhenAny(tcs.Task, Task.Delay(Timeout.Infinite, timeout_cts.Token)); + if (completed_task == tcs.Task) + { + return await tcs.Task; // 성공적으로 응답 받음 + } + else + { + // 타임아웃 발생 + m_pending_messages.TryRemove(traceId, out _); + result.setFail(ServerErrorCode.MqResponseTimeout, $"MqResponseTimeout traceId:{traceId} 이미 처리된"); + } + + return (result, null); // 타임아웃 시 응답 메시지는 null + } + + // 일정 시간 응답이 없으면 에러 처리하는 요청 생성 + public async Task<(Result, IMessage?)> reqBroadcastMessageWaitOnAck(string[] targets, IMessage message, string traceId, + CancellationToken cancellationToken = default) + { + var result = new Result(); + var tcs = new TaskCompletionSource<(Result, IMessage)>(TaskCreationOptions.RunContinuationsAsynchronously); + m_pending_messages.TryAdd(traceId, tcs); + + foreach (var to in targets) + { + sendMessage(to, message); + } + + // 타임아웃 처리 + using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + timeoutCts.CancelAfter(TimeSpan.FromSeconds(15)); // todo: 설정으로 처리: 15초 타임아웃 + + var completedTask = await Task.WhenAny(tcs.Task, Task.Delay(Timeout.Infinite, timeoutCts.Token)); + if (completedTask == tcs.Task) + { + return await tcs.Task; // 성공적으로 응답 받음 + } + else + { + // 타임아웃 발생 + m_pending_messages.TryRemove(traceId, out _); + result.setFail(ServerErrorCode.MqResponseTimeout, $"MqResponseTimeout traceId:{traceId} 이미 처리된"); + } + + return (result, null); // 타임아웃 시 응답 메시지는 null + } + + + // 응답 성공 처리 (응답 메시지 추가) + public Result setAckResult(string traceId, ServerMessage responseMessage = null!) + { + var result = new Result(); + if (m_pending_messages.TryRemove(traceId, out var tcs)) + { + tcs.SetResult((result, responseMessage)); + return result; + } + + // 이미 처리된 메시지 + // result.setFail(ServerErrorCode.GameMatchError, ""); + return result; + } + + public void sendMessage(string to, IMessage message) + { + if (message is not ServerMessage server_message) + { + Log.getLogger().Error( $"sendMessage Fail {message.GetType().Name} is not ServerMessage !!!"); + return; + } + m_mq.SendMessage(to, server_message); + } +} diff --git a/ServerCommon/Contents/Match/ResultException.cs b/ServerCommon/Contents/Match/ResultException.cs new file mode 100644 index 0000000..a31992f --- /dev/null +++ b/ServerCommon/Contents/Match/ResultException.cs @@ -0,0 +1,29 @@ +namespace ServerCommon; + +public class ResultException : Exception +{ + public ServerErrorCode ErrorCode => Result.ErrorCode; + public string ResultString => Result.ResultString; + private string _cacheString = string.Empty; + + public ResultException(Result result) + { + Result = result; + } + + public ResultException(ServerErrorCode errorCode, string resultString) + { + Result = new Result { ErrorCode = errorCode, ResultString = resultString }; + } + + public Result Result { get; } + + public override string ToString() + { + if (string.IsNullOrEmpty(_cacheString)) + { + _cacheString = $"ResultException ErrorCode:{Result} => {base.Message}"; + } + return _cacheString; + } +} diff --git a/ServerCommon/Contents/ReservationEnterToServer/ReservationMessageManager.cs b/ServerCommon/Contents/ReservationEnterToServer/ReservationMessageManager.cs index c00bbee..458099a 100644 --- a/ServerCommon/Contents/ReservationEnterToServer/ReservationMessageManager.cs +++ b/ServerCommon/Contents/ReservationEnterToServer/ReservationMessageManager.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; - - +using Newtonsoft.Json; using ServerCore; using ServerBase; @@ -30,6 +29,7 @@ public class ReservationMessageManager var is_add = m_tasks.TryAdd(userGuid, action); if (is_add) { + //Log.getLogger().info($"ReservationMessageManager task tryAdd success UserGuid : {userGuid}, args : {JsonConvert.SerializeObject(args)}"); m_messages[userGuid] = args; return true; } @@ -43,6 +43,9 @@ public class ReservationMessageManager { m_messages.Remove(userGuid, out _); m_tasks.Remove(userGuid, out _); + + // Log.getLogger().info($"ReservationMessageManager deleteReservationMessage UserGuid : {userGuid}, m_messages : {JsonConvert.SerializeObject(m_messages)}, " + + // $"m_tasks : {JsonConvert.SerializeObject(m_tasks)}"); } private ServerMessage.Types.GS2GS_ACK_RESERVATION_ENTER_TO_SERVER? getMessage(USER_GUID userGuid) diff --git a/ServerCommon/Contents/ReservationEnterToServer/ReservationUserManager.cs b/ServerCommon/Contents/ReservationEnterToServer/ReservationUserManager.cs index 42bc352..3861572 100644 --- a/ServerCommon/Contents/ReservationEnterToServer/ReservationUserManager.cs +++ b/ServerCommon/Contents/ReservationEnterToServer/ReservationUserManager.cs @@ -8,6 +8,9 @@ using ServerBase; namespace ServerCommon; + +// HANDOVER: 플레이어의 GameServer 접속을 위한 사전 예약 관리 클래스 이다. + public class ReservationUserManager { private ServerLogicBase? m_server_logic { get; set; } diff --git a/ServerCommon/Contents/ReturnUserToServer/ReturnUserManager.cs b/ServerCommon/Contents/ReturnUserToServer/ReturnUserManager.cs index 7c9ae41..a26eee5 100644 --- a/ServerCommon/Contents/ReturnUserToServer/ReturnUserManager.cs +++ b/ServerCommon/Contents/ReturnUserToServer/ReturnUserManager.cs @@ -3,6 +3,9 @@ namespace ServerCommon; + +// HANDOVER: 플레이어의 직전 접속한 GameServer 정보와 함께 활용할 플레이어 식별용 클래스 이다. + public class ReturnUserManager { private ConcurrentDictionary m_return_user_ids { get; set; } = new(); diff --git a/ServerCommon/Doc/Global/BannerDoc.cs b/ServerCommon/Doc/Global/BannerDoc.cs new file mode 100644 index 0000000..eb677f3 --- /dev/null +++ b/ServerCommon/Doc/Global/BannerDoc.cs @@ -0,0 +1,94 @@ +using Newtonsoft.Json; +using ServerBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class BannerAttrib : AttribBase + { + [JsonProperty("banner_id")] + public int BannerId { get; set; } = 0; + + [JsonProperty("banner_type")] + public BannerType BannerType { get; set; } = BannerType.None; + + [JsonProperty("start_time")] + public DateTime StartTime { get; set; } = new(); + + [JsonProperty("end_time")] + public DateTime EndTime { get; set; } = new(); + + [JsonProperty("order_id")] + public int OrderId { get; set; } = 0; + + [JsonProperty("image_url")] + public Dictionary ImageUrl { get; set; } = new(); + + [JsonProperty("link_address")] + public Dictionary LinkAddress { get; set; } = new(); + + public BannerAttrib() + : base(typeof(BannerAttrib).Name) + { } + } + + //============================================================================================= + // PK(Partition Key) : "banner#global" + // SK(Sort Key) : "banner_id" + // DocType : BannerDoc + // LandAttrib : {} + // ... + //============================================================================================= + public class BannerDoc : DynamoDbDocBase + { + private static string getPrefixOfPK() { return "banner#"; } + private static string getPrefixOfSK() { return ""; } + + public BannerDoc() + : base(typeof(BannerDoc).Name) + { + appendAttribWrapperAll(); + } + + public BannerDoc(int bannerId) + : base(typeof(BannerDoc).Name) + { + setCombinationKeyForPKSK(DynamoDbClient.PK_GLOBAL, bannerId.ToString()); + + appendAttribWrapperAll(); + + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return getPrefixOfPK(); + } + + protected override string onGetPrefixOfSK() + { + return getPrefixOfSK(); + } + + protected override string onMakeSK() + { + return $"{onGetPrefixOfSK()}{getCombinationKeyForSK()}"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + getPrimaryKey().fillUpSK(sortKey); + + return ServerErrorCode.Success; + } + } +} diff --git a/ServerCommon/Doc/Global/CaliumStorageDoc.cs b/ServerCommon/Doc/Global/CaliumStorageDoc.cs index 7405da9..697c4e0 100644 --- a/ServerCommon/Doc/Global/CaliumStorageDoc.cs +++ b/ServerCommon/Doc/Global/CaliumStorageDoc.cs @@ -12,7 +12,8 @@ namespace ServerCommon; public class CaliumConverterInfo { // 잔여 수량 - [JsonProperty("converter_total_calium")] + [JsonProperty("converter_total_calium")] + [JsonConverter(typeof(StringToDoubleEpsilonRoundConverter))] public double TotalCalium { get; set; } = 0.0; // 마지막 누적 시간 @@ -21,6 +22,7 @@ public class CaliumConverterInfo // Daily 누적 Calium 수 [JsonProperty("converter_daily_fill_up_standard_calium")] + [JsonConverter(typeof(StringToDoubleEpsilonRoundConverter))] public double DailyFillUpStandardCalium { get; set; } = 0.0; // 컨버터 칼리움 전환 비율 @@ -29,6 +31,7 @@ public class CaliumConverterInfo // 누적 총량 [JsonProperty("converter_cumulative_calium")] + [JsonConverter(typeof(StringToDoubleEpsilonRoundConverter))] public double ConverterCumulativeCalium { get; set; } = 0.0; } @@ -43,6 +46,7 @@ public class CaliumOperatorInfo { // 운영 승인 누적량 [JsonProperty("operator_total_calium")] + [JsonConverter(typeof(StringToDoubleEpsilonRoundConverter))] public double TotalCalium { get; set; } = 0.0; // 운영 승인 누적 날짜 @@ -51,6 +55,7 @@ public class CaliumOperatorInfo // 운영용 사용 가능량 [JsonProperty("operator_calium")] + [JsonConverter(typeof(StringToDoubleEpsilonRoundConverter))] public double OperatorCalium { get; set; } = 0.0; } diff --git a/ServerCommon/Doc/Global/GameMatchSettings/GameModeMatchSettingsDoc.cs b/ServerCommon/Doc/Global/GameMatchSettings/GameModeMatchSettingsDoc.cs new file mode 100644 index 0000000..f6e5871 --- /dev/null +++ b/ServerCommon/Doc/Global/GameMatchSettings/GameModeMatchSettingsDoc.cs @@ -0,0 +1,80 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; + +public class GameModeMatchSettingsAttrib : AttribBase +{ + // 기획문서의 Filter Id + [JsonProperty("GameModeMatchID")] public int GameModeMatchID { get; set; } + + [JsonProperty("GameModeMatchDescription")] + public string GameModeMatchDescription { get; set; } = string.Empty; + + // 매칭 대기 시간 + [JsonProperty("MatchWaitTimeSec")] public int MatchWaitTimeSec { get; set; } + + // 매칭 재시도 횟수 + [JsonProperty("MatchRetryCount")] public int MatchRetryCount { get; set; } + + // 입장 방식 + [JsonProperty("JoinInProgress")] public JoinInProgressType JoinInProgress { get; set; } = JoinInProgressType.None; + + //=================================== + // MMR - 현재 미정 (기획수정이 예상되므로 운영툴에 반영하지 말 것) + // 예정 + [JsonProperty("MmrCheck")] public bool MmrCheck { get; set; } + + // 예정 + [JsonProperty("GameModeMmrID")] public int GameModeMmrID { get; set; } + + // 예정 + [JsonProperty("MmrExpansionPhase")] public int MmrExpansionPhase { get; set; } + + // 예정 - 파티원 입장 가능 인원 설정 + [JsonProperty("PartyMatchPlayerCountAbleArray")] public List PartyMatchPlayerCountAbleArray { get; set; } = new(); + + // 예정 - 팀 배정 방식 + [JsonProperty("TeamAssignment")] public TeamAssignmentType TeamAssignment { get; set; } = TeamAssignmentType.Random; + //=================================== + + + // 입장 최대 시간 + // [JsonProperty("JoinInMaxTimeSec")] public int JoinInMaxTimeSec { get; set; } + // 입장 불가 시간 + // [JsonProperty("EntranceClosingTime")] public int EntranceClosingTime { get; set; } + + public GameModeMatchSettingsAttrib() + : base(nameof(GameModeMatchSettingsAttrib)) + { + } +} + +public class GameModeMatchSettingsDoc : DynamoDbDocBase +{ + public GameModeMatchSettingsDoc() : base(nameof(GameModeMatchSettingsDoc)) + { + setCombinationKeyForPK(DynamoDbClient.PK_GLOBAL); + appendAttribWrapperAll(); + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return "management_game_match_settings#"; + } + + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(sortKey); + return ServerErrorCode.Success; + } +} diff --git a/ServerCommon/Doc/Global/GameMatchSettings/GameModeSettingsDoc.cs b/ServerCommon/Doc/Global/GameMatchSettings/GameModeSettingsDoc.cs new file mode 100644 index 0000000..bc8bbc8 --- /dev/null +++ b/ServerCommon/Doc/Global/GameMatchSettings/GameModeSettingsDoc.cs @@ -0,0 +1,53 @@ +using MetaAssets; +using ServerBase; + +namespace ServerCommon; + +public class GameModeSettingsAttrib : AttribBase +{ + public readonly int GameModeID; + // public readonly string GameModeDescription; + // public readonly GameGenreType GameGenreType; + // public readonly GameModeType GameModeType; + // public readonly int GameModeConfigID; + // public readonly int GameModeCommonID; + + public readonly int GameModeMatchID; + + // public readonly int GameModeRewardGroupID; + // public readonly int GameModeOptionID; + // public readonly int GameModeStartID; + + public readonly int GameModeTeamID; + public readonly int InstanceGroupID; + public GameModeSettingsAttrib() : base(nameof(GameModeSettingsAttrib)) + { + } +} + +public class GameModeSettingsDoc: DynamoDbDocBase +{ + public GameModeSettingsDoc() : base(nameof(GameModeSettingsDoc)) + { + setCombinationKeyForPK(DynamoDbClient.PK_GLOBAL); + appendAttribWrapperAll(); + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return "management_game_mode_settings#"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(sortKey); + return ServerErrorCode.Success; + } +} diff --git a/ServerCommon/Doc/Global/GameMatchSettings/GameModeTeamDoc.cs b/ServerCommon/Doc/Global/GameMatchSettings/GameModeTeamDoc.cs new file mode 100644 index 0000000..68a8947 --- /dev/null +++ b/ServerCommon/Doc/Global/GameMatchSettings/GameModeTeamDoc.cs @@ -0,0 +1,39 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; + +public class GameModeTeamSettingsAttrib : AttribBase +{ + public GameModeTeamSettingsAttrib() : base(nameof(GameModeTeamSettingsAttrib)) { } + + [JsonProperty("id")] public int ID { get; set; } + [JsonProperty("minTeam")] public int MinTeam { get; set; } + [JsonProperty("maxTeam")] public int MaxTeam { get; set; } + [JsonProperty("minTeamPlayers")] public List MinTeamPlayers { get; set; } = new(); + [JsonProperty("maxTeamPlayers")] public List MaxTeamPlayers { get; set; } = new(); + [JsonProperty("anchorAssignmentType")] public AnchorAssignmentType AnchorAssignmentType { get; set; } +} + +public class GameModeTeamSettingsDoc : DynamoDbDocBase +{ + public GameModeTeamSettingsDoc() : base(nameof(GameModeTeamSettingsDoc)) + { + setCombinationKeyForPK(DynamoDbClient.PK_GLOBAL); + appendAttribWrapper(new AttribWrapper()); + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + protected override string onGetPrefixOfPK() + { + return "management_game_team_settings#"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(sortKey); + return ServerErrorCode.Success; + } +} diff --git a/ServerCommon/Doc/Global/RankDoc.cs b/ServerCommon/Doc/Global/RankDoc.cs new file mode 100644 index 0000000..0530440 --- /dev/null +++ b/ServerCommon/Doc/Global/RankDoc.cs @@ -0,0 +1,101 @@ +using Newtonsoft.Json; +using ServerBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankAttrib : AttribBase + { + [JsonProperty("ranking_guid")] + public string RankingGuid { get; set; } = string.Empty; + + [JsonProperty("ranker_guid")] + public string RankerGuid { get; set; } = string.Empty; + + [JsonProperty("ranker_entity_type")] + public EntityType RankerEntityType { get; set; } = EntityType.None; + + [JsonProperty("prev_rank_num")] + public int PrevRankNum { get; set; } = 0; + + [JsonProperty("rank_num")] + public int Rank { get; set; } = 0; + + [JsonProperty("score")] + public int Score { get; set; } = 0; + + [JsonProperty("score_type")] + public ScoreType ScoreType { get; set; } = ScoreType.None; + + public RankAttrib() + : base(typeof(RankAttrib).Name, false) + { } + } + + //============================================================================================= + // PK(Partition Key) : "ranking#ranking_guid" + // SK(Sort Key) : "rank#rank(숫자)" + // DocType : RankDoc + // RankAttrib : {} + // ... + //============================================================================================= + public class RankDoc : DynamoDbDocBase + { + private static string getPrefixOfPK() { return "ranking#"; } + public static string getPrefixOfSK() { return "rank#"; } + + public RankDoc() + : base(typeof(RankDoc).Name) + { + appendAttribWrapperAll(); + } + + public RankDoc(string rankingGuid, int rank) + : base(typeof(RankDoc).Name) + { + setCombinationKeyForPKSK(rankingGuid, rank.ToString()); + + appendAttribWrapperAll(); + + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return getPrefixOfPK(); + } + + protected override string onGetPrefixOfSK() + { + return getPrefixOfSK(); + } + + protected override string onMakeSK() + { + return $"{onGetPrefixOfSK()}{getCombinationKeyForSK()}"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + var error_code = onFillupCombinationKeyForSK(sortKey, out var fillup_key); + if (error_code.isFail()) + { + return error_code; + } + + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(fillup_key); + + return ServerErrorCode.Success; + } + } +} diff --git a/ServerCommon/Doc/Global/RankerDoc.cs b/ServerCommon/Doc/Global/RankerDoc.cs new file mode 100644 index 0000000..0aed2dd --- /dev/null +++ b/ServerCommon/Doc/Global/RankerDoc.cs @@ -0,0 +1,99 @@ +using Newtonsoft.Json; +using ServerBase; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankerAttrib : AttribBase + { + [JsonProperty("ranking_guid")] + public string RankingGuid { get; set; } = string.Empty; + + [JsonProperty("ranker_guid")] + public string RankerGuid { get; set; } = string.Empty; + + [JsonProperty("ranker_entity_type")] + public EntityType RankerEntityType { get; set; } = EntityType.None; + + [JsonProperty("score")] + public int Score { get; set; } = 0; + + [JsonProperty("score_type")] + public ScoreType ScoreType { get; set; } = ScoreType.None; + + [JsonProperty("update_time")] + public DateTime UpdateTime { get; set; } = new(); + + public RankerAttrib() + : base(typeof(RankerAttrib).Name, false) + { } + } + + //============================================================================================= + // PK(Partition Key) : "ranking#ranking_guid" + // SK(Sort Key) : "ranker#ranker_guid" + // DocType : RankerDoc + // RankAttrib : {} + // ... + //============================================================================================= + public class RankerDoc : DynamoDbDocBase + { + private static string getPrefixOfPK() { return "ranking#"; } + public static string getPrefixOfSK() { return "ranker#"; } + + public RankerDoc() + : base(typeof(RankerDoc).Name) + { + appendAttribWrapperAll(); + } + + public RankerDoc(string rankingGuid, string rankerGuid) + : base(typeof(RankerDoc).Name) + { + setCombinationKeyForPKSK(rankingGuid, rankerGuid); + + appendAttribWrapperAll(); + + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return getPrefixOfPK(); + } + + protected override string onGetPrefixOfSK() + { + return getPrefixOfSK(); + } + + protected override string onMakeSK() + { + return $"{onGetPrefixOfSK()}{getCombinationKeyForSK()}"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + var error_code = onFillupCombinationKeyForSK(sortKey, out var fillup_key); + if (error_code.isFail()) + { + return error_code; + } + + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(fillup_key); + + return ServerErrorCode.Success; + } + } +} diff --git a/ServerCommon/Doc/Global/RankerSnapshotDoc.cs b/ServerCommon/Doc/Global/RankerSnapshotDoc.cs new file mode 100644 index 0000000..98dc337 --- /dev/null +++ b/ServerCommon/Doc/Global/RankerSnapshotDoc.cs @@ -0,0 +1,108 @@ +using Newtonsoft.Json; +using ServerBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankerSnapshotAttrib : AttribBase + { + [JsonProperty("ranking_guid")] + public string RankingGuid { get; set; } = string.Empty; + + [JsonProperty("snapshot_index")] + public int SnapshotIndex { get; set; } = 0; + + [JsonProperty("snapshot_time")] + public DateTime SnapshotTime { get; set; } = new(); + + [JsonProperty("ranker_guid")] + public string RankerGuid { get; set; } = string.Empty; + + [JsonProperty("ranker_entity_type")] + public EntityType RankerEntityType { get; set; } = EntityType.None; + + [JsonProperty("score")] + public int Score { get; set; } = 0; + + [JsonProperty("score_type")] + public ScoreType ScoreType { get; set; } = ScoreType.None; + + [JsonProperty("update_time")] + public DateTime UpdateTime { get; set; } = new(); + + [JsonProperty("rank_num")] + public int RankNum { get; set; } = 0; + + public RankerSnapshotAttrib() + : base(typeof(RankerSnapshotAttrib).Name, false) + { } + } + + //============================================================================================= + // PK(Partition Key) : "ranking#ranking_guid" + // SK(Sort Key) : "ranker_snapshot#index#ranker_guid" + // DocType : RankDoc + // RankAttrib : {} + // ... + //============================================================================================= + public class RankerSnapshotDoc : DynamoDbDocBase + { + private static string getPrefixOfPK() { return "ranking#"; } + public static string getPrefixOfSK() { return "ranker_snapshot#"; } + + public RankerSnapshotDoc() + : base(typeof(RankerSnapshotDoc).Name) + { + appendAttribWrapperAll(); + } + + public RankerSnapshotDoc(string rankingGuid, int index, string rankerGuid) + : base(typeof(RankerSnapshotDoc).Name) + { + var key_for_sk = $"{index}#{rankerGuid}"; + setCombinationKeyForPKSK(rankingGuid, key_for_sk); + + appendAttribWrapperAll(); + + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return getPrefixOfPK(); + } + + protected override string onGetPrefixOfSK() + { + return getPrefixOfSK(); + } + + protected override string onMakeSK() + { + return $"{onGetPrefixOfSK()}{getCombinationKeyForSK()}"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + var error_code = onFillupCombinationKeyForSK(sortKey, out var fillup_key); + if (error_code.isFail()) + { + return error_code; + } + + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(fillup_key); + + return ServerErrorCode.Success; + } + } +} diff --git a/ServerCommon/Doc/Global/RankingDoc.cs b/ServerCommon/Doc/Global/RankingDoc.cs new file mode 100644 index 0000000..8945245 --- /dev/null +++ b/ServerCommon/Doc/Global/RankingDoc.cs @@ -0,0 +1,69 @@ +using Newtonsoft.Json; +using ServerBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankingAttrib : AttribBase + { + [JsonProperty("ranking_guid")] + public string RankingGuid { get; set; } = string.Empty; + + [JsonProperty("ranking_meta_id")] + public int RankingMetaId { get; set; } = 0; + + [JsonProperty("next_times")] + public Dictionary NextTimes { get; set; } = new(); + + [JsonProperty("lastest_snapshot_index")] + public int LastestSnapshotIndex { get; set; } = 0; + + + public RankingAttrib() + : base(typeof(RankingAttrib).Name, false) + { } + } + + //============================================================================================= + // PK(Partition Key) : "ranking#ranking_guid" + // SK(Sort Key) : + // DocType : RankingDoc + // RankAttrib : {} + // ... + //============================================================================================= + public class RankingDoc : DynamoDbDocBase + { + private static string getPrefixOfPK() { return "ranking#"; } + private static string getPrefixOfSK() { return ""; } + + public RankingDoc() + : base(typeof(RankingDoc).Name) + { + appendAttribWrapperAll(); + } + + public RankingDoc(string rankingGuid) + : base(typeof(RankingDoc).Name) + { + setCombinationKeyForPK(rankingGuid); + + appendAttribWrapperAll(); + + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return getPrefixOfPK(); + } + } +} diff --git a/ServerCommon/Doc/Global/RankingScheduleDoc.cs b/ServerCommon/Doc/Global/RankingScheduleDoc.cs new file mode 100644 index 0000000..c1e2077 --- /dev/null +++ b/ServerCommon/Doc/Global/RankingScheduleDoc.cs @@ -0,0 +1,101 @@ +using Newtonsoft.Json; +using ServerBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankingScheduleAttrib : AttribBase + { + [JsonProperty("ranking_guid")] + public string RankingGuid { get; set; } = string.Empty; + + [JsonProperty("ranking_meta_id")] + public int RankingMetaId { get; set; } = 0; + + [JsonProperty("event_action_score_group_id")] + public int EventActionScoreGroupId { get; set; } = 0; + + [JsonProperty("start_time")] + public DateTime StartTime { get; set; } = new(); + + [JsonProperty("end_time")] + public DateTime EndTime { get; set; } = new(); + + [JsonProperty("interval_base_time")] + public DateTime IntervalBaseTime { get; set; } = new(); + + [JsonProperty("interval_times")] + public Dictionary IntervalTimes { get; set; } = new(); + + public RankingScheduleAttrib() + : base(typeof(RankingScheduleAttrib).Name, false) + { } + } + + //============================================================================================= + // PK(Partition Key) : "ranking_schedule#global" + // SK(Sort Key) : "ranking_guid" + // DocType : RankingScheduleDoc + // LandAttrib : {} + // ... + //============================================================================================= + public class RankingScheduleDoc : DynamoDbDocBase + { + private static string getPrefixOfPK() { return "ranking_schedule#"; } + private static string getPrefixOfSK() { return ""; } + + public RankingScheduleDoc() + : base(typeof(RankingScheduleDoc).Name) + { + appendAttribWrapperAll(); + } + + public RankingScheduleDoc(string rankingGuid) + : base(typeof(RankingScheduleDoc).Name) + { + setCombinationKeyForPKSK(DynamoDbClient.PK_GLOBAL, rankingGuid); + + appendAttribWrapperAll(); + + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return getPrefixOfPK(); + } + + protected override string onGetPrefixOfSK() + { + return getPrefixOfSK(); + } + + protected override string onMakeSK() + { + return $"{onGetPrefixOfSK()}{getCombinationKeyForSK()}"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + var error_code = onFillupCombinationKeyForSK(sortKey, out var fillup_key); + if (error_code.isFail()) + { + return error_code; + } + + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(fillup_key); + + return ServerErrorCode.Success; + } + } +} diff --git a/ServerCommon/Doc/Global/RankingSnapshotDoc.cs b/ServerCommon/Doc/Global/RankingSnapshotDoc.cs new file mode 100644 index 0000000..9ce9a71 --- /dev/null +++ b/ServerCommon/Doc/Global/RankingSnapshotDoc.cs @@ -0,0 +1,90 @@ +using Newtonsoft.Json; +using ServerBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankingSnapshotAttrib : AttribBase + { + [JsonProperty("ranking_guid")] + public string RankingGuid { get; set; } = string.Empty; + + [JsonProperty("snapshot_index")] + public int SnapshotIndex { get; set; } = 0; + + [JsonProperty("snapshot_time")] + public DateTime SnapshotTime { get; set; } = new(); + + + public RankingSnapshotAttrib() + : base(typeof(RankingSnapshotAttrib).Name, false) + { } + } + + //============================================================================================= + // PK(Partition Key) : "ranking#ranking_guid" + // SK(Sort Key) : "ranking_snapshot#index" + // DocType : RankDoc + // RankAttrib : {} + // ... + //============================================================================================= + public class RankingSnapshotDoc : DynamoDbDocBase + { + private static string getPrefixOfPK() { return "ranking#"; } + public static string getPrefixOfSK() { return "ranking_snapshot#"; } + + public RankingSnapshotDoc() + : base(typeof(RankingSnapshotDoc).Name) + { + appendAttribWrapperAll(); + } + + public RankingSnapshotDoc(string rankingGuid, int index) + : base(typeof(RankingSnapshotDoc).Name) + { + setCombinationKeyForPKSK(rankingGuid, index.ToString()); + + appendAttribWrapperAll(); + + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return getPrefixOfPK(); + } + + protected override string onGetPrefixOfSK() + { + return getPrefixOfSK(); + } + + protected override string onMakeSK() + { + return $"{onGetPrefixOfSK()}{getCombinationKeyForSK()}"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + var error_code = onFillupCombinationKeyForSK(sortKey, out var fillup_key); + if (error_code.isFail()) + { + return error_code; + } + + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(fillup_key); + + return ServerErrorCode.Success; + } + } +} diff --git a/ServerCommon/Doc/Global/UgcNpcRankDoc.cs b/ServerCommon/Doc/Global/UgcNpcRankDoc.cs index b96b0fe..76fadd7 100644 --- a/ServerCommon/Doc/Global/UgcNpcRankDoc.cs +++ b/ServerCommon/Doc/Global/UgcNpcRankDoc.cs @@ -8,12 +8,12 @@ namespace ServerCommon; public class UgcNpcRankAttrib : AttribBase { - /// - /// rank 정보 - /// key: {ownerGuid}_{ugcnpcMetaGuid} - /// value: score - /// - /// UgcNpcRankHelper.makeRankKey() + //============================================================================================= + // TODO: 유저 4천명이상이 본 이벤트 발생시, 본 정보가 삭제전까지는 저장 크기의 임계치를 돌파할 가능성이 높아서 리펙토링 해야 함 !!! + // Dictionary <= UserGuid_BeaconMetaGuid, score + // UgcNpcRankHelper.makeRankKey() 사용 + // 리펙토링의 방향 : 개인별 기록 => 이벤트 발생시 개인별 기록을 읽고 순위 산정 (전체 소요 시간 : 1시간내에 충분히 가능 !!!) + //============================================================================================= [JsonProperty("data")] public Dictionary ranks { get; set; } = new(); public UgcNpcRankAttrib() : base(nameof(UgcNpcRankAttrib), false) diff --git a/ServerCommon/Doc/Global/UserNicknameRegistryDoc.cs b/ServerCommon/Doc/Global/UserNicknameRegistryDoc.cs index 394ce1c..a27e147 100644 --- a/ServerCommon/Doc/Global/UserNicknameRegistryDoc.cs +++ b/ServerCommon/Doc/Global/UserNicknameRegistryDoc.cs @@ -43,7 +43,7 @@ namespace ServerCommon } //============================================================================================= - // PK(Partition Key) : "user_nickname_registry#" + // PK(Partition Key) : "user_nickname_registry#global" // SK(Sort Key) : "user_nickname" // DocType : UserNicknameRegistryDoc // UserNicknameRegistryAttrib : {} diff --git a/ServerCommon/Doc/Global/WorldEventGlobalScoreDoc.cs b/ServerCommon/Doc/Global/WorldEventGlobalScoreDoc.cs new file mode 100644 index 0000000..96ee563 --- /dev/null +++ b/ServerCommon/Doc/Global/WorldEventGlobalScoreDoc.cs @@ -0,0 +1,81 @@ +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; + +/// +/// Doc 내부에 저장될 실제 데이터 속성 클래스 +/// +public class WorldEventGlobalScoreAttrib : AttribBase +{ + [JsonProperty("world_event_id")] public int WorldEventId { get; set; } + + [JsonProperty("score")] public long Score { get; set; } + + [JsonProperty("update_time")] public DateTime UpdateTime { get; set; } = new(); + + public WorldEventGlobalScoreAttrib() + : base(nameof(WorldEventGlobalScoreAttrib), false) + { + } +} + +/// +/// DynamoDB의 'PlayerWorldEventScore' 테이블과 매핑되는 문서 클래스. +/// WorldEventTotalScoreDoc 패턴에 따라 PK/SK 조합 및 Attrib 중첩 구조를 가집니다. +/// +public class WorldEventGlobalScoreDoc : DynamoDbDocBase +{ + public static string getPrefixOfPK() { return "world_event_total_score#"; } + public static string getPrefixOfSK() { return ""; } + + public WorldEventGlobalScoreDoc() + : base(nameof(WorldEventGlobalScoreDoc)) + { + setCombinationKeyForPK(DynamoDbClient.PK_GLOBAL); + appendAttribWrapperAll(); + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + public WorldEventGlobalScoreDoc(int worldEventId) + : base(nameof(WorldEventGlobalScoreDoc)) + { + setCombinationKeyForPKSK(DynamoDbClient.PK_GLOBAL, worldEventId.ToString()); + appendAttribWrapperAll(); + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return getPrefixOfPK(); + } + + protected override string onGetPrefixOfSK() + { + return getPrefixOfSK(); + } + + protected override string onMakeSK() + { + return $"{onGetPrefixOfSK()}{getCombinationKeyForSK()}"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + var errorCode = onFillupCombinationKeyForSK(sortKey, out var fillupKey); + if (errorCode.isFail()) + { + return errorCode; + } + + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(fillupKey); + + return ServerErrorCode.Success; + } +} diff --git a/ServerCommon/Doc/Global/WorldEventScheduleDoc.cs b/ServerCommon/Doc/Global/WorldEventScheduleDoc.cs new file mode 100644 index 0000000..3179ffd --- /dev/null +++ b/ServerCommon/Doc/Global/WorldEventScheduleDoc.cs @@ -0,0 +1,120 @@ +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; + +public class WorldEventScheduleAttrib : AttribBase, IEquatable +{ + [JsonProperty("id")] public Int32 Id { get; set; } = 999999; + [JsonProperty("start_time")] public DateTime StartTime { get; set; } = DateTime.MinValue; + [JsonProperty("end_time")] public DateTime EndTime { get; set; } = DateTime.MinValue; + + [JsonProperty("global_event_contribution_point_max")] + public int GlobalEventContributionPointMax { get; set; } = 100000; + + [JsonProperty("global_event_action_group_id")] + public Int32 GlobalEventActionGroupId { get; set; } = 0; + + [JsonProperty("personal_event_action_group_id")] + public Int32 PersonalEventActionGroupId { get; set; } = 0; + + [JsonProperty("is_active")] public bool IsActive { get; set; } = false; + + public WorldEventScheduleAttrib() : base(nameof(WorldEventScheduleAttrib)) { } + + public override string ToString() + { + return + $"id:{Id}, is_active:{IsActive}, start_time:{StartTime}, end_time:{EndTime}, global_event_action_group_id:{GlobalEventActionGroupId}, personal_event_action_group_id:{PersonalEventActionGroupId}"; + } + + public bool Equals(WorldEventScheduleAttrib? other) + { + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return Id == other.Id && StartTime.Equals(other.StartTime) && EndTime.Equals(other.EndTime) && + GlobalEventContributionPointMax == other.GlobalEventContributionPointMax && + GlobalEventActionGroupId == other.GlobalEventActionGroupId && + PersonalEventActionGroupId == other.PersonalEventActionGroupId && + IsActive == other.IsActive; + } +} + +//============================================================================================= +// PK(Partition Key) : "world_event_schedule#global" +// SK(Sort Key) : "id" +// DocType : WorldEventScheduleDoc +// WorldEventScheduleAttrib : {} +// ... +//============================================================================================= + +public class WorldEventScheduleDoc : DynamoDbDocBase +{ + private static string getPrefixOfPK() { return "world_event_schedule#"; } + private static string getPrefixOfSK() { return ""; } + + public WorldEventScheduleDoc() + : base(nameof(WorldEventScheduleDoc)) + { + setCombinationKeyForPK(DynamoDbClient.PK_GLOBAL); + appendAttribWrapperAll(); + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + public WorldEventScheduleDoc(int id) + : base(nameof(WorldEventScheduleDoc)) + { + setCombinationKeyForPKSK(DynamoDbClient.PK_GLOBAL, id.ToString()); + appendAttribWrapperAll(); + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return getPrefixOfPK(); + } + + protected override string onGetPrefixOfSK() + { + return getPrefixOfSK(); + } + + protected override string onMakeSK() + { + return $"{onGetPrefixOfSK()}{getCombinationKeyForSK()}"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + var errorCode = onFillupCombinationKeyForSK(sortKey, out var fillupKey); + if (errorCode.isFail()) + { + return errorCode; + } + + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(fillupKey); + + return ServerErrorCode.Success; + } + + public override string ToString() + { + var attrib = getAttrib(); + return attrib?.ToString() ?? string.Empty; + } +} diff --git a/ServerCommon/Doc/Global/WorldEventScoreDoc.cs b/ServerCommon/Doc/Global/WorldEventScoreDoc.cs new file mode 100644 index 0000000..32ac9fd --- /dev/null +++ b/ServerCommon/Doc/Global/WorldEventScoreDoc.cs @@ -0,0 +1,83 @@ +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; + +/// +/// Doc 내부에 저장될 실제 데이터 속성 클래스 +/// +public class WorldEventScoreAttrib : AttribBase +{ + [JsonProperty("world_event_id")] public int WorldEventId { get; set; } + + [JsonProperty("user_guid")] public string UserGuid { get; set; } = string.Empty; + + [JsonProperty("score")] public long Score { get; set; } + + [JsonProperty("update_time")] public DateTime UpdateTime { get; set; } = new(); + + public WorldEventScoreAttrib() + : base(nameof(WorldEventScoreAttrib), false) + { + } +} + +/// +/// DynamoDB의 'PlayerWorldEventScore' 테이블과 매핑되는 문서 클래스. +/// WorldEventScoreDoc 패턴에 따라 PK/SK 조합 및 Attrib 중첩 구조 +/// +public class WorldEventScoreDoc : DynamoDbDocBase +{ + public static string getPrefixOfPK() { return "world_event_score#"; } + public static string getPrefixOfSK() { return ""; } + + public WorldEventScoreDoc() + : base(nameof(WorldEventScoreDoc)) + { + appendAttribWrapperAll(); + } + + public WorldEventScoreDoc(int worldEventId, string userGuid) + : base(nameof(WorldEventScoreDoc)) + { + setCombinationKeyForPKSK(worldEventId.ToString(), userGuid.ToString()); + + appendAttribWrapperAll(); + + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return getPrefixOfPK(); + } + + protected override string onGetPrefixOfSK() + { + return getPrefixOfSK(); + } + + protected override string onMakeSK() + { + return $"{onGetPrefixOfSK()}{getCombinationKeyForSK()}"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + var errorCode = onFillupCombinationKeyForSK(sortKey, out var fillupKey); + if (errorCode.isFail()) + { + return errorCode; + } + + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(fillupKey); + + return ServerErrorCode.Success; + } +} diff --git a/ServerCommon/Doc/OwnerContents/GameModeBestRecordDoc.cs b/ServerCommon/Doc/OwnerContents/GameModeBestRecordDoc.cs new file mode 100644 index 0000000..59da5ab --- /dev/null +++ b/ServerCommon/Doc/OwnerContents/GameModeBestRecordDoc.cs @@ -0,0 +1,129 @@ +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; + +public class GameModeBestRecord +{ + public GameModeBestRecord() + { + } + + public GameModeBestRecord(int gameModeId, int instanceMetaId, long score) + { + GameModeId = gameModeId; + InstanceMetaId = instanceMetaId; + Score = score; + } + + [JsonProperty("best_records")] public int GameModeId { get; init; } + + [JsonProperty("instance_meta_id")] public int InstanceMetaId { get; init; } + + [JsonProperty("score")] public long Score { get; init; } +} + +public class GameModeBestRecordAttrib : AttribBase +{ + [JsonProperty("user_guid")] public string UserGuid { get; set; } + [JsonProperty("best_records")] public Dictionary Records { get; set; } + + public GameModeBestRecordAttrib(string userGuid, GameModeBestRecord[] records) : base(nameof(GameModeBestRecordAttrib), false) + { + UserGuid = userGuid; + Records = records.ToDictionary(x => $"{x.GameModeId}#{x.InstanceMetaId}", x => x); + } + + public GameModeBestRecordAttrib() : base(nameof(GameModeBestRecordAttrib), false) + { + UserGuid = string.Empty; + Records = new Dictionary(); + } +} + +/// +/// 특정 유저(userGuid)의 게임 모드(gameModeId)와 맵(instanceMetaId)에 대한 최고 기록을 저장하는 DynamoDB 문서입니다. +/// +/// PK (Partition Key): "game_mode_best_record#<userGuid>"
+/// SK (Sort Key): "<gameModeId>#<instanceMetaId>" +///
+///
+/// +/// 1. 특정 유저의 특정 게임 모드/맵 기록 조회 (GetItem) +/// +/// { +/// "TableName": "caliverse-main-dev", +/// "Key": { +/// "pk": { "S": "game_mode_best_record#some-user-guid" }, +/// "sk": { "S": "1001#2001" } +/// } +/// } +/// +/// +/// +/// 2. 특정 유저의 모든 게임 모드 기록 조회 (Query) +/// +/// { +/// "TableName": "caliverse-main-dev", +/// "KeyConditionExpression": "pk = :pk_val", +/// "ExpressionAttributeValues": { +/// ":pk_val": { "S": "game_mode_best_record#some-user-guid" } +/// } +/// } +/// +/// +/// +/// 3. 특정 유저의 특정 게임 모드에 대한 모든 맵 기록 조회 (Query) +/// +/// { +/// "TableName": "caliverse-main-dev", +/// "KeyConditionExpression": "pk = :pk_val AND begins_with(sk, :sk_prefix)", +/// "ExpressionAttributeValues": { +/// ":pk_val": { "S": "game_mode_best_record#some-user-guid" }, +/// ":sk_prefix": { "S": "1001#" } +/// } +/// } +/// +/// +public class GameModeBestRecordDoc: DynamoDbDocBase +{ + public const string PK = "game_mode_best_record#"; + + public GameModeBestRecordDoc(): base(nameof(GameModeBestRecordDoc)){ + appendAttribWrapper(new AttribWrapper()); + } + public GameModeBestRecordDoc(string userGuid) : base(nameof(GameModeBestRecordDoc)) + { + setCombinationKeyForPK(userGuid); + appendAttribWrapper(new AttribWrapper()); + fillUpPrimaryKey(onMakePK(), onMakeSK()); + + } + protected override string onGetPrefixOfPK() + { + return $"{PK}"; + } + + protected override string onGetPrefixOfSK() + { + return ""; + } + + + protected override string onMakePK() + { + return $"{onGetPrefixOfPK()}{getCombinationKeyForPK()}"; + } + + protected override string onMakeSK() + { + return onGetPrefixOfSK(); + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(sortKey); + return ServerErrorCode.Success; + } +} diff --git a/ServerCommon/Doc/OwnerContents/GameModePlayRegulationDoc.cs b/ServerCommon/Doc/OwnerContents/GameModePlayRegulationDoc.cs new file mode 100644 index 0000000..f11e8f6 --- /dev/null +++ b/ServerCommon/Doc/OwnerContents/GameModePlayRegulationDoc.cs @@ -0,0 +1,114 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace ServerCommon; + + +public class MatchRestriction +{ + [JsonProperty("matched_count")] + public Int32 m_current_matched_count { get; set; } = 0; + + [JsonProperty("next_refresh_time")] + public DateTime m_next_refresh_time { get; set; } = DateTimeHelper.Current.Date.AddDays(1); + + public MatchRestriction clone() + { + return new MatchRestriction + { + m_current_matched_count = this.m_current_matched_count, + m_next_refresh_time = this.m_next_refresh_time + }; + } +} + +public class GameModePlayPenalty +{ + [JsonProperty("penalty_count")] public Int32 m_penalty_count { get; set; } = 0; + [JsonProperty("penalty_start_time")] public DateTime m_penalty_start_time { get; set; } = DateTimeHelper.Current; + [JsonProperty("penalty_end_time")] public DateTime m_penalty_end_time { get; set; } = DateTimeHelper.Current; + [JsonProperty("is_max")] public bool m_is_max { get; set; } = false; + + public GameModePlayPenalty clone() + { + return new GameModePlayPenalty + { + m_penalty_count = this.m_penalty_count, + m_penalty_start_time = this.m_penalty_start_time, + m_penalty_end_time = this.m_penalty_end_time, + m_is_max = this.m_is_max + }; + } +} + +public class GameModePlayRegulationAttrib : AttribBase +{ + [JsonProperty("match_restriction")] + public Dictionary m_match_restriction { get; set; } = new(); + + [JsonProperty("play_penalty")] + public Dictionary m_play_penalty { get; set; } = new(); + + public GameModePlayRegulationAttrib() + : base(typeof(QuestAttrib).Name, false) + { } +} + + +//============================================================================================= +// PK(Partition Key) : "gamemode_play_regulation#user_guid" +// SK(Sort Key) : "" +// DocType : GameModePlayRegulationDoc +// QuestAttrib : {} +// ... +//============================================================================================= +public class GameModePlayRegulationDoc : DynamoDbDocBase +{ + public GameModePlayRegulationDoc() : base(typeof(GameModePlayRegulationDoc).Name) + { + appendAttribWrapperAll(); + } + + public GameModePlayRegulationDoc(string ownerGuid) + : base(typeof(GameModePlayRegulationDoc).Name) + { + setCombinationKeyForPK(ownerGuid); + + appendAttribWrapperAll(); + + fillUpPrimaryKey(onMakePK(), onMakeSK()); + + setExceptionHandler(new DynamoDbQueryExceptionNotifier.ExceptionHandler(DynamoDbQueryExceptionNotifier.ConditionalCheckFailed, ServerErrorCode.GameModePlayRegulationDocException)); + } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + + protected override string onGetPrefixOfPK() + { + return "gamemode_play_regulation#"; + } + + protected override string onMakePK() + { + return $"{onGetPrefixOfPK()}{getCombinationKeyForPK()}"; + } + + protected override string onMakeSK() + { + return $"{onGetPrefixOfSK()}{getCombinationKeyForSK()}"; + + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + getPrimaryKey().fillUpSK(sortKey); + return ServerErrorCode.Success; + } + +} \ No newline at end of file diff --git a/ServerCommon/Doc/OwnerContents/QuestDoc.cs b/ServerCommon/Doc/OwnerContents/QuestDoc.cs index 7b7d029..47fa10a 100644 --- a/ServerCommon/Doc/OwnerContents/QuestDoc.cs +++ b/ServerCommon/Doc/OwnerContents/QuestDoc.cs @@ -73,7 +73,7 @@ public class QuestAttrib : AttribBase public List m_completed_idx_strings { get; set; } = new(); public QuestAttrib() - : base(typeof(QuestAttrib).Name) + : base(typeof(QuestAttrib).Name, false) { } } @@ -125,12 +125,6 @@ public class QuestDoc : DynamoDbDocBase } - protected override ServerErrorCode onCheckAndSetPK(string pk) - { - getPrimaryKey().fillUpPK(pk); - return ServerErrorCode.Success; - } - protected override ServerErrorCode onCheckAndSetSK(string sortKey) { getPrimaryKey().fillUpSK(sortKey); diff --git a/ServerCommon/Doc/OwnerContents/QuestMailDoc.cs b/ServerCommon/Doc/OwnerContents/QuestMailDoc.cs index ff2df17..013b5b2 100644 --- a/ServerCommon/Doc/OwnerContents/QuestMailDoc.cs +++ b/ServerCommon/Doc/OwnerContents/QuestMailDoc.cs @@ -96,12 +96,6 @@ public class QuestMailDoc : DynamoDbDocBase } - protected override ServerErrorCode onCheckAndSetPK(string pk) - { - getPrimaryKey().fillUpPK(pk); - return ServerErrorCode.Success; - } - protected override ServerErrorCode onCheckAndSetSK(string sortKey) { getPrimaryKey().fillUpSK(sortKey); diff --git a/ServerCommon/Doc/UserBase/LocationDoc.cs b/ServerCommon/Doc/UserBase/LocationDoc.cs index 9545122..6c6b155 100644 --- a/ServerCommon/Doc/UserBase/LocationDoc.cs +++ b/ServerCommon/Doc/UserBase/LocationDoc.cs @@ -47,6 +47,9 @@ namespace ServerCommon [JsonProperty] public int InstanceMetaId { get; set; } = 0; + + // 게임 인던 진입 시에만 체크 + [JsonProperty] public string GameTeamId { get; set; } = string.Empty; } public class LocationAttrib : AttribBase diff --git a/ServerCommon/EchoSystem/Models/CaliumRollUpResponse.cs b/ServerCommon/EchoSystem/Models/CaliumRollUpResponse.cs index 715c79b..65288e4 100644 --- a/ServerCommon/EchoSystem/Models/CaliumRollUpResponse.cs +++ b/ServerCommon/EchoSystem/Models/CaliumRollUpResponse.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using ServerCore; namespace ServerCommon; @@ -21,7 +22,9 @@ public class RollUpData [JsonProperty("calium_exchange_volume")] public double m_exchange_volume { get; set; } // 컨버터 배정 수량 - [JsonProperty("calium_convertor_volume")] public double m_convertor_volume { get; set; } + [JsonProperty("calium_convertor_volume")] + [JsonConverter(typeof(StringToDoubleEpsilonRoundConverter))] + public double m_convertor_volume { get; set; } // 재원저장소 적재량 ( 교환소 ) [JsonProperty("calium_get_repository_volume")] public double m_exchange_repo_volume { get; set; } @@ -76,7 +79,9 @@ public class RollUpData [JsonProperty("previous")] public PreviousRollUpData m_previous_roll_up { get; set; } = new(); // 컨버터 배정 누적 수량 - [JsonProperty("stack_convertor_volume")] public double m_stack_converter_volume { get; set; } + [JsonProperty("stack_convertor_volume")] + [JsonConverter(typeof(StringToDoubleEpsilonRoundConverter))] + public double m_stack_converter_volume { get; set; } // 기본 배정 물량 [JsonProperty("standard_alloc_amount")] public double m_standard_alloc_amount { get; set; } diff --git a/ServerCommon/EchoSystem/Models/ConverterSyncResponse.cs b/ServerCommon/EchoSystem/Models/ConverterSyncResponse.cs index d0a0686..f80d714 100644 --- a/ServerCommon/EchoSystem/Models/ConverterSyncResponse.cs +++ b/ServerCommon/EchoSystem/Models/ConverterSyncResponse.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using ServerCore; namespace ServerCommon; @@ -22,5 +23,6 @@ public class ConverterSyncData // 현재까지 제공된 칼리움 컨버터 총 누적 수량 [JsonProperty("acc_volume")] + [JsonConverter(typeof(StringToDoubleEpsilonRoundConverter))] public double m_stack_calium { get; set; } } \ No newline at end of file diff --git a/ServerCommon/Entity/Attribute/AccountAttribute.cs b/ServerCommon/Entity/Attribute/AccountAttribute.cs index 1d0641e..8cceb65 100644 --- a/ServerCommon/Entity/Attribute/AccountAttribute.cs +++ b/ServerCommon/Entity/Attribute/AccountAttribute.cs @@ -24,7 +24,7 @@ using ITEM_GUID = System.String; using System.Runtime.InteropServices; using Amazon.S3.Model; using Amazon.Runtime.Telemetry; - +using YamlDotNet.Core.Tokens; namespace ServerCommon; @@ -68,7 +68,15 @@ public class AccountAttribute : EntityAttributeBase, ICopyEntityAttributeFromMet public string ToConnectGameServerName { get; set; } = string.Empty; public string OtpForServerConnect { get; set; } = string.Empty; + public DateTime BlockStartDateTime { get; set; } = DateTime.MaxValue; + public DateTime BlockEndDateTime { get; set; } = DateTime.MaxValue; + + public List BlockPolicy { get; set; } = new(); + + public string BlockReason { get; set; } = string.Empty; + + public AccountAttribute(EntityBase owner) : base(owner, owner) @@ -100,7 +108,10 @@ public class AccountAttribute : EntityAttributeBase, ICopyEntityAttributeFromMet ToConnectGameServerAddress.reset(); OtpForServerConnect = string.Empty; SsoAccountAuthJWT = string.Empty; - + BlockStartDateTime = DateTimeHelper.MaxTime; + BlockEndDateTime = DateTimeHelper.MaxTime; + BlockPolicy.Clear(); + BlockReason = string.Empty; getAttributeState().reset(); } @@ -127,6 +138,10 @@ public class AccountAttribute : EntityAttributeBase, ICopyEntityAttributeFromMet cloned.ToConnectGameServerAddress.Port = ToConnectGameServerAddress.Port; cloned.OtpForServerConnect = OtpForServerConnect; cloned.SsoAccountAuthJWT = SsoAccountAuthJWT; + cloned.BlockStartDateTime = BlockStartDateTime; + cloned.BlockEndDateTime = BlockEndDateTime; + cloned.BlockPolicy.AddRange(BlockPolicy); + cloned.BlockReason = BlockReason; return cloned; } @@ -175,6 +190,10 @@ public class AccountAttribute : EntityAttributeBase, ICopyEntityAttributeFromMet account_base_attrib.LogoutDateTime = LogoutDateTime; account_base_attrib.AccessToken = AccessToken; account_base_attrib.SsoAccountAuthJWT = SsoAccountAuthJWT; + account_base_attrib.BlockStartDateTime = BlockStartDateTime; + account_base_attrib.BlockEndDateTime = BlockEndDateTime; + account_base_attrib.BlockPolicy = BlockPolicy; + account_base_attrib.BlockReason = BlockReason; if(false == isForQuery) { @@ -239,6 +258,10 @@ public class AccountAttribute : EntityAttributeBase, ICopyEntityAttributeFromMet OtpForServerConnect = other_account_attribute.OtpForServerConnect; AccessToken = other_account_attribute.AccessToken; SsoAccountAuthJWT = other_account_attribute.SsoAccountAuthJWT; + BlockStartDateTime = other_account_attribute.BlockStartDateTime; + BlockEndDateTime = other_account_attribute.BlockEndDateTime; + BlockPolicy = other_account_attribute.BlockPolicy; + BlockReason = other_account_attribute.BlockReason; //===================================================================================== // Attribute Try Pending Doc => Origin Doc @@ -272,6 +295,10 @@ public class AccountAttribute : EntityAttributeBase, ICopyEntityAttributeFromMet account_base_attrib.LogoutDateTime = LogoutDateTime; account_base_attrib.AccessToken = AccessToken; account_base_attrib.SsoAccountAuthJWT = SsoAccountAuthJWT; + account_base_attrib.BlockStartDateTime = BlockStartDateTime; + account_base_attrib.BlockEndDateTime = BlockEndDateTime; + account_base_attrib.BlockPolicy = BlockPolicy; + account_base_attrib.BlockReason = BlockReason; return result; } @@ -409,6 +436,10 @@ public class AccountAttribute : EntityAttributeBase, ICopyEntityAttributeFromMet LoginDateTime = account_base_attrib.LoginDateTime; AccessToken = account_base_attrib.AccessToken; SsoAccountAuthJWT = account_base_attrib.SsoAccountAuthJWT; + BlockStartDateTime = account_base_attrib.BlockStartDateTime; + BlockEndDateTime = account_base_attrib.BlockEndDateTime; + BlockPolicy = account_base_attrib.BlockPolicy; + BlockReason = account_base_attrib.BlockReason; return true; } @@ -464,6 +495,10 @@ public class AccountAttributeTransactor : EntityAttributeTransactorBase ImageUrl { get; set; } = new(); + + [JsonProperty] + public Dictionary LinkAddress { get; set; } = new(); + + public BannerAttribute(EntityBase owner) + : base(owner) + { + } + + public override IEntityAttributeTransactor onNewEntityAttributeTransactor() + { + return new BannerAttributeTransactor(getOwner()); + } + + public override void onClear() + { + BannerId = 0; + BannerType = BannerType.None; + StartTime = new(); + EndTime = new(); + OrderId = 9; + ImageUrl = new(); + LinkAddress = new(); + + getAttributeState().reset(); + } + + public override EntityAttributeBase onCloned() + { + var cloned = new BannerAttribute(getOwner()); + + cloned.deepCopyFromBase(this); + + cloned.BannerId = BannerId; + cloned.BannerType = BannerType; + cloned.StartTime = StartTime; + cloned.EndTime = EndTime; + cloned.OrderId = OrderId; + cloned.ImageUrl = ImageUrl; + cloned.LinkAddress = LinkAddress; + + return cloned; + } + + public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) + { + var result = new Result(); + + var owner = getOwner(); + NullReferenceCheckHelper.throwIfNull(owner, () => "owner is null !!!"); + + //===================================================================================== + // Attribute => try pending Doc + //===================================================================================== + var try_pending_doc = getTryPendingDocBase() as BannerDoc; + if (null == try_pending_doc) + { + var to_copy_doc = new BannerDoc(BannerId); + + var origin_doc = getOriginDocBase(); + if (null != origin_doc) + { + to_copy_doc.copyTimestampsFromOriginDocBase(origin_doc); + } + + try_pending_doc = to_copy_doc; + + setTryPendingDocBase(try_pending_doc); + } + + var to_copy_doc_attrib = try_pending_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(to_copy_doc_attrib, () => $"to_copy_doc_attrib is null !!! - {owner.toBasicString()}"); + + to_copy_doc_attrib.BannerId = BannerId; + to_copy_doc_attrib.BannerType = BannerType; + to_copy_doc_attrib.StartTime = StartTime; + to_copy_doc_attrib.EndTime = EndTime; + to_copy_doc_attrib.OrderId = OrderId; + to_copy_doc_attrib.ImageUrl = ImageUrl; + to_copy_doc_attrib.LinkAddress = LinkAddress; + + if (false == isForQuery) + { + return (result, try_pending_doc); + } + + //===================================================================================== + // Doc QueryType 반영 + //===================================================================================== + (result, var to_query_doc) = await applyDoc4Query(try_pending_doc); + if (result.isFail()) + { + return (result, null); + } + + return (result, to_query_doc); + } + + public bool copyEntityAttributeFromDoc(DynamoDbDocBase? docBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(BannerDoc).Name; + + var banner_doc_base = docBase as BannerDoc; + if (null == banner_doc_base) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, banner_doc_base is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + //===================================================================================== + // New Doc => Origin Doc + //===================================================================================== + syncOriginDocBaseWithNewDoc(banner_doc_base); + + //===================================================================================== + // Doc => Attribute + //===================================================================================== + var doc_attrib = banner_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(doc_attrib, () => "doc_attrib is null !!!"); + + BannerId = doc_attrib.BannerId; + BannerType = doc_attrib.BannerType; + StartTime = doc_attrib.StartTime; + EndTime = doc_attrib.EndTime; + OrderId = doc_attrib.OrderId; + ImageUrl = doc_attrib.ImageUrl; + LinkAddress = doc_attrib.LinkAddress; + + return true; + } + + public Result onMerge(EntityAttributeBase otherEntityAttribute) + { + var owner = getOwner(); + NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); + + var result = new Result(); + var err_msg = string.Empty; + + if (null == otherEntityAttribute) + { + err_msg = $"Invalid Param !!!, otherEntityAttribute is null"; + result.setFail(ServerErrorCode.FunctionParamNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + //===================================================================================== + // OtherAttribute => Attribute + //===================================================================================== + var banner_attribute = otherEntityAttribute as BannerAttribute; + if (null == banner_attribute) + { + err_msg = $"Failed to cast BannerAttribute !!!, banner_attribute is null"; + result.setFail(ServerErrorCode.ClassTypeCastIsNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + BannerId = banner_attribute.BannerId; + BannerType = banner_attribute.BannerType; + StartTime = banner_attribute.StartTime; + EndTime = banner_attribute.EndTime; + OrderId = banner_attribute.OrderId; + ImageUrl = banner_attribute.ImageUrl; + LinkAddress = banner_attribute.LinkAddress; + + //===================================================================================== + // Attribute Try Pending Doc => Origin Doc + //===================================================================================== + var try_pending_doc = banner_attribute.getTryPendingDocBase() as BannerDoc; + if (null != try_pending_doc) + { + banner_attribute.resetTryPendingDocBase(); + + syncOriginDocBaseWithNewDoc(try_pending_doc); + } + + var origin_doc_base = getOriginDocBase(); + if (null == origin_doc_base) + { + // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! + return result; + } + + var banner_attrib = origin_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(banner_attrib, () => $"banner_attrib is null !!! - {owner.toBasicString()}"); + + banner_attrib.BannerId = BannerId; + banner_attrib.BannerType = BannerType; + banner_attrib.StartTime = StartTime; + banner_attrib.EndTime = EndTime; + banner_attrib.OrderId = OrderId; + banner_attrib.ImageUrl = ImageUrl; + banner_attrib.LinkAddress = LinkAddress; + + return result; + } + } + + public class BannerAttributeTransactor : EntityAttributeTransactorBase, ICopyEntityAttributeTransactorFromEntityAttribute + { + public BannerAttributeTransactor(EntityBase owner) + : base(owner) + { + } + + public bool copyEntityAttributeTransactorFromEntityAttribute(EntityAttributeBase? entityAttributeBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(BannerAttribute).Name; + + var copy_from_banner_attribute = entityAttributeBase as BannerAttribute; + if (null == copy_from_banner_attribute) + { + err_msg = $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_from_banner_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + var copy_to_banner_attribute = getClonedEntityAttribute() as BannerAttribute; + if (null == copy_to_banner_attribute) + { + err_msg = $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_to_banner_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + copy_to_banner_attribute.BannerId = copy_from_banner_attribute.BannerId; + copy_to_banner_attribute.BannerType = copy_from_banner_attribute.BannerType; + copy_to_banner_attribute.StartTime = copy_from_banner_attribute.StartTime; + copy_to_banner_attribute.EndTime = copy_from_banner_attribute.EndTime; + copy_to_banner_attribute.OrderId = copy_from_banner_attribute.OrderId; + copy_to_banner_attribute.ImageUrl = copy_from_banner_attribute.ImageUrl; + copy_to_banner_attribute.LinkAddress = copy_from_banner_attribute.LinkAddress; + + return true; + } + } +} diff --git a/ServerCommon/Entity/Attribute/FarmingEffectAttribute.cs b/ServerCommon/Entity/Attribute/FarmingEffectAttribute.cs index abeb592..a80d702 100644 --- a/ServerCommon/Entity/Attribute/FarmingEffectAttribute.cs +++ b/ServerCommon/Entity/Attribute/FarmingEffectAttribute.cs @@ -116,7 +116,7 @@ namespace ServerCommon return (result, null); } - (result, var found_nickname_attrib) = await dynamo_db_client.simpleQueryDocTypeToAttrib(table_name, OwnerGuid); + (result, var found_nickname_attrib) = await dynamo_db_client.simpleQueryDocTypeToAttrib(OwnerGuid); if (result.isFail()) { return (result, null); @@ -348,7 +348,7 @@ namespace ServerCommon var farming_effect_doc = docBase as FarmingEffectDoc; if (null == farming_effect_doc) { - err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, farming_effect_doc is null :{to_cast_string} - {owner.toBasicString}"; + err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, farming_effect_doc is null :{to_cast_string} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return false; } diff --git a/ServerCommon/Entity/Attribute/FarmingEffectLocationInTargetAttribute.cs b/ServerCommon/Entity/Attribute/FarmingEffectLocationInTargetAttribute.cs index bfac832..52bd943 100644 --- a/ServerCommon/Entity/Attribute/FarmingEffectLocationInTargetAttribute.cs +++ b/ServerCommon/Entity/Attribute/FarmingEffectLocationInTargetAttribute.cs @@ -228,7 +228,7 @@ namespace ServerCommon var farming_location_in_target_doc = docBase as FarmingEffectLocationInTargetDoc; if (null == farming_location_in_target_doc) { - err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, farming_location_in_target_doc is null :{to_cast_string} - {owner.toBasicString}"; + err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, farming_location_in_target_doc is null :{to_cast_string} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return false; } diff --git a/ServerCommon/Entity/Attribute/FarmingEffectOwnerAttribute.cs b/ServerCommon/Entity/Attribute/FarmingEffectOwnerAttribute.cs index a575b1b..efcb682 100644 --- a/ServerCommon/Entity/Attribute/FarmingEffectOwnerAttribute.cs +++ b/ServerCommon/Entity/Attribute/FarmingEffectOwnerAttribute.cs @@ -211,7 +211,7 @@ namespace ServerCommon var farming_location_owner_doc = docBase as FarmingEffectOwnerDoc; if (null == farming_location_owner_doc) { - err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, farming_location_owner_doc is null :{to_cast_string} - {owner.toBasicString}"; + err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, farming_location_owner_doc is null :{to_cast_string} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return false; } diff --git a/ServerCommon/Entity/Attribute/GameEventAttribute.cs b/ServerCommon/Entity/Attribute/GameEventAttribute.cs new file mode 100644 index 0000000..09ad192 --- /dev/null +++ b/ServerCommon/Entity/Attribute/GameEventAttribute.cs @@ -0,0 +1,110 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; + +namespace ServerCommon; + +public class BattleEventAttrib : AttribBase +{ + public BattleEventAttrib() : base(nameof(BattleEventAttrib)) { } + + [JsonProperty("event_id")] public Int32 m_event_id { get; set; } = 0; + [JsonProperty("is_active")] public bool m_is_active { get; set; } = false; + + [JsonProperty("start_day")] public DateTime m_start_day = DateTimeHelper.Current.Date;//2025-01-01T00:00:00 + [JsonProperty("start_hour")] public Int32 m_start_hour = 0; //00 ~ 23; + [JsonProperty("start_min")] public Int32 m_start_min = 0; //00 ~ 59; + + [JsonProperty("open_duration_minutes")] public Int32 m_open_duration_minutes = 240; + + [JsonProperty("end_date")] public DateTime m_end_date = DateTimeHelper.Current.Date.AddHours(12); //이시간이 지나면 더이상 이벤트 열지 않는다. + + [JsonProperty("game_mode_id")] public Int32 m_game_mode_id { get; set; } = 1; + [JsonProperty("instance_id")] public Int32 m_instance_id { get; set; } = 0;//미사용 예정 + [JsonProperty("once_period_type")] public OncePeriodRangeType m_period { get; set; } = OncePeriodRangeType.NONE; + [JsonProperty("day_of_week_type")] public HashSet m_day_of_week_type { get; set; } = new(); + [JsonProperty("ffa_config_data_id")] public Int32 m_ffa_config_data_id { get; set; } = 4;//미사용 예정 + [JsonProperty("ffa_reward_group_id")] public Int32 m_ffa_reward_group_id { get; set; } = 7;//미사용 예정 + [JsonProperty("ffa_hot_time")] public Int32 m_ffa_hot_time { get; set; } = 1; + [JsonProperty("round_count")] public Int32 m_round_count { get; set; } = 1;//미사용 예정 + + + // public BattleEventAttrib clone() + // { + // BattleEventAttrib new_attrib = new(); + // new_attrib.m_event_id = this.m_event_id; + // new_attrib.m_game_mode_id = this.m_game_mode_id; + // new_attrib.m_is_active = this.m_is_active; + // new_attrib.m_start_day = this.m_start_day; + // new_attrib.m_start_hour = this.m_start_hour; + // new_attrib.m_start_min = this.m_start_min; + // new_attrib.m_instance_id = this.m_instance_id; + // new_attrib.m_period = this.m_period; + // new_attrib.m_day_of_week_type = this.m_day_of_week_type; + // new_attrib.m_ffa_config_data_id = this.m_ffa_config_data_id; + // new_attrib.m_ffa_reward_group_id = this.m_ffa_reward_group_id; + // new_attrib.m_ffa_hot_time = this.m_ffa_hot_time; + // + // new_attrib.m_end_date = this.m_end_date; + // new_attrib.m_round_count = this.m_round_count; + // + // return new_attrib; + // } + + public override string ToString() + { + return $"m_event_id:{m_event_id}, game_mode_id {m_game_mode_id}, m_start_date:{m_start_day}, m_end_date:{m_end_date} m_ m_ffa_hot_time:{m_ffa_hot_time}"; + } +} + + + +//============================================================================================= +// PK(Partition Key) : "management_game_event#global" +// SK(Sort Key) : "event_id" +// DocType : GameEventDoc +// SystemMailAttrib : {} +// ... +//============================================================================================= +public class GameEventDoc : DynamoDbDocBase +{ + public GameEventDoc() + : base("BattleEventDoc") + { + setCombinationKeyForPK(DynamoDbClient.PK_GLOBAL); + appendAttribWrapperAll(); + fillUpPrimaryKey(onMakePK(), onMakeSK()); + } + + // public GameEventDoc(string eventId) + // : base(typeof(GameEventDoc).Name) + // { + // setCombinationKeyForPKSK(DynamoDbClient.PK_GLOBAL, eventId); + // appendAttribWrapperAll(); + // fillUpPrimaryKey(onMakePK(), onMakeSK()); + // } + + private void appendAttribWrapperAll() + { + appendAttribWrapper(new AttribWrapper()); + } + + protected override string onGetPrefixOfPK() + { + return "management_battle_event#"; + } + + protected override ServerErrorCode onCheckAndSetSK(string sortKey) + { + getPrimaryKey().fillUpSK(sortKey); + setCombinationKeyForSK(sortKey); + return ServerErrorCode.Success; + } + + public override string ToString() + { + var attrib = getAttrib(); + return attrib?.ToString() ?? string.Empty; + } +} diff --git a/ServerCommon/Entity/Attribute/GameModeBestRecordAttribute.cs b/ServerCommon/Entity/Attribute/GameModeBestRecordAttribute.cs new file mode 100644 index 0000000..1e70073 --- /dev/null +++ b/ServerCommon/Entity/Attribute/GameModeBestRecordAttribute.cs @@ -0,0 +1,221 @@ +using System.Collections.Concurrent; +using Newtonsoft.Json; +using ServerBase; +using ServerCommon; +using ServerCore; + +namespace GameServer; + +// Key: "gameModeId#instanceMetaId" +// Value: GameModeBestRecordAttrib +using BestRecordKey = System.String; + +public class GameModeBestRecordAttribute : EntityAttributeBase, ICopyEntityAttributeFromDoc, IMergeWithEntityAttribute +{ + [JsonProperty] public ConcurrentDictionary Records { get; set; } = new(); + public GameModeBestRecordAttribute(UserBase owner) : base(owner, true) { } + + public override void onClear() + { + getAttributeState().reset(); + } + + public override EntityAttributeBase onCloned() + { + var owner = getOwner() as UserBase; + NullReferenceCheckHelper.throwIfNull(owner, () => "owner is null !!!"); + + var cloned = new GameModeBestRecordAttribute(owner); + cloned.onClear(); + return cloned; + } + + public override IEntityAttributeTransactor onNewEntityAttributeTransactor() + { + return new Transactor(getOwner()); + } + + public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) + { + var result = new Result(); + var userGuid = getOwner().getEntityGuid(); + + //===================================================================================== + // Attribute => try pending Doc + //===================================================================================== + if (getTryPendingDocBase() is not GameModeBestRecordDoc tryPendingDoc) + { + var toCopyDoc = new GameModeBestRecordDoc(this.getOwner().getEntityGuid()); + + var originDoc = getOriginDocBase(); + if (null != originDoc) + { + toCopyDoc.copyTimestampsFromOriginDocBase(originDoc); + } + + tryPendingDoc = toCopyDoc; + + setTryPendingDocBase(tryPendingDoc); + } + var toCopyDocAttrib = tryPendingDoc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(toCopyDocAttrib, () => $"GameModeBestRecordDoc to_copy_doc_attrib is null !!!"); + + toCopyDocAttrib.UserGuid = userGuid; + toCopyDocAttrib.Records.Clear(); + foreach ((BestRecordKey key, GameModeBestRecord value) in Records.ToDictionary()) + { + toCopyDocAttrib.Records.Add(key, value); + } + + if (false == isForQuery) + { + return (result, tryPendingDoc); + } + + //===================================================================================== + // Doc QueryType 반영 + //===================================================================================== + (result, var toQueryDoc) = await applyDoc4Query(tryPendingDoc); + if (result.isFail()) + { + return (result, null); + } + return (result, tryPendingDoc); + } + + public bool copyEntityAttributeFromDoc(DynamoDbDocBase newDocBase) + { + var errMsg = string.Empty; + if (newDocBase is not GameModeBestRecordDoc recordDoc) + { + errMsg = $"Failed to copyEntityAttributeFromDoc() !!!, ranker_doc_base is null :{nameof(WorldEventScoreDoc)}"; + Log.getLogger().error(errMsg); + return false; + } + + syncOriginDocBaseWithNewDoc(recordDoc); + + //===================================================================================== + // New Doc => Origin Doc + //===================================================================================== + syncOriginDocBaseWithNewDoc(recordDoc); + + //===================================================================================== + // Doc => Attribute + //===================================================================================== + var docAttrib = recordDoc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(docAttrib, () => $"doc_attrib is null !!!"); + Records.Clear(); + foreach ((BestRecordKey key, GameModeBestRecord value) in docAttrib.Records) + { + Records.TryAdd(key, value); + } + return true; + } + + public Result onMerge(EntityAttributeBase? entityAttribute) + { + var result = new Result(); + var errMsg = string.Empty; + + + if (null == entityAttribute) + { + errMsg = $"Invalid Param !!!, otherEntityAttribute is null"; + result.setFail(ServerErrorCode.FunctionParamNull, errMsg); + Log.getLogger().error(result.toBasicString()); + } + + if (entityAttribute is not GameModeBestRecordAttribute otherEntityAttribute) + { + errMsg = $"Failed to cast GameModeBestRecordAttribute !!!, otherEntityAttribute is null"; + result.setFail(ServerErrorCode.ClassTypeCastIsNull, errMsg); + Log.getLogger().error(result.toBasicString()); + return result; + } + + Records = otherEntityAttribute.Records; + + //===================================================================================== + // Attribute Try Pending Doc => Origin Doc + //===================================================================================== + if (otherEntityAttribute.getTryPendingDocBase() is GameModeBestRecordDoc tryPendingDoc) + { + otherEntityAttribute.resetTryPendingDocBase(); + + syncOriginDocBaseWithNewDoc(tryPendingDoc); + } + + var originDocBase = getOriginDocBase(); + if (null == originDocBase) + { + // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! + return result; + } + + var attrib = originDocBase.getAttrib(); + NullReferenceCheckHelper.throwIfNull(attrib, () => $"GameModeBestRecordAttrib is null !!!"); + + attrib.Records.Clear(); + foreach ((BestRecordKey key, GameModeBestRecord value) in Records) + { + attrib.Records.Add(key, value); + } + return result; + } + + public bool tryUpdateBestRecord(int gameModeId, int instanceMetaId, long newScore) + { + var key = $"{gameModeId}#{instanceMetaId}"; + + if (Records.TryGetValue(key, out var existingRecord) && newScore >= existingRecord.Score) + { + return false; // Not a new best record + } + + var newRecord = + new GameModeBestRecord(gameModeId: gameModeId, instanceMetaId: instanceMetaId, score: newScore); + Records.AddOrUpdate(key, newRecord, (k, v) => newRecord); + modifiedEntityAttribute(); + return true; + } + + public override string toBasicString() => $"UserGuid:{this.getOwner().getEntityGuid()}, Records:{Records}"; + + + public class Transactor : EntityAttributeTransactorBase, + ICopyEntityAttributeTransactorFromEntityAttribute + { + public Transactor(EntityBase owner) + : base(owner) + { + } + + public bool copyEntityAttributeTransactorFromEntityAttribute(EntityAttributeBase? entityAttributeBase) + { + var err_msg = string.Empty; + + var to_cast_string = nameof(GameModeBestRecordAttribute); + + if (entityAttributeBase is not GameModeBestRecordAttribute src) + { + err_msg = + $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_from_building_floor_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + if (getClonedEntityAttribute() is not GameModeBestRecordAttribute dest) + { + err_msg = + $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_to_building_floor_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + dest.Records.Clear(); + dest.Records.TryAdd(src.Records.Keys.First(), src.Records.Values.First()); + return true; + } + } +} diff --git a/ServerCommon/Entity/Attribute/GameModePlayRegulationAttribute.cs b/ServerCommon/Entity/Attribute/GameModePlayRegulationAttribute.cs new file mode 100644 index 0000000..9ea2060 --- /dev/null +++ b/ServerCommon/Entity/Attribute/GameModePlayRegulationAttribute.cs @@ -0,0 +1,323 @@ +using MetaAssets; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; +using USER_GUID = System.String; +namespace ServerCommon; + +public class GameModePlayRegulationAttribute : EntityAttributeBase, ICopyEntityAttributeFromDoc, IMergeWithEntityAttribute +{ + [JsonProperty] + public Dictionary m_match_restriction { get; set; } = new(); + + [JsonProperty] + public Dictionary m_play_penalty { get; set; } = new(); + + + public GameModePlayRegulationAttribute(EntityBase owner) : base(owner, false) + { + } + + public override void onClear() + { + m_match_restriction = new(); + m_play_penalty.Clear(); + getAttributeState().reset(); + } + + public override IEntityAttributeTransactor onNewEntityAttributeTransactor() + { + var owner = getOwner() as UserBase; + NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); + + return new GameModePlayRegulationAttributeTransactor(owner); + } + + public override EntityAttributeBase onCloned() + { + var owner = getOwner() as UserBase; + NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); + + var cloned = new GameModePlayRegulationAttribute(owner); + + cloned.m_match_restriction = m_match_restriction.ToDictionary( + x => x.Key, + x => x.Value.clone() + );; + cloned.m_play_penalty = m_play_penalty.ToDictionary( + x => x.Key, + x => x.Value.clone() + ); + + return cloned; + } + + public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) + { + var result = new Result(); + var err_msg = string.Empty; + + var owner = getOwner(); + NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); + + var acoount_attribute = owner.getEntityAttribute(); + NullReferenceCheckHelper.throwIfNull(acoount_attribute, () => $"acoount_attribute is null !!!"); + + USER_GUID user_guid = acoount_attribute.UserGuid; + + //===================================================================================== + // Attribute => try pending Doc + //===================================================================================== + var try_pending_doc = getTryPendingDocBase() as GameModePlayRegulationDoc; + if (null == try_pending_doc) + { + var to_copy_doc = new GameModePlayRegulationDoc(user_guid); + + var origin_doc = getOriginDocBase(); + if (null != origin_doc) + { + to_copy_doc.copyTimestampsFromOriginDocBase(origin_doc); + } + + try_pending_doc = to_copy_doc; + + setTryPendingDocBase(try_pending_doc); + } + + var to_copy_doc_attrib = try_pending_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(to_copy_doc_attrib, () => $"to_copy_doc_attrib is null !!!"); + + to_copy_doc_attrib.m_match_restriction = m_match_restriction.ToDictionary( + x => x.Key, + x => x.Value.clone() + );; + to_copy_doc_attrib.m_play_penalty = m_play_penalty + .ToDictionary( + x => x.Key, + x => x.Value.clone() + ); + + if (false == isForQuery) + { + return (result, try_pending_doc); + } + + //===================================================================================== + // Doc QueryType 반영 + //===================================================================================== + (result, var to_query_doc) = await applyDoc4Query(try_pending_doc); + if (result.isFail()) + { + return (result, null); + } + + return (result, to_query_doc); + } + + public bool copyEntityAttributeFromDoc(DynamoDbDocBase customDoc) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(GameModePlayRegulationAttrib).Name; + + var regulation_doc_base = customDoc as GameModePlayRegulationDoc; + if (null == regulation_doc_base) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, regulation_doc_base is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + //===================================================================================== + // New Doc => Origin Doc + //===================================================================================== + syncOriginDocBaseWithNewDoc(regulation_doc_base); + + //===================================================================================== + // Doc => Attribute + //===================================================================================== + var doc_attrib = regulation_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(doc_attrib, () => $"doc_attrib is null !!!"); + + m_match_restriction = doc_attrib.m_match_restriction.ToDictionary( + x => x.Key, + x => x.Value.clone() + );; + m_play_penalty = doc_attrib.m_play_penalty + .ToDictionary( + x => x.Key, + x => x.Value.clone() + ); + + return true; + } + + public Result onMerge(EntityAttributeBase otherEntityAttribute) + { + var owner = getOwner(); + NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); + + var result = new Result(); + var err_msg = string.Empty; + + if (null == otherEntityAttribute) + { + err_msg = $"Invalid Param !!!, otherEntityAttribute is null"; + result.setFail(ServerErrorCode.FunctionParamNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + //===================================================================================== + // OtherAttribute => Attribute + //===================================================================================== + var game_mode_play_regulation_attribute = otherEntityAttribute as GameModePlayRegulationAttribute; + if (null == game_mode_play_regulation_attribute) + { + err_msg = $"Failed to cast GameModePlayRegulationAttribute !!!, game_mode_play_regulation_attribute is null"; + result.setFail(ServerErrorCode.ClassTypeCastIsNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + m_match_restriction = game_mode_play_regulation_attribute.m_match_restriction.ToDictionary( + x => x.Key, + x => x.Value.clone() + );; + m_play_penalty = game_mode_play_regulation_attribute.m_play_penalty + .ToDictionary( + x => x.Key, + x => x.Value.clone() + ); + + //===================================================================================== + // Attribute Try Pending Doc => Origin Doc + //===================================================================================== + var try_pending_doc = game_mode_play_regulation_attribute.getTryPendingDocBase() as GameModePlayRegulationDoc; + if (null != try_pending_doc) + { + game_mode_play_regulation_attribute.resetTryPendingDocBase(); + + syncOriginDocBaseWithNewDoc(try_pending_doc); + } + + var origin_doc_base = getOriginDocBase(); + if (null == origin_doc_base) + { + // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! + return result; + } + + var game_mode_play_regulation_attrib = origin_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(game_mode_play_regulation_attrib, () => $"game_mode_play_regulation_attrib is null !!! - {owner.toBasicString()}"); + + game_mode_play_regulation_attrib.m_match_restriction = m_match_restriction.ToDictionary( + x => x.Key, + x => x.Value.clone() + );; + game_mode_play_regulation_attrib.m_play_penalty = m_play_penalty + .ToDictionary( + x => x.Key, + x => x.Value.clone() + ); + + return result; + } + + public void addDefaultGameModePlayRegulation() + { + var mode_cnt = GameModeType.GetValues(typeof(GameModeType)).Length - 1; + if (m_match_restriction.Count != mode_cnt) + { + foreach (GameModeType gameMode in GameModeType.GetValues(typeof(GameModeType))) + { + if (gameMode == GameModeType.None) + continue; + + // MatchRestriction 누락 시 추가 + if (!m_match_restriction.ContainsKey(gameMode)) + { + m_match_restriction[gameMode] = new ServerCommon.MatchRestriction + { + m_current_matched_count = 0, + m_next_refresh_time = DateTimeHelper.Current.Date.AddDays(1) + }; + } + } + } + + if (m_play_penalty.Count != mode_cnt) + { + foreach (GameModeType gameMode in GameModeType.GetValues(typeof(GameModeType))) + { + if (gameMode == GameModeType.None) + continue; + + // PlayPenalty 누락 시 추가 + if (!m_play_penalty.ContainsKey(gameMode)) + { + m_play_penalty[gameMode] = new ServerCommon.GameModePlayPenalty + { + m_penalty_count = 0, + m_penalty_end_time = DateTimeHelper.Current, + m_penalty_start_time = DateTimeHelper.Current, + m_is_max = false + }; + } + } + } + } +} + + +public class GameModePlayRegulationAttributeTransactor : EntityAttributeTransactorBase, ICopyEntityAttributeTransactorFromEntityAttribute +{ + public GameModePlayRegulationAttributeTransactor(UserBase owner) : base(owner) + { + } + + public bool copyEntityAttributeTransactorFromEntityAttribute(EntityAttributeBase? entityAttributeBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(GameModePlayRegulationAttribute).Name; + + var copy_from = entityAttributeBase as GameModePlayRegulationAttribute; + if (null == copy_from) + { + err_msg = $"Failed to copyEntityAttributeTransactorFromEntityAttribute() !!!, copy_from_game_mode_play_regulation_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + var copy_to = getClonedEntityAttribute() as GameModePlayRegulationAttribute; + if (null == copy_to) + { + err_msg = $"Failed to copyEntityAttributeTransactorFromEntityAttribute() !!!, copy_to_game_mode_play_regulation_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + copy_to.m_match_restriction = copy_from.m_match_restriction.ToDictionary( + x => x.Key, + x => x.Value.clone() + );; + copy_to.m_play_penalty = copy_from.m_play_penalty + .ToDictionary( + x => x.Key, + x => x.Value.clone() + ); + + + + + return true; + } +} + +public static class GameModePlayRegulationAttributeHelper +{ + +} \ No newline at end of file diff --git a/ServerCommon/Entity/Attribute/LocationAttribute.cs b/ServerCommon/Entity/Attribute/LocationAttribute.cs index d616c66..f2f0c16 100644 --- a/ServerCommon/Entity/Attribute/LocationAttribute.cs +++ b/ServerCommon/Entity/Attribute/LocationAttribute.cs @@ -20,19 +20,19 @@ namespace ServerCommon { public class LocationAttribute : EntityAttributeBase, ICopyEntityAttributeFromDoc, ICopyEntityAttributeFromCache, IMergeWithEntityAttribute { - [JsonProperty] public ChannelServerLocation CurrentChannelServerLoaction { get; set; } = new(); [JsonProperty] public ChannelServerLocation LastestChannelServerLocation { get; set; } = new(); - + [JsonProperty] public IndunLocation ReJoinIndunLocation { get; set; } = new(); public List ReturnIndunLocations { get; set; } = new(); public IndunLocation EnterIndunLocation { get; set; } = new(); public IndunLocation CurrentIndunLocation { get; set; } = new(); - public Timestamp MoveChannelTime { get; set; } = DateTimeHelper.MinTime.ToTimestamp(); + public Timestamp MoveChannelTime { get; set; } = DateTimeHelper.MinTime.ToTimestamp(); + public bool MoveToAccountStartPosAtLogin { get; set; } = false; public LocationAttribute(EntityBase owner) : base(owner) @@ -46,13 +46,16 @@ namespace ServerCommon public override void onClear() { + CurrentChannelServerLoaction.clear(); LastestChannelServerLocation.clear(); + ReJoinIndunLocation.clear(); ReturnIndunLocations.Clear(); EnterIndunLocation.clear(); - CurrentIndunLocation.clear(); + MoveChannelTime = DateTimeHelper.MinTime.ToTimestamp(); + MoveToAccountStartPosAtLogin = false; getAttributeState().reset(); } @@ -63,14 +66,16 @@ namespace ServerCommon cloned.deepCopyFromBase(this); + cloned.CurrentChannelServerLoaction = CurrentChannelServerLoaction.clone(); cloned.LastestChannelServerLocation = LastestChannelServerLocation.clone(); + cloned.ReJoinIndunLocation = ReJoinIndunLocation.clone(); cloned.ReturnIndunLocations = ReturnIndunLocations.Select(x => x.clone()).ToList(); cloned.EnterIndunLocation = EnterIndunLocation.clone(); - cloned.CurrentIndunLocation = CurrentIndunLocation.clone(); cloned.MoveChannelTime = MoveChannelTime; + cloned.MoveToAccountStartPosAtLogin = MoveToAccountStartPosAtLogin; return cloned; } @@ -174,9 +179,12 @@ namespace ServerCommon // Cache => Attribute //===================================================================================== LastestChannelServerLocation = location_cache.LastestChannelServerLocation.clone(); + ReturnIndunLocations = location_cache.ReturnIndunLocations.Select(x => x.clone()).ToList(); EnterIndunLocation = location_cache.EnterIndunLocation.clone(); + MoveChannelTime = location_cache.MoveChannelTime; + MoveToAccountStartPosAtLogin = location_cache.MoveToAccountStartPosAtLogin; return true; } @@ -211,12 +219,16 @@ namespace ServerCommon return result; } + CurrentChannelServerLoaction = location_attribute.CurrentChannelServerLoaction.clone(); LastestChannelServerLocation = location_attribute.LastestChannelServerLocation.clone(); + + ReJoinIndunLocation = location_attribute.ReJoinIndunLocation.clone(); ReturnIndunLocations = location_attribute.ReturnIndunLocations.Select(x => x.clone()).ToList(); EnterIndunLocation = location_attribute.EnterIndunLocation.clone(); - CurrentIndunLocation = location_attribute.CurrentIndunLocation.clone(); + MoveChannelTime = location_attribute.MoveChannelTime; + MoveToAccountStartPosAtLogin = location_attribute.MoveToAccountStartPosAtLogin; //===================================================================================== // Attribute Try Pending Doc => Origin Doc @@ -275,13 +287,16 @@ namespace ServerCommon return false; } + copy_to_location_attribute.CurrentChannelServerLoaction = copy_from_location_attribute.CurrentChannelServerLoaction.clone(); copy_to_location_attribute.LastestChannelServerLocation = copy_from_location_attribute.LastestChannelServerLocation.clone(); + copy_to_location_attribute.ReJoinIndunLocation = copy_from_location_attribute.ReJoinIndunLocation.clone(); copy_to_location_attribute.ReturnIndunLocations = copy_from_location_attribute.ReturnIndunLocations.Select(x => x.clone()).ToList(); copy_to_location_attribute.EnterIndunLocation = copy_from_location_attribute.EnterIndunLocation.clone(); - copy_to_location_attribute.CurrentIndunLocation = copy_from_location_attribute.CurrentIndunLocation.clone(); + copy_to_location_attribute.MoveChannelTime = copy_from_location_attribute.MoveChannelTime; + copy_to_location_attribute.MoveToAccountStartPosAtLogin = copy_from_location_attribute.MoveToAccountStartPosAtLogin; return true; } diff --git a/ServerCommon/Entity/Attribute/RankAttribute.cs b/ServerCommon/Entity/Attribute/RankAttribute.cs new file mode 100644 index 0000000..c500708 --- /dev/null +++ b/ServerCommon/Entity/Attribute/RankAttribute.cs @@ -0,0 +1,275 @@ +using Newtonsoft.Json; +using ServerBase; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankAttribute : EntityAttributeBase, ICopyEntityAttributeFromDoc, IMergeWithEntityAttribute + { + [JsonProperty] + public string RankingGuid { get; set; } = string.Empty; + + [JsonProperty] + public string RankerGuid { get; set; } = string.Empty; + + [JsonProperty] + public EntityType RankerEntityType { get; set; } = EntityType.None; + + [JsonProperty] + public int PrevRankNum { get; set; } = 0; + + [JsonProperty] + public int RankNum { get; set; } = 0; + + [JsonProperty] + public int Score { get; set; } = 0; + + [JsonProperty] + public ScoreType ScoreType { get; set; } = ScoreType.None; + + public RankAttribute(EntityBase owner) + : base(owner) + { + } + + public override IEntityAttributeTransactor onNewEntityAttributeTransactor() + { + return new RankAttributeTransactor(getOwner()); + } + + public override void onClear() + { + RankingGuid = string.Empty; + RankerGuid = string.Empty; + RankerEntityType = EntityType.None; + PrevRankNum = 0; + RankNum = 0; + Score = 0; + ScoreType = ScoreType.None; + + getAttributeState().reset(); + } + + public override EntityAttributeBase onCloned() + { + var cloned = new RankAttribute(getOwner()); + + cloned.deepCopyFromBase(this); + + cloned.RankingGuid = RankingGuid; + cloned.RankerGuid = RankerGuid; + cloned.RankerEntityType = RankerEntityType; + cloned.PrevRankNum = PrevRankNum; + cloned.RankNum = RankNum; + cloned.Score = Score; + cloned.ScoreType = ScoreType; + + return cloned; + } + + + public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) + { + var result = new Result(); + + //===================================================================================== + // Attribute => try pending Doc + //===================================================================================== + var try_pending_doc = getTryPendingDocBase() as RankDoc; + if (null == try_pending_doc) + { + var to_copy_doc = new RankDoc(RankingGuid, RankNum); + + var origin_doc = getOriginDocBase(); + if (null != origin_doc) + { + to_copy_doc.copyTimestampsFromOriginDocBase(origin_doc); + } + + try_pending_doc = to_copy_doc; + + setTryPendingDocBase(try_pending_doc); + } + + var to_copy_doc_attrib = try_pending_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(to_copy_doc_attrib, () => $"to_copy_doc_attrib is null !!!"); + + to_copy_doc_attrib.RankingGuid = RankingGuid; + to_copy_doc_attrib.RankerGuid = RankerGuid; + to_copy_doc_attrib.RankerEntityType = RankerEntityType; + to_copy_doc_attrib.PrevRankNum = PrevRankNum; + to_copy_doc_attrib.Rank = RankNum; + to_copy_doc_attrib.Score = Score; + to_copy_doc_attrib.ScoreType = ScoreType; + + if (false == isForQuery) + { + return (result, try_pending_doc); + } + + //===================================================================================== + // Doc QueryType 반영 + //===================================================================================== + (result, var to_query_doc) = await applyDoc4Query(try_pending_doc); + if (result.isFail()) + { + return (result, null); + } + + return (result, to_query_doc); + } + + public bool copyEntityAttributeFromDoc(DynamoDbDocBase docBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(RankDoc).Name; + + var rank_doc_base = docBase as RankDoc; + if (null == rank_doc_base) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, rank_doc_base is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + //===================================================================================== + // New Doc => Origin Doc + //===================================================================================== + syncOriginDocBaseWithNewDoc(rank_doc_base); + + //===================================================================================== + // Doc => Attribute + //===================================================================================== + var doc_attrib = rank_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(doc_attrib, () => $"doc_attrib is null !!!"); + + RankingGuid = doc_attrib.RankingGuid; + RankerGuid = doc_attrib.RankerGuid; + RankerEntityType = doc_attrib.RankerEntityType; + PrevRankNum = doc_attrib.PrevRankNum; + RankNum = doc_attrib.Rank; + Score = doc_attrib.Score; + ScoreType = doc_attrib.ScoreType; + + return true; + } + + public Result onMerge(EntityAttributeBase otherEntityAttribute) + { + var owner = getOwner(); + NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); + + var result = new Result(); + var err_msg = string.Empty; + + if (null == otherEntityAttribute) + { + err_msg = $"Invalid Param !!!, otherEntityAttribute is null"; + result.setFail(ServerErrorCode.FunctionParamNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + //===================================================================================== + // OtherAttribute => Attribute + //===================================================================================== + var rank_attribute = otherEntityAttribute as RankAttribute; + if (null == rank_attribute) + { + err_msg = $"Failed to cast RankAttribute !!!, rank_attribute is null"; + result.setFail(ServerErrorCode.ClassTypeCastIsNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + RankingGuid = rank_attribute.RankingGuid; + RankerGuid = rank_attribute.RankerGuid; + RankerEntityType = rank_attribute.RankerEntityType; + PrevRankNum = rank_attribute.PrevRankNum; + RankNum = rank_attribute.RankNum; + Score = rank_attribute.Score; + ScoreType = rank_attribute.ScoreType; + + //===================================================================================== + // Attribute Try Pending Doc => Origin Doc + //===================================================================================== + var try_pending_doc = rank_attribute.getTryPendingDocBase() as RankDoc; + if (null != try_pending_doc) + { + rank_attribute.resetTryPendingDocBase(); + + syncOriginDocBaseWithNewDoc(try_pending_doc); + } + + var origin_doc_base = getOriginDocBase(); + if (null == origin_doc_base) + { + // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! + return result; + } + + var rank_attrib = origin_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(rank_attrib, () => $"rank_attrib is null !!!"); + + rank_attrib.RankingGuid = RankingGuid; + rank_attrib.RankerGuid = RankerGuid; + rank_attrib.RankerEntityType = RankerEntityType; + rank_attrib.PrevRankNum = PrevRankNum; + rank_attrib.Rank = RankNum; + rank_attrib.Score = Score; + rank_attrib.ScoreType = ScoreType; + + return result; + } + } + + public class RankAttributeTransactor : EntityAttributeTransactorBase, ICopyEntityAttributeTransactorFromEntityAttribute + { + public RankAttributeTransactor(EntityBase owner) + : base(owner) + { + } + + public bool copyEntityAttributeTransactorFromEntityAttribute(EntityAttributeBase? entityAttributeBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(RankAttribute).Name; + + var copy_from_rank_attribute = entityAttributeBase as RankAttribute; + if (null == copy_from_rank_attribute) + { + err_msg = $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_from_rank_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + var copy_to_rank_attribute = getClonedEntityAttribute() as RankAttribute; + if (null == copy_to_rank_attribute) + { + err_msg = $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_to_rank_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + copy_to_rank_attribute.RankingGuid = copy_from_rank_attribute.RankingGuid; + copy_to_rank_attribute.RankerGuid = copy_from_rank_attribute.RankerGuid; + copy_to_rank_attribute.RankerEntityType = copy_from_rank_attribute.RankerEntityType; + copy_to_rank_attribute.PrevRankNum = copy_from_rank_attribute.PrevRankNum; + copy_to_rank_attribute.RankNum = copy_from_rank_attribute.RankNum; + copy_to_rank_attribute.Score = copy_from_rank_attribute.Score; + copy_to_rank_attribute.ScoreType = copy_from_rank_attribute.ScoreType; + + return true; + } + } +} diff --git a/ServerCommon/Entity/Attribute/RankerAttribute.cs b/ServerCommon/Entity/Attribute/RankerAttribute.cs new file mode 100644 index 0000000..8c03f1b --- /dev/null +++ b/ServerCommon/Entity/Attribute/RankerAttribute.cs @@ -0,0 +1,266 @@ +using Google.Protobuf.WellKnownTypes; +using Newtonsoft.Json; +using ServerBase; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankerAttribute : EntityAttributeBase, ICopyEntityAttributeFromDoc, IMergeWithEntityAttribute + { + [JsonProperty] + public string RankingGuid { get; set; } = string.Empty; + + [JsonProperty] + public string RankerGuid { get; set; } = string.Empty; + + [JsonProperty] + public EntityType RankerEntityType { get; set; } = EntityType.None; + + [JsonProperty] + public int Score { get; set; } = 0; + + [JsonProperty] + public ScoreType ScoreType { get; set; } = ScoreType.None; + + [JsonProperty] + public DateTime UpdateTime { get; set; } = new(); + + public RankerAttribute(EntityBase owner) + : base(owner) + { + } + + public override void newEntityAttribute() + { + base.newEntityAttribute(); + } + + public override IEntityAttributeTransactor onNewEntityAttributeTransactor() + { + return new RankerAttributeTransactor(getOwner()); + } + + public override void onClear() + { + RankingGuid = string.Empty; + RankerGuid = string.Empty; + RankerEntityType = EntityType.None; + Score = 0; + ScoreType = ScoreType.None; + UpdateTime = new(); + + getAttributeState().reset(); + } + + public override EntityAttributeBase onCloned() + { + var cloned = new RankerAttribute(getOwner()); + + cloned.deepCopyFromBase(this); + + cloned.RankingGuid = RankingGuid; + cloned.RankerGuid = RankerGuid; + cloned.RankerEntityType = RankerEntityType; + cloned.Score = Score; + cloned.ScoreType = ScoreType; + cloned.UpdateTime = UpdateTime; + + return cloned; + } + + public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) + { + var result = new Result(); + + //===================================================================================== + // Attribute => try pending Doc + //===================================================================================== + var try_pending_doc = getTryPendingDocBase() as RankerDoc; + if (null == try_pending_doc) + { + var to_copy_doc = new RankerDoc(RankingGuid, RankerGuid); + + var origin_doc = getOriginDocBase(); + if (null != origin_doc) + { + to_copy_doc.copyTimestampsFromOriginDocBase(origin_doc); + } + + try_pending_doc = to_copy_doc; + + setTryPendingDocBase(try_pending_doc); + } + + var to_copy_doc_attrib = try_pending_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(to_copy_doc_attrib, () => $"to_copy_doc_attrib is null !!!"); + + to_copy_doc_attrib.RankingGuid = RankingGuid; + to_copy_doc_attrib.RankerGuid = RankerGuid; + to_copy_doc_attrib.RankerEntityType = RankerEntityType; + to_copy_doc_attrib.Score = Score; + to_copy_doc_attrib.ScoreType = ScoreType; + to_copy_doc_attrib.UpdateTime = UpdateTime; + + if (false == isForQuery) + { + return (result, try_pending_doc); + } + + //===================================================================================== + // Doc QueryType 반영 + //===================================================================================== + (result, var to_query_doc) = await applyDoc4Query(try_pending_doc); + if (result.isFail()) + { + return (result, null); + } + + return (result, to_query_doc); + } + + public bool copyEntityAttributeFromDoc(DynamoDbDocBase docBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(RankerDoc).Name; + + var ranker_doc_base = docBase as RankerDoc; + if (null == ranker_doc_base) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, ranker_doc_base is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + //===================================================================================== + // New Doc => Origin Doc + //===================================================================================== + syncOriginDocBaseWithNewDoc(ranker_doc_base); + + //===================================================================================== + // Doc => Attribute + //===================================================================================== + var doc_attrib = ranker_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(doc_attrib, () => $"doc_attrib is null !!!"); + + RankingGuid = doc_attrib.RankingGuid; + RankerGuid = doc_attrib.RankerGuid; + RankerEntityType = doc_attrib.RankerEntityType; + Score = doc_attrib.Score; + ScoreType = doc_attrib.ScoreType; + UpdateTime = doc_attrib.UpdateTime; + + return true; + } + + public Result onMerge(EntityAttributeBase otherEntityAttribute) + { + var result = new Result(); + var err_msg = string.Empty; + + if (null == otherEntityAttribute) + { + err_msg = $"Invalid Param !!!, otherEntityAttribute is null"; + result.setFail(ServerErrorCode.FunctionParamNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + //===================================================================================== + // OtherAttribute => Attribute + //===================================================================================== + var ranker_attribute = otherEntityAttribute as RankerAttribute; + if (null == ranker_attribute) + { + err_msg = $"Failed to cast RankAttribute !!!, ranker_attribute is null"; + result.setFail(ServerErrorCode.ClassTypeCastIsNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + RankingGuid = ranker_attribute.RankingGuid; + RankerGuid = ranker_attribute.RankerGuid; + RankerEntityType = ranker_attribute.RankerEntityType; + Score = ranker_attribute.Score; + ScoreType = ranker_attribute.ScoreType; + UpdateTime = ranker_attribute.UpdateTime; + + //===================================================================================== + // Attribute Try Pending Doc => Origin Doc + //===================================================================================== + var try_pending_doc = ranker_attribute.getTryPendingDocBase() as RankerDoc; + if (null != try_pending_doc) + { + ranker_attribute.resetTryPendingDocBase(); + + syncOriginDocBaseWithNewDoc(try_pending_doc); + } + + var origin_doc_base = getOriginDocBase(); + if (null == origin_doc_base) + { + // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! + return result; + } + + var ranker_attrib = origin_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(ranker_attrib, () => $"ranker_attrib is null !!!"); + + ranker_attrib.RankingGuid = RankingGuid; + ranker_attrib.RankerGuid = RankerGuid; + ranker_attrib.RankerEntityType = RankerEntityType; + ranker_attrib.Score = Score; + ranker_attrib.ScoreType = ScoreType; + ranker_attrib.UpdateTime = UpdateTime; + + return result; + } + } + + public class RankerAttributeTransactor : EntityAttributeTransactorBase, ICopyEntityAttributeTransactorFromEntityAttribute + { + public RankerAttributeTransactor(EntityBase owner) + : base(owner) + { + } + + public bool copyEntityAttributeTransactorFromEntityAttribute(EntityAttributeBase? entityAttributeBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(RankerAttribute).Name; + + var copy_from_ranker_attribute = entityAttributeBase as RankerAttribute; + if (null == copy_from_ranker_attribute) + { + err_msg = $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_from_ranker_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + var copy_to_ranker_attribute = getClonedEntityAttribute() as RankerAttribute; + if (null == copy_to_ranker_attribute) + { + err_msg = $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_to_ranker_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + copy_to_ranker_attribute.RankingGuid = copy_from_ranker_attribute.RankingGuid; + copy_to_ranker_attribute.RankerGuid = copy_from_ranker_attribute.RankerGuid; + copy_to_ranker_attribute.RankerEntityType = copy_from_ranker_attribute.RankerEntityType; + copy_to_ranker_attribute.Score = copy_from_ranker_attribute.Score; + copy_to_ranker_attribute.ScoreType = copy_from_ranker_attribute.ScoreType; + copy_to_ranker_attribute.UpdateTime = copy_from_ranker_attribute.UpdateTime; + + return true; + } + } +} diff --git a/ServerCommon/Entity/Attribute/RankingAttribute.cs b/ServerCommon/Entity/Attribute/RankingAttribute.cs new file mode 100644 index 0000000..47b35a9 --- /dev/null +++ b/ServerCommon/Entity/Attribute/RankingAttribute.cs @@ -0,0 +1,246 @@ +using Newtonsoft.Json; +using ServerBase; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankingAttribute : EntityAttributeBase, ICopyEntityAttributeFromDoc, IMergeWithEntityAttribute + { + [JsonProperty] + public string RankingGuid { get; set; } = string.Empty; + + [JsonProperty] + public int RankingMetaId { get; set; } = 0; + + [JsonProperty] + public Dictionary NextTimes { get; set; } = new(); + + [JsonProperty] + public int LastestSnapshotIndex { get; set; } = 0; + + public RankingAttribute(EntityBase owner) + : base(owner) + { + } + + public override void newEntityAttribute() + { + base.newEntityAttribute(); + } + + public override IEntityAttributeTransactor onNewEntityAttributeTransactor() + { + return new RankingAttributeTransactor(getOwner()); + } + + public override void onClear() + { + RankingGuid = string.Empty; + RankingMetaId = 0; + NextTimes = new(); + LastestSnapshotIndex = 0; + + getAttributeState().reset(); + } + + public override EntityAttributeBase onCloned() + { + var cloned = new RankingAttribute(getOwner()); + + cloned.deepCopyFromBase(this); + + cloned.RankingGuid = RankingGuid; + cloned.RankingMetaId = RankingMetaId; + cloned.NextTimes = new(NextTimes); + cloned.LastestSnapshotIndex = LastestSnapshotIndex; + + return cloned; + } + + public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) + { + var result = new Result(); + + //===================================================================================== + // Attribute => try pending Doc + //===================================================================================== + var try_pending_doc = getTryPendingDocBase() as RankingDoc; + if (null == try_pending_doc) + { + var to_copy_doc = new RankingDoc(RankingGuid); + + var origin_doc = getOriginDocBase(); + if (null != origin_doc) + { + to_copy_doc.copyTimestampsFromOriginDocBase(origin_doc); + } + + try_pending_doc = to_copy_doc; + + setTryPendingDocBase(try_pending_doc); + } + + var to_copy_doc_attrib = try_pending_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(to_copy_doc_attrib, () => $"to_copy_doc_attrib is null !!!"); + + to_copy_doc_attrib.RankingGuid = RankingGuid; + to_copy_doc_attrib.RankingMetaId = RankingMetaId; + to_copy_doc_attrib.NextTimes = new(NextTimes); + to_copy_doc_attrib.LastestSnapshotIndex = LastestSnapshotIndex; + + if (false == isForQuery) + { + return (result, try_pending_doc); + } + + //===================================================================================== + // Doc QueryType 반영 + //===================================================================================== + (result, var to_query_doc) = await applyDoc4Query(try_pending_doc); + if (result.isFail()) + { + return (result, null); + } + + return (result, to_query_doc); + } + + public bool copyEntityAttributeFromDoc(DynamoDbDocBase docBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(RankingDoc).Name; + + var ranking_doc_base = docBase as RankingDoc; + if (null == ranking_doc_base) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, ranking_doc_base is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + //===================================================================================== + // New Doc => Origin Doc + //===================================================================================== + syncOriginDocBaseWithNewDoc(ranking_doc_base); + + //===================================================================================== + // Doc => Attribute + //===================================================================================== + var doc_attrib = ranking_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(doc_attrib, () => $"doc_attrib is null !!!"); + + RankingGuid = doc_attrib.RankingGuid; + RankingMetaId = doc_attrib.RankingMetaId; + NextTimes = new(doc_attrib.NextTimes); + LastestSnapshotIndex = doc_attrib.LastestSnapshotIndex; + + return true; + } + + public Result onMerge(EntityAttributeBase otherEntityAttribute) + { + var result = new Result(); + var err_msg = string.Empty; + + if (null == otherEntityAttribute) + { + err_msg = $"Invalid Param !!!, otherEntityAttribute is null"; + result.setFail(ServerErrorCode.FunctionParamNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + //===================================================================================== + // OtherAttribute => Attribute + //===================================================================================== + var ranking_attribute = otherEntityAttribute as RankingAttribute; + if (null == ranking_attribute) + { + err_msg = $"Failed to cast RankingAttribute !!!, ranking_attribute is null"; + result.setFail(ServerErrorCode.ClassTypeCastIsNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + RankingGuid = ranking_attribute.RankingGuid; + RankingMetaId = ranking_attribute.RankingMetaId; + NextTimes = new(ranking_attribute.NextTimes); + LastestSnapshotIndex = ranking_attribute.LastestSnapshotIndex; + + //===================================================================================== + // Attribute Try Pending Doc => Origin Doc + //===================================================================================== + var try_pending_doc = ranking_attribute.getTryPendingDocBase() as RankingDoc; + if (null != try_pending_doc) + { + ranking_attribute.resetTryPendingDocBase(); + + syncOriginDocBaseWithNewDoc(try_pending_doc); + } + + var origin_doc_base = getOriginDocBase(); + if (null == origin_doc_base) + { + // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! + return result; + } + + var ranking_attrib = origin_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(ranking_attrib, () => $"ranking_attrib is null !!!"); + + ranking_attrib.RankingGuid = RankingGuid; + ranking_attrib.RankingMetaId = RankingMetaId; + ranking_attrib.NextTimes = new(NextTimes); + ranking_attrib.LastestSnapshotIndex = LastestSnapshotIndex; + + return result; + } + } + + public class RankingAttributeTransactor : EntityAttributeTransactorBase, ICopyEntityAttributeTransactorFromEntityAttribute + { + public RankingAttributeTransactor(EntityBase owner) + : base(owner) + { + } + + public bool copyEntityAttributeTransactorFromEntityAttribute(EntityAttributeBase? entityAttributeBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(RankingAttribute).Name; + + var copy_from_ranking_attribute = entityAttributeBase as RankingAttribute; + if (null == copy_from_ranking_attribute) + { + err_msg = $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_from_ranking_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + var copy_to_ranking_attribute = getClonedEntityAttribute() as RankingAttribute; + if (null == copy_to_ranking_attribute) + { + err_msg = $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_to_ranking_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + copy_to_ranking_attribute.RankingGuid = copy_from_ranking_attribute.RankingGuid; + copy_to_ranking_attribute.RankingMetaId = copy_from_ranking_attribute.RankingMetaId; + copy_to_ranking_attribute.NextTimes = new(copy_from_ranking_attribute.NextTimes); + copy_to_ranking_attribute.LastestSnapshotIndex = copy_from_ranking_attribute.LastestSnapshotIndex; + + return true; + } + } +} diff --git a/ServerCommon/Entity/Attribute/RankingScheduleAttribute.cs b/ServerCommon/Entity/Attribute/RankingScheduleAttribute.cs new file mode 100644 index 0000000..1b8b660 --- /dev/null +++ b/ServerCommon/Entity/Attribute/RankingScheduleAttribute.cs @@ -0,0 +1,282 @@ +using Newtonsoft.Json; +using ServerBase; +using ServerCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServerCommon +{ + public class RankingScheduleAttribute : EntityAttributeBase, ICopyEntityAttributeFromDoc, IMergeWithEntityAttribute + { + [JsonProperty] + public string RankingGuid { get; set; } = string.Empty; + + [JsonProperty] + public int RankingMetaId { get; set; } = 0; + + [JsonProperty] + public int EventActionScoreGroupId { get; set; } = 0; + + [JsonProperty] + public DateTime StartTime { get; set; } = new(); + + [JsonProperty] + public DateTime EndTime { get; set; } = new(); + + [JsonProperty] + public DateTime IntervalBaseTime { get; set; } = new(); + + [JsonProperty] + public Dictionary IntervalTimes { get; set; } = new(); + + public RankingScheduleStateType RankingScheduleState { get; set; } = RankingScheduleStateType.None; + + public RankingScheduleAttribute(EntityBase owner) + : base(owner) + { + } + + public override IEntityAttributeTransactor onNewEntityAttributeTransactor() + { + return new RankingScheduleAttributeTransactor(getOwner()); + } + + public override void onClear() + { + RankingGuid = string.Empty; + RankingMetaId = 0; + EventActionScoreGroupId = 0; + StartTime = new(); + EndTime = new(); + IntervalBaseTime = new(); + IntervalTimes = new(); + RankingScheduleState = RankingScheduleStateType.None; + + getAttributeState().reset(); + } + + public override EntityAttributeBase onCloned() + { + var cloned = new RankingScheduleAttribute(getOwner()); + + cloned.deepCopyFromBase(this); + + cloned.RankingGuid = RankingGuid; + cloned.RankingMetaId = RankingMetaId; + cloned.EventActionScoreGroupId = EventActionScoreGroupId; + cloned.StartTime = StartTime; + cloned.EndTime = EndTime; + cloned.IntervalBaseTime = IntervalBaseTime; + cloned.IntervalTimes = new(IntervalTimes); + cloned.RankingScheduleState = RankingScheduleState; + + return cloned; + } + + public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) + { + var result = new Result(); + + var owner = getOwner(); + NullReferenceCheckHelper.throwIfNull(owner, () => "owner is null !!!"); + + //===================================================================================== + // Attribute => try pending Doc + //===================================================================================== + var try_pending_doc = getTryPendingDocBase() as RankingScheduleDoc; + if (null == try_pending_doc) + { + var to_copy_doc = new RankingScheduleDoc(RankingGuid); + + var origin_doc = getOriginDocBase(); + if (null != origin_doc) + { + to_copy_doc.copyTimestampsFromOriginDocBase(origin_doc); + } + + try_pending_doc = to_copy_doc; + + setTryPendingDocBase(try_pending_doc); + } + + var to_copy_doc_attrib = try_pending_doc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(to_copy_doc_attrib, () => $"to_copy_doc_attrib is null !!! - {owner.toBasicString()}"); + + to_copy_doc_attrib.RankingGuid = RankingGuid; + to_copy_doc_attrib.RankingMetaId = RankingMetaId; + to_copy_doc_attrib.EventActionScoreGroupId = EventActionScoreGroupId; + to_copy_doc_attrib.StartTime = StartTime; + to_copy_doc_attrib.EndTime = EndTime; + to_copy_doc_attrib.IntervalBaseTime = IntervalBaseTime; + to_copy_doc_attrib.IntervalTimes = new(IntervalTimes); + + if (false == isForQuery) + { + return (result, try_pending_doc); + } + + //===================================================================================== + // Doc QueryType 반영 + //===================================================================================== + (result, var to_query_doc) = await applyDoc4Query(try_pending_doc); + if (result.isFail()) + { + return (result, null); + } + + return (result, to_query_doc); + } + + public bool copyEntityAttributeFromDoc(DynamoDbDocBase? docBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(RankingScheduleDoc).Name; + + var ranking_schedule_doc_base = docBase as RankingScheduleDoc; + if (null == ranking_schedule_doc_base) + { + err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, ranking_schedule_doc_base is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + //===================================================================================== + // New Doc => Origin Doc + //===================================================================================== + syncOriginDocBaseWithNewDoc(ranking_schedule_doc_base); + + //===================================================================================== + // Doc => Attribute + //===================================================================================== + var doc_attrib = ranking_schedule_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(doc_attrib, () => "doc_attrib is null !!!"); + + RankingGuid = doc_attrib.RankingGuid; + RankingMetaId = doc_attrib.RankingMetaId; + EventActionScoreGroupId = doc_attrib.EventActionScoreGroupId; + StartTime = doc_attrib.StartTime; + EndTime = doc_attrib.EndTime; + IntervalBaseTime = doc_attrib.IntervalBaseTime; + IntervalTimes= new(doc_attrib.IntervalTimes); + + return true; + } + + public Result onMerge(EntityAttributeBase otherEntityAttribute) + { + var owner = getOwner(); + NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); + + var result = new Result(); + var err_msg = string.Empty; + + if (null == otherEntityAttribute) + { + err_msg = $"Invalid Param !!!, otherEntityAttribute is null"; + result.setFail(ServerErrorCode.FunctionParamNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + //===================================================================================== + // OtherAttribute => Attribute + //===================================================================================== + var ranking_schedule_attribute = otherEntityAttribute as RankingScheduleAttribute; + if (null == ranking_schedule_attribute) + { + err_msg = $"Failed to cast RankingScheduleAttribute !!!, ranking_schedule_attribute is null"; + result.setFail(ServerErrorCode.ClassTypeCastIsNull, err_msg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + RankingGuid = ranking_schedule_attribute.RankingGuid; + RankingMetaId = ranking_schedule_attribute.RankingMetaId; + EventActionScoreGroupId = ranking_schedule_attribute.EventActionScoreGroupId; + StartTime = ranking_schedule_attribute.StartTime; + EndTime = ranking_schedule_attribute.EndTime; + IntervalBaseTime = ranking_schedule_attribute.IntervalBaseTime; + IntervalTimes = new(ranking_schedule_attribute.IntervalTimes); + RankingScheduleState = ranking_schedule_attribute.RankingScheduleState; + + //===================================================================================== + // Attribute Try Pending Doc => Origin Doc + //===================================================================================== + var try_pending_doc = ranking_schedule_attribute.getTryPendingDocBase() as RankingScheduleDoc; + if (null != try_pending_doc) + { + ranking_schedule_attribute.resetTryPendingDocBase(); + + syncOriginDocBaseWithNewDoc(try_pending_doc); + } + + var origin_doc_base = getOriginDocBase(); + if (null == origin_doc_base) + { + // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! + return result; + } + + var ranking_schedule_attrib = origin_doc_base.getAttrib(); + NullReferenceCheckHelper.throwIfNull(ranking_schedule_attrib, () => $"ranking_schedule_attrib is null !!! - {owner.toBasicString()}"); + + ranking_schedule_attrib.RankingGuid = RankingGuid; + ranking_schedule_attrib.RankingMetaId = RankingMetaId; + ranking_schedule_attrib.EventActionScoreGroupId = EventActionScoreGroupId; + ranking_schedule_attrib.StartTime = StartTime; + ranking_schedule_attrib.EndTime = EndTime; + ranking_schedule_attrib.IntervalBaseTime = IntervalBaseTime; + ranking_schedule_attrib.IntervalTimes = new(IntervalTimes); + + return result; + } + } + + public class RankingScheduleAttributeTransactor : EntityAttributeTransactorBase, ICopyEntityAttributeTransactorFromEntityAttribute + { + public RankingScheduleAttributeTransactor(EntityBase owner) + : base(owner) + { + } + + public bool copyEntityAttributeTransactorFromEntityAttribute(EntityAttributeBase? entityAttributeBase) + { + var err_msg = string.Empty; + + var to_cast_string = typeof(RankingScheduleAttribute).Name; + + var copy_from_ranking_schedule_attribute = entityAttributeBase as RankingScheduleAttribute; + if (null == copy_from_ranking_schedule_attribute) + { + err_msg = $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_from_ranking_schedule_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + var copy_to_ranking_schedule_attribute = getClonedEntityAttribute() as RankingScheduleAttribute; + if (null == copy_to_ranking_schedule_attribute) + { + err_msg = $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_to_ranking_schedule_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + copy_to_ranking_schedule_attribute.RankingGuid = copy_from_ranking_schedule_attribute.RankingGuid; + copy_to_ranking_schedule_attribute.RankingMetaId = copy_from_ranking_schedule_attribute.RankingMetaId; + copy_to_ranking_schedule_attribute.EventActionScoreGroupId = copy_from_ranking_schedule_attribute.EventActionScoreGroupId; + copy_to_ranking_schedule_attribute.StartTime = copy_from_ranking_schedule_attribute.StartTime; + copy_to_ranking_schedule_attribute.EndTime = copy_from_ranking_schedule_attribute.EndTime; + copy_to_ranking_schedule_attribute.IntervalBaseTime = copy_from_ranking_schedule_attribute .IntervalBaseTime; + copy_to_ranking_schedule_attribute.IntervalTimes = new(copy_from_ranking_schedule_attribute.IntervalTimes); + copy_to_ranking_schedule_attribute.RankingScheduleState = copy_from_ranking_schedule_attribute.RankingScheduleState; + + return true; + } + } +} diff --git a/ServerCommon/Entity/Attribute/UserAttribute.cs b/ServerCommon/Entity/Attribute/UserAttribute.cs index e26405d..b5f1571 100644 --- a/ServerCommon/Entity/Attribute/UserAttribute.cs +++ b/ServerCommon/Entity/Attribute/UserAttribute.cs @@ -45,7 +45,7 @@ namespace ServerCommon [JsonProperty] public bool IsIntroCompleted { get; set; } = false; - + public PlayerStateType PlayerState { get; set; } public string OccupiedAnchorGuid { get; set; } = string.Empty; @@ -270,7 +270,7 @@ namespace ServerCommon Log.getLogger().error(err_msg); return false; } - + //===================================================================================== // Cache => Attribute //===================================================================================== @@ -368,7 +368,8 @@ namespace ServerCommon EOA = user_base_attrib.EOA; SelectedCharacterGuid = user_base_attrib.SelectedCharacterGuid; IsIntroCompleted = user_base_attrib.IsIntroCompleted; - + GameLoginDateTime = user_base_attrib.GameLoginDateTime; + GameLogoutDateTime = user_base_attrib.GameLogoutDateTime; return true; } diff --git a/ServerCommon/Entity/Attribute/WorldEventScoreAttribute.cs b/ServerCommon/Entity/Attribute/WorldEventScoreAttribute.cs new file mode 100644 index 0000000..3e0b607 --- /dev/null +++ b/ServerCommon/Entity/Attribute/WorldEventScoreAttribute.cs @@ -0,0 +1,224 @@ +using ServerCore; +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; +/// +/// WorldEventScore 엔티티의 데이터 속성을 관리 +/// +public class WorldEventScoreAttribute : EntityAttributeBase, ICopyEntityAttributeFromDoc, IMergeWithEntityAttribute +{ + [JsonProperty] public int WorldEventId { get; set; } + [JsonProperty] public string UserGuid { get; set; } = string.Empty; + [JsonProperty] public long Score { get; set; } + [JsonProperty] public DateTime UpdateTime { get; set; } = new(); + + public WorldEventScoreAttribute(EntityBase owner) : base(owner) { } + + public override IEntityAttributeTransactor onNewEntityAttributeTransactor() + { + return new Transactor(getOwner()); + } + + public override void onClear() + { + WorldEventId = 0; + UserGuid = string.Empty; + Score = 0; + UpdateTime = new(); + + getAttributeState().reset(); + } + + public override EntityAttributeBase onCloned() + { + var cloned = new WorldEventScoreAttribute(getOwner()); + + cloned.deepCopyFromBase(this); + + cloned.WorldEventId = WorldEventId; + cloned.UserGuid = UserGuid; + cloned.Score = Score; + cloned.UpdateTime = UpdateTime; + + return cloned; + } + + public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) + { + var result = new Result(); + + //===================================================================================== + // Attribute => try pending Doc + //===================================================================================== + if (getTryPendingDocBase() is not WorldEventScoreDoc tryPendingDoc) + { + var toCopyDoc = new WorldEventScoreDoc(WorldEventId, UserGuid); + + var originDoc = getOriginDocBase(); + if (null != originDoc) + { + toCopyDoc.copyTimestampsFromOriginDocBase(originDoc); + } + + tryPendingDoc = toCopyDoc; + + setTryPendingDocBase(tryPendingDoc); + } + + var toCopyDocAttrib = tryPendingDoc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(toCopyDocAttrib, () => $"PlayerWorldEventScoreDoc to_copy_doc_attrib is null !!!"); + + toCopyDocAttrib.WorldEventId = WorldEventId; + toCopyDocAttrib.UserGuid = UserGuid; + toCopyDocAttrib.Score = Score; + toCopyDocAttrib.UpdateTime = UpdateTime; + + if (false == isForQuery) + { + return (result, tryPendingDoc); + } + + //===================================================================================== + // Doc QueryType 반영 + //===================================================================================== + (result, var toQueryDoc) = await applyDoc4Query(tryPendingDoc); + if (result.isFail()) + { + return (result, null); + } + + return (result, toQueryDoc); + } + + + public bool copyEntityAttributeFromDoc(DynamoDbDocBase docBase) + { + var errMsg = string.Empty; + if (docBase is not WorldEventScoreDoc worldEventScoreDoc) + { + errMsg = $"Failed to copyEntityAttributeFromDoc() !!!, ranker_doc_base is null :{nameof(WorldEventScoreDoc)}"; + Log.getLogger().error(errMsg); + return false; + } + + //===================================================================================== + // New Doc => Origin Doc + //===================================================================================== + syncOriginDocBaseWithNewDoc(worldEventScoreDoc); + + //===================================================================================== + // Doc => Attribute + //===================================================================================== + var docAttrib = worldEventScoreDoc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(docAttrib, () => $"doc_attrib is null !!!"); + + WorldEventId = docAttrib.WorldEventId; + UserGuid = docAttrib.UserGuid; + Score = docAttrib.Score; + UpdateTime = docAttrib.UpdateTime; + + return true; + } + + + public override string toBasicString() => $"WorldEventId:{WorldEventId}, UserGuid:{UserGuid}, Score:{Score}"; + + public Result onMerge(EntityAttributeBase? otherEntityAttribute) + { + var result = new Result(); + var errMsg = string.Empty; + + if (null == otherEntityAttribute) + { + errMsg = $"Invalid Param !!!, otherEntityAttribute is null"; + result.setFail(ServerErrorCode.FunctionParamNull, errMsg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + //===================================================================================== + // OtherAttribute => Attribute + //===================================================================================== + if (otherEntityAttribute is not WorldEventScoreAttribute worldEventScoreAttribute) + { + errMsg = $"Failed to cast WorldEventScoreAttribute !!!, WorldEventScoreAttribute is null"; + result.setFail(ServerErrorCode.ClassTypeCastIsNull, errMsg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + WorldEventId = worldEventScoreAttribute.WorldEventId; + UserGuid = worldEventScoreAttribute.UserGuid; + Score = worldEventScoreAttribute.Score; + UpdateTime = worldEventScoreAttribute.UpdateTime; + + //===================================================================================== + // Attribute Try Pending Doc => Origin Doc + //===================================================================================== + if (worldEventScoreAttribute.getTryPendingDocBase() is WorldEventScoreDoc tryPendingDoc) + { + worldEventScoreAttribute.resetTryPendingDocBase(); + + syncOriginDocBaseWithNewDoc(tryPendingDoc); + } + + var originDocBase = getOriginDocBase(); + if (null == originDocBase) + { + // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! + return result; + } + + var attrib = originDocBase.getAttrib(); + NullReferenceCheckHelper.throwIfNull(attrib, () => $"WorldEventScoreAttrib is null !!!"); + + attrib.WorldEventId = WorldEventId; + attrib.UserGuid = UserGuid; + attrib.Score = Score; + attrib.UpdateTime = UpdateTime; + + return result; + } + + public class Transactor : EntityAttributeTransactorBase, + ICopyEntityAttributeTransactorFromEntityAttribute + { + public Transactor(EntityBase owner) + : base(owner) + { + } + + public bool copyEntityAttributeTransactorFromEntityAttribute(EntityAttributeBase? entityAttributeBase) + { + var err_msg = string.Empty; + + var to_cast_string = nameof(WorldEventScoreAttribute); + + if (entityAttributeBase is not WorldEventScoreAttribute src) + { + err_msg = + $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_from_building_floor_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + if (getClonedEntityAttribute() is not WorldEventScoreAttribute dest) + { + err_msg = + $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_to_building_floor_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + dest.WorldEventId = src.WorldEventId; + dest.UserGuid = src.UserGuid; + dest.UpdateTime = src.UpdateTime; + dest.Score = src.Score; + return true; + } + } +} + diff --git a/ServerCommon/Entity/Attribute/WorldEventTotalScoreAttribute.cs b/ServerCommon/Entity/Attribute/WorldEventTotalScoreAttribute.cs new file mode 100644 index 0000000..d9b0184 --- /dev/null +++ b/ServerCommon/Entity/Attribute/WorldEventTotalScoreAttribute.cs @@ -0,0 +1,218 @@ +using ServerCore; +using Newtonsoft.Json; +using ServerBase; + +namespace ServerCommon; + +/// +/// PlayerWorldEventScore 엔티티의 데이터 속성을 관리합니다. +/// +public class WorldEventTotalScoreAttribute : EntityAttributeBase, ICopyEntityAttributeFromDoc, IMergeWithEntityAttribute +{ + [JsonProperty] public int WorldEventId { get; set; } + [JsonProperty] public long Score { get; set; } + [JsonProperty] public DateTime UpdateTime { get; set; } = new(); + + public WorldEventTotalScoreAttribute(EntityBase owner) : base(owner) { } + + public override IEntityAttributeTransactor onNewEntityAttributeTransactor() + { + return new Transactor(getOwner()); + } + + public override void onClear() + { + WorldEventId = 0; + Score = 0; + UpdateTime = new(); + + getAttributeState().reset(); + } + + public override EntityAttributeBase onCloned() + { + var cloned = new WorldEventTotalScoreAttribute(getOwner()); + + cloned.deepCopyFromBase(this); + + cloned.WorldEventId = WorldEventId; + cloned.Score = Score; + cloned.UpdateTime = UpdateTime; + + return cloned; + } + + public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) + { + var result = new Result(); + + //===================================================================================== + // Attribute => try pending Doc + //===================================================================================== + if (getTryPendingDocBase() is not WorldEventGlobalScoreDoc tryPendingDoc) + { + var toCopyDoc = new WorldEventGlobalScoreDoc(WorldEventId); + + var originDoc = getOriginDocBase(); + if (null != originDoc) + { + toCopyDoc.copyTimestampsFromOriginDocBase(originDoc); + } + + tryPendingDoc = toCopyDoc; + + setTryPendingDocBase(tryPendingDoc); + } + + var toCopyDocAttrib = tryPendingDoc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(toCopyDocAttrib, + () => $"PlayerWorldEventScoreDoc to_copy_doc_attrib is null !!!"); + + toCopyDocAttrib.WorldEventId = WorldEventId; + toCopyDocAttrib.Score = Score; + toCopyDocAttrib.UpdateTime = UpdateTime; + + if (false == isForQuery) + { + return (result, tryPendingDoc); + } + + //===================================================================================== + // Doc QueryType 반영 + //===================================================================================== + (result, var toQueryDoc) = await applyDoc4Query(tryPendingDoc); + if (result.isFail()) + { + return (result, null); + } + + return (result, toQueryDoc); + } + + + public bool copyEntityAttributeFromDoc(DynamoDbDocBase docBase) + { + var errMsg = string.Empty; + if (docBase is not WorldEventGlobalScoreDoc worldEventScoreDoc) + { + errMsg = + $"Failed to copyEntityAttributeFromDoc() !!!, ranker_doc_base is null :{nameof(WorldEventGlobalScoreDoc)}"; + Log.getLogger().error(errMsg); + return false; + } + + //===================================================================================== + // New Doc => Origin Doc + //===================================================================================== + syncOriginDocBaseWithNewDoc(worldEventScoreDoc); + + //===================================================================================== + // Doc => Attribute + //===================================================================================== + var docAttrib = worldEventScoreDoc.getAttrib(); + NullReferenceCheckHelper.throwIfNull(docAttrib, () => $"doc_attrib is null !!!"); + + WorldEventId = docAttrib.WorldEventId; + Score = docAttrib.Score; + UpdateTime = docAttrib.UpdateTime; + + return true; + } + + + public override string toBasicString() => $"WorldEventId:{WorldEventId}, TotalScore:{Score}"; + + public Result onMerge(EntityAttributeBase? otherEntityAttribute) + { + var result = new Result(); + var errMsg = string.Empty; + + if (null == otherEntityAttribute) + { + errMsg = $"Invalid Param !!!, otherEntityAttribute is null"; + result.setFail(ServerErrorCode.FunctionParamNull, errMsg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + //===================================================================================== + // OtherAttribute => Attribute + //===================================================================================== + if (otherEntityAttribute is not WorldEventTotalScoreAttribute playerWorldEventScoreAttribute) + { + errMsg = $"Failed to cast RankAttribute !!!, ranker_attribute is null"; + result.setFail(ServerErrorCode.ClassTypeCastIsNull, errMsg); + Log.getLogger().error(result.toBasicString()); + + return result; + } + + WorldEventId = playerWorldEventScoreAttribute.WorldEventId; + Score = playerWorldEventScoreAttribute.Score; + UpdateTime = playerWorldEventScoreAttribute.UpdateTime; + + //===================================================================================== + // Attribute Try Pending Doc => Origin Doc + //===================================================================================== + if (playerWorldEventScoreAttribute.getTryPendingDocBase() is WorldEventGlobalScoreDoc tryPendingDoc) + { + playerWorldEventScoreAttribute.resetTryPendingDocBase(); + + syncOriginDocBaseWithNewDoc(tryPendingDoc); + } + + var originDocBase = getOriginDocBase(); + if (null == originDocBase) + { + // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! + return result; + } + + var rankerAttrib = originDocBase.getAttrib(); + NullReferenceCheckHelper.throwIfNull(rankerAttrib, () => $"ranker_attrib is null !!!"); + + rankerAttrib.WorldEventId = WorldEventId; + rankerAttrib.Score = Score; + rankerAttrib.UpdateTime = UpdateTime; + + return result; + } + + public class Transactor : EntityAttributeTransactorBase, + ICopyEntityAttributeTransactorFromEntityAttribute + { + public Transactor(EntityBase owner) + : base(owner) + { + } + + public bool copyEntityAttributeTransactorFromEntityAttribute(EntityAttributeBase? entityAttributeBase) + { + var err_msg = string.Empty; + + var to_cast_string = nameof(WorldEventTotalScoreAttribute); + + if (entityAttributeBase is not WorldEventTotalScoreAttribute src) + { + err_msg = + $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_from_building_floor_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + if (getClonedEntityAttribute() is not WorldEventTotalScoreAttribute dest) + { + err_msg = + $"Failed to copyEntityAttributeTransactorBaseFromEntityAttribute() !!!, copy_to_building_floor_attribute is null :{to_cast_string}"; + Log.getLogger().error(err_msg); + return false; + } + + dest.WorldEventId = src.WorldEventId; + dest.UpdateTime = src.UpdateTime; + dest.Score = src.Score; + return true; + } + } +} diff --git a/ServerCommon/Entity/DbQuery/DBQEntityReadToAttribute.cs b/ServerCommon/Entity/DbQuery/DBQEntityReadToAttribute.cs index 57dc8ec..a75ecd3 100644 --- a/ServerCommon/Entity/DbQuery/DBQEntityReadToAttribute.cs +++ b/ServerCommon/Entity/DbQuery/DBQEntityReadToAttribute.cs @@ -126,7 +126,7 @@ public class DBQEntityReadToAttribute : QueryExecutorBase return result; } - var msg = $"{this.getTypeName()} : readCount:{read_docs.Count}, isUseTransact:{query_batch.isUseTransact} - TransId:{query_batch.getTransId()}"; + var msg = $"{this.getTypeName()} : readCount:{read_docs.Count}, isUseTransact:{query_batch.isUseTransact()} - TransId:{query_batch.getTransId()}"; Log.getLogger().info(msg); foreach ( var read_doc in read_docs ) diff --git a/ServerCommon/Entity/Player/UserBase.cs b/ServerCommon/Entity/Player/UserBase.cs index 1f1d300..9bc8345 100644 --- a/ServerCommon/Entity/Player/UserBase.cs +++ b/ServerCommon/Entity/Player/UserBase.cs @@ -46,6 +46,10 @@ public abstract partial class UserBase : EntityBase, IEntityWithSession, IWithLo m_login_start_time = DateTimeHelper.Current; } + public UserBase(EntityType entityType) : base(entityType) + { + } + public override async Task onInit() { addEntityAttribute(new AccountAttribute(this)); diff --git a/ServerCommon/Helper/ConfigHelper.cs b/ServerCommon/Helper/ConfigHelper.cs index c2f9bd0..a4047c6 100644 --- a/ServerCommon/Helper/ConfigHelper.cs +++ b/ServerCommon/Helper/ConfigHelper.cs @@ -31,8 +31,7 @@ public static class ConfigHelper public static ServerType getServerType(this IConfiguration config) { - var value = config["config"]; - + var value = config["serverType"]; if (Enum.TryParse(value, true, out var serverType)) { return serverType; diff --git a/ServerCommon/Helper/EntityHelper.cs b/ServerCommon/Helper/EntityHelper.cs index 6a7141b..6bcebf7 100644 --- a/ServerCommon/Helper/EntityHelper.cs +++ b/ServerCommon/Helper/EntityHelper.cs @@ -47,7 +47,7 @@ public static class EntityHelper master_entity = owner.onGetMasterEntity() as TMasterEntity; if (null == master_entity) { - err_msg = $"Not related Master !!! : TMasterEntity:{nameof(TMasterEntity)} - {owner.toBasicString()}"; + err_msg = $"Not related Master !!! : TMasterEntity:{typeof(TMasterEntity).getTypeName()} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.MasterNotFound, err_msg); Log.getLogger().error(err_msg); @@ -56,7 +56,7 @@ public static class EntityHelper } else { - err_msg = $"Not related Master !!! : TMasterEntity:{nameof(TMasterEntity)} - {owner.toBasicString()}"; + err_msg = $"Not related Master !!! : TMasterEntity:{typeof(TMasterEntity).getTypeName()} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.MasterNotRelated, err_msg); Log.getLogger().error(err_msg); diff --git a/ServerCommon/Helper/InventoryRuleHelper.cs b/ServerCommon/Helper/InventoryRuleHelper.cs index 3eba63f..d32128f 100644 --- a/ServerCommon/Helper/InventoryRuleHelper.cs +++ b/ServerCommon/Helper/InventoryRuleHelper.cs @@ -27,6 +27,9 @@ using ITEM_GUID = System.String; namespace ServerCommon; + +// HANDOVER: 아이템 인벤토리 관련 정책 Helper 클래스 이다. + public static class InventoryRuleHelper { public static Int16 getBagTapMaxSlotCount() diff --git a/ServerCommon/Helper/LoadBalanceServerHelper.cs b/ServerCommon/Helper/LoadBalanceServerHelper.cs index ca014e4..3177e76 100644 --- a/ServerCommon/Helper/LoadBalanceServerHelper.cs +++ b/ServerCommon/Helper/LoadBalanceServerHelper.cs @@ -7,6 +7,10 @@ using WORLD_META_ID = System.UInt32; namespace ServerCommon; + +// HANDOVER: 메타버스 GameServer 로드밸런싱 처리 Hepler 클래스 이다. + + public static class LoadBalanceServerHelper { private const int READ_SERVER_DATA_LIMIT_COUNT = 5; @@ -137,18 +141,22 @@ public static class LoadBalanceServerHelper // 1. 기본 Default 정보 확인 if (!MetaData.Instance._GameConfigMetaTable.TryGetValue(ConstantKeyType.DefaultEntryWorldIdWhenLoginToAuth.ToString(), out var found_value)) { - var err_msg = $"ConstantKeyType.DefaultEntryWorldIdWhenLoginToAuth not found !!! : worldId:{found_value} - {entityUser.toBasicString()}"; + var err_msg = $"ConstantKeyType.DefaultEntryWorldIdWhenLoginToAuth not found !!! - {entityUser.toBasicString()}"; Log.getLogger().fatal(err_msg); - return 0; + return target_world_id; } + target_world_id = Convert.ToUInt16(found_value); // 기본 값 세팅 + // 2. LastConnectionChannel 정보 확인 var location_attribute = entityUser.getOriginEntityAttribute(); if (null == location_attribute) return target_world_id; - target_world_id = (location_attribute.LastestChannelServerLocation.WorldMetaId == 0) - ? Convert.ToUInt16(found_value) - : (UInt16)location_attribute.LastestChannelServerLocation.WorldMetaId; + var lastest_world_id = location_attribute.LastestChannelServerLocation.WorldMetaId; + if (MetaData.Instance._WorldMetaTable.TryGetValue(lastest_world_id, out _)) + { + target_world_id = (UInt16)lastest_world_id; // 마지막 접속 월드 세팅(월드메타 존재시) + } return target_world_id; } diff --git a/ServerCommon/Helper/MetaHelper.cs b/ServerCommon/Helper/MetaHelper.cs index e0c2b87..23b0220 100644 --- a/ServerCommon/Helper/MetaHelper.cs +++ b/ServerCommon/Helper/MetaHelper.cs @@ -490,6 +490,8 @@ public static partial class MetaHelper case MetaAssets.EItemSmallType.BACKPACK: case MetaAssets.EItemSmallType.BAG: case MetaAssets.EItemSmallType.SHOULDERBAG: + case MetaAssets.EItemSmallType.DRESS_SHOES: + case MetaAssets.EItemSmallType.DRESS_ALL: break; default: @@ -524,6 +526,8 @@ public static partial class MetaHelper break; case MetaAssets.EItemSmallType.SHIRT: case MetaAssets.EItemSmallType.DRESS: + case MetaAssets.EItemSmallType.DRESS_SHOES: + case MetaAssets.EItemSmallType.DRESS_ALL: clothInfo.ClothTops = meta_id; break; case MetaAssets.EItemSmallType.PANTS: diff --git a/ServerCommon/Helper/ServerConnectionSwitchHelper.cs b/ServerCommon/Helper/ServerConnectionSwitchHelper.cs index b910048..73a10f6 100644 --- a/ServerCommon/Helper/ServerConnectionSwitchHelper.cs +++ b/ServerCommon/Helper/ServerConnectionSwitchHelper.cs @@ -31,6 +31,8 @@ using ITEM_GUID = System.String; namespace ServerCommon; +// HANDOVER: GameServer 접속 이동 Helper 클래스 이다. + public static class ServerConnectionSwitchHelper { public static async Task registerUserOtpToTargetServer(UserBase entityUser, RedisConnector redisConnector diff --git a/ServerCommon/Helper/ServerMetricsHelper.cs b/ServerCommon/Helper/ServerMetricsHelper.cs index 1974ce2..aa748d1 100644 --- a/ServerCommon/Helper/ServerMetricsHelper.cs +++ b/ServerCommon/Helper/ServerMetricsHelper.cs @@ -13,6 +13,10 @@ using WORLD_META_ID = System.UInt32; namespace ServerCommon; + +// HANDOVER: 서버 지표 관련 Helper 클래스 이다. + + public static class ServerMetricsHelper { public static async Task<(Result, List)> getServerInfoAll(this IWithServerMetrics serverMetrics) diff --git a/ServerCommon/Helper/StringRuleHelper.cs b/ServerCommon/Helper/StringRuleHelper.cs index 153302a..5e9c1d8 100644 --- a/ServerCommon/Helper/StringRuleHelper.cs +++ b/ServerCommon/Helper/StringRuleHelper.cs @@ -15,6 +15,9 @@ using ServerBase; namespace ServerCommon; + +// HANDOVER: 문자열 정책 관련 Helper 클래스 이다. + public static class StringRuleHelper { public static ServerErrorCode isValidNickname(string nickName) diff --git a/ServerCommon/Helper/TypeHelper.cs b/ServerCommon/Helper/TypeHelper.cs index 502961b..2432713 100644 --- a/ServerCommon/Helper/TypeHelper.cs +++ b/ServerCommon/Helper/TypeHelper.cs @@ -41,6 +41,8 @@ public static class TypeHelper return EPlaceType.BattleRoom; case ContentsType.ArcadeRunning: return EPlaceType.ArcadeRunning; + case ContentsType.GameRoom: + return EPlaceType.GameRoom; } return EPlaceType.NONE; diff --git a/ServerCommon/Meta/GameConfigMeta.cs b/ServerCommon/Meta/GameConfigMeta.cs index 546ea8c..7cea4d1 100644 --- a/ServerCommon/Meta/GameConfigMeta.cs +++ b/ServerCommon/Meta/GameConfigMeta.cs @@ -23,668 +23,683 @@ using CHARACTER_GUID = System.String; using ITEM_GUID = System.String; -namespace ServerCommon +namespace ServerCommon; + + +// HANDOVER: 메타 데이터 관련 Helper 클래스 이다. + + +public static partial class MetaHelper { - public static partial class MetaHelper + //============================================================================================= + // GameConfigMeta 데이터 정보를 지원 한다. + // + // author : kangms + // + //============================================================================================= + + public static class GameConfigMeta { - //============================================================================================= - // GameConfigMeta 데이터 정보를 지원 한다. - // - // author : kangms - // - //============================================================================================= + public static string MetaverseOwnerName => m_metaverse_owner_name.Value; + private static Lazy m_metaverse_owner_name = new Lazy(() => getValueToString("MetaverseOwnerName", "Caliverse")); + public static string DefaultTestAccountId => m_default_test_account_id.Value; + private static Lazy m_default_test_account_id = new Lazy(() => getValueToString("DefaultTestAccountId", "DefaultTestId")); - public static class GameConfigMeta - { - public static string MetaverseOwnerName => m_metaverse_owner_name.Value; - private static Lazy m_metaverse_owner_name = new Lazy(() => getValueToString("MetaverseOwnerName", "Caliverse")); - public static string DefaultTestAccountId => m_default_test_account_id.Value; - private static Lazy m_default_test_account_id = new Lazy(() => getValueToString("DefaultTestAccountId", "DefaultTestId")); + public static Int16 UserNicknameMinLength => m_user_nickname_min_length.Value; + private static Lazy m_user_nickname_min_length = new Lazy(() => getValueToInt16("UserNicknameMinLength", 2)); - public static Int16 UserNicknameMinLength => m_user_nickname_min_length.Value; - private static Lazy m_user_nickname_min_length = new Lazy(() => getValueToInt16("UserNicknameMinLength", 2)); + public static Int16 UserNicknameMaxLength => m_user_nickname_max_length.Value; + private static Lazy m_user_nickname_max_length = new Lazy(() => getValueToInt16("UserNicknameMaxLength", 12)); + + public static Int16 MaxItemCategorySlotNum => m_max_item_category_slot_num.Value; + private static Lazy m_max_item_category_slot_num = new Lazy(() => getValueToInt16("MaxItemCategorySlotNum", 80)); - public static Int16 UserNicknameMaxLength => m_user_nickname_max_length.Value; - private static Lazy m_user_nickname_max_length = new Lazy(() => getValueToInt16("UserNicknameMaxLength", 12)); - - public static Int16 MaxItemCategorySlotNum => m_max_item_category_slot_num.Value; - private static Lazy m_max_item_category_slot_num = new Lazy(() => getValueToInt16("MaxItemCategorySlotNum", 80)); + public static Int16 CharacterCreateMaxCount => m_character_create_max_count.Value; + private static Lazy m_character_create_max_count = new Lazy(() => getValueToInt16("CharacterCreateMaxCount", 1)); - public static Int16 CharacterCreateMaxCount => m_character_create_max_count.Value; - private static Lazy m_character_create_max_count = new Lazy(() => getValueToInt16("CharacterCreateMaxCount", 1)); + public static Int16 MaxFriendsNum => m_max_friends_num.Value; + private static Lazy m_max_friends_num = new Lazy(() => getValueToInt16("MaxFriendsNum", 50)); - public static Int16 MaxFriendsNum => m_max_friends_num.Value; - private static Lazy m_max_friends_num = new Lazy(() => getValueToInt16("MaxFriendsNum", 50)); + public static Int16 MaxBlockUserNum => m_max_block_user_num.Value; + private static Lazy m_max_block_user_num = new Lazy(() => getValueToInt16("MaxBlockUserNum", 50)); - public static Int16 MaxBlockUserNum => m_max_block_user_num.Value; - private static Lazy m_max_block_user_num = new Lazy(() => getValueToInt16("MaxBlockUserNum", 50)); + public static Int32 SentFriendRequestValidTime => m_sent_friend_request_valid_time.Value; + private static Lazy m_sent_friend_request_valid_time = new Lazy(() => getValueToInt32("SentFriendRequestValidTime", 2592000)); //초 - public static Int32 SentFriendRequestValidTime => m_sent_friend_request_valid_time.Value; - private static Lazy m_sent_friend_request_valid_time = new Lazy(() => getValueToInt32("SentFriendRequestValidTime", 2592000)); //초 + public static Int16 EscapeCoolTime => m_eacape_cool_time.Value; + private static Lazy m_eacape_cool_time = new Lazy(() => getValueToInt16("EscapeCoolTime", 60)); //분 - public static Int16 EscapeCoolTime => m_eacape_cool_time.Value; - private static Lazy m_eacape_cool_time = new Lazy(() => getValueToInt16("EscapeCoolTime", 60)); //분 + public static Int16 MaxTutorialQuest => m_max_tutorial_quest.Value; + private static Lazy m_max_tutorial_quest = new Lazy(() => getValueToInt16("MaxTutorialQuest", 1)); - public static Int16 MaxTutorialQuest => m_max_tutorial_quest.Value; - private static Lazy m_max_tutorial_quest = new Lazy(() => getValueToInt16("MaxTutorialQuest", 1)); + public static Int16 MaxUGQuest => m_max_ugq_quest.Value; + private static Lazy m_max_ugq_quest = new Lazy(() => getValueToInt16("MaxUGQuest ", 2)); - public static Int16 MaxUGQuest => m_max_ugq_quest.Value; - private static Lazy m_max_ugq_quest = new Lazy(() => getValueToInt16("MaxUGQuest ", 2)); + public static Int16 MaxEpicQuest => m_max_epic_quest.Value; + private static Lazy m_max_epic_quest = new Lazy(() => getValueToInt16("MaxEpicQuest", 3)); - public static Int16 MaxEpicQuest => m_max_epic_quest.Value; - private static Lazy m_max_epic_quest = new Lazy(() => getValueToInt16("MaxEpicQuest", 3)); + public static Int16 MaxNormalQuest => m_max_normal_quest.Value; + private static Lazy m_max_normal_quest = new Lazy(() => getValueToInt16("MaxNormalQuest", 5)); - public static Int16 MaxNormalQuest => m_max_normal_quest.Value; - private static Lazy m_max_normal_quest = new Lazy(() => getValueToInt16("MaxNormalQuest", 5)); + public static Int16 CooltimeNormalQuestSendMail => m_cooltime_normal_quest_send_mail.Value; + private static Lazy m_cooltime_normal_quest_send_mail = new Lazy(() => getValueToInt16("CooltimeNormalQuestSendMail", 180)); //분 - public static Int16 CooltimeNormalQuestSendMail => m_cooltime_normal_quest_send_mail.Value; - private static Lazy m_cooltime_normal_quest_send_mail = new Lazy(() => getValueToInt16("CooltimeNormalQuestSendMail", 180)); //분 + public static Int16 MaxQuestSendMail => m_max_quest_send_mail.Value; + private static Lazy m_max_quest_send_mail = new Lazy(() => getValueToInt16("MaxQuestSendMail", 20)); - public static Int16 MaxQuestSendMail => m_max_quest_send_mail.Value; - private static Lazy m_max_quest_send_mail = new Lazy(() => getValueToInt16("MaxQuestSendMail", 20)); + public static Int16 FriendFolderNameMaxLength => m_friend_folder_name_max_length.Value; + private static Lazy m_friend_folder_name_max_length = new Lazy(() => getValueToInt16("FriendFolderNameMaxLength", 8)); - public static Int16 FriendFolderNameMaxLength => m_friend_folder_name_max_length.Value; - private static Lazy m_friend_folder_name_max_length = new Lazy(() => getValueToInt16("FriendFolderNameMaxLength", 8)); + public static Int16 FriendFolderMaxCount => m_friend_folder_max_count.Value; + private static Lazy m_friend_folder_max_count = new Lazy(() => getValueToInt16("FriendFolderMaxCount", 10)); - public static Int16 FriendFolderMaxCount => m_friend_folder_max_count.Value; - private static Lazy m_friend_folder_max_count = new Lazy(() => getValueToInt16("FriendFolderMaxCount", 10)); + public static Int16 FriendFolderMaxHoldCount => m_friend_folder_max_hold_count.Value; + private static Lazy m_friend_folder_max_hold_count = new Lazy(() => getValueToInt16("FriendFolderMaxHoldCount", 3)); - public static Int16 FriendFolderMaxHoldCount => m_friend_folder_max_hold_count.Value; - private static Lazy m_friend_folder_max_hold_count = new Lazy(() => getValueToInt16("FriendFolderMaxHoldCount", 3)); + public static Int32 FriendIntiveCoolTime => m_friend_intive_cool_time.Value; + private static Lazy m_friend_intive_cool_time = new Lazy(() => getValueToInt32("FriendIntiveCoolTime", 600)); - public static Int32 FriendIntiveCoolTime => m_friend_intive_cool_time.Value; - private static Lazy m_friend_intive_cool_time = new Lazy(() => getValueToInt32("FriendIntiveCoolTime", 600)); + public static Int32 FriendInviteReplyTime => m_friend_invite_reply_time.Value; + private static Lazy m_friend_invite_reply_time = new Lazy(() => getValueToInt32("FriendInviteReplyTime", 60)); - public static Int32 FriendInviteReplyTime => m_friend_invite_reply_time.Value; - private static Lazy m_friend_invite_reply_time = new Lazy(() => getValueToInt32("FriendInviteReplyTime", 60)); + public static Int32 MaxPartyMembers => m_max_party_members.Value; + private static Lazy m_max_party_members = new Lazy(() => getValueToInt32("MaxPartyMembers", 5)); - public static Int32 MaxPartyMembers => m_max_party_members.Value; - private static Lazy m_max_party_members = new Lazy(() => getValueToInt32("MaxPartyMembers", 5)); + public static Int32 MaxPartyNameInput => m_max_party_name_input.Value; + private static Lazy m_max_party_name_input = new Lazy(() => getValueToInt32("MaxPartyNameInput", 10)); - public static Int32 MaxPartyNameInput => m_max_party_name_input.Value; - private static Lazy m_max_party_name_input = new Lazy(() => getValueToInt32("MaxPartyNameInput", 10)); + public static Int32 MaxVoteAgendaInput => m_max_vote_agenda_input.Value; + private static Lazy m_max_vote_agenda_input = new Lazy(() => getValueToInt32("MaxVoteAgendaInput", 50)); + public static Int32 ItemAmountPartyInstance => m_item_amount_party_instance.Value; + private static Lazy m_item_amount_party_instance = new Lazy(() => getValueToInt32("ItemAmountPartyInstance", 1)); - public static Int32 MaxVoteAgendaInput => m_max_vote_agenda_input.Value; - private static Lazy m_max_vote_agenda_input = new Lazy(() => getValueToInt32("MaxVoteAgendaInput", 50)); - public static Int32 ItemAmountPartyInstance => m_item_amount_party_instance.Value; - private static Lazy m_item_amount_party_instance = new Lazy(() => getValueToInt32("ItemAmountPartyInstance", 1)); + public static Int32 PartyConcertWaitingTimeSec => m_party_concert_wait_time.Value; + private static Lazy m_party_concert_wait_time = new Lazy(() => getValueToInt32("PartyConcertWaitingTime", 180)); - public static Int32 PartyConcertWaitingTimeSec => m_party_concert_wait_time.Value; - private static Lazy m_party_concert_wait_time = new Lazy(() => getValueToInt32("PartyConcertWaitingTime", 180)); + public static Int32 ItemAmountPartySummon => m_item_amount_party_summon.Value; + private static Lazy m_item_amount_party_summon = new Lazy(() => getValueToInt32("ItemAmountPartySummon", 1)); - public static Int32 ItemAmountPartySummon => m_item_amount_party_summon.Value; - private static Lazy m_item_amount_party_summon = new Lazy(() => getValueToInt32("ItemAmountPartySummon", 1)); + public static Int32 SummonStoneItemMetaId => m_summon_stone_item_meta_id.Value; + private static Lazy m_summon_stone_item_meta_id = new Lazy(() => getValueToInt32("SummonStone", 12630001)); - public static Int32 SummonStoneItemMetaId => m_summon_stone_item_meta_id.Value; - private static Lazy m_summon_stone_item_meta_id = new Lazy(() => getValueToInt32("SummonStone", 12630001)); + public static Int32 VoteTimeLimitSec => m_vote_time_limit_sec.Value; + private static Lazy m_vote_time_limit_sec = new Lazy(() => getValueToInt32("VoteTimeLimit", 60)); - public static Int32 VoteTimeLimitSec => m_vote_time_limit_sec.Value; - private static Lazy m_vote_time_limit_sec = new Lazy(() => getValueToInt32("VoteTimeLimit", 60)); + public static Int32 VoteCoolTimeSec => m_vote_cool_time_sec.Value; + private static Lazy m_vote_cool_time_sec = new(() => getValueToInt32("PartyVoteCoolTime", 60 * 5)); - public static Int32 VoteCoolTimeSec => m_vote_cool_time_sec.Value; - private static Lazy m_vote_cool_time_sec = new(() => getValueToInt32("PartyVoteCoolTime", 60 * 5)); + public static Int32 CraftingHelpLimitCount => m_crafting_help_limit_count.Value; + private static Lazy m_crafting_help_limit_count = new Lazy(() => getValueToInt32("CraftingHelpLimitCountAll", 5)); - public static Int32 CraftingHelpLimitCount => m_crafting_help_limit_count.Value; - private static Lazy m_crafting_help_limit_count = new Lazy(() => getValueToInt32("CraftingHelpLimitCountAll", 5)); + public static Int32 CraftingHelpLimitCountReceive => m_crafting_help_limit_count_receive.Value; + private static Lazy m_crafting_help_limit_count_receive = new Lazy(() => getValueToInt32("CraftingHelpLimitCountReceive", 5)); - public static Int32 CraftingHelpLimitCountReceive => m_crafting_help_limit_count_receive.Value; - private static Lazy m_crafting_help_limit_count_receive = new Lazy(() => getValueToInt32("CraftingHelpLimitCountReceive", 5)); + public static Int32 CraftingHelpLimitCountSameUser => m_crafting_help_limit_count_sameuser.Value; + private static Lazy m_crafting_help_limit_count_sameuser = new Lazy(() => getValueToInt32("CraftingHelpLimitCountSameUser", 1)); - public static Int32 CraftingHelpLimitCountSameUser => m_crafting_help_limit_count_sameuser.Value; - private static Lazy m_crafting_help_limit_count_sameuser = new Lazy(() => getValueToInt32("CraftingHelpLimitCountSameUser", 1)); + public static Int32 CraftingHelpReduceTime => m_crafting_help_reduce_time.Value; + private static Lazy m_crafting_help_reduce_time = new Lazy(() => getValueToInt32("CraftingHelpReduceTime", 30)); - public static Int32 CraftingHelpReduceTime => m_crafting_help_reduce_time.Value; - private static Lazy m_crafting_help_reduce_time = new Lazy(() => getValueToInt32("CraftingHelpReduceTime", 30)); + public static Int32 CraftingHelpGetItemId => m_crafting_help_get_item_id.Value; + private static Lazy m_crafting_help_get_item_id = new Lazy(() => getValueToInt32("CraftingHelpGetItemId", 11851001)); - public static Int32 CraftingHelpGetItemId => m_crafting_help_get_item_id.Value; - private static Lazy m_crafting_help_get_item_id = new Lazy(() => getValueToInt32("CraftingHelpGetItemId", 11851001)); + public static Int32 CraftingHelpGetItemValue => m_crafting_get_item_value.Value; + private static Lazy m_crafting_get_item_value = new Lazy(() => getValueToInt32("CraftingHelpGetItemValue", 30)); - public static Int32 CraftingHelpGetItemValue => m_crafting_get_item_value.Value; - private static Lazy m_crafting_get_item_value = new Lazy(() => getValueToInt32("CraftingHelpGetItemValue", 30)); + public static Int32 AutoMoveRate => m_auto_move_rate.Value; + private static Lazy m_auto_move_rate = new Lazy(() => getValueToInt32("AutoMoveRate", 80)); - public static Int32 AutoMoveRate => m_auto_move_rate.Value; - private static Lazy m_auto_move_rate = new Lazy(() => getValueToInt32("AutoMoveRate", 80)); + public static Int32 ReturnKickTimeMinute => m_return_kick_time_minute.Value; + private static Lazy m_return_kick_time_minute = new Lazy(() => getValueToInt32("ReturnKickTimeMinute", 60)); - public static Int32 ReturnKickTimeMinute => m_return_kick_time_minute.Value; - private static Lazy m_return_kick_time_minute = new Lazy(() => getValueToInt32("ReturnKickTimeMinute", 60)); + public static Int32 BeaconCreateMaxCount => m_beacon_create_max_count.Value; + private static Lazy m_beacon_create_max_count = new Lazy(() => getValueToInt32("BeaconCreateMaxCount", 5)); - public static Int32 BeaconCreateMaxCount => m_beacon_create_max_count.Value; - private static Lazy m_beacon_create_max_count = new Lazy(() => getValueToInt32("BeaconCreateMaxCount", 5)); + public static Int32 MaxNpcFrequentSocial => m_max_npc_frequent_social.Value; + private static Lazy m_max_npc_frequent_social = new Lazy(() => getValueToInt32("MaxNpcFrequentSocial", 3)); - public static Int32 MaxNpcFrequentSocial => m_max_npc_frequent_social.Value; - private static Lazy m_max_npc_frequent_social = new Lazy(() => getValueToInt32("MaxNpcFrequentSocial", 3)); + public static Int32 MaxNpcDialogueSocial => m_max_npc_dialogue_social.Value; + private static Lazy m_max_npc_dialogue_social = new Lazy(() => getValueToInt32("MaxNpcDialogueSocial", 1)); - public static Int32 MaxNpcDialogueSocial => m_max_npc_dialogue_social.Value; - private static Lazy m_max_npc_dialogue_social = new Lazy(() => getValueToInt32("MaxNpcDialogueSocial", 1)); + public static Int32 NpcDialogueSocial => m_npc_dialogue_social.Value; + private static Lazy m_npc_dialogue_social = new Lazy(() => getValueToInt32("NpcDialogueSocial", 110054)); - public static Int32 NpcDialogueSocial => m_npc_dialogue_social.Value; - private static Lazy m_npc_dialogue_social = new Lazy(() => getValueToInt32("NpcDialogueSocial", 110054)); + public static Int32 NpcCreateDressRequirement => m_npc_create_dress_requirement.Value; + private static Lazy m_npc_create_dress_requirement = new Lazy(() => getValueToInt32("NpcCreateDressRequirement", 4)); - public static Int32 NpcCreateDressRequirement => m_npc_create_dress_requirement.Value; - private static Lazy m_npc_create_dress_requirement = new Lazy(() => getValueToInt32("NpcCreateDressRequirement", 4)); - - public static Int32 NpcCreateTattooRequirement => m_npc_create_tattoo_requirement.Value; - private static Lazy m_npc_create_tattoo_requirement = new Lazy(() => getValueToInt32("NpcCreateTattooRequirement", 3)); - - public static Int32 NpcCreateCurrency => m_npc_create_currency.Value; - private static Lazy m_npc_create_currency = new Lazy(() => getValueToInt32("NpcCreateCurrency", 3)); - - public static Int32 NpcCreateCost => m_npc_create_cost.Value; - private static Lazy m_npc_create_cost = new Lazy(() => getValueToInt32("NpcCreateCost", 1000)); - - public static Int32 NpcEditCurrency => m_npc_edit_currency.Value; - private static Lazy m_npc_edit_currency = new Lazy(() => getValueToInt32("NpcEditCurrency", 3)); - - public static Int32 NpcEditCost => m_npc_edit_cost.Value; - private static Lazy m_npc_edit_cost = new Lazy(() => getValueToInt32("NpcEditCost", 1000)); - - public static Int32 MaxNpcIntroductionChar => m_max_npc_introduction_char.Value; - private static Lazy m_max_npc_introduction_char = new Lazy(() => getValueToInt32("MaxNpcIntroductionChar", 100)); - - public static Int32 MaxNpcGreetingChar => m_max_npc_greeting_char.Value; - private static Lazy m_max_npc_greeting_char = new Lazy(() => getValueToInt32("MaxNpcGreetingChar", 20)); - - public static Int32 MaxNpcTag => m_max_npc_tag.Value; - private static Lazy m_max_npc_tag = new Lazy(() => getValueToInt32("MaxNpcTag", 7)); - - public static Int32 MaxNpcBackGroundChar => m_max_npc_background_char.Value; - private static Lazy m_max_npc_background_char = new Lazy(() => getValueToInt32("MaxNpcBackGroundChar", 200)); - - public static Int32 MaxNpcDescriptionChar => m_max_npc_description_char.Value; - private static Lazy m_max_npc_description_char = new Lazy(() => getValueToInt32("MaxNpcDescriptionChar", 400)); - - public static Int32 ConcertEntranceLimitTime => m_concert_entrance_limit_time.Value; - private static Lazy m_concert_entrance_limit_time = new Lazy(() => getValueToInt32("ConcertEntranceLimitTime", 10)); - - public static Int32 ChatStringInputMax => m_chat_string_input_max.Value; - private static Lazy m_chat_string_input_max = new Lazy(() => getValueToInt32("ChatStringInputMax", 1200)); - - public static Int32 MaxSocialActionSlot => m_max_social_action_slot.Value; - private static Lazy m_max_social_action_slot = new Lazy(() => getValueToInt32("MaxSocialActionSlot", 46)); - - public static Int32 MinReportTitleNum => m_min_report_title_num.Value; - private static Lazy m_min_report_title_num = new Lazy(() => getValueToInt32("MinReportTitleNum", 5)); - - public static Int32 MaxReportTitleNum => m_max_report_title_num.Value; - private static Lazy m_max_report_title_num = new Lazy(() => getValueToInt32("MaxReportTitleNum", 30)); - - public static Int32 MinReportContentNum => m_min_report_content_num.Value; - private static Lazy m_min_report_content_num = new Lazy(() => getValueToInt32("MinReportContentNum", 10)); - - public static Int32 MaxReportContentNum => m_max_report_content_num.Value; - private static Lazy m_max_report_content_num = new Lazy(() => getValueToInt32("MaxReportContentNum", 2000)); - - public static Int64 SystemMailStoragePeriod => m_system_mail_storage_period.Value; - private static Lazy m_system_mail_storage_period = new Lazy(() => getValueToInt64("SystemMailStoragePeriod", 144000)); - - public static Int64 UserMailStoragePeriod => m_user_mail_storage_period.Value; - private static Lazy m_user_mail_storage_period = new Lazy(() => getValueToInt64("UserMailStoragePeriod", 43200)); - - public static Int64 SendMailStoragePeriod => m_send_mail_storage_period.Value; - private static Lazy m_send_mail_storage_period = new Lazy(() => getValueToInt64("SendMailStoragePeriod", 43200)); - - public static Int64 QuestMailStoragePeriod => m_quest_mail_storage_period.Value; - private static Lazy m_quest_mail_storage_period = new Lazy(() => getValueToInt64("QuestMailStoragePeriod", 0)); - - public static Int32 ChannelChangeCoolTime => m_channel_change_cool_time.Value; - private static Lazy m_channel_change_cool_time = new Lazy(() => getValueToInt32("ChannelChangeCoolTime", 180)); - - public static Int32 UgqUsageFeeAmateur => m_ugq_usage_fee_amateur.Value; - private static Lazy m_ugq_usage_fee_amateur = new Lazy(() => getValueToInt32("UgqUsageFeeAmateur", 0)); - public static Int32 UgqUsageFeeRisingStar => m_ugq_usage_fee_risingstar.Value; - private static Lazy m_ugq_usage_fee_risingstar = new Lazy(() => getValueToInt32("UgqUsageFeeRisingStar", 5)); - public static Int32 UgqUsageFeeMaster1 => m_ugq_usage_fee_master1.Value; - private static Lazy m_ugq_usage_fee_master1 = new Lazy(() => getValueToInt32("UgqUsageFeeMaster1", 10)); - public static Int32 UgqUsageFeeMaster2 => m_ugq_usage_fee_master2.Value; - private static Lazy m_ugq_usage_fee_master2 = new Lazy(() => getValueToInt32("UgqUsageFeeMaster2", 15)); - public static Int32 UgqUsageFeeMaster3 => m_ugq_usage_fee_master3.Value; - private static Lazy m_ugq_usage_fee_master3 = new Lazy(() => getValueToInt32("UgqUsageFeeMaster3", 20)); - - public static string UgqUsageFeeTypeAmateur => m_ugq_usage_fee_type_amateur.Value; - private static Lazy m_ugq_usage_fee_type_amateur = new Lazy(() => getValueToString("UgqUsageFeeTypeAmateur", "Sapphire")); - public static string UgqUsageFeeTypeRisingStar => m_ugq_usage_fee_type_rising_star.Value; - private static Lazy m_ugq_usage_fee_type_rising_star = new Lazy(() => getValueToString("UgqUsageFeeTypeRisingStar", "Ruby")); - public static string UgqUsageFeeTypeMaster1 => m_ugq_usage_fee_type_master1.Value; - private static Lazy m_ugq_usage_fee_type_master1 = new Lazy(() => getValueToString("UgqUsageFeeTypeMaster1", "Sapphire")); - public static string UgqUsageFeeTypeMaster2 => m_ugq_usage_fee_type_master2.Value; - private static Lazy m_ugq_usage_fee_type_master2 = new Lazy(() => getValueToString("UgqUsageFeeTypeMaster2", "Sapphire")); - public static string UgqUsageFeeTypeMaster3 => m_ugq_usage_fee_type_master3.Value; - private static Lazy m_ugq_usage_fee_type_master3 = new Lazy(() => getValueToString("UgqUsageFeeTypeMaster3", "Sapphire")); - - public static float UgqRevenueDistributionCreator => m_ugq_revenue_distribution_creator.Value; - private static Lazy m_ugq_revenue_distribution_creator = new Lazy(() => getValueToFloat("UgqRevenueDistributionCreator", 0.5f)); - public static float UgqRevenueDistributionBeaconOwner => m_ugq_revenue_distribution_beacon_owner.Value; - private static Lazy m_ugq_revenue_distribution_beacon_owner = new Lazy(() => getValueToFloat("UgqRevenueDistributionBeaconOwner", 1)); - - - public static Int32 WorldBeaconAmateurUGQReward => m_world_beacon_amateur_ugq_reward.Value; - private static Lazy m_world_beacon_amateur_ugq_reward = new Lazy(() => getValueToInt32("WorldBeaconAmateurUGQReward", 0)); - public static Int32 WorldBeaconRisingstarUGQReward => m_world_beacon_risingstar_ugq_reward.Value; - private static Lazy m_world_beacon_risingstar_ugq_reward = new Lazy(() => getValueToInt32("WorldBeaconRisingstarUGQReward", 0)); - public static Int32 WorldBeaconMasterOneUGQReward => m_world_beacon_master_one_ugq_reward.Value; - private static Lazy m_world_beacon_master_one_ugq_reward = new Lazy(() => getValueToInt32("WorldBeaconMasterOneUGQReward", 0)); - public static Int32 WorldBeaconMasterTwoUGQReward => m_world_beacon_master_two_ugq_reward.Value; - private static Lazy m_world_beacon_master_two_ugq_reward = new Lazy(() => getValueToInt32("WorldBeaconMasterTwoUGQReward", 0)); - public static Int32 WorldBeaconMasterThreeUGQReward => m_world_beacon_master_three_ugq_reward.Value; - private static Lazy m_world_beacon_master_three_ugq_reward = new Lazy(() => getValueToInt32("WorldBeaconMasterThreeUGQReward", 0)); - public static Int32 NormalBeaconAmateurUGQReward => m_normal_beacon_amateur_ugq_Reward.Value; - private static Lazy m_normal_beacon_amateur_ugq_Reward = new Lazy(() => getValueToInt32("NormalBeaconAmateurUGQReward", 0)); - public static Int32 NormalBeaconRisingstarUGQReward => m_normal_beacon_risingstar_ugq_Reward.Value; - private static Lazy m_normal_beacon_risingstar_ugq_Reward = new Lazy(() => getValueToInt32("NormalBeaconRisingstarUGQReward", 0)); - public static Int32 NormalBeaconMasterOneUGQReward => m_normal_beacon_master_one_ugq_reward.Value; - private static Lazy m_normal_beacon_master_one_ugq_reward = new Lazy(() => getValueToInt32("NormalBeaconMasterOneUGQReward", 0)); - public static Int32 NormalBeaconMasterTwoUGQReward => m_normal_beacon_master_two_ugq_reward.Value; - private static Lazy m_normal_beacon_master_two_ugq_reward = new Lazy(() => getValueToInt32("NormalBeaconMasterTwoUGQReward", 0)); - public static Int32 NormalBeaconMasterThreeUGQReward => m_normal_beacon_master_three_ugq_reward.Value; - private static Lazy m_normal_beacon_master_three_ugq_reward = new Lazy(() => getValueToInt32("NormalBeaconMasterThreeUGQReward", 0)); - public static Int32 AiChatInsentiveMax => m_ai_chat_insentive_max.Value; - private static Lazy m_ai_chat_insentive_max = new Lazy(() => getValueToInt32("Beacon_Insentive_Max", 1000)); // Beam 재화 - public static Int32 AiChatInsentivePeriod => m_ai_chat_insentive_period.Value; - private static Lazy m_ai_chat_insentive_period = new Lazy(() => getValueToInt32("Beacon_Insentive_Period", 10080)); // 분 단위 - - public static Int32 MyHomeMaxSaveSlot => m_myhome_max_save_slot.Value; - private static Lazy m_myhome_max_save_slot = new Lazy(() => getValueToInt32("MyHomeMaxSaveSlot", 5)); - - public static Int32 MyHomeDefaultSaveSlot => m_myhome_default_save_slot.Value; - private static Lazy m_myhome_default_save_slot = new Lazy(() => getValueToInt32("MyHomeDefaultSaveSlot", 1)); - - public static string MyHomeDefaultSaveFileName => m_myhome_default_save_file_name.Value; - private static Lazy m_myhome_default_save_file_name = new Lazy(() => getValueToString("MyHomeDefaultSaveFileName", "temp.myhome")); - - public static Int32 MyHomeSaveSlotExpandCurrency => m_myhome_save_slot_expand_currency.Value; - private static Lazy m_myhome_save_slot_expand_currency = new Lazy(() => getValueToInt32("MyHomeSaveSlotExpandCurrency", 2)); - - public static Int32 MyHomeSaveSlotExpandCost => m_myhome_save_slot_expand_cost.Value; - private static Lazy m_myhome_save_slot_expand_cost = new Lazy(() => getValueToInt32("MyHomeSaveSlotExpandCost", 100)); - - public static Int32 MyhomenameMinLength => m_myhome_min_length.Value; - private static Lazy m_myhome_min_length = new Lazy(() => getValueToInt32("MyhomenameMinLength", 2)); - - public static Int32 MyhomenameMaxLength => m_myhome_max_length.Value; - private static Lazy m_myhome_max_length = new Lazy(() => getValueToInt32("MyhomenameMaxLength", 20)); - - public static Int32 MyhomeInterphone => m_myhome_interphone.Value; - private static Lazy m_myhome_interphone = new Lazy(() => getValueToInt32("MyHomeInterphone", 1)); - - public static Int32 MyhomeStartPoint => m_myhome_start_point.Value; - private static Lazy m_myhome_start_point = new Lazy(() => getValueToInt32("MyHomeStartPoint", 1)); - - public static Int32 SmallTypeMyhome => m_small_type_myhome.Value; - private static Lazy m_small_type_myhome = new Lazy(() => getValueToInt32("SmallTypeMyhome", 10001)); - - public static Int32 MediumTypeMyhome => m_medium_type_myhome.Value; - private static Lazy m_medium_type_myhome = new Lazy(() => getValueToInt32("MediumTypeMyhome", 10003)); - - public static Int32 LargeTypeMyhome => m_large_type_myhome.Value; - private static Lazy m_large_type_myhome = new Lazy(() => getValueToInt32("LargeTypeMyhome", 10004)); - - public static Int32 GiantTypeMyhome => m_giant_type_myhome.Value; - private static Lazy m_giant_type_myhome = new Lazy(() => getValueToInt32("GiantTypeMyhome", 10005)); - - public static Int32 MinEnterPlayer => m_min_enter_player.Value; - private static Lazy m_min_enter_player = new Lazy(() => getValueToInt32("MinEnterPlayer", 1)); - - public static Int32 MaxEnterPlayer => m_max_enter_player.Value; - private static Lazy m_max_enter_player = new Lazy(() => getValueToInt32("MaxEnterPlayer", 1)); - - public static Int32 MinRentalPeriod => m_min_rental_period.Value; - private static Lazy m_min_rental_period = new Lazy(() => getValueToInt32("MinRentalPeriod", 1)); - - public static Int32 MaxRentalPeriod => m_max_rental_period.Value; - private static Lazy m_max_rental_period = new Lazy(() => getValueToInt32("MaxRentalPeriod", 1)); - - public static bool UseRentalStateSwitch => m_use_rental_state_switch.Value; - private static Lazy m_use_rental_state_switch = new Lazy(() => getValueToBool("UseRentalStateSwitch", true)); - - public static DateTime NpcRankingCalculateTime => m_npc_ranking_calculate_time.Value; - private static Lazy m_npc_ranking_calculate_time = new Lazy(() => - { - var time_string = getValueToString("NpcRankingCalculateTime", "T03:00:00"); - time_string = time_string.Replace("T", ""); - var datetime = Convert.ToDateTime(time_string); - return datetime; - }); - - public static DateTime BusinessLogRefreshTime => m_businesslog_refresh_time.Value; - private static Lazy m_businesslog_refresh_time = new Lazy(() => - { - var time_string = getValueToString("BusinessLogRefreshTime", "T00:01:00"); - time_string = time_string.Replace("T", ""); - var datetime = Convert.ToDateTime(time_string); - return datetime; - }); - - public static int NpcRankingCalculatePeriod => m_npc_ranking_calculate_period.Value; - private static Lazy m_npc_ranking_calculate_period = new Lazy(() => getValueToInt32("NpcRankingCalculatePeriod", 24)); - - public static int NpcRankingRetentionPeriod => m_npc_ranking_retention_period.Value; - private static Lazy m_npc_ranking_retention_period = new Lazy(() => getValueToInt32("NpcRankingRetentionPeriod", 2160)); - - - - public static int UGQDefaultSlot => m_ugq_default_slot.Value; - private static Lazy m_ugq_default_slot = new Lazy(() => getValueToInt32("UGQDefaultSlot", 2)); - - public static int UGQMaximumSlot => m_ugq_maximum_slot.Value; - private static Lazy m_ugq_maximum_slot = new Lazy(() => getValueToInt32("UGQMaximumSlot", 5)); - - public static string UGQAddSlotType => m_ugq_add_slot_type.Value; - private static Lazy m_ugq_add_slot_type = new Lazy(() => getValueToString("UGQAddSlotType", "SAPPHIRE")); - - public static double UGQAddSlotAmount => m_ugq_add_slot_amount.Value; - private static Lazy m_ugq_add_slot_amount = new Lazy(() => getValueToDouble("UGQAddSlotAmount", 50)); - - public static decimal CaliumConverterInitialValue => m_calium_converter_initial_value.Value; - private static Lazy m_calium_converter_initial_value = new Lazy(() => getValueToDecimal("CaliumConverterInitialValue", 875.5)); - - public static decimal CaliumConverterEfficiency => m_calium_converter_efficiency.Value; - private static Lazy m_calium_converter_efficiency = new Lazy(() => getValueToDecimal("CaliumConverterEfficiency", 1)); - - public static int CaliumConverterPivotTime => m_calium_converter_pivot_time.Value; - private static Lazy m_calium_converter_pivot_time = new Lazy(() => getValueToInt32("CaliumConverterPivotTime", 9)); - - public static int CaliumConverterCycleTime => m_calium_converter_cycle_time.Value; - private static Lazy m_calium_converter_cycle_time = new Lazy(() => getValueToInt32("CaliumConverterCycleTime", 8)); - - public static decimal CaliumConverterDailyLimit => m_calium_converter_daily_limit.Value; - private static Lazy m_calium_converter_daily_limit = new Lazy(() => getValueToDecimal("CaliumConverterDailyLimit", 875.05)); - - public static decimal CaliumConverterLimitPerPerson => m_calium_converter_limit_per_person.Value; - private static Lazy m_calium_converter_limit_per_person = new Lazy(() => getValueToDecimal("CaliumConverterLimitPerPerson", 1)); - - public static CurrencyType CaliumConverterCommissionType => m_calium_converter_commission_type.Value; - private static Lazy m_calium_converter_commission_type = new Lazy(() => getValueToEnum("CaliumConverterCommissionType", CurrencyType.Sapphire)); - - public static decimal CaliumConverterCommissionRate => m_calium_converter_commission_rate.Value; - private static Lazy m_calium_converter_commission_rate = new Lazy(() => getValueToDecimal("CaliumConverterCommissionRate", 0.1)); - - public static decimal CaliumConverterConversionRate => m_calium_converter_conversion_rate.Value; - private static Lazy m_calium_converter_conversion_rate = new Lazy(() => getValueToDecimal("CaliumConverterConversionRate", 0.01)); - - public static int UGQBoardMaxListAmount => m_ugq_board_max_list_amount.Value; - private static Lazy m_ugq_board_max_list_amount = new Lazy(() => getValueToInt32("UGQBoardMaxListAmount", 10)); - - - public static int UGQPromoteConditionRisingstar => m_ugq_dpromote_condition_rising_star.Value; - private static Lazy m_ugq_dpromote_condition_rising_star = new Lazy(() => getValueToInt32("UGQPromoteConditionRisingstar", 20)); - - public static Pos UGQNormalBeaconQuestMarkerPos => m_ugq_normal_beacon_quest_marker_pos.Value; - private static Lazy m_ugq_normal_beacon_quest_marker_pos = new Lazy(() => - { - var pos = new Pos() { X = -20310, Y = 20760, Z = 20 }; - var pos_string = getValueToString("UGQNormalBeaconQuestMarkerPos", "-20310 20760 20"); - var posList = pos_string.Split(' '); - if(posList.Length == 3) - { - if(posList[0].toInt32(out var x)) - pos.X = x; + public static Int32 NpcCreateTattooRequirement => m_npc_create_tattoo_requirement.Value; + private static Lazy m_npc_create_tattoo_requirement = new Lazy(() => getValueToInt32("NpcCreateTattooRequirement", 3)); - if (posList[1].toInt32(out var y)) - pos.Y = y; + public static Int32 NpcCreateCurrency => m_npc_create_currency.Value; + private static Lazy m_npc_create_currency = new Lazy(() => getValueToInt32("NpcCreateCurrency", 3)); - if (posList[2].toInt32(out var z)) - pos.Z = z; - } - return pos; - }); + public static Int32 NpcCreateCost => m_npc_create_cost.Value; + private static Lazy m_npc_create_cost = new Lazy(() => getValueToInt32("NpcCreateCost", 1000)); - public static int SentMailNumsResetTime => m_sent_mail_nums_reset_time.Value; - private static Lazy m_sent_mail_nums_reset_time = new Lazy(() => getValueToInt32("SentMailNumsResetTime", 15)); + public static Int32 NpcEditCurrency => m_npc_edit_currency.Value; + private static Lazy m_npc_edit_currency = new Lazy(() => getValueToInt32("NpcEditCurrency", 3)); - public static Int32 UgqBonusGoldAmateur => m_ugq_bonus_gold_amateur.Value; - private static Lazy m_ugq_bonus_gold_amateur = new Lazy(() => getValueToInt32("UgqBonusGoldAmateur", 500)); - - public static Int32 UgqBonusGoldRisingStar => m_ugq_bonus_gold_rising_star.Value; - private static Lazy m_ugq_bonus_gold_rising_star = new Lazy(() => getValueToInt32("UgqBonusGoldRisingStar", 250)); - - public static Int32 UgqBonusGoldMaster1 => m_ugq_bonus_gold_master1.Value; - private static Lazy m_ugq_bonus_gold_master1 = new Lazy(() => getValueToInt32("UgqBonusGoldMaster1", 200)); - - public static Int32 UgqBonusGoldMaster2 => m_ugq_bonus_gold_master2.Value; - private static Lazy m_ugq_bonus_gold_master2 = new Lazy(() => getValueToInt32("UgqBonusGoldMaster2", 150)); - - public static Int32 UgqBonusGoldMaster3 => m_ugq_bonus_gold_master3.Value; - private static Lazy m_ugq_bonus_gold_master3 = new Lazy(() => getValueToInt32("UgqBonusGoldMaster3", 150)); - - public static Int32 UgqAmateurBonusCount => m_ugq_amateur_bonus_count.Value; - private static Lazy m_ugq_amateur_bonus_count = new Lazy(() => getValueToInt32("UgqAmateurBonusCount", 10)); - - public static Int32 UgqRisingStarBonusCount => m_ugq_rising_star_bonus_count.Value; - private static Lazy m_ugq_rising_star_bonus_count = new Lazy(() => getValueToInt32("UgqRisingStarBonusCount", 5)); - - public static Int32 UgqMaster1BonusCount => m_ugq_master1_bonus_count.Value; - private static Lazy m_ugq_master1_bonus_count = new Lazy(() => getValueToInt32("UgqMaster1BonusCount", 3)); - - public static Int32 UgqMaster2BonusCount => m_ugq_master2_bonus_count.Value; - private static Lazy m_ugq_master2_bonus_count = new Lazy(() => getValueToInt32("UgqMaster2BonusCount", 2)); - - public static Int32 UgqMaster3BonusCount => m_ugq_master3_bonus_count.Value; - private static Lazy m_ugq_master3_bonus_count = new Lazy(() => getValueToInt32("UgqMaster3BonusCount", 1)); - - public static string UgqBonusCountInit => m_ugq_bonus_count_init.Value; - private static Lazy m_ugq_bonus_count_init = new Lazy(() => getValueToString("UgqBonusCountInit", "T00:00:00")); + public static Int32 NpcEditCost => m_npc_edit_cost.Value; + private static Lazy m_npc_edit_cost = new Lazy(() => getValueToInt32("NpcEditCost", 1000)); - public static CurrencyType CharacterCustomizeCostType => m_character_customize_cost_type.Value; - private static Lazy m_character_customize_cost_type = new Lazy(() => getValueToEnum("CharacterCustomizeCostType", CurrencyType.None)); + public static Int32 MaxNpcIntroductionChar => m_max_npc_introduction_char.Value; + private static Lazy m_max_npc_introduction_char = new Lazy(() => getValueToInt32("MaxNpcIntroductionChar", 100)); - public static double CharacterCustomizeCost => m_character_customize_cost.Value; - private static Lazy m_character_customize_cost = new Lazy(() => getValueToDouble("CharacterCustomizeCostValue", 0)); - - public static CurrencyType NpcCustomizeCostType => m_npc_customize_cost_type.Value; - private static Lazy m_npc_customize_cost_type = new Lazy(() => getValueToEnum("NpcCustomizeCurrency", CurrencyType.None)); - - public static double NpcCustomizeCost => m_npc_customize_cost.Value; - private static Lazy m_npc_customize_cost = new Lazy(() => getValueToDouble("NpcCustomizeCost", 0)); - - public static Int32 BeaconAppProfileCooltime => m_beacon_app_profile_cooltime.Value; - private static Lazy m_beacon_app_profile_cooltime = new Lazy(() => getValueToInt32("BeaconAppProfileCooltime", 3600)); - - // 인벤토리 슬롯 최대 갯수 관련 임시 추가 메타 정보 - public static Int16 Inventory1Slot => m_inventory_1_slot.Value; - private static Lazy m_inventory_1_slot = new Lazy(() => getValueToInt16("Inventory1Slot", 200)); - - public static Int16 Inventory2Slot => m_inventory_2_slot.Value; - private static Lazy m_inventory_2_slot = new Lazy(() => getValueToInt16("Inventory2Slot", 180)); - - public static Int16 Inventory3Slot => m_inventory_3_slot.Value; - private static Lazy m_inventory_3_slot = new Lazy(() => getValueToInt16("Inventory3Slot", 50)); - - public static Int16 Inventory4Slot => m_inventory_4_slot.Value; - private static Lazy m_inventory_4_slot = new Lazy(() => getValueToInt16("Inventory4Slot", 50)); - - public static Int16 Inventory5Slot => m_inventory_5_slot.Value; - private static Lazy m_inventory_5_slot = new Lazy(() => getValueToInt16("Inventory5Slot", 180)); - - public static Int16 Inventory6Slot => m_inventory_6_slot.Value; - private static Lazy m_inventory_6_slot = new Lazy(() => getValueToInt16("Inventory6Slot", 30)); - - public static Int16 Inventory7Slot => m_inventory_7_slot.Value; - private static Lazy m_inventory_7_slot = new Lazy(() => getValueToInt16("Inventory7Slot", 30)); - - public static Int16 Inventory8Slot => m_inventory_8_slot.Value; - private static Lazy m_inventory_8_slot = new Lazy(() => getValueToInt16("Inventory8Slot", 10)); - - public static Int16 Inventory9Slot => m_inventory_9_slot.Value; - private static Lazy m_inventory_9_slot = new Lazy(() => getValueToInt16("Inventory9Slot", 10)); - - public static Int16 Inventory10Slot => m_inventory_10_slot.Value; - private static Lazy m_inventory_10_slot = new Lazy(() => getValueToInt16("Inventory10Slot", 60)); - - - // MyHome 인벤토리 슬롯 최대 갯수 관련 임시 추가 메타 정보EGenderType - public static Int16 MyHomeInventory1Slot => m_my_home_inventory_1_slot.Value; - private static Lazy m_my_home_inventory_1_slot = new Lazy(() => getValueToInt16("MyHomeInventory1Slot", 200)); - - public static Int16 MyHomeInventory2Slot => m_my_home_inventory_2_slot.Value; - private static Lazy m_my_home_inventory_2_slot = new Lazy(() => getValueToInt16("MyHomeInventory2Slot", 0)); - - public static Int16 MyHomeInventory3Slot => m_my_home_inventory_3_slot.Value; - private static Lazy m_my_home_inventory_3_slot = new Lazy(() => getValueToInt16("MyHomeInventory3Slot", 0)); - - public static Int16 MyHomeInventory4Slot => m_my_home_inventory_4_slot.Value; - private static Lazy m_my_home_inventory_4_slot = new Lazy(() => getValueToInt16("MyHomeInventory4Slot", 0)); - - public static Int16 MyHomeInventory5Slot => m_my_home_inventory_5_slot.Value; - private static Lazy m_my_home_inventory_5_slot = new Lazy(() => getValueToInt16("MyHomeInventory5Slot", 0)); - - public static Int16 MyHomeInventory6Slot => m_my_home_inventory_6_slot.Value; - private static Lazy m_my_home_inventory_6_slot = new Lazy(() => getValueToInt16("MyHomeInventory6Slot", 30)); - - public static Int16 MyHomeInventory7Slot => m_my_home_inventory_7_slot.Value; - private static Lazy m_my_home_inventory_7_slot = new Lazy(() => getValueToInt16("MyHomeInventory7Slot", 0)); - - public static Int16 MyHomeInventory8Slot => m_my_home_inventory_8_slot.Value; - private static Lazy m_my_home_inventory_8_slot = new Lazy(() => getValueToInt16("MyHomeInventory8Slot", 0)); - - public static Int16 MyHomeInventory9Slot => m_my_home_inventory_9_slot.Value; - private static Lazy m_my_home_inventory_9_slot = new Lazy(() => getValueToInt16("MyHomeInventory9Slot", 0)); - - public static Int16 MyHomeInventory10Slot => m_my_home_inventory_10_slot.Value; - private static Lazy m_my_home_inventory_10_slot = new Lazy(() => getValueToInt16("MyHomeInventory10Slot", 0)); - - // NFT 관련 - public static Int32 CPNFTHolderKeyItem => m_cp_nft_holder_key_item.Value; - private static Lazy m_cp_nft_holder_key_item = new Lazy(() => getValueToInt32("CPNFTHolderKeyItem", 0)); - - public static Int32 ShopRenewalBlockTime => m_shop_renewal_block_time.Value; - private static Lazy m_shop_renewal_block_time = new Lazy(() => getValueToInt32("ShopRenewalBlockTime", 5)); //sec - - // Large Packet 관련 - public static Int32 LargePacketCheckSize => m_large_packet_check_size.Value; - private static Lazy m_large_packet_check_size = new Lazy(() => getValueToInt32("LargePacketxCheckSize", 256000)); - public static Int32 PacketChunkSize => m_packet_chunk_size.Value; - private static Lazy m_packet_chunk_size = new Lazy(() => getValueToInt32("PacketChunkSize", 60000)); - - public static Int32 LandAuctionReloadIntervalSec => m_land_auction_reload_interval_sec.Value; - private static Lazy m_land_auction_reload_interval_sec = new Lazy(() => getValueToInt32("LandAuctionReloadIntervalSec", 5)); - - public static Int32 BlindBidStartBeforeLandAuctionEndMinutes => m_blind_bid_start_before_land_auction_end_minuts.Value; - private static Lazy m_blind_bid_start_before_land_auction_end_minuts = new Lazy(() => getValueToInt32("LandAuctionBlindTimeSet", 10)); - - public static Int32 LandAuctionFailedMailPeriodMinutes => m_land_auction_failed_mail_period_minutes.Value; - private static Lazy m_land_auction_failed_mail_period_minutes = new Lazy(() => getValueToInt32("AuctionLandMailValidTime", 2592000)); - - public static Int32 LandAuctionSuccessedMailPeriodMinutes => m_land_auction_successed_mail_period_minutes.Value; - private static Lazy m_land_auction_successed_mail_period_minutes = new Lazy(() => getValueToInt32("AuctionLandReturnMailValidTime", 2592000)); - - public static double BeaconShopMinCaliumPrice => m_beacon_shop_min_calium_price.Value; - private static Lazy m_beacon_shop_min_calium_price = new Lazy(() => getValueToDouble("BeaconShopMinCaliumPrice", 0.01)); - - public static double BeaconShopMaxCaliumPrice => m_beacon_shop_max_calium_price.Value; - private static Lazy m_beacon_shop_max_calium_price = new Lazy(() => getValueToDouble("BeaconShopMaxCaliumPrice", 99999999.00)); - - public static double BeaconShopSellFee => m_beacon_shop_sell_fee.Value; - private static Lazy m_beacon_shop_sell_fee = new Lazy(() => getValueToDouble("BeaconShopSellFee", 0.3)); - - public static int BeaconShopDailyRegistration => m_beacon_shop_daily_registration.Value; - private static Lazy m_beacon_shop_daily_registration = new Lazy(() => getValueToInt32("BeaconShopDailyRegistration", 10)); - - public static int BeaconShopSellPeriod => m_beacon_shop_sell_period.Value; - private static Lazy m_beacon_shop_sell_period = new Lazy(() => getValueToInt32("BeaconShopSellPeriod", 72)); - - public static int BeaconShopMaxSlot => m_beacon_shop_max_slot.Value; - private static Lazy m_beacon_shop_max_slot = new Lazy(() => getValueToInt32("BeaconShopMaxSlot", 20)); - public static int BeaconShopDailyRegistrationResetTime => m_beacon_shop_daily_registration_reset_time.Value; - private static Lazy m_beacon_shop_daily_registration_reset_time = new Lazy(() => getValueToInt32("BeaconShopDailyRegistrationResetTime", 0)); - - public static int ItemExpireAlertBeforeHourTime => m_item_expire_alert_before_hour_time.Value; - private static Lazy m_item_expire_alert_before_hour_time = new Lazy(() => getValueToInt32("ItemExpireAlertBeforeHourTime", ConstValue.default_1_day_to_hour * 7)); - - public static double BeaconShopPaymentListMaxCount => m_beacon_shop_payment_max_count.Value; - private static Lazy m_beacon_shop_payment_max_count = new Lazy(() => getValueToDouble("BeaconShopPaymentListMaxCount", 50)); - } - - //========================================================================================= - // GameConfigMeta 데이터내의 문자열 데이터를 해당하는 타입으로 변환 한다. - //========================================================================================= - public static bool getValueToBool(string key, bool defaultValue) - { - MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); - if (found_string == null) - return defaultValue; - - var is_success = found_string.toBool(out var converted_value); - return is_success == true ? converted_value : defaultValue; - } - - public static Int16 getValueToInt16(string key, Int16 defaultValue) - { - MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); - if (found_string == null) - return defaultValue; - - var is_success = found_string.toInt16(out var converted_value); - return is_success == true ? converted_value : defaultValue; - } - public static Int32 getValueToInt32(string key, Int32 defaultValue) - { - MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); - if (found_string == null) - return defaultValue; - - var is_success = found_string.toInt32(out var converted_value); - return is_success == true ? converted_value : defaultValue; - } - - public static Int64 getValueToInt64(string key, Int64 defaultValue) - { - MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); - if (found_string == null) - return defaultValue; - - var is_success = found_string.toInt64(out var converted_value); - return is_success == true ? converted_value : defaultValue; - } - - public static float getValueToFloat(string key, float defaultValue) - { - MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); - if (found_string == null) - return defaultValue; - - var is_success = found_string.toFloat(out var converted_value); - return is_success == true ? converted_value : defaultValue; - } - - public static double getValueToDouble(string key, double defaultValue) - { - MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); - if (found_string == null) - return defaultValue; - - var is_success = found_string.toDouble(out var converted_value); - return is_success == true ? converted_value : defaultValue; - } - - public static decimal getValueToDecimal(string key, double defaultValue) - { - MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); - if (found_string == null) - return (decimal)defaultValue; - - var is_success = found_string.toDecimal(out var converted_value); - return is_success == true ? converted_value : (decimal)defaultValue; - } - - public static string getValueToString(string key, string defaultValue) - { - MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); - return found_string?.isNullOrWhiteSpace() == false ? found_string : defaultValue; - } - - public static TEnum getValueToEnum(string key, TEnum defaultValue) - { - MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); - if (found_string == null) - return defaultValue; - - var is_success = EnumHelper.tryParse(found_string, out var converted_value); - if (!is_success) - return defaultValue; - - return converted_value == null ? defaultValue : converted_value; - } + public static Int32 MaxNpcGreetingChar => m_max_npc_greeting_char.Value; + private static Lazy m_max_npc_greeting_char = new Lazy(() => getValueToInt32("MaxNpcGreetingChar", 20)); + + public static Int32 MaxNpcTag => m_max_npc_tag.Value; + private static Lazy m_max_npc_tag = new Lazy(() => getValueToInt32("MaxNpcTag", 7)); + + public static Int32 MaxNpcBackGroundChar => m_max_npc_background_char.Value; + private static Lazy m_max_npc_background_char = new Lazy(() => getValueToInt32("MaxNpcBackGroundChar", 200)); + + public static Int32 MaxNpcDescriptionChar => m_max_npc_description_char.Value; + private static Lazy m_max_npc_description_char = new Lazy(() => getValueToInt32("MaxNpcDescriptionChar", 400)); + + public static Int32 ConcertEntranceLimitTime => m_concert_entrance_limit_time.Value; + private static Lazy m_concert_entrance_limit_time = new Lazy(() => getValueToInt32("ConcertEntranceLimitTime", 10)); + + public static Int32 ChatStringInputMax => m_chat_string_input_max.Value; + private static Lazy m_chat_string_input_max = new Lazy(() => getValueToInt32("ChatStringInputMax", 1200)); + + public static Int32 MaxSocialActionSlot => m_max_social_action_slot.Value; + private static Lazy m_max_social_action_slot = new Lazy(() => getValueToInt32("MaxSocialActionSlot", 46)); + + public static Int32 MinReportTitleNum => m_min_report_title_num.Value; + private static Lazy m_min_report_title_num = new Lazy(() => getValueToInt32("MinReportTitleNum", 5)); + + public static Int32 MaxReportTitleNum => m_max_report_title_num.Value; + private static Lazy m_max_report_title_num = new Lazy(() => getValueToInt32("MaxReportTitleNum", 30)); + + public static Int32 MinReportContentNum => m_min_report_content_num.Value; + private static Lazy m_min_report_content_num = new Lazy(() => getValueToInt32("MinReportContentNum", 10)); + + public static Int32 MaxReportContentNum => m_max_report_content_num.Value; + private static Lazy m_max_report_content_num = new Lazy(() => getValueToInt32("MaxReportContentNum", 2000)); + + public static Int64 SystemMailStoragePeriod => m_system_mail_storage_period.Value; + private static Lazy m_system_mail_storage_period = new Lazy(() => getValueToInt64("SystemMailStoragePeriod", 144000)); + + public static Int64 UserMailStoragePeriod => m_user_mail_storage_period.Value; + private static Lazy m_user_mail_storage_period = new Lazy(() => getValueToInt64("UserMailStoragePeriod", 43200)); + + public static Int64 SendMailStoragePeriod => m_send_mail_storage_period.Value; + private static Lazy m_send_mail_storage_period = new Lazy(() => getValueToInt64("SendMailStoragePeriod", 43200)); + + public static Int64 QuestMailStoragePeriod => m_quest_mail_storage_period.Value; + private static Lazy m_quest_mail_storage_period = new Lazy(() => getValueToInt64("QuestMailStoragePeriod", 0)); + + public static Int32 ChannelChangeCoolTime => m_channel_change_cool_time.Value; + private static Lazy m_channel_change_cool_time = new Lazy(() => getValueToInt32("ChannelChangeCoolTime", 180)); + + public static Int32 UgqUsageFeeAmateur => m_ugq_usage_fee_amateur.Value; + private static Lazy m_ugq_usage_fee_amateur = new Lazy(() => getValueToInt32("UgqUsageFeeAmateur", 0)); + public static Int32 UgqUsageFeeRisingStar => m_ugq_usage_fee_risingstar.Value; + private static Lazy m_ugq_usage_fee_risingstar = new Lazy(() => getValueToInt32("UgqUsageFeeRisingStar", 5)); + public static Int32 UgqUsageFeeMaster1 => m_ugq_usage_fee_master1.Value; + private static Lazy m_ugq_usage_fee_master1 = new Lazy(() => getValueToInt32("UgqUsageFeeMaster1", 10)); + public static Int32 UgqUsageFeeMaster2 => m_ugq_usage_fee_master2.Value; + private static Lazy m_ugq_usage_fee_master2 = new Lazy(() => getValueToInt32("UgqUsageFeeMaster2", 15)); + public static Int32 UgqUsageFeeMaster3 => m_ugq_usage_fee_master3.Value; + private static Lazy m_ugq_usage_fee_master3 = new Lazy(() => getValueToInt32("UgqUsageFeeMaster3", 20)); + + public static string UgqUsageFeeTypeAmateur => m_ugq_usage_fee_type_amateur.Value; + private static Lazy m_ugq_usage_fee_type_amateur = new Lazy(() => getValueToString("UgqUsageFeeTypeAmateur", "Sapphire")); + public static string UgqUsageFeeTypeRisingStar => m_ugq_usage_fee_type_rising_star.Value; + private static Lazy m_ugq_usage_fee_type_rising_star = new Lazy(() => getValueToString("UgqUsageFeeTypeRisingStar", "Ruby")); + public static string UgqUsageFeeTypeMaster1 => m_ugq_usage_fee_type_master1.Value; + private static Lazy m_ugq_usage_fee_type_master1 = new Lazy(() => getValueToString("UgqUsageFeeTypeMaster1", "Sapphire")); + public static string UgqUsageFeeTypeMaster2 => m_ugq_usage_fee_type_master2.Value; + private static Lazy m_ugq_usage_fee_type_master2 = new Lazy(() => getValueToString("UgqUsageFeeTypeMaster2", "Sapphire")); + public static string UgqUsageFeeTypeMaster3 => m_ugq_usage_fee_type_master3.Value; + private static Lazy m_ugq_usage_fee_type_master3 = new Lazy(() => getValueToString("UgqUsageFeeTypeMaster3", "Sapphire")); + + public static float UgqRevenueDistributionCreator => m_ugq_revenue_distribution_creator.Value; + private static Lazy m_ugq_revenue_distribution_creator = new Lazy(() => getValueToFloat("UgqRevenueDistributionCreator", 0.5f)); + public static float UgqRevenueDistributionBeaconOwner => m_ugq_revenue_distribution_beacon_owner.Value; + private static Lazy m_ugq_revenue_distribution_beacon_owner = new Lazy(() => getValueToFloat("UgqRevenueDistributionBeaconOwner", 1)); + public static Int32 WorldBeaconAmateurUGQReward => m_world_beacon_amateur_ugq_reward.Value; + private static Lazy m_world_beacon_amateur_ugq_reward = new Lazy(() => getValueToInt32("WorldBeaconAmateurUGQReward", 0)); + public static Int32 WorldBeaconRisingstarUGQReward => m_world_beacon_risingstar_ugq_reward.Value; + private static Lazy m_world_beacon_risingstar_ugq_reward = new Lazy(() => getValueToInt32("WorldBeaconRisingstarUGQReward", 0)); + public static Int32 WorldBeaconMasterOneUGQReward => m_world_beacon_master_one_ugq_reward.Value; + private static Lazy m_world_beacon_master_one_ugq_reward = new Lazy(() => getValueToInt32("WorldBeaconMasterOneUGQReward", 0)); + public static Int32 WorldBeaconMasterTwoUGQReward => m_world_beacon_master_two_ugq_reward.Value; + private static Lazy m_world_beacon_master_two_ugq_reward = new Lazy(() => getValueToInt32("WorldBeaconMasterTwoUGQReward", 0)); + public static Int32 WorldBeaconMasterThreeUGQReward => m_world_beacon_master_three_ugq_reward.Value; + private static Lazy m_world_beacon_master_three_ugq_reward = new Lazy(() => getValueToInt32("WorldBeaconMasterThreeUGQReward", 0)); + public static Int32 NormalBeaconAmateurUGQReward => m_normal_beacon_amateur_ugq_Reward.Value; + private static Lazy m_normal_beacon_amateur_ugq_Reward = new Lazy(() => getValueToInt32("NormalBeaconAmateurUGQReward", 0)); + public static Int32 NormalBeaconRisingstarUGQReward => m_normal_beacon_risingstar_ugq_Reward.Value; + private static Lazy m_normal_beacon_risingstar_ugq_Reward = new Lazy(() => getValueToInt32("NormalBeaconRisingstarUGQReward", 0)); + public static Int32 NormalBeaconMasterOneUGQReward => m_normal_beacon_master_one_ugq_reward.Value; + private static Lazy m_normal_beacon_master_one_ugq_reward = new Lazy(() => getValueToInt32("NormalBeaconMasterOneUGQReward", 0)); + public static Int32 NormalBeaconMasterTwoUGQReward => m_normal_beacon_master_two_ugq_reward.Value; + private static Lazy m_normal_beacon_master_two_ugq_reward = new Lazy(() => getValueToInt32("NormalBeaconMasterTwoUGQReward", 0)); + public static Int32 NormalBeaconMasterThreeUGQReward => m_normal_beacon_master_three_ugq_reward.Value; + private static Lazy m_normal_beacon_master_three_ugq_reward = new Lazy(() => getValueToInt32("NormalBeaconMasterThreeUGQReward", 0)); + public static Int32 AiChatInsentiveMax => m_ai_chat_insentive_max.Value; + private static Lazy m_ai_chat_insentive_max = new Lazy(() => getValueToInt32("Beacon_Insentive_Max", 1000)); // Beam 재화 + public static Int32 AiChatInsentivePeriod => m_ai_chat_insentive_period.Value; + private static Lazy m_ai_chat_insentive_period = new Lazy(() => getValueToInt32("Beacon_Insentive_Period", 10080)); // 분 단위 + + public static Int32 MyHomeMaxSaveSlot => m_myhome_max_save_slot.Value; + private static Lazy m_myhome_max_save_slot = new Lazy(() => getValueToInt32("MyHomeMaxSaveSlot", 5)); + + public static Int32 MyHomeDefaultSaveSlot => m_myhome_default_save_slot.Value; + private static Lazy m_myhome_default_save_slot = new Lazy(() => getValueToInt32("MyHomeDefaultSaveSlot", 1)); + + public static string MyHomeDefaultSaveFileName => m_myhome_default_save_file_name.Value; + private static Lazy m_myhome_default_save_file_name = new Lazy(() => getValueToString("MyHomeDefaultSaveFileName", "temp.myhome")); + + public static Int32 MyHomeSaveSlotExpandCurrency => m_myhome_save_slot_expand_currency.Value; + private static Lazy m_myhome_save_slot_expand_currency = new Lazy(() => getValueToInt32("MyHomeSaveSlotExpandCurrency", 2)); + + public static Int32 MyHomeSaveSlotExpandCost => m_myhome_save_slot_expand_cost.Value; + private static Lazy m_myhome_save_slot_expand_cost = new Lazy(() => getValueToInt32("MyHomeSaveSlotExpandCost", 100)); + + public static Int32 MyhomenameMinLength => m_myhome_min_length.Value; + private static Lazy m_myhome_min_length = new Lazy(() => getValueToInt32("MyhomenameMinLength", 2)); + + public static Int32 MyhomenameMaxLength => m_myhome_max_length.Value; + private static Lazy m_myhome_max_length = new Lazy(() => getValueToInt32("MyhomenameMaxLength", 20)); + + public static Int32 MyhomeInterphone => m_myhome_interphone.Value; + private static Lazy m_myhome_interphone = new Lazy(() => getValueToInt32("MyHomeInterphone", 1)); + + public static Int32 MyhomeStartPoint => m_myhome_start_point.Value; + private static Lazy m_myhome_start_point = new Lazy(() => getValueToInt32("MyHomeStartPoint", 1)); + + public static Int32 SmallTypeMyhome => m_small_type_myhome.Value; + private static Lazy m_small_type_myhome = new Lazy(() => getValueToInt32("SmallTypeMyhome", 10001)); + + public static Int32 MediumTypeMyhome => m_medium_type_myhome.Value; + private static Lazy m_medium_type_myhome = new Lazy(() => getValueToInt32("MediumTypeMyhome", 10003)); + + public static Int32 LargeTypeMyhome => m_large_type_myhome.Value; + private static Lazy m_large_type_myhome = new Lazy(() => getValueToInt32("LargeTypeMyhome", 10004)); + + public static Int32 GiantTypeMyhome => m_giant_type_myhome.Value; + private static Lazy m_giant_type_myhome = new Lazy(() => getValueToInt32("GiantTypeMyhome", 10005)); + + public static Int32 MinEnterPlayer => m_min_enter_player.Value; + private static Lazy m_min_enter_player = new Lazy(() => getValueToInt32("MinEnterPlayer", 1)); + + public static Int32 MaxEnterPlayer => m_max_enter_player.Value; + private static Lazy m_max_enter_player = new Lazy(() => getValueToInt32("MaxEnterPlayer", 1)); + + public static Int32 MinRentalPeriod => m_min_rental_period.Value; + private static Lazy m_min_rental_period = new Lazy(() => getValueToInt32("MinRentalPeriod", 1)); + + public static Int32 MaxRentalPeriod => m_max_rental_period.Value; + private static Lazy m_max_rental_period = new Lazy(() => getValueToInt32("MaxRentalPeriod", 1)); + + public static bool UseRentalStateSwitch => m_use_rental_state_switch.Value; + private static Lazy m_use_rental_state_switch = new Lazy(() => getValueToBool("UseRentalStateSwitch", true)); + + public static DateTime NpcRankingCalculateTime => m_npc_ranking_calculate_time.Value; + private static Lazy m_npc_ranking_calculate_time = new Lazy(() => + { + var time_string = getValueToString("NpcRankingCalculateTime", "T03:00:00"); + time_string = time_string.Replace("T", ""); + var datetime = Convert.ToDateTime(time_string); + return datetime; + }); + + public static DateTime BusinessLogRefreshTime => m_businesslog_refresh_time.Value; + private static Lazy m_businesslog_refresh_time = new Lazy(() => + { + var time_string = getValueToString("BusinessLogRefreshTime", "T00:01:00"); + time_string = time_string.Replace("T", ""); + var datetime = Convert.ToDateTime(time_string); + return datetime; + }); + + public static int NpcRankingCalculatePeriod => m_npc_ranking_calculate_period.Value; + private static Lazy m_npc_ranking_calculate_period = new Lazy(() => getValueToInt32("NpcRankingCalculatePeriod", 24)); + + public static int NpcRankingRetentionPeriod => m_npc_ranking_retention_period.Value; + private static Lazy m_npc_ranking_retention_period = new Lazy(() => getValueToInt32("NpcRankingRetentionPeriod", 2160)); + + + + public static int UGQDefaultSlot => m_ugq_default_slot.Value; + private static Lazy m_ugq_default_slot = new Lazy(() => getValueToInt32("UGQDefaultSlot", 2)); + + public static int UGQMaximumSlot => m_ugq_maximum_slot.Value; + private static Lazy m_ugq_maximum_slot = new Lazy(() => getValueToInt32("UGQMaximumSlot", 5)); + + public static string UGQAddSlotType => m_ugq_add_slot_type.Value; + private static Lazy m_ugq_add_slot_type = new Lazy(() => getValueToString("UGQAddSlotType", "SAPPHIRE")); + + public static double UGQAddSlotAmount => m_ugq_add_slot_amount.Value; + private static Lazy m_ugq_add_slot_amount = new Lazy(() => getValueToDouble("UGQAddSlotAmount", 50)); + + public static decimal CaliumConverterInitialValue => m_calium_converter_initial_value.Value; + private static Lazy m_calium_converter_initial_value = new Lazy(() => getValueToDecimal("CaliumConverterInitialValue", 875.5)); + + public static decimal CaliumConverterEfficiency => m_calium_converter_efficiency.Value; + private static Lazy m_calium_converter_efficiency = new Lazy(() => getValueToDecimal("CaliumConverterEfficiency", 1)); + + public static int CaliumConverterPivotTime => m_calium_converter_pivot_time.Value; + private static Lazy m_calium_converter_pivot_time = new Lazy(() => getValueToInt32("CaliumConverterPivotTime", 9)); + + public static int CaliumConverterCycleTime => m_calium_converter_cycle_time.Value; + private static Lazy m_calium_converter_cycle_time = new Lazy(() => getValueToInt32("CaliumConverterCycleTime", 8)); + + public static decimal CaliumConverterDailyLimit => m_calium_converter_daily_limit.Value; + private static Lazy m_calium_converter_daily_limit = new Lazy(() => getValueToDecimal("CaliumConverterDailyLimit", 875.05)); + + public static decimal CaliumConverterLimitPerPerson => m_calium_converter_limit_per_person.Value; + private static Lazy m_calium_converter_limit_per_person = new Lazy(() => getValueToDecimal("CaliumConverterLimitPerPerson", 1)); + + public static CurrencyType CaliumConverterCommissionType => m_calium_converter_commission_type.Value; + private static Lazy m_calium_converter_commission_type = new Lazy(() => getValueToEnum("CaliumConverterCommissionType", CurrencyType.Sapphire)); + + public static decimal CaliumConverterCommissionRate => m_calium_converter_commission_rate.Value; + private static Lazy m_calium_converter_commission_rate = new Lazy(() => getValueToDecimal("CaliumConverterCommissionRate", 0.1)); + + public static decimal CaliumConverterConversionRate => m_calium_converter_conversion_rate.Value; + private static Lazy m_calium_converter_conversion_rate = new Lazy(() => getValueToDecimal("CaliumConverterConversionRate", 0.01)); + + public static int UGQBoardMaxListAmount => m_ugq_board_max_list_amount.Value; + private static Lazy m_ugq_board_max_list_amount = new Lazy(() => getValueToInt32("UGQBoardMaxListAmount", 10)); + + + public static int UGQPromoteConditionRisingstar => m_ugq_dpromote_condition_rising_star.Value; + private static Lazy m_ugq_dpromote_condition_rising_star = new Lazy(() => getValueToInt32("UGQPromoteConditionRisingstar", 20)); + + public static Pos UGQNormalBeaconQuestMarkerPos => m_ugq_normal_beacon_quest_marker_pos.Value; + private static Lazy m_ugq_normal_beacon_quest_marker_pos = new Lazy(() => + { + var pos = new Pos() { X = -20310, Y = 20760, Z = 20 }; + var pos_string = getValueToString("UGQNormalBeaconQuestMarkerPos", "-20310 20760 20"); + var posList = pos_string.Split(' '); + if(posList.Length == 3) + { + if(posList[0].toInt32(out var x)) + pos.X = x; + + if (posList[1].toInt32(out var y)) + pos.Y = y; + + if (posList[2].toInt32(out var z)) + pos.Z = z; + } + return pos; + }); + + public static int SentMailNumsResetTime => m_sent_mail_nums_reset_time.Value; + private static Lazy m_sent_mail_nums_reset_time = new Lazy(() => getValueToInt32("SentMailNumsResetTime", 15)); + + public static Int32 UgqBonusGoldAmateur => m_ugq_bonus_gold_amateur.Value; + private static Lazy m_ugq_bonus_gold_amateur = new Lazy(() => getValueToInt32("UgqBonusGoldAmateur", 500)); + + public static Int32 UgqBonusGoldRisingStar => m_ugq_bonus_gold_rising_star.Value; + private static Lazy m_ugq_bonus_gold_rising_star = new Lazy(() => getValueToInt32("UgqBonusGoldRisingStar", 250)); + + public static Int32 UgqBonusGoldMaster1 => m_ugq_bonus_gold_master1.Value; + private static Lazy m_ugq_bonus_gold_master1 = new Lazy(() => getValueToInt32("UgqBonusGoldMaster1", 200)); + + public static Int32 UgqBonusGoldMaster2 => m_ugq_bonus_gold_master2.Value; + private static Lazy m_ugq_bonus_gold_master2 = new Lazy(() => getValueToInt32("UgqBonusGoldMaster2", 150)); + + public static Int32 UgqBonusGoldMaster3 => m_ugq_bonus_gold_master3.Value; + private static Lazy m_ugq_bonus_gold_master3 = new Lazy(() => getValueToInt32("UgqBonusGoldMaster3", 150)); + + public static Int32 UgqAmateurBonusCount => m_ugq_amateur_bonus_count.Value; + private static Lazy m_ugq_amateur_bonus_count = new Lazy(() => getValueToInt32("UgqAmateurBonusCount", 10)); + + public static Int32 UgqRisingStarBonusCount => m_ugq_rising_star_bonus_count.Value; + private static Lazy m_ugq_rising_star_bonus_count = new Lazy(() => getValueToInt32("UgqRisingStarBonusCount", 5)); + + public static Int32 UgqMaster1BonusCount => m_ugq_master1_bonus_count.Value; + private static Lazy m_ugq_master1_bonus_count = new Lazy(() => getValueToInt32("UgqMaster1BonusCount", 3)); + + public static Int32 UgqMaster2BonusCount => m_ugq_master2_bonus_count.Value; + private static Lazy m_ugq_master2_bonus_count = new Lazy(() => getValueToInt32("UgqMaster2BonusCount", 2)); + + public static Int32 UgqMaster3BonusCount => m_ugq_master3_bonus_count.Value; + private static Lazy m_ugq_master3_bonus_count = new Lazy(() => getValueToInt32("UgqMaster3BonusCount", 1)); + + public static string UgqBonusCountInit => m_ugq_bonus_count_init.Value; + private static Lazy m_ugq_bonus_count_init = new Lazy(() => getValueToString("UgqBonusCountInit", "T00:00:00")); + + public static CurrencyType CharacterCustomizeCostType => m_character_customize_cost_type.Value; + private static Lazy m_character_customize_cost_type = new Lazy(() => getValueToEnum("CharacterCustomizeCostType", CurrencyType.None)); + + public static double CharacterCustomizeCost => m_character_customize_cost.Value; + private static Lazy m_character_customize_cost = new Lazy(() => getValueToDouble("CharacterCustomizeCostValue", 0)); + + public static CurrencyType NpcCustomizeCostType => m_npc_customize_cost_type.Value; + private static Lazy m_npc_customize_cost_type = new Lazy(() => getValueToEnum("NpcCustomizeCurrency", CurrencyType.None)); + + public static double NpcCustomizeCost => m_npc_customize_cost.Value; + private static Lazy m_npc_customize_cost = new Lazy(() => getValueToDouble("NpcCustomizeCost", 0)); + + public static Int32 BeaconAppProfileCooltime => m_beacon_app_profile_cooltime.Value; + private static Lazy m_beacon_app_profile_cooltime = new Lazy(() => getValueToInt32("BeaconAppProfileCooltime", 3600)); + + // 인벤토리 슬롯 최대 갯수 관련 임시 추가 메타 정보 + public static Int16 Inventory1Slot => m_inventory_1_slot.Value; + private static Lazy m_inventory_1_slot = new Lazy(() => getValueToInt16("Inventory1Slot", 200)); + + public static Int16 Inventory2Slot => m_inventory_2_slot.Value; + private static Lazy m_inventory_2_slot = new Lazy(() => getValueToInt16("Inventory2Slot", 180)); + + public static Int16 Inventory3Slot => m_inventory_3_slot.Value; + private static Lazy m_inventory_3_slot = new Lazy(() => getValueToInt16("Inventory3Slot", 50)); + + public static Int16 Inventory4Slot => m_inventory_4_slot.Value; + private static Lazy m_inventory_4_slot = new Lazy(() => getValueToInt16("Inventory4Slot", 50)); + + public static Int16 Inventory5Slot => m_inventory_5_slot.Value; + private static Lazy m_inventory_5_slot = new Lazy(() => getValueToInt16("Inventory5Slot", 180)); + + public static Int16 Inventory6Slot => m_inventory_6_slot.Value; + private static Lazy m_inventory_6_slot = new Lazy(() => getValueToInt16("Inventory6Slot", 30)); + + public static Int16 Inventory7Slot => m_inventory_7_slot.Value; + private static Lazy m_inventory_7_slot = new Lazy(() => getValueToInt16("Inventory7Slot", 30)); + + public static Int16 Inventory8Slot => m_inventory_8_slot.Value; + private static Lazy m_inventory_8_slot = new Lazy(() => getValueToInt16("Inventory8Slot", 10)); + + public static Int16 Inventory9Slot => m_inventory_9_slot.Value; + private static Lazy m_inventory_9_slot = new Lazy(() => getValueToInt16("Inventory9Slot", 10)); + + public static Int16 Inventory10Slot => m_inventory_10_slot.Value; + private static Lazy m_inventory_10_slot = new Lazy(() => getValueToInt16("Inventory10Slot", 60)); + + + // MyHome 인벤토리 슬롯 최대 갯수 관련 임시 추가 메타 정보EGenderType + public static Int16 MyHomeInventory1Slot => m_my_home_inventory_1_slot.Value; + private static Lazy m_my_home_inventory_1_slot = new Lazy(() => getValueToInt16("MyHomeInventory1Slot", 200)); + + public static Int16 MyHomeInventory2Slot => m_my_home_inventory_2_slot.Value; + private static Lazy m_my_home_inventory_2_slot = new Lazy(() => getValueToInt16("MyHomeInventory2Slot", 0)); + + public static Int16 MyHomeInventory3Slot => m_my_home_inventory_3_slot.Value; + private static Lazy m_my_home_inventory_3_slot = new Lazy(() => getValueToInt16("MyHomeInventory3Slot", 0)); + + public static Int16 MyHomeInventory4Slot => m_my_home_inventory_4_slot.Value; + private static Lazy m_my_home_inventory_4_slot = new Lazy(() => getValueToInt16("MyHomeInventory4Slot", 0)); + + public static Int16 MyHomeInventory5Slot => m_my_home_inventory_5_slot.Value; + private static Lazy m_my_home_inventory_5_slot = new Lazy(() => getValueToInt16("MyHomeInventory5Slot", 0)); + + public static Int16 MyHomeInventory6Slot => m_my_home_inventory_6_slot.Value; + private static Lazy m_my_home_inventory_6_slot = new Lazy(() => getValueToInt16("MyHomeInventory6Slot", 30)); + + public static Int16 MyHomeInventory7Slot => m_my_home_inventory_7_slot.Value; + private static Lazy m_my_home_inventory_7_slot = new Lazy(() => getValueToInt16("MyHomeInventory7Slot", 0)); + + public static Int16 MyHomeInventory8Slot => m_my_home_inventory_8_slot.Value; + private static Lazy m_my_home_inventory_8_slot = new Lazy(() => getValueToInt16("MyHomeInventory8Slot", 0)); + + public static Int16 MyHomeInventory9Slot => m_my_home_inventory_9_slot.Value; + private static Lazy m_my_home_inventory_9_slot = new Lazy(() => getValueToInt16("MyHomeInventory9Slot", 0)); + + public static Int16 MyHomeInventory10Slot => m_my_home_inventory_10_slot.Value; + private static Lazy m_my_home_inventory_10_slot = new Lazy(() => getValueToInt16("MyHomeInventory10Slot", 0)); + + // NFT 관련 + public static Int32 CPNFTHolderKeyItem => m_cp_nft_holder_key_item.Value; + private static Lazy m_cp_nft_holder_key_item = new Lazy(() => getValueToInt32("CPNFTHolderKeyItem", 0)); + + public static Int32 ShopRenewalBlockTime => m_shop_renewal_block_time.Value; + private static Lazy m_shop_renewal_block_time = new Lazy(() => getValueToInt32("ShopRenewalBlockTime", 5)); //sec + + // Large Packet 관련 + public static Int32 LargePacketCheckSize => m_large_packet_check_size.Value; + private static Lazy m_large_packet_check_size = new Lazy(() => getValueToInt32("LargePacketxCheckSize", 256000)); + public static Int32 PacketChunkSize => m_packet_chunk_size.Value; + private static Lazy m_packet_chunk_size = new Lazy(() => getValueToInt32("PacketChunkSize", 60000)); + + public static Int32 LandAuctionReloadIntervalSec => m_land_auction_reload_interval_sec.Value; + private static Lazy m_land_auction_reload_interval_sec = new Lazy(() => getValueToInt32("LandAuctionReloadIntervalSec", 5)); + + public static Int32 BlindBidStartBeforeLandAuctionEndMinutes => m_blind_bid_start_before_land_auction_end_minuts.Value; + private static Lazy m_blind_bid_start_before_land_auction_end_minuts = new Lazy(() => getValueToInt32("LandAuctionBlindTimeSet", 10)); + + public static Int32 LandAuctionFailedMailPeriodMinutes => m_land_auction_failed_mail_period_minutes.Value; + private static Lazy m_land_auction_failed_mail_period_minutes = new Lazy(() => getValueToInt32("AuctionLandMailValidTime", 2592000)); + + public static Int32 LandAuctionSuccessedMailPeriodMinutes => m_land_auction_successed_mail_period_minutes.Value; + private static Lazy m_land_auction_successed_mail_period_minutes = new Lazy(() => getValueToInt32("AuctionLandReturnMailValidTime", 2592000)); + + public static double BeaconShopMinCaliumPrice => m_beacon_shop_min_calium_price.Value; + private static Lazy m_beacon_shop_min_calium_price = new Lazy(() => getValueToDouble("BeaconShopMinCaliumPrice", 0.01)); + + public static double BeaconShopMaxCaliumPrice => m_beacon_shop_max_calium_price.Value; + private static Lazy m_beacon_shop_max_calium_price = new Lazy(() => getValueToDouble("BeaconShopMaxCaliumPrice", 99999999.00)); + + public static double BeaconShopSellFee => m_beacon_shop_sell_fee.Value; + private static Lazy m_beacon_shop_sell_fee = new Lazy(() => getValueToDouble("BeaconShopSellFee", 0.3)); + + public static int BeaconShopDailyRegistration => m_beacon_shop_daily_registration.Value; + private static Lazy m_beacon_shop_daily_registration = new Lazy(() => getValueToInt32("BeaconShopDailyRegistration", 10)); + + public static int BeaconShopSellPeriod => m_beacon_shop_sell_period.Value; + private static Lazy m_beacon_shop_sell_period = new Lazy(() => getValueToInt32("BeaconShopSellPeriod", 72)); + + public static int BeaconShopMaxSlot => m_beacon_shop_max_slot.Value; + private static Lazy m_beacon_shop_max_slot = new Lazy(() => getValueToInt32("BeaconShopMaxSlot", 20)); + public static int BeaconShopDailyRegistrationResetTime => m_beacon_shop_daily_registration_reset_time.Value; + private static Lazy m_beacon_shop_daily_registration_reset_time = new Lazy(() => getValueToInt32("BeaconShopDailyRegistrationResetTime", 0)); + + public static int ItemExpireAlertBeforeHourTime => m_item_expire_alert_before_hour_time.Value; + private static Lazy m_item_expire_alert_before_hour_time = new Lazy(() => getValueToInt32("ItemExpireAlertBeforeHourTime", ConstValue.default_1_day_to_hour * 7)); + + public static double BeaconShopPaymentListMaxCount => m_beacon_shop_payment_max_count.Value; + private static Lazy m_beacon_shop_payment_max_count = new Lazy(() => getValueToDouble("BeaconShopPaymentListMaxCount", 50)); + + public static int BeaconShopRecentItemCount => m_beacon_shop_recent_item_count.Value; + private static Lazy m_beacon_shop_recent_item_count = new Lazy(() => getValueToInt32("BeaconShopRecentItemCount", 5)); + + public static int MainMenuTaxiId => m_main_menu_taxi_id.Value; + private static Lazy m_main_menu_taxi_id = new Lazy(() => getValueToInt32("Taxi_menucall", 5)); + + public static int BeaconMoveCost => m_beacon_move_cost.Value; + private static Lazy m_beacon_move_cost = new Lazy(() => getValueToInt32("BeaconMoveCost", 500)); + + public static int BeaconShopSearchCount => m_beacon_shop_search_count.Value; + private static Lazy m_beacon_shop_search_count = new Lazy(() => getValueToInt32("BeaconShopSearchCount", 100)); } + + //========================================================================================= + // GameConfigMeta 데이터내의 문자열 데이터를 해당하는 타입으로 변환 한다. + //========================================================================================= + public static bool getValueToBool(string key, bool defaultValue) + { + MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); + if (found_string == null) + return defaultValue; + + var is_success = found_string.toBool(out var converted_value); + return is_success == true ? converted_value : defaultValue; + } + + public static Int16 getValueToInt16(string key, Int16 defaultValue) + { + MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); + if (found_string == null) + return defaultValue; + + var is_success = found_string.toInt16(out var converted_value); + return is_success == true ? converted_value : defaultValue; + } + public static Int32 getValueToInt32(string key, Int32 defaultValue) + { + MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); + if (found_string == null) + return defaultValue; + + var is_success = found_string.toInt32(out var converted_value); + return is_success == true ? converted_value : defaultValue; + } + + public static Int64 getValueToInt64(string key, Int64 defaultValue) + { + MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); + if (found_string == null) + return defaultValue; + + var is_success = found_string.toInt64(out var converted_value); + return is_success == true ? converted_value : defaultValue; + } + + public static float getValueToFloat(string key, float defaultValue) + { + MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); + if (found_string == null) + return defaultValue; + + var is_success = found_string.toFloat(out var converted_value); + return is_success == true ? converted_value : defaultValue; + } + + public static double getValueToDouble(string key, double defaultValue) + { + MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); + if (found_string == null) + return defaultValue; + + var is_success = found_string.toDouble(out var converted_value); + return is_success == true ? converted_value : defaultValue; + } + + public static decimal getValueToDecimal(string key, double defaultValue) + { + MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); + if (found_string == null) + return (decimal)defaultValue; + + var is_success = found_string.toDecimal(out var converted_value); + return is_success == true ? converted_value : (decimal)defaultValue; + } + + public static string getValueToString(string key, string defaultValue) + { + MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); + return found_string?.isNullOrWhiteSpace() == false ? found_string : defaultValue; + } + + public static TEnum getValueToEnum(string key, TEnum defaultValue) + { + MetaData.Instance._GameConfigMetaTable.TryGetValue(key, out var found_string); + if (found_string == null) + return defaultValue; + + var is_success = EnumHelper.tryParse(found_string, out var converted_value); + if (!is_success) + return defaultValue; + + return converted_value == null ? defaultValue : converted_value; + } + + } diff --git a/ServerCommon/MetaAssets/ContentLoader.cs b/ServerCommon/MetaAssets/ContentLoader.cs index 87f432e..175a856 100644 --- a/ServerCommon/MetaAssets/ContentLoader.cs +++ b/ServerCommon/MetaAssets/ContentLoader.cs @@ -3,49 +3,54 @@ using System.Collections.Generic; using System.IO; using System.Text; -namespace MetaAssets + +namespace MetaAssets; + + +// HANDOVER: 메타 데이터를 로딩해 주는 클래스 이다. +// 본 로직들은 ServerBase 모듈로 이관 시켜야 한다. + + +public static class ContentLoader { - public static class ContentLoader + public static T? loadFile(string dataDir, string fileName) where T : ContentTableBase { - public static T? loadFile(string dataDir, string fileName) where T : ContentTableBase + try { - try - { - string exactPath = Path.GetFullPath(dataDir); + string exactPath = Path.GetFullPath(dataDir); - string data = System.IO.File.ReadAllText(Path.Combine(dataDir, fileName)); - return Newtonsoft.Json.JsonConvert.DeserializeObject(data); - } - catch (Exception ex) - { - throw new Exception($"content load fail. dataDir: {dataDir}, fileName: {fileName}", ex); - } + string data = System.IO.File.ReadAllText(Path.Combine(dataDir, fileName)); + return Newtonsoft.Json.JsonConvert.DeserializeObject(data); } - - public static T? loadMultipleFiles(string dataDir, string filePattern) where T : ContentTableBase + catch (Exception ex) { - var files = Directory.GetFiles(dataDir, filePattern, SearchOption.TopDirectoryOnly); - - List tables = new List(); - foreach (string file in files) - { - string data = System.IO.File.ReadAllText(file); - T? json = Newtonsoft.Json.JsonConvert.DeserializeObject(data); - - if(json != null) - tables.Add(json); - } - - T? oneTable = null; - foreach (var table in tables) - { - if (oneTable == null) - oneTable = table; - else - oneTable.merge(table); - } - - return oneTable; + throw new Exception($"content load fail. dataDir: {dataDir}, fileName: {fileName}", ex); } } + + public static T? loadMultipleFiles(string dataDir, string filePattern) where T : ContentTableBase + { + var files = Directory.GetFiles(dataDir, filePattern, SearchOption.TopDirectoryOnly); + + List tables = new List(); + foreach (string file in files) + { + string data = System.IO.File.ReadAllText(file); + T? json = Newtonsoft.Json.JsonConvert.DeserializeObject(data); + + if(json != null) + tables.Add(json); + } + + T? oneTable = null; + foreach (var table in tables) + { + if (oneTable == null) + oneTable = table; + else + oneTable.merge(table); + } + + return oneTable; + } } diff --git a/ServerCommon/MetaAssets/ContentTableBase.cs b/ServerCommon/MetaAssets/ContentTableBase.cs index e35026a..5a2290b 100644 --- a/ServerCommon/MetaAssets/ContentTableBase.cs +++ b/ServerCommon/MetaAssets/ContentTableBase.cs @@ -2,10 +2,16 @@ using System.Collections.Generic; using System.Text; -namespace MetaAssets + + +namespace MetaAssets; + + +// HANDOVER: 메타 스키마를 제네릭 기반으로 관리해 주는 클래스 이다. +// 본 로직들은 ServerBase 모듈로 이관 시켜야 한다. + + +public class ContentTableBase { - public class ContentTableBase - { - public virtual void merge(T table) { } - } + public virtual void merge(T table) { } } diff --git a/ServerCommon/MetaAssets/ItemMetaHelper.cs b/ServerCommon/MetaAssets/ItemMetaHelper.cs index 63dc1b3..dcf8902 100644 --- a/ServerCommon/MetaAssets/ItemMetaHelper.cs +++ b/ServerCommon/MetaAssets/ItemMetaHelper.cs @@ -45,6 +45,8 @@ public static class ItemMetaHelper return reward; } + + public static bool isCreatableItem(this ItemMetaData metaData) { diff --git a/ServerCommon/MetaAssets/MetaTable.cs b/ServerCommon/MetaAssets/MetaTable.cs index aface8c..edc4883 100644 --- a/ServerCommon/MetaAssets/MetaTable.cs +++ b/ServerCommon/MetaAssets/MetaTable.cs @@ -83,6 +83,20 @@ namespace MetaAssets public WeblinkLocalizeData WeblinkLocalizeData { get; private set; } public PlanetItemExchangePolicyMetaTable PlanetItemExchangePolicyMetaTable { get; private set; } public GameModeOptionMetaTable GameModeOptionMetaTable { get; private set; } + public GameModeMetaTable GameModeMetaTable { get; private set; } + public GameModeRewardMetaTable GameModeRewardMetaTable { get; private set; } + public GameModeConditionMetaTable GameModeConditionMetaTable { get; private set; } + public GameModeStartMetaTable GameModeStartMetaTable { get; private set; } + public GameModeTpsFfaMetaTable GameModeTpsFfaMetaTable { get; private set; } + public GameModeTeamMetaTable GameModeTeamMetaTable { get; private set; } + public GameModeMatchMetaTable GameModeMatchMetaTable { get; private set; } + public GameModeCommonMetaTable GameModeCommonMetaTable { get; private set; } + public GameModeInstanceMetaTable GameModeInstanceMetaTable { get; private set; } + public GameModeRunRaceMetaTable GameModeRunRaceMetaTable { get; private set; } + public GameModeRunRaceCheckPointMonitorMetaTable GameModeRunRaceCheckPointMonitorMetaTable { get; private set; } + public ContentsMenuDataTable ContentsMenuDataTable { get; private set; } + public RankingDataTable RankingDataTable { get; private set; } + public EventActionScoreMetaTable EventActionScoreMetaTable { get; private set; } public void load(string dataDir) { loadCurrencyMetaTable(dataDir); @@ -155,6 +169,20 @@ namespace MetaAssets loadWeblinkLocalizeData(dataDir); loadPlanetItemExchangePolicyMetaTable(dataDir); loadGameModeOptionMetaTable(dataDir); + loadGameModeMetaTable(dataDir); + loadGameModeRewardMetaTable(dataDir); + loadGameModeConditionMetaTable(dataDir); + loadGameModeStartMetaTable(dataDir); + loadGameModeTpsFfaMetaTable(dataDir); + loadGameModeTeamMetaTable(dataDir); + loadGameModeMatchMetaTable(dataDir); + loadGameModeCommonMetaTable(dataDir); + loadGameModeInstanceMetaTable(dataDir); + loadGameModeRunRaceMetaTable(dataDir); + loadGameModeRunRaceCheckPointMonitorMetaTable(dataDir); + loadContentsMenuDataTable(dataDir); + loadRankingDataTable(dataDir); + loadEventActionScoreMetaTable(dataDir); } void loadCurrencyMetaTable(string dataDir) @@ -456,10 +484,9 @@ namespace MetaAssets RequirementMetaTable = new RequirementMetaTable(table); RequirementMetaTable.build(); } - void loadTextStringMetaTable(string dataDir) { - var table = ContentLoader.loadFile(dataDir, "TextStringData.json"); + var table = ContentLoader.loadMultipleFiles(Path.Combine(dataDir, "TextString"), "TextStringData*"); TextStringMetaTable = new TextStringMetaTable(table); TextStringMetaTable.build(); } @@ -645,6 +672,104 @@ namespace MetaAssets GameModeOptionMetaTable = new GameModeOptionMetaTable(table); GameModeOptionMetaTable.build(); } + + void loadGameModeMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeData.json"); + GameModeMetaTable = new GameModeMetaTable(table); + GameModeMetaTable.build(); + } + + void loadGameModeRewardMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeRewardData.json"); + GameModeRewardMetaTable = new GameModeRewardMetaTable(table); + GameModeRewardMetaTable.build(); + } + + void loadGameModeConditionMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeConditionData.json"); + GameModeConditionMetaTable = new GameModeConditionMetaTable(table); + GameModeConditionMetaTable.build(); + } + + void loadGameModeStartMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeStartData.json"); + GameModeStartMetaTable = new GameModeStartMetaTable(table); + GameModeStartMetaTable.build(); + } + + void loadGameModeTpsFfaMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeTpsFfaData.json"); + GameModeTpsFfaMetaTable = new GameModeTpsFfaMetaTable(table); + GameModeTpsFfaMetaTable.build(); + } + + void loadGameModeTeamMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeTeamData.json"); + GameModeTeamMetaTable = new GameModeTeamMetaTable(table); + GameModeTeamMetaTable.build(); + } + + void loadGameModeMatchMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeMatchData.json"); + GameModeMatchMetaTable = new GameModeMatchMetaTable(table); + GameModeMatchMetaTable.build(); + } + + void loadGameModeCommonMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeCommonData.json"); + GameModeCommonMetaTable = new GameModeCommonMetaTable(table); + GameModeCommonMetaTable.build(); + } + + void loadGameModeInstanceMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeInstanceData.json"); + GameModeInstanceMetaTable = new GameModeInstanceMetaTable(table); + GameModeInstanceMetaTable.build(); + } + + void loadGameModeRunRaceMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeRunRaceData.json"); + GameModeRunRaceMetaTable = new GameModeRunRaceMetaTable(table); + GameModeRunRaceMetaTable.build(); + } + + void loadGameModeRunRaceCheckPointMonitorMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "GameModeRunRaceCheckPointMonitorData.json"); + GameModeRunRaceCheckPointMonitorMetaTable = new GameModeRunRaceCheckPointMonitorMetaTable(table); + GameModeRunRaceCheckPointMonitorMetaTable.build(); + } + + void loadContentsMenuDataTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "ContentsMenuData.json"); + ContentsMenuDataTable = new ContentsMenuDataTable(table); + ContentsMenuDataTable.build(); + } + + void loadRankingDataTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "RankingData.json"); + RankingDataTable = new RankingDataTable(table); + RankingDataTable.build(); + } + + void loadEventActionScoreMetaTable(string dataDir) + { + var table = ContentLoader.loadFile(dataDir, "EventActionScoreData.json"); + EventActionScoreMetaTable = new EventActionScoreMetaTable(table); + EventActionScoreMetaTable.build(); + } } public partial class ACZoneContent { @@ -2109,4 +2234,298 @@ namespace MetaAssets } } } + public partial class GameModeMetaTable + { + public IReadOnlyDictionary GameModeMetaDataListbyId { get; private set; } + public void build() + { + if(GameModeMetaDataList != null) + { + GameModeMetaDataListbyId = new ReadOnlyDictionary(GameModeMetaDataList.ToDictionary(item => item.GameModeID)); + } + } + } + public partial class GameModeMetaTableMutable : ContentTableBase + { + public override void merge(GameModeMetaTableMutable table) + { + if(GameModeMetaDataList != null && table.GameModeMetaDataList != null) + { + GameModeMetaDataList = GameModeMetaDataList.Concat(table.GameModeMetaDataList).ToList(); + } + } + } + public partial class GameModeRewardMetaTable + { + public IReadOnlyDictionary GameModeRewardMetaDataListbyId { get; private set; } + public void build() + { + if(GameModeRewardMetaDataList != null) + { + GameModeRewardMetaDataListbyId = new ReadOnlyDictionary(GameModeRewardMetaDataList.ToDictionary(item => item.GameModeRewardID)); + } + } + } + public partial class GameModeRewardMetaTableMutable : ContentTableBase + { + public override void merge(GameModeRewardMetaTableMutable table) + { + if(GameModeRewardMetaDataList != null && table.GameModeRewardMetaDataList != null) + { + GameModeRewardMetaDataList = GameModeRewardMetaDataList.Concat(table.GameModeRewardMetaDataList).ToList(); + } + } + } + public partial class GameModeConditionMetaTable + { + public IReadOnlyDictionary GameModeConditionMetaDataListbyId { get; private set; } + public void build() + { + if(GameModeConditionMetaDataList != null) + { + GameModeConditionMetaDataListbyId = new ReadOnlyDictionary(GameModeConditionMetaDataList.ToDictionary(item => item.GameModeConditionID)); + } + } + } + public partial class GameModeConditionMetaTableMutable : ContentTableBase + { + public override void merge(GameModeConditionMetaTableMutable table) + { + if(GameModeConditionMetaDataList != null && table.GameModeConditionMetaDataList != null) + { + GameModeConditionMetaDataList = GameModeConditionMetaDataList.Concat(table.GameModeConditionMetaDataList).ToList(); + } + } + } + public partial class GameModeStartMetaTable + { + public IReadOnlyDictionary GameModeStartMetaDataListbyId { get; private set; } + public void build() + { + if(GameModeStartMetaDataList != null) + { + GameModeStartMetaDataListbyId = new ReadOnlyDictionary(GameModeStartMetaDataList.ToDictionary(item => item.GameModeStartID)); + } + } + } + public partial class GameModeStartMetaTableMutable : ContentTableBase + { + public override void merge(GameModeStartMetaTableMutable table) + { + if(GameModeStartMetaDataList != null && table.GameModeStartMetaDataList != null) + { + GameModeStartMetaDataList = GameModeStartMetaDataList.Concat(table.GameModeStartMetaDataList).ToList(); + } + } + } + public partial class GameModeTpsFfaMetaTable + { + public IReadOnlyDictionary GameModeTpsFfaMetaDataListbyId { get; private set; } + public void build() + { + if(GameModeTpsFfaMetaDataList != null) + { + GameModeTpsFfaMetaDataListbyId = new ReadOnlyDictionary(GameModeTpsFfaMetaDataList.ToDictionary(item => item.GameModeTpsFfaID)); + } + } + } + public partial class GameModeTpsFfaMetaTableMutable : ContentTableBase + { + public override void merge(GameModeTpsFfaMetaTableMutable table) + { + if(GameModeTpsFfaMetaDataList != null && table.GameModeTpsFfaMetaDataList != null) + { + GameModeTpsFfaMetaDataList = GameModeTpsFfaMetaDataList.Concat(table.GameModeTpsFfaMetaDataList).ToList(); + } + } + } + public partial class GameModeTeamMetaTable + { + public IReadOnlyDictionary GameModeTeamDataListbyGameModeTeamID { get; private set; } + public void build() + { + if(GameModeTeamDataList != null) + { + GameModeTeamDataListbyGameModeTeamID = new ReadOnlyDictionary(GameModeTeamDataList.ToDictionary(item => item.GameModeTeamID)); + } + } + } + public partial class GameModeTeamMetaTableMutable : ContentTableBase + { + public override void merge(GameModeTeamMetaTableMutable table) + { + if(GameModeTeamDataList != null && table.GameModeTeamDataList != null) + { + GameModeTeamDataList = GameModeTeamDataList.Concat(table.GameModeTeamDataList).ToList(); + } + } + } + public partial class GameModeMatchMetaTable + { + public IReadOnlyDictionary GameModeMatchDataListbyGameModeMatchID { get; private set; } + public void build() + { + if(GameModeMatchDataList != null) + { + GameModeMatchDataListbyGameModeMatchID = new ReadOnlyDictionary(GameModeMatchDataList.ToDictionary(item => item.GameModeMatchID)); + } + } + } + public partial class GameModeMatchMetaTableMutable : ContentTableBase + { + public override void merge(GameModeMatchMetaTableMutable table) + { + if(GameModeMatchDataList != null && table.GameModeMatchDataList != null) + { + GameModeMatchDataList = GameModeMatchDataList.Concat(table.GameModeMatchDataList).ToList(); + } + } + } + public partial class GameModeCommonMetaTable + { + public IReadOnlyDictionary GameModeCommonMetaDataListbyGameModeCommonID { get; private set; } + public void build() + { + if(GameModeCommonMetaDataList != null) + { + GameModeCommonMetaDataListbyGameModeCommonID = new ReadOnlyDictionary(GameModeCommonMetaDataList.ToDictionary(item => item.GameModeCommonID)); + } + } + } + public partial class GameModeCommonMetaTableMutable : ContentTableBase + { + public override void merge(GameModeCommonMetaTableMutable table) + { + if(GameModeCommonMetaDataList != null && table.GameModeCommonMetaDataList != null) + { + GameModeCommonMetaDataList = GameModeCommonMetaDataList.Concat(table.GameModeCommonMetaDataList).ToList(); + } + } + } + public partial class GameModeInstanceMetaTable + { + public IReadOnlyDictionary GameModeInstanceMetaDataListbyId { get; private set; } + public void build() + { + if(GameModeInstanceMetaDataList != null) + { + GameModeInstanceMetaDataListbyId = new ReadOnlyDictionary(GameModeInstanceMetaDataList.ToDictionary(item => item.ID)); + } + } + } + public partial class GameModeInstanceMetaTableMutable : ContentTableBase + { + public override void merge(GameModeInstanceMetaTableMutable table) + { + if(GameModeInstanceMetaDataList != null && table.GameModeInstanceMetaDataList != null) + { + GameModeInstanceMetaDataList = GameModeInstanceMetaDataList.Concat(table.GameModeInstanceMetaDataList).ToList(); + } + } + } + public partial class GameModeRunRaceMetaTable + { + public IReadOnlyDictionary GameModeRunRaceMetaDataListbyId { get; private set; } + public void build() + { + if(GameModeRunRaceMetaDataList != null) + { + GameModeRunRaceMetaDataListbyId = new ReadOnlyDictionary(GameModeRunRaceMetaDataList.ToDictionary(item => item.Id)); + } + } + } + public partial class GameModeRunRaceMetaTableMutable : ContentTableBase + { + public override void merge(GameModeRunRaceMetaTableMutable table) + { + if(GameModeRunRaceMetaDataList != null && table.GameModeRunRaceMetaDataList != null) + { + GameModeRunRaceMetaDataList = GameModeRunRaceMetaDataList.Concat(table.GameModeRunRaceMetaDataList).ToList(); + } + } + } + public partial class GameModeRunRaceCheckPointMonitorMetaTable + { + public IReadOnlyDictionary GameModeRunRaceCheckPointMonitorMetaDataListbyId { get; private set; } + public void build() + { + if(GameModeRunRaceCheckPointMonitorMetaDataList != null) + { + GameModeRunRaceCheckPointMonitorMetaDataListbyId = new ReadOnlyDictionary(GameModeRunRaceCheckPointMonitorMetaDataList.ToDictionary(item => item.Id)); + } + } + } + public partial class GameModeRunRaceCheckPointMonitorMetaTableMutable : ContentTableBase + { + public override void merge(GameModeRunRaceCheckPointMonitorMetaTableMutable table) + { + if(GameModeRunRaceCheckPointMonitorMetaDataList != null && table.GameModeRunRaceCheckPointMonitorMetaDataList != null) + { + GameModeRunRaceCheckPointMonitorMetaDataList = GameModeRunRaceCheckPointMonitorMetaDataList.Concat(table.GameModeRunRaceCheckPointMonitorMetaDataList).ToList(); + } + } + } + public partial class ContentsMenuDataTable + { + public IReadOnlyDictionary ContentsMenuDataListbyContents_ID { get; private set; } + public void build() + { + if(ContentsMenuDataList != null) + { + ContentsMenuDataListbyContents_ID = new ReadOnlyDictionary(ContentsMenuDataList.ToDictionary(item => item.Contents_ID)); + } + } + } + public partial class ContentsMenuDataTableMutable : ContentTableBase + { + public override void merge(ContentsMenuDataTableMutable table) + { + if(ContentsMenuDataList != null && table.ContentsMenuDataList != null) + { + ContentsMenuDataList = ContentsMenuDataList.Concat(table.ContentsMenuDataList).ToList(); + } + } + } + public partial class RankingDataTable + { + public IReadOnlyDictionary RankingDataListbyRankingId { get; private set; } + public void build() + { + if(RankingDataList != null) + { + RankingDataListbyRankingId = new ReadOnlyDictionary(RankingDataList.ToDictionary(item => item.RankingId)); + } + } + } + public partial class RankingDataTableMutable : ContentTableBase + { + public override void merge(RankingDataTableMutable table) + { + if(RankingDataList != null && table.RankingDataList != null) + { + RankingDataList = RankingDataList.Concat(table.RankingDataList).ToList(); + } + } + } + public partial class EventActionScoreMetaTable + { + public IReadOnlyDictionary EventActionScoreMetaDataListbyIndex { get; private set; } + public void build() + { + if(EventActionScoreMetaDataList != null) + { + EventActionScoreMetaDataListbyIndex = new ReadOnlyDictionary(EventActionScoreMetaDataList.ToDictionary(item => item.Index)); + } + } + } + public partial class EventActionScoreMetaTableMutable : ContentTableBase + { + public override void merge(EventActionScoreMetaTableMutable table) + { + if(EventActionScoreMetaDataList != null && table.EventActionScoreMetaDataList != null) + { + EventActionScoreMetaDataList = EventActionScoreMetaDataList.Concat(table.EventActionScoreMetaDataList).ToList(); + } + } + } } diff --git a/ServerCommon/MetaAssets/MetaTable/BattleObjectData.cs b/ServerCommon/MetaAssets/MetaTable/BattleObjectData.cs index 5298979..f8fd2b3 100644 --- a/ServerCommon/MetaAssets/MetaTable/BattleObjectData.cs +++ b/ServerCommon/MetaAssets/MetaTable/BattleObjectData.cs @@ -27,7 +27,7 @@ namespace MetaAssets [JsonProperty("RespawnTime")] public int RespawnTime { get; set; } [JsonProperty("Type")] - public EBattleObjectType ObjectType { get; set; } + public EGameModeObjectType ObjectType { get; set; } [JsonProperty("TypeID")] public int TypeID { get; set; } [JsonProperty("Value")] @@ -56,7 +56,7 @@ namespace MetaAssets public readonly string Desc; public readonly string AnchorBP; public readonly int RespawnTime; - public readonly EBattleObjectType ObjectType; + public readonly EGameModeObjectType ObjectType; public readonly int TypeID; public readonly int ObjectValue; public readonly int InteractionTime; diff --git a/ServerCommon/MetaAssets/MetaTable/ContentsMenuData.cs b/ServerCommon/MetaAssets/MetaTable/ContentsMenuData.cs new file mode 100644 index 0000000..5df1047 --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/ContentsMenuData.cs @@ -0,0 +1,99 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class ContentsMenuDataMutable + { + [JsonProperty("Contents_ID")] + public int Contents_ID { get; set; } + [JsonProperty("Contents_Name")] + public string Contents_Name { get; set; } + [JsonProperty("_description")] + public string _description { get; set; } + [JsonProperty("Contents_Category")] + public string Contents_Category { get; set; } + [JsonProperty("GameModeType")] + public GameModeType GameModeType { get; set; } + [JsonProperty("ConnectionType")] + public string ConnectionType { get; set; } + [JsonProperty("Img_thumbnail")] + public string Img_thumbnail { get; set; } + [JsonProperty("Warp_ID")] + public int Warp_ID { get; set; } + [JsonProperty("Use_require_quest")] + public string Use_require_quest { get; set; } + [JsonProperty("Notice")] + public bool Notice { get; set; } + [JsonProperty("Cost_Require")] + public bool Cost_Require { get; set; } + [JsonProperty("Contents_Cost")] + public double Contents_Cost { get; set; } + [JsonProperty("Order")] + public double Order { get; set; } + } + + public partial class ContentsMenuDataTableMutable + { + [JsonProperty("ContentsMenuDataList")] + public IList ContentsMenuDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class ContentsMenuData + { + public readonly int Contents_ID; + public readonly string Contents_Name; + public readonly string _description; + public readonly string Contents_Category; + public readonly GameModeType GameModeType; + public readonly string ConnectionType; + public readonly string Img_thumbnail; + public readonly int Warp_ID; + public readonly string Use_require_quest; + public readonly bool Notice; + public readonly bool Cost_Require; + public readonly double Contents_Cost; + public readonly double Order; + public ContentsMenuData(ContentsMenuDataMutable data) + { + Contents_ID = data.Contents_ID; + Contents_Name = data.Contents_Name; + _description = data._description; + Contents_Category = data.Contents_Category; + GameModeType = data.GameModeType; + ConnectionType = data.ConnectionType; + Img_thumbnail = data.Img_thumbnail; + Warp_ID = data.Warp_ID; + Use_require_quest = data.Use_require_quest; + Notice = data.Notice; + Cost_Require = data.Cost_Require; + Contents_Cost = data.Contents_Cost; + Order = data.Order; + } + } + + public partial class ContentsMenuDataTable + { + public readonly IReadOnlyList ContentsMenuDataList; + public ContentsMenuDataTable(ContentsMenuDataTableMutable data) + { + if(data.ContentsMenuDataList != null) + ContentsMenuDataList = data.ContentsMenuDataList.Select(x => new ContentsMenuData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/CraftingData.cs b/ServerCommon/MetaAssets/MetaTable/CraftingData.cs index b6c7fb4..2fbbf06 100644 --- a/ServerCommon/MetaAssets/MetaTable/CraftingData.cs +++ b/ServerCommon/MetaAssets/MetaTable/CraftingData.cs @@ -40,6 +40,8 @@ namespace MetaAssets public int Crafting_ItemId { get; set; } [JsonProperty("Crafting_ItemValue")] public int Crafting_ItemValue { get; set; } + [JsonProperty("Enable")] + public bool Enable { get; set; } [JsonProperty("RecipeType")] public ERecipeType RecipeType { get; set; } [JsonProperty("Recipe_ItemId")] @@ -56,6 +58,8 @@ namespace MetaAssets public int Beacon_ReduceTime { get; set; } [JsonProperty("Beacon_BonusItemId")] public int Beacon_BonusItemId { get; set; } + [JsonProperty("Beacon_BonusItemValue")] + public int Beacon_BonusItemValue { get; set; } [JsonProperty("max_craft_limit")] public int max_craft_limit { get; set; } } @@ -97,6 +101,7 @@ namespace MetaAssets public readonly EPropSmallType PropSmallType; public readonly int Crafting_ItemId; public readonly int Crafting_ItemValue; + public readonly bool Enable; public readonly ERecipeType RecipeType; public readonly int Recipe_ItemId; public readonly IReadOnlyList Material; @@ -105,6 +110,7 @@ namespace MetaAssets public readonly int CraftingTime; public readonly int Beacon_ReduceTime; public readonly int Beacon_BonusItemId; + public readonly int Beacon_BonusItemValue; public readonly int max_craft_limit; public CraftingMetaData(CraftingMetaDataMutable data) { @@ -112,6 +118,7 @@ namespace MetaAssets PropSmallType = data.PropSmallType; Crafting_ItemId = data.Crafting_ItemId; Crafting_ItemValue = data.Crafting_ItemValue; + Enable = data.Enable; RecipeType = data.RecipeType; Recipe_ItemId = data.Recipe_ItemId; if(data.Material != null) @@ -123,6 +130,7 @@ namespace MetaAssets CraftingTime = data.CraftingTime; Beacon_ReduceTime = data.Beacon_ReduceTime; Beacon_BonusItemId = data.Beacon_BonusItemId; + Beacon_BonusItemValue = data.Beacon_BonusItemValue; max_craft_limit = data.max_craft_limit; } } diff --git a/ServerCommon/MetaAssets/MetaTable/EventActionScoreData.cs b/ServerCommon/MetaAssets/MetaTable/EventActionScoreData.cs new file mode 100644 index 0000000..e7bca68 --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/EventActionScoreData.cs @@ -0,0 +1,99 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class EventActionScoreMetaDataMutable + { + [JsonProperty("Index")] + public int Index { get; set; } + [JsonProperty("desc_")] + public string desc_ { get; set; } + [JsonProperty("GroupId")] + public int GroupId { get; set; } + [JsonProperty("RankType")] + public string RankType { get; set; } + [JsonProperty("WorldEventType")] + public string WorldEventType { get; set; } + [JsonProperty("EventTarget")] + public string EventTarget { get; set; } + [JsonProperty("Event")] + public string Event { get; set; } + [JsonProperty("EventCondition1")] + public string EventCondition1 { get; set; } + [JsonProperty("EventCondition2")] + public string EventCondition2 { get; set; } + [JsonProperty("EventCondition3")] + public string EventCondition3 { get; set; } + [JsonProperty("Score")] + public int Score { get; set; } + [JsonProperty("ScoreModifyType")] + public string ScoreModifyType { get; set; } + [JsonProperty("Active")] + public bool Active { get; set; } + } + + public partial class EventActionScoreMetaTableMutable + { + [JsonProperty("EventActionScoreMetaDataList")] + public IList EventActionScoreMetaDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class EventActionScoreMetaData + { + public readonly int Index; + public readonly string desc_; + public readonly int GroupId; + public readonly string RankType; + public readonly string WorldEventType; + public readonly string EventTarget; + public readonly string Event; + public readonly string EventCondition1; + public readonly string EventCondition2; + public readonly string EventCondition3; + public readonly int Score; + public readonly string ScoreModifyType; + public readonly bool Active; + public EventActionScoreMetaData(EventActionScoreMetaDataMutable data) + { + Index = data.Index; + desc_ = data.desc_; + GroupId = data.GroupId; + RankType = data.RankType; + WorldEventType = data.WorldEventType; + EventTarget = data.EventTarget; + Event = data.Event; + EventCondition1 = data.EventCondition1; + EventCondition2 = data.EventCondition2; + EventCondition3 = data.EventCondition3; + Score = data.Score; + ScoreModifyType = data.ScoreModifyType; + Active = data.Active; + } + } + + public partial class EventActionScoreMetaTable + { + public readonly IReadOnlyList EventActionScoreMetaDataList; + public EventActionScoreMetaTable(EventActionScoreMetaTableMutable data) + { + if(data.EventActionScoreMetaDataList != null) + EventActionScoreMetaDataList = data.EventActionScoreMetaDataList.Select(x => new EventActionScoreMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeCommonData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeCommonData.cs new file mode 100644 index 0000000..3aa3b8d --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeCommonData.cs @@ -0,0 +1,83 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeCommonDataMutable + { + [JsonProperty("GameModeCommonID")] + public int GameModeCommonID { get; set; } + [JsonProperty("MaxLoadingWaitTime")] + public int MaxLoadingWaitTime { get; set; } + [JsonProperty("MaxStartWaitTime")] + public int MaxStartWaitTime { get; set; } + [JsonProperty("MaxAdditionalStartWaitTime")] + public int MaxAdditionalStartWaitTime { get; set; } + [JsonProperty("ProceedIfNoEnemies")] + public bool ProceedIfNoEnemies { get; set; } + [JsonProperty("MaxPlayTime")] + public int MaxPlayTime { get; set; } + [JsonProperty("LoadingKickDuration")] + public int LoadingKickDuration { get; set; } + [JsonProperty("GameReadyCount")] + public int GameReadyCount { get; set; } + [JsonProperty("ResultUIWaitTime")] + public int ResultUIWaitTime { get; set; } + } + + public partial class GameModeCommonMetaTableMutable + { + [JsonProperty("GameModeCommonMetaDataList")] + public IList GameModeCommonMetaDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeCommonData + { + public readonly int GameModeCommonID; + public readonly int MaxLoadingWaitTime; + public readonly int MaxStartWaitTime; + public readonly int MaxAdditionalStartWaitTime; + public readonly bool ProceedIfNoEnemies; + public readonly int MaxPlayTime; + public readonly int LoadingKickDuration; + public readonly int GameReadyCount; + public readonly int ResultUIWaitTime; + public GameModeCommonData(GameModeCommonDataMutable data) + { + GameModeCommonID = data.GameModeCommonID; + MaxLoadingWaitTime = data.MaxLoadingWaitTime; + MaxStartWaitTime = data.MaxStartWaitTime; + MaxAdditionalStartWaitTime = data.MaxAdditionalStartWaitTime; + ProceedIfNoEnemies = data.ProceedIfNoEnemies; + MaxPlayTime = data.MaxPlayTime; + LoadingKickDuration = data.LoadingKickDuration; + GameReadyCount = data.GameReadyCount; + ResultUIWaitTime = data.ResultUIWaitTime; + } + } + + public partial class GameModeCommonMetaTable + { + public readonly IReadOnlyList GameModeCommonMetaDataList; + public GameModeCommonMetaTable(GameModeCommonMetaTableMutable data) + { + if(data.GameModeCommonMetaDataList != null) + GameModeCommonMetaDataList = data.GameModeCommonMetaDataList.Select(x => new GameModeCommonData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeConditionData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeConditionData.cs new file mode 100644 index 0000000..5c53216 --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeConditionData.cs @@ -0,0 +1,71 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeConditionMetaDataMutable + { + [JsonProperty("GameModeConditionID")] + public int GameModeConditionID { get; set; } + [JsonProperty("GamdModeConditionDescription")] + public string GamdModeConditionDescription { get; set; } + [JsonProperty("GameModeConditionGroupID")] + public int GameModeConditionGroupID { get; set; } + [JsonProperty("ConditionType")] + public GameModeConditionType ConditionType { get; set; } + [JsonProperty("VariableName")] + public GameModeConditionVariableName VariableName { get; set; } + [JsonProperty("VariableValue")] + public double VariableValue { get; set; } + } + + public partial class GameModeConditionMetaTableMutable + { + [JsonProperty("GameModeConditionMetaDataList")] + public IList GameModeConditionMetaDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeConditionMetaData + { + public readonly int GameModeConditionID; + public readonly string GamdModeConditionDescription; + public readonly int GameModeConditionGroupID; + public readonly GameModeConditionType ConditionType; + public readonly GameModeConditionVariableName VariableName; + public readonly double VariableValue; + public GameModeConditionMetaData(GameModeConditionMetaDataMutable data) + { + GameModeConditionID = data.GameModeConditionID; + GamdModeConditionDescription = data.GamdModeConditionDescription; + GameModeConditionGroupID = data.GameModeConditionGroupID; + ConditionType = data.ConditionType; + VariableName = data.VariableName; + VariableValue = data.VariableValue; + } + } + + public partial class GameModeConditionMetaTable + { + public readonly IReadOnlyList GameModeConditionMetaDataList; + public GameModeConditionMetaTable(GameModeConditionMetaTableMutable data) + { + if(data.GameModeConditionMetaDataList != null) + GameModeConditionMetaDataList = data.GameModeConditionMetaDataList.Select(x => new GameModeConditionMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeData.cs new file mode 100644 index 0000000..51a3176 --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeData.cs @@ -0,0 +1,99 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeMetaDataMutable + { + [JsonProperty("GameModeID")] + public int GameModeID { get; set; } + [JsonProperty("GameModeDescription")] + public string GameModeDescription { get; set; } + [JsonProperty("GameGenreType")] + public GameGenreType GameGenreType { get; set; } + [JsonProperty("GameModeType")] + public GameModeType GameModeType { get; set; } + [JsonProperty("GameModeConfigID")] + public int GameModeConfigID { get; set; } + [JsonProperty("GameModeCommonID")] + public int GameModeCommonID { get; set; } + [JsonProperty("InstanceID")] + public int InstanceID { get; set; } + [JsonProperty("GameModeMatchID")] + public int GameModeMatchID { get; set; } + [JsonProperty("GameModeRewardGroupID")] + public int GameModeRewardGroupID { get; set; } + [JsonProperty("GameModeOptionID")] + public int GameModeOptionID { get; set; } + [JsonProperty("GameModeStartID")] + public int GameModeStartID { get; set; } + [JsonProperty("GameModeTeamID")] + public int GameModeTeamID { get; set; } + [JsonProperty("InstanceGroupID")] + public int InstanceGroupID { get; set; } + } + + public partial class GameModeMetaTableMutable + { + [JsonProperty("GameModeMetaDataList")] + public IList GameModeMetaDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeMetaData + { + public readonly int GameModeID; + public readonly string GameModeDescription; + public readonly GameGenreType GameGenreType; + public readonly GameModeType GameModeType; + public readonly int GameModeConfigID; + public readonly int GameModeCommonID; + public readonly int InstanceID; + public readonly int GameModeMatchID; + public readonly int GameModeRewardGroupID; + public readonly int GameModeOptionID; + public readonly int GameModeStartID; + public readonly int GameModeTeamID; + public readonly int InstanceGroupID; + public GameModeMetaData(GameModeMetaDataMutable data) + { + GameModeID = data.GameModeID; + GameModeDescription = data.GameModeDescription; + GameGenreType = data.GameGenreType; + GameModeType = data.GameModeType; + GameModeConfigID = data.GameModeConfigID; + GameModeCommonID = data.GameModeCommonID; + InstanceID = data.InstanceID; + GameModeMatchID = data.GameModeMatchID; + GameModeRewardGroupID = data.GameModeRewardGroupID; + GameModeOptionID = data.GameModeOptionID; + GameModeStartID = data.GameModeStartID; + GameModeTeamID = data.GameModeTeamID; + InstanceGroupID = data.InstanceGroupID; + } + } + + public partial class GameModeMetaTable + { + public readonly IReadOnlyList GameModeMetaDataList; + public GameModeMetaTable(GameModeMetaTableMutable data) + { + if(data.GameModeMetaDataList != null) + GameModeMetaDataList = data.GameModeMetaDataList.Select(x => new GameModeMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeInstanceData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeInstanceData.cs new file mode 100644 index 0000000..be49424 --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeInstanceData.cs @@ -0,0 +1,71 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeInstanceMetaDataMutable + { + [JsonProperty("ID")] + public int ID { get; set; } + [JsonProperty("GroupID")] + public int GroupID { get; set; } + [JsonProperty("InstanceID")] + public int InstanceID { get; set; } + [JsonProperty("Probability")] + public int Probability { get; set; } + [JsonProperty("NextInstanceGroupId")] + public int NextInstanceGroupId { get; set; } + [JsonProperty("SlotOrder")] + public int SlotOrder { get; set; } + } + + public partial class GameModeInstanceMetaTableMutable + { + [JsonProperty("GameModeInstanceMetaDataList")] + public IList GameModeInstanceMetaDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeInstanceMetaData + { + public readonly int ID; + public readonly int GroupID; + public readonly int InstanceID; + public readonly int Probability; + public readonly int NextInstanceGroupId; + public readonly int SlotOrder; + public GameModeInstanceMetaData(GameModeInstanceMetaDataMutable data) + { + ID = data.ID; + GroupID = data.GroupID; + InstanceID = data.InstanceID; + Probability = data.Probability; + NextInstanceGroupId = data.NextInstanceGroupId; + SlotOrder = data.SlotOrder; + } + } + + public partial class GameModeInstanceMetaTable + { + public readonly IReadOnlyList GameModeInstanceMetaDataList; + public GameModeInstanceMetaTable(GameModeInstanceMetaTableMutable data) + { + if(data.GameModeInstanceMetaDataList != null) + GameModeInstanceMetaDataList = data.GameModeInstanceMetaDataList.Select(x => new GameModeInstanceMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeMatchData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeMatchData.cs new file mode 100644 index 0000000..a1f60ec --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeMatchData.cs @@ -0,0 +1,92 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeMatchMetaDataMutable + { + [JsonProperty("GameModeMatchID")] + public int GameModeMatchID { get; set; } + [JsonProperty("GameModeMatchDescription")] + public string GameModeMatchDescription { get; set; } + [JsonProperty("MmrCheck")] + public bool MmrCheck { get; set; } + [JsonProperty("PartyMatchPlayerCountAbleArray")] + public IList PartyMatchPlayerCountAbleArray { get; set; } + [JsonProperty("MatchWaitTimeSec")] + public int MatchWaitTimeSec { get; set; } + [JsonProperty("GameModeMmrID")] + public int GameModeMmrID { get; set; } + [JsonProperty("MatchRetryCount")] + public int MatchRetryCount { get; set; } + [JsonProperty("MmrExpansionPhase")] + public int MmrExpansionPhase { get; set; } + [JsonProperty("TeamAssignment")] + public TeamAssignmentType TeamAssignment { get; set; } + [JsonProperty("JoinInProgress")] + public JoinInProgressType JoinInProgress { get; set; } + [JsonProperty("JoinInMaxTimeSec")] + public int JoinInMaxTimeSec { get; set; } + } + + public partial class GameModeMatchMetaTableMutable + { + [JsonProperty("GameModeMatchDataList")] + public IList GameModeMatchDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeMatchMetaData + { + public readonly int GameModeMatchID; + public readonly string GameModeMatchDescription; + public readonly bool MmrCheck; + public readonly IReadOnlyList PartyMatchPlayerCountAbleArray; + public readonly int MatchWaitTimeSec; + public readonly int GameModeMmrID; + public readonly int MatchRetryCount; + public readonly int MmrExpansionPhase; + public readonly TeamAssignmentType TeamAssignment; + public readonly JoinInProgressType JoinInProgress; + public readonly int JoinInMaxTimeSec; + public GameModeMatchMetaData(GameModeMatchMetaDataMutable data) + { + GameModeMatchID = data.GameModeMatchID; + GameModeMatchDescription = data.GameModeMatchDescription; + MmrCheck = data.MmrCheck; + if(data.PartyMatchPlayerCountAbleArray != null) + PartyMatchPlayerCountAbleArray = data.PartyMatchPlayerCountAbleArray.ToList().AsReadOnly(); + MatchWaitTimeSec = data.MatchWaitTimeSec; + GameModeMmrID = data.GameModeMmrID; + MatchRetryCount = data.MatchRetryCount; + MmrExpansionPhase = data.MmrExpansionPhase; + TeamAssignment = data.TeamAssignment; + JoinInProgress = data.JoinInProgress; + JoinInMaxTimeSec = data.JoinInMaxTimeSec; + } + } + + public partial class GameModeMatchMetaTable + { + public readonly IReadOnlyList GameModeMatchDataList; + public GameModeMatchMetaTable(GameModeMatchMetaTableMutable data) + { + if(data.GameModeMatchDataList != null) + GameModeMatchDataList = data.GameModeMatchDataList.Select(x => new GameModeMatchMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeRewardData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeRewardData.cs new file mode 100644 index 0000000..8ba91a2 --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeRewardData.cs @@ -0,0 +1,75 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeRewardMetaDataMutable + { + [JsonProperty("GameModeRewardID")] + public int GameModeRewardID { get; set; } + [JsonProperty("GameModeRewardDescription")] + public string GameModeRewardDescription { get; set; } + [JsonProperty("GameModeRewardGroupID")] + public int GameModeRewardGroupID { get; set; } + [JsonProperty("CompesationMoment")] + public GameModeCompesationType CompesationMoment { get; set; } + [JsonProperty("GameModeConditionGroupID")] + public int GameModeConditionGroupID { get; set; } + [JsonProperty("RewardGroupID")] + public int RewardGroupID { get; set; } + [JsonProperty("RewardRate")] + public double RewardRate { get; set; } + } + + public partial class GameModeRewardMetaTableMutable + { + [JsonProperty("GameModeRewardMetaDataList")] + public IList GameModeRewardMetaDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeRewardMetaData + { + public readonly int GameModeRewardID; + public readonly string GameModeRewardDescription; + public readonly int GameModeRewardGroupID; + public readonly GameModeCompesationType CompesationMoment; + public readonly int GameModeConditionGroupID; + public readonly int RewardGroupID; + public readonly double RewardRate; + public GameModeRewardMetaData(GameModeRewardMetaDataMutable data) + { + GameModeRewardID = data.GameModeRewardID; + GameModeRewardDescription = data.GameModeRewardDescription; + GameModeRewardGroupID = data.GameModeRewardGroupID; + CompesationMoment = data.CompesationMoment; + GameModeConditionGroupID = data.GameModeConditionGroupID; + RewardGroupID = data.RewardGroupID; + RewardRate = data.RewardRate; + } + } + + public partial class GameModeRewardMetaTable + { + public readonly IReadOnlyList GameModeRewardMetaDataList; + public GameModeRewardMetaTable(GameModeRewardMetaTableMutable data) + { + if(data.GameModeRewardMetaDataList != null) + GameModeRewardMetaDataList = data.GameModeRewardMetaDataList.Select(x => new GameModeRewardMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeRunRaceCheckPointMonitorData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeRunRaceCheckPointMonitorData.cs new file mode 100644 index 0000000..75d6fbc --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeRunRaceCheckPointMonitorData.cs @@ -0,0 +1,63 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeRunRaceCheckPointMonitorMetaDataMutable + { + [JsonProperty("Id")] + public int Id { get; set; } + [JsonProperty("InstanceId")] + public int InstanceId { get; set; } + [JsonProperty("CheckPointId")] + public int CheckPointId { get; set; } + [JsonProperty("TransitTimeNormalRange")] + public int TransitTimeNormalRange { get; set; } + } + + public partial class GameModeRunRaceCheckPointMonitorMetaTableMutable + { + [JsonProperty("GameModeRunRaceCheckPointMonitorMetaDataList")] + public IList GameModeRunRaceCheckPointMonitorMetaDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeRunRaceCheckPointMonitorMetaData + { + public readonly int Id; + public readonly int InstanceId; + public readonly int CheckPointId; + public readonly int TransitTimeNormalRange; + public GameModeRunRaceCheckPointMonitorMetaData(GameModeRunRaceCheckPointMonitorMetaDataMutable data) + { + Id = data.Id; + InstanceId = data.InstanceId; + CheckPointId = data.CheckPointId; + TransitTimeNormalRange = data.TransitTimeNormalRange; + } + } + + public partial class GameModeRunRaceCheckPointMonitorMetaTable + { + public readonly IReadOnlyList GameModeRunRaceCheckPointMonitorMetaDataList; + public GameModeRunRaceCheckPointMonitorMetaTable(GameModeRunRaceCheckPointMonitorMetaTableMutable data) + { + if(data.GameModeRunRaceCheckPointMonitorMetaDataList != null) + GameModeRunRaceCheckPointMonitorMetaDataList = data.GameModeRunRaceCheckPointMonitorMetaDataList.Select(x => new GameModeRunRaceCheckPointMonitorMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeRunRaceData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeRunRaceData.cs new file mode 100644 index 0000000..b551cec --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeRunRaceData.cs @@ -0,0 +1,75 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeRunRaceMetaDataMutable + { + [JsonProperty("Id")] + public int Id { get; set; } + [JsonProperty("FinalTimer")] + public int FinalTimer { get; set; } + [JsonProperty("PenaltyQuitCount")] + public int PenaltyQuitCount { get; set; } + [JsonProperty("PenaltyTime")] + public int PenaltyTime { get; set; } + [JsonProperty("MatchCountDeductionPoint")] + public int MatchCountDeductionPoint { get; set; } + [JsonProperty("ResultSceneTransitionWaitTime")] + public int ResultSceneTransitionWaitTime { get; set; } + [JsonProperty("RespawnCntLimit")] + public int RespawnCntLimit { get; set; } + } + + public partial class GameModeRunRaceMetaTableMutable + { + [JsonProperty("GameModeRunRaceMetaDataList")] + public IList GameModeRunRaceMetaDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeRunRaceMetaData + { + public readonly int Id; + public readonly int FinalTimer; + public readonly int PenaltyQuitCount; + public readonly int PenaltyTime; + public readonly int MatchCountDeductionPoint; + public readonly int ResultSceneTransitionWaitTime; + public readonly int RespawnCntLimit; + public GameModeRunRaceMetaData(GameModeRunRaceMetaDataMutable data) + { + Id = data.Id; + FinalTimer = data.FinalTimer; + PenaltyQuitCount = data.PenaltyQuitCount; + PenaltyTime = data.PenaltyTime; + MatchCountDeductionPoint = data.MatchCountDeductionPoint; + ResultSceneTransitionWaitTime = data.ResultSceneTransitionWaitTime; + RespawnCntLimit = data.RespawnCntLimit; + } + } + + public partial class GameModeRunRaceMetaTable + { + public readonly IReadOnlyList GameModeRunRaceMetaDataList; + public GameModeRunRaceMetaTable(GameModeRunRaceMetaTableMutable data) + { + if(data.GameModeRunRaceMetaDataList != null) + GameModeRunRaceMetaDataList = data.GameModeRunRaceMetaDataList.Select(x => new GameModeRunRaceMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeStartData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeStartData.cs new file mode 100644 index 0000000..38ec2b9 --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeStartData.cs @@ -0,0 +1,67 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeStartMetaDataMutable + { + [JsonProperty("GameModeStartID")] + public int GameModeStartID { get; set; } + [JsonProperty("GameModeStartDescription")] + public string GameModeStartDescription { get; set; } + [JsonProperty("CostCurrencyType")] + public CurrencyType CostCurrencyType { get; set; } + [JsonProperty("CostCurrencyCount")] + public int CostCurrencyCount { get; set; } + [JsonProperty("DailyJoinLimit")] + public int DailyJoinLimit { get; set; } + } + + public partial class GameModeStartMetaTableMutable + { + [JsonProperty("GameModeStartMetaDataList")] + public IList GameModeStartMetaDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeStartMetaData + { + public readonly int GameModeStartID; + public readonly string GameModeStartDescription; + public readonly CurrencyType CostCurrencyType; + public readonly int CostCurrencyCount; + public readonly int DailyJoinLimit; + public GameModeStartMetaData(GameModeStartMetaDataMutable data) + { + GameModeStartID = data.GameModeStartID; + GameModeStartDescription = data.GameModeStartDescription; + CostCurrencyType = data.CostCurrencyType; + CostCurrencyCount = data.CostCurrencyCount; + DailyJoinLimit = data.DailyJoinLimit; + } + } + + public partial class GameModeStartMetaTable + { + public readonly IReadOnlyList GameModeStartMetaDataList; + public GameModeStartMetaTable(GameModeStartMetaTableMutable data) + { + if(data.GameModeStartMetaDataList != null) + GameModeStartMetaDataList = data.GameModeStartMetaDataList.Select(x => new GameModeStartMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeTeamData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeTeamData.cs new file mode 100644 index 0000000..01d5471 --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeTeamData.cs @@ -0,0 +1,73 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeTeamMetaDataMutable + { + [JsonProperty("GameModeTeamID")] + public int GameModeTeamID { get; set; } + [JsonProperty("MinTeam")] + public int MinTeam { get; set; } + [JsonProperty("MaxTeam")] + public int MaxTeam { get; set; } + [JsonProperty("MinTeamPlayer")] + public IList MinTeamPlayer { get; set; } + [JsonProperty("MaxTeamPlayer")] + public IList MaxTeamPlayer { get; set; } + [JsonProperty("AnchorAssignmentType")] + public AnchorAssignmentType AnchorAssignmentType { get; set; } + } + + public partial class GameModeTeamMetaTableMutable + { + [JsonProperty("GameModeTeamDataList")] + public IList GameModeTeamDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeTeamMetaData + { + public readonly int GameModeTeamID; + public readonly int MinTeam; + public readonly int MaxTeam; + public readonly IReadOnlyList MinTeamPlayer; + public readonly IReadOnlyList MaxTeamPlayer; + public readonly AnchorAssignmentType AnchorAssignmentType; + public GameModeTeamMetaData(GameModeTeamMetaDataMutable data) + { + GameModeTeamID = data.GameModeTeamID; + MinTeam = data.MinTeam; + MaxTeam = data.MaxTeam; + if(data.MinTeamPlayer != null) + MinTeamPlayer = data.MinTeamPlayer.ToList().AsReadOnly(); + if(data.MaxTeamPlayer != null) + MaxTeamPlayer = data.MaxTeamPlayer.ToList().AsReadOnly(); + AnchorAssignmentType = data.AnchorAssignmentType; + } + } + + public partial class GameModeTeamMetaTable + { + public readonly IReadOnlyList GameModeTeamDataList; + public GameModeTeamMetaTable(GameModeTeamMetaTableMutable data) + { + if(data.GameModeTeamDataList != null) + GameModeTeamDataList = data.GameModeTeamDataList.Select(x => new GameModeTeamMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/GameModeTpsFfaData.cs b/ServerCommon/MetaAssets/MetaTable/GameModeTpsFfaData.cs new file mode 100644 index 0000000..4b236c1 --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/GameModeTpsFfaData.cs @@ -0,0 +1,83 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class GameModeTpsFfaMetaDataMutable + { + [JsonProperty("GameModeTpsFfaID")] + public int GameModeTpsFfaID { get; set; } + [JsonProperty("GameModeTpsFfaDescription")] + public string GameModeTpsFfaDescription { get; set; } + [JsonProperty("PlayerRespawnTime")] + public int PlayerRespawnTime { get; set; } + [JsonProperty("DefaultRoundCount")] + public int DefaultRoundCount { get; set; } + [JsonProperty("RoundTime")] + public int RoundTime { get; set; } + [JsonProperty("NextRoundWaitTime")] + public int NextRoundWaitTime { get; set; } + [JsonProperty("ResultUIWaitTime")] + public int ResultUIWaitTime { get; set; } + [JsonProperty("GetRewardTime")] + public int GetRewardTime { get; set; } + [JsonProperty("DefaultWeapons")] + public string DefaultWeapons { get; set; } + } + + public partial class GameModeTpsFfaMetaTableMutable + { + [JsonProperty("GameModeTpsFfaMetaDataList")] + public IList GameModeTpsFfaMetaDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class GameModeTpsFfaMetaData + { + public readonly int GameModeTpsFfaID; + public readonly string GameModeTpsFfaDescription; + public readonly int PlayerRespawnTime; + public readonly int DefaultRoundCount; + public readonly int RoundTime; + public readonly int NextRoundWaitTime; + public readonly int ResultUIWaitTime; + public readonly int GetRewardTime; + public readonly string DefaultWeapons; + public GameModeTpsFfaMetaData(GameModeTpsFfaMetaDataMutable data) + { + GameModeTpsFfaID = data.GameModeTpsFfaID; + GameModeTpsFfaDescription = data.GameModeTpsFfaDescription; + PlayerRespawnTime = data.PlayerRespawnTime; + DefaultRoundCount = data.DefaultRoundCount; + RoundTime = data.RoundTime; + NextRoundWaitTime = data.NextRoundWaitTime; + ResultUIWaitTime = data.ResultUIWaitTime; + GetRewardTime = data.GetRewardTime; + DefaultWeapons = data.DefaultWeapons; + } + } + + public partial class GameModeTpsFfaMetaTable + { + public readonly IReadOnlyList GameModeTpsFfaMetaDataList; + public GameModeTpsFfaMetaTable(GameModeTpsFfaMetaTableMutable data) + { + if(data.GameModeTpsFfaMetaDataList != null) + GameModeTpsFfaMetaDataList = data.GameModeTpsFfaMetaDataList.Select(x => new GameModeTpsFfaMetaData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/RankingData.cs b/ServerCommon/MetaAssets/MetaTable/RankingData.cs new file mode 100644 index 0000000..d7f9b5e --- /dev/null +++ b/ServerCommon/MetaAssets/MetaTable/RankingData.cs @@ -0,0 +1,83 @@ +// +// generated using ContentTool. DO NOT EDIT! +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; + +namespace MetaAssets +{ + #pragma warning disable + + public partial class RankingDataMutable + { + [JsonProperty("RankingId")] + public int RankingId { get; set; } + [JsonProperty("desc_")] + public string desc_ { get; set; } + [JsonProperty("RankingType")] + public RankingType RankingType { get; set; } + [JsonProperty("TypeId")] + public double TypeId { get; set; } + [JsonProperty("TypeValue")] + public double TypeValue { get; set; } + [JsonProperty("EventType")] + public EventType EventType { get; set; } + [JsonProperty("SortType")] + public SortType SortType { get; set; } + [JsonProperty("ScoreType")] + public ScoreType ScoreType { get; set; } + [JsonProperty("ScoreModifyType")] + public ScoreModifyType ScoreModifyType { get; set; } + } + + public partial class RankingDataTableMutable + { + [JsonProperty("RankingDataList")] + public IList RankingDataList { get; set; } + } + + ////////////////////////////// + // readonly class + ////////////////////////////// + public partial class RankingData + { + public readonly int RankingId; + public readonly string desc_; + public readonly RankingType RankingType; + public readonly double TypeId; + public readonly double TypeValue; + public readonly EventType EventType; + public readonly SortType SortType; + public readonly ScoreType ScoreType; + public readonly ScoreModifyType ScoreModifyType; + public RankingData(RankingDataMutable data) + { + RankingId = data.RankingId; + desc_ = data.desc_; + RankingType = data.RankingType; + TypeId = data.TypeId; + TypeValue = data.TypeValue; + EventType = data.EventType; + SortType = data.SortType; + ScoreType = data.ScoreType; + ScoreModifyType = data.ScoreModifyType; + } + } + + public partial class RankingDataTable + { + public readonly IReadOnlyList RankingDataList; + public RankingDataTable(RankingDataTableMutable data) + { + if(data.RankingDataList != null) + RankingDataList = data.RankingDataList.Select(x => new RankingData(x)).ToList().AsReadOnly(); + } + } + +} diff --git a/ServerCommon/MetaAssets/MetaTable/ShopProductData.cs b/ServerCommon/MetaAssets/MetaTable/ShopProductData.cs index 8c6fcfe..556a50d 100644 --- a/ServerCommon/MetaAssets/MetaTable/ShopProductData.cs +++ b/ServerCommon/MetaAssets/MetaTable/ShopProductData.cs @@ -32,6 +32,8 @@ namespace MetaAssets public string Description { get; set; } [JsonProperty("Group_Id")] public int Group_Id { get; set; } + [JsonProperty("Enable")] + public bool Enable { get; set; } [JsonProperty("Is_Random")] public bool Is_Random { get; set; } [JsonProperty("Weight")] @@ -82,6 +84,7 @@ namespace MetaAssets public readonly string Name; public readonly string Description; public readonly int Group_Id; + public readonly bool Enable; public readonly bool Is_Random; public readonly int Weight; public readonly ProductData ProductData; @@ -98,6 +101,7 @@ namespace MetaAssets Name = data.Name; Description = data.Description; Group_Id = data.Group_Id; + Enable = data.Enable; Is_Random = data.Is_Random; Weight = data.Weight; if(data.ProductData != null) diff --git a/ServerCommon/MetaAssets/MetaTable/TaxiData.cs b/ServerCommon/MetaAssets/MetaTable/TaxiData.cs index 3691810..e76a59f 100644 --- a/ServerCommon/MetaAssets/MetaTable/TaxiData.cs +++ b/ServerCommon/MetaAssets/MetaTable/TaxiData.cs @@ -50,6 +50,8 @@ namespace MetaAssets public double guideoffset_ { get; set; } [JsonProperty("Unloading_cost")] public int UnloadingCost { get; set; } + [JsonProperty("menu_cost")] + public int MenuCost { get; set; } [JsonProperty("Unloading_world_id")] public int UnloadingWorldId { get; set; } [JsonProperty("Unloading_land_id")] @@ -108,6 +110,7 @@ namespace MetaAssets public readonly double guideheight_; public readonly double guideoffset_; public readonly int UnloadingCost; + public readonly int MenuCost; public readonly int UnloadingWorldId; public readonly int UnloadingLandId; public readonly int UnloadingFloorId; @@ -141,6 +144,7 @@ namespace MetaAssets guideheight_ = data.guideheight_; guideoffset_ = data.guideoffset_; UnloadingCost = data.UnloadingCost; + MenuCost = data.MenuCost; UnloadingWorldId = data.UnloadingWorldId; UnloadingLandId = data.UnloadingLandId; UnloadingFloorId = data.UnloadingFloorId; diff --git a/ServerCommon/MetaData/GameMode/GameModeAllMeta.cs b/ServerCommon/MetaData/GameMode/GameModeAllMeta.cs new file mode 100644 index 0000000..51fe325 --- /dev/null +++ b/ServerCommon/MetaData/GameMode/GameModeAllMeta.cs @@ -0,0 +1,42 @@ +using MetaAssets; +using CONDITION_GROUP_ID = System.Int32; +namespace ServerCommon.GameMode; + +public class GameModeAllMeta +{ + public readonly int m_game_mode_id; + public readonly GameModeType m_game_mode_type; + public readonly int m_instance_id; + public readonly int m_config_id; + public readonly List m_condition_metas; + public readonly Dictionary m_condition_reward_metas; //이거가 보상 조건일수도 있고 그냥 게임 룰일 수도 있다... + public readonly IGameModeDetailMeta m_detail_meta; + + public readonly GameModeOptionMetaData m_option_meta; + public readonly GameModeStartMetaData m_start_meta; + public readonly GameModeCommonData m_common_data; + + + public GameModeAllMeta(int gameModeId, GameModeType gameModeType, int instanceId, int configId, + List conditionMetas, + Dictionary conditionRewardMetas, + GameModeOptionMetaData optionMeta, + GameModeStartMetaData startMeta, + GameModeCommonData commonData, + IGameModeDetailMeta detailMeta) + { + m_game_mode_id = gameModeId; + m_game_mode_type = gameModeType; + m_instance_id = instanceId; + m_config_id = configId; + m_condition_metas = conditionMetas; + m_condition_reward_metas = conditionRewardMetas; + m_option_meta = optionMeta; + m_start_meta = startMeta; + m_common_data = commonData; + m_detail_meta = detailMeta; + } +} + + + diff --git a/ServerCommon/MetaData/GameMode/GameModeConditionRewardMeta.cs b/ServerCommon/MetaData/GameMode/GameModeConditionRewardMeta.cs new file mode 100644 index 0000000..be14a9b --- /dev/null +++ b/ServerCommon/MetaData/GameMode/GameModeConditionRewardMeta.cs @@ -0,0 +1,27 @@ +using MetaAssets; + +namespace ServerCommon.GameMode; + +public class GameModeConditionRewardMeta +{ + public int m_condition_group_id { get; } = 0; + public int m_reward_group_id { get; }= 0; + public GameModeCompesationType m_compesation_type { get; } = GameModeCompesationType.None; + public double m_reward_rate { get; }= 0; + public List m_conditions { get; }= new (); + + public GameModeConditionRewardMeta(int conditionGroupId, int rewardGroupId, GameModeCompesationType compesationType, double rewardRate, List conditions) + { + m_condition_group_id = conditionGroupId; + m_reward_group_id = rewardGroupId; + m_compesation_type = compesationType; + m_reward_rate = rewardRate; + m_conditions = conditions; + } + +} + + + + + diff --git a/ServerCommon/MetaData/GameMode/GameModeMeta.cs b/ServerCommon/MetaData/GameMode/GameModeMeta.cs new file mode 100644 index 0000000..6ca577c --- /dev/null +++ b/ServerCommon/MetaData/GameMode/GameModeMeta.cs @@ -0,0 +1,6 @@ +namespace ServerCommon.GameMode; + +public class GameModeMeta +{ + +} \ No newline at end of file diff --git a/ServerCommon/MetaData/GameMode/GameModeMetaHelper.cs b/ServerCommon/MetaData/GameMode/GameModeMetaHelper.cs new file mode 100644 index 0000000..7b36bb9 --- /dev/null +++ b/ServerCommon/MetaData/GameMode/GameModeMetaHelper.cs @@ -0,0 +1,152 @@ +using MetaAssets; +using ServerCore; + +using CONDITION_GROUP_ID = System.Int32; + +namespace ServerCommon.GameMode; + +public static class GameModeMetaHelper +{ + public static IReadOnlyDictionary LoadGameModeMetaDatas() + { + //리워드 그룹 쪼개 놓기 + var rewards_by_group = makeRewardsByGroup(MetaData.Instance.Meta.GameModeRewardMetaTable); + //컨디션 그룹 쪼개 놓기 + var conditions_by_group = makeConditionsByGroup(MetaData.Instance.Meta.GameModeConditionMetaTable); + + var metas = new Dictionary(); + foreach (var game_mode_meta in MetaData.Instance.Meta.GameModeMetaTable.GameModeMetaDataList) + { + bool is_validate = true; + + var game_mode_id = game_mode_meta.GameModeID; + var game_mode_type = game_mode_meta.GameModeType; + var instance_id = game_mode_meta.InstanceID; + var reward_group_id = game_mode_meta.GameModeRewardGroupID; + var config_id = game_mode_meta.GameModeConfigID; + var match_id = game_mode_meta.GameModeMatchID; //kihoo todo : 이건 아직 테이블 안올라옴 나중에 추가 할것 + + if (false == rewards_by_group.TryGetValue(reward_group_id, out var rewardMetas)) + { + Log.getLogger().error($"rewardMeta is null!!!! check MetaData!!! reward_group_id : {reward_group_id} "); + is_validate = false; + continue; + } + + var condition_metas = new List(); + var condition_reward_metas = new Dictionary(); + foreach (var reward_meta in rewardMetas) + { + var group_id = reward_meta.GameModeConditionGroupID; + if (false == conditions_by_group.TryGetValue(group_id, out var conditionMeta)) + { + Log.getLogger().error($"conditionMeta is null!!!! check MetaData!!! group_id : {reward_group_id} "); + is_validate = false; + break; + } + var condition_reward_meta = new GameModeConditionRewardMeta(group_id, reward_meta.RewardGroupID, reward_meta.CompesationMoment, reward_meta.RewardRate, conditionMeta); + condition_reward_metas.TryAdd(group_id, condition_reward_meta); + condition_metas.AddRange(conditionMeta); + } + if (is_validate == false) continue; + + + + var option_id = game_mode_meta.GameModeOptionID; + if (false == MetaData.Instance.Meta.GameModeOptionMetaTable.GameModeOptionDataListbyGameModeOptionId.TryGetValue(option_id, out var optionMeta)) + { + Log.getLogger().error($"optionMeta is null!!!! check MetaData!!! option_id : {option_id} "); + is_validate = false; + continue; + } + + + var start_id = game_mode_meta.GameModeStartID; + if (false == MetaData.Instance.Meta.GameModeStartMetaTable.GameModeStartMetaDataListbyId.TryGetValue(start_id, out var startMeta)) + { + Log.getLogger().error($"startMeta is null!!!! check MetaData!!! start_id : {start_id} "); + is_validate = false; + continue; + } + + var common_id = game_mode_meta.GameModeCommonID; + if (false == MetaData.Instance.Meta.GameModeCommonMetaTable.GameModeCommonMetaDataListbyGameModeCommonID.TryGetValue(common_id, out var commonMeta)) + { + Log.getLogger().error($"commonMeta is null!!!! check MetaData!!! common_id : {common_id} "); + is_validate = false; + continue; + } + + var detail_meta = loadDetailMeta(game_mode_type); + detail_meta.loadData(rewardMetas, conditions_by_group, config_id); + var all_meta = new GameModeAllMeta(game_mode_id,game_mode_type, instance_id, config_id, condition_metas, condition_reward_metas, optionMeta, startMeta, commonMeta, detail_meta); + metas.Add(game_mode_id, all_meta); + } + + return metas; + } + + + private static Dictionary> makeRewardsByGroup(GameModeRewardMetaTable gameModeRewardMetaTable) + { + Dictionary> rewards_by_group = new(); + foreach (var reward_group in gameModeRewardMetaTable.GameModeRewardMetaDataList) + { + var group_id = reward_group.GameModeRewardGroupID; + + if (false == rewards_by_group.TryGetValue(group_id, out var rewardMeta)) + { + rewardMeta = new List(); + } + rewardMeta.Add(reward_group); + + rewards_by_group.TryAdd(group_id, rewardMeta); + } + + return rewards_by_group; + } + + + private static Dictionary> makeConditionsByGroup(GameModeConditionMetaTable gameModeConditionMetaTable) + { + Dictionary> conditions_by_group = new(); + foreach (var condition_group in gameModeConditionMetaTable.GameModeConditionMetaDataList) + { + var condition_group_id = condition_group.GameModeConditionGroupID; + + if (false == conditions_by_group.TryGetValue(condition_group_id, out var conditionMeta)) + { + conditionMeta = new List(); + } + conditionMeta.Add(condition_group); + conditions_by_group.TryAdd(condition_group_id, conditionMeta); + } + + return conditions_by_group; + } + + private static IGameModeDetailMeta loadDetailMeta(GameModeType type) + { + + switch (type) + { + case GameModeType.TPS_FFA: + return new TPSFfaMeta(); + case GameModeType.TPS_TDM: + return new TPSTdmMeta(); + case GameModeType.RUN_ADV: + return new RunAdventureMeta(); + case GameModeType.RUN_RACE: + case GameModeType.RUN_HONOR: + return new RunRaceMeta(); + case GameModeType.RUN_PRACTICE: + return new RunPracticeMeta(); + default: + Log.getLogger().error($"load detail meta not implements!!!! type : {type}"); + throw new NotImplementedException(); + } + } + + //컨디션 그룹 쪼개 놓기 + +} diff --git a/ServerCommon/MetaData/GameMode/IGameModeDetailMeta.cs b/ServerCommon/MetaData/GameMode/IGameModeDetailMeta.cs new file mode 100644 index 0000000..c8c768f --- /dev/null +++ b/ServerCommon/MetaData/GameMode/IGameModeDetailMeta.cs @@ -0,0 +1,8 @@ +using MetaAssets; + +namespace ServerCommon.GameMode; + +public interface IGameModeDetailMeta +{ + void loadData(List rewardMetas, Dictionary> conditionMetas, int configId); +} \ No newline at end of file diff --git a/ServerCommon/MetaData/GameMode/RunAdventureMeta.cs b/ServerCommon/MetaData/GameMode/RunAdventureMeta.cs new file mode 100644 index 0000000..b5950cc --- /dev/null +++ b/ServerCommon/MetaData/GameMode/RunAdventureMeta.cs @@ -0,0 +1,11 @@ +using MetaAssets; + +namespace ServerCommon.GameMode; + +public class RunAdventureMeta : IGameModeDetailMeta +{ + public void loadData(List rewardMetas, Dictionary> conditionMetas, int configId) + { + return; + } +} \ No newline at end of file diff --git a/ServerCommon/MetaData/GameMode/RunPracticeMeta.cs b/ServerCommon/MetaData/GameMode/RunPracticeMeta.cs new file mode 100644 index 0000000..efa4213 --- /dev/null +++ b/ServerCommon/MetaData/GameMode/RunPracticeMeta.cs @@ -0,0 +1,14 @@ +using MetaAssets; + +namespace ServerCommon.GameMode; + +public class RunPracticeMeta : IGameModeDetailMeta +{ + public int m_result_scene_tracsition_wait_time { get; private set; } = 3; + public void loadData(List rewardMetas, Dictionary> conditionMetas, int configId) + { + //khoon todo : 필요한 메타 여기에 추가 해야 된다. + + + } +} \ No newline at end of file diff --git a/ServerCommon/MetaData/GameMode/RunRaceMeta.cs b/ServerCommon/MetaData/GameMode/RunRaceMeta.cs new file mode 100644 index 0000000..a46a6b4 --- /dev/null +++ b/ServerCommon/MetaData/GameMode/RunRaceMeta.cs @@ -0,0 +1,175 @@ +using System.Collections.Concurrent; +using MetaAssets; +using ServerCommon.Contents.GameMode; +using ServerCore; + +namespace ServerCommon.GameMode; + + +using PLAYER_COUNT = System.Int32; +using PLAYER_RANK = System.Int32; +using MIN_SPAWN_RANK = System.Int32; +using LAST_SAVE_CHECK_POINT = System.Int32; +using INSTANCE_ID = System.Int32; + +public class RunRaceMeta : IGameModeDetailMeta +{ + private Dictionary<(PLAYER_COUNT, PLAYER_RANK), IGameModeRewardCondition> m_finish_rank_reward_conditions = new Dictionary<(PLAYER_COUNT, PLAYER_RANK), IGameModeRewardCondition>(); + private Dictionary<(PLAYER_COUNT, MIN_SPAWN_RANK), IGameModeRewardCondition> m_min_respawn_rank_reward_conditions = new Dictionary<(PLAYER_COUNT, MIN_SPAWN_RANK), IGameModeRewardCondition>(); + private Dictionary<(PLAYER_COUNT, LAST_SAVE_CHECK_POINT), IGameModeRewardCondition> m_unfinish_rank_reward_conditions = new Dictionary<(PLAYER_COUNT, LAST_SAVE_CHECK_POINT), IGameModeRewardCondition>(); + public int m_final_timer { get; private set; } = 20; + public int m_penalty_quit_count { get; private set;} = 3; + public int m_penalty_time { get; private set; } = 600; + public int m_match_count_deduction_point { get; private set; } = 15; + public int m_result_scene_tracsition_wait_time { get; private set; } = 3; + + public int m_respawn_cnt_limit { get; set; } + + public ConcurrentDictionary> m_check_point_monitor_data = new ConcurrentDictionary>(); + + + + public void loadData(List rewardMetas, Dictionary> conditionMetas, int configId) + { + foreach (var reward_meta in rewardMetas) + { + var reward_group_id = reward_meta.RewardGroupID; + var condition_group_id = reward_meta.GameModeConditionGroupID; + if(false == conditionMetas.TryGetValue(condition_group_id, out var conditions)) + { + Log.getLogger().error($"not exist TPSFfaMeta condition group!!!! condition_group_id : {condition_group_id}"); + continue; + } + + var condition_type = conditions.Select(c => c.ConditionType).FirstOrDefault(); + switch (condition_type) + { + case GameModeConditionType.FinishRankReward: + loadFinishRankReward(condition_group_id, reward_group_id, conditions); + break; + case GameModeConditionType.MinRespawnBonusReward: + loadRespawnBonusReward(condition_group_id, reward_group_id, conditions); + break; + case GameModeConditionType.UnfinishRankReward: + loadUnfinishRankReward(condition_group_id, reward_group_id, conditions); + break; + default: + Log.getLogger().error($"Invalid condition type : {condition_type}"); + break; + } + } + + leadDetailMeta(configId); + } + + private void leadDetailMeta(int configId) + { + if (false == MetaData.Instance.Meta.GameModeRunRaceMetaTable.GameModeRunRaceMetaDataListbyId.TryGetValue(configId, out var meta)) + { + Log.getLogger().error($"not exist GameModeRunRaceMeta configId : {configId}"); + return; + } + + foreach (var check_point_mata in MetaData.Instance.Meta.GameModeRunRaceCheckPointMonitorMetaTable.GameModeRunRaceCheckPointMonitorMetaDataList) + { + var instanc_id = check_point_mata.InstanceId; + if (false == m_check_point_monitor_data.TryGetValue(instanc_id, out var data)) + { + data = new ConcurrentDictionary(); + } + data.AddOrUpdate(check_point_mata.CheckPointId, check_point_mata.TransitTimeNormalRange, (key, oldValue) => check_point_mata.TransitTimeNormalRange); + m_check_point_monitor_data.AddOrUpdate(instanc_id, data, (key, oldValue) => data); + } + + m_final_timer = meta.FinalTimer; + m_penalty_quit_count = meta.PenaltyQuitCount; + m_penalty_time = meta.PenaltyTime; + m_match_count_deduction_point = meta.MatchCountDeductionPoint; + m_result_scene_tracsition_wait_time = meta.ResultSceneTransitionWaitTime; + m_respawn_cnt_limit = meta.RespawnCntLimit; + } + + private void loadFinishRankReward(int conditionGroupId, int rewardGroupId, List conditionMetas) + { + int player_count = 0; + int rank = 0; + foreach (var condition in conditionMetas) + { + switch (condition.VariableName) + { + case GameModeConditionVariableName.PlayerCount: + player_count = (int)condition.VariableValue; + break; + case GameModeConditionVariableName.Rank: + rank = (int)condition.VariableValue; + break; + default: + Log.getLogger().error($"Invalid condition name : {condition.VariableName}"); + return; + } + } + var cond = new RunRaceRankRewardCondition(player_count, rank, conditionGroupId, rewardGroupId); + m_finish_rank_reward_conditions.TryAdd((player_count, rank), cond); + } + + private void loadRespawnBonusReward(int conditionGroupId, int rewardGroupId, List conditionMetas) + { + int player_count = 0; + int minimum_respawn_rank = 0; + foreach (var condition in conditionMetas) + { + switch (condition.VariableName) + { + case GameModeConditionVariableName.PlayerCount: + player_count = (int)condition.VariableValue; + break; + case GameModeConditionVariableName.MinimumRespawnRank: + minimum_respawn_rank = (int)condition.VariableValue; + break; + default: + Log.getLogger().error($"Invalid condition name : {condition.VariableName}"); + return; + } + } + var cond = new RunRaceMinimumRespawnRewardCondition(player_count, minimum_respawn_rank, conditionGroupId, rewardGroupId); + m_min_respawn_rank_reward_conditions.TryAdd((player_count, minimum_respawn_rank), cond); + } + + private void loadUnfinishRankReward(int conditionGroupId, int rewardGroupId, List conditionMetas) + { + int player_count = 0; + int last_save_checkpoint = 0; + foreach (var condition in conditionMetas) + { + switch (condition.VariableName) + { + case GameModeConditionVariableName.PlayerCount: + player_count = (int)condition.VariableValue; + break; + case GameModeConditionVariableName.LastSaveCheckPoint: + last_save_checkpoint = (int)condition.VariableValue; + break; + default: + Log.getLogger().error($"Invalid condition name : {condition.VariableName}"); + return; + } + } + var cond = new RunRaceUnfinishRankRewardCondition(player_count, last_save_checkpoint, conditionGroupId, rewardGroupId); + m_unfinish_rank_reward_conditions.TryAdd((player_count, last_save_checkpoint), cond); + } + + public IReadOnlyDictionary<(PLAYER_COUNT, PLAYER_RANK), IGameModeRewardCondition> getFinishRankReward() + { + return m_finish_rank_reward_conditions; + } + + public IReadOnlyDictionary<(PLAYER_COUNT, LAST_SAVE_CHECK_POINT), IGameModeRewardCondition> getUnFinishRankReward() + { + return m_unfinish_rank_reward_conditions; + } + + public IReadOnlyDictionary<(PLAYER_COUNT, MIN_SPAWN_RANK), IGameModeRewardCondition> getRespawnReward() + { + return m_min_respawn_rank_reward_conditions; + } +} diff --git a/ServerCommon/MetaData/GameMode/TPSFfaMeta.cs b/ServerCommon/MetaData/GameMode/TPSFfaMeta.cs new file mode 100644 index 0000000..4d2ac43 --- /dev/null +++ b/ServerCommon/MetaData/GameMode/TPSFfaMeta.cs @@ -0,0 +1,78 @@ +using MetaAssets; +using ServerCommon.Contents.GameMode; +using ServerCore; + +namespace ServerCommon.GameMode; + + +using STEP_ID = System.Int32; +public class TPSFfaMeta : IGameModeDetailMeta +{ + private IGameModeRewardCondition m_pickup_pod_reward_codition = new GameModeRewardConditionBase(); + private Dictionary m_pod_storage_reward_conditions = new Dictionary(); + public void loadData(List rewardMetas, Dictionary> conditionMetas, int configId) + { + foreach (var reward_meta in rewardMetas) + { + var reward_group_id = reward_meta.RewardGroupID; + var condition_group_id = reward_meta.GameModeConditionGroupID; + if(false == conditionMetas.TryGetValue(condition_group_id, out var conditions)) + { + Log.getLogger().error($"not exist TPSFfaMeta condition group!!!! condition_group_id : {condition_group_id}"); + continue; + } + + var condition_type = conditions.Select(c => c.ConditionType).FirstOrDefault(); + switch (condition_type) + { + case GameModeConditionType.GetPickUpPod: + loadPickupPodReward(condition_group_id, reward_group_id); + break; + case GameModeConditionType.ObtainedByOwningForTime: + loadPodStorageReward(condition_group_id, reward_group_id, conditions); + break; + default: + Log.getLogger().error($"Invalid condition type : {condition_type}"); + break; + + } + } + + } + + private void loadPodStorageReward(int conditionGroupId, int rewardGroupId, List conditionMetas) + { + int charge_level = 0; + int charge_time = 0; + foreach (var condition in conditionMetas) + { + switch (condition.VariableName) + { + case GameModeConditionVariableName.ChargeLevel: + charge_level = (int)condition.VariableValue; + break; + case GameModeConditionVariableName.ChargeTime: + charge_time = (int)condition.VariableValue; + break; + default: + Log.getLogger().error($"Invalid condition name : {condition.VariableName}"); + return; + } + } + var cond = new TpsFfaPodStorageRewardCondition(charge_level, charge_time, conditionGroupId, rewardGroupId); + m_pod_storage_reward_conditions.TryAdd(charge_level, cond); + } + + private void loadPickupPodReward(int conditionGroupId, int rewardGroupId) + { + m_pickup_pod_reward_codition = new TpsFfaPickupPodRewardCondition(conditionGroupId, rewardGroupId); + } + + public IGameModeRewardCondition getPickupPodRewardCondition() + { + + return m_pickup_pod_reward_codition;//TpsFfaPickupPodRewardCondition + } + + +} \ No newline at end of file diff --git a/ServerCommon/MetaData/GameMode/TPSTdmMeta.cs b/ServerCommon/MetaData/GameMode/TPSTdmMeta.cs new file mode 100644 index 0000000..921712b --- /dev/null +++ b/ServerCommon/MetaData/GameMode/TPSTdmMeta.cs @@ -0,0 +1,11 @@ +using MetaAssets; + +namespace ServerCommon.GameMode; + +public class TPSTdmMeta : IGameModeDetailMeta +{ + public void loadData(List rewardMetas, Dictionary> conditionMetas, int configId) + { + return; + } +} \ No newline at end of file diff --git a/ServerCommon/MetaData/MapData.cs b/ServerCommon/MetaData/MapData.cs index d5a642e..a331bba 100644 --- a/ServerCommon/MetaData/MapData.cs +++ b/ServerCommon/MetaData/MapData.cs @@ -34,7 +34,9 @@ public class Anchor [JsonProperty("MannequinItems")] public List MannequinItems = new(); - + [JsonProperty("HurdleValues")] + public string HurdleValues = string.Empty; + } public class Building diff --git a/ServerCommon/MetaData/MapDataTable.cs b/ServerCommon/MetaData/MapDataTable.cs index cf65627..f5c0a53 100644 --- a/ServerCommon/MetaData/MapDataTable.cs +++ b/ServerCommon/MetaData/MapDataTable.cs @@ -134,7 +134,16 @@ public class MapDataTable foreach (var anchor in one.Anchors) { - m_anchors.TryAdd(anchor.GUID, anchor); + if (false == m_anchors.TryAdd(anchor.GUID, anchor)) + { + m_anchors.TryGetValue(anchor.GUID, out var origin_anchor); + string editorname = string.Empty; + if (origin_anchor is not null) + { + editorname = origin_anchor.EditorName; + } + Log.getLogger().error($"fileName fileName anchor Exist AnchorGuid:{anchor.GUID} - EditorName:{editorname}, file name : {fileName}"); + } } } diff --git a/ServerCommon/MetaData/MetaData.cs b/ServerCommon/MetaData/MetaData.cs index 6239321..fa47769 100644 --- a/ServerCommon/MetaData/MetaData.cs +++ b/ServerCommon/MetaData/MetaData.cs @@ -12,6 +12,7 @@ using Google.Protobuf.WellKnownTypes; using ServerCore; using ServerBase; using MetaAssets; +using ServerCommon.GameMode; namespace ServerCommon; @@ -275,16 +276,24 @@ public class MetaData public IReadOnlyDictionary _BattleObjectSpawnGroupMetaTable => Meta.BattleObjectSpawnGroupMetaTable.BattleObjectSpawnGroupMetaDataListbyId; - public IReadOnlyDictionary _BattleFFAConfigMetaTable => - Meta.BattleFFAConfigMetaTable.BattleFFAConfigMetaDataListbyId; + // public IReadOnlyDictionary _BattleFFAConfigMetaTable => + // Meta.BattleFFAConfigMetaTable.BattleFFAConfigMetaDataListbyId; - public IReadOnlyDictionary> _BattleFFARewardMetaTable = new Dictionary>(); + //public IReadOnlyDictionary> _BattleFFARewardMetaTable = new Dictionary>(); public BattleObjectMetaData _BattlePodStorageMeta = new(new()); - public BattleObjectMetaData _BattlePickupPodMeta = new(new()); + //public BattleObjectMetaData _BattlePickupPodMeta = new(new()); public IReadOnlyDictionary m_user_log_meta_datas => Meta.UserLogMetaTable.UserLogMetaDataListbyMetaId; + public IReadOnlyDictionary m_game_mode_all_metas = new Dictionary(); + + public IReadOnlyDictionary _ContentsMenuMetaTable => + Meta.ContentsMenuDataTable.ContentsMenuDataListbyContents_ID; + + public IReadOnlyDictionary _RankingMetaTable => + Meta.RankingDataTable.RankingDataListbyRankingId; + MetaData() { } @@ -384,7 +393,6 @@ public class MetaData if (_ShopRandomProductByGroupIdTable.TryGetValue(one.Group_Id, out var productDataList) == false) { productDataList = new ShopProductRandomGroupInfo(); - productDataList.TotalWeight += one.Weight; _ShopRandomProductByGroupIdTable.Add(one.Group_Id, productDataList); } productDataList.TotalWeight += one.Weight; @@ -486,31 +494,14 @@ public class MetaData } { - _BattleFFARewardMetaTable = Meta.BattleFFARewardMetaTable.BattleFFARewardMetaDataList - .GroupBy(data => data.GroupID) - .ToDictionary( - group => group.Key, - group => (IReadOnlyDictionary)group - .OrderBy(data => data.ChargeLevel) - .ToDictionary(data => data.ChargeLevel, data => data) - ) as IReadOnlyDictionary>; - - var pod_storage_meta = Meta.BattleObjectMetaTable.BattleObjectMetaDataList.FirstOrDefault(metaData => metaData.Name == "Pod_CombatStand"); if (pod_storage_meta is null) throw new Exception($"pod_storage_meta is null!!!!!!"); _BattlePodStorageMeta = pod_storage_meta; - - - var pickup_pod_meta = Meta.BattleObjectMetaTable.BattleObjectMetaDataList.FirstOrDefault(metaData => metaData.ObjectType == EBattleObjectType.Pod_Box); - if (pickup_pod_meta is null) throw new Exception($"pod_storage_meta is null!!!!!!"); - _BattlePickupPodMeta = pickup_pod_meta; - - } - LoadQuest(); - + m_game_mode_all_metas = GameModeMetaHelper.LoadGameModeMetaDatas(); + ValidatorErrorCollection errors = new(); MetaTableValidator validator = new MetaTableValidator(); validator.validate(errors); @@ -900,11 +891,6 @@ public class MetaData questBaseInfo.QuestTaskGroupList.TryGetValue(task_number, out var quest_task_group); NullReferenceCheckHelper.throwIfNull(quest_task_group, () => $"Quest Task Group is null !!! - task_number:{task_number}"); - if (questBaseInfo.QuestId == 20001) - { - Log.getLogger().error(""); - } - for (Int32 i = script_start_idx; i <= script_end_idx; i++) { //EventTarget 이 없으면 continue; diff --git a/ServerCommon/MetaData/MetaEnums.cs b/ServerCommon/MetaData/MetaEnums.cs index cc788b2..7ea8ebd 100644 --- a/ServerCommon/MetaData/MetaEnums.cs +++ b/ServerCommon/MetaData/MetaEnums.cs @@ -12,7 +12,8 @@ namespace MetaAssets; public class CustomStringEnumConverter : StringEnumConverter { - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, + JsonSerializer serializer) { try { @@ -28,17 +29,14 @@ public class CustomStringEnumConverter : StringEnumConverter } catch (Exception ex) { - throw new Exception($"Error converting value {reader.Value?.ToString()} to type '{objectType}'." , ex); + throw new Exception($"Error converting value {reader.Value?.ToString()} to type '{objectType}'.", ex); } // we don't actually expect to get here. throw new Exception($"Unexpected token {reader.TokenType} when parsing enum."); } - } - - [JsonConverter(typeof(StringEnumConverter))] public enum ESeasonPassType { @@ -55,7 +53,6 @@ public enum EActionType MULTI = 2, } - [JsonConverter(typeof(StringEnumConverter))] public enum TaxiType { @@ -64,7 +61,6 @@ public enum TaxiType WAYPOINT = 2, } - [JsonConverter(typeof(StringEnumConverter))] public enum ERequirementItemType { @@ -82,7 +78,6 @@ public enum ERequirementQuestType COMPLETED = 2, } - [JsonConverter(typeof(StringEnumConverter))] public enum EQuestType { @@ -248,6 +243,8 @@ public enum EItemSmallType SHOULDERBAG = 87, LANDCERTIFICATE = 88, SETBOX = 89, + DRESS_SHOES = 90, + DRESS_ALL = 91, } [JsonConverter(typeof(StringEnumConverter))] @@ -268,7 +265,6 @@ public enum ESnapType Free = 3, } - [JsonConverter(typeof(StringEnumConverter))] public enum AccessType { @@ -384,7 +380,6 @@ public enum EBuffCategory WEAR = 5, } - [JsonConverter(typeof(CustomStringEnumConverter))] public enum BuffMotionSetType { @@ -513,6 +508,12 @@ public enum ActivateFuncType PHOTO = 2, MUSICBOX = 3, MUSICPLAYER = 4, + STREAMINGRADIO = 5, // 스트리밍 테이블을 이용한 URL 재생 목적 + TPS_FIELD_PISTOL = 6, // 필드용 무기 + TPS_FIELD_RIFLE = 7, // 필드용 무기 + TPS_FIELD_SHOTGUN = 8, // 필드용 무기 + TPS_FIELD_GLAUNCHER = 9, // 필드용 무기 + RUN_FIELD = 10, // 필드용 러닝표시 } [JsonConverter(typeof(StringEnumConverter))] @@ -549,13 +550,17 @@ public enum EditorType } [JsonConverter(typeof(StringEnumConverter))] -public enum EBattleObjectType +public enum EGameModeObjectType { None = 0, Weapon = 1, Pod_Combat = 2, Pod_Box = 3, Buff = 4, + CheckPoint = 5, + Goal = 6, + RunningBuff = 7, + PlayStartPos = 8, } [JsonConverter(typeof(StringEnumConverter))] @@ -579,15 +584,132 @@ public enum EPropSmallType PLAY = 7, } +// [JsonConverter(typeof(StringEnumConverter))] +// public enum EGameObjectType +// { +// None = 0, +// Weapon = 1, +// Pod_Combat = 2, +// Pod_Box = 3, +// Buff = 4, +// +// Check_Point = 5, +// Goal_Line = 6, +// } [JsonConverter(typeof(StringEnumConverter))] -public enum EGameObjectType +public enum GameGenreType { None = 0, - Weapon = 1, - Pod_Combat = 2, - Pod_Box = 3, - Buff = 4, - - Save_Point = 5, -} \ No newline at end of file + + TPS = 1, + Running = 2, + //Dance = 3, +} + +[JsonConverter(typeof(StringEnumConverter))] +public enum GameModeType +{ + None = 0, + + TPS_FFA = 11, + TPS_TDM = 12, + + RUN_ADV = 21, + RUN_RACE = 22, + RUN_PRACTICE = 23, + // 싱글 게임 명예 모드 TODO: 임시 코드로 수정해야 함 + RUN_HONOR = 24, + //DANCE_ = 5, +} + +[JsonConverter(typeof(StringEnumConverter))] +public enum GameModeCompesationType +{ + None = 0, + Immediately = 1, + GameEnd = 2, +} + +[JsonConverter(typeof(StringEnumConverter))] +public enum GameModeReleaseStatus +{ + None = 0, + Open = 1, + CommingSoon = 2, + Test = 3, +} + +[JsonConverter(typeof(StringEnumConverter))] +public enum GameModeConditionType +{ + None = 0, + + GetPickUpPod = 1, + ObtainedByOwningForTime = 2, + TotalKill = 3, + TotalAssist = 4, + GameTime = 5, + TotalDamage = 6, + GameResult = 7, + KillStreak = 8, + FirstKill = 9, + LastKill = 10, + FinishRankReward = 11, + MinRespawnBonusReward = 12, + UnfinishRankReward = 13, +} + +[JsonConverter(typeof(StringEnumConverter))] +public enum GameModeConditionVariableName +{ + None = 0, + + GetPickUpPod = 1, + ChargeLevel = 2, + ChargeTime = 3, + Kill = 4, + Assist = 5, + Time = 6, + Damage = 7, + Win = 8, + Lose = 9, + PreviousKillStage = 10, + NextKillStage = 11, + TimeFromLastKill = 12, + FirstKill = 13, + LastKill = 14, + PlayerCount = 15, + Rank = 16, + MinimumRespawnRank = 17, + LastSaveCheckPoint = 18, +} + +[JsonConverter(typeof(StringEnumConverter))] +public enum JoinInProgressType +{ + None = 0, + JoinIn = 1, // 난입 + JoinInSnapShot = 2, // 난입 - 퇴장 후에도 인스턴스 유지 + ReEntry = 3, // 재입장 +} + +[JsonConverter(typeof(StringEnumConverter))] +public enum TeamAssignmentType +{ + None = 0, + Random = 1, + Greedy = 2, +} + +/// +/// 게임 시작 시 위치를 지정하는 앵커가 동작하는 유형 +/// +public enum AnchorAssignmentType +{ + None = 0, + Random = 1, // 팀 구분 없이 무작위 인덱스로 이동 + TeamRandom = 2, // 팀 넘버 구분을 하고 무작위 인덱스로 이동 + EntryOrder = 3, //입장 순서에 따라 이동 + TeamEntryOrder = 4, // 팀 넘버 구분을 하고 입장 순서에 따라 이동 +} diff --git a/ServerCommon/MetaData/MetaTableValidation/CommonValidator.cs b/ServerCommon/MetaData/MetaTableValidation/CommonValidator.cs index 7c2ae20..bff532d 100644 --- a/ServerCommon/MetaData/MetaTableValidation/CommonValidator.cs +++ b/ServerCommon/MetaData/MetaTableValidation/CommonValidator.cs @@ -33,4 +33,9 @@ public class CommonValidator { return MetaData.Instance.Meta.ProductMetaTable.ProductMetaDataListbyId.ContainsKey(id); } + + public static bool isExistInteriorId(int id) + { + return MetaData.Instance.Meta.InteriorMetaTable.InteriorMetaDataListbyId.ContainsKey(id); + } } \ No newline at end of file diff --git a/ServerCommon/MetaData/MetaTableValidation/ItemMetaDataValidator.cs b/ServerCommon/MetaData/MetaTableValidation/ItemMetaDataValidator.cs index 545e8db..fed2778 100644 --- a/ServerCommon/MetaData/MetaTableValidation/ItemMetaDataValidator.cs +++ b/ServerCommon/MetaData/MetaTableValidation/ItemMetaDataValidator.cs @@ -37,6 +37,10 @@ public static class ItemMetaTableValidator // 6. brand 체크 : 0이 될 수 있어. if(item.Brand_ > 0 && false == CommonValidator.isExistBrandById(item.Brand_)) errors.add($"Invalid Item : Brand Id is Invalid - brand [{item.Brand_}]"); + + // 7. Interior Item expire time check + if (!checkInteriorItemExpireItem(item)) + errors.add($"Invalid Item : InteriorItem has expire time - [{item.ItemId}]"); } private static bool checkItemWorth(ItemMetaData item) @@ -90,6 +94,7 @@ public static class ItemMetaTableValidator smallList.Add(EItemSmallType.BAG); smallList.Add(EItemSmallType.CAP); smallList.Add(EItemSmallType.DRESS); + smallList.Add(EItemSmallType.EARRING); smallList.Add(EItemSmallType.GLASSES); smallList.Add(EItemSmallType.GLOVES); smallList.Add(EItemSmallType.MASK); @@ -99,6 +104,8 @@ public static class ItemMetaTableValidator smallList.Add(EItemSmallType.SHIRT); smallList.Add(EItemSmallType.SHOES); smallList.Add(EItemSmallType.SHOULDERBAG); + smallList.Add(EItemSmallType.DRESS_SHOES); + smallList.Add(EItemSmallType.DRESS_ALL); dict.Add(EItemLargeType.CLOTH, smallList); smallList = new List(); @@ -196,5 +203,27 @@ public static class ItemMetaTableValidator return dict; } + + static bool checkInteriorItemExpireItem(ItemMetaData item) + { + if (CommonValidator.isExistInteriorId(item.ItemId)) + { + switch(item.ExpireType) + { + case EExpireType.NONE: + break; + case EExpireType.DELAY: + { + if (item.ExpireTimeSec > 0) + { + return false; + } + } + break; + } + } + + return true; + } } diff --git a/ServerCommon/MetaData/QuestAssignData.cs b/ServerCommon/MetaData/QuestAssignData.cs index 3d1286e..77f1122 100644 --- a/ServerCommon/MetaData/QuestAssignData.cs +++ b/ServerCommon/MetaData/QuestAssignData.cs @@ -39,6 +39,7 @@ namespace ServerCommon MYHOME, NPC, PARTY, + PHOTOMODE, PROP, QUEST, RENTAL, @@ -66,6 +67,7 @@ namespace ServerCommon ADDED , ARRIVED, BOUGHT, + CAPTURE, CHANGED, COMPLETED, CONVERSIONED, diff --git a/ServerCommon/ProudNet/ProudNetListener.cs b/ServerCommon/ProudNet/ProudNetListener.cs index 24fd11b..3c0ec2c 100644 --- a/ServerCommon/ProudNet/ProudNetListener.cs +++ b/ServerCommon/ProudNet/ProudNetListener.cs @@ -13,6 +13,7 @@ using MODULE_ID = System.UInt32; namespace ServerCommon; +// HANDOVER: ListenSessionBase 실체적 구현 클래스 이다. public abstract partial class ProudNetListener : ListenSessionBase, IModule { @@ -115,8 +116,7 @@ public abstract partial class ProudNetListener : ListenSessionBase, IModule net_server.SetDefaultTimeoutTimeMs(config_param.KeepAliveMSec); net_server.Start(config_param.StartServerParam); - - getLargePacketHandler().onInit(this); + } catch (Exception e) { diff --git a/ServerCommon/Redis/GameInstanceRoomInfo.cs b/ServerCommon/Redis/GameInstanceRoomInfo.cs new file mode 100644 index 0000000..79410b2 --- /dev/null +++ b/ServerCommon/Redis/GameInstanceRoomInfo.cs @@ -0,0 +1,7 @@ +namespace ServerCommon; + +// public class GameInstanceRoomInfo: InstanceRoomInfo +// { +// public required int GameModeId { get; init; } +// public MatchRoomInfo MatchRoom { get; init; } = null!; +// } diff --git a/ServerCommon/Redis/GameInstanceRoomStorage.cs b/ServerCommon/Redis/GameInstanceRoomStorage.cs new file mode 100644 index 0000000..d15f4a2 --- /dev/null +++ b/ServerCommon/Redis/GameInstanceRoomStorage.cs @@ -0,0 +1,258 @@ +using Newtonsoft.Json; +using ServerBase; +using ServerCore; +using StackExchange.Redis; + +namespace ServerCommon; + +public class GameInstanceRoomStorage : InstanceRoomStorage +{ + public const string Game = "game"; + + public string GetRoomGameInfoKey(string instanceRoomId) + { + return $"{_instanceKeyPrefix}:{instanceRoomId}:game"; + } + public async Task SetInstanceRoomGameInfoAsync(string instanceRoomId, TGameInfo gameInoObj) + { + var result = new Result(); + try + { + var roomGameInfoKey = GetRoomGameInfoKey(instanceRoomId); + var gameInfo = JsonConvert.SerializeObject(gameInoObj); + await _database.StringSetAsync(roomGameInfoKey, gameInfo); + await _database.KeyExpireAsync(roomGameInfoKey, + TimeSpan.FromMilliseconds(INSTANCE_ROOM_CREATION_TIMEOUT_MSEC)); + } + catch (Exception e) + { + string errMsg = $"Failed to createInstanceRoom from Redis !!! : message:{e}"; + result.setFail(ServerErrorCode.TryCatchException, errMsg); + Log.getLogger().fatal(errMsg); + } + return result; + } + + public async Task DeleteGameRoomInfoAsync(string instanceRoomId) + { + var result = new Result(); + try + { + var roomInfoKey = GetRoomGameInfoKey(instanceRoomId); + var roomListKey = GetInstanceRoomListKey(instanceRoomId); + await _database.KeyDeleteAsync(roomInfoKey); + await _database.KeyDeleteAsync(roomListKey); + Log.getLogger().info($"DeleteGameRoomInfoAsync => {roomListKey}, {roomListKey}"); + } + catch (Exception e) + { + string errMsg = $"Failed to deleteInstanceRoom from Redis !!! : message:{e}"; + } + return result; + } + + public async Task<(Result, TGameInfo?)> GetInstanceRoomGameInfoAsync(string instanceRoomId) where TGameInfo : class + { + var result = new Result(); + var roomGameInfoKey = GetRoomGameInfoKey(instanceRoomId); + try + { + var value = await _database.StringGetAsync(roomGameInfoKey); + if (value.HasValue) + { + var gameInfo = JsonConvert.DeserializeObject((string)value!); + return (result, gameInfo); + } + else + { + result.setFail(ServerErrorCode.RedisStringsReadFailed, $"{roomGameInfoKey} is not exist"); + } + return (result, null); + } + catch (Exception e) + { + string errMsg = $"Failed to get {roomGameInfoKey} from Redis : message:{e}"; + result.setFail(ServerErrorCode.TryCatchException, errMsg); + } + return (result, null); + } + + public async Task<(Result, GameRoomInfo)> GetGameInfoAsync(string instanceRoomId) + { + var (result, gameInfo) = await GetInstanceRoomGameInfoAsync(instanceRoomId); + return (result, gameInfo!); + } + + public async Task UpdateInstanceRoomInfo(InstanceRoomInfo instanceRoomInfo) + { + var result = new Result(); + try + { + var instanceRoomId = instanceRoomInfo.roomId; + string roomInfoKey = GetRoomInfoKey(instanceRoomId); + var hashEntries = TypeConvertHelper.toHashEntries(instanceRoomInfo); + await _database.HashSetAsync(roomInfoKey, hashEntries); + } + catch (Exception e) + { + string errMsg = $"Failed to createInstanceRoom from Redis !!! : message:{e}"; + result.setFail(ServerErrorCode.TryCatchException, errMsg); + Log.getLogger().fatal(errMsg); + } + return result; + } + + public async Task CreateInstanceGameRoom(InstanceRoomInfo instanceRoomInfo) + { + var result = new Result(); + string errMsg; + try + { + var instanceRoomId = instanceRoomInfo.roomId; + var instanceMetaId = instanceRoomInfo.InstanceId; + string roomInfoKey = GetRoomInfoKey(instanceRoomId); + + // 중복 확인 + var value = await _database.HashGetAllAsync(roomInfoKey, CommandFlags.PreferReplica); + if (value.Length > 0) + { + // 중복 + var instance_room_info = TypeConvertHelper.toClassFromHashEntries(value); + } + var existInstanceRoomState = await isExistInstanceRoom(instanceRoomId, instanceMetaId); + switch (existInstanceRoomState) + { + case ExistInstanceRoomState.None: + { + var hashEntries = TypeConvertHelper.toHashEntries(instanceRoomInfo); + await _database.HashSetAsync(roomInfoKey, hashEntries); + await _database.KeyExpireAsync(roomInfoKey, + TimeSpan.FromMilliseconds(INSTANCE_ROOM_CREATION_TIMEOUT_MSEC)); + } + break; + case ExistInstanceRoomState.Exist: + case ExistInstanceRoomState.Different: + errMsg = + $"InstanceRoomId is Duplicated !!! : instanceRoomId:{instanceRoomId}, instanceMetaId:{instanceMetaId}"; + result.setFail(ServerErrorCode.InstanceRoomIdDuplicated, errMsg); + break; + case ExistInstanceRoomState.Exception: + errMsg = $"Failed to isExistInstanceRoom() !!!"; + result.setFail(ServerErrorCode.TryCatchException, errMsg); + break; + default: + errMsg = $"ExistInstanceRoomState Invalid !!! : state:{existInstanceRoomState}"; + result.setFail(ServerErrorCode.NotImplemented, errMsg); + break; + } + } + catch (Exception e) + { + errMsg = $"Failed to createInstanceRoom from Redis !!! : message:{e}"; + result.setFail(ServerErrorCode.TryCatchException, errMsg); + Log.getLogger().fatal(errMsg); + } + + if (result.isFail()) + { + Log.getLogger().error(result.toBasicString()); + } + return result; + } + + public async Task GetInstanceRoomInfo(string instanceRoomId) where T : InstanceRoomInfo + { + var result = new Result(); + var err_msg = string.Empty; + + try + { + var instance_room_info_key = GetRoomInfoKey(instanceRoomId); + var value = await _database.HashGetAllAsync(instance_room_info_key, CommandFlags.PreferReplica); + if (value.Length <= 0) return null; + + return TypeConvertHelper.toClassFromHashEntries(value); + } + catch (Exception e) + { + err_msg = $"Failed to GetInstanceRoomInfo() from Redis !!! : message:{e}"; + result.setFail(ServerErrorCode.TryCatchException, err_msg); + Log.getLogger().error(err_msg); + + return null; + } + } + + public async Task AddBattleInstanceRoomList(string subCategoryKey, string instanceRoomId) + { + var result = new Result(); + try + { + string instanceRoomListKey = GetGameInstanceRoomListKey(subCategoryKey); + + await _database.SortedSetAddAsync(instanceRoomListKey, instanceRoomId, 0); + await _database.KeyExpireAsync(instanceRoomListKey, TimeSpan.FromMilliseconds(300000)); + } + catch (Exception e) + { + string errMsg = $"Failed to addInstanceRoomList from Redis !!! : message:{e}"; + result.setFail(ServerErrorCode.TryCatchException, errMsg); + Log.getLogger().fatal(errMsg); + } + + return result; + } + + public string GetGameInstanceRoomListKey(string subCategoryKey) + { + // subCategoryKey -> 1:asia <- 게임모드 1의 asia 리전 + // 예> instanceroom:game:1:asia:roomlist + return $"{_instanceKeyPrefix}:{Game}:{subCategoryKey}:roomlist"; + } + + public async Task> GetGameInstanceRoomKeys(string subCategoryKey) + { + var resultRoomList = new List(); + try + { + string instanceRoomListKey = GetGameInstanceRoomListKey(subCategoryKey); + RedisValue[] roomList = await _database.SortedSetRangeByScoreAsync(instanceRoomListKey, 0, + double.PositiveInfinity, + Exclude.Stop, Order.Descending); + resultRoomList.AddRange(roomList.Select(roomId => roomId.ToString())); + } + catch (Exception e) + { + var errorCode = ServerErrorCode.TryCatchException; + string errMsg = $"Failed to get InstanceRoomList from Redis !!! errorCode{errorCode}, errMsg:{e.Message}"; + Log.getLogger().error(errMsg); + } + + return resultRoomList; + } + + public async Task<(Result, RedisValue[]?)> GetBattleInstanceTotalRoomList(string subCategoryKey) + { + var result = new Result(); + string instanceRoomListKey = string.Empty; + try + { + instanceRoomListKey = GetInstanceRoomListKey(subCategoryKey); + + RedisValue[] roomList = + await _database.SortedSetRangeByScoreAsync(instanceRoomListKey); + + return (result, roomList); + } + catch (Exception e) + { + var errorCode = ServerErrorCode.TryCatchException; + var errMsg = + $"Exception !!!, Failed to process in getBattleInstanceTotalRoomListWithScore() !!! : errorCode{errorCode}, instanceRoomIdBase:{subCategoryKey}, instance_room_list_key : {instanceRoomListKey}, exception:{e}"; + result.setFail(errorCode, errMsg); + Log.getLogger().error(result.toBasicString()); + } + + return (result, null); + } +} diff --git a/ServerCommon/Redis/InstanceRoomStorage.cs b/ServerCommon/Redis/InstanceRoomStorage.cs index a3e556a..89e18f9 100644 --- a/ServerCommon/Redis/InstanceRoomStorage.cs +++ b/ServerCommon/Redis/InstanceRoomStorage.cs @@ -1,13 +1,11 @@ using Google.Protobuf.WellKnownTypes; -using MetaAssets; -using Newtonsoft.Json; -using ServerControlCenter; using ServerCore; using ServerBase; using StackExchange.Redis; -using System.Numerics; namespace ServerCommon { + // HANDOVER: GameRoom 관련 Redis 캐시 처리 클래스 이다. + public class InstanceRoomInfo { public string roomId { get; set; } = string.Empty; @@ -17,26 +15,31 @@ namespace ServerCommon public int UgcNpcCount { get; set; } = 0; public string MyhomeGuid { get; set; } = string.Empty; public EPlaceType InstancePlaceType { get; set; } = EPlaceType.NONE; + public int GameModeId { get; init; } + // 게임 이벤트 아이디 : 테스트 또는 이벤트가 없을 경우 0으로 설정됨, 클라이언트에서 보낸 값으로 설정됨 + public int GameEventId { get; init; } public Timestamp InstanceStartTime { get; set; } = DateTimeHelper.MinTime.ToTimestamp(); } public class InstanceRoomStorage { - private enum ExistInstanceRoomState + protected enum ExistInstanceRoomState { None = 0, Different = 1, Exist = 2, Exception = 3 } - - readonly int KEEP_INSTANCE_ROOM_TIME = (60 * 1000); + + protected readonly int INSTANCE_ROOM_CREATION_TIMEOUT_MSEC = (30 * 60 * 1000); // 생성 대기 최대 30분 + + protected readonly int KEEP_INSTANCE_ROOM_TIME = (60 * 1000); //ConnectionMultiplexer _connection = default!; - IDatabase _database = default!; + protected IDatabase _database = default!; - string _roomKeyPrefix = String.Empty; - string _instanceKeyPrefix = String.Empty; + protected string _roomKeyPrefix = String.Empty; + protected string _instanceKeyPrefix = String.Empty; public void Init(IDatabase redisDB, string keyPrefix) { @@ -47,7 +50,7 @@ namespace ServerCommon //var servers = _connection.GetEndPoints().Select(endpoint => _connection.GetServer(endpoint)); //foreach (var server in servers) //{ - // Console.WriteLine(server.EndPoint); + // Console.WriteLine(server.EndPoint); //} _roomKeyPrefix = keyPrefix; @@ -133,7 +136,7 @@ namespace ServerCommon { var hash_entries = TypeConvertHelper.toHashEntries(instance_room_info); await _database.HashSetAsync(room_info_key, hash_entries); - await _database.KeyExpireAsync(room_info_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); + await _database.KeyExpireAsync(room_info_key, TimeSpan.FromMilliseconds(INSTANCE_ROOM_CREATION_TIMEOUT_MSEC)); is_created = true; } @@ -180,7 +183,7 @@ namespace ServerCommon return (result, is_created); } - async Task isExistInstanceRoom(string instanceRoomId, int instanceMetaId) + protected async Task isExistInstanceRoom(string instanceRoomId, int instanceMetaId) { var result = new Result(); var err_msg = string.Empty; @@ -195,7 +198,7 @@ namespace ServerCommon return ExistInstanceRoomState.None; // 내가 원하는 방인지 확인 - var instance_room_info = TypeConvertHelper.toClassFromHashEntries(value); + var instance_room_info = TypeConvertHelper.toClassFromHashEntries(value); if (instance_room_info.roomId != instanceRoomId || instance_room_info.InstanceId != instanceMetaId) return ExistInstanceRoomState.Different; } @@ -221,7 +224,7 @@ namespace ServerCommon string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); await _database.SortedSetAddAsync(instance_room_list_key, instanceRoomId, 0); - await _database.KeyExpireAsync(instance_room_list_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); + await _database.KeyExpireAsync(instance_room_list_key, TimeSpan.FromMilliseconds(INSTANCE_ROOM_CREATION_TIMEOUT_MSEC)); } catch (Exception e) { @@ -241,7 +244,7 @@ namespace ServerCommon try { string instance_room_member_list_key = GetRoomMemberListKey(instanceRoomId); - + var push_after_length = await _database.ListRightPushAsync(instance_room_member_list_key, userGuid); if (push_after_length > limitCount) { @@ -256,7 +259,7 @@ namespace ServerCommon return result; } } - + await _database.KeyExpireAsync(instance_room_member_list_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); } catch (Exception e) @@ -289,7 +292,7 @@ namespace ServerCommon return result; } - + public async Task increaseInstanceRoomUgcNpcScore(string instanceRoomId) { var result = new Result(); @@ -303,7 +306,7 @@ namespace ServerCommon catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to decreaseInstanceRoomUgcNpcScore from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; + err_msg = $"Failed to decreaseInstanceRoomUgcNpcScore from Redis !!! : errorCode{error_code}, exception:{e}"; result.setFail(error_code, err_msg); Log.getLogger().error(err_msg); } @@ -316,9 +319,9 @@ namespace ServerCommon List returnList = new(); string roomKey = GetRoomMemberListKey(roomId); var roomMemberList = await _database.ListRangeAsync(roomKey); - + if (roomMemberList == null) return returnList; - + returnList.AddRange(roomMemberList.Select(x => x.ToString())); return returnList; } @@ -365,7 +368,7 @@ namespace ServerCommon return result; } - + public async Task decreaseInstanceRoomUgcNpcScore(string instanceRoomId) { var result = new Result(); @@ -379,7 +382,7 @@ namespace ServerCommon catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; - err_msg = $"Failed to decreaseInstanceRoomUgcNpcScore from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; + err_msg = $"Failed to decreaseInstanceRoomUgcNpcScore from Redis !!! : errorCode{error_code}, exception:{e}"; result.setFail(error_code, err_msg); Log.getLogger().error(err_msg); } @@ -459,7 +462,7 @@ namespace ServerCommon public async Task setInstanceRoomExtraInfo(string roomId, EPlaceType placeType, DateTime startTime) { var result = new Result(); - + try { var instance_info = await GetInstanceRoomInfo(roomId); @@ -471,7 +474,7 @@ namespace ServerCommon instance_info.InstancePlaceType = placeType; instance_info.InstanceStartTime = startTime.ToTimestamp(); - + var room_info_key = GetRoomInfoKey(roomId); var hash_entries = TypeConvertHelper.toHashEntries(instance_info); await _database.HashSetAsync(room_info_key, hash_entries); diff --git a/ServerCommon/ServerCommon.csproj b/ServerCommon/ServerCommon.csproj index 68b274d..64a0ac7 100644 --- a/ServerCommon/ServerCommon.csproj +++ b/ServerCommon/ServerCommon.csproj @@ -1,60 +1,55 @@ - - - - net8.0 - enable - enable - true - true - Debug;Release;Shipping - true - - true - - - - full - $(DefineConstants); - True - - - - full - $(DefineConstants); - True - - - - full - $(DefineConstants); - true - True - - - - - - - - - - - - - - + + + net8.0 + enable + enable + true + true + Debug;Release;Shipping + true + + true + + + full + $(DefineConstants); + True + + + full + $(DefineConstants); + True + + + full + $(DefineConstants); + true + True + + + + + + + + + + + + - - - - - - - ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetClient.dll - - - ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetServer.dll - - - - + + + + + + ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetClient.dll + + + ..\..\ThirdPartyPackages\ProudNet\1.9.58941\ProudNet\lib\DotNet\ProudDotNetServer.dll + + + + + + \ No newline at end of file diff --git a/ServerCore/AWS/AwsHelper.cs b/ServerCore/AWS/AwsHelper.cs index 9c74696..cd5880c 100644 --- a/ServerCore/AWS/AwsHelper.cs +++ b/ServerCore/AWS/AwsHelper.cs @@ -7,6 +7,9 @@ using System.Threading.Tasks; namespace ServerCore; + +// HANDOVER: AWS SDK 연동하는 주요 함수를 호출해주는 Wrapper 클래스 이다. + public static class AwsHelper { // EC2에서 실행중인 상태 종류 @@ -121,7 +124,7 @@ public static class AwsHelper } catch (Exception e) { - var err_msg = $"Exception !!!, Failed to function in getPublicIPv4() : exception:{e}, awsMetaToken:{aws_meta_token}"; + var err_msg = $"Exception !!!, Failed to perform in getPublicIPv4() !!! : exception:{e}, awsMetaToken:{aws_meta_token}"; Log.getLogger().error(err_msg); return await Task.FromResult(string.Empty); } diff --git a/ServerCore/Comparer/KeyComparer.cs b/ServerCore/Comparer/KeyComparer.cs index aa9e506..a6f3b7f 100644 --- a/ServerCore/Comparer/KeyComparer.cs +++ b/ServerCore/Comparer/KeyComparer.cs @@ -7,15 +7,33 @@ using System.Threading.Tasks; namespace ServerCore; +/// +/// 두 컬렉션의 키(Key)를 비교하는 기능을 제공하는 정적 유틸리티 클래스입니다. +/// public static class KeyComparer { + /// + /// 두 개의 딕셔너리를 비교하여, 두 번째 딕셔너리(second)에만 존재하는 키(Key)들의 리스트를 반환합니다. + /// 첫 번째 딕셔너리(first)에는 없고 두 번째 딕셔너리에 새로 추가된 키를 찾을 때 유용합니다. + /// + /// 딕셔너리의 키 타입 + /// 딕셔너리의 값 타입 + /// 비교의 기준이 되는 첫 번째 딕셔너리 + /// 새로운 키를 찾을 대상이 되는 두 번째 딕셔너리 + /// 두 번째 딕셔너리에만 존재하는 키의 리스트 public static List getKeysOnlyInSecond(Dictionary first, Dictionary second) where TKey : notnull where TValue : notnull { return second.Keys.Except(first.Keys).ToList(); } - + /// + /// 두 개의 리스트를 비교하여, 두 번째 리스트(second)에만 존재하는 요소들의 리스트를 반환합니다. + /// + /// 리스트 요소의 타입 + /// 비교의 기준이 되는 첫 번째 리스트 + /// 새로운 요소를 찾을 대상이 되는 두 번째 리스트 + /// 두 번째 리스트에만 존재하는 요소의 리스트 public static List getKeysOnlyInSecond(List first, List second) where TKey : notnull { diff --git a/ServerCore/Config/ConfigManager.cs b/ServerCore/Config/ConfigManager.cs index 497d00d..5deabc7 100644 --- a/ServerCore/Config/ConfigManager.cs +++ b/ServerCore/Config/ConfigManager.cs @@ -12,6 +12,9 @@ using YamlDotNet.Serialization.NamingConventions; namespace ServerCore; + +// HANDOVER: Config 정보를 Json & Yaml 기반으로 관리해 주는 클래스 이다. + public sealed class ConfigManager { private readonly Dictionary m_key_to_source = new(); diff --git a/ServerCore/Container/MultiIndexDictionary.cs b/ServerCore/Container/MultiIndexDictionary.cs index 1801217..66a71f9 100644 --- a/ServerCore/Container/MultiIndexDictionary.cs +++ b/ServerCore/Container/MultiIndexDictionary.cs @@ -12,6 +12,10 @@ using System.Threading.Tasks; namespace ServerCore; + +// HANDOVER: 다중 Key로 특정 정보를 관리할 수 있는 색인 자료 구조를 제공 한다. + + public class MultiIndexDictionary where TPrimaryKey : notnull where TSubKey : notnull diff --git a/ServerCore/Dump/DotNetDumpHelper.cs b/ServerCore/Dump/DotNetDumpHelper.cs index 0dae9f0..8621ebb 100644 --- a/ServerCore/Dump/DotNetDumpHelper.cs +++ b/ServerCore/Dump/DotNetDumpHelper.cs @@ -10,6 +10,9 @@ using Microsoft.Diagnostics.NETCore.Client; namespace ServerCore; + +// HANDOVER: .Net 전용 DiagnosticsClient 기반 예외 이벤트를 수신 받는 클래스 이다. (리눅스 환겨에서 정상적인 호출을 보장하지 않는다 !!!) + public static class DotNetDumpHelper { private static DumpConfig DumpConfig; diff --git a/ServerCore/DynamoDB/DynamoDbConnectorBase.cs b/ServerCore/DynamoDB/DynamoDbConnectorBase.cs index eadbc47..1ccfd5a 100644 --- a/ServerCore/DynamoDB/DynamoDbConnectorBase.cs +++ b/ServerCore/DynamoDB/DynamoDbConnectorBase.cs @@ -22,6 +22,11 @@ using MongoDB.Driver.Core.Configuration; namespace ServerCore; + +// HANDOVER: AmazonDynamoDBClient SDK Wrapper 클래스 이다. +// Table 생성시 없으면 자동 생성 기능을 제공 한다. + + public abstract class DynamoDbConnectorBase { // 하나의 아이템 최대 저장 크기 : PK + SK + ITEM <= ITEM_MAX_SIZE_BYTES diff --git a/ServerCore/HttpClient/SharedHttpClient.cs b/ServerCore/HttpClient/SharedHttpClient.cs index fbeef55..ac5196c 100644 --- a/ServerCore/HttpClient/SharedHttpClient.cs +++ b/ServerCore/HttpClient/SharedHttpClient.cs @@ -9,6 +9,9 @@ using Amazon.S3.Model; namespace ServerCore; + +// HANDOVER: HttpClient SDK Wrapper 클래스 이다. + public sealed class SharedHttpClient : Singleton { private readonly HttpClient m_http_client = new(); diff --git a/ServerCore/Log/CloudWatchLog.cs b/ServerCore/Log/CloudWatchLog.cs index 2409d24..be9f5c9 100644 --- a/ServerCore/Log/CloudWatchLog.cs +++ b/ServerCore/Log/CloudWatchLog.cs @@ -9,6 +9,10 @@ using NLog.Layouts; namespace ServerCore; + +// HANDOVER: CloudWatch Log 설정을 제공 한다 + + public static class CloudWatchLog { private static bool m_is_opened = true; diff --git a/ServerCore/Log/NLog.cs b/ServerCore/Log/NLog.cs index 2752207..334f458 100644 --- a/ServerCore/Log/NLog.cs +++ b/ServerCore/Log/NLog.cs @@ -5,112 +5,118 @@ using NLog.Fluent; namespace ServerCore; +// HANDOVER: NLog SDK Wrapper 클래스 이다. +// Log.sequence() 사용시 작성된 로그를 PlantUML의 시퀀스 다이어그램으로 볼 수 있다. + //=========================================================================================== // 로그 처리자 // // author : kangms -// +// //=========================================================================================== public static class Log { - public static string NLogFileName { get; set; } = "./Config/nlog.config"; + public static string NLogFileName { get; set; } = "./Config/nlog.config"; - private static string m_process_type = string.Empty; - private static string m_ip_port = string.Empty; - private static string m_default_logger_name_pattern = string.Empty; + private static string m_process_type = string.Empty; + private static string m_ip_port = string.Empty; + private static string m_default_logger_name_pattern = string.Empty; - private static bool m_is_initialized = false; + private static bool m_is_initialized = false; - private static bool m_is_reconfigure = true; + private static bool m_is_reconfigure = true; - public static void initLog( string processType, string defaultLoggerNamePattern = "" - , EventHandler? handler = null ) + public static void initLog(string processType, string defaultLoggerNamePattern = "" + , EventHandler? handler = null) + { + m_process_type = processType; + m_default_logger_name_pattern = defaultLoggerNamePattern; + + if (null != handler) { - m_process_type = processType; - m_default_logger_name_pattern = defaultLoggerNamePattern; - - if(null != handler) - { - registerConfigurationChangedHandler(handler); - } - - m_is_initialized = true; + registerConfigurationChangedHandler(handler); } - public static void setIPnPort(string ip, UInt16 port) + m_is_initialized = true; + } + + public static void setIPnPort(string ip, UInt16 port) + { + m_ip_port = $"{ip}:{port}"; + } + + public static NLog.Logger getLogger(string loggerName = "") + { + if (true == m_is_reconfigure) { - m_ip_port = $"{ip}:{port}"; + reconfigureXML(NLogFileName); } - public static NLog.Logger getLogger(string loggerName = "") - { - if (true == m_is_reconfigure) + if (0 == loggerName.Length) + { + loggerName = m_default_logger_name_pattern; + } + + return LogManager.GetLogger(loggerName); + } + + private static void registerConfigurationChangedHandler(EventHandler handler) + { + LogManager.ConfigurationChanged += handler; + } + + private static bool reconfigureXML(string xmlCofigFilePath) + { + if (true == m_is_reconfigure) + { + var to_reload_configure = new NLog.Config.XmlLoggingConfiguration(xmlCofigFilePath); + if (null == to_reload_configure) { - reconfigureXML(NLogFileName); + return false; } - if (0 == loggerName.Length) - { - loggerName = m_default_logger_name_pattern; - } + LogManager.Configuration = to_reload_configure; + LogManager.ReconfigExistingLoggers(); - return LogManager.GetLogger(loggerName); - } - - private static void registerConfigurationChangedHandler(EventHandler handler) - { - LogManager.ConfigurationChanged += handler; + m_is_reconfigure = false; } - private static bool reconfigureXML(string xmlCofigFilePath) - { - if (true == m_is_reconfigure) - { - var to_reload_configure = new NLog.Config.XmlLoggingConfiguration(xmlCofigFilePath); - if (null == to_reload_configure) - { - return false; - } + return true; + } - LogManager.Configuration = to_reload_configure; - LogManager.ReconfigExistingLoggers(); + public static void shutdown() + { + CloudWatchLog.setClosed(); + LogManager.Shutdown(); + } - m_is_reconfigure = false; - } + public static void sequence(string sender, string message) + { + NLog.Logger sequence_logger = LogManager.GetLogger("SequenceLogger"); - return true; - } - public static void shutdown() - { - CloudWatchLog.setClosed(); - LogManager.Shutdown(); - } + LogEventInfo logEventInfo = new LogEventInfo(LogLevel.Debug, "SequenceLogger", message); + logEventInfo.Properties["sender"] = sender; + sequence_logger.Debug(logEventInfo); + } - public static void sequence(string sender, string message) - { - NLog.Logger sequence_logger = LogManager.GetLogger("SequenceLogger"); + public static void sequence(string sender, string receiver, string message, string errDesc = "") + { + NLog.Logger sequence_logger = LogManager.GetLogger("SequenceLogger"); - LogEventInfo logEventInfo = new LogEventInfo(LogLevel.Debug, "SequenceLogger", message); - logEventInfo.Properties["sender"] = sender; - sequence_logger.Debug(logEventInfo); - } - - public static void sequence(string sender, string receiver, string message, string errDesc = "") - { - NLog.Logger sequence_logger = LogManager.GetLogger("SequenceLogger"); - - LogEventInfo logEventInfo = new LogEventInfo(LogLevel.Debug, "SequenceLogger", message); - logEventInfo.Properties["sender"] = sender; - logEventInfo.Properties["receiver"] = receiver; - logEventInfo.Properties["errordesc"] = (errDesc == string.Empty || errDesc.Contains("Success") == true ? $"{errDesc}" : $"ERR-{errDesc}"); - sequence_logger.Debug(logEventInfo); - } + LogEventInfo logEventInfo = new LogEventInfo(LogLevel.Debug, "SequenceLogger", message); + logEventInfo.Properties["sender"] = sender; + logEventInfo.Properties["receiver"] = receiver; + logEventInfo.Properties["errordesc"] = (errDesc == string.Empty || errDesc.Contains("Success") == true + ? $"{errDesc}" + : $"ERR-{errDesc}"); + sequence_logger.Debug(logEventInfo); + } - public static string getProcessType() => m_process_type; + public static string getProcessType() => m_process_type; - public static string getIpPort() => m_ip_port; + public static string getIpPort() => m_ip_port; - public static bool isInitialized() => m_is_initialized; + public static bool isInitialized() => m_is_initialized; } diff --git a/ServerCore/Math/MathHelper.cs b/ServerCore/Math/MathHelper.cs index c7df902..0ed6e70 100644 --- a/ServerCore/Math/MathHelper.cs +++ b/ServerCore/Math/MathHelper.cs @@ -11,6 +11,8 @@ using Amazon.DynamoDBv2.Model; namespace ServerCore; +// HANDOVER: 수학적 처리 함수를 제공 한다. (float, double, decimal 자료형 제공) + public static class MathHelper { // The infamous ''3.14159265358979...'' value (RO). diff --git a/ServerCore/Math/QuaternionHelper.cs b/ServerCore/Math/QuaternionHelper.cs index fea95be..11b87e5 100644 --- a/ServerCore/Math/QuaternionHelper.cs +++ b/ServerCore/Math/QuaternionHelper.cs @@ -9,6 +9,10 @@ using System.Numerics; namespace ServerCore; + +// HANDOVER: 3차원 기반 회전 함수를 제공 한다. + + [Serializable] public struct QuaternionHelper { diff --git a/ServerCore/Math/TransformHelper.cs b/ServerCore/Math/TransformHelper.cs index 6e383c2..35df5d2 100644 --- a/ServerCore/Math/TransformHelper.cs +++ b/ServerCore/Math/TransformHelper.cs @@ -8,6 +8,11 @@ using System.Threading.Tasks; namespace ServerCore; + +// HANDOVER: 3차원 기반 회전 함수를 제공 한다. +// TransformHelper.rotate() 짐벌락 (Gimbal Lock) 이슈가 있기 때문에 본 함수를 사용하면 안되고, +// QuaternionHelper 클래스의 함수를 사용해야 한다. + public class TransformHelper { public static Position rotateX(Position pos, Rotation rot) diff --git a/ServerCore/MongoDB/MongoDbConnectorBase.cs b/ServerCore/MongoDB/MongoDbConnectorBase.cs index 0466a9d..c07d428 100644 --- a/ServerCore/MongoDB/MongoDbConnectorBase.cs +++ b/ServerCore/MongoDB/MongoDbConnectorBase.cs @@ -12,6 +12,8 @@ using MONGO_DB_LIST = System.String; namespace ServerCore; +// HANDOVER: MongoDB Wrapper 클래스 이고, MongoDB 연결을 위한 함수를 제공 한다. + public abstract class MongoDbConnectorBase : IDisposable { private IMongoClient? m_mongo_client; diff --git a/ServerCore/MongoDB/MongoDbRepository.cs b/ServerCore/MongoDB/MongoDbRepository.cs index c241472..e8a36db 100644 --- a/ServerCore/MongoDB/MongoDbRepository.cs +++ b/ServerCore/MongoDB/MongoDbRepository.cs @@ -4,6 +4,8 @@ namespace ServerBase; +// HANDOVER: MongoDB IMongoCollection SDK Wrapper 클래스 이다. + public class MongoDbRepository where TCollection : class { diff --git a/ServerCore/RabbitMQ/RabbitMqConnectorBase.cs b/ServerCore/RabbitMQ/RabbitMqConnectorBase.cs index 5213e6b..6d11547 100644 --- a/ServerCore/RabbitMQ/RabbitMqConnectorBase.cs +++ b/ServerCore/RabbitMQ/RabbitMqConnectorBase.cs @@ -11,6 +11,10 @@ using RabbitMQ.Client.Events; namespace ServerCore; + +// HANDOVER: RabbitMQ Wrapper 클래스 이고, 채널 생성 및 해당 채널의 이벤트를 수신 받고 처리해주는 연결 구조를 제공 한다. + + public abstract class RabbitMQConnectorBase { private readonly string m_service_name; diff --git a/ServerCore/Random/RandomHelper.cs b/ServerCore/Random/RandomHelper.cs index f93784a..18abe83 100644 --- a/ServerCore/Random/RandomHelper.cs +++ b/ServerCore/Random/RandomHelper.cs @@ -14,7 +14,7 @@ public static class RandomHelper { // 만분율을 기본 비율단위로 사용 한다 !!! public static readonly Int32 DefaultRatio = 10000; - private static Random random = new Random(); + private static Random random = new Random((int)DateTimeHelper.Current.Ticks); public static double nextDouble(double min, double max) { diff --git a/ServerCore/Redis/RedisConnectorBase.cs b/ServerCore/Redis/RedisConnectorBase.cs index b4710c4..2a7f0ed 100644 --- a/ServerCore/Redis/RedisConnectorBase.cs +++ b/ServerCore/Redis/RedisConnectorBase.cs @@ -18,6 +18,11 @@ using StackExchange.Redis.Extensions.Core.Implementations; namespace ServerCore; + +// HANDOVER: AWS S3 SDK Wrapper 클래스 이다. +// S3 저장소에 관리할 Bucket 정보를 관리해준다. + + public abstract class RedisConnectorBase { private readonly string m_connection_string = string.Empty; diff --git a/ServerCore/Redis/RedisWithLuaScriptExecutorBase.cs b/ServerCore/Redis/RedisWithLuaScriptExecutorBase.cs index 3b8f320..4955bec 100644 --- a/ServerCore/Redis/RedisWithLuaScriptExecutorBase.cs +++ b/ServerCore/Redis/RedisWithLuaScriptExecutorBase.cs @@ -11,6 +11,10 @@ using StackExchange.Redis; namespace ServerCore; + +// HANDOVER: Redis + Lua 연동 및 Lua 실행 기반 구조를 제공 한다. + + //============================================================================================= // Server-side transactional scripting in Redis // diff --git a/ServerCore/Redis/RedisWithLuaScriptExecutorHelper.cs b/ServerCore/Redis/RedisWithLuaScriptExecutorHelper.cs index ddcb713..9f0295f 100644 --- a/ServerCore/Redis/RedisWithLuaScriptExecutorHelper.cs +++ b/ServerCore/Redis/RedisWithLuaScriptExecutorHelper.cs @@ -16,6 +16,9 @@ using StackExchange.Redis; namespace ServerCore; + +// HANDOVER: RedisWithLuaScriptExecutorBase 참조하고, Redis에 저장하고자 하는 정보의 무결성을 보장해 주기위한 구조를 제공 한다. + public static class RedisWithLuaScriptExecutorHelper { //============================================================================================= diff --git a/ServerCore/S3/S3ConnectorBase.cs b/ServerCore/S3/S3ConnectorBase.cs index a2db30c..b6a7bdf 100644 --- a/ServerCore/S3/S3ConnectorBase.cs +++ b/ServerCore/S3/S3ConnectorBase.cs @@ -20,6 +20,10 @@ using Amazon.Runtime.Internal; namespace ServerCore; + +// HANDOVER: AWS S3 SDK Wrapper 클래스 이다. +// S3 저장소에 관리할 Bucket 정보를 관리해 준다. + public abstract class S3ConnectorBase { private AmazonS3Client? m_s3_client; diff --git a/ServerCore/ServerCore.csproj b/ServerCore/ServerCore.csproj index eccde22..6ed806a 100644 --- a/ServerCore/ServerCore.csproj +++ b/ServerCore/ServerCore.csproj @@ -33,15 +33,6 @@ - - - - - - - - - diff --git a/ServerCore/Task/TaskSerializer.cs b/ServerCore/Task/TaskSerializer.cs index 5309687..c57cfdb 100644 --- a/ServerCore/Task/TaskSerializer.cs +++ b/ServerCore/Task/TaskSerializer.cs @@ -17,6 +17,11 @@ using NeoSmart.AsyncLock; namespace ServerCore; + +// HANDOVER: 넘겨받은 Task를 순서가 보장된 절차로 처리해 주고, +// 중첩된 Task 실행 방지를 위해 Transaction ID로 필터링 해준다. + + //============================================================================================= // TaskSerializer에 Func 함수를 담기위한 클래스 이다. // @@ -100,7 +105,7 @@ public class TaskSerializer } return true; - } + } public async Task postLogicFunc(Func logicFunc, string logicFuncName = "") { @@ -142,11 +147,7 @@ public class TaskSerializer #endif//LOCK_LOG_ON if (null == m_processing_task || true == m_processing_task.IsCompleted) { - m_processing_task = await LogicThread.Factory.StartNew( runTask - , CancellationToken.None - , TaskCreationOptions.DenyChildAttach - , TaskScheduler.Default - ).ConfigureAwait(false); + m_processing_task = Task.Run(() => runTask()); #if TASK_SERIALIZER_LOG_ON Log.getLogger().debug( $"Create Task in TaskSerializer.enqueueLogicFunc() !!! : {logicFunc.toBasicString()}" @@ -160,49 +161,54 @@ public class TaskSerializer } } - private async Task runTask() + private async Task runTask() { - while(true) - { + while (true) + { await m_semaphore.WaitAsync().ConfigureAwait(false); - if (true == m_queue_logic_funcs.TryDequeue(out var next_logic_func)) + LogicFunction? next_logic_func = null; + + if (false == m_queue_logic_funcs.TryDequeue(out next_logic_func)) { - var trans_id = string.Empty; - Func? logic_func = null; - var logic_func_name = string.Empty; + Log.getLogger().error("Semaphore released but queue was empty in TaskSerializer.runTask() !!!"); + continue; + } - try - { - NullReferenceCheckHelper.throwIfNull(next_logic_func, () => $"next_logic_func is null !!!"); + var trans_id = string.Empty; + Func? logic_func = null; + var logic_func_name = string.Empty; - trans_id = next_logic_func.getTransId(); - logic_func = next_logic_func.getLogicFunc(); - NullReferenceCheckHelper.throwIfNull(logic_func, () => $"logic_func is null !!! - transId:{trans_id}"); + try + { + NullReferenceCheckHelper.throwIfNull(next_logic_func, () => $"next_logic_func is null !!!"); - logic_func_name = next_logic_func.getLogicFuncName(); + trans_id = next_logic_func.getTransId(); + logic_func = next_logic_func.getLogicFunc(); + NullReferenceCheckHelper.throwIfNull(logic_func, () => $"logic_func is null !!! - transId:{trans_id}"); + + logic_func_name = next_logic_func.getLogicFuncName(); #if TASK_SERIALIZER_LOG_ON - Log.getLogger().debug( $"Dequeue LogicFunc in TaskSerializer.runTask() : {next_logic_func.toBasicString()}" - + $" - QLFC:{getQueueingLogicFuncCount()}, WTIDC:{m_waiting_trans_ids.Count}"); + Log.getLogger().debug( $"Dequeue LogicFunc in TaskSerializer.runTask() : {next_logic_func.toBasicString()}" + + $" - QLFC:{getQueueingLogicFuncCount()}, WTIDC:{m_waiting_trans_ids.Count}"); #endif//TASK_SERIALIZER_LOG_ON - if (false == trans_id.isNullOrWhiteSpace()) - { - m_waiting_trans_ids.Remove(trans_id); - } + if (false == trans_id.isNullOrWhiteSpace()) + { + m_waiting_trans_ids.Remove(trans_id); + } - await logic_func().ConfigureAwait(false); + await logic_func().ConfigureAwait(false); #if TASK_SERIALIZER_LOG_ON - Log.getLogger().debug( $"logic_func().ConfigureAwait(false) in TaskSerializer.runTask() !!!" - + $" - {next_logic_func.toBasicString()}, QLFC:{getQueueingLogicFuncCount()}, WTIDC:{m_waiting_trans_ids.Count}"); + Log.getLogger().debug( $"logic_func().ConfigureAwait(false) in TaskSerializer.runTask() !!!" + + $" - {next_logic_func.toBasicString()}, QLFC:{getQueueingLogicFuncCount()}, WTIDC:{m_waiting_trans_ids.Count}"); #endif//TASK_SERIALIZER_LOG_ON - } - catch (Exception e) - { - Log.getLogger().debug( $"Failed to logic_func().ConfigureAwait(false) in TaskSerializer.runTask() !!!, Exception:{e}, {next_logic_func.toBasicString()}" - + $" - QLFC:{getQueueingLogicFuncCount()}, WTIDC:{m_waiting_trans_ids.Count}" ); - } + } + catch (Exception e) + { + Log.getLogger().debug( $"Failed to logic_func().ConfigureAwait(false) in TaskSerializer.runTask() !!!, Exception:{e}, {next_logic_func.toBasicString()}" + + $" - QLFC:{getQueueingLogicFuncCount()}, WTIDC:{m_waiting_trans_ids.Count}"); } #if LOCK_LOG_ON @@ -221,7 +227,7 @@ public class TaskSerializer NullReferenceCheckHelper.throwIfNull(next_logic_func, () => $"next_logic_func is null !!!"); - Log.getLogger().debug( $"Release Task in TaskSerializer.runTask() !!! : {next_logic_func.toBasicString()}" + Log.getLogger().debug($"Release Task in TaskSerializer.runTask() !!! : {next_logic_func.toBasicString()}" + $" - QLFC:{getQueueingLogicFuncCount()}, WTIDC:{m_waiting_trans_ids.Count}"); #if LOCK_LOG_ON @@ -236,7 +242,7 @@ public class TaskSerializer #endif//LOCK_LOG_ON } } - } + } public Int32 getQueueingLogicFuncCount() => m_queue_logic_funcs.Count; diff --git a/ServerCore/TypeHelper/JsonHelper.cs b/ServerCore/TypeHelper/JsonHelper.cs index b466a3f..487ac2f 100644 --- a/ServerCore/TypeHelper/JsonHelper.cs +++ b/ServerCore/TypeHelper/JsonHelper.cs @@ -30,6 +30,20 @@ public class JsonHelper return json_property_attribute?.PropertyName ?? propertyName; } + public static Dictionary getJsonPropertyNames(List propertyNames) + { + var json_property_names = new Dictionary(); + + foreach (var property_name in propertyNames) + { + var json_property_name = getJsonPropertyName(property_name); + + json_property_names.TryAdd(property_name, json_property_name); + } + + return json_property_names; + } + public static bool fillupKeyPaths(string jsonString, string toFindKey, out string fillupKeyPaths) { fillupKeyPaths = string.Empty; @@ -235,6 +249,11 @@ public class StringToDoubleEpsilonRoundConverter : JsonConverter NullReferenceCheckHelper.throwIfNull(reader.Value, () => $"reader.Value is null !!! - {existingValue}"); return ((double)reader.Value).withEpsilon(2); } + else if(JsonToken.Integer == reader.TokenType) + { + NullReferenceCheckHelper.throwIfNull(reader.Value, () => $"reader.Value is null !!! - {existingValue}"); + return Convert.ToDouble(reader.Value).withEpsilon(2); + } throw new JsonSerializationException($"Invalid double Type !!! : {reader.TokenType}"); } diff --git a/ServerCore/TypeHelper/StringHelper.cs b/ServerCore/TypeHelper/StringHelper.cs index 9b8b399..0c5c425 100644 --- a/ServerCore/TypeHelper/StringHelper.cs +++ b/ServerCore/TypeHelper/StringHelper.cs @@ -54,7 +54,7 @@ public static class StringHelper public static bool hasDot(this string stringValue) { - var splitted_value = stringValue.Split("."); + var splitted_value = stringValue.Split("."); if (2 <= splitted_value.Length) { return true; diff --git a/UGQApiServer/Program.cs b/UGQApiServer/Program.cs index 31222f0..85da16c 100644 --- a/UGQApiServer/Program.cs +++ b/UGQApiServer/Program.cs @@ -58,7 +58,8 @@ namespace UGQApiServer options.m_port = usingKestrel(builder, options.m_port); Log.NLogFileName = "./Config/nlog-UGQApiServer.config"; - Log.initLog("UGQApiServer", "Developer"); + Log.initLog(options.m_type.ToString(), "Developer"); + Log.setIPnPort(AwsHelper.getAwsPublicIPv4OrEthernetIPv4(), (ushort)options.m_port); UgqApiBusinessLogger.initBusinessLog(); Log.getLogger().info("startup");