Skip to content

Commit

Permalink
Script auto-reload
Browse files Browse the repository at this point in the history
  • Loading branch information
Limiana committed Dec 23, 2024
1 parent aa8e116 commit a0fffe6
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 10 deletions.
2 changes: 1 addition & 1 deletion NightmareUI
11 changes: 11 additions & 0 deletions Splatoon/Gui/CGuiDebug.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Environment;
using Lumina.Excel.Sheets;
using Splatoon.Memory;
using Splatoon.SplatoonScripting;
using Splatoon.Utility;
using System.Globalization;

Expand All @@ -26,6 +27,16 @@ unsafe partial class CGui

void DisplayDebug()
{
if(ImGui.CollapsingHeader("Scripts w/prio list"))
{
foreach(var s in ScriptingProcessor.Scripts)
{
if(s.InternalData.ContainsPriorityLists())
{
ImGuiEx.Text($"{s.InternalData.FullName} : enabled={!s.IsDisabledByUser}");
}
}
}
if(ImGui.Button("Copy config"))
{
try
Expand Down
37 changes: 37 additions & 0 deletions Splatoon/Gui/CGuiGeneralSettings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using ECommons.LanguageHelpers;
using NightmareUI.PrimaryUI;
using Splatoon.Modules;
using Splatoon.Serializables;
using Splatoon.Utility;
Expand Down Expand Up @@ -127,5 +128,41 @@ void DisplayGeneralSettings()
Utils.ProcessStart(Splatoon.DiscordURL);
}
ImGui.Checkbox("Disable stream notice (effective only after restart)".Loc(), ref P.Config.NoStreamWarning);

new NuiBuilder().Section("Script auto-reloading (for developers)", collapsible: true)
.TextWrapped("Add pathes to folders that contain scripts that you are editing. Do NOT add Splatoon's own configuration folder here.")
.Widget(() =>
{
for(int i = 0; i < P.Config.FileWatcherPathes.Count; i++)
{
var index = i;
var f = P.Config.FileWatcherPathes[i];
ImGuiEx.InputWithRightButtonsArea(() =>
{
if(ImGui.InputTextWithHint("##path to folder", "Path to folder...", ref f, 2000))
{
P.Config.FileWatcherPathes[index] = f;
}
}, () =>
{
if(ImGuiEx.IconButton(FontAwesomeIcon.Trash))
{
new TickScheduler(() => P.Config.FileWatcherPathes.RemoveAt(index));
}
});
}
ImGuiEx.LineCentered(() =>
{
if(ImGuiEx.IconButtonWithText(FontAwesomeIcon.Plus, "Add New"))
{
P.Config.FileWatcherPathes.Add("");
}
ImGui.SameLine();
if(ImGuiEx.IconButtonWithText(FontAwesomeIcon.Check, "Apply settings"))
{
S.ScriptFileWatcher.StartWatching();
}
});
}).Draw();
}
}
6 changes: 4 additions & 2 deletions Splatoon/Gui/Priority/PriorityPopupWindow.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Dalamud.Interface.Colors;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Windowing;
using ECommons.ChatMethods;
using ECommons.ExcelServices;
Expand Down Expand Up @@ -152,6 +153,7 @@ public void Autofill()
});
}


int GetOrderedRoleIndex(Job job)
{
if(Svc.Data.GetExcelSheet<ClassJob>().TryGetRow((uint)job, out var data))
Expand All @@ -166,7 +168,7 @@ int GetOrderedRoleIndex(Job job)

public bool ShouldAutoOpen()
{
return ScriptingProcessor.Scripts.Any(x => !x.IsDisabledByUser && x.ValidTerritories?.Contains(this.TerritoryType) == true && !P.Config.NoPrioPopupTerritories.Contains(this.TerritoryType));
return ScriptingProcessor.Scripts.Any(x => !x.IsDisabledByUser && x.InternalData.ContainsPriorityLists() && x.ValidTerritories?.Contains(this.TerritoryType) == true && !P.Config.NoPrioPopupTerritories.Contains(this.TerritoryType)) && !Svc.Condition[ConditionFlag.DutyRecorderPlayback];
}

public void Open(bool force)
Expand Down
1 change: 1 addition & 0 deletions Splatoon/Serializables/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ internal class Configuration : IEzConfig
public List<uint> NoPrioPopupTerritories = [];
public List<RolePlayerAssignment> RolePlayerAssignments = [];
public bool PrioUnifyDps = false;
public List<string> FileWatcherPathes = [];

public uint ClampFillColorAlpha(uint fillColor)
{
Expand Down
2 changes: 2 additions & 0 deletions Splatoon/Services/!ServiceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
namespace Splatoon.Services;
public static class S
{
public static ThreadPool ThreadPool { get; private set; }
internal static RenderManager RenderManager { get; private set; }
internal static VbmCamera VbmCamera { get; private set; }
internal static ScriptFileWatcher ScriptFileWatcher { get; private set; }
//internal static StatusEffectManager StatusEffectManager { get; private set; }
}
112 changes: 112 additions & 0 deletions Splatoon/Services/ScriptFileWatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using ECommons.ChatMethods;
using Splatoon.SplatoonScripting;
using System.Threading;

namespace Splatoon.Services;
public class ScriptFileWatcher : IDisposable
{
List<FileSystemWatcher> Watchers = [];
private ScriptFileWatcher()
{
StartWatching();
}

public void Dispose()
{
StopWatching();
}

void StopWatching()
{
foreach(var w in Watchers)
{
w.EnableRaisingEvents = false;
w.Dispose();
}
Watchers.Clear();
}

internal void StartWatching()
{
StopWatching();
foreach(var s in P.Config.FileWatcherPathes)
{
var x = s.Replace("\"", "");
if(x == "") continue;
try
{
if(!Directory.Exists(x)) throw new DirectoryNotFoundException();
var watcher = new FileSystemWatcher(x, "*.cs")
{
IncludeSubdirectories = true,
NotifyFilter = NotifyFilters.Attributes |
NotifyFilters.CreationTime |
NotifyFilters.FileName |
NotifyFilters.LastAccess |
NotifyFilters.LastWrite |
NotifyFilters.Size |
NotifyFilters.Security,
EnableRaisingEvents = true
};
watcher.Created += Watcher_Created;
watcher.Changed += Watcher_Created;
PluginLog.Information($"Watching for changed scripts in {x}");
Watchers.Add(watcher);
}
catch(Exception e)
{
PluginLog.Error($"Error starting FileSystemWatcher on {x}");
e.Log();
}
}
}

private void Watcher_Created(object sender, FileSystemEventArgs e)
{
PluginLog.Debug($"File changed: {e.FullPath} / {e.ChangeType}");
if(e.ChangeType == WatcherChangeTypes.Created || e.ChangeType == WatcherChangeTypes.Changed)
{
try
{
S.ThreadPool.Run(() =>
{
try
{
Thread.Sleep(1000);
using var reader = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var sreader = new StreamReader(reader);
var text = sreader.ReadToEnd();
var scriptNamespace = ScriptingProcessor.ExtractNamespaceFromCode(text);
var scriptName = ScriptingProcessor.ExtractClassFromCode(text);
if(scriptName == null || scriptNamespace == null)
{
throw new NullReferenceException($"Could not parse namespace or name from script {e.FullPath}");
}
var script = ScriptingProcessor.Scripts.FirstOrDefault(x => x.InternalData.Name == scriptName && x.InternalData.Namespace == scriptNamespace);
if(script != null)
{
if(script.IsDisabledByUser)
{
PluginLog.Warning($"Script {script.InternalData.FullName} is disabled by user, skipping reload for {e.FullPath}");
}
else
{
PluginLog.Information($"Auto-reloading {script.InternalData.FullName} from {e.FullPath}");
ChatPrinter.Green($"Auto-reloading script {script.InternalData.Name}");
ScriptingProcessor.CompileAndLoad(text, script.InternalData.Path);
}
}
}
catch(Exception ex)
{
ex.Log();
}
});
}
catch(Exception ex)
{
ex.Log();
}
}
}
}
2 changes: 1 addition & 1 deletion Splatoon/Splatoon.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Authors>NightmareXIV</Authors>
<Version>3.7.4.0</Version>
<Version>3.7.4.1</Version>
</PropertyGroup>

<PropertyGroup>
Expand Down
20 changes: 20 additions & 0 deletions Splatoon/SplatoonScripting/InternalData.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#nullable enable
using ECommons.Configuration;
using ECommons.Reflection;
using Splatoon.Services;
using Splatoon.SplatoonScripting.Priority;

namespace Splatoon.SplatoonScripting;

Expand Down Expand Up @@ -55,6 +57,24 @@ public string GetConfigPathForConfigurationKey(string key)
return $"{Path}{(key == "" ? "" : $".{key}")}.json";
}

public bool ContainsPriorityLists()
{
foreach(var s in Script.GetType().Assembly.GetTypes())
{
foreach(var x in s.GetFieldPropertyUnions(ReflectionHelper.AllFlags))
{
if(x.UnionType.FullName == typeof(PriorityData).FullName) return true;
var t = x.UnionType.BaseType;
while(t != null)
{
if(t.FullName == typeof(PriorityData).FullName) return true;
t = t.BaseType;
}
}
}
return false;
}

internal string GetFreeConfigurationKey()
{
for(int i = 0; i < 1000000; i++)
Expand Down
2 changes: 1 addition & 1 deletion SplatoonScripts/Tests/GenericTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
namespace SplatoonScriptsOfficial.Tests
{
public unsafe class GenericTest : SplatoonScript
{
{
public override HashSet<uint> ValidTerritories => new() { };
//bool __fastcall sub_1400AA130(__int16 a1)
//NumberArrayData_SetValueIfDifferentAndNotify(__int64 a1, int a2, int a3)
Expand Down
8 changes: 4 additions & 4 deletions SplatoonScripts/Tests/GenericTest4.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
using ImGuiNET;
using Lumina.Excel.Sheets;
using Splatoon.SplatoonScripting;
using System;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
#pragma warning disable
namespace SplatoonScriptsOfficial.Tests;
namespace SplatoonScriptsOfficial.Tests;
public unsafe class GenericTest4 : SplatoonScript
{
{
public override HashSet<uint>? ValidTerritories => new();
public override Metadata? Metadata { get; } = new(2, "NightmareXIV");
int a1;
Expand All @@ -30,7 +30,7 @@ public override void OnSettingsDraw()
{
if (ImGui.Button("Stop")) TaskManager.Abort();
return;
}
}
ImGui.InputInt("Duty ID", ref a1);
if(ImGui.BeginCombo("Duty", Svc.Data.GetExcelSheet<ContentFinderCondition>().GetRowOrDefault((uint)a1)?.Name.ExtractText()))
{
Expand Down

0 comments on commit a0fffe6

Please sign in to comment.