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]
# CA1822: Mark members as static
dotnet_diagnostic.CA1822.severity = none
# 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" />
</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>

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

View file

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

View file

@ -38,6 +38,9 @@ public class SessionManager
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;
}
[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.Gameserver.Extensions;
using RPG.Services.Gameserver.Modules;
using RPG.Services.Gameserver.Network.Command;
namespace RPG.Services.Gameserver;
@ -13,6 +17,9 @@ internal static class Program
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.SetupRPGService<RPGGameserver, GameserverCommandHandler>();
builder.Services.AddModules()
.AddScoped<ModuleManager>()
.AddSingleton<ExcelTables>();
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;
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.Gameserver.Modules;
namespace RPG.Services.Gameserver.Session;
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 RPG.Network.Proto;
using RPG.Services.Core.Network;
using RPG.Services.Core.Network.Command;
using RPG.Services.Core.Session;
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);
if (result == NetPacket.DeserializationResult.BufferExceeded) break;
if (result == NetPacket.DeserializationResult.Corrupted) return;
if (result == NetPacket.DeserializationResult.Corrupted) throw new Exception();
HandleSessionPacketAsync(packet!);
Buffer.BlockCopy(_recvBuffer, recvBufferIdx, _recvBuffer, 0, recvBufferIdx -= bytesRead);
Buffer.BlockCopy(_recvBuffer, bytesRead, _recvBuffer, 0, recvBufferIdx -= bytesRead);
}
while (recvBufferIdx >= NetPacket.Overhead);
}
@ -66,7 +65,7 @@ internal class NetworkSession : RPGSession
private void HandleSessionPacketAsync(NetPacket packet)
{
switch (packet.CmdType)
switch ((CmdType)packet.CmdType)
{
case CmdType.CmdPlayerGetTokenCsReq:
HandlePlayerGetTokenCsReq(PlayerGetTokenCsReq.Parser.ParseFrom(packet.Body.Span));
@ -102,4 +101,9 @@ internal class NetworkSession : RPGSession
CancellationTokenSource cts = new(TimeSpan.FromMilliseconds(timeoutMs));
return await socket.ReceiveAsync(buffer, cts.Token);
}
public override void Dispose()
{
Socket?.Close();
}
}