auth handlers

This commit is contained in:
rfi 2024-04-20 12:24:36 +07:00
parent 477b05df2f
commit 9c70eac424
14 changed files with 847 additions and 33 deletions

View File

@ -48,11 +48,11 @@ namespace SCHALE.Common.Database
if (counter is null) if (counter is null)
{ {
counter = new Counter() { Id = Collection, Seq = 0 }; counter = new Counter() { Id = Collection, Seq = 0 };
context.Counters.Add(counter); context.Add(counter);
context.SaveChanges();
} }
counter.Seq++; counter.Seq++;
context.Update(counter);
return counter.Seq; return counter.Seq;
} }

View File

@ -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<AttendanceBookReward> AttendanceBookRewards { get; set; }
public IEnumerable<AttendanceHistoryDB> AttendanceHistoryDBs { get; set; }
public IEnumerable<OpenConditionDB> OpenConditions { get; set; }
public IEnumerable<PurchaseCountDB> RepurchasableMonthlyProductCountDBs { get; set; }
public IEnumerable<ParcelInfo> MonthlyProductParcel { get; set; }
public IEnumerable<ParcelInfo> MonthlyProductMail { get; set; }
public IEnumerable<ParcelInfo> BiweeklyProductParcel { get; set; }
public IEnumerable<ParcelInfo> BiweeklyProductMail { get; set; }
public IEnumerable<ParcelInfo> WeeklyProductParcel { get; set; }
public IEnumerable<ParcelInfo> 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; }
}
}

View File

@ -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;
}
}

View File

@ -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; }
}
}

View File

@ -1,18 +1,17 @@
namespace SCHALE.Common.NetworkProtocol using System.Text.Json.Serialization;
namespace SCHALE.Common.NetworkProtocol
{ {
public class SessionKey public class SessionKey
{ {
public long AccountServerId { get; set; } public long AccountServerId { get; set; }
public required string MxToken { get; set; } public required string MxToken { get; set; }
} }
public abstract class BasePacket public abstract class BasePacket
{ {
public SessionKey? SessionKey { get; set; } public SessionKey? SessionKey { get; set; }
public abstract Protocol Protocol { get; } public abstract Protocol Protocol { get; }
public long AccountId => SessionKey?.AccountServerId ?? 0; public long AccountId => SessionKey?.AccountServerId ?? 0;
} }
@ -21,14 +20,9 @@
private static int _counter; private static int _counter;
public int ClientUpTime { get; set; } public int ClientUpTime { get; set; }
public bool Resendable { get; set; } = true; public bool Resendable { get; set; } = true;
public long Hash { get; set; } public long Hash { get; set; }
public bool IsTest { get; set; } public bool IsTest { get; set; }
public DateTime? ModifiedServerTime__DebugOnly { get; set; } public DateTime? ModifiedServerTime__DebugOnly { get; set; }
public static long CreateHash(Protocol protocol) public static long CreateHash(Protocol protocol)
@ -37,51 +31,33 @@
} }
} }
// TODO: Fix properties
public abstract class ResponsePacket : BasePacket public abstract class ResponsePacket : BasePacket
{ {
public long ServerTimeTicks { get; set; } public long ServerTimeTicks { get; set; } = DateTimeOffset.Now.Ticks;
public ServerNotificationFlag ServerNotification { get; set; } public ServerNotificationFlag ServerNotification { get; set; }
// public List<MissionProgressDB> MissionProgressDBs { get; set; } // public List<MissionProgressDB> MissionProgressDBs { get; set; }
// public Dictionary<long, List<MissionProgressDB>> EventMissionProgressDBDict { get; set; } // public Dictionary<long, List<MissionProgressDB>> EventMissionProgressDBDict { get; set; }
// public Dictionary<OpenConditionContent, OpenConditionLockReason> StaticOpenConditions { get; set; } // public Dictionary<OpenConditionContent, OpenConditionLockReason> StaticOpenConditions { get; set; }
} }
[Flags] [Flags]
public enum ServerNotificationFlag public enum ServerNotificationFlag
{ {
// Token: 0x04009560 RID: 38240
None = 0, None = 0,
// Token: 0x04009561 RID: 38241
NewMailArrived = 4, NewMailArrived = 4,
// Token: 0x04009562 RID: 38242
HasUnreadMail = 8, HasUnreadMail = 8,
// Token: 0x04009563 RID: 38243
NewToastDetected = 16, NewToastDetected = 16,
// Token: 0x04009564 RID: 38244
CanReceiveArenaDailyReward = 32, CanReceiveArenaDailyReward = 32,
// Token: 0x04009565 RID: 38245
CanReceiveRaidReward = 64, CanReceiveRaidReward = 64,
// Token: 0x04009566 RID: 38246
ServerMaintenance = 256, ServerMaintenance = 256,
// Token: 0x04009567 RID: 38247
CannotReceiveMail = 512, CannotReceiveMail = 512,
// Token: 0x04009568 RID: 38248
InventoryFullRewardMail = 1024, InventoryFullRewardMail = 1024,
// Token: 0x04009569 RID: 38249
CanReceiveClanAttendanceReward = 2048, CanReceiveClanAttendanceReward = 2048,
// Token: 0x0400956A RID: 38250
HasClanApplicant = 4096, HasClanApplicant = 4096,
// Token: 0x0400956B RID: 38251
HasFriendRequest = 8192, HasFriendRequest = 8192,
// Token: 0x0400956C RID: 38252
CheckConquest = 16384, CheckConquest = 16384,
// Token: 0x0400956D RID: 38253
CanReceiveEliminateRaidReward = 32768, CanReceiveEliminateRaidReward = 32768,
// Token: 0x0400956E RID: 38254
CanReceiveMultiFloorRaidReward = 65536 CanReceiveMultiFloorRaidReward = 65536
} }
} }

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -1,13 +1,26 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SCHALE.Common.Crypto; using SCHALE.Common.Crypto;
using SCHALE.Common.NetworkProtocol;
using SCHALE.GameServer.Controllers.Api.ProtocolHandlers;
using System.Buffers.Binary;
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
using System.Text.Json;
namespace SCHALE.GameServer.Controllers.Api namespace SCHALE.GameServer.Controllers.Api
{ {
[Route("/api/[controller]")] [Route("/api/[controller]")]
public class GatewayController : ControllerBase public class GatewayController : ControllerBase
{ {
IProtocolHandlerFactory protocolHandlerFactory;
ILogger<GatewayController> logger;
public GatewayController(IProtocolHandlerFactory _protocolHandlerFactory, ILogger<GatewayController> _logger)
{
protocolHandlerFactory = _protocolHandlerFactory;
logger = _logger;
}
[HttpPost] [HttpPost]
public IResult GatewayRequest() public IResult GatewayRequest()
{ {
@ -17,7 +30,11 @@ namespace SCHALE.GameServer.Controllers.Api
using var reader = new BinaryReader(formFile.OpenReadStream()); 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; reader.BaseStream.Position = 12;
byte[] compressedPayload = reader.ReadBytes((int)reader.BaseStream.Length - 12); byte[] compressedPayload = reader.ReadBytes((int)reader.BaseStream.Length - 12);
@ -26,7 +43,45 @@ namespace SCHALE.GameServer.Controllers.Api
using var payloadMs = new MemoryStream(); using var payloadMs = new MemoryStream();
gzStream.CopyTo(payloadMs); 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;
}
} }
} }
} }

View File

@ -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
};
}
}
}

View File

@ -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<Protocol, MethodInfo> handlers = [];
private readonly Dictionary<Protocol, Type> requestPacketTypes = [];
public ProtocolHandlerFactory()
{
foreach (var method in Assembly.GetExecutingAssembly().GetTypes().SelectMany(x => x.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Where(x => x.GetCustomAttribute<ProtocolHandlerAttribute>() is not null)))
{
var attr = method.GetCustomAttribute<ProtocolHandlerAttribute>()!;
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<IProtocolHandlerFactory, ProtocolHandlerFactory>();
}
}
}

View File

@ -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
};
}
}
}

View File

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SCHALE.Common.Database; using SCHALE.Common.Database;
using SCHALE.Common.Database.Models;
using SCHALE.GameServer.Models; using SCHALE.GameServer.Models;
namespace SCHALE.GameServer.Controllers namespace SCHALE.GameServer.Controllers

View File

@ -3,6 +3,7 @@ using Serilog.Events;
using Serilog; using Serilog;
using System.Reflection; using System.Reflection;
using SCHALE.Common.Database; using SCHALE.Common.Database;
using SCHALE.GameServer.Controllers.Api.ProtocolHandlers;
namespace SCHALE.GameServer namespace SCHALE.GameServer
{ {
@ -44,6 +45,7 @@ namespace SCHALE.GameServer
// Add services to the container. // Add services to the container.
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddMongoDBProvider(config.GetConnectionString("MongoDB") ?? throw new ArgumentNullException("ConnectionStrings/MongoDB in appsettings is missing")); builder.Services.AddMongoDBProvider(config.GetConnectionString("MongoDB") ?? throw new ArgumentNullException("ConnectionStrings/MongoDB in appsettings is missing"));
builder.Services.AddProtocolHandlerFactory();
var app = builder.Build(); var app = builder.Build();

View File

@ -1,2 +1,7 @@
{ {
"Serilog": {
"MinimumLevel": {
"Default": "Debug"
}
}
} }