mirror of
https://github.com/sebastian-heinz/Arrowgene.DragonsDogmaOnline
synced 2026-08-03 11:12:41 -04:00
refactor: Refactor code and show different scripting example
- Refactored code so implementation can be shared between Server and DdonGameServer. - Moved settings into settings module and implemented an example using a less structured scripting module interface. - Created new ScriptableSettings class to assist with interacting with non-structured settings module. - Created README.md files for suggestions and guidelines for module implementation.
This commit is contained in:
parent
62d1d74e0e
commit
e6e5073737
24 changed files with 1107 additions and 716 deletions
|
|
@ -20,7 +20,7 @@ namespace Arrowgene.Ddon.Cli.Command
|
|||
private readonly Setting _setting;
|
||||
private DdonLoginServer _loginServer;
|
||||
private DdonGameServer _gameServer;
|
||||
private ScriptedServerSettings _scriptServerSettings;
|
||||
private ServerScriptManager _serverScriptManager;
|
||||
private DdonWebServer _webServer;
|
||||
private RpcWebServer _rpcWebServer;
|
||||
private IDatabase _database;
|
||||
|
|
@ -111,15 +111,15 @@ namespace Arrowgene.Ddon.Cli.Command
|
|||
_assetRepository.Initialize();
|
||||
}
|
||||
|
||||
if (_scriptServerSettings == null)
|
||||
if (_serverScriptManager == null)
|
||||
{
|
||||
_scriptServerSettings = new ScriptedServerSettings(_setting.AssetPath);
|
||||
_scriptServerSettings.LoadSettings();
|
||||
_serverScriptManager = new ServerScriptManager(_setting.AssetPath);
|
||||
_serverScriptManager.Initialize();
|
||||
}
|
||||
|
||||
if (_loginServer == null)
|
||||
{
|
||||
_loginServer = new DdonLoginServer(_setting.LoginServerSetting, _scriptServerSettings.GameLogicSetting, _database, _assetRepository);
|
||||
_loginServer = new DdonLoginServer(_setting.LoginServerSetting, _serverScriptManager.GameServerSettings.GameLogicSetting, _database, _assetRepository);
|
||||
}
|
||||
|
||||
if (_webServer == null)
|
||||
|
|
@ -129,7 +129,7 @@ namespace Arrowgene.Ddon.Cli.Command
|
|||
|
||||
if (_gameServer == null)
|
||||
{
|
||||
_gameServer = new DdonGameServer(_setting.GameServerSetting, _scriptServerSettings.GameLogicSetting, _database, _assetRepository);
|
||||
_gameServer = new DdonGameServer(_setting.GameServerSetting, _serverScriptManager.GameServerSettings.GameLogicSetting, _database, _assetRepository);
|
||||
}
|
||||
|
||||
if (_rpcWebServer == null)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using Arrowgene.Ddon.GameServer.Party;
|
||||
using Arrowgene.Ddon.Server;
|
||||
using Arrowgene.Ddon.Server.Network;
|
||||
using Arrowgene.Ddon.Server.Scripting.interfaces;
|
||||
using Arrowgene.Ddon.Shared.Entity.PacketStructure;
|
||||
using Arrowgene.Ddon.Shared.Entity.Structure;
|
||||
using Arrowgene.Ddon.Shared.Model;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ using Arrowgene.Ddon.GameServer.Shop;
|
|||
using Arrowgene.Ddon.Server;
|
||||
using Arrowgene.Ddon.Server.Handler;
|
||||
using Arrowgene.Ddon.Server.Network;
|
||||
using Arrowgene.Ddon.Server.Scripting.interfaces;
|
||||
using Arrowgene.Ddon.Shared;
|
||||
using Arrowgene.Ddon.Shared.Entity;
|
||||
using Arrowgene.Ddon.Shared.Entity.PacketStructure;
|
||||
|
|
@ -55,7 +56,7 @@ namespace Arrowgene.Ddon.GameServer
|
|||
{
|
||||
ServerSetting = new GameServerSetting(setting);
|
||||
GameLogicSettings = gameLogicSettings;
|
||||
ScriptManager = new ScriptManager(this);
|
||||
ScriptManager = new GameServerScriptManager(this);
|
||||
ClientLookup = new GameClientLookup();
|
||||
ChatLogHandler = new ChatLogHandler();
|
||||
ChatManager = new ChatManager(this);
|
||||
|
|
@ -95,7 +96,7 @@ namespace Arrowgene.Ddon.GameServer
|
|||
public event EventHandler<ClientConnectionChangeArgs> ClientConnectionChangeEvent;
|
||||
public GameServerSetting ServerSetting { get; }
|
||||
public GameLogicSetting GameLogicSettings { get; }
|
||||
public ScriptManager ScriptManager { get; }
|
||||
public GameServerScriptManager ScriptManager { get; }
|
||||
public ChatManager ChatManager { get; }
|
||||
public ItemManager ItemManager { get; }
|
||||
public CraftManager CraftManager { get; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
using Arrowgene.Ddon.Server;
|
||||
using Arrowgene.Ddon.Shared.Scripting;
|
||||
using Arrowgene.Logging;
|
||||
|
||||
namespace Arrowgene.Ddon.GameServer.Scripting
|
||||
{
|
||||
public class GlobalVariables
|
||||
{
|
||||
public GlobalVariables(DdonGameServer server)
|
||||
{
|
||||
Server = server;
|
||||
}
|
||||
|
||||
public DdonGameServer Server { get; }
|
||||
};
|
||||
|
||||
public class GameServerScriptManager : ScriptManager<GlobalVariables>
|
||||
{
|
||||
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(GameServerScriptManager));
|
||||
|
||||
private DdonGameServer Server { get; }
|
||||
private GlobalVariables Globals { get; }
|
||||
public NpcExtendedFacilityModule NpcExtendedFacilityModule { get; private set; } = new NpcExtendedFacilityModule();
|
||||
|
||||
public GameServerScriptManager(DdonGameServer server) : base(server.AssetRepository.AssetsPath)
|
||||
{
|
||||
Server = server;
|
||||
Globals = new GlobalVariables(Server);
|
||||
|
||||
// Add modules to the list so the generic logic can iterate over all scripting modules
|
||||
ScriptModules[NpcExtendedFacilityModule.ModuleRoot] = NpcExtendedFacilityModule;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize(Globals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ namespace Arrowgene.Ddon.GameServer.Scripting
|
|||
public override string ModuleRoot => "extended_facilities";
|
||||
public override string Filter => "*.csx";
|
||||
public override bool ScanSubdirectories => true;
|
||||
public override bool EnableHotLoad => true;
|
||||
|
||||
public Dictionary<NpcId, INpcExtendedFacility> NpcExtendedFacilities { get; private set; }
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ namespace Arrowgene.Ddon.GameServer.Scripting
|
|||
.AddImports("Arrowgene.Ddon.Shared.Model.Quest");
|
||||
}
|
||||
|
||||
public override bool EvaluateResult(ScriptState<object> result)
|
||||
public override bool EvaluateResult(string path, ScriptState<object> result)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ using Arrowgene.Ddon.LoginServer.Handler;
|
|||
using Arrowgene.Ddon.Server;
|
||||
using Arrowgene.Ddon.Server.Handler;
|
||||
using Arrowgene.Ddon.Server.Network;
|
||||
using Arrowgene.Ddon.Server.Scripting.interfaces;
|
||||
using Arrowgene.Ddon.Shared;
|
||||
using Arrowgene.Ddon.Shared.Network;
|
||||
using Arrowgene.Logging;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using Arrowgene.Ddon.Server;
|
||||
using Arrowgene.Ddon.Server.Network;
|
||||
using Arrowgene.Ddon.Server.Scripting.interfaces;
|
||||
using Arrowgene.Ddon.Shared.Entity.PacketStructure;
|
||||
using Arrowgene.Ddon.Shared.Entity.Structure;
|
||||
using Arrowgene.Ddon.Shared.Network;
|
||||
|
|
|
|||
|
|
@ -25,5 +25,8 @@
|
|||
<ProjectReference Include="..\Arrowgene.Ddon.Database\Arrowgene.Ddon.Database.csproj" />
|
||||
<ProjectReference Include="..\Arrowgene.Ddon.Shared\Arrowgene.Ddon.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Settings\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,325 +0,0 @@
|
|||
using Arrowgene.Ddon.Shared.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Arrowgene.Ddon.Server
|
||||
{
|
||||
public class GameLogicSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// Additional factor to change how long crafting a recipe will take to finish.
|
||||
/// </summary>
|
||||
public double AdditionalProductionSpeedFactor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional factor to change how much a recipe will cost.
|
||||
/// </summary>
|
||||
public double AdditionalCostPerformanceFactor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximim level that the exp ring will reward a bonus.
|
||||
/// </summary>
|
||||
public uint RookiesRingMaxLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The multiplier applied to the bonus amount of exp rewarded.
|
||||
/// Must be a non-negtive value. If it is less than 0.0, a default of 1.0
|
||||
/// will be selected.
|
||||
/// </summary>
|
||||
public double RookiesRingBonus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether to pass lobby context packets on demand or only on entry to the server.
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool NaiveLobbyContextHandling { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public byte CraftConsumableProductionTimesMax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configures if party exp is adjusted based on level differences of members.
|
||||
/// </summary>
|
||||
public bool EnableAdjustPartyEnemyExp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public List<(uint MinLv, uint MaxLv, double ExpMultiplier)> AdjustPartyEnemyExpTiers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configures if exp is adjusted based on level differences of members vs target level.
|
||||
/// </summary>
|
||||
public bool EnableAdjustTargetLvEnemyExp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public List<(uint MinLv, uint MaxLv, double ExpMultiplier)> AdjustTargetLvEnemyExpTiers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of real world minutes that make up an in-game day.
|
||||
/// </summary>
|
||||
public uint GameClockTimescale { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use a poisson process to randomly generate a weather cycle containing this many events, using the statistics in WeatherStatistics.
|
||||
/// </summary>
|
||||
public uint WeatherSequenceLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public List<(uint MeanLength, uint Weight)> WeatherStatistics { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool EnablePawnCatchup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the flag EnablePawnCatchup=true, this is the multiplier value used when calculating exp to catch the pawns level back up to the player.
|
||||
/// </summary>
|
||||
public double PawnCatchupMultiplier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the flag EnablePawnCatchup=true, this is the range of level that the pawn falls behind the player before the catchup mechanic kicks in.
|
||||
/// </summary>
|
||||
public uint PawnCatchupLvDiff { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configures the default time in seconds a latern is active after igniting it.
|
||||
/// </summary>
|
||||
public uint LaternBurnTimeInSeconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public uint PlayPointMax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum level for each job.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public uint JobLevelMax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of members in a single clan.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public uint ClanMemberMax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of characters per account.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public byte CharacterNumMax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the visual equip set for all characters.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public bool EnableVisualEquip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum entries in the friends list.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public uint FriendListMax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Limits for each wallet type.
|
||||
/// </summary>
|
||||
public Dictionary<WalletType, uint> WalletLimits { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of bazaar entries that are given to new characters.
|
||||
/// </summary>
|
||||
public uint DefaultMaxBazaarExhibits { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of favorite warps that are given to new characters.
|
||||
/// </summary>
|
||||
public uint DefaultWarpFavorites { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Disables the exp correction if all party members are owned by the same character.
|
||||
/// </summary>
|
||||
public bool DisableExpCorrectionForMyPawn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for enemy exp calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double EnemyExpModifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for quest exp calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double QuestExpModifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for pp calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double PpModifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for Gold calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double GoldModifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for Rift calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double RiftModifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for BO calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double BoModifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for HO calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double HoModifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for JP calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double JpModifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configures the maximum amount of reward box slots.
|
||||
/// </summary>
|
||||
public byte RewardBoxMax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configures the maximum amount of quests that can be ordered at one time.
|
||||
/// </summary>
|
||||
public byte QuestOrderMax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configures if epitaph rewards are limited once per weekly reset.
|
||||
/// </summary>
|
||||
public bool EnableEpitaphWeeklyRewards { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public bool EnableMainPartyPawnsQuestRewards { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the time in seconds that a bazaar exhibit will last.
|
||||
/// By default, the equivalent of 3 days
|
||||
/// </summary>
|
||||
public ulong BazaarExhibitionTimeSeconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public ulong BazaarCooldownTimeSeconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Various URLs used by the client.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
void ValidateSettings()
|
||||
{
|
||||
if (RookiesRingBonus < 0)
|
||||
{
|
||||
RookiesRingBonus = 1.0;
|
||||
}
|
||||
if (AdditionalProductionSpeedFactor < 0)
|
||||
{
|
||||
CraftConsumableProductionTimesMax = 1;
|
||||
}
|
||||
if (AdditionalCostPerformanceFactor < 0)
|
||||
{
|
||||
CraftConsumableProductionTimesMax = 1;
|
||||
}
|
||||
if (CraftConsumableProductionTimesMax < 1)
|
||||
{
|
||||
CraftConsumableProductionTimesMax = 10;
|
||||
}
|
||||
if (GameClockTimescale <= 0)
|
||||
{
|
||||
GameClockTimescale = 90;
|
||||
}
|
||||
if (PawnCatchupMultiplier < 0)
|
||||
{
|
||||
PawnCatchupMultiplier = 1.0;
|
||||
}
|
||||
if (EnemyExpModifier < 0)
|
||||
{
|
||||
EnemyExpModifier = 1.0;
|
||||
}
|
||||
if (QuestExpModifier < 0)
|
||||
{
|
||||
QuestExpModifier = 1.0;
|
||||
}
|
||||
if (PpModifier < 0)
|
||||
{
|
||||
PpModifier = 1.0;
|
||||
}
|
||||
if (GoldModifier < 0)
|
||||
{
|
||||
GoldModifier = 1.0;
|
||||
}
|
||||
if (RiftModifier < 0)
|
||||
{
|
||||
RiftModifier = 1.0;
|
||||
}
|
||||
if (BoModifier < 0)
|
||||
{
|
||||
BoModifier = 1.0;
|
||||
}
|
||||
if (HoModifier < 0)
|
||||
{
|
||||
HoModifier = 1.0;
|
||||
}
|
||||
if (JpModifier < 0)
|
||||
{
|
||||
JpModifier = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
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;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Arrowgene.Ddon.Server
|
||||
{
|
||||
public class ScriptedServerSettings
|
||||
{
|
||||
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(ScriptedServerSettings));
|
||||
|
||||
public class Globals
|
||||
{
|
||||
public GameLogicSetting GameLogicSetting { get; set; }
|
||||
}
|
||||
|
||||
private string ScriptsRoot { get; set; }
|
||||
private Dictionary<string, Script> CompiledScripts;
|
||||
public GameLogicSetting GameLogicSetting { get; private set; }
|
||||
public FileSystemWatcher Watcher { get; private set; }
|
||||
|
||||
public Script GameLogicSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (CompiledScripts)
|
||||
{
|
||||
return CompiledScripts["GameLogicSettings"];
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (CompiledScripts)
|
||||
{
|
||||
CompiledScripts["GameLogicSettings"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ScriptedServerSettings(string AssetsPath)
|
||||
{
|
||||
ScriptsRoot = $"{AssetsPath}\\scripts";
|
||||
|
||||
GameLogicSetting = new GameLogicSetting();
|
||||
|
||||
CompiledScripts = new Dictionary<string, Script>();
|
||||
|
||||
var ScriptsDirectory = new DirectoryInfo(ScriptsRoot);
|
||||
if (!ScriptsDirectory.Exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Watcher = SetupFileWatcher();
|
||||
}
|
||||
|
||||
public void LoadSettings()
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
Logger.Info($"Loading Scriptable game settings from {ScriptsRoot}");
|
||||
Logger.Info($"{settingsPath}");
|
||||
|
||||
var code = Util.ReadAllText(settingsPath);
|
||||
|
||||
// Load The Game Settings
|
||||
GameLogicSettings = CSharpScript.Create(
|
||||
code: code,
|
||||
options: options,
|
||||
globalsType: typeof(Globals)
|
||||
);
|
||||
|
||||
if (GameLogicSettings != null)
|
||||
{
|
||||
// Execute the script file to populate the settings
|
||||
GameLogicSettings.RunAsync(globals);
|
||||
}
|
||||
}
|
||||
|
||||
private FileSystemWatcher SetupFileWatcher()
|
||||
{
|
||||
var watcher = new FileSystemWatcher(ScriptsRoot);
|
||||
watcher.Filter = "GameLogicSettings.csx";
|
||||
|
||||
watcher.NotifyFilter = (NotifyFilters.LastWrite);
|
||||
|
||||
watcher.Changed += OnChanged;
|
||||
watcher.Error += OnError;
|
||||
watcher.EnableRaisingEvents = true;
|
||||
|
||||
return watcher;
|
||||
}
|
||||
|
||||
private void OnChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
if (e.ChangeType != WatcherChangeTypes.Changed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Info($"Reloading {e.FullPath}");
|
||||
|
||||
try
|
||||
{
|
||||
Watcher.EnableRaisingEvents = false;
|
||||
|
||||
LoadSettings();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnError(object sender, ErrorEventArgs e) =>
|
||||
PrintException(e.GetException());
|
||||
|
||||
private void PrintException(Exception ex)
|
||||
{
|
||||
if (ex != null)
|
||||
{
|
||||
Logger.Error($"{ex.Message}");
|
||||
Logger.Error($"Stacktrace:");
|
||||
PrintException(ex.InnerException);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +1,40 @@
|
|||
using Arrowgene.Ddon.GameServer.Scripting;
|
||||
using Arrowgene.Ddon.Server;
|
||||
using Arrowgene.Ddon.Shared;
|
||||
using Arrowgene.Logging;
|
||||
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using static Arrowgene.Ddon.Server.ServerScriptManager;
|
||||
|
||||
namespace Arrowgene.Ddon.GameServer.Scripting
|
||||
namespace Arrowgene.Ddon.Shared.Scripting
|
||||
{
|
||||
public class ScriptManager
|
||||
public abstract class ScriptManager<T>
|
||||
{
|
||||
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(ScriptManager));
|
||||
public class GlobalVariables
|
||||
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(ScriptManager<T>));
|
||||
|
||||
protected Dictionary<string, ScriptModule> ScriptModules { get; private set; }
|
||||
public string ScriptsRoot { get; private set; }
|
||||
public T GlobalVariables { get; protected set; }
|
||||
|
||||
public ScriptManager(string assetsPath)
|
||||
{
|
||||
public GlobalVariables(DdonGameServer server)
|
||||
{
|
||||
Server = server;
|
||||
}
|
||||
|
||||
public DdonGameServer Server { get; }
|
||||
};
|
||||
|
||||
public NpcExtendedFacilityModule NpcExtendedFacilityModule { get; private set; } = new NpcExtendedFacilityModule();
|
||||
|
||||
private Dictionary<string, ScriptModule> ScriptModules;
|
||||
|
||||
public ScriptManager(DdonGameServer server)
|
||||
{
|
||||
Server = server;
|
||||
ScriptsRoot = $"{server.AssetRepository.AssetsPath}\\scripts";
|
||||
|
||||
ScriptModules = new Dictionary<string, ScriptModule>()
|
||||
{
|
||||
{NpcExtendedFacilityModule.ModuleRoot, NpcExtendedFacilityModule}
|
||||
};
|
||||
|
||||
Globals = new GlobalVariables(Server);
|
||||
ScriptModules = new Dictionary<string, ScriptModule>();
|
||||
ScriptsRoot = $"{assetsPath}\\scripts";
|
||||
}
|
||||
|
||||
private DdonGameServer Server { get; }
|
||||
private string ScriptsRoot { get; }
|
||||
private GlobalVariables Globals { get; }
|
||||
public abstract void Initialize();
|
||||
|
||||
public void Initialize()
|
||||
protected void Initialize(T globalVariables)
|
||||
{
|
||||
GlobalVariables = globalVariables;
|
||||
|
||||
CompileScripts();
|
||||
SetupFileWatchers();
|
||||
}
|
||||
|
||||
private void CompileScript(ScriptModule module, string path)
|
||||
protected void CompileScript(ScriptModule module, string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -58,11 +43,11 @@ namespace Arrowgene.Ddon.GameServer.Scripting
|
|||
var script = CSharpScript.Create(
|
||||
code: Util.ReadAllText(path),
|
||||
options: module.Options(),
|
||||
globalsType: typeof(GlobalVariables)
|
||||
globalsType: typeof(T)
|
||||
);
|
||||
|
||||
var result = script.RunAsync(Globals).Result;
|
||||
if (!module.EvaluateResult(result))
|
||||
var result = script.RunAsync(GlobalVariables).Result;
|
||||
if (!module.EvaluateResult(path, result))
|
||||
{
|
||||
Logger.Error($"Failed to evaluate the result of executing '{path}'");
|
||||
}
|
||||
|
|
@ -74,7 +59,7 @@ namespace Arrowgene.Ddon.GameServer.Scripting
|
|||
}
|
||||
}
|
||||
|
||||
private void CompileScripts()
|
||||
protected void CompileScripts()
|
||||
{
|
||||
foreach (var module in ScriptModules.Values)
|
||||
{
|
||||
|
|
@ -83,6 +68,11 @@ namespace Arrowgene.Ddon.GameServer.Scripting
|
|||
Logger.Info($"Compiling scripts for module '{module.ModuleRoot}'");
|
||||
foreach (var file in Directory.EnumerateFiles(path))
|
||||
{
|
||||
if (Path.GetExtension(file) != ".csx")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
module.Scripts.Add(file);
|
||||
CompileScript(module, file);
|
||||
}
|
||||
|
|
@ -93,6 +83,11 @@ namespace Arrowgene.Ddon.GameServer.Scripting
|
|||
{
|
||||
foreach (var module in ScriptModules.Values)
|
||||
{
|
||||
if (!module.EnableHotLoad)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var watcher = new FileSystemWatcher($"{ScriptsRoot}\\{module.ModuleRoot}");
|
||||
watcher.Filter = module.Filter;
|
||||
watcher.NotifyFilter = (NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.CreationTime);
|
||||
|
|
@ -108,11 +103,13 @@ namespace Arrowgene.Ddon.GameServer.Scripting
|
|||
// Enable all the watchers
|
||||
foreach (var module in ScriptModules.Values)
|
||||
{
|
||||
module.Watcher.EnableRaisingEvents = true;
|
||||
if (module.EnableHotLoad)
|
||||
{
|
||||
module.Watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChanged(object sender, FileSystemEventArgs e)
|
||||
protected void OnChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
if (e.ChangeType != WatcherChangeTypes.Changed)
|
||||
{
|
||||
|
|
@ -11,6 +11,13 @@ namespace Arrowgene.Ddon.GameServer.Scripting
|
|||
public abstract bool ScanSubdirectories { get; }
|
||||
public FileSystemWatcher Watcher { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this module is able to be hot-loadable or not.
|
||||
/// If a module is not hot-loadable, it will only be evaluated once
|
||||
/// when the server first loads.
|
||||
/// </summary>
|
||||
public abstract bool EnableHotLoad { get; }
|
||||
|
||||
public HashSet<string> Scripts { get; set; }
|
||||
|
||||
public ScriptModule()
|
||||
|
|
@ -27,8 +34,9 @@ namespace Arrowgene.Ddon.GameServer.Scripting
|
|||
/// <summary>
|
||||
/// Evaluates the result returned by the script.
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <param name="path">Path to the script that was executed</param>
|
||||
/// <param name="result">The result object of the script that executed</param>
|
||||
/// <returns></returns>
|
||||
public abstract bool EvaluateResult(ScriptState<object> result);
|
||||
public abstract bool EvaluateResult(string path, ScriptState<object> result);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Arrowgene.Ddon.GameServer.Scripting
|
||||
{
|
||||
649
Arrowgene.Ddon.Server/Scripting/interfaces/GameLogicSetting.cs
Normal file
649
Arrowgene.Ddon.Server/Scripting/interfaces/GameLogicSetting.cs
Normal file
|
|
@ -0,0 +1,649 @@
|
|||
using Arrowgene.Ddon.Server.Scripting.utils;
|
||||
using Arrowgene.Ddon.Shared.Model;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Arrowgene.Ddon.Server.Scripting.interfaces
|
||||
{
|
||||
public class GameLogicSetting
|
||||
{
|
||||
private ScriptableSettings SettingsData { get; set; }
|
||||
public GameLogicSetting(ScriptableSettings settingsData)
|
||||
{
|
||||
SettingsData = settingsData;
|
||||
}
|
||||
|
||||
private T GetSetting<T>(string key)
|
||||
{
|
||||
return SettingsData.Get<T>("GameLogicSettings", key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additional factor to change how long crafting a recipe will take to finish.
|
||||
/// </summary>
|
||||
public double AdditionalProductionSpeedFactor
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("AdditionalProductionSpeedFactor");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additional factor to change how much a recipe will cost.
|
||||
/// </summary>
|
||||
public double AdditionalCostPerformanceFactor
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("AdditionalCostPerformanceFactor");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximim level that the exp ring will reward a bonus.
|
||||
/// </summary>
|
||||
public uint RookiesRingMaxLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("RookiesRingMaxLevel");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The multiplier applied to the bonus amount of exp rewarded.
|
||||
/// Must be a non-negtive value. If it is less than 0.0, a default of 1.0
|
||||
/// will be selected.
|
||||
/// </summary>
|
||||
public double RookiesRingBonus
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("RookiesRingBonus");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether to pass lobby context packets on demand or only on entry to the server.
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool NaiveLobbyContextHandling
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<bool>("NaiveLobbyContextHandling");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public byte CraftConsumableProductionTimesMax
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<byte>("CraftConsumableProductionTimesMax");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures if party exp is adjusted based on level differences of members.
|
||||
/// </summary>
|
||||
public bool EnableAdjustPartyEnemyExp
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<bool>("EnableAdjustPartyEnemyExp");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public List<(uint MinLv, uint MaxLv, double ExpMultiplier)> AdjustPartyEnemyExpTiers
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<List<(uint MinLv, uint MaxLv, double ExpMultiplier)>>("AdjustPartyEnemyExpTiers");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures if exp is adjusted based on level differences of members vs target level.
|
||||
/// </summary>
|
||||
public bool EnableAdjustTargetLvEnemyExp
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<bool>("EnableAdjustTargetLvEnemyExp");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public List<(uint MinLv, uint MaxLv, double ExpMultiplier)> AdjustTargetLvEnemyExpTiers
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<List<(uint MinLv, uint MaxLv, double ExpMultiplier)>>("AdjustTargetLvEnemyExpTiers");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of real world minutes that make up an in-game day.
|
||||
/// </summary>
|
||||
public uint GameClockTimescale
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("GameClockTimescale");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use a poisson process to randomly generate a weather cycle containing this many events, using the statistics in WeatherStatistics.
|
||||
/// </summary>
|
||||
public uint WeatherSequenceLength
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("WeatherSequenceLength");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public List<(uint MeanLength, uint Weight)> WeatherStatistics
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<List<(uint MeanLength, uint Weight)>>("WeatherStatistics");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool EnablePawnCatchup
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<bool>("EnablePawnCatchup");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the flag EnablePawnCatchup=true, this is the multiplier value used when calculating exp to catch the pawns level back up to the player.
|
||||
/// </summary>
|
||||
public double PawnCatchupMultiplier
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("PawnCatchupMultiplier");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the flag EnablePawnCatchup=true, this is the range of level that the pawn falls behind the player before the catchup mechanic kicks in.
|
||||
/// </summary>
|
||||
public uint PawnCatchupLvDiff
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("PawnCatchupLvDiff");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the default time in seconds a latern is active after igniting it.
|
||||
/// </summary>
|
||||
public uint LaternBurnTimeInSeconds
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("LaternBurnTimeInSeconds");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public uint PlayPointMax
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("PlayPointMax");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum level for each job.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public uint JobLevelMax
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("JobLevelMax");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of members in a single clan.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public uint ClanMemberMax
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("ClanMemberMax");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of characters per account.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public byte CharacterNumMax
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<byte>("CharacterNumMax");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the visual equip set for all characters.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public bool EnableVisualEquip
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<bool>("EnableVisualEquip");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum entries in the friends list.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public uint FriendListMax
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("FriendListMax");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Limits for each wallet type.
|
||||
/// </summary>
|
||||
public Dictionary<WalletType, uint> WalletLimits
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<Dictionary<WalletType, uint>>("WalletLimits");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of bazaar entries that are given to new characters.
|
||||
/// </summary>
|
||||
public uint DefaultMaxBazaarExhibits
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("DefaultMaxBazaarExhibits");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of favorite warps that are given to new characters.
|
||||
/// </summary>
|
||||
public uint DefaultWarpFavorites
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<uint>("DefaultWarpFavorites");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the exp correction if all party members are owned by the same character.
|
||||
/// </summary>
|
||||
public bool DisableExpCorrectionForMyPawn
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<bool>("DisableExpCorrectionForMyPawn");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for enemy exp calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double EnemyExpModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("EnemyExpModifier");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for quest exp calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double QuestExpModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("QuestExpModifier");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for pp calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double PpModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("PpModifier");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for Gold calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double GoldModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("GoldModifier");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for Rift calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double RiftModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("RiftModifier");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for BO calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double BoModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("BoModifier");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for HO calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double HoModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("HoModifier");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global modifier for JP calculations to scale up or down.
|
||||
/// </summary>
|
||||
public double JpModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<double>("JpModifier");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the maximum amount of reward box slots.
|
||||
/// </summary>
|
||||
public byte RewardBoxMax
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<byte>("RewardBoxMax");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the maximum amount of quests that can be ordered at one time.
|
||||
/// </summary>
|
||||
public byte QuestOrderMax
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<byte>("QuestOrderMax");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures if epitaph rewards are limited once per weekly reset.
|
||||
/// </summary>
|
||||
public bool EnableEpitaphWeeklyRewards
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<bool>("EnableEpitaphWeeklyRewards");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public bool EnableMainPartyPawnsQuestRewards
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<bool>("EnableMainPartyPawnsQuestRewards");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the time in seconds that a bazaar exhibit will last.
|
||||
/// By default, the equivalent of 3 days
|
||||
/// </summary>
|
||||
public ulong BazaarExhibitionTimeSeconds
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<ulong>("BazaarExhibitionTimeSeconds");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public ulong BazaarCooldownTimeSeconds
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<ulong>("BazaarCooldownTimeSeconds");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Various URLs used by the client.
|
||||
/// Shared with the login server.
|
||||
/// </summary>
|
||||
public string UrlManual
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlManual");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlShopDetail
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlShopDetail");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlShopCounterA
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlShopCounterA");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlShopAttention
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlShopAttention");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlShopStoneLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlShopStoneLimit");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlShopCounterB
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlShopCounterB");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlChargeCallback
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlChargeCallback");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlChargeA
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlChargeA");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlSample9
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlSample9");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlSample10
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlSample10");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlCampaignBanner
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlCampaignBanner");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlSupportIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlSupportIndex");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlPhotoupAuthorize
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlPhotoupAuthorize");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlApiA
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlApiA");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlApiB
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlApiB");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlIndex");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlCampaign
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlCampaign");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlChargeB
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlChargeB");
|
||||
}
|
||||
}
|
||||
|
||||
public string UrlCompanionImage
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSetting<string>("UrlCompanionImage");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
using Arrowgene.Ddon.GameServer.Scripting;
|
||||
using Arrowgene.Ddon.Server.Scripting.interfaces;
|
||||
using Arrowgene.Ddon.Server.Scripting.utils;
|
||||
using Arrowgene.Ddon.Shared.Model;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using System.IO;
|
||||
|
||||
namespace Arrowgene.Ddon.Server.Scripting.modules
|
||||
{
|
||||
public class GameServerSettingsModule : ScriptModule
|
||||
{
|
||||
public override string ModuleRoot => "settings";
|
||||
public override string Filter => "*.csx";
|
||||
public override bool ScanSubdirectories => true;
|
||||
public override bool EnableHotLoad => true;
|
||||
|
||||
/// <summary>
|
||||
/// Settings data is organized as ScriptName.FieldName
|
||||
/// </summary>
|
||||
private ScriptableSettings SettingsData { get; set; }
|
||||
public GameLogicSetting GameLogicSetting { get; private set; }
|
||||
|
||||
public GameServerSettingsModule()
|
||||
{
|
||||
SettingsData = new ScriptableSettings();
|
||||
GameLogicSetting = new GameLogicSetting(SettingsData);
|
||||
}
|
||||
|
||||
public override ScriptOptions Options()
|
||||
{
|
||||
return 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");
|
||||
}
|
||||
|
||||
public override bool EvaluateResult(string path, ScriptState<object> result)
|
||||
{
|
||||
var scriptName = Path.GetFileNameWithoutExtension(path);
|
||||
|
||||
foreach (var variable in result.Variables)
|
||||
{
|
||||
SettingsData.Set(scriptName, variable.Name, variable.Value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Arrowgene.Ddon.Server/Scripting/utils/ScriptableSettings.cs
Normal file
52
Arrowgene.Ddon.Server/Scripting/utils/ScriptableSettings.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Arrowgene.Ddon.Server.Scripting.utils
|
||||
{
|
||||
public class ScriptableSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings data is organized as ScriptName.FieldName
|
||||
/// </summary>
|
||||
private Dictionary<string, Dictionary<string, object>> Data { get; set; }
|
||||
|
||||
public ScriptableSettings()
|
||||
{
|
||||
Data = new Dictionary<string, Dictionary<string, object>>();
|
||||
}
|
||||
|
||||
public Dictionary<string, object> GetScriptData(string scriptName)
|
||||
{
|
||||
if (!Data.ContainsKey(scriptName))
|
||||
{
|
||||
throw new Exception($"The script '{scriptName}' doesn't exist");
|
||||
}
|
||||
|
||||
return Data[scriptName];
|
||||
}
|
||||
|
||||
public T Get<T>(string scriptName, string variableName)
|
||||
{
|
||||
if (!Data.ContainsKey(scriptName))
|
||||
{
|
||||
throw new Exception($"The script '{scriptName}' doesn't exist");
|
||||
}
|
||||
|
||||
if (!Data[scriptName].ContainsKey(variableName))
|
||||
{
|
||||
throw new Exception($"The setting '{scriptName}.{variableName}' doesn't exist");
|
||||
}
|
||||
return (T)Data[scriptName][variableName];
|
||||
}
|
||||
|
||||
public void Set<T>(string scriptName, string variableName, T value)
|
||||
{
|
||||
if (!Data.ContainsKey(scriptName))
|
||||
{
|
||||
Data[scriptName] = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
Data[scriptName][variableName] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Arrowgene.Ddon.Server/ServerScriptManager.cs
Normal file
36
Arrowgene.Ddon.Server/ServerScriptManager.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using Arrowgene.Ddon.Server.Scripting.interfaces;
|
||||
using Arrowgene.Ddon.Server.Scripting.modules;
|
||||
using Arrowgene.Ddon.Shared.Scripting;
|
||||
using Arrowgene.Logging;
|
||||
|
||||
namespace Arrowgene.Ddon.Server
|
||||
{
|
||||
public class Globals
|
||||
{
|
||||
// Currently no script globals are required
|
||||
// but leave this class as a way to introduce
|
||||
// globals if needed.
|
||||
}
|
||||
|
||||
public class ServerScriptManager : ScriptManager<Globals>
|
||||
{
|
||||
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(ServerScriptManager));
|
||||
|
||||
private Globals Globals { get; set; }
|
||||
|
||||
public GameServerSettingsModule GameServerSettings { get; private set; } = new GameServerSettingsModule();
|
||||
|
||||
public ServerScriptManager(string assetsPath) : base(assetsPath)
|
||||
{
|
||||
Globals = new Globals();
|
||||
|
||||
// Add modules to the list so the generic logic can iterate over all scripting modules
|
||||
ScriptModules[GameServerSettings.ModuleRoot] = GameServerSettings;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize(Globals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,25 +38,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Files\Assets\scripts\events\" />
|
||||
<Folder Include="Files\Assets\scripts\quests\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Files\Assets\scripts\extended_facilities\Anita1.csx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Files\Assets\scripts\extended_facilities\Damad1.csx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Files\Assets\scripts\extended_facilities\Isel1.csx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Files\Assets\scripts\extended_facilities\Pehr1.csx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Files\Assets\scripts\GameLogicSettings.csx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Content Include="Files\Assets\scripts\**">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,163 +0,0 @@
|
|||
/**
|
||||
* Settings file for Server customization.
|
||||
* This file supports hotloading.
|
||||
*/
|
||||
|
||||
// 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
|
||||
|
||||
/**
|
||||
* @brief Handles EXP penalties for the party based on the
|
||||
* difference between the lowest leveled member and highest
|
||||
* leveled member of the party. If the range is larger than
|
||||
* the last entry in AdjustPartyEnemyExpTiers, a 0% exp rate
|
||||
* is automatically applied.
|
||||
*
|
||||
* Can be turned on/off by configuring AdjustPartyEnemyExp.
|
||||
*/
|
||||
GameLogicSetting.EnableAdjustPartyEnemyExp = true;
|
||||
GameLogicSetting.AdjustPartyEnemyExpTiers = new List<(uint MinLv, uint MaxLv, double ExpMultiplier)>()
|
||||
{
|
||||
// MinLv and MaxLv define the relative level difference between the levels of the lowest and
|
||||
// highest members in the party.
|
||||
// The ExpMultiplier value can be a value between [0.0, 1.0] (1.0 = 100%, 0.0 = 0%)
|
||||
//
|
||||
// MinLv, MaxLv, ExpMultiplier
|
||||
( 0, 2, 1.0),
|
||||
( 3, 4, 0.9),
|
||||
( 5, 6, 0.8),
|
||||
( 7, 8, 0.6),
|
||||
( 9, 10, 0.5),
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Handles EXP penalties based on the highest leveled member
|
||||
* in the party and the level of the target enemy. If the range is
|
||||
* larger than the last entry in AdjustTargetLvEnemyExpTiers, a 0%
|
||||
* exp rate is automatically applied.
|
||||
*
|
||||
* Can be turned on/off by configuring AdjustTargetLvEnemyExp.
|
||||
*/
|
||||
GameLogicSetting.EnableAdjustTargetLvEnemyExp = false;
|
||||
GameLogicSetting.AdjustTargetLvEnemyExpTiers = new List<(uint MinLv, uint MaxLv, double ExpMultiplier)>()
|
||||
{
|
||||
// MinLv and MaxLv define the relative level difference between the target and highest member in the party.
|
||||
// The ExpMultiplier value can be a value between [0.0, 1.0] (1.0 = 100%, 0.0 = 0%)
|
||||
//
|
||||
// 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, uint>()
|
||||
{
|
||||
{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}/";
|
||||
12
Arrowgene.Ddon.Shared/Files/Assets/scripts/README.md
Normal file
12
Arrowgene.Ddon.Shared/Files/Assets/scripts/README.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# DDON Scripting
|
||||
|
||||
The DDON scripting is intended to expose certain server internal details of the game server that a server admin wish to be configure.
|
||||
While initially implemented as JSON file, as more complex features come into the picture, a more complex configuration archirecture
|
||||
is required.
|
||||
|
||||
The scripting root is `scripts` directory inside the assets directory. Internally the functions which perform reverse lookups for modules will
|
||||
terminate once reaching the root.
|
||||
|
||||
Each directory inside scripts defines the scripting module. A module name should be all lowercase. Inside each module, there should be a `README.md`
|
||||
file which describes the purpose and usage of the module. It should also describe any guidelines required. When implementing a module, be aware if
|
||||
you want the module to be hotloadable. If you do, make sure to program in such a way that the settings can reflect as such after an update.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# NPC Extended Facilities
|
||||
|
||||
It is possible to inject new NPC options by defining what the game calls `NpcExtendedFacilities`. The type of menu options what can be added are defined in the class [NpcFunction.cs](https://github.com/sebastian-heinz/Arrowgene.DragonsDogmaOnline/blob/develop/Arrowgene.Ddon.Shared/Model/NpcFunction.cs).
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Name the file after the named constant in [NpcId.cs](https://github.com/sebastian-heinz/Arrowgene.DragonsDogmaOnline/blob/develop/Arrowgene.Ddon.Shared/Model/NpcId.cs)
|
||||
- The abstract class `INpcExtendedFacility` is used as the interface module.
|
||||
- When extending `INpcExtendedFacility` keep it simple and name it `NpcExtendedFacility`. The script engine will mangle the object name this avoiding any sort of class name conflicts.
|
||||
- At the bottom of the file return a new `NpcExtendedFacility`.
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
/**
|
||||
* Settings file for Server customization.
|
||||
* This file supports hotloading.
|
||||
*/
|
||||
|
||||
// Generic Server Settings
|
||||
bool NaiveLobbyContextHandling = true;
|
||||
uint GameClockTimescale = 90;
|
||||
|
||||
// Game Settings
|
||||
byte RewardBoxMax = 100;
|
||||
byte QuestOrderMax = 20;
|
||||
byte CharacterNumMax = 4;
|
||||
uint FriendListMax = 200;
|
||||
uint JobLevelMax = 120;
|
||||
bool EnableVisualEquip = true;
|
||||
uint DefaultWarpFavorites = 3;
|
||||
uint LaternBurnTimeInSeconds = 1500;
|
||||
|
||||
// Crafting Settings
|
||||
double AdditionalProductionSpeedFactor = 1.0;
|
||||
double AdditionalCostPerformanceFactor = 1.0;
|
||||
double CraftConsumableProductionTimesMax = 10;
|
||||
|
||||
// Exp Ring Settings
|
||||
bool EnableRookiesRing = false;
|
||||
uint RookiesRingMaxLevel = 89;
|
||||
double RookiesRingBonus = 1.0;
|
||||
|
||||
// EXP Penalty Settings
|
||||
|
||||
/**
|
||||
* @brief Handles EXP penalties for the party based on the
|
||||
* difference between the lowest leveled member and highest
|
||||
* leveled member of the party. If the range is larger than
|
||||
* the last entry in AdjustPartyEnemyExpTiers, a 0% exp rate
|
||||
* is automatically applied.
|
||||
*
|
||||
* Can be turned on/off by configuring AdjustPartyEnemyExp.
|
||||
*/
|
||||
bool EnableAdjustPartyEnemyExp = true;
|
||||
var AdjustPartyEnemyExpTiers = new List<(uint MinLv, uint MaxLv, double ExpMultiplier)>()
|
||||
{
|
||||
// MinLv and MaxLv define the relative level difference between the levels of the lowest and
|
||||
// highest members in the party.
|
||||
// The ExpMultiplier value can be a value between [0.0, 1.0] (1.0 = 100%, 0.0 = 0%)
|
||||
//
|
||||
// MinLv, MaxLv, ExpMultiplier
|
||||
( 0, 2, 1.0),
|
||||
( 3, 4, 0.9),
|
||||
( 5, 6, 0.8),
|
||||
( 7, 8, 0.6),
|
||||
( 9, 10, 0.5),
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Handles EXP penalties based on the highest leveled member
|
||||
* in the party and the level of the target enemy. If the range is
|
||||
* larger than the last entry in AdjustTargetLvEnemyExpTiers, a 0%
|
||||
* exp rate is automatically applied.
|
||||
*
|
||||
* Can be turned on/off by configuring AdjustTargetLvEnemyExp.
|
||||
*/
|
||||
bool EnableAdjustTargetLvEnemyExp = false;
|
||||
var AdjustTargetLvEnemyExpTiers = new List<(uint MinLv, uint MaxLv, double ExpMultiplier)>()
|
||||
{
|
||||
// MinLv and MaxLv define the relative level difference between the target and highest member in the party.
|
||||
// The ExpMultiplier value can be a value between [0.0, 1.0] (1.0 = 100%, 0.0 = 0%)
|
||||
//
|
||||
// MinLv, MaxLv, ExpMultiplier
|
||||
( 0, 2, 1.0),
|
||||
( 3, 4, 0.9),
|
||||
( 5, 6, 0.8),
|
||||
( 7, 8, 0.6),
|
||||
( 9, 10, 0.5),
|
||||
};
|
||||
|
||||
// Weather Settings
|
||||
uint WeatherSequenceLength = 20;
|
||||
var WeatherStatistics = new List<(uint MeanLength, uint Weight)>()
|
||||
{
|
||||
(60 * 30, 1), // Fair
|
||||
(60 * 30, 1), // Cloudy
|
||||
(60 * 30, 1), // Rainy
|
||||
};
|
||||
|
||||
// Pawn Settings
|
||||
bool EnableMainPartyPawnsQuestRewards = false;
|
||||
bool DisableExpCorrectionForMyPawn = true;
|
||||
bool EnablePawnCatchup = true;
|
||||
double PawnCatchupMultiplier = 1.5;
|
||||
uint PawnCatchupLvDiff = 5;
|
||||
|
||||
// Bazaar Settings
|
||||
uint DefaultMaxBazaarExhibits = 5;
|
||||
ulong BazaarExhibitionTimeSeconds = (ulong) TimeSpan.FromDays(3).TotalSeconds;
|
||||
ulong BazaarCooldownTimeSeconds = (ulong) TimeSpan.FromDays(1).TotalSeconds;
|
||||
|
||||
// Clan Settings
|
||||
uint ClanMemberMax = 100;
|
||||
|
||||
// Epitaph Settings
|
||||
bool EnableEpitaphWeeklyRewards = false;
|
||||
|
||||
// Point Settings
|
||||
uint PlayPointMax = 2000;
|
||||
|
||||
// Global Point Modifiers
|
||||
double EnemyExpModifier = 1;
|
||||
double QuestExpModifier = 1;
|
||||
double PpModifier = 1;
|
||||
double GoldModifier = 1;
|
||||
double RiftModifier = 1;
|
||||
double BoModifier = 1;
|
||||
double HoModifier = 1;
|
||||
double JpModifier = 1;
|
||||
|
||||
// Wallet Settings
|
||||
var WalletLimits = new Dictionary<WalletType, uint>()
|
||||
{
|
||||
{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}";
|
||||
string UrlManual = $"{urlDomain}/manual_nfb/";
|
||||
string UrlShopDetail = $"{urlDomain}/shop/ingame/stone/detail";
|
||||
string UrlShopCounterA = $"{urlDomain}/shop/ingame/counter?";
|
||||
string UrlShopAttention = $"{urlDomain}/shop/ingame/attention?";
|
||||
string UrlShopStoneLimit = $"{urlDomain}/shop/ingame/stone/limit";
|
||||
string UrlShopCounterB = $"{urlDomain}/shop/ingame/counter?";
|
||||
string UrlChargeCallback = $"{urlDomain}/opening/entry/ddo/cog_callback/charge";
|
||||
string UrlChargeA = $"{urlDomain}/sp_ingame/charge/";
|
||||
string UrlSample9 = "http://sample09.html";
|
||||
string UrlSample10 = "http://sample10.html";
|
||||
string UrlCampaignBanner = $"{urlDomain}/sp_ingame/campaign/bnr/bnr01.html?";
|
||||
string UrlSupportIndex = $"{urlDomain}/sp_ingame/support/index.html";
|
||||
string UrlPhotoupAuthorize = $"{urlDomain}/api/photoup/authorize";
|
||||
string UrlApiA = $"{urlDomain}/link/api";
|
||||
string UrlApiB = $"{urlDomain}/link/api";
|
||||
string UrlIndex = $"{urlDomain}/sp_ingame/link/index.html";
|
||||
string UrlCampaign = $"{urlDomain}/sp_ingame/campaign/bnr/slide.html";
|
||||
string UrlChargeB = $"{urlDomain}/sp_ingame/charge/";
|
||||
string UrlCompanionImage = $"{urlDomain}/";
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Scriptable Settings
|
||||
|
||||
This file attemps to establish some guidelines when defining new settings which are parsed by the scripting engine.
|
||||
|
||||
## General Considerations
|
||||
|
||||
While implementing new GameLogicSettings, consider the possibility that the file can be hotlaoded after the server starts and code according to that assumption.
|
||||
If the settings interacts with a feature which uses some caching mechanism, make sure to invalid all those caches when the file is reloaded.
|
||||
|
||||
## Naming Guidelines
|
||||
|
||||
#### Enable Variables
|
||||
|
||||
All settings which can enable a feature when set to `true`, should start with the prefix `Enable`.
|
||||
|
||||
### Disable Variables
|
||||
|
||||
All settings which can disable a feature when set to `true`, should start with the prefix `Disable`.
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using Arrowgene.Ddon.GameServer.Scripting;
|
||||
using Arrowgene.Ddon.Server;
|
||||
using Arrowgene.Ddon.Server.Scripting.interfaces;
|
||||
using Arrowgene.Ddon.Server.Scripting.utils;
|
||||
using Arrowgene.Ddon.Shared;
|
||||
using Arrowgene.Ddon.Shared.Entity.Structure;
|
||||
using Arrowgene.Ddon.Shared.Model;
|
||||
|
|
@ -13,22 +15,23 @@ public class CraftManagerTest
|
|||
{
|
||||
private readonly DdonGameServer _mockServer;
|
||||
private readonly CraftManager _craftManager;
|
||||
private readonly ScriptableSettings _scriptableSettings;
|
||||
|
||||
public CraftManagerTest()
|
||||
{
|
||||
|
||||
var settings = new GameServerSetting();
|
||||
|
||||
var gameLogicSetting = new GameLogicSetting();
|
||||
gameLogicSetting.GameClockTimescale = 90;
|
||||
gameLogicSetting.WeatherSequenceLength = 20;
|
||||
gameLogicSetting.WeatherStatistics = new List<(uint MeanLength, uint Weight)>()
|
||||
_scriptableSettings = new ScriptableSettings();
|
||||
_scriptableSettings.Set("GameLogicSettings", "GameClockTimescale", 90);
|
||||
_scriptableSettings.Set("GameLogicSettings", "WeatherSequenceLength", 20);
|
||||
_scriptableSettings.Set("GameLogicSettings", "WeatherStatistics", new List<(uint MeanLength, uint Weight)>()
|
||||
{
|
||||
(60 * 30, 1), // Fair
|
||||
(60 * 30, 1), // Cloudy
|
||||
(60 * 30, 1), // Rainy
|
||||
};
|
||||
});
|
||||
|
||||
var gameLogicSetting = new GameLogicSetting(_scriptableSettings);
|
||||
_mockServer = new DdonGameServer(settings, gameLogicSetting, new MockDatabase(), new AssetRepository("TestFiles"));
|
||||
_craftManager = new CraftManager(_mockServer);
|
||||
}
|
||||
|
|
@ -37,7 +40,7 @@ public class CraftManagerTest
|
|||
public void GetCraftingTimeReductionRate_ShouldReturnCorrectValue()
|
||||
{
|
||||
List<uint> productionSpeedLevels = new List<uint> { 10, 20, 30 };
|
||||
_mockServer.GameLogicSettings.AdditionalProductionSpeedFactor = 1.0;
|
||||
_scriptableSettings.Set("GameLogicSettings", "AdditionalProductionSpeedFactor", 1.0);
|
||||
|
||||
double result = _craftManager.GetCraftingTimeReductionRate(productionSpeedLevels);
|
||||
|
||||
|
|
@ -49,7 +52,7 @@ public class CraftManagerTest
|
|||
{
|
||||
List<uint> productionSpeedLevels = new List<uint> { 70, 70, 70, 70 };
|
||||
const uint recipeTime = 100;
|
||||
_mockServer.GameLogicSettings.AdditionalProductionSpeedFactor = 1.0;
|
||||
_scriptableSettings.Set("GameLogicSettings", "AdditionalProductionSpeedFactor", 1.0);
|
||||
|
||||
uint result = _craftManager.CalculateRecipeProductionSpeed(recipeTime, productionSpeedLevels);
|
||||
|
||||
|
|
@ -61,7 +64,7 @@ public class CraftManagerTest
|
|||
{
|
||||
List<uint> productionSpeedLevels = new List<uint> { 70, 70, 70, 70 };
|
||||
const uint recipeTime = 100;
|
||||
_mockServer.GameLogicSettings.AdditionalProductionSpeedFactor = 100;
|
||||
_scriptableSettings.Set("GameLogicSettings", "AdditionalProductionSpeedFactor", 100.0);
|
||||
|
||||
uint result = _craftManager.CalculateRecipeProductionSpeed(recipeTime, productionSpeedLevels);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue