diff --git a/.editorconfig b/.editorconfig index b344e721e..4ee64a6dc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,7 +9,7 @@ root = true indent_style = space # Code files -[*.{cs,sql,json}] +[*.{cs,sql,json,csx}] indent_size = 4 insert_final_newline = true charset = utf-8 diff --git a/Arrowgene.Ddon.Cli/Command/ServerCommand.cs b/Arrowgene.Ddon.Cli/Command/ServerCommand.cs index d24689333..f5903d0bd 100644 --- a/Arrowgene.Ddon.Cli/Command/ServerCommand.cs +++ b/Arrowgene.Ddon.Cli/Command/ServerCommand.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Threading; using Arrowgene.Ddon.Database; using Arrowgene.Ddon.GameServer; using Arrowgene.Ddon.LoginServer; @@ -10,6 +5,10 @@ using Arrowgene.Ddon.Shared; using Arrowgene.Ddon.WebServer; using Arrowgene.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Threading; namespace Arrowgene.Ddon.Cli.Command { @@ -20,6 +19,7 @@ public class ServerCommand : ICommand private readonly Setting _setting; private DdonLoginServer _loginServer; private DdonGameServer _gameServer; + private ScriptedServerSettings _scriptServerSettings; private DdonWebServer _webServer; private RpcWebServer _rpcWebServer; private IDatabase _database; @@ -110,6 +110,12 @@ public CommandResultType Run(CommandParameter parameter) _assetRepository.Initialize(); } + if (_scriptServerSettings == null) + { + _scriptServerSettings = new ScriptedServerSettings(_setting.AssetPath); + _scriptServerSettings.LoadSettings(_setting.GameServerSetting.GameLogicSetting); + } + if (_loginServer == null) { _loginServer = new DdonLoginServer(_setting.LoginServerSetting, _setting.GameServerSetting.GameLogicSetting, _database, _assetRepository); diff --git a/Arrowgene.Ddon.Cli/Program.cs b/Arrowgene.Ddon.Cli/Program.cs index 1a07b2393..042d3a08b 100644 --- a/Arrowgene.Ddon.Cli/Program.cs +++ b/Arrowgene.Ddon.Cli/Program.cs @@ -20,15 +20,15 @@ * along with Arrowgene.Ddon.Cli. If not, see . */ +using Arrowgene.Ddon.Cli.Command; +using Arrowgene.Ddon.Shared; +using Arrowgene.Ddon.Shared.Network; +using Arrowgene.Logging; using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; -using Arrowgene.Ddon.Cli.Command; -using Arrowgene.Ddon.Shared; -using Arrowgene.Ddon.Shared.Network; -using Arrowgene.Logging; namespace Arrowgene.Ddon.Cli { diff --git a/Arrowgene.Ddon.GameServer/Arrowgene.Ddon.GameServer.csproj b/Arrowgene.Ddon.GameServer/Arrowgene.Ddon.GameServer.csproj index c1272072c..620340a1a 100644 --- a/Arrowgene.Ddon.GameServer/Arrowgene.Ddon.GameServer.csproj +++ b/Arrowgene.Ddon.GameServer/Arrowgene.Ddon.GameServer.csproj @@ -17,6 +17,6 @@ - + diff --git a/Arrowgene.Ddon.GameServer/Characters/EpitaphRoadManager.cs b/Arrowgene.Ddon.GameServer/Characters/EpitaphRoadManager.cs index 41bce2482..195abe54c 100644 --- a/Arrowgene.Ddon.GameServer/Characters/EpitaphRoadManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/EpitaphRoadManager.cs @@ -1322,6 +1322,22 @@ public List RollGatheringLoot(GameClient client, Charact return results; } + public bool CheckUnlockConditions(GameClient client, EpitaphBarrier barrier) + { + foreach (var sectionId in barrier.DependentSectionIds) + { + var sectionInfo = _Server.EpitaphRoadManager.GetSectionById(sectionId); + foreach (var unlockId in sectionInfo.UnlockDependencies) + { + if (!client.Character.EpitaphRoadState.UnlockedContent.Contains(unlockId)) + { + return false; + } + } + } + return true; + } + /// /// Called by the task manager. The main task will signal all channels /// to flush the cached information queried by the player when first diff --git a/Arrowgene.Ddon.GameServer/Characters/StageManager.cs b/Arrowgene.Ddon.GameServer/Characters/StageManager.cs index 234d6647c..45aa0ec8a 100644 --- a/Arrowgene.Ddon.GameServer/Characters/StageManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/StageManager.cs @@ -1,15 +1,9 @@ using Arrowgene.Ddon.GameServer.Dump; -using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity; +using Arrowgene.Ddon.Shared.Entity.PacketStructure; +using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Arrowgene.Ddon.Shared.Entity.Structure; -using Arrowgene.Ddon.Shared.Network; -using System.IO; namespace Arrowgene.Ddon.GameServer.Characters { diff --git a/Arrowgene.Ddon.GameServer/DdonGameServer.cs b/Arrowgene.Ddon.GameServer/DdonGameServer.cs index a1b4bea1e..e2fffaa87 100644 --- a/Arrowgene.Ddon.GameServer/DdonGameServer.cs +++ b/Arrowgene.Ddon.GameServer/DdonGameServer.cs @@ -29,6 +29,7 @@ using Arrowgene.Ddon.GameServer.Dump; using Arrowgene.Ddon.GameServer.Handler; using Arrowgene.Ddon.GameServer.Party; +using Arrowgene.Ddon.GameServer.Scripting; using Arrowgene.Ddon.GameServer.Shop; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Server.Handler; @@ -53,6 +54,7 @@ public DdonGameServer(GameServerSetting setting, IDatabase database, AssetReposi : base(ServerType.Game, setting.ServerSetting, database, assetRepository) { Setting = new GameServerSetting(setting); + ScriptManager = new ScriptManager(this); ClientLookup = new GameClientLookup(); ChatLogHandler = new ChatLogHandler(); ChatManager = new ChatManager(this); @@ -91,6 +93,7 @@ public DdonGameServer(GameServerSetting setting, IDatabase database, AssetReposi public event EventHandler ClientConnectionChangeEvent; public GameServerSetting Setting { get; } + public ScriptManager ScriptManager { get; } public ChatManager ChatManager { get; } public ItemManager ItemManager { get; } public CraftManager CraftManager { get; } @@ -129,6 +132,8 @@ public DdonGameServer(GameServerSetting setting, IDatabase database, AssetReposi public override void Start() { + ScriptManager.CompileScripts(); + QuestManager.LoadQuests(this); GpCourseManager.EvaluateCourses(); @@ -136,7 +141,7 @@ public override void Start() { ScheduleManager.StartServerTasks(); } - + LoadChatHandler(); LoadPacketHandler(); base.Start(); diff --git a/Arrowgene.Ddon.GameServer/GameServerSetting.cs b/Arrowgene.Ddon.GameServer/GameServerSetting.cs index b1fba56c1..a5ff91c50 100644 --- a/Arrowgene.Ddon.GameServer/GameServerSetting.cs +++ b/Arrowgene.Ddon.GameServer/GameServerSetting.cs @@ -7,7 +7,7 @@ namespace Arrowgene.Ddon.GameServer public class GameServerSetting { [DataMember(Order = 1)] public ServerSetting ServerSetting { get; set; } - [DataMember(Order = 2)] public GameLogicSetting GameLogicSetting { get; set; } + public GameLogicSetting GameLogicSetting { get; set; } public GameServerSetting() { diff --git a/Arrowgene.Ddon.GameServer/Handler/NpcGetExtendedFacilityHandler.cs b/Arrowgene.Ddon.GameServer/Handler/NpcGetExtendedFacilityHandler.cs index 8733982ce..ab29d2b62 100644 --- a/Arrowgene.Ddon.GameServer/Handler/NpcGetExtendedFacilityHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/NpcGetExtendedFacilityHandler.cs @@ -22,86 +22,16 @@ public NpcGetExtendedFacilityHandler(DdonGameServer server) : base(server) public override S2CNpcGetNpcExtendedFacilityRes Handle(GameClient client, C2SNpcGetNpcExtendedFacilityReq request) { var result = new S2CNpcGetNpcExtendedFacilityRes(); - if (gNpcExtendedBehavior.ContainsKey(request.NpcId)) + + if (Server.ScriptManager.NpcExtendedFacilities.ContainsKey(request.NpcId)) { result.NpcId = request.NpcId; - gNpcExtendedBehavior[request.NpcId](Server, client, result); + Server.ScriptManager.NpcExtendedFacilities[request.NpcId].GetExtendedOptions(Server, client, result); } - return result; - } - private static bool CheckUnlockConditions(DdonGameServer server, GameClient client, EpitaphBarrier barrier) - { - foreach (var sectionId in barrier.DependentSectionIds) - { - var sectionInfo = server.EpitaphRoadManager.GetSectionById(sectionId); - foreach (var unlockId in sectionInfo.UnlockDependencies) - { - if (!client.Character.EpitaphRoadState.UnlockedContent.Contains(unlockId)) - { - return false; - } - } - } - return true; + return result; } - private static readonly Dictionary> gNpcExtendedBehavior = new Dictionary>() - { - [NpcId.Pehr1] = (DdonGameServer server, GameClient client, S2CNpcGetNpcExtendedFacilityRes result) => - { - if (client.Character.CompletedQuests.ContainsKey((QuestId) 60300020) || (client.QuestState.IsQuestActive(60300020) && client.QuestState.GetQuestState(60300020).Step > 2)) - { - result.ExtendedMenuItemList.Add(new CDataNpcExtendedFacilityMenuItem() { FunctionClass = NpcFunction.WarMissions, FunctionSelect = NpcFunction.HeroicSpiritSleepingPath, Unk2 = 4452 }); - } - }, - [NpcId.Anita1] = (DdonGameServer server, GameClient client, S2CNpcGetNpcExtendedFacilityRes result) => - { - var barrier = server.EpitaphRoadManager.GetBarrier(NpcId.Anita1); - if (!client.Character.EpitaphRoadState.UnlockedContent.Contains(barrier.EpitaphId)) - { - if (!CheckUnlockConditions(server, client, barrier)) - { - return; - } - result.ExtendedMenuItemList.Add(new CDataNpcExtendedFacilityMenuItem() { FunctionClass = NpcFunction.WarMissions, FunctionSelect = NpcFunction.GiveSpirits }); - } - }, - [NpcId.Isel1] = (DdonGameServer server, GameClient client, S2CNpcGetNpcExtendedFacilityRes result) => - { - var barrier = server.EpitaphRoadManager.GetBarrier(NpcId.Isel1); - if (!client.Character.EpitaphRoadState.UnlockedContent.Contains(barrier.EpitaphId)) - { - if (!CheckUnlockConditions(server, client, barrier)) - { - return; - } - result.ExtendedMenuItemList.Add(new CDataNpcExtendedFacilityMenuItem() { FunctionClass = NpcFunction.WarMissions, FunctionSelect = NpcFunction.GiveSpirits }); - } - }, - [NpcId.Damad1] = (DdonGameServer server, GameClient client, S2CNpcGetNpcExtendedFacilityRes result) => - { - var barrier = server.EpitaphRoadManager.GetBarrier(NpcId.Damad1); - if (!client.Character.EpitaphRoadState.UnlockedContent.Contains(barrier.EpitaphId)) - { - if (!CheckUnlockConditions(server, client, barrier)) - { - return; - } - result.ExtendedMenuItemList.Add(new CDataNpcExtendedFacilityMenuItem() { FunctionClass = NpcFunction.WarMissions, FunctionSelect = NpcFunction.GiveSpirits }); - } - } -#if false - // NPC which controls entrance to Memory of Megadosys - // Currently commented out since area is not completed and personal quest is missing - [NpcId.Morgan] = new List() - { - // Memory of Megadosys - new CDataNpcExtendedFacilityMenuItem() { FunctionClass = NpcFunction.WarMissions, FunctionSelect = NpcFunction.HeroicSpiritSleepingPath, Unk2 = 4452} - }, -#endif - }; - private readonly byte[] pcap_data = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xC2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x11, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x43, 0x20, 0xFB, 0xE8, 0xC0, 0xA0, 0xEC}; } } diff --git a/Arrowgene.Ddon.GameServer/Scripting/DdonLibrary.cs b/Arrowgene.Ddon.GameServer/Scripting/DdonLibrary.cs new file mode 100644 index 000000000..eb2e74743 --- /dev/null +++ b/Arrowgene.Ddon.GameServer/Scripting/DdonLibrary.cs @@ -0,0 +1,14 @@ +namespace Arrowgene.Ddon.GameServer.Scripting +{ + public class DdonLibrary + { + public DdonLibrary() + { + } + + public static uint GetValue() + { + return 1; + } + } +} diff --git a/Arrowgene.Ddon.GameServer/Scripting/INpcExtendedFacility.cs b/Arrowgene.Ddon.GameServer/Scripting/INpcExtendedFacility.cs new file mode 100644 index 000000000..54cfbb244 --- /dev/null +++ b/Arrowgene.Ddon.GameServer/Scripting/INpcExtendedFacility.cs @@ -0,0 +1,22 @@ +using Arrowgene.Ddon.GameServer; +using Arrowgene.Ddon.Shared.Entity.PacketStructure; +using Arrowgene.Ddon.Shared.Model; + +namespace Arrowgene.Ddon.GameServer.Scripting +{ + public abstract class INpcExtendedFacility + { + /// + /// NPC ID associated with the extended options. + /// + public NpcId NpcId { get; protected set; } + + /// + /// Gets extended menu options for the NPC. + /// + /// + /// + /// The result object for the extended NPC options + public abstract void GetExtendedOptions(DdonGameServer server, GameClient client, S2CNpcGetNpcExtendedFacilityRes result); + } +} diff --git a/Arrowgene.Ddon.GameServer/Scripting/ScriptManager.cs b/Arrowgene.Ddon.GameServer/Scripting/ScriptManager.cs new file mode 100644 index 000000000..c97e3a0af --- /dev/null +++ b/Arrowgene.Ddon.GameServer/Scripting/ScriptManager.cs @@ -0,0 +1,81 @@ +using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Shared; +using Arrowgene.Ddon.Shared.Model; +using Arrowgene.Logging; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; +using System.Collections.Generic; +using System.IO; + +namespace Arrowgene.Ddon.GameServer.Scripting +{ + public class ScriptManager + { + private static readonly ServerLogger Logger = LogProvider.Logger(typeof(ScriptManager)); + public class Globals + { + public Globals(DdonGameServer server) + { + Server = server; + } + + public DdonGameServer Server { get; } + }; + + + public ScriptManager(DdonGameServer server) + { + Server = server; + ScriptsRoot = $"{server.AssetRepository.AssetsPath}\\scripts"; + NpcExtendedFacilities = new Dictionary(); + } + + private DdonGameServer Server { get; } + private string ScriptsRoot { get; set; } + public Dictionary NpcExtendedFacilities { get; private set; } + + public void CompileScripts() + { + var ExtendedFacilitiesPath = $"{ScriptsRoot}\\extended_facilities"; + + var options = ScriptOptions.Default + .AddReferences(MetadataReference.CreateFromFile(typeof(DdonGameServer).Assembly.Location)) + .AddReferences(MetadataReference.CreateFromFile(typeof(AssetRepository).Assembly.Location)) + .AddImports("System", "System.Collections", "System.Collections.Generic") + .AddImports("Arrowgene.Ddon.Shared") + .AddImports("Arrowgene.Ddon.Shared.Model") + .AddImports("Arrowgene.Ddon.GameServer") + .AddImports("Arrowgene.Ddon.GameServer.Characters") + .AddImports("Arrowgene.Ddon.GameServer.Scripting") + .AddImports("Arrowgene.Ddon.Shared.Entity.PacketStructure") + .AddImports("Arrowgene.Ddon.Shared.Entity.Structure") + .AddImports("Arrowgene.Ddon.Shared.Model.Quest"); + + + var globals = new Globals(Server); + + Logger.Info($"Compiling NPC extended facility scripts from {ExtendedFacilitiesPath}"); + foreach (var file in Directory.EnumerateFiles(ExtendedFacilitiesPath)) + { + Logger.Info($"{file}"); + + var script = CSharpScript.Create( + code: File.ReadAllText(file), + options: options, + globalsType: typeof(Globals) + ); + + var result = script.RunAsync(globals).Result; + if (result == null) + { + Logger.Error($"Failed to execute '{file}' successfully. Skipping."); + continue; + } + + INpcExtendedFacility extendedFacility = (INpcExtendedFacility)result.ReturnValue; + NpcExtendedFacilities[extendedFacility.NpcId] = extendedFacility; + } + } + } +} diff --git a/Arrowgene.Ddon.Server/GameLogicSetting.cs b/Arrowgene.Ddon.Server/GameLogicSetting.cs index 3b67942ab..20f81ed2d 100644 --- a/Arrowgene.Ddon.Server/GameLogicSetting.cs +++ b/Arrowgene.Ddon.Server/GameLogicSetting.cs @@ -5,25 +5,21 @@ namespace Arrowgene.Ddon.Server { - [DataContract] public class GameLogicSetting { /// /// Additional factor to change how long crafting a recipe will take to finish. /// - [DataMember(Order = 0)] public double AdditionalProductionSpeedFactor { get; set; } /// /// Additional factor to change how much a recipe will cost. /// - [DataMember(Order = 1)] public double AdditionalCostPerformanceFactor { get; set; } /// /// Sets the maximim level that the exp ring will reward a bonus. /// - [DataMember(Order = 2)] public uint RookiesRingMaxLevel { get; set; } /// @@ -31,7 +27,6 @@ public class GameLogicSetting /// Must be a non-negtive value. If it is less than 0.0, a default of 1.0 /// will be selected. /// - [DataMember(Order = 3)] public double RookiesRingBonus { get; set; } /// @@ -39,328 +34,232 @@ public class GameLogicSetting /// True = Server entry only. Lower packet load, but also causes invisible people in lobbies. /// False = On-demand. May cause performance issues due to packet load. /// - [DataMember(Order = 4)] public bool NaiveLobbyContextHandling { get; set; } /// /// Determines the maximum amount of consumable items that can be crafted in one go with a pawn. /// The default is a value of 10 which is equivalent to the original game's behavior. /// - [DataMember(Order = 5)] public byte CraftConsumableProductionTimesMax { get; set; } + public byte CraftConsumableProductionTimesMax { get; set; } /// /// Configures if party exp is adjusted based on level differences of members. /// - [DataMember(Order = 6)] public bool AdjustPartyEnemyExp { get; set; } + public bool AdjustPartyEnemyExp { get; set; } /// /// List of the inclusive ranges of (MinLv, Maxlv, ExpMultiplier). ExpMultiplier is a value /// from (0.0 - 1.0) which is multipled into the base exp amount to determine the adjusted exp. /// The minlv and maxlv determine the relative level range that this multiplier should be applied to. /// - [DataMember(Order = 7)] public List<(uint MinLv, uint MaxLv, double ExpMultiplier)> AdjustPartyEnemyExpTiers { get; set; } + public List<(uint MinLv, uint MaxLv, double ExpMultiplier)> AdjustPartyEnemyExpTiers { get; set; } /// /// Configures if exp is adjusted based on level differences of members vs target level. /// - [DataMember(Order = 8)] public bool AdjustTargetLvEnemyExp { get; set; } + public bool AdjustTargetLvEnemyExp { get; set; } /// /// List of the inclusive ranges of (MinLv, Maxlv, ExpMultiplier). ExpMultiplier is a value from /// (0.0 - 1.0) which is multipled into the base exp amount to determine the adjusted exp. /// The minlv and maxlv determine the relative level range that this multiplier should be applied to. /// - [DataMember(Order = 9)] public List<(uint MinLv, uint MaxLv, double ExpMultiplier)> AdjustTargetLvEnemyExpTiers { get; set; } + public List<(uint MinLv, uint MaxLv, double ExpMultiplier)> AdjustTargetLvEnemyExpTiers { get; set; } /// /// The number of real world minutes that make up an in-game day. /// - [DataMember(Order = 10)] public uint GameClockTimescale { get; set; } + public uint GameClockTimescale { get; set; } /// /// Use a poisson process to randomly generate a weather cycle containing this many events, using the statistics in WeatherStatistics. /// - [DataMember(Order = 11)] public uint WeatherSequenceLength { get; set; } + public uint WeatherSequenceLength { get; set; } /// /// Statistics that drive semirandom weather generation. List is expected to be in (Fair, Cloudy, Rainy) order. /// meanLength: Average length of the weather, in seconds, when it gets rolled. /// weight: Relative weight of rolling that weather. Set to 0 to disable. /// - [DataMember(Order = 12)] public List<(uint MeanLength, uint Weight)> WeatherStatistics { get; set; } + public List<(uint MeanLength, uint Weight)> WeatherStatistics { get; set; } /// /// Configures if the Pawn Exp Catchup mechanic is enabled. This mechanic still rewards the player pawn EXP when the pawn is outside /// the allowed level range and a lower level than the owner. /// - [DataMember(Order = 13)] public bool EnablePawnCatchup { get; set; } + public bool EnablePawnCatchup { get; set; } /// /// If the flag EnablePawnCatchup=true, this is the multiplier value used when calculating exp to catch the pawns level back up to the player. /// - [DataMember(Order = 14)] public double PawnCatchupMultiplier { get; set; } + public double PawnCatchupMultiplier { get; set; } /// /// If the flag EnablePawnCatchup=true, this is the range of level that the pawn falls behind the player before the catchup mechanic kicks in. /// - [DataMember(Order = 15)] public uint PawnCatchupLvDiff { get; set; } + public uint PawnCatchupLvDiff { get; set; } /// /// Configures the default time in seconds a latern is active after igniting it. /// - [DataMember(Order = 16)] public uint LaternBurnTimeInSeconds { get; set; } + public uint LaternBurnTimeInSeconds { get; set; } /// /// Maximum amount of play points the client will display in the UI. /// Play points past this point will also trigger a chat log message saying you've reached the cap. /// - [DataMember(Order = 17)] public uint PlayPointMax { get; set; } + public uint PlayPointMax { get; set; } /// /// Maximum level for each job. /// Shared with the login server. /// - [DataMember(Order = 18)] public uint JobLevelMax { get; set; } + public uint JobLevelMax { get; set; } /// /// Maximum number of members in a single clan. /// Shared with the login server. /// - [DataMember(Order = 19)] public uint ClanMemberMax { get; set; } + public uint ClanMemberMax { get; set; } /// /// Maximum number of characters per account. /// Shared with the login server. /// - [DataMember(Order = 20)] public byte CharacterNumMax { get; set; } + public byte CharacterNumMax { get; set; } /// /// Toggles the visual equip set for all characters. /// Shared with the login server. /// - [DataMember(Order = 21)] public bool EnableVisualEquip { get; set; } + public bool EnableVisualEquip { get; set; } /// /// Maximum entries in the friends list. /// Shared with the login server. /// - [DataMember(Order = 22)] public uint FriendListMax { get; set; } + public uint FriendListMax { get; set; } /// /// Limits for each wallet type. /// - [DataMember(Order = 23)] public Dictionary WalletLimits { get; set; } + public Dictionary WalletLimits { get; set; } /// /// Number of bazaar entries that are given to new characters. /// - [DataMember(Order = 24)] public uint DefaultMaxBazaarExhibits { get; set; } + public uint DefaultMaxBazaarExhibits { get; set; } /// /// Number of favorite warps that are given to new characters. /// - [DataMember(Order = 25)] public uint DefaultWarpFavorites { get; set; } + public uint DefaultWarpFavorites { get; set; } /// /// Disables the exp correction if all party members are owned by the same character. /// - [DataMember(Order = 26)] public bool DisableExpCorrectionForMyPawn { get; set; } + public bool DisableExpCorrectionForMyPawn { get; set; } /// /// Global modifier for enemy exp calculations to scale up or down. /// - [DataMember(Order = 27)] public double EnemyExpModifier { get; set; } + public double EnemyExpModifier { get; set; } /// /// Global modifier for quest exp calculations to scale up or down. /// - - [DataMember(Order = 28)] public double QuestExpModifier { get; set; } + public double QuestExpModifier { get; set; } /// /// Global modifier for pp calculations to scale up or down. /// - [DataMember(Order = 29)] public double PpModifier { get; set; } + public double PpModifier { get; set; } /// /// Global modifier for Gold calculations to scale up or down. /// - [DataMember(Order = 30)] public double GoldModifier { get; set; } + public double GoldModifier { get; set; } /// /// Global modifier for Rift calculations to scale up or down. /// - [DataMember(Order = 31)] public double RiftModifier { get; set; } + public double RiftModifier { get; set; } /// /// Global modifier for BO calculations to scale up or down. /// - [DataMember(Order = 32)] public double BoModifier { get; set; } + public double BoModifier { get; set; } /// /// Global modifier for HO calculations to scale up or down. /// - [DataMember(Order = 33)] public double HoModifier { get; set; } + public double HoModifier { get; set; } /// /// Global modifier for JP calculations to scale up or down. /// - [DataMember(Order = 34)] public double JpModifier { get; set; } + public double JpModifier { get; set; } /// /// Configures the maximum amount of reward box slots. /// - [DataMember(Order = 35)] public byte RewardBoxMax { get; set; } + public byte RewardBoxMax { get; set; } /// /// Configures the maximum amount of quests that can be ordered at one time. /// - [DataMember(Order = 36)] public byte QuestOrderMax { get; set; } + public byte QuestOrderMax { get; set; } /// /// Configures if epitaph rewards are limited once per weekly reset. /// - [DataMember(Order = 37)] public bool EnableEpitaphWeeklyRewards { get; set; } + public bool EnableEpitaphWeeklyRewards { get; set; } /// /// Enables main pawns in party to gain EXP and JP from quests /// Original game apparantly did not have pawns share quest reward, so will set to false for default, /// change as needed /// - [DataMember(Order = 38)] public bool EnableMainPartyPawnsQuestRewards { get; set; } + public bool EnableMainPartyPawnsQuestRewards { get; set; } /// /// Specifies the time in seconds that a bazaar exhibit will last. /// By default, the equivalent of 3 days /// - [DataMember(Order = 37)] public ulong BazaarExhibitionTimeSeconds { get; set; } + public ulong BazaarExhibitionTimeSeconds { get; set; } /// /// Specifies the time in seconds that a slot in the bazaar won't be able to be used again. /// By default, the equivalent of 1 day /// - [DataMember(Order = 38)] public ulong BazaarCooldownTimeSeconds { get; set; } + public ulong BazaarCooldownTimeSeconds { get; set; } /// /// Various URLs used by the client. /// Shared with the login server. /// - [DataMember(Order = 200)] public string UrlManual { get; set; } - [DataMember(Order = 200)] public string UrlShopDetail { get; set; } - [DataMember(Order = 200)] public string UrlShopCounterA { get; set; } - [DataMember(Order = 200)] public string UrlShopAttention { get; set; } - [DataMember(Order = 200)] public string UrlShopStoneLimit { get; set; } - [DataMember(Order = 200)] public string UrlShopCounterB { get; set; } - [DataMember(Order = 200)] public string UrlChargeCallback { get; set; } - [DataMember(Order = 200)] public string UrlChargeA { get; set; } - [DataMember(Order = 200)] public string UrlSample9 { get; set; } - [DataMember(Order = 200)] public string UrlSample10 { get; set; } - [DataMember(Order = 200)] public string UrlCampaignBanner { get; set; } - [DataMember(Order = 200)] public string UrlSupportIndex { get; set; } - [DataMember(Order = 200)] public string UrlPhotoupAuthorize { get; set; } - [DataMember(Order = 200)] public string UrlApiA { get; set; } - [DataMember(Order = 200)] public string UrlApiB { get; set; } - [DataMember(Order = 200)] public string UrlIndex { get; set; } - [DataMember(Order = 200)] public string UrlCampaign { get; set; } - [DataMember(Order = 200)] public string UrlChargeB { get; set; } - [DataMember(Order = 200)] public string UrlCompanionImage { get; set; } + public string UrlManual { get; set; } + public string UrlShopDetail { get; set; } + public string UrlShopCounterA { get; set; } + public string UrlShopAttention { get; set; } + public string UrlShopStoneLimit { get; set; } + public string UrlShopCounterB { get; set; } + public string UrlChargeCallback { get; set; } + public string UrlChargeA { get; set; } + public string UrlSample9 { get; set; } + public string UrlSample10 { get; set; } + public string UrlCampaignBanner { get; set; } + public string UrlSupportIndex { get; set; } + public string UrlPhotoupAuthorize { get; set; } + public string UrlApiA { get; set; } + public string UrlApiB { get; set; } + public string UrlIndex { get; set; } + public string UrlCampaign { get; set; } + public string UrlChargeB { get; set; } + public string UrlCompanionImage { get; set; } public GameLogicSetting() { - SetDefaultValues(); - } - - void SetDefaultValues() - { - LaternBurnTimeInSeconds = 1500; - AdditionalProductionSpeedFactor = 1.0; - AdditionalCostPerformanceFactor = 1.0; - RookiesRingMaxLevel = 89; - RookiesRingBonus = 1.0; - NaiveLobbyContextHandling = true; - CraftConsumableProductionTimesMax = 10; - - AdjustPartyEnemyExp = true; - AdjustPartyEnemyExpTiers = new List<(uint MinLv, uint MaxLv, double ExpMultiplier)>() - { - (0, 2, 1.0), - (3, 4, 0.9), - (5, 6, 0.8), - (7, 8, 0.6), - (9, 10, 0.5), - }; - - AdjustTargetLvEnemyExp = false; - AdjustTargetLvEnemyExpTiers = new List<(uint MinLv, uint MaxLv, double ExpMultiplier)>() - { - (0, 2, 1.0), - (3, 4, 0.9), - (5, 6, 0.8), - (7, 8, 0.6), - (9, 10, 0.5), - }; - - EnablePawnCatchup = true; - PawnCatchupMultiplier = 1.5; - PawnCatchupLvDiff = 5; - - DisableExpCorrectionForMyPawn = true; - - GameClockTimescale = 90; - - WeatherSequenceLength = 20; - WeatherStatistics = new List<(uint MeanLength, uint Weight)> - { - (60 * 30, 1), //Fair - (60 * 30, 1), //Cloudy - (60 * 30, 1), //Rainy - }; - - PlayPointMax = 2000; - - JobLevelMax = 120; - ClanMemberMax = 100; - CharacterNumMax = 4; - EnableVisualEquip = true; - FriendListMax = 200; - - WalletLimits = DefaultWalletLimits; - - DefaultMaxBazaarExhibits = 5; - DefaultWarpFavorites = 3; - - EnableEpitaphWeeklyRewards = false; - EnableMainPartyPawnsQuestRewards = false; - - BazaarExhibitionTimeSeconds = (ulong) TimeSpan.FromDays(3).TotalSeconds; - BazaarCooldownTimeSeconds = (ulong) TimeSpan.FromDays(1).TotalSeconds; - - string urlDomain = $"http://localhost:{52099}"; - UrlManual = $"{urlDomain}/manual_nfb/"; - UrlShopDetail = $"{urlDomain}/shop/ingame/stone/detail"; - UrlShopCounterA = $"{urlDomain}/shop/ingame/counter?"; - UrlShopAttention = $"{urlDomain}/shop/ingame/attention?"; - UrlShopStoneLimit = $"{urlDomain}/shop/ingame/stone/limit"; - UrlShopCounterB = $"{urlDomain}/shop/ingame/counter?"; - UrlChargeCallback = $"{urlDomain}/opening/entry/ddo/cog_callback/charge"; - UrlChargeA = $"{urlDomain}/sp_ingame/charge/"; - UrlSample9 = "http://sample09.html"; - UrlSample10 = "http://sample10.html"; - UrlCampaignBanner = $"{urlDomain}/sp_ingame/campaign/bnr/bnr01.html?"; - UrlSupportIndex = $"{urlDomain}/sp_ingame/support/index.html"; - UrlPhotoupAuthorize = $"{urlDomain}/api/photoup/authorize"; - UrlApiA = $"{urlDomain}/link/api"; - UrlApiB = $"{urlDomain}/link/api"; - UrlIndex = $"{urlDomain}/sp_ingame/link/index.html"; - UrlCampaign = $"{urlDomain}/sp_ingame/campaign/bnr/slide.html"; - UrlChargeB = $"{urlDomain}/sp_ingame/charge/"; - UrlCompanionImage = $"{urlDomain}/"; - } - - [OnDeserializing] - void OnDeserializing(StreamingContext context) - { - SetDefaultValues(); } public GameLogicSetting(GameLogicSetting setting) @@ -431,9 +330,7 @@ public GameLogicSetting(GameLogicSetting setting) UrlCompanionImage = setting.UrlCompanionImage; } - // Note: method is called after the object is completely deserialized - constructors are skipped. - [OnDeserialized] - void OnDeserialized(StreamingContext context) + void ValidateSettings() { if (RookiesRingBonus < 0) { @@ -491,34 +388,6 @@ void OnDeserialized(StreamingContext context) { JpModifier = 1.0; } - - foreach (var walletMax in DefaultWalletLimits) - { - if (!WalletLimits.ContainsKey(walletMax.Key)) - { - WalletLimits.Add(walletMax.Key, walletMax.Value); - } - } } - - private static readonly Dictionary DefaultWalletLimits = new() - { - {WalletType.Gold, 999999999}, - {WalletType.RiftPoints, 999999999}, - {WalletType.BloodOrbs, 500000}, - {WalletType.SilverTickets, 999999999}, - {WalletType.GoldenGemstones, 99999}, - {WalletType.RentalPoints, 99999}, - {WalletType.ResetJobPoints, 99}, - {WalletType.ResetCraftSkills, 99}, - {WalletType.HighOrbs, 5000}, - {WalletType.DominionPoints, 999999999}, - {WalletType.AdventurePassPoints, 80}, - {WalletType.UnknownTickets, 999999999}, - {WalletType.BitterblackMazeResetTicket, 3}, - {WalletType.GoldenDragonMark, 30}, - {WalletType.SilverDragonMark, 150}, - {WalletType.RedDragonMark, 99999}, - }; } } diff --git a/Arrowgene.Ddon.Server/ScriptedServerSettings.cs b/Arrowgene.Ddon.Server/ScriptedServerSettings.cs new file mode 100644 index 000000000..2f4f62be0 --- /dev/null +++ b/Arrowgene.Ddon.Server/ScriptedServerSettings.cs @@ -0,0 +1,73 @@ +using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Shared.Model; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; +using System.Collections.Generic; +using System.IO; + +namespace Arrowgene.Ddon.Shared +{ + public class ScriptedServerSettings + { + public class Globals + { + public GameLogicSetting GameLogicSetting { get; set; } + } + + private string ScriptsRoot { get; set; } + private Dictionary CompiledScripts; + + public Script GameLogicSettings + { + get + { + return CompiledScripts["GameLogicSettings"]; + } + set + { + CompiledScripts["GameLogicSettings"] = value; + } + } + + public ScriptedServerSettings(string AssetsPath) + { + ScriptsRoot = $"{AssetsPath}\\scripts"; + + CompiledScripts = new Dictionary(); + + var ScriptsDirectory = new DirectoryInfo(ScriptsRoot); + if (!ScriptsDirectory.Exists) + { + return; + } + } + + public void LoadSettings(GameLogicSetting gameLogicSetting) + { + string SettingsPath = $"{ScriptsRoot}\\GameLogicSettings.csx"; + + var options = ScriptOptions.Default + .AddReferences(MetadataReference.CreateFromFile(typeof(GameLogicSetting).Assembly.Location)) + .AddReferences(MetadataReference.CreateFromFile(typeof(WalletType).Assembly.Location)) + .AddImports("System", "System.Collections", "System.Collections.Generic") + .AddImports("Arrowgene.Ddon.Shared.Model") + .AddImports("Arrowgene.Ddon.Shared.Model.Quest"); + + Globals globals = new Globals() + { + GameLogicSetting = gameLogicSetting + }; + + // Load The Game Settings + GameLogicSettings = CSharpScript.Create( + code: File.ReadAllText(SettingsPath), + options: options, + globalsType: typeof(Globals) + ); + + // Execute the script file to populate the settings + GameLogicSettings.RunAsync(globals); + } + } +} diff --git a/Arrowgene.Ddon.Shared/Arrowgene.Ddon.Shared.csproj b/Arrowgene.Ddon.Shared/Arrowgene.Ddon.Shared.csproj index 94100155d..fb6d683c5 100644 --- a/Arrowgene.Ddon.Shared/Arrowgene.Ddon.Shared.csproj +++ b/Arrowgene.Ddon.Shared/Arrowgene.Ddon.Shared.csproj @@ -22,6 +22,7 @@ + @@ -31,4 +32,38 @@ Files\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + diff --git a/Arrowgene.Ddon.Shared/AssetRepository.cs b/Arrowgene.Ddon.Shared/AssetRepository.cs index 2444d1a03..5812e3945 100644 --- a/Arrowgene.Ddon.Shared/AssetRepository.cs +++ b/Arrowgene.Ddon.Shared/AssetRepository.cs @@ -16,6 +16,8 @@ namespace Arrowgene.Ddon.Shared { public class AssetRepository { + public string AssetsPath { get; private set; } + // Client data public const string ClientErrorCodesKey = "ClientErrorCodes.csv"; public const string ItemListKey = "itemlist.csv"; @@ -70,6 +72,8 @@ public AssetRepository(string folder) return; } + AssetsPath = folder; + _fileSystemWatchers = new Dictionary(); ClientErrorCodes = new List(); diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CNpcGetNpcExtendedFacilityRes.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CNpcGetNpcExtendedFacilityRes.cs index 24c85883c..919758528 100644 --- a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CNpcGetNpcExtendedFacilityRes.cs +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CNpcGetNpcExtendedFacilityRes.cs @@ -3,7 +3,6 @@ using Arrowgene.Ddon.Shared.Model; using Arrowgene.Ddon.Shared.Network; using System.Collections.Generic; -using System.Diagnostics.Contracts; namespace Arrowgene.Ddon.Shared.Entity.PacketStructure { diff --git a/Arrowgene.Ddon.Shared/Files/Assets/scripts/GameLogicSettings.csx b/Arrowgene.Ddon.Shared/Files/Assets/scripts/GameLogicSettings.csx new file mode 100644 index 000000000..89b09177b --- /dev/null +++ b/Arrowgene.Ddon.Shared/Files/Assets/scripts/GameLogicSettings.csx @@ -0,0 +1,142 @@ +/** + * Settings file for Server customization. + */ + +// Generic Server Settings +GameLogicSetting.NaiveLobbyContextHandling = true; + +// Crafting Settings +GameLogicSetting.AdditionalProductionSpeedFactor = 1.0; +GameLogicSetting.AdditionalCostPerformanceFactor = 1.0; +GameLogicSetting.CraftConsumableProductionTimesMax = 10; + +// Exp Ring Settings +GameLogicSetting.RookiesRingMaxLevel = 89; +GameLogicSetting.RookiesRingBonus = 1.0; + +// EXP Penalty Settings +GameLogicSetting.AdjustPartyEnemyExp = true; +GameLogicSetting.AdjustPartyEnemyExpTiers = new List<(uint MinLv, uint MaxLv, double ExpMultiplier)>() +{ + // 1.0 = 100%, 0 = 0% + // If the range is larger than the last entry, + // a 0% exp rate is automatically applied if + // AdjustPartyEnemyExp = true + // + // MinLv, MaxLv, ExpMultiplier + ( 0, 2, 1.0), + ( 3, 4, 0.9), + ( 5, 6, 0.8), + ( 7, 8, 0.6), + ( 9, 10, 0.5), +}; + +GameLogicSetting.AdjustTargetLvEnemyExp = false; +GameLogicSetting.AdjustTargetLvEnemyExpTiers = new List<(uint MinLv, uint MaxLv, double ExpMultiplier)>() +{ + // MinLv, MaxLv, ExpMultiplier + ( 0, 2, 1.0), + ( 3, 4, 0.9), + ( 5, 6, 0.8), + ( 7, 8, 0.6), + ( 9, 10, 0.5), +}; + +// Pawn Catchup Settings +GameLogicSetting.EnablePawnCatchup = true; +GameLogicSetting.PawnCatchupMultiplier = 1.5; +GameLogicSetting.PawnCatchupLvDiff = 5; + +// Game Time Settings +GameLogicSetting.GameClockTimescale = 90; + +// Weather Settings +GameLogicSetting.WeatherSequenceLength = 20; +GameLogicSetting.WeatherStatistics = new List<(uint MeanLength, uint Weight)>() +{ + (60 * 30, 1), // Fair + (60 * 30, 1), // Cloudy + (60 * 30, 1), // Rainy +}; + +// Account Settings +GameLogicSetting.CharacterNumMax = 4; +GameLogicSetting.FriendListMax = 200; + +// Player Settings +GameLogicSetting.JobLevelMax = 120; +GameLogicSetting.EnableVisualEquip = true; +GameLogicSetting.DefaultWarpFavorites = 3; +GameLogicSetting.LaternBurnTimeInSeconds = 1500; + +// Pawn Settings +GameLogicSetting.EnableMainPartyPawnsQuestRewards = false; + +// Bazaar Settings +GameLogicSetting.DefaultMaxBazaarExhibits = 5; +GameLogicSetting.BazaarExhibitionTimeSeconds = (ulong) TimeSpan.FromDays(3).TotalSeconds; +GameLogicSetting.BazaarCooldownTimeSeconds = (ulong) TimeSpan.FromDays(1).TotalSeconds; + +// Clan Settings +GameLogicSetting.ClanMemberMax = 100; + +// Epitaph Settings +GameLogicSetting.EnableEpitaphWeeklyRewards = false; + +// Point Settings +GameLogicSetting.PlayPointMax = 2000; + +// Global Point Modifiers +GameLogicSetting.EnemyExpModifier = 1; +GameLogicSetting.QuestExpModifier = 1; +GameLogicSetting.PpModifier = 1; +GameLogicSetting.GoldModifier = 1; +GameLogicSetting.RiftModifier = 1; +GameLogicSetting.BoModifier = 1; +GameLogicSetting.HoModifier = 1; +GameLogicSetting.JpModifier = 1; +GameLogicSetting.RewardBoxMax = 100; +GameLogicSetting.QuestOrderMax = 20; + +// Wallet Settings +GameLogicSetting.WalletLimits = new Dictionary() +{ + {WalletType.Gold, 999999999}, + {WalletType.RiftPoints, 999999999}, + {WalletType.BloodOrbs, 500000}, + {WalletType.SilverTickets, 999999999}, + {WalletType.GoldenGemstones, 99999}, + {WalletType.RentalPoints, 99999}, + {WalletType.ResetJobPoints, 99}, + {WalletType.ResetCraftSkills, 99}, + {WalletType.HighOrbs, 5000}, + {WalletType.DominionPoints, 999999999}, + {WalletType.AdventurePassPoints, 80}, + {WalletType.UnknownTickets, 999999999}, + {WalletType.BitterblackMazeResetTicket, 3}, + {WalletType.GoldenDragonMark, 30}, + {WalletType.SilverDragonMark, 150}, + {WalletType.RedDragonMark, 99999}, +}; + +// URL Settings +string urlDomain = $"http://localhost:{52099}"; +GameLogicSetting.UrlManual = $"{urlDomain}/manual_nfb/"; +GameLogicSetting.UrlShopDetail = $"{urlDomain}/shop/ingame/stone/detail"; +GameLogicSetting.UrlShopCounterA = $"{urlDomain}/shop/ingame/counter?"; +GameLogicSetting.UrlShopAttention = $"{urlDomain}/shop/ingame/attention?"; +GameLogicSetting.UrlShopStoneLimit = $"{urlDomain}/shop/ingame/stone/limit"; +GameLogicSetting.UrlShopCounterB = $"{urlDomain}/shop/ingame/counter?"; +GameLogicSetting.UrlChargeCallback = $"{urlDomain}/opening/entry/ddo/cog_callback/charge"; +GameLogicSetting.UrlChargeA = $"{urlDomain}/sp_ingame/charge/"; +GameLogicSetting.UrlSample9 = "http://sample09.html"; +GameLogicSetting.UrlSample10 = "http://sample10.html"; +GameLogicSetting.UrlCampaignBanner = $"{urlDomain}/sp_ingame/campaign/bnr/bnr01.html?"; +GameLogicSetting.UrlSupportIndex = $"{urlDomain}/sp_ingame/support/index.html"; +GameLogicSetting.UrlPhotoupAuthorize = $"{urlDomain}/api/photoup/authorize"; +GameLogicSetting.UrlApiA = $"{urlDomain}/link/api"; +GameLogicSetting.UrlApiB = $"{urlDomain}/link/api"; +GameLogicSetting.UrlIndex = $"{urlDomain}/sp_ingame/link/index.html"; +GameLogicSetting.UrlCampaign = $"{urlDomain}/sp_ingame/campaign/bnr/slide.html"; +GameLogicSetting.UrlChargeB = $"{urlDomain}/sp_ingame/charge/"; +GameLogicSetting.UrlCompanionImage = $"{urlDomain}/"; diff --git a/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Anita1.csx b/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Anita1.csx new file mode 100644 index 000000000..a85a22c88 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Anita1.csx @@ -0,0 +1,22 @@ +public class NpcExtendedFacility : INpcExtendedFacility +{ + public NpcExtendedFacility() + { + NpcId = NpcId.Anita1; + } + + public override void GetExtendedOptions(DdonGameServer server, GameClient client, S2CNpcGetNpcExtendedFacilityRes result) + { + var barrier = server.EpitaphRoadManager.GetBarrier(NpcId); + if (!client.Character.EpitaphRoadState.UnlockedContent.Contains(barrier.EpitaphId)) + { + if (!server.EpitaphRoadManager.CheckUnlockConditions(client, barrier)) + { + return; + } + result.ExtendedMenuItemList.Add(new CDataNpcExtendedFacilityMenuItem() { FunctionClass = NpcFunction.WarMissions, FunctionSelect = NpcFunction.GiveSpirits }); + } + } +} + +return new NpcExtendedFacility(); diff --git a/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Damad1.csx b/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Damad1.csx new file mode 100644 index 000000000..65af77410 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Damad1.csx @@ -0,0 +1,22 @@ +public class NpcExtendedFacility : INpcExtendedFacility +{ + public NpcExtendedFacility() + { + NpcId = NpcId.Damad1; + } + + public override void GetExtendedOptions(DdonGameServer server, GameClient client, S2CNpcGetNpcExtendedFacilityRes result) + { + var barrier = server.EpitaphRoadManager.GetBarrier(NpcId); + if (!client.Character.EpitaphRoadState.UnlockedContent.Contains(barrier.EpitaphId)) + { + if (!server.EpitaphRoadManager.CheckUnlockConditions(client, barrier)) + { + return; + } + result.ExtendedMenuItemList.Add(new CDataNpcExtendedFacilityMenuItem() { FunctionClass = NpcFunction.WarMissions, FunctionSelect = NpcFunction.GiveSpirits }); + } + } +} + +return new NpcExtendedFacility(); diff --git a/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Isel1.csx b/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Isel1.csx new file mode 100644 index 000000000..11d1cb6d4 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Isel1.csx @@ -0,0 +1,22 @@ +public class NpcExtendedFacility : INpcExtendedFacility +{ + public NpcExtendedFacility() + { + NpcId = NpcId.Isel1; + } + + public override void GetExtendedOptions(DdonGameServer server, GameClient client, S2CNpcGetNpcExtendedFacilityRes result) + { + var barrier = server.EpitaphRoadManager.GetBarrier(NpcId); + if (!client.Character.EpitaphRoadState.UnlockedContent.Contains(barrier.EpitaphId)) + { + if (!server.EpitaphRoadManager.CheckUnlockConditions(client, barrier)) + { + return; + } + result.ExtendedMenuItemList.Add(new CDataNpcExtendedFacilityMenuItem() { FunctionClass = NpcFunction.WarMissions, FunctionSelect = NpcFunction.GiveSpirits }); + } + } +} + +return new NpcExtendedFacility(); diff --git a/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Pehr1.csx b/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Pehr1.csx new file mode 100644 index 000000000..1c311a495 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Files/Assets/scripts/extended_facilities/Pehr1.csx @@ -0,0 +1,17 @@ +public class NpcExtendedFacility : INpcExtendedFacility +{ + public NpcExtendedFacility() + { + NpcId = NpcId.Pehr1; + } + + public override void GetExtendedOptions(DdonGameServer server, GameClient client, S2CNpcGetNpcExtendedFacilityRes result) + { + if (client.Character.CompletedQuests.ContainsKey((QuestId) 60300020) || (client.QuestState.IsQuestActive(60300020) && client.QuestState.GetQuestState(60300020).Step > 2)) + { + result.ExtendedMenuItemList.Add(new CDataNpcExtendedFacilityMenuItem() { FunctionClass = NpcFunction.WarMissions, FunctionSelect = NpcFunction.HeroicSpiritSleepingPath, Unk2 = 4452 }); + } + } +} + +return new NpcExtendedFacility(); diff --git a/Arrowgene.Ddon.Test/GameServer/Characters/CraftManagerTest.cs b/Arrowgene.Ddon.Test/GameServer/Characters/CraftManagerTest.cs index d6d0db9af..b958e0fbf 100644 --- a/Arrowgene.Ddon.Test/GameServer/Characters/CraftManagerTest.cs +++ b/Arrowgene.Ddon.Test/GameServer/Characters/CraftManagerTest.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Arrowgene.Ddon.GameServer.Scripting; using Arrowgene.Ddon.Shared; using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model; @@ -14,7 +15,17 @@ public class CraftManagerTest public CraftManagerTest() { - _mockServer = new DdonGameServer(new GameServerSetting(), new MockDatabase(), new AssetRepository("TestFiles")); + var settings = new GameServerSetting(); + settings.GameLogicSetting.GameClockTimescale = 90; + settings.GameLogicSetting.WeatherSequenceLength = 20; + settings.GameLogicSetting.WeatherStatistics = new List<(uint MeanLength, uint Weight)>() + { + (60 * 30, 1), // Fair + (60 * 30, 1), // Cloudy + (60 * 30, 1), // Rainy + }; + + _mockServer = new DdonGameServer(settings, new MockDatabase(), new AssetRepository("TestFiles")); _craftManager = new CraftManager(_mockServer); } diff --git a/Arrowgene.Ddon.Test/GameServer/SettingTest.cs b/Arrowgene.Ddon.Test/GameServer/SettingTest.cs index 9f13ea76c..07c58a52c 100644 --- a/Arrowgene.Ddon.Test/GameServer/SettingTest.cs +++ b/Arrowgene.Ddon.Test/GameServer/SettingTest.cs @@ -13,9 +13,6 @@ public void Serialize_DefaultSetting_ObjectShouldMatchJson() string json = Setting.Serialize(setting); Assert.Contains("\"LogPath\": \"Logs\"", json); - Assert.Contains("\"GameLogicSetting\":", json); - Assert.Contains("\"AdditionalProductionSpeedFactor\": 1", json); - Assert.Contains("\"AdditionalCostPerformanceFactor\": 1", json); } [Fact] @@ -29,10 +26,6 @@ public void Deserialize_ValidJson_ShouldReturnCorrectObject() ""Id"": 10, ""Name"": ""CustomServerName"", ""ServerPort"": 42000 - }, - ""GameLogicSetting"": { - ""AdditionalProductionSpeedFactor"": 1.5, - ""AdditionalCostPerformanceFactor"": 1.2 } } }"; @@ -43,8 +36,6 @@ public void Deserialize_ValidJson_ShouldReturnCorrectObject() Assert.Equal("C:\\Assets", setting.AssetPath); Assert.Equal("CustomServerName", setting.GameServerSetting.ServerSetting.Name); Assert.Equal(42000, setting.GameServerSetting.ServerSetting.ServerPort); - Assert.Equal(1.5, setting.GameServerSetting.GameLogicSetting.AdditionalProductionSpeedFactor); - Assert.Equal(1.2, setting.GameServerSetting.GameLogicSetting.AdditionalCostPerformanceFactor); } [Fact] @@ -58,7 +49,6 @@ public void Deserialize_MissingFields_ShouldAssignDefaultValues() Assert.NotNull(setting.GameServerSetting); Assert.Equal("Game", setting.GameServerSetting.ServerSetting.Name); Assert.Equal(52000, setting.GameServerSetting.ServerSetting.ServerPort); - Assert.Equal(1.0, setting.GameServerSetting.GameLogicSetting.AdditionalProductionSpeedFactor); } [Fact] @@ -69,8 +59,6 @@ public void RoundTrip_SerializeAndDeserialize_ShouldMaintainObjectState() string json = Setting.Serialize(originalSetting); Setting deserializedSetting = Setting.Deserialize(json); - - Assert.Equal(2.0, deserializedSetting.GameServerSetting.GameLogicSetting.AdditionalProductionSpeedFactor); Assert.Equal(originalSetting.LogPath, deserializedSetting.LogPath); } } diff --git a/Arrowgene.DragonsDogmaOnline.sln b/Arrowgene.DragonsDogmaOnline.sln index d5d35f3bd..95bce9303 100644 --- a/Arrowgene.DragonsDogmaOnline.sln +++ b/Arrowgene.DragonsDogmaOnline.sln @@ -1,29 +1,29 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30204.135 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35013.160 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.LoginServer", "Arrowgene.Ddon.LoginServer\Arrowgene.Ddon.LoginServer.csproj", "{5C0F18BB-B735-4284-82A2-27506DFC9E56}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.LoginServer", "Arrowgene.Ddon.LoginServer\Arrowgene.Ddon.LoginServer.csproj", "{5C0F18BB-B735-4284-82A2-27506DFC9E56}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.Cli", "Arrowgene.Ddon.Cli\Arrowgene.Ddon.Cli.csproj", "{72B2D640-DEA0-4BA7-A3B5-899954BFBBF5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.Cli", "Arrowgene.Ddon.Cli\Arrowgene.Ddon.Cli.csproj", "{72B2D640-DEA0-4BA7-A3B5-899954BFBBF5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.WebServer", "Arrowgene.Ddon.WebServer\Arrowgene.Ddon.WebServer.csproj", "{B6813ECD-CF44-4899-838B-94D5F85B39A6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.WebServer", "Arrowgene.Ddon.WebServer\Arrowgene.Ddon.WebServer.csproj", "{B6813ECD-CF44-4899-838B-94D5F85B39A6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.Database", "Arrowgene.Ddon.Database\Arrowgene.Ddon.Database.csproj", "{E8FD9C06-57D7-4117-97AC-DE7779A73FB1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.Database", "Arrowgene.Ddon.Database\Arrowgene.Ddon.Database.csproj", "{E8FD9C06-57D7-4117-97AC-DE7779A73FB1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.Shared", "Arrowgene.Ddon.Shared\Arrowgene.Ddon.Shared.csproj", "{840B5617-2B22-461F-96F5-9062303F08E0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.Shared", "Arrowgene.Ddon.Shared\Arrowgene.Ddon.Shared.csproj", "{840B5617-2B22-461F-96F5-9062303F08E0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.Server", "Arrowgene.Ddon.Server\Arrowgene.Ddon.Server.csproj", "{7D39A3F8-4E2E-4E6F-9136-86EC13D64328}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.Server", "Arrowgene.Ddon.Server\Arrowgene.Ddon.Server.csproj", "{7D39A3F8-4E2E-4E6F-9136-86EC13D64328}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.GameServer", "Arrowgene.Ddon.GameServer\Arrowgene.Ddon.GameServer.csproj", "{87B8E9EB-CCCF-481F-8D01-74676ED62AAF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.GameServer", "Arrowgene.Ddon.GameServer\Arrowgene.Ddon.GameServer.csproj", "{87B8E9EB-CCCF-481F-8D01-74676ED62AAF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.Test", "Arrowgene.Ddon.Test\Arrowgene.Ddon.Test.csproj", "{86D48D8F-AA02-4BB8-9B5C-1313C8310F5F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.Test", "Arrowgene.Ddon.Test\Arrowgene.Ddon.Test.csproj", "{86D48D8F-AA02-4BB8-9B5C-1313C8310F5F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.Rpc", "Arrowgene.Ddon.Rpc\Arrowgene.Ddon.Rpc.csproj", "{2DE5385B-B8ED-491D-AC6C-895DC154C79E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.Rpc", "Arrowgene.Ddon.Rpc\Arrowgene.Ddon.Rpc.csproj", "{2DE5385B-B8ED-491D-AC6C-895DC154C79E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.Rpc.Web", "Arrowgene.Ddon.Rpc.Web\Arrowgene.Ddon.Rpc.Web.csproj", "{A98B686D-6C3D-473D-B69D-940ED343D7AF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.Rpc.Web", "Arrowgene.Ddon.Rpc.Web\Arrowgene.Ddon.Rpc.Web.csproj", "{A98B686D-6C3D-473D-B69D-940ED343D7AF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arrowgene.Ddon.Client", "Arrowgene.Ddon.Client\Arrowgene.Ddon.Client.csproj", "{DAC06C22-BD06-4592-A60A-3990B85EF7A8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arrowgene.Ddon.Client", "Arrowgene.Ddon.Client\Arrowgene.Ddon.Client.csproj", "{DAC06C22-BD06-4592-A60A-3990B85EF7A8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution