From 9c70eac424aed3c9402a335ae665babb2f278b67 Mon Sep 17 00:00:00 2001 From: rfi Date: Sat, 20 Apr 2024 12:24:36 +0700 Subject: [PATCH] auth handlers --- SCHALE.Common/Database/SCHALEContext.cs | 4 +- .../NetworkProtocol/Account/AccountAuth.cs | 43 ++ .../Account/AccountCheckYostar.cs | 27 + SCHALE.Common/NetworkProtocol/ErrorPacket.cs | 11 + SCHALE.Common/NetworkProtocol/Packet.cs | 34 +- .../Queuing/QueuingGetTicket.cs | 26 + .../NetworkProtocol/WebAPIErrorCode.cs | 542 ++++++++++++++++++ .../Controllers/Api/GatewayController.cs | 59 +- .../Api/ProtocolHandlers/Account.cs | 31 + .../ProtocolHandlerFactory.cs | 78 +++ .../Api/ProtocolHandlers/Queuing.cs | 17 + .../Controllers/UserController.cs | 1 + SCHALE.GameServer/GameServer.cs | 2 + .../appsettings.Development.json | 5 + 14 files changed, 847 insertions(+), 33 deletions(-) create mode 100644 SCHALE.Common/NetworkProtocol/Account/AccountAuth.cs create mode 100644 SCHALE.Common/NetworkProtocol/Account/AccountCheckYostar.cs create mode 100644 SCHALE.Common/NetworkProtocol/ErrorPacket.cs create mode 100644 SCHALE.Common/NetworkProtocol/Queuing/QueuingGetTicket.cs create mode 100644 SCHALE.Common/NetworkProtocol/WebAPIErrorCode.cs create mode 100644 SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Account.cs create mode 100644 SCHALE.GameServer/Controllers/Api/ProtocolHandlers/ProtocolHandlerFactory.cs create mode 100644 SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Queuing.cs diff --git a/SCHALE.Common/Database/SCHALEContext.cs b/SCHALE.Common/Database/SCHALEContext.cs index 73d67f5..399c033 100644 --- a/SCHALE.Common/Database/SCHALEContext.cs +++ b/SCHALE.Common/Database/SCHALEContext.cs @@ -48,11 +48,11 @@ namespace SCHALE.Common.Database if (counter is null) { counter = new Counter() { Id = Collection, Seq = 0 }; - context.Counters.Add(counter); + context.Add(counter); + context.SaveChanges(); } counter.Seq++; - context.Update(counter); return counter.Seq; } diff --git a/SCHALE.Common/NetworkProtocol/Account/AccountAuth.cs b/SCHALE.Common/NetworkProtocol/Account/AccountAuth.cs new file mode 100644 index 0000000..0d9ba20 --- /dev/null +++ b/SCHALE.Common/NetworkProtocol/Account/AccountAuth.cs @@ -0,0 +1,43 @@ +using SCHALE.Common.Database.Models; +namespace SCHALE.Common.NetworkProtocol.Account +{ + public class AccountAuthResponse : ResponsePacket + { + public override Protocol Protocol => Protocol.Account_Auth; + public long CurrentVersion { get; set; } + public long MinimumVersion { get; set; } + public bool IsDevelopment { get; set; } + public bool UpdateRequired { get; set; } + public required string TTSCdnUri { get; set; } + /*public AccountDB AccountDB { get; set; } + public IEnumerable AttendanceBookRewards { get; set; } + public IEnumerable AttendanceHistoryDBs { get; set; } + public IEnumerable OpenConditions { get; set; } + public IEnumerable RepurchasableMonthlyProductCountDBs { get; set; } + public IEnumerable MonthlyProductParcel { get; set; } + public IEnumerable MonthlyProductMail { get; set; } + public IEnumerable BiweeklyProductParcel { get; set; } + public IEnumerable BiweeklyProductMail { get; set; } + public IEnumerable WeeklyProductParcel { get; set; } + public IEnumerable WeeklyProductMail { get; set; }*/ + public required string EncryptedUID { get; set; } + } + + + public class AccountAuthRequest : RequestPacket + { + public override Protocol Protocol => Protocol.Account_Auth; + public long Version { get; set; } + public string? DevId { get; set; } + public long IMEI { get; set; } + public string AccessIP { get; set; } = string.Empty; + public string MarketId { get; set; } = string.Empty; + public string? UserType { get; set; } + public string? AdvertisementId { get; set; } + public string OSType { get; set; } = string.Empty; + public string OSVersion { get; set; } = string.Empty; + public string DeviceUniqueId { get; set; } = string.Empty; + public string DeviceModel { get; set; } = string.Empty; + public int DeviceSystemMemorySize { get; set; } + } +} diff --git a/SCHALE.Common/NetworkProtocol/Account/AccountCheckYostar.cs b/SCHALE.Common/NetworkProtocol/Account/AccountCheckYostar.cs new file mode 100644 index 0000000..527b67f --- /dev/null +++ b/SCHALE.Common/NetworkProtocol/Account/AccountCheckYostar.cs @@ -0,0 +1,27 @@ +namespace SCHALE.Common.NetworkProtocol.Account +{ + public class AccountCheckYostarRequest : RequestPacket + { + public override Protocol Protocol => NetworkProtocol.Protocol.Account_CheckYostar; + public long UID { get; set; } + public string YostarToken { get; set; } = string.Empty; + public string EnterTicket { get; set; } = string.Empty; + public bool PassCookieResult { get; set; } + public string Cookie { get; set; } = string.Empty; + } + + + public class AccountCheckYostarResponse : ResponsePacket + { + public override Protocol Protocol + { + get + { + return NetworkProtocol.Protocol.Account_CheckYostar; + } + } + public int ResultState { get; set; } + public string ResultMessag { get; set; } = string.Empty; + public string Birth { get; set; } = string.Empty; + } +} diff --git a/SCHALE.Common/NetworkProtocol/ErrorPacket.cs b/SCHALE.Common/NetworkProtocol/ErrorPacket.cs new file mode 100644 index 0000000..5ec0693 --- /dev/null +++ b/SCHALE.Common/NetworkProtocol/ErrorPacket.cs @@ -0,0 +1,11 @@ +namespace SCHALE.Common.NetworkProtocol +{ + public class ErrorPacket : ResponsePacket + { + public override Protocol Protocol => Protocol.Error; + + public string? Reason { get; set; } + + public WebAPIErrorCode ErrorCode { get; set; } + } +} diff --git a/SCHALE.Common/NetworkProtocol/Packet.cs b/SCHALE.Common/NetworkProtocol/Packet.cs index 544a401..a1118d4 100644 --- a/SCHALE.Common/NetworkProtocol/Packet.cs +++ b/SCHALE.Common/NetworkProtocol/Packet.cs @@ -1,18 +1,17 @@ -namespace SCHALE.Common.NetworkProtocol +using System.Text.Json.Serialization; + +namespace SCHALE.Common.NetworkProtocol { public class SessionKey { public long AccountServerId { get; set; } - public required string MxToken { get; set; } } public abstract class BasePacket { public SessionKey? SessionKey { get; set; } - public abstract Protocol Protocol { get; } - public long AccountId => SessionKey?.AccountServerId ?? 0; } @@ -21,14 +20,9 @@ private static int _counter; public int ClientUpTime { get; set; } - public bool Resendable { get; set; } = true; - - public long Hash { get; set; } - public bool IsTest { get; set; } - public DateTime? ModifiedServerTime__DebugOnly { get; set; } public static long CreateHash(Protocol protocol) @@ -37,51 +31,33 @@ } } + // TODO: Fix properties public abstract class ResponsePacket : BasePacket { - public long ServerTimeTicks { get; set; } - + public long ServerTimeTicks { get; set; } = DateTimeOffset.Now.Ticks; public ServerNotificationFlag ServerNotification { get; set; } - // public List MissionProgressDBs { get; set; } - // public Dictionary> EventMissionProgressDBDict { get; set; } - // public Dictionary StaticOpenConditions { get; set; } } [Flags] public enum ServerNotificationFlag { - // Token: 0x04009560 RID: 38240 None = 0, - // Token: 0x04009561 RID: 38241 NewMailArrived = 4, - // Token: 0x04009562 RID: 38242 HasUnreadMail = 8, - // Token: 0x04009563 RID: 38243 NewToastDetected = 16, - // Token: 0x04009564 RID: 38244 CanReceiveArenaDailyReward = 32, - // Token: 0x04009565 RID: 38245 CanReceiveRaidReward = 64, - // Token: 0x04009566 RID: 38246 ServerMaintenance = 256, - // Token: 0x04009567 RID: 38247 CannotReceiveMail = 512, - // Token: 0x04009568 RID: 38248 InventoryFullRewardMail = 1024, - // Token: 0x04009569 RID: 38249 CanReceiveClanAttendanceReward = 2048, - // Token: 0x0400956A RID: 38250 HasClanApplicant = 4096, - // Token: 0x0400956B RID: 38251 HasFriendRequest = 8192, - // Token: 0x0400956C RID: 38252 CheckConquest = 16384, - // Token: 0x0400956D RID: 38253 CanReceiveEliminateRaidReward = 32768, - // Token: 0x0400956E RID: 38254 CanReceiveMultiFloorRaidReward = 65536 } } diff --git a/SCHALE.Common/NetworkProtocol/Queuing/QueuingGetTicket.cs b/SCHALE.Common/NetworkProtocol/Queuing/QueuingGetTicket.cs new file mode 100644 index 0000000..c0421de --- /dev/null +++ b/SCHALE.Common/NetworkProtocol/Queuing/QueuingGetTicket.cs @@ -0,0 +1,26 @@ +namespace SCHALE.Common.NetworkProtocol.Queuing +{ + public class QueuingGetTicketRequest : RequestPacket + { + public override Protocol Protocol => Protocol.Queuing_GetTicket; + public long YostarUID { get; set; } + public required string YostarToken { get; set; } + public bool MakeStandby { get; set; } + public bool PassCheck { get; set; } + public bool PassCheckYostar { get; set; } + public string WaitingTicket { get; set; } = string.Empty; + public string ClientVersion { get; set; } = "0.0.0"; + } + + public class QueuingGetTicketResponse : ResponsePacket + { + public override Protocol Protocol => Protocol.Queuing_GetTicket; + public string WaitingTicket { get; set; } = string.Empty; + public string EnterTicket { get; set; } = string.Empty; + public long TicketSequence { get; set; } + public long AllowedSequence { get; set; } + public double RequiredSecondsPerUser { get; set; } + public string Birth { get; set; } = string.Empty; + public string ServerSeed { get; set; } = string.Empty; + } +} diff --git a/SCHALE.Common/NetworkProtocol/WebAPIErrorCode.cs b/SCHALE.Common/NetworkProtocol/WebAPIErrorCode.cs new file mode 100644 index 0000000..9804202 --- /dev/null +++ b/SCHALE.Common/NetworkProtocol/WebAPIErrorCode.cs @@ -0,0 +1,542 @@ +namespace SCHALE.Common.NetworkProtocol +{ + public enum WebAPIErrorCode + { + None = 0, + InvalidPacket = 1, + InvalidProtocol = 2, + InvalidSession = 3, + InvalidVersion = 4, + InternalServerError = 5, + DBError = 6, + InvalidToken = 7, + FailedToLockAccount = 8, + InvalidCheatError = 9, + AccountCurrencyCannotAffordCost = 10, + ExceedTranscendenceCountLimit = 11, + MailBoxFull = 12, + InventoryAlreadyFull = 13, + AccountNotFound = 14, + DataClassNotFound = 15, + DataEntityNotFound = 16, + AccountGemPaidCannotAffordCost = 17, + AccountGemBonusCannotAffordCost = 18, + AccountItemCannotAffordCost = 19, + APITimeoutError = 20, + FunctionTimeoutError = 21, + DBDistributeTransactionError = 22, + OccasionalJobError = 23, + FailedToConsumeParcel = 100, + InvalidString = 200, + InvalidStringLength = 201, + EmptyString = 202, + SpecialSymbolNotAllowed = 203, + InvalidDate = 300, + CoolTimeRemain = 301, + TimeElapseError = 302, + ClientSendBadRequest = 400, + ClientSendTooManyRequest = 401, + ClientSuspectedAsCheater = 402, + ServerFailedToHandleRequest = 500, + DocumentDBFailedToHandleRequest = 501, + ServerCacheFailedToHandleRequest = 502, + ReconnectBundleUpdateRequired = 800, + GatewayMakeStandbyNotSupport = 900, + GatewayPassCheckNotSupport = 901, + GatewayWaitingTicketTimeOut = 902, + ClientUpdateRequire = 903, + AccountCreateNoDevId = 1000, + AccountCreateDuplicatedDevId = 1001, + AccountAuthEmptyDevId = 1002, + AccountAuthNotCreated = 1003, + AccountAccessControlWithoutPermission = 1004, + AccountNicknameEmptyString = 1005, + AccountNicknameSameName = 1006, + AccountNicknameWithInvalidString = 1007, + AccountNicknameWithInvalidLength = 1008, + YostarServerNotSuccessStatusCode = 1009, + YostarNetworkException = 1010, + YostarException = 1011, + AccoountPassCheckNotSupportCheat = 1012, + AccountCreateFail = 1013, + AccountAddPubliserAccountFail = 1014, + AccountAddDevIdFail = 1015, + AccountCreateAlreadyPublisherAccoundId = 1016, + AccountUpdateStateFail = 1017, + YostarCheckFail = 1018, + EnterTicketInvalid = 1019, + EnterTicketTimeOut = 1020, + EnterTicketUsed = 1021, + AccountCommentLengthOverLimit = 1022, + AccountUpdateBirthdayFailed = 1023, + AccountLoginError = 1024, + AccountCurrencySyncError = 1025, + InvalidClientCookie = 1026, + CharacterNotFound = 2000, + CharacterLocked = 2001, + CharacterAlreadyHas = 2002, + CharacterAssignedEchelon = 2003, + CharacterFavorDownException = 2004, + CharacterFavorMaxLevelExceed = 2005, + CannotLevelUpSkill = 2006, + CharacterLevelAlreadyMax = 2007, + InvalidCharacterExpGrowthRequest = 2008, + CharacterWeaponDataNotFound = 2009, + CharacterWeaponNotFound = 2010, + CharacterWeaponAlreadyUnlocked = 2011, + CharacterWeaponUnlockConditionFail = 2012, + CharacterWeaponExpGrowthNotValidItem = 2013, + InvalidCharacterWeaponExpGrowthRequest = 2014, + CharacterWeaponTranscendenceRecipeNotFound = 2015, + CharacterWeaponTranscendenceConditionFail = 2016, + CharacterWeaponUpdateFail = 2017, + CharacterGearNotFound = 2018, + CharacterGearAlreadyEquiped = 2019, + CharacterGearCannotTierUp = 2020, + CharacterGearCannotUnlock = 2021, + CharacterCostumeNotFound = 2022, + CharacterCostumeAlreadySet = 2023, + CharacterCannotEquipCostume = 2024, + InvalidCharacterSkillLevelUpdateRequest = 2025, + InvalidCharacterPotentialGrowthRequest = 2026, + CharacterPotentialGrowthDataNotFound = 2027, + EquipmentNotFound = 3000, + InvalidEquipmentExpGrowthRequest = 3001, + EquipmentNotMatchingSlotItemCategory = 3002, + EquipmentLocked = 3003, + EquipmentAlreadyEquiped = 3004, + EquipmentConsumeItemLimitCountOver = 3005, + EquipmentNotEquiped = 3006, + EquipmentCanNotEquip = 3007, + EquipmentIngredientEmtpy = 3008, + EquipmentCannotLevelUp = 3009, + EquipmentCannotTierUp = 3010, + EquipmentGearCannotUnlock = 3011, + EquipmentBatchGrowthNotValid = 3012, + ItemNotFound = 4000, + ItemLocked = 4001, + ItemCreateWithoutStackCount = 4002, + ItemCreateStackCountFull = 4003, + ItemNotUsingType = 4004, + ItemEnchantIngredientFail = 4005, + ItemInvalidConsumeRequest = 4006, + ItemInsufficientStackCount = 4007, + ItemOverExpirationDateTime = 4008, + ItemCannotAutoSynth = 4009, + EchelonEmptyLeader = 5000, + EchelonNotFound = 5001, + EchelonNotDeployed = 5002, + EchelonSlotOverMaxCount = 5003, + EchelonAssignCharacterOnOtherEchelon = 5004, + EchelonTypeNotAcceptable = 5005, + EchelonEmptyNotAcceptable = 5006, + EchelonPresetInvalidSave = 5007, + EchelonPresetLabelLengthInvalid = 5008, + CampaignStageNotOpen = 6000, + CampaignStagePlayLimit = 6001, + CampaignStageEnterFail = 6002, + CampaignStageInvalidSaveData = 6003, + CampaignStageNotPlayerTurn = 6004, + CampaignStageStageNotFound = 6005, + CampaignStageHistoryNotFound = 6006, + CampaignStageChapterNotFound = 6007, + CampaignStageEchelonNotFound = 6008, + CampaignStageWithdrawedCannotReUse = 6009, + CampaignStageChapterRewardInvalidReward = 6010, + CampaignStageChapterRewardAlreadyReceived = 6011, + CampaignStageTacticWinnerInvalid = 6012, + CampaignStageActionCountZero = 6013, + CampaignStageHealNotAcceptable = 6014, + CampaignStageHealLimit = 6015, + CampaignStageLocationCanNotEngage = 6016, + CampaignEncounterWaitingCannotEndTurn = 6017, + CampaignTacticResultEmpty = 6018, + CampaignPortalExitNotFound = 6019, + CampaignCannotReachDestination = 6020, + CampaignChapterRewardConditionNotSatisfied = 6021, + CampaignStageDataInvalid = 6022, + ContentSweepNotOpened = 6023, + CampaignTacticSkipFailed = 6024, + CampaignUnableToRemoveFixedEchelon = 6025, + CampaignCharacterIsNotWhitelist = 6026, + CampaignFailedToSkipStrategy = 6027, + InvalidSweepRequest = 6028, + MailReceiveRequestInvalid = 7000, + MissionCannotComplete = 8000, + MissionRewardInvalid = 8001, + AttendanceInvalid = 9000, + ShopExcelNotFound = 10000, + ShopAndGoodsNotMatched = 10001, + ShopGoodsNotFound = 10002, + ShopExceedPurchaseCountLimit = 10003, + ShopCannotRefresh = 10004, + ShopInfoNotFound = 10005, + ShopCannotPurchaseActionPointLimitOver = 10006, + ShopNotOpened = 10007, + ShopInvalidGoods = 10008, + ShopInvalidCostOrReward = 10009, + ShopEligmaOverPurchase = 10010, + ShopFreeRecruitInvalid = 10011, + ShopNewbieGachaInvalid = 10012, + ShopCannotNewGoodsRefresh = 10013, + GachaCostNotValid = 10014, + ShopRestrictBuyWhenInventoryFull = 10015, + BeforehandGachaMetadataNotFound = 10016, + BeforehandGachaCandidateNotFound = 10017, + BeforehandGachaInvalidLastIndex = 10018, + BeforehandGachaInvalidSaveIndex = 10019, + BeforehandGachaInvalidPickIndex = 10020, + BeforehandGachaDuplicatedResults = 10021, + RecipeCraftNoData = 11000, + RecipeCraftInsufficientIngredients = 11001, + RecipeCraftDataError = 11002, + MemoryLobbyNotFound = 12000, + LobbyModeChangeFailed = 12001, + CumulativeTimeRewardNotFound = 13000, + CumulativeTimeRewardAlreadyReceipt = 13001, + CumulativeTimeRewardInsufficientConnectionTime = 13002, + OpenConditionClosed = 14000, + OpenConditionSetNotSupport = 14001, + CafeNotFound = 15000, + CafeFurnitureNotFound = 15001, + CafeDeployFail = 15002, + CafeRelocateFail = 15003, + CafeInteractionNotFound = 15004, + CafeProductionEmpty = 15005, + CafeRankUpFail = 15006, + CafePresetNotFound = 15007, + CafeRenamePresetFail = 15008, + CafeClearPresetFail = 15009, + CafeUpdatePresetFurnitureFail = 15010, + CafeReservePresetActivationTimeFail = 15011, + CafePresetApplyFail = 15012, + CafePresetIsEmpty = 15013, + CafeAlreadyVisitCharacter = 15014, + CafeCannotSummonCharacter = 15015, + CafeCanRefreshVisitCharacter = 15016, + CafeAlreadyInteraction = 15017, + CafeTemplateNotFound = 15018, + CafeAlreadyOpened = 15019, + ScenarioMode_Fail = 16000, + ScenarioMode_DuplicatedScenarioModeId = 16001, + ScenarioMode_LimitClearedScenario = 16002, + ScenarioMode_LimitAccountLevel = 16003, + ScenarioMode_LimitClearedStage = 16004, + ScenarioMode_LimitClubStudent = 16005, + ScenarioMode_FailInDBProcess = 16006, + ScenarioGroup_DuplicatedScenarioGroupId = 16007, + ScenarioGroup_FailInDBProcess = 16008, + ScenarioGroup_DataNotFound = 16009, + ScenarioGroup_MeetupConditionFail = 16010, + CraftInfoNotFound = 17000, + CraftCanNotCreateNode = 17001, + CraftCanNotUpdateNode = 17002, + CraftCanNotBeginProcess = 17003, + CraftNodeDepthError = 17004, + CraftAlreadyProcessing = 17005, + CraftCanNotCompleteProcess = 17006, + CraftProcessNotComplete = 17007, + CraftInvalidIngredient = 17008, + CraftError = 17009, + CraftInvalidData = 17010, + CraftNotAvailableToCafePresets = 17011, + CraftNotEnoughEmptySlotCount = 17012, + CraftInvalidPresetSlotDB = 17013, + RaidExcelDataNotFound = 18000, + RaidSeasonNotOpen = 18001, + RaidDBDataNotFound = 18002, + RaidBattleNotFound = 18003, + RaidBattleUpdateFail = 18004, + RaidCompleteListEmpty = 18005, + RaidRoomCanNotCreate = 18006, + RaidActionPointZero = 18007, + RaidTicketZero = 18008, + RaidRoomCanNotJoin = 18009, + RaidRoomMaxPlayer = 18010, + RaidRewardDataNotFound = 18011, + RaidSeasonRewardNotFound = 18012, + RaidSeasonAlreadyReceiveReward = 18013, + RaidSeasonAddRewardPointError = 18014, + RaidSeasonRewardNotUpdate = 18015, + RaidSeasonReceiveRewardFail = 18016, + RaidSearchNotFound = 18017, + RaidShareNotFound = 18018, + RaidEndRewardFlagError = 18019, + RaidCanNotFoundPlayer = 18020, + RaidAlreadyParticipateCharacters = 18021, + RaidClearHistoryNotSave = 18022, + RaidBattleAlreadyEnd = 18023, + RaidEchelonNotFound = 18024, + RaidSeasonOpen = 18025, + RaidRoomIsAlreadyClose = 18026, + RaidRankingNotFound = 18027, + WeekDungeonInfoNotFound = 19000, + WeekDungeonNotOpenToday = 19001, + WeekDungeonBattleWinnerInvalid = 19002, + WeekDungeonInvalidSaveData = 19003, + FindGiftRewardNotFound = 20000, + FindGiftRewardAlreadyAcquired = 20001, + FindGiftClearCountOverTotalCount = 20002, + ArenaInfoNotFound = 21000, + ArenaGroupNotFound = 21001, + ArenaRankHistoryNotFound = 21002, + ArenaRankInvalid = 21003, + ArenaBattleFail = 21004, + ArenaDailyRewardAlreadyBeenReceived = 21005, + ArenaNoSeasonAvailable = 21006, + ArenaAttackCoolTime = 21007, + ArenaOpponentAlreadyBeenAttacked = 21008, + ArenaOpponentRankInvalid = 21009, + ArenaNeedFormationSetting = 21010, + ArenaNoHistory = 21011, + ArenaInvalidRequest = 21012, + ArenaInvalidIndex = 21013, + ArenaNotFoundBattle = 21014, + ArenaBattleTimeOver = 21015, + ArenaRefreshTimeOver = 21016, + ArenaEchelonSettingTimeOver = 21017, + ArenaCannotReceiveReward = 21018, + ArenaRewardNotExist = 21019, + ArenaCannotSetMap = 21020, + ArenaDefenderRankChange = 21021, + AcademyNotFound = 22000, + AcademyScheduleTableNotFound = 22001, + AcademyScheduleOperationNotFound = 22002, + AcademyAlreadyAttendedSchedule = 22003, + AcademyAlreadyAttendedFavorSchedule = 22004, + AcademyRewardCharacterNotFound = 22005, + AcademyScheduleCanNotAttend = 22006, + AcademyTicketZero = 22007, + AcademyMessageCanNotSend = 22008, + ContentSaveDBNotFound = 26000, + ContentSaveDBEntranceFeeEmpty = 26001, + AccountBanned = 27000, + ServerNowLoadingProhibitedWord = 28000, + ServerIsUnderMaintenance = 28001, + ServerMaintenanceSoon = 28002, + AccountIsNotInWhiteList = 28003, + ServerContentsLockUpdating = 28004, + ServerContentsLock = 28005, + CouponIsEmpty = 29000, + CouponIsInvalid = 29001, + UseCouponUsedListReadFail = 29002, + UseCouponUsedCoupon = 29003, + UseCouponNotFoundSerials = 29004, + UseCouponDeleteSerials = 29005, + UseCouponUnapprovedSerials = 29006, + UseCouponExpiredSerials = 29007, + UseCouponMaximumSerials = 29008, + UseCouponNotFoundMeta = 29009, + UseCouponDuplicateUseCoupon = 29010, + UseCouponDuplicateUseSerial = 29011, + BillingStartShopCashIdNotFound = 30000, + BillingStartNotServiceTime = 30001, + BillingStartUseConditionCheckError = 30002, + BillingStartSmallLevel = 30003, + BillingStartMaxPurchaseCount = 30004, + BillingStartFailAddOrder = 30005, + BillingStartExistPurchase = 30006, + BillingEndFailGetOrder = 30007, + BillingEndShopCashIdNotFound = 30008, + BillingEndProductIdNotFound = 30009, + BillingEndMonthlyProductIdNotFound = 30010, + BillingEndInvalidState = 30011, + BillingEndFailUpdteState = 30012, + BillingEndFailSendMail = 30013, + BillingEndInvalidAccount = 30014, + BillingEndNotFoundPurchaseCount = 30015, + BillingEndFailUpdteMonthlyProduct = 30016, + BillingStartMailFull = 30017, + BillingStartInventoryAndMailFull = 30018, + BillingEndRecvedErrorMonthlyProduct = 30019, + MonthlyProductNotOutdated = 30020, + ClanNotFound = 31000, + ClanSearchFailed = 31001, + ClanEmptySearchString = 31002, + ClanAccountAlreadyJoinedClan = 31003, + ClanAccountAlreadyQuitClan = 31004, + ClanCreateFailed = 31005, + ClanMemberExceedCapacity = 31006, + ClanDoesNotHavePermission = 31007, + ClanTargetAccountIsNotApplicant = 31008, + ClanMemberNotFound = 31009, + ClanCanNotKick = 31010, + ClanCanNotDismiss = 31011, + ClanCanNotQuit = 31012, + ClanRejoinCoolOff = 31013, + ClanChangeMemberGradeFailed = 31014, + ClanHasBeenDisMissed = 31015, + ClanCannotChangeJoinOption = 31016, + ClanExceedConferCountLimit = 31017, + ClanBusy = 31018, + ClanNameEmptyString = 31019, + ClanNameWithInvalidLength = 31020, + ClanAssistCharacterAlreadyDeployed = 31021, + ClanAssistNotValidUse = 31022, + ClanAssistCharacterChanged = 31023, + ClanAssistCoolTime = 31024, + ClanAssistAlreadyUsedInRaidRoom = 31025, + ClanAssistAlreadyUsedInTimeAttackDungeonRoom = 31026, + ClanAssistEchelonHasAssistOnly = 31027, + PaymentInvalidSign = 32000, + PaymentInvalidSeed1 = 32001, + PaymentInvalidSeed2 = 32002, + PaymentInvalidInput = 32003, + PaymentNotFoundPurchase = 32004, + PaymentGetPurchaseOrderNotZero = 32005, + PaymentSetPurchaseOrderNotZero = 32006, + PaymentException = 32007, + PaymentInvalidState = 32008, + SessionNotFound = 33000, + SessionParseFail = 33001, + SessionInvalidInput = 33002, + SessionNotAuth = 33003, + SessionDuplicateLogin = 33004, + SessionTimeOver = 33005, + SessionInvalidVersion = 33006, + SessionChangeDate = 33007, + CallName_RenameCoolTime = 34000, + CallName_EmptyString = 34001, + CallName_InvalidString = 34002, + CallName_TTSServerIsNotAvailable = 34003, + CouchbaseInvalidCas = 35000, + CouchbaseOperationFailed = 35001, + CouchbaseRollBackFailed = 35002, + EventContentCannotSelectBuff = 36000, + EventContentNoBuffGroupAvailable = 36001, + EventContentBuffGroupIdDuplicated = 36002, + EventContentNotOpen = 36003, + EventContentNoTotalRewardAvailable = 36004, + EventContentBoxGachaPurchaseFailed = 36005, + EventContentBoxGachaCannotRefresh = 36006, + EventContentCardShopCannotShuffle = 36007, + EventContentElementDoesNotExist = 36008, + EventContentElementAlreadyPurchased = 36009, + EventContentLocationNotFound = 36010, + EventContentLocationScheduleCanNotAttend = 36011, + EventContentDiceRaceDataNotFound = 36012, + EventContentDiceRaceAlreadyReceiveLapRewardAll = 36013, + EventContentDiceRaceInvalidDiceRaceResultType = 36014, + EventContentTreasureDataNotFound = 36015, + EventContentTreasureNotComplete = 36016, + EventContentTreasureFlipFailed = 36017, + MiniGameStageIsNotOpen = 37000, + MiniGameStageInvalidResult = 37001, + MiniGameShootingStageInvlid = 37002, + MiniGameShootingCannotSweep = 37003, + MiniGameTableBoardSaveNotExist = 37004, + MiniGameTableBoardPlayerCannotMove = 37005, + MiniGameTableBoardNoActiveEncounter = 37006, + MiniGameTableBoardInvalidEncounterRequest = 37007, + MiniGameTableBoardProcessEncounterFailed = 37008, + MiniGameTableBoardItemNotExist = 37009, + MiniGameTableBoardInvalidItemUse = 37010, + MiniGameTableBoardInvalidClearThemaRequest = 37011, + MiniGameTableBoardInvalidSeason = 37012, + MiniGameTableBoardInvalidResurrectRequest = 37013, + MiniGameTableBoardSweepConditionFail = 37014, + MiniGameTableBoardInvalidData = 37015, + ProofTokenNotSubmitted = 38000, + SchoolDungeonInfoNotFound = 39000, + SchoolDungeonNotOpened = 39001, + SchoolDungeonInvalidSaveData = 39002, + SchoolDungeonBattleWinnerInvalid = 39003, + SchoolDungeonInvalidReward = 39004, + TimeAttackDungeonDataNotFound = 40000, + TimeAttackDungeonNotOpen = 40001, + TimeAttackDungeonRoomTimeOut = 40002, + TimeAttackDungeonRoomPlayCountOver = 40003, + TimeAttackDungeonRoomAlreadyExists = 40004, + TimeAttackDungeonRoomAlreadyClosed = 40005, + TimeAttackDungeonRoomNotExist = 40006, + TimeAttackDungeonInvalidRequest = 40007, + TimeAttackDungeonInvalidData = 40008, + WorldRaidDataNotFound = 41000, + WorldRaidSeasonNotOpen = 41001, + WorldRaidBossGroupNotOpen = 41002, + WorldRaidInvalidOpenCondition = 41003, + WorldRaidDifficultyNotOpen = 41004, + WorldRaidAssistCharacterLimitOver = 41005, + WorldRaidContainBlackListCharacter = 41006, + WorldRaidValidFixedEchelonSetting = 41007, + WorldRaidAlredayReceiveRewardAll = 41008, + WorldRaidCannotReceiveReward = 41009, + WorldRaidBossAlreadyDead = 41010, + WorldRaidNotAnotherBossKilled = 41011, + WorldRaidBattleResultUpdateFailed = 41012, + WorldRaidGemEnterCountLimitOver = 41013, + WorldRaidCannotGemEnter = 41014, + WorldRaidNeedClearScenarioBoss = 41015, + WorldRaidBossIsAlive = 41016, + ConquestDataNotFound = 42000, + ConquestAlreadyConquested = 42001, + ConquestNotFullyConquested = 42002, + ConquestStepNotOpened = 42003, + ConquestUnableToReach = 42004, + ConquestUnableToAttack = 42005, + ConquestEchelonChangedCountMax = 42006, + ConquestEchelonNotFound = 42007, + ConquestCharacterAlreadyDeployed = 42008, + ConquestMaxUpgrade = 42009, + ConquestUnitNotFound = 42010, + ConquestObjectNotFound = 42011, + ConquestCalculateRewardNotFound = 42012, + ConquestInvalidTileType = 42013, + ConquestInvalidObjectType = 42014, + ConquestInvalidSaveData = 42015, + ConquestMaxAssistCountReached = 42016, + ConquestErosionConditionNotSatisfied = 42017, + ConquestAdditionalContentNotInUse = 42018, + ConquestCannotUseManageEchelon = 42019, + FriendUserIsNotFriend = 43000, + FriendFailedToCreateFriendIdCard = 43001, + FriendRequestNotFound = 43002, + FriendInvalidFriendCode = 43003, + FriendAlreadyFriend = 43004, + FriendMaxSentRequestReached = 43005, + FriendMaxReceivedRequestReached = 43006, + FriendCannotRequestMaxFriendCountReached = 43007, + FriendCannotAcceptMaxFriendCountReached = 43008, + FriendOpponentMaxFriendCountReached = 43009, + FriendTargetIsBusy = 43010, + FriendRequestTargetIsYourself = 43011, + FriendSearchTargetIsYourself = 43012, + FriendInvalidBackgroundId = 43013, + FriendIdCardCommentLengthOverLimit = 43014, + FriendBackgroundNotOwned = 43015, + EliminateStageIsNotOpened = 44000, + MultiSweepPresetDocumentNotFound = 45000, + MultiSweepPresetNameEmpty = 45001, + MultiSweepPresetInvalidStageId = 45002, + MultiSweepPresetInvalidId = 45003, + MultiSweepPresetNameInvalidLength = 45004, + EmblemDataNotFound = 46000, + EmblemAttachFailed = 46001, + EmblemCannotReceive = 46002, + EmblemPassCheckEmblemIsEmpty = 46003, + StickerDataNotFound = 47000, + StickerNotAcquired = 47001, + StickerDocumentNotFound = 47002, + StickerAlreadyUsed = 47003, + ClearDeckInvalidKey = 48000, + ClearDeckOutOfDate = 48001, + MultiFloorRaidSeasonNotOpened = 49000, + MultiFloorRaidDataNotFound = 49001, + MultiFloorRaidAssistCharacterLimitOver = 49002, + MultiFloorRaidStageOpenConditionFail = 49003, + MultiFloorRaidInvalidSummary = 49004, + MultiFloorRaidInvalidRewardRequest = 49005, + FieldDataNotFound = 60000, + FieldInteracionFailed = 60001, + FieldQuestClearFailed = 60002, + FieldInvalidSceneChangedRequest = 60003, + FieldInvalidEndDateRequest = 60004, + FieldCreateDailyQuestFailed = 60005, + FieldResetReplayFailed = 60006, + FieldIncreaseMasteryFailed = 60007, + FieldStageDataInvalid = 60008, + FieldStageEnterFail = 60009, + FieldContentIsClosed = 60010, + FieldEventStageNotCleared = 60011 + } +} diff --git a/SCHALE.GameServer/Controllers/Api/GatewayController.cs b/SCHALE.GameServer/Controllers/Api/GatewayController.cs index 050ec51..58c8b9e 100644 --- a/SCHALE.GameServer/Controllers/Api/GatewayController.cs +++ b/SCHALE.GameServer/Controllers/Api/GatewayController.cs @@ -1,13 +1,26 @@ using Microsoft.AspNetCore.Mvc; using SCHALE.Common.Crypto; +using SCHALE.Common.NetworkProtocol; +using SCHALE.GameServer.Controllers.Api.ProtocolHandlers; +using System.Buffers.Binary; using System.IO.Compression; using System.Text; +using System.Text.Json; namespace SCHALE.GameServer.Controllers.Api { [Route("/api/[controller]")] public class GatewayController : ControllerBase { + IProtocolHandlerFactory protocolHandlerFactory; + ILogger logger; + + public GatewayController(IProtocolHandlerFactory _protocolHandlerFactory, ILogger _logger) + { + protocolHandlerFactory = _protocolHandlerFactory; + logger = _logger; + } + [HttpPost] public IResult GatewayRequest() { @@ -17,7 +30,11 @@ namespace SCHALE.GameServer.Controllers.Api using var reader = new BinaryReader(formFile.OpenReadStream()); - // CRC + Protocol type conversion (?) + // CRC + Protocol type conversion + payload length + reader.BaseStream.Position = 4; + + var protocol = (Protocol)BinaryPrimitives.ReadUInt32LittleEndian(reader.ReadBytes(4)); + reader.BaseStream.Position = 12; byte[] compressedPayload = reader.ReadBytes((int)reader.BaseStream.Length - 12); @@ -26,7 +43,45 @@ namespace SCHALE.GameServer.Controllers.Api using var payloadMs = new MemoryStream(); gzStream.CopyTo(payloadMs); - return Results.Text(Encoding.UTF8.GetString(payloadMs.ToArray()), "application/json"); + try + { + var requestType = protocolHandlerFactory.GetRequestPacketTypeByProtocol(protocol); + if (requestType is null) + { + logger.LogError("Protocol {Protocol} doesn't have corresponding type registered", protocol); + goto protocolErrorRet; + } + + var payloadStr = Encoding.UTF8.GetString(payloadMs.ToArray()); + var payload = (JsonSerializer.Deserialize(payloadStr, requestType) as RequestPacket)!; + var handler = protocolHandlerFactory.GetProtocolHandler(payload.Protocol); + if (handler is null) + { + logger.LogDebug("{Protocol} {Payload:j}", payload.Protocol, payloadStr); + logger.LogError("Protocol {Protocol} is unimplemented and left unhandled", payload.Protocol); + + goto protocolErrorRet; + } + + var rsp = handler.Invoke(null, [payload]); + + return Results.Json(new + { + packet = JsonSerializer.Serialize(rsp), + protocol = payload.Protocol.ToString() + }); + +protocolErrorRet: + return Results.Json(new + { + packet = JsonSerializer.Serialize(new ErrorPacket() { Reason = "Protocol not implemented (Server Error)", ErrorCode = WebAPIErrorCode.InternalServerError }), + protocol = Protocol.Error.ToString() + }); + } + catch (Exception) + { + throw; + } } } } diff --git a/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Account.cs b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Account.cs new file mode 100644 index 0000000..413804c --- /dev/null +++ b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Account.cs @@ -0,0 +1,31 @@ +using SCHALE.Common.NetworkProtocol; +using SCHALE.Common.NetworkProtocol.Account; + +namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers +{ + public static class Account + { + [ProtocolHandler(Protocol.Account_CheckYostar)] + public static ResponsePacket CheckYostarHandler(AccountCheckYostarRequest req) + { + return new AccountCheckYostarResponse() + { + ResultState = 1, + SessionKey = new() + { + MxToken = req.EnterTicket, + AccountServerId = 1 + } + }; + } + + [ProtocolHandler(Protocol.Account_Auth)] + public static ResponsePacket AuthHandler(AccountAuthRequest req) + { + return new ErrorPacket() + { + ErrorCode = WebAPIErrorCode.AccountAuthNotCreated + }; + } + } +} diff --git a/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/ProtocolHandlerFactory.cs b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/ProtocolHandlerFactory.cs new file mode 100644 index 0000000..91f9523 --- /dev/null +++ b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/ProtocolHandlerFactory.cs @@ -0,0 +1,78 @@ +using SCHALE.Common.NetworkProtocol; +using Serilog; +using System.Reflection; + +namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + internal class ProtocolHandlerAttribute : Attribute + { + public Protocol Protocol { get; } + + public ProtocolHandlerAttribute(Protocol protocol) + { + Protocol = protocol; + } + } + + public interface IProtocolHandlerFactory + { + public MethodInfo? GetProtocolHandler(Protocol protocol); + public Type? GetRequestPacketTypeByProtocol(Protocol protocol); + } + + public class ProtocolHandlerFactory : IProtocolHandlerFactory + { + private readonly Dictionary handlers = []; + private readonly Dictionary requestPacketTypes = []; + + public ProtocolHandlerFactory() + { + foreach (var method in Assembly.GetExecutingAssembly().GetTypes().SelectMany(x => x.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Where(x => x.GetCustomAttribute() is not null))) + { + var attr = method.GetCustomAttribute()!; + if (handlers.ContainsKey(attr.Protocol)) + continue; + + handlers.Add(attr.Protocol, method); + Log.Debug($"Loaded {method.Name} for {attr.Protocol}"); + } + + foreach (var requestPacketType in Assembly.GetAssembly(typeof(RequestPacket))!.GetTypes().Where(x => x.IsAssignableTo(typeof(RequestPacket)))) + { + if (requestPacketType == typeof(RequestPacket)) + continue; + + var obj = Activator.CreateInstance(requestPacketType); + var protocol = requestPacketType.GetProperty("Protocol")!.GetValue(obj); + + if (!requestPacketTypes.ContainsKey((Protocol)protocol!)) + { + requestPacketTypes.Add((Protocol)protocol!, requestPacketType); + } + } + } + + public MethodInfo? GetProtocolHandler(Protocol protocol) + { + handlers.TryGetValue(protocol, out var handler); + + return handler; + } + + public Type? GetRequestPacketTypeByProtocol(Protocol protocol) + { + requestPacketTypes.TryGetValue(protocol, out var type); + + return type; + } + } + + internal static class ServiceExtensions + { + public static void AddProtocolHandlerFactory(this IServiceCollection services) + { + services.AddSingleton(); + } + } +} diff --git a/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Queuing.cs b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Queuing.cs new file mode 100644 index 0000000..a490a52 --- /dev/null +++ b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Queuing.cs @@ -0,0 +1,17 @@ +using SCHALE.Common.NetworkProtocol; +using SCHALE.Common.NetworkProtocol.Queuing; + +namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers +{ + public static class Queuing + { + [ProtocolHandler(Protocol.Queuing_GetTicket)] + public static ResponsePacket GetTicketHandler(QueuingGetTicketRequest req) + { + return new QueuingGetTicketResponse() + { + EnterTicket = req.YostarToken + }; + } + } +} diff --git a/SCHALE.GameServer/Controllers/UserController.cs b/SCHALE.GameServer/Controllers/UserController.cs index 525734c..c0803ed 100644 --- a/SCHALE.GameServer/Controllers/UserController.cs +++ b/SCHALE.GameServer/Controllers/UserController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc; using SCHALE.Common.Database; +using SCHALE.Common.Database.Models; using SCHALE.GameServer.Models; namespace SCHALE.GameServer.Controllers diff --git a/SCHALE.GameServer/GameServer.cs b/SCHALE.GameServer/GameServer.cs index 0999810..a5ebe98 100644 --- a/SCHALE.GameServer/GameServer.cs +++ b/SCHALE.GameServer/GameServer.cs @@ -3,6 +3,7 @@ using Serilog.Events; using Serilog; using System.Reflection; using SCHALE.Common.Database; +using SCHALE.GameServer.Controllers.Api.ProtocolHandlers; namespace SCHALE.GameServer { @@ -44,6 +45,7 @@ namespace SCHALE.GameServer // Add services to the container. builder.Services.AddControllers(); builder.Services.AddMongoDBProvider(config.GetConnectionString("MongoDB") ?? throw new ArgumentNullException("ConnectionStrings/MongoDB in appsettings is missing")); + builder.Services.AddProtocolHandlerFactory(); var app = builder.Build(); diff --git a/SCHALE.GameServer/appsettings.Development.json b/SCHALE.GameServer/appsettings.Development.json index 2c63c08..6b2463b 100644 --- a/SCHALE.GameServer/appsettings.Development.json +++ b/SCHALE.GameServer/appsettings.Development.json @@ -1,2 +1,7 @@ { + "Serilog": { + "MinimumLevel": { + "Default": "Debug" + } + } }