Minimal PS

This commit is contained in:
xeon 2024-01-19 17:45:18 +03:00
parent 228709bb6f
commit c6680fcb43
35 changed files with 9875 additions and 229 deletions

View file

@ -1,4 +1,7 @@
[*.cs] [*.cs]
# CA1822: Mark members as static
dotnet_diagnostic.CA1822.severity = none
# IDE0290: Use primary constructor # IDE0290: Use primary constructor
csharp_style_prefer_primary_constructors = false csharp_style_prefer_primary_constructors = false

View file

@ -0,0 +1,14 @@
namespace RPG.GameCore.Excel.Attributes;
[AttributeUsage(AttributeTargets.Class)]
internal class ExcelTableAttribute : Attribute
{
public string Path { get; }
public ExcelType Type { get; }
public ExcelTableAttribute(string path, ExcelType type)
{
Path = path;
Type = type;
}
}

View file

@ -0,0 +1,49 @@
using RPG.GameCore.Excel.Attributes;
namespace RPG.GameCore.Excel;
[ExcelTable("AvatarExcelTable.json", ExcelType.Avatar)]
public class AvatarExcelRow : ExcelRow
{
public override uint Id => AvatarID;
public uint AvatarID { get; set; }
public uint AdventurePlayerID { get; set; }
public string AvatarVOTag { get; set; } = string.Empty;
public uint Rarity { get; set; }
public string JsonPath { get; set; } = string.Empty;
public uint NatureID { get; set; }
public string DamageType { get; set; } = string.Empty;
public SPValue SPNeed { get; set; } = new();
public uint ExpGroup { get; set; }
public uint MaxPromotion { get; set; }
public uint MaxRank { get; set; }
public List<string> RankUpCostList { get; set; } = [];
public uint MaxRankRepay { get; set; }
public List<uint> SkillList { get; set; } = [];
public string AvatarBaseType { get; set; } = string.Empty;
public string DefaultAvatarImagePath { get; set; } = string.Empty;
public string DefaultAvatarModelPath { get; set; } = string.Empty;
public string DefaultAvatarHeadIconPath { get; set; } = string.Empty;
public string DefaultAvatarHalfImagePath { get; set; } = string.Empty;
public string AvatarSideIconPath { get; set; } = string.Empty;
public string ActionAvatarHeadIconPath { get; set; } = string.Empty;
public string DefaultAvatarQHeadIconPath { get; set; } = string.Empty;
public string AvatarBaseTypeIconPath { get; set; } = string.Empty;
public string AvatarDialogHalfImagePath { get; set; } = string.Empty;
public string UltraSkillCutInPrefabPath { get; set; } = string.Empty;
public string UIAvatarModelPath { get; set; } = string.Empty;
public string ManikinJsonPath { get; set; } = string.Empty;
public string AIPath { get; set; } = string.Empty;
public string SkilltreePrefabPath { get; set; } = string.Empty;
public bool Release { get; set; }
public string SideAvatarHeadIconPath { get; set; } = string.Empty;
public string WaitingAvatarHeadIconPath { get; set; } = string.Empty;
public string AvatarCutinImgPath { get; set; } = string.Empty;
public string AvatarCutinBgImgPath { get; set; } = string.Empty;
public class SPValue
{
public long RawValue { get; set; }
}
}

View file

@ -0,0 +1,5 @@
namespace RPG.GameCore.Excel;
public abstract class ExcelRow
{
public abstract uint Id { get; }
}

View file

@ -0,0 +1,73 @@
using System.Collections.Immutable;
using System.Reflection;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using RPG.GameCore.Excel.Attributes;
namespace RPG.GameCore.Excel;
public class ExcelTables
{
private readonly ILogger _logger;
private ImmutableDictionary<ExcelType, ImmutableArray<ExcelRow>>? _tables;
public ExcelTables(ILogger<ExcelTables> logger)
{
_logger = logger;
}
public TExcelRow? GetExcelRow<TExcelRow>(ExcelType type, int id) where TExcelRow : ExcelRow
{
if (_tables == null) throw new InvalidOperationException("GetExcelRow called when ExcelTables not loaded.");
if (_tables.TryGetValue(type, out ImmutableArray<ExcelRow> rows))
{
return rows.SingleOrDefault(row => row.Id == id) as TExcelRow;
}
throw new ArgumentException($"GetExcelRow: table for excel type not found {type}");
}
public IEnumerable<ExcelRow> GetAllRows(ExcelType type)
{
if (_tables == null) throw new InvalidOperationException("GetAllRows called when ExcelTables not loaded.");
if (_tables.TryGetValue(type, out ImmutableArray<ExcelRow> rows))
{
return rows;
}
throw new ArgumentException($"GetAllRows: table for excel type not found {type}");
}
public void Load()
{
ImmutableDictionary<ExcelType, ImmutableArray<ExcelRow>>.Builder tables = ImmutableDictionary.CreateBuilder<ExcelType, ImmutableArray<ExcelRow>>();
IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => type.GetCustomAttribute<ExcelTableAttribute>() != null);
foreach (Type type in types)
{
ExcelTableAttribute attribute = type.GetCustomAttribute<ExcelTableAttribute>()!;
// TODO: asset provider
JsonDocument tableJson = JsonDocument.Parse(File.ReadAllText("data/excel/" + attribute.Path));
ImmutableArray<ExcelRow>.Builder rows = ImmutableArray.CreateBuilder<ExcelRow>();
foreach (JsonProperty property in tableJson.RootElement.EnumerateObject())
{
if (property.Value.ValueKind != JsonValueKind.Object)
throw new ArgumentException($"Failed to load excel: expected an object, got {property.Value.ValueKind}");
ExcelRow row = (property.Value.Deserialize(type) as ExcelRow)!;
rows.Add(row);
}
tables.Add(attribute.Type, rows.ToImmutable());
}
_tables = tables.ToImmutable();
_logger.LogInformation("Loaded {count} excel tables", _tables.Count);
}
}

View file

@ -0,0 +1,6 @@
namespace RPG.GameCore.Excel;
public enum ExcelType
{
Avatar,
MainMission
}

View file

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using RPG.GameCore.Excel.Attributes;
namespace RPG.GameCore.Excel;
[ExcelTable("MainMissionExcelTable.json", ExcelType.MainMission)]
public class MainMissionRow : ExcelRow
{
public override uint Id => MainMissionID;
public uint MainMissionID { get; set; }
public string Type { get; set; } = string.Empty;
public bool IsLoop { get; set; }
public List<uint> NextMainMissionList { get; set; } = [];
public string TakeType { get; set; } = string.Empty;
public uint TakeParamInt1 { get; set; }
public List<uint> TakeParamIntList { get; set; } = [];
public string BeginType { get; set; } = string.Empty;
public uint BeginParamInt1 { get; set; }
public List<uint> BeginParamIntList { get; set; } = [];
public List<uint> StartSubMissionList { get; set; } = [];
public List<uint> FinishSubMissionList { get; set; } = [];
public uint NextTrackMainMission { get; set; }
public uint TrackWeight { get; set; }
public bool IsShowStartHint { get; set; }
public bool IsShowFinishHint { get; set; }
public int RewardID { get; set; }
public int DisplayRewardID { get; set; }
}

View file

@ -10,4 +10,17 @@
<None Include="..\.editorconfig" Link=".editorconfig" /> <None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="data\excel\AvatarExcelTable.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="data\excel\MainMissionExcelTable.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,218 +1,218 @@
namespace RPG.Network.Proto; namespace RPG.Network.Proto;
public static class CmdType public enum CmdType
{ {
public const ushort CmdTypeNone = 0; CmdTypeNone = 0,
public const ushort CmdPlayerLoginCsReq = 1; CmdPlayerLoginCsReq = 1,
public const ushort CmdPlayerLoginScRsp = 2; CmdPlayerLoginScRsp = 2,
public const ushort CmdPlayerLogoutCsReq = 3; CmdPlayerLogoutCsReq = 3,
public const ushort CmdPlayerLogoutScRsp = 4; CmdPlayerLogoutScRsp = 4,
public const ushort CmdPlayerGetTokenCsReq = 5; CmdPlayerGetTokenCsReq = 5,
public const ushort CmdPlayerGetTokenScRsp = 6; CmdPlayerGetTokenScRsp = 6,
public const ushort CmdPlayerKeepAliveNotify = 7; CmdPlayerKeepAliveNotify = 7,
public const ushort CmdGmTalkScNotify = 8; CmdGmTalkScNotify = 8,
public const ushort CmdPlayerKickOutScNotify = 9; CmdPlayerKickOutScNotify = 9,
public const ushort CmdGmTalkCsReq = 10; CmdGmTalkCsReq = 10,
public const ushort CmdGmTalkScRsp = 11; CmdGmTalkScRsp = 11,
public const ushort CmdGetStaminaExchangeCsReq = 12; CmdGetStaminaExchangeCsReq = 12,
public const ushort CmdGetStaminaExchangeScRsp = 13; CmdGetStaminaExchangeScRsp = 13,
public const ushort CmdExchangeStaminaCsReq = 14; CmdExchangeStaminaCsReq = 14,
public const ushort CmdExchangeStaminaScRsp = 15; CmdExchangeStaminaScRsp = 15,
public const ushort CmdGetAuthkeyCsReq = 16; CmdGetAuthkeyCsReq = 16,
public const ushort CmdGetAuthkeyScRsp = 17; CmdGetAuthkeyScRsp = 17,
public const ushort CmdRegionStopScNotify = 18; CmdRegionStopScNotify = 18,
public const ushort CmdAntiAddictScNotify = 19; CmdAntiAddictScNotify = 19,
public const ushort CmdSetNicknameCsReq = 20; CmdSetNicknameCsReq = 20,
public const ushort CmdSetNicknameScRsp = 21; CmdSetNicknameScRsp = 21,
public const ushort CmdGetLevelRewardTakenListCsReq = 22; CmdGetLevelRewardTakenListCsReq = 22,
public const ushort CmdGetLevelRewardTakenListScRsp = 23; CmdGetLevelRewardTakenListScRsp = 23,
public const ushort CmdGetLevelRewardCsReq = 24; CmdGetLevelRewardCsReq = 24,
public const ushort CmdGetLevelRewardScRsp = 25; CmdGetLevelRewardScRsp = 25,
public const ushort CmdSyncTimeCsReq = 26; CmdSyncTimeCsReq = 26,
public const ushort CmdSyncTimeScRsp = 27; CmdSyncTimeScRsp = 27,
public const ushort CmdSetLanguageCsReq = 28; CmdSetLanguageCsReq = 28,
public const ushort CmdSetLanguageScRsp = 29; CmdSetLanguageScRsp = 29,
public const ushort CmdServerAnnounceNotify = 30; CmdServerAnnounceNotify = 30,
public const ushort CmdPVEBattleResultCsReq = 101; CmdPVEBattleResultCsReq = 101,
public const ushort CmdPVEBattleResultScRsp = 102; CmdPVEBattleResultScRsp = 102,
public const ushort CmdQuitBattleCsReq = 103; CmdQuitBattleCsReq = 103,
public const ushort CmdQuitBattleScRsp = 104; CmdQuitBattleScRsp = 104,
public const ushort CmdGetCurBattleInfoCsReq = 105; CmdGetCurBattleInfoCsReq = 105,
public const ushort CmdGetCurBattleInfoScRsp = 106; CmdGetCurBattleInfoScRsp = 106,
public const ushort CmdSyncClientResVersionCsReq = 107; CmdSyncClientResVersionCsReq = 107,
public const ushort CmdSyncClientResVersionScRsp = 108; CmdSyncClientResVersionScRsp = 108,
public const ushort CmdGetStageDataCsReq = 201; CmdGetStageDataCsReq = 201,
public const ushort CmdGetStageDataScRsp = 202; CmdGetStageDataScRsp = 202,
public const ushort CmdStageBeginCsReq = 203; CmdStageBeginCsReq = 203,
public const ushort CmdStageBeginScRsp = 204; CmdStageBeginScRsp = 204,
public const ushort CmdGetAvatarDataCsReq = 301; CmdGetAvatarDataCsReq = 301,
public const ushort CmdGetAvatarDataScRsp = 302; CmdGetAvatarDataScRsp = 302,
public const ushort CmdAvatarExpUpCsReq = 303; CmdAvatarExpUpCsReq = 303,
public const ushort CmdAvatarExpUpScRsp = 304; CmdAvatarExpUpScRsp = 304,
public const ushort CmdUnlockSkilltreeCsReq = 305; CmdUnlockSkilltreeCsReq = 305,
public const ushort CmdUnlockSkilltreeScRsp = 306; CmdUnlockSkilltreeScRsp = 306,
public const ushort CmdPromoteAvatarCsReq = 307; CmdPromoteAvatarCsReq = 307,
public const ushort CmdPromoteAvatarScRsp = 308; CmdPromoteAvatarScRsp = 308,
public const ushort CmdDressAvatarCsReq = 309; CmdDressAvatarCsReq = 309,
public const ushort CmdDressAvatarScRsp = 310; CmdDressAvatarScRsp = 310,
public const ushort CmdTakeOffEquipmentCsReq = 311; CmdTakeOffEquipmentCsReq = 311,
public const ushort CmdTakeOffEquipmentScRsp = 312; CmdTakeOffEquipmentScRsp = 312,
public const ushort CmdAddAvatarScNotify = 313; CmdAddAvatarScNotify = 313,
public const ushort CmdGetWaypointCsReq = 401; CmdGetWaypointCsReq = 401,
public const ushort CmdGetWaypointScRsp = 402; CmdGetWaypointScRsp = 402,
public const ushort CmdSetCurWaypointCsReq = 403; CmdSetCurWaypointCsReq = 403,
public const ushort CmdSetCurWaypointScRsp = 404; CmdSetCurWaypointScRsp = 404,
public const ushort CmdGetChapterCsReq = 405; CmdGetChapterCsReq = 405,
public const ushort CmdGetChapterScRsp = 406; CmdGetChapterScRsp = 406,
public const ushort CmdWaypointShowNewCsNotify = 407; CmdWaypointShowNewCsNotify = 407,
public const ushort CmdTakeChapterRewardCsReq = 408; CmdTakeChapterRewardCsReq = 408,
public const ushort CmdTakeChapterRewardScRsp = 409; CmdTakeChapterRewardScRsp = 409,
public const ushort CmdGetBagCsReq = 501; CmdGetBagCsReq = 501,
public const ushort CmdGetBagScRsp = 502; CmdGetBagScRsp = 502,
public const ushort CmdPromoteEquipmentCsReq = 503; CmdPromoteEquipmentCsReq = 503,
public const ushort CmdPromoteEquipmentScRsp = 504; CmdPromoteEquipmentScRsp = 504,
public const ushort CmdLockEquipmentCsReq = 505; CmdLockEquipmentCsReq = 505,
public const ushort CmdLockEquipmentScRsp = 506; CmdLockEquipmentScRsp = 506,
public const ushort CmdUseItemCsReq = 507; CmdUseItemCsReq = 507,
public const ushort CmdUseItemScRsp = 508; CmdUseItemScRsp = 508,
public const ushort CmdRankUpEquipmentCsReq = 509; CmdRankUpEquipmentCsReq = 509,
public const ushort CmdRankUpEquipmentScRsp = 510; CmdRankUpEquipmentScRsp = 510,
public const ushort CmdExpUpEquipmentCsReq = 511; CmdExpUpEquipmentCsReq = 511,
public const ushort CmdExpUpEquipmentScRsp = 512; CmdExpUpEquipmentScRsp = 512,
public const ushort CmdUseItemFoodCsReq = 513; CmdUseItemFoodCsReq = 513,
public const ushort CmdUseItemFoodScRsp = 514; CmdUseItemFoodScRsp = 514,
public const ushort CmdComposeItemCsReq = 515; CmdComposeItemCsReq = 515,
public const ushort CmdComposeItemScRsp = 516; CmdComposeItemScRsp = 516,
public const ushort CmdPlayerSyncScNotify = 601; CmdPlayerSyncScNotify = 601,
public const ushort CmdGetStageLineupCsReq = 701; CmdGetStageLineupCsReq = 701,
public const ushort CmdGetStageLineupScRsp = 702; CmdGetStageLineupScRsp = 702,
public const ushort CmdGetCurLineupDataCsReq = 703; CmdGetCurLineupDataCsReq = 703,
public const ushort CmdGetCurLineupDataScRsp = 704; CmdGetCurLineupDataScRsp = 704,
public const ushort CmdJoinLineupCsReq = 705; CmdJoinLineupCsReq = 705,
public const ushort CmdJoinLineupScRsp = 706; CmdJoinLineupScRsp = 706,
public const ushort CmdQuitLineupCsReq = 707; CmdQuitLineupCsReq = 707,
public const ushort CmdQuitLineupScRsp = 708; CmdQuitLineupScRsp = 708,
public const ushort CmdSwapLineupCsReq = 709; CmdSwapLineupCsReq = 709,
public const ushort CmdSwapLineupScRsp = 710; CmdSwapLineupScRsp = 710,
public const ushort CmdSyncLineupNotify = 711; CmdSyncLineupNotify = 711,
public const ushort CmdGetLineupAvatarDataCsReq = 712; CmdGetLineupAvatarDataCsReq = 712,
public const ushort CmdGetLineupAvatarDataScRsp = 713; CmdGetLineupAvatarDataScRsp = 713,
public const ushort CmdChangeLineupLeaderCsReq = 714; CmdChangeLineupLeaderCsReq = 714,
public const ushort CmdChangeLineupLeaderScRsp = 715; CmdChangeLineupLeaderScRsp = 715,
public const ushort CmdSwitchLineupIndexCsReq = 716; CmdSwitchLineupIndexCsReq = 716,
public const ushort CmdSwitchLineupIndexScRsp = 717; CmdSwitchLineupIndexScRsp = 717,
public const ushort CmdSetLineupNameCsReq = 718; CmdSetLineupNameCsReq = 718,
public const ushort CmdSetLineupNameScRsp = 719; CmdSetLineupNameScRsp = 719,
public const ushort CmdGetAllLineupDataCsReq = 720; CmdGetAllLineupDataCsReq = 720,
public const ushort CmdGetAllLineupDataScRsp = 721; CmdGetAllLineupDataScRsp = 721,
public const ushort CmdVirtualLineupDestroyNotify = 722; CmdVirtualLineupDestroyNotify = 722,
public const ushort CmdGetMailCsReq = 801; CmdGetMailCsReq = 801,
public const ushort CmdGetMailScRsp = 802; CmdGetMailScRsp = 802,
public const ushort CmdMarkReadMailCsReq = 803; CmdMarkReadMailCsReq = 803,
public const ushort CmdMarkReadMailScRsp = 804; CmdMarkReadMailScRsp = 804,
public const ushort CmdDelMailCsReq = 805; CmdDelMailCsReq = 805,
public const ushort CmdDelMailScRsp = 806; CmdDelMailScRsp = 806,
public const ushort CmdTakeMailAttachmentCsReq = 807; CmdTakeMailAttachmentCsReq = 807,
public const ushort CmdTakeMailAttachmentScRsp = 808; CmdTakeMailAttachmentScRsp = 808,
public const ushort CmdNewMailScNotify = 809; CmdNewMailScNotify = 809,
public const ushort CmdGetQuestDataCsReq = 901; CmdGetQuestDataCsReq = 901,
public const ushort CmdGetQuestDataScRsp = 902; CmdGetQuestDataScRsp = 902,
public const ushort CmdTakeQuestRewardCsReq = 903; CmdTakeQuestRewardCsReq = 903,
public const ushort CmdTakeQuestRewardScRsp = 904; CmdTakeQuestRewardScRsp = 904,
public const ushort CmdGetMazeCsReq = 1001; CmdGetMazeCsReq = 1001,
public const ushort CmdGetMazeScRsp = 1002; CmdGetMazeScRsp = 1002,
public const ushort CmdChooseMazeSeriesCsReq = 1003; CmdChooseMazeSeriesCsReq = 1003,
public const ushort CmdChooseMazeSeriesScRsp = 1004; CmdChooseMazeSeriesScRsp = 1004,
public const ushort CmdChooseMazeAbilityCsReq = 1005; CmdChooseMazeAbilityCsReq = 1005,
public const ushort CmdChooseMazeAbilityScRsp = 1006; CmdChooseMazeAbilityScRsp = 1006,
public const ushort CmdEnterMazeCsReq = 1007; CmdEnterMazeCsReq = 1007,
public const ushort CmdEnterMazeScRsp = 1008; CmdEnterMazeScRsp = 1008,
public const ushort CmdMazeBuffScNotify = 1011; CmdMazeBuffScNotify = 1011,
public const ushort CmdCastMazeSkillCsReq = 1012; CmdCastMazeSkillCsReq = 1012,
public const ushort CmdCastMazeSkillScRsp = 1013; CmdCastMazeSkillScRsp = 1013,
public const ushort CmdMazePlaneEventScNotify = 1014; CmdMazePlaneEventScNotify = 1014,
public const ushort CmdEnterMazeByServerScNotify = 1015; CmdEnterMazeByServerScNotify = 1015,
public const ushort CmdGetMazeMapInfoCsReq = 1016; CmdGetMazeMapInfoCsReq = 1016,
public const ushort CmdGetMazeMapInfoScRsp = 1017; CmdGetMazeMapInfoScRsp = 1017,
public const ushort CmdFinishPlotCsReq = 1101; CmdFinishPlotCsReq = 1101,
public const ushort CmdFinishPlotScRsp = 1102; CmdFinishPlotScRsp = 1102,
public const ushort CmdGetMissionDataCsReq = 1201; CmdGetMissionDataCsReq = 1201,
public const ushort CmdGetMissionDataScRsp = 1202; CmdGetMissionDataScRsp = 1202,
public const ushort CmdFinishTalkMissionCsReq = 1203; CmdFinishTalkMissionCsReq = 1203,
public const ushort CmdFinishTalkMissionScRsp = 1204; CmdFinishTalkMissionScRsp = 1204,
public const ushort CmdMissionRewardScNotify = 1205; CmdMissionRewardScNotify = 1205,
public const ushort CmdSyncTaskCsReq = 1206; CmdSyncTaskCsReq = 1206,
public const ushort CmdSyncTaskScRsp = 1207; CmdSyncTaskScRsp = 1207,
public const ushort CmdDailyTaskDataScNotify = 1208; CmdDailyTaskDataScNotify = 1208,
public const ushort CmdTakeDailyTaskExtraRewardCsReq = 1209; CmdTakeDailyTaskExtraRewardCsReq = 1209,
public const ushort CmdTakeDailyTaskExtraRewardScRsp = 1210; CmdTakeDailyTaskExtraRewardScRsp = 1210,
public const ushort CmdDailyTaskRewardScNotify = 1211; CmdDailyTaskRewardScNotify = 1211,
public const ushort CmdMissionGroupWarnScNotify = 1212; CmdMissionGroupWarnScNotify = 1212,
public const ushort CmdFinishCosumeItemMissionCsReq = 1213; CmdFinishCosumeItemMissionCsReq = 1213,
public const ushort CmdFinishCosumeItemMissionScRsp = 1214; CmdFinishCosumeItemMissionScRsp = 1214,
public const ushort CmdEnterAdventureCsReq = 1301; CmdEnterAdventureCsReq = 1301,
public const ushort CmdEnterAdventureScRsp = 1302; CmdEnterAdventureScRsp = 1302,
public const ushort CmdSceneEntityMoveCsReq = 1401; CmdSceneEntityMoveCsReq = 1401,
public const ushort CmdSceneEntityMoveScRsp = 1402; CmdSceneEntityMoveScRsp = 1402,
public const ushort CmdInteractPropCsReq = 1403; CmdInteractPropCsReq = 1403,
public const ushort CmdInteractPropScRsp = 1404; CmdInteractPropScRsp = 1404,
public const ushort CmdSceneCastSkillCsReq = 1405; CmdSceneCastSkillCsReq = 1405,
public const ushort CmdSceneCastSkillScRsp = 1406; CmdSceneCastSkillScRsp = 1406,
public const ushort CmdGetCurSceneInfoCsReq = 1407; CmdGetCurSceneInfoCsReq = 1407,
public const ushort CmdGetCurSceneInfoScRsp = 1408; CmdGetCurSceneInfoScRsp = 1408,
public const ushort CmdSceneEntityUpdateScNotify = 1409; CmdSceneEntityUpdateScNotify = 1409,
public const ushort CmdSceneEntityDisappearScNotify = 1410; CmdSceneEntityDisappearScNotify = 1410,
public const ushort CmdSceneEntityMoveScNotify = 1411; CmdSceneEntityMoveScNotify = 1411,
public const ushort CmdWaitCustomStringCsReq = 1412; CmdWaitCustomStringCsReq = 1412,
public const ushort CmdWaitCustomStringScRsp = 1413; CmdWaitCustomStringScRsp = 1413,
public const ushort CmdSpringTransferCsReq = 1414; CmdSpringTransferCsReq = 1414,
public const ushort CmdSpringTransferScRsp = 1415; CmdSpringTransferScRsp = 1415,
public const ushort CmdUpdateBuffScNotify = 1416; CmdUpdateBuffScNotify = 1416,
public const ushort CmdDelBuffScNotify = 1417; CmdDelBuffScNotify = 1417,
public const ushort CmdSpringRefreshCsReq = 1418; CmdSpringRefreshCsReq = 1418,
public const ushort CmdSpringRefreshScRsp = 1419; CmdSpringRefreshScRsp = 1419,
public const ushort CmdLastSpringRefreshTimeNotify = 1420; CmdLastSpringRefreshTimeNotify = 1420,
public const ushort CmdReturnLastTownCsReq = 1421; CmdReturnLastTownCsReq = 1421,
public const ushort CmdReturnLastTownScRsp = 1422; CmdReturnLastTownScRsp = 1422,
public const ushort CmdSceneEnterStageCsReq = 1423; CmdSceneEnterStageCsReq = 1423,
public const ushort CmdSceneEnterStageScRsp = 1424; CmdSceneEnterStageScRsp = 1424,
public const ushort CmdEnterSectionCsReq = 1427; CmdEnterSectionCsReq = 1427,
public const ushort CmdEnterSectionScRsp = 1428; CmdEnterSectionScRsp = 1428,
public const ushort CmdSetCurInteractEntityCsReq = 1431; CmdSetCurInteractEntityCsReq = 1431,
public const ushort CmdSetCurInteractEntityScRsp = 1432; CmdSetCurInteractEntityScRsp = 1432,
public const ushort CmdRecoverAllLineupCsReq = 1433; CmdRecoverAllLineupCsReq = 1433,
public const ushort CmdRecoverAllLineupScRsp = 1434; CmdRecoverAllLineupScRsp = 1434,
public const ushort CmdSavePointsInfoNotify = 1435; CmdSavePointsInfoNotify = 1435,
public const ushort CmdStartCocoonStageCsReq = 1436; CmdStartCocoonStageCsReq = 1436,
public const ushort CmdStartCocoonStageScRsp = 1437; CmdStartCocoonStageScRsp = 1437,
public const ushort CmdEntityBindPropCsReq = 1438; CmdEntityBindPropCsReq = 1438,
public const ushort CmdEntityBindPropScRsp = 1439; CmdEntityBindPropScRsp = 1439,
public const ushort CmdSetClientPausedCsReq = 1440; CmdSetClientPausedCsReq = 1440,
public const ushort CmdSetClientPausedScRsp = 1441; CmdSetClientPausedScRsp = 1441,
public const ushort CmdPropBeHitCsReq = 1442; CmdPropBeHitCsReq = 1442,
public const ushort CmdPropBeHitScRsp = 1443; CmdPropBeHitScRsp = 1443,
public const ushort CmdGetShopListCsReq = 1501; CmdGetShopListCsReq = 1501,
public const ushort CmdGetShopListScRsp = 1502; CmdGetShopListScRsp = 1502,
public const ushort CmdBuyGoodsCsReq = 1503; CmdBuyGoodsCsReq = 1503,
public const ushort CmdBuyGoodsScRsp = 1504; CmdBuyGoodsScRsp = 1504,
public const ushort CmdGetTutorialCsReq = 1601; CmdGetTutorialCsReq = 1601,
public const ushort CmdGetTutorialScRsp = 1602; CmdGetTutorialScRsp = 1602,
public const ushort CmdGetTutorialGuideCsReq = 1603; CmdGetTutorialGuideCsReq = 1603,
public const ushort CmdGetTutorialGuideScRsp = 1604; CmdGetTutorialGuideScRsp = 1604,
public const ushort CmdUnlockTutorialCsReq = 1605; CmdUnlockTutorialCsReq = 1605,
public const ushort CmdUnlockTutorialScRsp = 1606; CmdUnlockTutorialScRsp = 1606,
public const ushort CmdUnlockTutorialGuideCsReq = 1607; CmdUnlockTutorialGuideCsReq = 1607,
public const ushort CmdUnlockTutorialGuideScRsp = 1608; CmdUnlockTutorialGuideScRsp = 1608,
public const ushort CmdFinishTutorialCsReq = 1609; CmdFinishTutorialCsReq = 1609,
public const ushort CmdFinishTutorialScRsp = 1610; CmdFinishTutorialScRsp = 1610,
public const ushort CmdFinishTutorialGuideCsReq = 1611; CmdFinishTutorialGuideCsReq = 1611,
public const ushort CmdFinishTutorialGuideScRsp = 1612; CmdFinishTutorialGuideScRsp = 1612,
public const ushort CmdGetChallengeCsReq = 1701; CmdGetChallengeCsReq = 1701,
public const ushort CmdGetChallengeScRsp = 1702; CmdGetChallengeScRsp = 1702,
public const ushort CmdStartChallengeCsReq = 1703; CmdStartChallengeCsReq = 1703,
public const ushort CmdStartChallengeScRsp = 1704; CmdStartChallengeScRsp = 1704,
public const ushort CmdLeaveChallengeCsReq = 1705; CmdLeaveChallengeCsReq = 1705,
public const ushort CmdLeaveChallengeScRsp = 1706; CmdLeaveChallengeScRsp = 1706,
public const ushort CmdChallengeSettleNotify = 1707; CmdChallengeSettleNotify = 1707,
public const ushort CmdFinishChallengeCsReq = 1708; CmdFinishChallengeCsReq = 1708,
public const ushort CmdFinishChallengeScRsp = 1709; CmdFinishChallengeScRsp = 1709
} }

