From f6b0209e2356ed546cec5130c0b889caa4f6d962 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 20 Apr 2024 12:37:57 -0400 Subject: [PATCH 1/3] feat: Add support for GP course effects Added support for GP course effects which are configurable by the new file GpCourseInfo.json. Extracted 15 course entries from the packet captures and encoded them into this file. Also added a new custom course called "Arrowgene QOL Course" (quality of life). This is an example of how to create new course effects which were not present when the game was live. --- Arrowgene.Ddon.GameServer/DdonGameServer.cs | 17 +- .../CharacterDecideCharacterIdHandler.cs | 16 +- .../GpGpCourseGetAvailableListHandler.cs | 39 + .../Handler/GetCharacterListHandler.cs | 30 +- .../Handler/GpCourseGetInfoHandler.cs | 50 +- .../Asset/GPCourseInfoAsset.cs | 18 + .../AssetReader/GPCourseInfoDeserializer.cs | 73 ++ Arrowgene.Ddon.Shared/AssetRepository.cs | 4 + .../Entity/EntitySerializer.cs | 6 + .../PacketStructure/C2LGpCourseGetInfoReq.cs | 36 + .../PacketStructure/L2CGpCourseGetInfoRes.cs | 50 ++ .../PacketStructure/S2CGPCourseStartNtc.cs | 31 + .../S2CGpGpCourseGetAvailableListRes.cs | 36 + .../Structure/CDataCharacterListInfo.cs | 20 +- .../Structure/CDataGPCourseAvailable.cs | 50 ++ .../Structure/CDataGPCourseEffectParam.cs | 42 + .../Entity/Structure/CDataGPCourseInfo.cs | 65 ++ .../Files/Assets/GpCourseInfo.json | 801 ++++++++++++++++++ Arrowgene.Ddon.Shared/Model/GPCourse.cs | 22 + Arrowgene.Ddon.Shared/Model/GPCourseEffect.cs | 15 + Arrowgene.Ddon.Shared/Model/GPValidCourse.cs | 14 + Arrowgene.Ddon.Shared/Network/PacketId.cs | 16 +- 22 files changed, 1415 insertions(+), 36 deletions(-) create mode 100644 Arrowgene.Ddon.GameServer/Handler/GpGpCourseGetAvailableListHandler.cs create mode 100644 Arrowgene.Ddon.Shared/Asset/GPCourseInfoAsset.cs create mode 100644 Arrowgene.Ddon.Shared/AssetReader/GPCourseInfoDeserializer.cs create mode 100644 Arrowgene.Ddon.Shared/Entity/PacketStructure/C2LGpCourseGetInfoReq.cs create mode 100644 Arrowgene.Ddon.Shared/Entity/PacketStructure/L2CGpCourseGetInfoRes.cs create mode 100644 Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CGPCourseStartNtc.cs create mode 100644 Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CGpGpCourseGetAvailableListRes.cs create mode 100644 Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseAvailable.cs create mode 100644 Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseEffectParam.cs create mode 100644 Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseInfo.cs create mode 100644 Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json create mode 100644 Arrowgene.Ddon.Shared/Model/GPCourse.cs create mode 100644 Arrowgene.Ddon.Shared/Model/GPCourseEffect.cs create mode 100644 Arrowgene.Ddon.Shared/Model/GPValidCourse.cs diff --git a/Arrowgene.Ddon.GameServer/DdonGameServer.cs b/Arrowgene.Ddon.GameServer/DdonGameServer.cs index f2b454bae..392da7364 100644 --- a/Arrowgene.Ddon.GameServer/DdonGameServer.cs +++ b/Arrowgene.Ddon.GameServer/DdonGameServer.cs @@ -86,7 +86,7 @@ public DdonGameServer(GameServerSetting setting, IDatabase database, AssetReposi public List StageList { get; } public override GameClientLookup ClientLookup { get; } - + // TODO: Maybe place somewhere else public readonly Dictionary LastRevivalPowerRechargeTime = new Dictionary(); @@ -164,7 +164,7 @@ private void LoadPacketHandler() AddHandler(new BazaarGetItemInfoHandler(this)); AddHandler(new BazaarGetItemListHandler(this)); AddHandler(new BazaarProceedsHandler(this)); - + AddHandler(new BinarySaveSetCharacterBinSavedataHandler(this)); AddHandler(new BlackListGetBlackListHandler(this)); @@ -216,7 +216,7 @@ private void LoadPacketHandler() AddHandler(new CraftGetCraftSettingHandler(this)); AddHandler(new CraftRecipeGetCraftRecipeHandler(this)); AddHandler(new CraftStartCraftHandler(this)); - + AddHandler(new DailyMissionListGetHandler(this)); AddHandler(new EquipChangeCharacterEquipHandler(this)); @@ -239,8 +239,9 @@ private void LoadPacketHandler() AddHandler(new Gp_28_2_1_Handler(this)); AddHandler(new GpGetUpdateAppCourseBonusFlagHandler(this)); AddHandler(new GpGetValidChatComGroupHandler(this)); - AddHandler(new GpGpEditGetVoiceListHandler(this)); - AddHandler(new GpGetGpHandler(this)); + AddHandler(new GpGpEditGetVoiceListHandler(this)); + AddHandler(new GpGetGpHandler(this)); + AddHandler(new GpGpCourseGetAvailableListHandler(this)); AddHandler(new GroupChatGroupChatGetMemberListHandler(this)); @@ -333,10 +334,10 @@ private void LoadPacketHandler() AddHandler(new PawnJoinPartyMypawnHandler(this)); AddHandler(new PawnPawnLostHandler(this)); AddHandler(new PawnTrainingGetPreparetionInfoToAdviceHandler(this)); - + AddHandler(new ProfileGetCharacterProfileHandler(this)); AddHandler(new ProfileGetMyCharacterProfileHandler(this)); - + AddHandler(new QuestEndDistributionQuestCancelHandler(this)); AddHandler(new QuestGetAdventureGuideQuestListHandler(this)); AddHandler(new QuestGetAdventureGuideQuestNoticeHandler(this)); @@ -363,7 +364,7 @@ private void LoadPacketHandler() AddHandler(new QuestSendLeaderQuestOrderConditionInfoHandler(this)); AddHandler(new QuestSendLeaderWaitOrderQuestListHandler(this)); AddHandler(new QuestSetPriorityQuestHandler(this)); - + AddHandler(new EntryBoardEntryBoardList(this)); AddHandler(new EntryBoardEntryBoardItemCreate(this)); AddHandler(new EntryBoardEntryBoardItemForceStart(this)); diff --git a/Arrowgene.Ddon.GameServer/Handler/CharacterDecideCharacterIdHandler.cs b/Arrowgene.Ddon.GameServer/Handler/CharacterDecideCharacterIdHandler.cs index d1d8984c5..c33947a46 100644 --- a/Arrowgene.Ddon.GameServer/Handler/CharacterDecideCharacterIdHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/CharacterDecideCharacterIdHandler.cs @@ -1,6 +1,7 @@ using Arrowgene.Ddon.GameServer.Dump; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Server.Network; +using Arrowgene.Ddon.Shared; using Arrowgene.Ddon.Shared.Entity; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; @@ -13,9 +14,11 @@ public class CharacterDecideCharacterIdHandler : PacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(CharacterDecideCharacterIdHandler)); + private AssetRepository _AssetRepo; public CharacterDecideCharacterIdHandler(DdonGameServer server) : base(server) { + _AssetRepo = server.AssetRepository; } public override PacketId Id => PacketId.C2S_CHARACTER_DECIDE_CHARACTER_ID_REQ; @@ -27,12 +30,21 @@ public override void Handle(GameClient client, IPacket packet) res.CharacterId = client.Character.CharacterId; res.CharacterInfo = new CDataCharacterInfo(client.Character); res.Unk0 = pcap.Unk0; // Removing this makes tons of tutorials pop up - + client.Send(res); - + // Unlocks menu options such as inventory, warping, etc. S2CCharacterContentsReleaseElementNtc contentsReleaseElementNotice = EntitySerializer.Get().Read(GameFull.data_Dump_20); client.Send(contentsReleaseElementNotice); + + foreach (var ValidCourse in _AssetRepo.GPCourseInfoAsset.ValidCourses) + { + client.Send(new S2CGPCourseStartNtc() + { + CourseID = ValidCourse.Value.Id, + ExpiryTimestamp = ValidCourse.Value.EndTime + }); + } } } } diff --git a/Arrowgene.Ddon.GameServer/Handler/GpGpCourseGetAvailableListHandler.cs b/Arrowgene.Ddon.GameServer/Handler/GpGpCourseGetAvailableListHandler.cs new file mode 100644 index 000000000..a72164062 --- /dev/null +++ b/Arrowgene.Ddon.GameServer/Handler/GpGpCourseGetAvailableListHandler.cs @@ -0,0 +1,39 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.GameServer.Dump; +using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Server.Network; +using Arrowgene.Ddon.Shared; +using Arrowgene.Ddon.Shared.Entity.PacketStructure; +using Arrowgene.Ddon.Shared.Network; +using Arrowgene.Logging; + +namespace Arrowgene.Ddon.GameServer.Handler +{ + public class GpGpCourseGetAvailableListHandler : PacketHandler + { + private static readonly ServerLogger Logger = LogProvider.Logger(typeof(GpGpCourseGetAvailableListHandler)); + + private AssetRepository _AssetRepo; + + public GpGpCourseGetAvailableListHandler(DdonGameServer server) : base(server) + { + _AssetRepo = server.AssetRepository; + } + + public override PacketId Id => PacketId.C2S_GP_GP_COURSE_GET_AVAILABLE_LIST_REQ; + + public override void Handle(GameClient client, IPacket packet) + { + S2CGpGpCourseGetAvailableListRes Response = new S2CGpGpCourseGetAvailableListRes(); + + // foreach (var Course in _AssetRepo.GPCourseInfoAsset.ValidCourses) + // { + // + // } + + // TODO: Send back real data based on JSON contents? + // TODO: PCAP doesn't have sample packet contents to see what is in it. + client.Send(Response); + } + } +} diff --git a/Arrowgene.Ddon.LoginServer/Handler/GetCharacterListHandler.cs b/Arrowgene.Ddon.LoginServer/Handler/GetCharacterListHandler.cs index 1ef683e2e..ebf125bd6 100644 --- a/Arrowgene.Ddon.LoginServer/Handler/GetCharacterListHandler.cs +++ b/Arrowgene.Ddon.LoginServer/Handler/GetCharacterListHandler.cs @@ -3,7 +3,9 @@ using Arrowgene.Buffers; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Server.Network; +using Arrowgene.Ddon.Shared; using Arrowgene.Ddon.Shared.Entity; +using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model; using Arrowgene.Ddon.Shared.Network; @@ -14,10 +16,13 @@ namespace Arrowgene.Ddon.LoginServer.Handler public class GetCharacterListHandler : PacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(GetCharacterListHandler)); + private AssetRepository _AssetRepo; + public GetCharacterListHandler(DdonLoginServer server) : base(server) { + _AssetRepo = server.AssetRepository; } public override PacketId Id => PacketId.C2L_GET_CHARACTER_LIST_REQ; @@ -39,6 +44,27 @@ public override void Handle(LoginClient client, IPacket packet) cResponse.CharacterListElement.CommunityCharacterBaseInfo.CharacterName.LastName = c.LastName; cResponse.CharacterListElement.CurrentJobBaseInfo.Job = c.Job; cResponse.CharacterListElement.CurrentJobBaseInfo.Level = (byte) c.ActiveCharacterJobData.Lv; + + List ValidCourses = new List(); + + foreach (var ValidCourse in _AssetRepo.GPCourseInfoAsset.ValidCourses) + { + CDataGPCourseValid cDataGPCourseValid = new CDataGPCourseValid() + { + Id = c.CharacterId, + CourseId = ValidCourse.Value.Id, + NameA = _AssetRepo.GPCourseInfoAsset.Courses[ValidCourse.Value.Id].Name, // Course Name + NameB = "https://members.dd-on.jp/sp_ingame/img/icon/bouken.jpg", // Link to a JPEG + StartTime = ValidCourse.Value.StartTime, + EndTime = ValidCourse.Value.EndTime, + }; + + ValidCourses.Add(cDataGPCourseValid); + } + + cResponse.GpCourseValidList = ValidCourses; + cResponse.NextFlowType = 1; + cResponse.IsClanMemberNotice = 1; // REMOVE // maybe? //cResponse.CharacterListElement.CurrentJobBaseInfo.Job = c.CharacterInfo.MatchingProfile.CurrentJob; //cResponse.CharacterListElement.CurrentJobBaseInfo.Level = (byte) c.CharacterInfo.MatchingProfile.CurrentJobLevel; @@ -48,11 +74,11 @@ public override void Handle(LoginClient client, IPacket packet) cResponse.MatchingProfile = c.MatchingProfile; cResponse.EquipItemInfo = c.Equipment.getEquipmentAsCDataEquipItemInfo(c.Job, EquipType.Performance) .Union(c.Equipment.getEquipmentAsCDataEquipItemInfo(c.Job, EquipType.Visual)) - .ToList(); + .ToList(); characterListResponse.Add(cResponse); } - + EntitySerializer.Get().WriteList(buffer, characterListResponse); Packet response = new Packet(PacketId.L2C_GET_CHARACTER_LIST_RES, buffer.GetAllBytes()); client.Send(response); diff --git a/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs b/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs index ea5eb5fbc..93e9a50a0 100644 --- a/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs +++ b/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs @@ -1,25 +1,63 @@ -using Arrowgene.Ddon.LoginServer.Dump; +using Arrowgene.Ddon.LoginServer.Dump; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Server.Network; +using Arrowgene.Ddon.Shared; +using Arrowgene.Ddon.Shared.Entity.PacketStructure; +using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; +using System; +using System.Linq; namespace Arrowgene.Ddon.LoginServer.Handler { - public class GpCourseGetInfoHandler : PacketHandler + public class GpCourseGetInfoHandler : StructurePacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(GpCourseGetInfoHandler)); + private AssetRepository _AssetRepo; + + private L2CGpCourseGetInfoRes _Response; public GpCourseGetInfoHandler(DdonLoginServer server) : base(server) { - } + _AssetRepo = server.AssetRepository; + _Response = new L2CGpCourseGetInfoRes(); + + foreach (var Course in _AssetRepo.GPCourseInfoAsset.Courses) + { + CDataGPCourseInfo cDataGPCourseInfo = new CDataGPCourseInfo() + { + CourseId = Course.Value.Id, + CourseName = Course.Value.Name, + DoubleCourseTarget = Course.Value.Target, + PrioGroup = (byte)Course.Value.PriorityGroup, + PrioSameTime = (byte)Course.Value.PrioritySameTime, + AnnounceType = (byte)Course.Value.AnnounceType, + EffectUIDs = Course.Value.Effects + }; - public override PacketId Id => PacketId.C2L_GP_COURSE_GET_INFO_REQ; + _Response.CourseInfo.Add(cDataGPCourseInfo); + } + + foreach (var Effect in _AssetRepo.GPCourseInfoAsset.Effects) + { + CDataGPCourseEffectParam cDataGPCourseEffectParam = new CDataGPCourseEffectParam() + { + EffectUID = Effect.Value.Uid, + EffectID = Effect.Value.Id, + Param0 = Effect.Value.Param0, + Param1 = Effect.Value.Param1 + }; + + _Response.Effects.Add(cDataGPCourseEffectParam); + } + } - public override void Handle(LoginClient client, IPacket packet) + public override void Handle(LoginClient client, StructurePacket packet) { - client.Send(LoginDump.Dump_22); + // client.Send(LoginDump.Dump_22); + client.Send(_Response); } } } diff --git a/Arrowgene.Ddon.Shared/Asset/GPCourseInfoAsset.cs b/Arrowgene.Ddon.Shared/Asset/GPCourseInfoAsset.cs new file mode 100644 index 000000000..566170152 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Asset/GPCourseInfoAsset.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Arrowgene.Ddon.Shared.Model; + +namespace Arrowgene.Ddon.Shared.Asset +{ + public class GPCourseInfoAsset + { + public GPCourseInfoAsset() + { + Courses = new Dictionary(); + Effects = new Dictionary(); + ValidCourses = new Dictionary(); + } + public Dictionary Courses { get; set; } + public Dictionary Effects { get; set; } + public Dictionary ValidCourses { get; set; } + } +} diff --git a/Arrowgene.Ddon.Shared/AssetReader/GPCourseInfoDeserializer.cs b/Arrowgene.Ddon.Shared/AssetReader/GPCourseInfoDeserializer.cs new file mode 100644 index 000000000..677bc4a44 --- /dev/null +++ b/Arrowgene.Ddon.Shared/AssetReader/GPCourseInfoDeserializer.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using Arrowgene.Ddon.Shared.Model; +using Arrowgene.Logging; +using Arrowgene.Ddon.Shared.Asset; + +namespace Arrowgene.Ddon.Shared.Csv +{ + public class GPCourseInfoDeserializer : IAssetDeserializer + { + private static readonly ILogger Logger = LogProvider.Logger(typeof(GPCourseInfoDeserializer)); + + public GPCourseInfoAsset ReadPath(string path) + { + Logger.Info($"Reading {path}"); + + GPCourseInfoAsset asset = new GPCourseInfoAsset(); + + string json = File.ReadAllText(path); + JsonDocument document = JsonDocument.Parse(json); + + var ValidCourses = document.RootElement.GetProperty("valid_courses").EnumerateArray().ToList(); + foreach (var ValidCourse in ValidCourses) + { + GPValidCourse obj = new GPValidCourse(); + obj.Id = ValidCourse.GetProperty("course_id").GetUInt32(); + obj.StartTime = ValidCourse.GetProperty("start_time").GetUInt64(); + obj.EndTime = ValidCourse.GetProperty("end_time").GetUInt64(); + obj.Comment = ValidCourse.GetProperty("comment").GetString(); + + asset.ValidCourses.Add(obj.Id, obj); + } + + var Courses = document.RootElement.GetProperty("courses").EnumerateArray().ToList(); + foreach (var Course in Courses) + { + GPCourse obj = new GPCourse(); + obj.Id = Course.GetProperty("id").GetUInt32(); + obj.Name = Course.GetProperty("name").GetString(); + obj.Target = Course.GetProperty("target").GetUInt32() == 1; + obj.PriorityGroup = Course.GetProperty("priority_grp").GetUInt32(); + obj.PrioritySameTime = Course.GetProperty("priority_same_time").GetUInt32(); + obj.AnnounceType = Course.GetProperty("announce_type").GetUInt32(); + obj.Comment = Course.GetProperty("comment").GetString(); + + var EffectUIDs = Course.GetProperty("effects").EnumerateArray().ToList(); + foreach (var EffectUID in EffectUIDs) + { + obj.Effects.Add(EffectUID.GetUInt32()); + } + + asset.Courses.Add(obj.Id, obj); + } + + var Effects = document.RootElement.GetProperty("effects").EnumerateArray().ToList(); + foreach (var Effect in Effects) + { + GpCourseEffect obj = new GpCourseEffect(); + obj.Uid = Effect.GetProperty("uid").GetUInt32(); + obj.Id = Effect.GetProperty("id").GetUInt32(); + obj.Param0 = Effect.GetProperty("param0").GetUInt32(); + obj.Param1 = Effect.GetProperty("param1").GetUInt32(); + obj.Comment = Effect.GetProperty("comment").GetString(); + + asset.Effects.Add(obj.Uid, obj); + } + + return asset; + } + } +} diff --git a/Arrowgene.Ddon.Shared/AssetRepository.cs b/Arrowgene.Ddon.Shared/AssetRepository.cs index fd300fa99..7d5951f7c 100644 --- a/Arrowgene.Ddon.Shared/AssetRepository.cs +++ b/Arrowgene.Ddon.Shared/AssetRepository.cs @@ -31,6 +31,7 @@ public class AssetRepository public const string WarpPointsKey = "WarpPoints.csv"; public const string CraftingRecipesKey = "CraftingRecipes.json"; public const string LearnedNormalSkillsKey = "LearnedNormalSkills.json"; + public const string GPCourseInfoKey = "GpCourseInfo.json"; private static readonly ILogger Logger = LogProvider.Logger(typeof(AssetRepository)); @@ -64,6 +65,7 @@ public AssetRepository(string folder) WarpPoints = new List(); CraftingRecipesAsset = new List(); LearnedNormalSkillsAsset = new LearnedNormalSkillsAsset(); + GPCourseInfoAsset = new GPCourseInfoAsset(); } public List ClientErrorCodes { get; private set; } @@ -80,6 +82,7 @@ public AssetRepository(string folder) public List WarpPoints { get; private set; } public List CraftingRecipesAsset { get; private set; } public LearnedNormalSkillsAsset LearnedNormalSkillsAsset { get; set; } + public GPCourseInfoAsset GPCourseInfoAsset { get; private set; } public void Initialize() { @@ -97,6 +100,7 @@ public void Initialize() RegisterAsset(value => WarpPoints = value, WarpPointsKey, new WarpPointCsv()); RegisterAsset(value => CraftingRecipesAsset = value, CraftingRecipesKey, new JsonReaderWriter>()); RegisterAsset(value => LearnedNormalSkillsAsset = value, LearnedNormalSkillsKey, new LearnedNormalSkillsDeserializer()); + RegisterAsset(value => GPCourseInfoAsset = value, GPCourseInfoKey, new GPCourseInfoDeserializer()); } private void RegisterAsset(Action onLoadAction, string key, IAssetDeserializer readerWriter) diff --git a/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs b/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs index 42e094725..e9557fb99 100644 --- a/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs +++ b/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs @@ -105,6 +105,9 @@ static EntitySerializer() Create(new CDataGoodsParamUnk7.Serializer()); Create(new CDataGatheringItemElement.Serializer()); Create(new CDataGPCourseValidSerializer()); + Create(new CDataGPCourseInfoSerializer()); + Create(new CDataGPCourseEffectParamSerializer()); + Create(new CDataGPCourseAvailableSerializer()); Create(new CDataHistoryElement.Serializer()); Create(new CDataItemEquipElement.Serializer()); Create(new CDataItemEquipElementParam.Serializer()); @@ -426,6 +429,7 @@ static EntitySerializer() Create(new L2CLoginRes.Serializer()); Create(new L2CLoginWaitNumNtc.Serializer()); Create(new L2CNextConnectionServerNtc.Serializer()); + Create(new L2CGpCourseGetInfoRes.Serializer()); Create(new S2CActionSetPlayerActionHistoryRes.Serializer()); @@ -510,6 +514,8 @@ static EntitySerializer() Create(new S2CEquipUpdateHidePawnHeadArmorRes.Serializer()); Create(new S2CEquipUpdateHidePawnLanternRes.Serializer()); Create(new S2CGpGetValidChatComGroupRes.Serializer()); + Create(new S2CGpGpCourseGetAvailableListRes.Serializer()); + Create(new S2CGPCourseStartNtc.Serializer()); Create(new S2CInnGetPenaltyHealStayPriceRes.Serializer()); Create(new S2CInnGetStayPriceRes.Serializer()); Create(new S2CInnStayInnRes.Serializer()); diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2LGpCourseGetInfoReq.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2LGpCourseGetInfoReq.cs new file mode 100644 index 000000000..06a107616 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2LGpCourseGetInfoReq.cs @@ -0,0 +1,36 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Network; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class C2LGpCourseGetInfoReq : IPacketStructure + { + public C2LGpCourseGetInfoReq() + { + } + + public PacketId Id => PacketId.C2L_GP_COURSE_GET_INFO_REQ; + + public class Serializer : PacketEntitySerializer + { + + public override void Write(IBuffer buffer, C2LGpCourseGetInfoReq obj) + { + // WriteEntity(buffer, obj.CharacterInfo); + // WriteUInt32(buffer, obj.WaitNum); + // WriteByte(buffer, obj.RotationServerId); + } + + public override C2LGpCourseGetInfoReq Read(IBuffer buffer) + { + // C2LCreateCharacterDataReq obj = new C2LCreateCharacterDataReq(); + // obj.CharacterInfo = ReadEntity(buffer); + // obj.WaitNum = ReadUInt32(buffer); + // obj.RotationServerId = ReadByte(buffer); + // return obj; + return new C2LGpCourseGetInfoReq(); + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/L2CGpCourseGetInfoRes.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/L2CGpCourseGetInfoRes.cs new file mode 100644 index 000000000..fa1318475 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/L2CGpCourseGetInfoRes.cs @@ -0,0 +1,50 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Network; +using System.Collections.Generic; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class L2CGpCourseGetInfoRes : ServerResponse + { + public L2CGpCourseGetInfoRes() + { + CourseInfo = new List(); + Effects = new List(); + Unk0 = new byte[] + { + 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x30, 0xB0, 0x00, 0x49, 0xE3, 0x83, 0x9F + }; + } + + public List CourseInfo { get; set; } + public List Effects { get; set; } + public byte[] Unk0 { get; set; } + + public override PacketId Id => PacketId.L2C_GP_COURSE_GET_INFO_RES; + + public class Serializer : PacketEntitySerializer + { + + public override void Write(IBuffer buffer, L2CGpCourseGetInfoRes obj) + { + WriteServerResponse(buffer, obj); + WriteEntityList(buffer, obj.CourseInfo); + WriteEntityList(buffer, obj.Effects); + WriteByteArray(buffer, obj.Unk0); + } + + public override L2CGpCourseGetInfoRes Read(IBuffer buffer) + { + L2CGpCourseGetInfoRes obj = new L2CGpCourseGetInfoRes(); + + ReadServerResponse(buffer, obj); + obj.CourseInfo = ReadEntityList(buffer); + obj.Effects = ReadEntityList(buffer); + obj.Unk0 = ReadByteArray(buffer, obj.Unk0.Length); + + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CGPCourseStartNtc.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CGPCourseStartNtc.cs new file mode 100644 index 000000000..a28351d94 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CGPCourseStartNtc.cs @@ -0,0 +1,31 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Network; +using System; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class S2CGPCourseStartNtc : IPacketStructure + { + public PacketId Id => PacketId.S2C_GP_COURSE_START_NTC; + + public UInt32 CourseID { get; set; } + public UInt64 ExpiryTimestamp { get; set; } + + public class Serializer : PacketEntitySerializer + { + public override void Write(IBuffer buffer, S2CGPCourseStartNtc obj) + { + WriteUInt32(buffer, obj.CourseID); + WriteUInt64(buffer, obj.ExpiryTimestamp); + } + + public override S2CGPCourseStartNtc Read(IBuffer buffer) + { + S2CGPCourseStartNtc obj = new S2CGPCourseStartNtc(); + obj.CourseID = ReadUInt32(buffer); + obj.ExpiryTimestamp = ReadUInt64(buffer); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CGpGpCourseGetAvailableListRes.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CGpGpCourseGetAvailableListRes.cs new file mode 100644 index 000000000..00e89237f --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CGpGpCourseGetAvailableListRes.cs @@ -0,0 +1,36 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Network; +using System.Collections.Generic; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class S2CGpGpCourseGetAvailableListRes : ServerResponse + { + public override PacketId Id => PacketId.S2C_GP_GP_COURSE_GET_AVAILABLE_LIST_RES; + + public S2CGpGpCourseGetAvailableListRes() + { + AvailableCourses = new List(); + } + + public List AvailableCourses { get; set; } + + public class Serializer : PacketEntitySerializer + { + public override void Write(IBuffer buffer, S2CGpGpCourseGetAvailableListRes obj) + { + WriteServerResponse(buffer, obj); + WriteEntityList(buffer, obj.AvailableCourses); + } + + public override S2CGpGpCourseGetAvailableListRes Read(IBuffer buffer) + { + S2CGpGpCourseGetAvailableListRes obj = new S2CGpGpCourseGetAvailableListRes(); + ReadServerResponse(buffer, obj); + obj.AvailableCourses = ReadEntityList(buffer); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/Structure/CDataCharacterListInfo.cs b/Arrowgene.Ddon.Shared/Entity/Structure/CDataCharacterListInfo.cs index e91f65a89..5242bfea6 100644 --- a/Arrowgene.Ddon.Shared/Entity/Structure/CDataCharacterListInfo.cs +++ b/Arrowgene.Ddon.Shared/Entity/Structure/CDataCharacterListInfo.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Arrowgene.Buffers; namespace Arrowgene.Ddon.Shared.Entity.Structure @@ -18,15 +18,15 @@ public CDataCharacterListInfo() IsClanMemberNotice = 0; } - public CDataCharacterListElement CharacterListElement; - public CDataEditInfo EditInfo; - public CDataMatchingProfile MatchingProfile; - public List EquipItemInfo; - public List GpCourseValidList; - public byte NextFlowType; - public string ClanName; - public string ClanNameShort; - public byte IsClanMemberNotice; + public CDataCharacterListElement CharacterListElement { get; set; } + public CDataEditInfo EditInfo { get; set; } + public CDataMatchingProfile MatchingProfile { get; set; } + public List EquipItemInfo { get; set; } + public List GpCourseValidList { get; set; } + public byte NextFlowType { get; set; } + public string ClanName { get; set; } + public string ClanNameShort { get; set; } + public byte IsClanMemberNotice { get; set; } } public class CDataCharacterListInfoSerializer : EntitySerializer diff --git a/Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseAvailable.cs b/Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseAvailable.cs new file mode 100644 index 000000000..de5a734d6 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseAvailable.cs @@ -0,0 +1,50 @@ +using Arrowgene.Buffers; +using System; + +namespace Arrowgene.Ddon.Shared.Entity.Structure +{ + public class CDataGPCourseAvailable + { + public CDataGPCourseAvailable() + { + CourseID = 0; + CourseName = ""; + } + + // Character ID? + public UInt32 ID { get; set; } + public string CourseName { get; set; } + public UInt64 UseLimitTime { get; set; } + public UInt32 CourseID { get; set; } + public UInt32 LineupID { get; set; } + public UInt32 BackIconID { get; set; } + public UInt32 FrameIconID { get; set; } + } + + public class CDataGPCourseAvailableSerializer : EntitySerializer + { + public override void Write(IBuffer buffer, CDataGPCourseAvailable obj) + { + WriteUInt32(buffer, obj.ID); + WriteMtString(buffer, obj.CourseName); + WriteUInt64(buffer, obj.UseLimitTime); + WriteUInt32(buffer, obj.CourseID); + WriteUInt32(buffer, obj.LineupID); + WriteUInt32(buffer, obj.BackIconID); + WriteUInt32(buffer, obj.FrameIconID); + } + + public override CDataGPCourseAvailable Read(IBuffer buffer) + { + CDataGPCourseAvailable obj = new CDataGPCourseAvailable(); + obj.ID = ReadUInt32(buffer); + obj.CourseName = ReadMtString(buffer); + obj.UseLimitTime = ReadUInt64(buffer); + obj.CourseID = ReadUInt32(buffer); + obj.LineupID = ReadUInt32(buffer); + obj.BackIconID = ReadUInt32(buffer); + obj.FrameIconID = ReadUInt32(buffer); + return obj; + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseEffectParam.cs b/Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseEffectParam.cs new file mode 100644 index 000000000..8a8ed4796 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseEffectParam.cs @@ -0,0 +1,42 @@ +using Arrowgene.Buffers; +using System; + +namespace Arrowgene.Ddon.Shared.Entity.Structure +{ + public class CDataGPCourseEffectParam + { + public CDataGPCourseEffectParam() + { + EffectUID = 0; + EffectID = 0; + Param0 = 0; + Param1 = 0; + } + + public UInt32 EffectUID { get; set; } + public UInt32 EffectID { get; set; } + public UInt32 Param0 { get; set; } + public UInt32 Param1 { get; set; } + } + + public class CDataGPCourseEffectParamSerializer : EntitySerializer + { + public override void Write(IBuffer buffer, CDataGPCourseEffectParam obj) + { + WriteUInt32(buffer, obj.EffectUID); + WriteUInt32(buffer, obj.EffectID); + WriteUInt32(buffer, obj.Param0); + WriteUInt32(buffer, obj.Param1); + } + + public override CDataGPCourseEffectParam Read(IBuffer buffer) + { + CDataGPCourseEffectParam obj = new CDataGPCourseEffectParam(); + obj.EffectUID = ReadUInt32(buffer); + obj.EffectID = ReadUInt32(buffer); + obj.Param0 = ReadUInt32(buffer); + obj.Param1 = ReadUInt32(buffer); + return obj; + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseInfo.cs b/Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseInfo.cs new file mode 100644 index 000000000..5573e8dc8 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/Structure/CDataGPCourseInfo.cs @@ -0,0 +1,65 @@ +using Arrowgene.Buffers; +using System; +using System.Collections.Generic; + +namespace Arrowgene.Ddon.Shared.Entity.Structure +{ + public class CDataGPCourseInfo + { + public CDataGPCourseInfo() + { + CourseId = 0; + CourseName = "unknown"; + DoubleCourseTarget = false; + PrioGroup = 0; + PrioSameTime = 0; + AnnounceType = 0; + EffectUIDs = new List(); + } + + public UInt32 CourseId { get; set; } + public string CourseName { get; set; } + public bool DoubleCourseTarget; + public byte PrioGroup; + public byte PrioSameTime; + public byte AnnounceType; + public List EffectUIDs; + } + + public class CDataGPCourseInfoSerializer : EntitySerializer + { + public override void Write(IBuffer buffer, CDataGPCourseInfo obj) + { + WriteUInt32(buffer, obj.CourseId); + WriteMtString(buffer,obj.CourseName); + WriteByte(buffer, Convert.ToByte(obj.DoubleCourseTarget)); + WriteByte(buffer, obj.PrioGroup); + WriteByte(buffer, obj.PrioSameTime); + WriteByte(buffer, obj.AnnounceType); + WriteMtArray(buffer, obj.EffectUIDs, WriteEffectUID); + } + + public override CDataGPCourseInfo Read(IBuffer buffer) + { + CDataGPCourseInfo obj = new CDataGPCourseInfo(); + obj.CourseId = ReadUInt32(buffer); + obj.CourseName = ReadMtString(buffer); + obj.DoubleCourseTarget = Convert.ToBoolean(ReadByte(buffer)); + obj.PrioGroup = ReadByte(buffer); + obj.PrioSameTime = ReadByte(buffer); + obj.AnnounceType = ReadByte(buffer); + obj.EffectUIDs = ReadMtArray(buffer, ReadEffectUID); + + return obj; + } + private UInt32 ReadEffectUID(IBuffer buffer) + { + return ReadUInt32(buffer); + } + + private void WriteEffectUID(IBuffer buffer, UInt32 Value) + { + WriteUInt32(buffer, Value); + } + } +} diff --git a/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json b/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json new file mode 100644 index 000000000..e3eebba91 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json @@ -0,0 +1,801 @@ +{ + "comment" : [ + "You can find official course effects described at https://web.archive.org/web/20170711035052/https://members.dd-on.jp/shop/payment/course/1", + "", + "It is possible to modify this file to change the course which is available to be used.", + "The 'valid_courses' key, contains a list of all valid courses for every character on the server.", + "The 'course_id' field corresponds to the 'id' field of the objects in the 'courses' key.", + "The 'start_time' and 'end_time' values are in unix timestamp format." + ], + "valid_courses": [ + { + "course_id": 17, + "start_time": 1440993600, + "end_time": 4103413199, + "comment": "Course 17 valid until the year 2100" + } + ], + "courses": [ + { + "id": 1, + "name": "冒険パスポート", + "comment": "Adventure Passport", + "target": 0, + "priority_grp": 100, + "priority_same_time": 70, + "announce_type": 2, + "effects": [ 1, 2, 3, 5, 6, 7, 9, 10, 11, 40, 41, 44, 50, 52, 128, 213, 215, 218, 253 ] + }, + { + "id": 2, + "name": "報酬サポートコース", + "comment": "Reward Support Course", + "target": 1, + "priority_grp": 100, + "priority_same_time": 40, + "announce_type": 3, + "effects": [ 12, 13, 14, 15, 16, 17, 51, 59, 149 ] + }, + { + "id": 3, + "name": "成長サポートコース", + "comment": "Growth Support Course", + "target": 1, + "priority_grp": 100, + "priority_same_time": 30, + "announce_type": 3, + "effects": [ 18, 19, 20, 22, 23, 25, 26, 27, 70, 119, 150, 151, 208, 209, 210, 211, 212, 246, 254 ] + }, + { + "id": 4, + "name": "安心アシストコース", + "comment": "Reliable Assist Course", + "target": 1, + "priority_grp": 100, + "priority_same_time": 50, + "announce_type": 3, + "effects": [ 71, 72, 73, 74, 75 ] + }, + { + "id": 5, + "name": "ダブル発動特典", + "comment": "Double Activation Benefit", + "target": 0, + "priority_grp": 90, + "priority_same_time": 20, + "announce_type": 1, + "effects": [ 189, 190, 191, 192, 193, 194, 195, 196, 248, 255, 266 ] + }, + { + "id": 6, + "name": "トリプル発動特典", + "comment": "Triple Activation Benefit", + "target": 0, + "priority_grp": 80, + "priority_same_time": 10, + "announce_type": 1, + "effects": [ 197, 198, 199, 200, 201, 202, 203, 204, 247, 256, 264, 265, 267 ] + }, + { + "id": 7, + "name": "グランドミッションコース", + "comment": "Grand Mission Course", + "target": 0, + "priority_grp": 200, + "priority_same_time": 100, + "announce_type": 1, + "effects": [] + }, + { + "id": 8, + "name": "ネットカフェ特典コース", + "comment": "Internet Cafe Special Course", + "target": 0, + "priority_grp": 50, + "priority_same_time": 0, + "announce_type": 1, + "effects": [ 49, 1, 2, 3, 5, 6, 7, 9, 10, 11, 40, 41, 44, 50, 52, 128, 213, 215, 218, 253, 12, 13, 14, 15, 16, 17, 51, 59, 149, 18, 19, 20, 22, 23, 25, 26, 27, 70, 119, 150, 151, 208, 209, 210, 211, 212, 246, 254, 189, 190, 191, 192, 193, 194, 195, 196, 248, 255, 266, 69, 216, 217 ] + }, + { + "id": 9, + "name": "格納チェストパスポート1", + "comment": "Storage Chest Passport 1", + "target": 0, + "priority_grp": 100, + "priority_same_time": 80, + "announce_type": 2, + "effects": [ 69 ] + }, + { + "id": 10, + "name": "討伐経験値5倍コース", + "comment": "Subjugation Experience Value 5x Course", + "target": 0, + "priority_grp": 100, + "priority_same_time": 25, + "announce_type": 3, + "effects": [ 76, 77, 78 ] + }, + { + "id": 11, + "name": "格納チェストパスポート2", + "comment": "Storage Chest Passport 2", + "target": 0, + "priority_grp": 100, + "priority_same_time": 85, + "announce_type": 2, + "effects": [ 216 ] + }, + { + "id": 12, + "name": "格納チェストパスポート3", + "comment": "Storage Chest Passport 3", + "target": 0, + "priority_grp": 100, + "priority_same_time": 90, + "announce_type": 2, + "effects": [ 217 ] + }, + { + "id": 13, + "name": "PP獲得量3倍コース", + "comment": "3x PP Acquisition Course", + "target": 0, + "priority_grp": 100, + "priority_same_time": 24, + "announce_type": 3, + "effects": [ 219 ] + }, + { + "id": 14, + "name": "PP獲得量5倍コース", + "comment": "5x PP Acquisition Course", + "target": 0, + "priority_grp": 100, + "priority_same_time": 23, + "announce_type": 3, + "effects": [ 229 ] + }, + { + "id": 15, + "name": "全コース無料開放", + "comment": "All courses free of charge", + "target": 0, + "priority_grp": 20, + "priority_same_time": 0, + "announce_type": 3, + "effects": [ 1, 2, 3, 5, 6, 7, 9, 10, 11, 40, 41, 44, 50, 52, 128, 213, 215, 218, 253, 12, 13, 14, 15, 16, 17, 51, 59, 149, 18, 19, 20, 22, 23, 25, 26, 27, 70, 119, 150, 151, 208, 209, 210, 211, 212, 246, 254, 71, 72, 73, 74, 75, 69, 216, 217 ] + }, + { + "id": 16, + "name": "PP獲得量5倍コースⅡ", + "comment": "5x PP Acquisition Course II", + "target": 0, + "priority_grp": 100, + "priority_same_time": 22, + "announce_type": 3, + "effects": [ 261, 262 ] + }, + { + "id": 17, + "name": "Arrowgene QOL Course", + "comment": "Example of custom made course effect. Allows item boxes to be used outside the main city, enable expanded storage tabs and allows craft items to be consumed from the storage box.", + "target": 0, + "priority_grp": 100, + "priority_same_time": 70, + "announce_type": 2, + "effects": [ 2, 3, 69, 216, 217, 128] + } + ], + "effects": [ + { + "uid": 1, + "id": 1, + "param0": 0, + "param1": 0, + "comment": "Restore your resilience as many times as you want in one day!" + }, + { + "uid": 2, + "id": 2, + "param0": 0, + "param1": 0, + "comment": "You can use the expansion frame of the storage box!" + }, + { + "uid": 3, + "id": 3, + "param0": 0, + "param1": 0, + "comment": "You can use the storage box for your base!" + }, + { + "uid": 5, + "id": 5, + "param0": 100, + "param1": 0, + "comment": "" + }, + { + "uid": 6, + "id": 6, + "param0": 50, + "param1": 0, + "comment": "" + }, + { + "uid": 7, + "id": 7, + "param0": 10, + "param1": 0, + "comment": "" + }, + { + "uid": 9, + "id": 9, + "param0": 5, + "param1": 0, + "comment": "" + }, + { + "uid": 10, + "id": 10, + "param0": 86400, + "param1": 0, + "comment": "" + }, + { + "uid": 11, + "id": 11, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 12, + "id": 12, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 13, + "id": 13, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 14, + "id": 14, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 15, + "id": 15, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 16, + "id": 16, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 17, + "id": 17, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 18, + "id": 18, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 19, + "id": 19, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 20, + "id": 20, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 22, + "id": 22, + "param0": 150, + "param1": 0, + "comment": "" + }, + { + "uid": 23, + "id": 23, + "param0": 150, + "param1": 0, + "comment": "" + }, + { + "uid": 25, + "id": 25, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 26, + "id": 26, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 27, + "id": 27, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 40, + "id": 28, + "param0": 150, + "param1": 0, + "comment": "" + }, + { + "uid": 41, + "id": 4, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 44, + "id": 38, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 49, + "id": 40, + "param0": 1500, + "param1": 0, + "comment": "" + }, + { + "uid": 50, + "id": 34, + "param0": 50, + "param1": 0, + "comment": "" + }, + { + "uid": 51, + "id": 37, + "param0": 150, + "param1": 0, + "comment": "" + }, + { + "uid": 52, + "id": 35, + "param0": 100, + "param1": 0, + "comment": "" + }, + { + "uid": 59, + "id": 41, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 69, + "id": 42, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 70, + "id": 43, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 71, + "id": 44, + "param0": 50, + "param1": 0, + "comment": "" + }, + { + "uid": 72, + "id": 45, + "param0": 125, + "param1": 0, + "comment": "" + }, + { + "uid": 73, + "id": 46, + "param0": 50, + "param1": 0, + "comment": "" + }, + { + "uid": 74, + "id": 47, + "param0": 66, + "param1": 200, + "comment": "" + }, + { + "uid": 75, + "id": 48, + "param0": 350, + "param1": 0, + "comment": "" + }, + { + "uid": 76, + "id": 18, + "param0": 500, + "param1": 0, + "comment": "" + }, + { + "uid": 77, + "id": 25, + "param0": 500, + "param1": 0, + "comment": "" + }, + { + "uid": 78, + "id": 43, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 119, + "id": 49, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 128, + "id": 50, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 149, + "id": 51, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 150, + "id": 52, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 151, + "id": 53, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 189, + "id": 18, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 190, + "id": 19, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 191, + "id": 20, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 192, + "id": 52, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 193, + "id": 25, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 194, + "id": 26, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 195, + "id": 27, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 196, + "id": 53, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 197, + "id": 18, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 198, + "id": 19, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 199, + "id": 20, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 200, + "id": 52, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 201, + "id": 25, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 202, + "id": 26, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 203, + "id": 27, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 204, + "id": 53, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 208, + "id": 60, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 209, + "id": 61, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 210, + "id": 64, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 211, + "id": 65, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 212, + "id": 59, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 213, + "id": 63, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 215, + "id": 57, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 216, + "id": 54, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 217, + "id": 55, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 218, + "id": 62, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 219, + "id": 49, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 229, + "id": 49, + "param0": 500, + "param1": 0, + "comment": "" + }, + { + "uid": 246, + "id": 72, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 247, + "id": 49, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 248, + "id": 49, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 253, + "id": 58, + "param0": 0, + "param1": 0, + "comment": "" + }, + { + "uid": 254, + "id": 73, + "param0": 200, + "param1": 0, + "comment": "" + }, + { + "uid": 255, + "id": 73, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 256, + "id": 73, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 261, + "id": 49, + "param0": 500, + "param1": 0, + "comment": "" + }, + { + "uid": 262, + "id": 73, + "param0": 500, + "param1": 0, + "comment": "" + }, + { + "uid": 264, + "id": 16, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 265, + "id": 17, + "param0": 300, + "param1": 0, + "comment": "" + }, + { + "uid": 266, + "id": 37, + "param0": 250, + "param1": 0, + "comment": "" + }, + { + "uid": 267, + "id": 37, + "param0": 300, + "param1": 0, + "comment": "" + } + ] +} \ No newline at end of file diff --git a/Arrowgene.Ddon.Shared/Model/GPCourse.cs b/Arrowgene.Ddon.Shared/Model/GPCourse.cs new file mode 100644 index 000000000..01b61a54a --- /dev/null +++ b/Arrowgene.Ddon.Shared/Model/GPCourse.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace Arrowgene.Ddon.Shared.Model +{ + public class GPCourse + { + public GPCourse() + { + Effects = new List(); + } + + public uint Id { get; set; } + public string Name { get; set; } + public bool Target { get; set; } + public uint PriorityGroup { get; set; } + public uint PrioritySameTime { get; set; } + public uint AnnounceType { get; set; } + public List Effects { get; set; } + + public string Comment { get; set; } + } +} diff --git a/Arrowgene.Ddon.Shared/Model/GPCourseEffect.cs b/Arrowgene.Ddon.Shared/Model/GPCourseEffect.cs new file mode 100644 index 000000000..c7037ff2c --- /dev/null +++ b/Arrowgene.Ddon.Shared/Model/GPCourseEffect.cs @@ -0,0 +1,15 @@ +namespace Arrowgene.Ddon.Shared.Model +{ + public class GpCourseEffect + { + public GpCourseEffect() + { + } + + public uint Uid { get; set; } + public uint Id { get; set; } + public uint Param0 { get; set; } + public uint Param1 { get; set; } + public string Comment { get; set; } + } +} diff --git a/Arrowgene.Ddon.Shared/Model/GPValidCourse.cs b/Arrowgene.Ddon.Shared/Model/GPValidCourse.cs new file mode 100644 index 000000000..208f34c46 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Model/GPValidCourse.cs @@ -0,0 +1,14 @@ +namespace Arrowgene.Ddon.Shared.Model +{ + public class GPValidCourse + { + public GPValidCourse() + { + } + + public uint Id { get; set; } + public ulong StartTime { get; set; } + public ulong EndTime { get; set; } + public string Comment { get; set; } + } +} diff --git a/Arrowgene.Ddon.Shared/Network/PacketId.cs b/Arrowgene.Ddon.Shared/Network/PacketId.cs index bb58e35bb..3f9f09866 100644 --- a/Arrowgene.Ddon.Shared/Network/PacketId.cs +++ b/Arrowgene.Ddon.Shared/Network/PacketId.cs @@ -50,7 +50,7 @@ public static PacketId GetPacketId(string name) return PacketId.UNKNOWN; } - + public static PacketId GetLoginPacketId(byte groupId, ushort handlerId, byte handlerSubId) { int hashCode = GetHashCode(groupId, handlerId, handlerSubId); @@ -72,7 +72,7 @@ public static PacketId GetGamePacketId(byte groupId, ushort handlerId, byte hand return new PacketId(groupId, handlerId, handlerSubId, "Unknown", ServerType.Game, PacketSource.Unknown); } - + public readonly byte GroupId; public readonly ushort HandlerId; public readonly byte HandlerSubId; @@ -130,13 +130,13 @@ public override int GetHashCode() { return GetHashCode(GroupId, HandlerId, HandlerSubId); } - - public static bool operator ==(PacketId a, PacketId b) + + public static bool operator ==(PacketId a, PacketId b) { return a.Equals(b); } - public static bool operator !=(PacketId a, PacketId b) + public static bool operator !=(PacketId a, PacketId b) { return !a.Equals(b); } @@ -1262,7 +1262,7 @@ private static Dictionary InitializeLoginPacketIds() public static readonly PacketId C2S_GP_GP_SHOP_CAN_BUY_REQ = new PacketId(28, 23, 1, "C2S_GP_GP_SHOP_CAN_BUY_REQ", ServerType.Game, PacketSource.Client); public static readonly PacketId S2C_GP_GP_SHOP_CAN_BUY_RES = new PacketId(28, 23, 2, "S2C_GP_GP_SHOP_CAN_BUY_RES", ServerType.Game, PacketSource.Server); // 課金商品購入可能状態の取得に public static readonly PacketId S2C_GP_28_24_16_NTC = new PacketId(28, 24, 16, "S2C_GP_28_24_16_NTC", ServerType.Game, PacketSource.Server); - public static readonly PacketId S2C_GP_28_25_16_NTC = new PacketId(28, 25, 16, "S2C_GP_28_25_16_NTC", ServerType.Game, PacketSource.Server); + public static readonly PacketId S2C_GP_COURSE_START_NTC = new PacketId(28, 25, 16, "S2C_GP_COURSE_START_NTC", ServerType.Game, PacketSource.Server); // S2C_GP_28_25_16_NTC public static readonly PacketId S2C_GP_28_26_16_NTC = new PacketId(28, 26, 16, "S2C_GP_28_26_16_NTC", ServerType.Game, PacketSource.Server); public static readonly PacketId S2C_GP_28_27_16_NTC = new PacketId(28, 27, 16, "S2C_GP_28_27_16_NTC", ServerType.Game, PacketSource.Server); public static readonly PacketId S2C_GP_28_28_16_NTC = new PacketId(28, 28, 16, "S2C_GP_28_28_16_NTC", ServerType.Game, PacketSource.Server); @@ -3168,7 +3168,7 @@ private static Dictionary InitializeGamePacketIds() AddPacketIdEntry(packetIds, C2S_GP_GP_SHOP_CAN_BUY_REQ); AddPacketIdEntry(packetIds, S2C_GP_GP_SHOP_CAN_BUY_RES); AddPacketIdEntry(packetIds, S2C_GP_28_24_16_NTC); - AddPacketIdEntry(packetIds, S2C_GP_28_25_16_NTC); + AddPacketIdEntry(packetIds, S2C_GP_COURSE_START_NTC); AddPacketIdEntry(packetIds, S2C_GP_28_26_16_NTC); AddPacketIdEntry(packetIds, S2C_GP_28_27_16_NTC); AddPacketIdEntry(packetIds, S2C_GP_28_28_16_NTC); @@ -4042,7 +4042,7 @@ private static Dictionary InitializeGamePacketIds() } #endregion - + // initialize at the very end private static readonly Dictionary LoginPacketIds = InitializeLoginPacketIds(); private static readonly Dictionary GamePacketIds = InitializeGamePacketIds(); From 50cf8a7716ab69994c555a4f1b689a17093b4cae Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 20 Apr 2024 23:22:28 -0400 Subject: [PATCH 2/3] feedback: Address code review comments - Removed unused packet req. - Added icon_path, description and url keys to the json definition. --- .../Handler/GetCharacterListHandler.cs | 2 +- .../Handler/GpCourseGetInfoHandler.cs | 13 ++-- .../AssetReader/GPCourseInfoDeserializer.cs | 3 + .../PacketStructure/C2LGpCourseGetInfoReq.cs | 36 --------- .../Files/Assets/GpCourseInfo.json | 75 ++++++++++++++++--- Arrowgene.Ddon.Shared/Model/GPCourse.cs | 6 +- 6 files changed, 77 insertions(+), 58 deletions(-) delete mode 100644 Arrowgene.Ddon.Shared/Entity/PacketStructure/C2LGpCourseGetInfoReq.cs diff --git a/Arrowgene.Ddon.LoginServer/Handler/GetCharacterListHandler.cs b/Arrowgene.Ddon.LoginServer/Handler/GetCharacterListHandler.cs index ebf125bd6..4de79a2b4 100644 --- a/Arrowgene.Ddon.LoginServer/Handler/GetCharacterListHandler.cs +++ b/Arrowgene.Ddon.LoginServer/Handler/GetCharacterListHandler.cs @@ -54,7 +54,7 @@ public override void Handle(LoginClient client, IPacket packet) Id = c.CharacterId, CourseId = ValidCourse.Value.Id, NameA = _AssetRepo.GPCourseInfoAsset.Courses[ValidCourse.Value.Id].Name, // Course Name - NameB = "https://members.dd-on.jp/sp_ingame/img/icon/bouken.jpg", // Link to a JPEG + NameB = _AssetRepo.GPCourseInfoAsset.Courses[ValidCourse.Value.Id].IconPath, // Link to a icon StartTime = ValidCourse.Value.StartTime, EndTime = ValidCourse.Value.EndTime, }; diff --git a/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs b/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs index 93e9a50a0..232952cb4 100644 --- a/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs +++ b/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs @@ -11,20 +11,17 @@ namespace Arrowgene.Ddon.LoginServer.Handler { - public class GpCourseGetInfoHandler : StructurePacketHandler + public class GpCourseGetInfoHandler : PacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(GpCourseGetInfoHandler)); - private AssetRepository _AssetRepo; - private L2CGpCourseGetInfoRes _Response; public GpCourseGetInfoHandler(DdonLoginServer server) : base(server) { - _AssetRepo = server.AssetRepository; _Response = new L2CGpCourseGetInfoRes(); - foreach (var Course in _AssetRepo.GPCourseInfoAsset.Courses) + foreach (var Course in server.AssetRepository.GPCourseInfoAsset.Courses) { CDataGPCourseInfo cDataGPCourseInfo = new CDataGPCourseInfo() { @@ -40,7 +37,7 @@ public GpCourseGetInfoHandler(DdonLoginServer server) : base(server) _Response.CourseInfo.Add(cDataGPCourseInfo); } - foreach (var Effect in _AssetRepo.GPCourseInfoAsset.Effects) + foreach (var Effect in server.AssetRepository.GPCourseInfoAsset.Effects) { CDataGPCourseEffectParam cDataGPCourseEffectParam = new CDataGPCourseEffectParam() { @@ -54,7 +51,9 @@ public GpCourseGetInfoHandler(DdonLoginServer server) : base(server) } } - public override void Handle(LoginClient client, StructurePacket packet) + public override PacketId Id => PacketId.C2L_GP_COURSE_GET_INFO_REQ; + + public override void Handle(LoginClient client, IPacket packet) { // client.Send(LoginDump.Dump_22); client.Send(_Response); diff --git a/Arrowgene.Ddon.Shared/AssetReader/GPCourseInfoDeserializer.cs b/Arrowgene.Ddon.Shared/AssetReader/GPCourseInfoDeserializer.cs index 677bc4a44..2750fdf13 100644 --- a/Arrowgene.Ddon.Shared/AssetReader/GPCourseInfoDeserializer.cs +++ b/Arrowgene.Ddon.Shared/AssetReader/GPCourseInfoDeserializer.cs @@ -44,6 +44,9 @@ public GPCourseInfoAsset ReadPath(string path) obj.PrioritySameTime = Course.GetProperty("priority_same_time").GetUInt32(); obj.AnnounceType = Course.GetProperty("announce_type").GetUInt32(); obj.Comment = Course.GetProperty("comment").GetString(); + obj.Url = Course.GetProperty("url").GetString(); + obj.IconPath = Course.GetProperty("icon_path").GetString(); + obj.Description = Course.GetProperty("description").GetString(); var EffectUIDs = Course.GetProperty("effects").EnumerateArray().ToList(); foreach (var EffectUID in EffectUIDs) diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2LGpCourseGetInfoReq.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2LGpCourseGetInfoReq.cs deleted file mode 100644 index 06a107616..000000000 --- a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2LGpCourseGetInfoReq.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Arrowgene.Buffers; -using Arrowgene.Ddon.Shared.Entity.Structure; -using Arrowgene.Ddon.Shared.Network; - -namespace Arrowgene.Ddon.Shared.Entity.PacketStructure -{ - public class C2LGpCourseGetInfoReq : IPacketStructure - { - public C2LGpCourseGetInfoReq() - { - } - - public PacketId Id => PacketId.C2L_GP_COURSE_GET_INFO_REQ; - - public class Serializer : PacketEntitySerializer - { - - public override void Write(IBuffer buffer, C2LGpCourseGetInfoReq obj) - { - // WriteEntity(buffer, obj.CharacterInfo); - // WriteUInt32(buffer, obj.WaitNum); - // WriteByte(buffer, obj.RotationServerId); - } - - public override C2LGpCourseGetInfoReq Read(IBuffer buffer) - { - // C2LCreateCharacterDataReq obj = new C2LCreateCharacterDataReq(); - // obj.CharacterInfo = ReadEntity(buffer); - // obj.WaitNum = ReadUInt32(buffer); - // obj.RotationServerId = ReadByte(buffer); - // return obj; - return new C2LGpCourseGetInfoReq(); - } - } - } -} diff --git a/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json b/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json index e3eebba91..a2fd7d083 100644 --- a/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json +++ b/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json @@ -1,14 +1,14 @@ { - "comment" : [ - "You can find official course effects described at https://web.archive.org/web/20170711035052/https://members.dd-on.jp/shop/payment/course/1", - "", - "It is possible to modify this file to change the course which is available to be used.", - "The 'valid_courses' key, contains a list of all valid courses for every character on the server.", - "The 'course_id' field corresponds to the 'id' field of the objects in the 'courses' key.", - "The 'start_time' and 'end_time' values are in unix timestamp format." + "comment": [ + "You can find official course effects described at https://web.archive.org/web/20170711054053/https://members.dd-on.jp/shop/payment/stone/top", + "", + "It is possible to modify this file to change the course which is available to be used.", + "The 'valid_courses' key, contains a list of all valid courses for every character on the server.", + "The 'course_id' field corresponds to the 'id' field of the objects in the 'courses' key.", + "The 'start_time' and 'end_time' values are in unix timestamp format." ], "valid_courses": [ - { + { "course_id": 17, "start_time": 1440993600, "end_time": 4103413199, @@ -20,6 +20,9 @@ "id": 1, "name": "冒険パスポート", "comment": "Adventure Passport", + "icon_path": "", + "description": "Resurrection power is restored as many times as you like! A set of basic services that make playing DDON useful, such as expanding storage boxes.", + "url": "https://web.archive.org/web/20170711035052/https://members.dd-on.jp/shop/payment/course/1", "target": 0, "priority_grp": 100, "priority_same_time": 70, @@ -30,6 +33,9 @@ "id": 2, "name": "報酬サポートコース", "comment": "Reward Support Course", + "icon_path": "", + "description": "Rarely small items are available! Acquisition amount of rims and blood orbs, acquisition number of collection/mining/treasure boxes, etc.", + "url": "https://web.archive.org/web/20170711035052/https://members.dd-on.jp/shop/payment/course/2", "target": 1, "priority_grp": 100, "priority_same_time": 40, @@ -40,6 +46,9 @@ "id": 3, "name": "成長サポートコース", "comment": "Growth Support Course", + "icon_path": "", + "description": "Experience of learners and pawns, a course to help grow learners and pawns that also raise area points!", + "url": "https://web.archive.org/web/20170711035052/https://members.dd-on.jp/shop/payment/course/3", "target": 1, "priority_grp": 100, "priority_same_time": 30, @@ -50,6 +59,9 @@ "id": 4, "name": "安心アシストコース", "comment": "Reliable Assist Course", + "icon_path": "", + "description": "Reduces damage to learners and pawns, as well as accumulation of abnormal conditions! It reduces the danger of adventure and provides a reassuring life.", + "url": "https://web.archive.org/web/20170711035052/https://members.dd-on.jp/shop/payment/course/4", "target": 1, "priority_grp": 100, "priority_same_time": 50, @@ -60,6 +72,9 @@ "id": 5, "name": "ダブル発動特典", "comment": "Double Activation Benefit", + "icon_path": "", + "description": "", + "url": "", "target": 0, "priority_grp": 90, "priority_same_time": 20, @@ -70,6 +85,9 @@ "id": 6, "name": "トリプル発動特典", "comment": "Triple Activation Benefit", + "icon_path": "", + "description": "", + "url": "", "target": 0, "priority_grp": 80, "priority_same_time": 10, @@ -80,6 +98,9 @@ "id": 7, "name": "グランドミッションコース", "comment": "Grand Mission Course", + "icon_path": "", + "description": "Grand Mission Earnings Score and Job Points Up!Cumulative score rewards make it easier to aim for high rank rewards!", + "url": "https://web.archive.org/web/20170711035052/https://members.dd-on.jp/shop/payment/course/7", "target": 0, "priority_grp": 200, "priority_same_time": 100, @@ -90,6 +111,9 @@ "id": 8, "name": "ネットカフェ特典コース", "comment": "Internet Cafe Special Course", + "icon_path": "", + "description": "", + "url": "", "target": 0, "priority_grp": 50, "priority_same_time": 0, @@ -100,6 +124,9 @@ "id": 9, "name": "格納チェストパスポート1", "comment": "Storage Chest Passport 1", + "icon_path": "", + "description": "Store 400 additional items!A passport that makes playing DDON even more convenient!", + "url": "https://web.archive.org/web/20170711035052/https://members.dd-on.jp/shop/payment/course/9", "target": 0, "priority_grp": 100, "priority_same_time": 80, @@ -110,6 +137,9 @@ "id": 10, "name": "討伐経験値5倍コース", "comment": "Subjugation Experience Value 5x Course", + "icon_path": "", + "description": "", + "url": "", "target": 0, "priority_grp": 100, "priority_same_time": 25, @@ -120,6 +150,9 @@ "id": 11, "name": "格納チェストパスポート2", "comment": "Storage Chest Passport 2", + "icon_path": "", + "description": "", + "url": "", "target": 0, "priority_grp": 100, "priority_same_time": 85, @@ -130,6 +163,9 @@ "id": 12, "name": "格納チェストパスポート3", "comment": "Storage Chest Passport 3", + "icon_path": "", + "description": "", + "url": "", "target": 0, "priority_grp": 100, "priority_same_time": 90, @@ -140,6 +176,9 @@ "id": 13, "name": "PP獲得量3倍コース", "comment": "3x PP Acquisition Course", + "icon_path": "", + "description": "", + "url": "", "target": 0, "priority_grp": 100, "priority_same_time": 24, @@ -150,6 +189,9 @@ "id": 14, "name": "PP獲得量5倍コース", "comment": "5x PP Acquisition Course", + "icon_path": "", + "description": "", + "url": "", "target": 0, "priority_grp": 100, "priority_same_time": 23, @@ -160,6 +202,9 @@ "id": 15, "name": "全コース無料開放", "comment": "All courses free of charge", + "icon_path": "", + "description": "", + "url": "", "target": 0, "priority_grp": 20, "priority_same_time": 0, @@ -170,21 +215,27 @@ "id": 16, "name": "PP獲得量5倍コースⅡ", "comment": "5x PP Acquisition Course II", + "icon_path": "", + "description": "", + "url": "", "target": 0, "priority_grp": 100, "priority_same_time": 22, "announce_type": 3, "effects": [ 261, 262 ] }, - { + { "id": 17, "name": "Arrowgene QOL Course", - "comment": "Example of custom made course effect. Allows item boxes to be used outside the main city, enable expanded storage tabs and allows craft items to be consumed from the storage box.", + "comment": "Example of custom made course effect.", + "icon_path": "", + "description": "Allows item boxes to be used outside the main city, enable expanded storage tabs and allows craft items to be consumed from the storage box.", + "url": "", "target": 0, "priority_grp": 100, "priority_same_time": 70, "announce_type": 2, - "effects": [ 2, 3, 69, 216, 217, 128] + "effects": [ 2, 3, 69, 216, 217, 128 ] } ], "effects": [ @@ -487,7 +538,7 @@ "id": 50, "param0": 0, "param1": 0, - "comment": "" + "comment": "Materials stored in storage chests can be used for crafting!" }, { "uid": 149, diff --git a/Arrowgene.Ddon.Shared/Model/GPCourse.cs b/Arrowgene.Ddon.Shared/Model/GPCourse.cs index 01b61a54a..8c93a8bb1 100644 --- a/Arrowgene.Ddon.Shared/Model/GPCourse.cs +++ b/Arrowgene.Ddon.Shared/Model/GPCourse.cs @@ -11,12 +11,14 @@ public GPCourse() public uint Id { get; set; } public string Name { get; set; } + public string Comment { get; set; } + public string Description { get; set; } + public string Url { get; set; } + public string IconPath { get; set; } public bool Target { get; set; } public uint PriorityGroup { get; set; } public uint PrioritySameTime { get; set; } public uint AnnounceType { get; set; } public List Effects { get; set; } - - public string Comment { get; set; } } } From 8dfe5c9d5812f1a36702f9bdef1305c55f56f29b Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sun, 21 Apr 2024 12:11:55 -0400 Subject: [PATCH 3/3] feedback: Address code review comments - Support hot reload of JSON file. - Add known enum names to comment field of effect list. --- .../Handler/GpCourseGetInfoHandler.cs | 26 ++-- .../Files/Assets/GpCourseInfo.json | 132 +++++++++--------- 2 files changed, 80 insertions(+), 78 deletions(-) diff --git a/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs b/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs index 232952cb4..19e9a5144 100644 --- a/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs +++ b/Arrowgene.Ddon.LoginServer/Handler/GpCourseGetInfoHandler.cs @@ -15,13 +15,20 @@ public class GpCourseGetInfoHandler : PacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(GpCourseGetInfoHandler)); - private L2CGpCourseGetInfoRes _Response; + private AssetRepository _AssetRepo; public GpCourseGetInfoHandler(DdonLoginServer server) : base(server) { - _Response = new L2CGpCourseGetInfoRes(); + _AssetRepo = server.AssetRepository; + } + + public override PacketId Id => PacketId.C2L_GP_COURSE_GET_INFO_REQ; + + public override void Handle(LoginClient client, IPacket packet) + { + L2CGpCourseGetInfoRes Response = new L2CGpCourseGetInfoRes(); - foreach (var Course in server.AssetRepository.GPCourseInfoAsset.Courses) + foreach (var Course in _AssetRepo.GPCourseInfoAsset.Courses) { CDataGPCourseInfo cDataGPCourseInfo = new CDataGPCourseInfo() { @@ -34,10 +41,10 @@ public GpCourseGetInfoHandler(DdonLoginServer server) : base(server) EffectUIDs = Course.Value.Effects }; - _Response.CourseInfo.Add(cDataGPCourseInfo); + Response.CourseInfo.Add(cDataGPCourseInfo); } - foreach (var Effect in server.AssetRepository.GPCourseInfoAsset.Effects) + foreach (var Effect in _AssetRepo.GPCourseInfoAsset.Effects) { CDataGPCourseEffectParam cDataGPCourseEffectParam = new CDataGPCourseEffectParam() { @@ -47,16 +54,11 @@ public GpCourseGetInfoHandler(DdonLoginServer server) : base(server) Param1 = Effect.Value.Param1 }; - _Response.Effects.Add(cDataGPCourseEffectParam); + Response.Effects.Add(cDataGPCourseEffectParam); } - } - - public override PacketId Id => PacketId.C2L_GP_COURSE_GET_INFO_REQ; - public override void Handle(LoginClient client, IPacket packet) - { // client.Send(LoginDump.Dump_22); - client.Send(_Response); + client.Send(Response); } } } diff --git a/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json b/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json index a2fd7d083..360810e77 100644 --- a/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json +++ b/Arrowgene.Ddon.Shared/Files/Assets/GpCourseInfo.json @@ -8,11 +8,11 @@ "The 'start_time' and 'end_time' values are in unix timestamp format." ], "valid_courses": [ - { + { "course_id": 17, "start_time": 1440993600, "end_time": 4103413199, - "comment": "Course 17 valid until the year 2100" + "comment": "Course 17 valid until the year 2100" } ], "courses": [ @@ -244,301 +244,301 @@ "id": 1, "param0": 0, "param1": 0, - "comment": "Restore your resilience as many times as you want in one day!" + "comment": "GP_COURSE_EFFECT_INFINITE_REVIVE" }, { "uid": 2, "id": 2, "param0": 0, "param1": 0, - "comment": "You can use the expansion frame of the storage box!" + "comment": "GP_COURSE_EFFECT_STRAGE_EXTEND" }, { "uid": 3, "id": 3, "param0": 0, "param1": 0, - "comment": "You can use the storage box for your base!" + "comment": "GP_COURSE_EFFECT_EXTRA_STRAGE_BOX" }, { "uid": 5, "id": 5, "param0": 100, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_FREE_MY_WARP_POINT" }, { "uid": 6, "id": 6, "param0": 50, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_WARP_DISCOUNT" }, { "uid": 7, "id": 7, "param0": 10, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_QUEST_ORDER_LIST_EXTEND" }, { "uid": 9, "id": 9, "param0": 5, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_BAZAAR_EXHIBIT_EXTEND" }, { "uid": 10, "id": 10, "param0": 86400, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_BAZAAR_RE_EXHIBIT_SHORTEN" }, { "uid": 11, "id": 11, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_AREA_MASTER_SUPPLY" }, { "uid": 12, "id": 12, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_WORLD_QUEST_REWARD_REALITY_UP" }, { "uid": 13, "id": 13, "param0": 0, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_WORLD_QUEST_SPECIAL_REWARD" }, { "uid": 14, "id": 14, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_RIM_BQ_REWARD_UP" }, { "uid": 15, "id": 15, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_RIM_WQ_REWARD_UP" }, { "uid": 16, "id": 16, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_DROP_UP" }, { "uid": 17, "id": 17, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_GATHERING_ITEM_NUM_UP" }, { "uid": 18, "id": 18, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_EXP_UP" }, { "uid": 19, "id": 19, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_BQ_REWARD_EXP_UP" }, { "uid": 20, "id": 20, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_WQ_REWARD_EXP_UP" }, { "uid": 22, "id": 22, "param0": 150, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_AREA_POINT_BQ_REWARD_UP" }, { "uid": 23, "id": 23, "param0": 150, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_AREA_POINT_WQ_REWARD_UP" }, { "uid": 25, "id": 25, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_ENEMY_EXP_UP" }, { "uid": 26, "id": 26, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_BQ_REWARD_EXP_UP" }, { "uid": 27, "id": 27, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_WQ_REWARD_EXP_UP" }, { "uid": 40, "id": 28, "param0": 150, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_CRAFT_EXP_UP" }, { "uid": 41, "id": 4, "param0": 0, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ALL_RELEASE_WARP_POINT" }, { "uid": 44, "id": 38, "param0": 0, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_REVIVE_BY_RIM" }, { "uid": 49, "id": 40, "param0": 1500, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_REGULARLY_SEND_MAIL" }, { "uid": 50, "id": 34, "param0": 50, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_RESCUE_MOTION_SHORTEN" }, { "uid": 51, "id": 37, "param0": 150, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_BLOOD_ORB_UP" }, { "uid": 52, "id": 35, "param0": 100, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_STAY_DISCOUNT" }, { "uid": 59, "id": 41, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_EXM_REWARD_UP" }, { "uid": 69, "id": 42, "param0": 0, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_BAGGAGE_RENTAL01" }, { "uid": 70, "id": 43, "param0": 0, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_DISABLE_PARTY_ADJUST_ENEMY_EXP" }, { "uid": 71, "id": 44, "param0": 50, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_INCOMING_DAMAGE_CUT_OFF" }, { "uid": 72, "id": 45, "param0": 125, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENDURANCE_UP" }, { "uid": 73, "id": 46, "param0": 50, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_DEBUFF_DAMAGE_CUT_OFF" }, { "uid": 74, "id": 47, "param0": 66, "param1": 200, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENABLE_LEVER_GACHA_ASSIST" }, { "uid": 75, "id": 48, "param0": 350, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_AUTO_RESCUE" }, { "uid": 76, "id": 18, "param0": 500, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_EXP_UP" }, { "uid": 77, "id": 25, "param0": 500, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_ENEMY_EXP_UP" }, { "uid": 78, "id": 43, "param0": 0, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_DISABLE_PARTY_ADJUST_ENEMY_EXP" }, { "uid": 119, "id": 49, "param0": 200, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_PP_UP" }, { "uid": 128, "id": 50, "param0": 0, "param1": 0, - "comment": "Materials stored in storage chests can be used for crafting!" + "comment": "GP_COURSE_EFFECT_CRAFT_BAGGAGE_EXTEND" }, { "uid": 149, @@ -566,21 +566,21 @@ "id": 18, "param0": 250, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_EXP_UP" }, { "uid": 190, "id": 19, "param0": 250, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_ENEMY_EXP_UP" }, { "uid": 191, "id": 20, "param0": 250, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_WQ_REWARD_EXP_UP" }, { "uid": 192, @@ -594,21 +594,21 @@ "id": 25, "param0": 250, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_ENEMY_EXP_UP" }, { "uid": 194, "id": 26, "param0": 250, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_BQ_REWARD_EXP_UP" }, { "uid": 195, "id": 27, "param0": 250, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_WQ_REWARD_EXP_UP" }, { "uid": 196, @@ -622,21 +622,21 @@ "id": 18, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_EXP_UP" }, { "uid": 198, "id": 19, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_BQ_REWARD_EXP_UP" }, { "uid": 199, "id": 20, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_WQ_REWARD_EXP_UP" }, { "uid": 200, @@ -650,21 +650,21 @@ "id": 25, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_ENEMY_EXP_UP" }, { "uid": 202, "id": 26, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_BQ_REWARD_EXP_UP" }, { "uid": 203, "id": 27, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_PAWN_BQ_REWARD_EXP_UP" }, { "uid": 204, @@ -748,14 +748,14 @@ "id": 49, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_PP_UP" }, { "uid": 229, "id": 49, "param0": 500, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_PP_UP" }, { "uid": 246, @@ -769,14 +769,14 @@ "id": 49, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_PP_UP" }, { "uid": 248, "id": 49, "param0": 250, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_PP_UP" }, { "uid": 253, @@ -811,7 +811,7 @@ "id": 49, "param0": 500, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_PP_UP" }, { "uid": 262, @@ -825,28 +825,28 @@ "id": 16, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_ENEMY_DROP_UP" }, { "uid": 265, "id": 17, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_GATHERING_ITEM_NUM_UP" }, { "uid": 266, "id": 37, "param0": 250, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_BLOOD_ORB_UP" }, { "uid": 267, "id": 37, "param0": 300, "param1": 0, - "comment": "" + "comment": "GP_COURSE_EFFECT_BLOOD_ORB_UP" } ] } \ No newline at end of file