View file

@ -4,7 +4,7 @@ using RPG.Services.Core.Network;
using RPG.Services.Core.Network.Command; using RPG.Services.Core.Network.Command;
namespace RPG.Services.Core.Session; namespace RPG.Services.Core.Session;
public abstract class RPGSession public abstract class RPGSession : IDisposable
{ {
private readonly ServiceBox _serviceBox; private readonly ServiceBox _serviceBox;
@ -44,4 +44,6 @@ public abstract class RPGSession
Reason = reason Reason = reason
}); });
} }
public abstract void Dispose();
} }

View file

@ -38,6 +38,9 @@ public class SessionManager
public void Remove(RPGSession session) public void Remove(RPGSession session)
{ {
_ = _sessions.TryRemove(session.SessionId, out _); if (_sessions.TryRemove(session.SessionId, out _))
{
session.Dispose();
}
} }
} }

View file

@ -0,0 +1,20 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using RPG.Services.Gameserver.Modules;
namespace RPG.Services.Gameserver.Extensions;
internal static class ServiceCollectionExtensions
{
public static IServiceCollection AddModules(this IServiceCollection services)
{
IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => type.IsAssignableTo(typeof(BaseModule)) && !type.IsAbstract);
foreach (Type type in types)
{
services.AddScoped(type);
}
return services;
}
}

View file

@ -0,0 +1,26 @@
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class AdventureModule : BaseModule
{
[OnCommand(CmdType.CmdGetCurSceneInfoCsReq)]
public Task OnCmdGetCurSceneInfoCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
Send(session, CmdType.CmdGetCurSceneInfoScRsp, new GetCurSceneInfoScRsp
{
Retcode = 0,
Scene = new SceneInfo
{
PlaneId = 20121,
FloorId = 20121001,
EntryId = 2012101,
EntityList = { },
LeaderEntityId = 0
}
});
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,14 @@
using RPG.Network.Proto;
namespace RPG.Services.Gameserver.Modules.Attributes;
[AttributeUsage(AttributeTargets.Method)]
internal class OnCommandAttribute : Attribute
{
public CmdType CmdType { get; }
public OnCommandAttribute(CmdType cmdType)
{
CmdType = cmdType;
}
}

View file

@ -0,0 +1,43 @@
using RPG.GameCore.Excel;
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class AvatarModule : BaseModule
{
private readonly ExcelTables _excelTables;
public AvatarModule(ExcelTables excelTables)
{
_excelTables = excelTables;
}
[OnCommand(CmdType.CmdGetAvatarDataCsReq)]
public Task OnCmdGetAvatarDataCsReq(PlayerSession session, ReadOnlyMemory<byte> body)
{
GetAvatarDataCsReq req = GetAvatarDataCsReq.Parser.ParseFrom(body.Span);
GetAvatarDataScRsp rsp = new()
{
IsAll = req.IsGetAll
};
foreach (ExcelRow row in _excelTables.GetAllRows(ExcelType.Avatar))
{
if (row is AvatarExcelRow avatarRow)
{
if (avatarRow.AvatarID >= 9000) continue;
rsp.AvatarList.Add(new Avatar
{
AvatarId = avatarRow.AvatarID,
Level = 1
});
}
}
Send(session, CmdType.CmdGetAvatarDataScRsp, rsp);
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,17 @@
using Google.Protobuf;
using RPG.Network.Proto;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal abstract class BaseModule
{
protected static void Send<TBody>(PlayerSession session, CmdType cmdType, TBody body) where TBody : IMessage<TBody>
{
session.SendToService(RPGServiceType.Gateserver, ServiceCommandType.ForwardGameMessage, new CmdForwardGameMessage
{
SessionId = session.SessionId,
CmdType = (ushort)cmdType,
Payload = body.ToByteString()
});
}
}

View file

@ -0,0 +1,19 @@
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class ChallengeModule : BaseModule
{
[OnCommand(CmdType.CmdGetChallengeCsReq)]
public Task OnCmdGetChallengeCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
Send(session, CmdType.CmdGetChallengeScRsp, new GetChallengeScRsp
{
Retcode = 0,
ChallengeList = { }
});
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,20 @@
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class InventoryModule : BaseModule
{
[OnCommand(CmdType.CmdGetBagCsReq)]
public Task OnCmdGetBagCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
Send(session, CmdType.CmdGetBagScRsp, new GetBagScRsp
{
Retcode = 0,
EquipmentList = { },
MaterialList = { }
});
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,27 @@
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class LoginModule : BaseModule
{
[OnCommand(CmdType.CmdPlayerLoginCsReq)]
public Task OnCmdPlayerLoginCsReq(PlayerSession session, ReadOnlyMemory<byte> body)
{
PlayerLoginCsReq req = PlayerLoginCsReq.Parser.ParseFrom(body.Span);
Send(session, CmdType.CmdPlayerLoginScRsp, new PlayerLoginScRsp
{
Retcode = 0,
LoginRandom = req.LoginRandom,
Stamina = 160,
ServerTimestampMs = (ulong)DateTimeOffset.Now.ToUnixTimeMilliseconds(),
BasicInfo = new()
{
Level = 5,
Nickname = "ReversedRooms"
}
});
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,18 @@
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class MailModule : BaseModule
{
[OnCommand(CmdType.CmdGetMailCsReq)]
public Task OnCmdGetMailCsReq(PlayerSession session, ReadOnlyMemory<byte> body)
{
Send(session, CmdType.CmdGetMailScRsp, new GetMailScRsp
{
Retcode = 0
});
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,29 @@
using RPG.GameCore.Excel;
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class MissionModule : BaseModule
{
private readonly ExcelTables _excelTables;
public MissionModule(ExcelTables excelTables)
{
_excelTables = excelTables;
}
[OnCommand(CmdType.CmdGetMissionDataCsReq)]
public Task OnCmdGetMissionDataCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
GetMissionDataScRsp rsp = new();
foreach (ExcelRow row in _excelTables.GetAllRows(ExcelType.MainMission))
{
rsp.FinishedMainMissionIdList.Add(row.Id);
}
Send(session, CmdType.CmdGetMissionDataScRsp, rsp);
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,75 @@
using System.Collections.Immutable;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class ModuleManager
{
private delegate Task ReqHandler(PlayerSession session, IServiceProvider serviceProvider, ReadOnlyMemory<byte> body);
private static readonly ImmutableDictionary<CmdType, ReqHandler> s_handlers;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
static ModuleManager()
{
s_handlers = MapHandlers();
}
public ModuleManager(IServiceProvider serviceProvider, ILogger<ModuleManager> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
public async Task HandleAsync(PlayerSession session, CmdType cmdType, ReadOnlyMemory<byte> body)
{
if (s_handlers.TryGetValue(cmdType, out var handler))
{
await handler(session, _serviceProvider, body);
_logger.LogInformation("Successfully handled command of type {cmdType}", cmdType);
}
else
{
_logger.LogWarning("Handler for command of type {cmdType} not defined!", cmdType);
}
}
private static ImmutableDictionary<CmdType, ReqHandler> MapHandlers()
{
var builder = ImmutableDictionary.CreateBuilder<CmdType, ReqHandler>();
IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => type.IsAssignableTo(typeof(BaseModule)) && !type.IsAbstract);
MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod("GetRequiredService", [typeof(IServiceProvider)])!;
foreach (Type type in types)
{
IEnumerable<MethodInfo> methods = type.GetMethods()
.Where(method => method.GetCustomAttribute<OnCommandAttribute>() != null);
foreach (MethodInfo method in methods)
{
OnCommandAttribute attribute = method.GetCustomAttribute<OnCommandAttribute>()!;
ParameterExpression sessionParam = Expression.Parameter(typeof(PlayerSession));
ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider));
ParameterExpression bodyParam = Expression.Parameter(typeof(ReadOnlyMemory<byte>));
MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam);
MethodCallExpression handlerCall = Expression.Call(getServiceCall, method, sessionParam, bodyParam);
Expression<ReqHandler> lambda = Expression.Lambda<ReqHandler>(handlerCall, sessionParam, serviceProviderParam, bodyParam);
builder.Add(attribute.CmdType, lambda.Compile());
}
}
return builder.ToImmutable();
}
}

View file

@ -0,0 +1,35 @@
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class PlayerModule : BaseModule
{
[OnCommand(CmdType.CmdSyncTimeCsReq)]
public Task OnCmdSyncTimeCsReq(PlayerSession session, ReadOnlyMemory<byte> body)
{
SyncTimeCsReq req = SyncTimeCsReq.Parser.ParseFrom(body.Span);
// TODO: TimeManager
Send(session, CmdType.CmdSyncTimeScRsp, new SyncTimeScRsp
{
ServerTimeMs = (ulong)DateTimeOffset.Now.ToUnixTimeMilliseconds(),
ClientTimeMs = req.ClientTimeMs,
Retcode = 0
});
return Task.CompletedTask;
}
[OnCommand(CmdType.CmdGetStaminaExchangeCsReq)]
public Task OnCmdGetStaminaExchangeCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
Send(session, CmdType.CmdGetStaminaExchangeScRsp, new GetStaminaExchangeScRsp
{
Retcode = 0
});
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,19 @@
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class QuestModule : BaseModule
{
[OnCommand(CmdType.CmdGetQuestDataCsReq)]
public Task OnCmdGetQuestDataCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
Send(session, CmdType.CmdGetQuestDataScRsp, new GetQuestDataScRsp
{
Retcode = 0,
QuestList = { }
});
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,18 @@
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class ShopModule : BaseModule
{
[OnCommand(CmdType.CmdGetShopListCsReq)]
public Task OnCmdGetShopListCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
Send(session, CmdType.CmdGetShopListScRsp, new GetShopListScRsp
{
Retcode = 0
});
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,85 @@
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class TeamModule : BaseModule
{
private static readonly uint[] StartingLineup = [1007, 1102, 1101, 1003];
[OnCommand(CmdType.CmdChangeLineupLeaderCsReq)]
public Task OnCmdChangeLineupLeaderCsReq(PlayerSession session, ReadOnlyMemory<byte> body)
{
ChangeLineupLeaderCsReq req = ChangeLineupLeaderCsReq.Parser.ParseFrom(body.Span);
Send(session, CmdType.CmdChangeLineupLeaderScRsp, new ChangeLineupLeaderScRsp
{
Retcode = 0,
Slot = req.Slot
});
return Task.CompletedTask;
}
[OnCommand(CmdType.CmdGetAllLineupDataCsReq)]
public Task OnCmdGetAllLineupDataCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
GetAllLineupDataScRsp rsp = new();
rsp.LineupList.Add(new LineupInfo
{
Name = "Test Squad",
LeaderSlot = 0,
ExtraLineupType = ExtraLineupType.LineupNone,
Mp = 3
});
for (uint i = 0; i < StartingLineup.Length; i++)
{
rsp.LineupList[0].AvatarList.Add(new LineupAvatar
{
Id = StartingLineup[i],
Satiety = 100,
Sp = 10000,
Hp = 10000,
Slot = i,
AvatarType = AvatarType.AvatarFormalType
});
}
Send(session, CmdType.CmdGetAllLineupDataScRsp, rsp);
return Task.CompletedTask;
}
[OnCommand(CmdType.CmdGetCurLineupDataCsReq)]
public Task OnCmdGetCurLineupDataCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
GetCurLineupDataScRsp rsp = new()
{
Lineup = new LineupInfo
{
Name = "Test Squad",
LeaderSlot = 0,
ExtraLineupType = ExtraLineupType.LineupNone,
Mp = 3
}
};
for (uint i = 0; i < StartingLineup.Length; i++)
{
rsp.Lineup.AvatarList.Add(new LineupAvatar
{
Id = StartingLineup[i],
Satiety = 100,
Sp = 10000,
Hp = 10000,
Slot = i,
AvatarType = AvatarType.AvatarFormalType
});
}
Send(session, CmdType.CmdGetCurLineupDataScRsp, rsp);
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,31 @@
using RPG.Network.Proto;
using RPG.Services.Gameserver.Modules.Attributes;
using RPG.Services.Gameserver.Session;
namespace RPG.Services.Gameserver.Modules;
internal class TutorialModule : BaseModule
{
[OnCommand(CmdType.CmdGetTutorialCsReq)]
public Task OnCmdGetTutorialCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
Send(session, CmdType.CmdGetTutorialScRsp, new GetTutorialScRsp
{
Retcode = 0,
TutorialList = { }
});
return Task.CompletedTask;
}
[OnCommand(CmdType.CmdGetTutorialGuideCsReq)]
public Task OnCmdGetTutorialGuideCsReq(PlayerSession session, ReadOnlyMemory<byte> _)
{
Send(session, CmdType.CmdGetTutorialGuideScRsp, new GetTutorialGuideScRsp
{
Retcode = 0,
TutorialGuideList = { }
});
return Task.CompletedTask;
}
}

View file

@ -40,4 +40,28 @@ internal class GameserverCommandHandler : ServiceCommandHandler
return Task.CompletedTask; return Task.CompletedTask;
} }
[ServiceCommand(ServiceCommandType.UnbindContainer)]
public Task OnCmdUnbindContainer(ServiceCommand command)
{
CmdUnbindContainer cmdUnbindContainer = CmdUnbindContainer.Parser.ParseFrom(command.Body.Span);
if (_sessionManager.TryGet(cmdUnbindContainer.SessionId, out PlayerSession? session))
{
_sessionManager.Remove(session);
}
return Task.CompletedTask;
}
[ServiceCommand(ServiceCommandType.ForwardGameMessage)]
public async Task OnCmdForwardGameMessage(ServiceCommand command)
{
CmdForwardGameMessage cmd = CmdForwardGameMessage.Parser.ParseFrom(command.Body.Span);
if (_sessionManager.TryGet(cmd.SessionId, out PlayerSession? session))
{
await session.HandleGameCommand((ushort)cmd.CmdType, cmd.Payload.Memory);
}
}
} }

View file

@ -1,5 +1,9 @@
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using RPG.GameCore.Excel;
using RPG.Services.Core.Extensions; using RPG.Services.Core.Extensions;
using RPG.Services.Gameserver.Extensions;
using RPG.Services.Gameserver.Modules;
using RPG.Services.Gameserver.Network.Command; using RPG.Services.Gameserver.Network.Command;
namespace RPG.Services.Gameserver; namespace RPG.Services.Gameserver;
@ -13,6 +17,9 @@ internal static class Program
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.SetupRPGService<RPGGameserver, GameserverCommandHandler>(); builder.SetupRPGService<RPGGameserver, GameserverCommandHandler>();
builder.Services.AddModules()
.AddScoped<ModuleManager>()
.AddSingleton<ExcelTables>();
await builder.Build().RunAsync(); await builder.Build().RunAsync();
} }

View file

@ -1,10 +1,19 @@
using RPG.Services.Core; using RPG.GameCore.Excel;
using RPG.Services.Core;
namespace RPG.Services.Gameserver; namespace RPG.Services.Gameserver;
internal class RPGGameserver : RPGServiceBase internal class RPGGameserver : RPGServiceBase
{ {
public RPGGameserver(ServiceManager serviceManager) : base(serviceManager) private readonly ExcelTables _excelTables;
public RPGGameserver(ServiceManager serviceManager, ExcelTables excelTables) : base(serviceManager)
{ {
// RPGGameserver. _excelTables = excelTables;
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
_excelTables.Load();
await base.StartAsync(cancellationToken);
} }
} }

View file

@ -1,10 +1,28 @@
using RPG.Services.Core.Network; using Microsoft.Extensions.DependencyInjection;
using RPG.Network.Proto;
using RPG.Services.Core.Network;
using RPG.Services.Core.Session; using RPG.Services.Core.Session;
using RPG.Services.Gameserver.Modules;
namespace RPG.Services.Gameserver.Session; namespace RPG.Services.Gameserver.Session;
internal class PlayerSession : RPGSession internal class PlayerSession : RPGSession
{ {
public PlayerSession(ulong sessionId, ServiceBox serviceBox) : base(sessionId, serviceBox) private readonly IServiceScope _scope;
private readonly ModuleManager _moduleManager;
public PlayerSession(ulong sessionId, ServiceBox serviceBox, IServiceScopeFactory scopeFactory) : base(sessionId, serviceBox)
{ {
_scope = scopeFactory.CreateScope();
_moduleManager = _scope.ServiceProvider.GetRequiredService<ModuleManager>();
}
public async Task HandleGameCommand(ushort cmdType, ReadOnlyMemory<byte> body)
{
await _moduleManager.HandleAsync(this, (CmdType)cmdType, body);
}
public override void Dispose()
{
_scope.Dispose();
} }
} }

View file

@ -38,7 +38,18 @@ internal class GateserverCommandHandler : ServiceCommandHandler
}; };
} }
await session.SendAsync(CmdType.CmdPlayerGetTokenScRsp, rsp); await session.SendAsync((ushort)CmdType.CmdPlayerGetTokenScRsp, rsp);
}
}
[ServiceCommand(ServiceCommandType.ForwardGameMessage)]
public async Task OnForwardGameMessage(ServiceCommand command)
{
CmdForwardGameMessage cmd = CmdForwardGameMessage.Parser.ParseFrom(command.Body.Span);
if (_sessionManager.TryGet(cmd.SessionId, out NetworkSession? session))
{
await session.SendAsync(new((ushort)cmd.CmdType, ReadOnlyMemory<byte>.Empty, cmd.Payload.Memory));
} }
} }
} }

View file

@ -2,7 +2,6 @@
using Google.Protobuf; using Google.Protobuf;
using RPG.Network.Proto; using RPG.Network.Proto;
using RPG.Services.Core.Network; using RPG.Services.Core.Network;
using RPG.Services.Core.Network.Command;
using RPG.Services.Core.Session; using RPG.Services.Core.Session;
using RPG.Services.Gateserver.Network; using RPG.Services.Gateserver.Network;
@ -40,10 +39,10 @@ internal class NetworkSession : RPGSession
{ {
NetPacket.DeserializationResult result = NetPacket.TryDeserialize(recvBufferMem[..recvBufferIdx], out NetPacket? packet, out int bytesRead); NetPacket.DeserializationResult result = NetPacket.TryDeserialize(recvBufferMem[..recvBufferIdx], out NetPacket? packet, out int bytesRead);
if (result == NetPacket.DeserializationResult.BufferExceeded) break; if (result == NetPacket.DeserializationResult.BufferExceeded) break;
if (result == NetPacket.DeserializationResult.Corrupted) return; if (result == NetPacket.DeserializationResult.Corrupted) throw new Exception();
HandleSessionPacketAsync(packet!); HandleSessionPacketAsync(packet!);
Buffer.BlockCopy(_recvBuffer, recvBufferIdx, _recvBuffer, 0, recvBufferIdx -= bytesRead); Buffer.BlockCopy(_recvBuffer, bytesRead, _recvBuffer, 0, recvBufferIdx -= bytesRead);
} }
while (recvBufferIdx >= NetPacket.Overhead); while (recvBufferIdx >= NetPacket.Overhead);
} }
@ -66,7 +65,7 @@ internal class NetworkSession : RPGSession
private void HandleSessionPacketAsync(NetPacket packet) private void HandleSessionPacketAsync(NetPacket packet)
{ {
switch (packet.CmdType) switch ((CmdType)packet.CmdType)
{ {
case CmdType.CmdPlayerGetTokenCsReq: case CmdType.CmdPlayerGetTokenCsReq:
HandlePlayerGetTokenCsReq(PlayerGetTokenCsReq.Parser.ParseFrom(packet.Body.Span)); HandlePlayerGetTokenCsReq(PlayerGetTokenCsReq.Parser.ParseFrom(packet.Body.Span));
@ -102,4 +101,9 @@ internal class NetworkSession : RPGSession
CancellationTokenSource cts = new(TimeSpan.FromMilliseconds(timeoutMs)); CancellationTokenSource cts = new(TimeSpan.FromMilliseconds(timeoutMs));
return await socket.ReceiveAsync(buffer, cts.Token); return await socket.ReceiveAsync(buffer, cts.Token);
} }
public override void Dispose()
{
Socket?.Close();
}
} }