public async Task SendItemSaleEvent(SeString name, string iconurl, uint itemId, string message, XivChatType chatType)
        {
            var applicableChannels =
                this.plugin.Config.ChannelConfigs.Where(x => x.Value.ChatTypes.Contains(chatType));

            if (!applicableChannels.Any())
            {
                return;
            }

            message = this.specialChars.TransformToUnicode(message);


            PluginLog.Information($"Retainer sold itemID: {itemId} with iconurl: {iconurl}");

            this.plugin.Config.PrefixConfigs.TryGetValue(chatType, out var prefix);

            foreach (var channelConfig in applicableChannels)
            {
                var socketChannel = this.socketClient.GetChannel(channelConfig.Key);

                if (socketChannel == null)
                {
                    PluginLog.Error("Could not find channel {0} for {1}", channelConfig.Key, chatType);
                    continue;
                }

                var webhookClient = await GetOrCreateWebhookClient(socketChannel);

                await webhookClient.SendMessageAsync($"{prefix} {message}",
                                                     username : $"Retainer sold {name}", avatarUrl : iconurl);
            }
        }
Exemplo n.º 2
0
        // Discover new mods.
        public void DiscoverMods()
        {
            NewMods.Clear();
            ModDiscoveryStarted?.Invoke();
            _mods.Clear();
            BasePath.Refresh();

            if (Valid && BasePath.Exists)
            {
                foreach (var modFolder in BasePath.EnumerateDirectories())
                {
                    var mod = LoadMod(modFolder);
                    if (mod == null)
                    {
                        continue;
                    }

                    mod.Index = _mods.Count;
                    _mods.Add(mod);
                }
            }

            ModDiscoveryFinished?.Invoke();
            PluginLog.Information("Rediscovered mods.");
        }
Exemplo n.º 3
0
    private void OnResourceRequested(Utf8GamePath data, bool synchronous)
    {
        var path = Match(data.Path);

        if (path != null)
        {
            PluginLog.Information($"{path} was requested {( synchronous ? "synchronously." : "asynchronously." )}");
        }
    }
Exemplo n.º 4
0
        public ModInfo FindModSettings(string name)
        {
            var settings = ModSettings.FirstOrDefault(
                x => string.Equals(x.FolderName, name, StringComparison.InvariantCultureIgnoreCase)
                );

#if DEBUG
            PluginLog.Information("finding mod {ModName} - found: {ModSettingsExist}", name, settings != null);
#endif
            return(settings);
        }
        private void UpgradeToVersion3()
        {
            PluginLog.Information("Upgrading configuration to version 3");
            foreach (var _ in Enum.GetValues(typeof(CustomComboPreset)))
#pragma warning disable CS0618 // Type or member is obsolete
            {
                _HiddenActionsBacker.Add(false);
            }
#pragma warning restore CS0618 // Type or member is obsolete
            Version = 3;
        }
        protected DivinationPlugin(DalamudPluginInterface pluginInterface)
        {
            Instance   = this as TPlugin ?? throw new TypeAccessException("クラス インスタンスが型パラメータ: TPlugin と一致しません。");
            IsDisposed = false;
            Dalamud    = new DalamudApi(pluginInterface);
            Divination = new DivinationApi <TConfiguration, TDefinition>(Dalamud, Assembly, this);

            PluginLog.Information("プラグイン: {Name} の初期化に成功しました。バージョン = {Version}",
                                  Name,
                                  Divination.Version.Plugin.InformationalVersion);
        }
Exemplo n.º 7
0
 protected override void Setup64Bit(SigScanner sig)
 {
     Offset1 = sig.ScanText("75 33 48 8B 0D ?? ?? ?? ?? BA ?? 00 00 00 48 83 C1 10 E8 ?? ?? ?? ?? 83 78");
     Offset2 = sig.ScanText("74 18 8B D7 48 8D 0D");
     PluginLog.Information(
         "Offset1: [\"ffxiv_dx11.exe\"+{0}]",
         (Offset1.ToInt64() - Process.GetCurrentProcess().MainModule.BaseAddress.ToInt64()).ToString("X")
         );
     PluginLog.Information(
         "Offset2: [\"ffxiv_dx11.exe\"+{0}]",
         (Offset2.ToInt64() - Process.GetCurrentProcess().MainModule.BaseAddress.ToInt64()).ToString("X")
         );
 }
Exemplo n.º 8
0
 internal static void Stop()
 {
     try
     {
         currentPlayback?.Stop();
         currentPlayback?.MoveToTime(new MidiTimeSpan(0));
     }
     catch (Exception e)
     {
         PluginLog.Information("Already stopped!");
     }
     finally
     {
         currentPlayback = null;
     }
 }
Exemplo n.º 9
0
        public override void Update()
        {
            if (!queue.Any())
            {
                return;
            }
            var item = queue.Dequeue();

            PluginLog.Information("Dequeue: {0}", item);

            var entry = parseChat(item);

            if (entry != null)
            {
                Plugin.pi.Framework.Gui.Chat.PrintChat(entry);
            }
        }
Exemplo n.º 10
0
        public void CreateWebServer()
        {
            const string prefix = "http://localhost:42069/";

            ShutdownWebServer();

            _webServer = new WebServer(o => o
                                       .WithUrlPrefix(prefix)
                                       .WithMode(HttpListenerMode.EmbedIO))
                         .WithCors(prefix)
                         .WithWebApi("/api", m => m
                                     .WithController(() => new ModsController(this)));

            _webServer.StateChanged += (s, e) => PluginLog.Information($"WebServer New State - {e.NewState}");

            _webServer.RunAsync();
        }
Exemplo n.º 11
0
        public ModInfo AddModSettings(ResourceMod mod)
        {
            var entry = new ModInfo
            {
                Priority   = ModSettings.Count,
                FolderName = mod.ModBasePath.Name,
                Enabled    = true,
                Mod        = mod
            };

#if DEBUG
            PluginLog.Information("creating mod settings {ModName}", entry.FolderName);
#endif

            ModSettings.Add(entry);
            return(entry);
        }
Exemplo n.º 12
0
        public void Initialize(DalamudPluginInterface pluginInterface)
        {
            services = new List <Service> {
                new ChatService()
            };

            pi = pluginInterface;
            pi.Framework.OnUpdateEvent += OnUpdate;

            server = new WebSocketServer(10078);
            foreach (var serv in services)
            {
                serv.Enable();
            }
            server.Start();

            PluginLog.Information("Loaded");
        }
Exemplo n.º 13
0
        public void GetTarget()
        {
            string messageText = "";
            float  coordX      = 0;
            float  coordY      = 0;
            float  scale       = 100;

            var target        = TargetManager.Target;
            var territoryType = ClientState.TerritoryType;
            var place         = DataManager.GetExcelSheet <Map>(ClientState.ClientLanguage).FirstOrDefault(m => m.TerritoryType.Row == territoryType);
            var placeName     = place.PlaceName.Row;

            scale = place.SizeFactor;
            var placeNameRow = DataManager.GetExcelSheet <PlaceName>(ClientState.ClientLanguage).GetRow(placeName).Name;

            if (target != null)
            {
                coordX       = (float)ToMapCoordinate(target.Position.X, scale);
                coordY       = (float)ToMapCoordinate(target.Position.Z, scale);
                messageText += placeNameRow;
                messageText += " X:" + coordX.ToString("#0.0");
                messageText += " Y:" + coordY.ToString("#0.0");
                var newMapLinkMessage = new MapLinkMessage(
                    (ushort)XivChatType.Debug,
                    target.Name.ToString(),
                    messageText,
                    coordX,
                    coordY,
                    scale,
                    territoryType,
                    placeNameRow,
                    DateTime.Now
                    );
                Config.MapLinkMessageList.Add(newMapLinkMessage);
                if (Config.MapLinkMessageList.Count > Config.MaxRecordings)
                {
                    var tempList = Config.MapLinkMessageList.OrderBy(e => e.RecordTime);
                    Config.MapLinkMessageList.RemoveRange(0, Config.MapLinkMessageList.Count - Config.MaxRecordings);
                    var infoMsg = $"There are too many records, truncated to the latest {Config.MaxRecordings} records";
                    PluginLog.Information(infoMsg);
                }
            }
        }
Exemplo n.º 14
0
        internal static void Play()
        {
            if (currentPlayback == null)
            {
                if (!PlaylistManager.Filelist.Any())
                {
                    PluginLog.Information("empty playlist");
                }
                try
                {
                    var valueTuple = PlaylistManager.Filelist[PlaylistManager.CurrentPlaying];
                    currentPlayback = valueTuple.GetFilePlayback();
                }
                catch (Exception e)
                {
                    try
                    {
                        currentPlayback = PlaylistManager.Filelist[0].GetFilePlayback();
                        PlaylistManager.CurrentPlaying = 0;
                    }
                    catch (Exception exception)
                    {
                        PluginLog.Error(exception, "error when getting playback.");
                    }
                }
            }

            try
            {
                if (currentPlayback?.GetCurrentTime <MidiTimeSpan>() == currentPlayback?.GetDuration <MidiTimeSpan>())
                {
                    currentPlayback?.MoveToStart();
                }

                currentPlayback?.Start();
            }
            catch (Exception e)
            {
                PluginLog.Error(e,
                                "error when try to start playing, maybe the playback has been disopsed?");
            }
        }
        private void UpgradeToVersion4()
        {
#pragma warning disable CS0612,CS0618 // Type or member is obsolete
            PluginLog.Information("Upgrading configuration to version 4");
            foreach (LegacyCustomComboPreset legacyPreset in Enum.GetValues(typeof(LegacyCustomComboPreset)))
            {
                if (_ComboPresetsBacker.HasFlag(legacyPreset))
                {
                    int legacyPresetIndex    = (int)Math.Log((long)legacyPreset, 2);
                    CustomComboPreset preset = (CustomComboPreset)legacyPresetIndex;
                    if (Enum.IsDefined(typeof(CustomComboPreset), preset))
                    {
                        EnabledActions.Add(preset);
                    }
                }
            }
            _ComboPresetsBacker  = 0;
            _HiddenActionsBacker = null;
            Version = 4;
#pragma warning restore CS0612,CS0618 // Type or member is obsolete
        }
Exemplo n.º 16
0
        public void Initialize(DalamudPluginInterface pluginInterface)
        {
            _pluginInterface = pluginInterface;
            _commandManager  = new Managers.CommandManager(pluginInterface);
            _configuration   = pluginInterface.GetPluginConfig() as GatherBuddyConfiguration ?? new GatherBuddyConfiguration();
            Gatherer         = new Gatherer(pluginInterface, _configuration, _commandManager);
            Alarms           = Gatherer.Alarms;
            _gatherInterface = new Interface(this, pluginInterface, _configuration);
            _fishingTimer    = new FishingTimer(_pluginInterface, _configuration, Gatherer !.FishManager);

            var tmp = _pluginInterface.TargetModuleScanner.GetStaticAddressFromSig("0F 84 AD 01 00 00 49 89 5B 08 4C 8D 15");

            PluginLog.Information($"Doop : {tmp.ToInt64():X16} {tmp.ToInt64() - _pluginInterface.TargetModuleScanner.Module.BaseAddress.ToInt64():X16}");

            var count = _pluginInterface.Data.Excel.GetSheet <Lumina.Excel.GeneratedSheets.FishParameter>().Count(f => f.IsInLog);

            PluginLog.Information($"Derp : {tmp.ToInt64() + count / 8:X16} {tmp.ToInt64() - _pluginInterface.TargetModuleScanner.Module.BaseAddress.ToInt64() + count / 8:X16}");

            if (!Gatherer !.FishManager.GetSaveFileName(_pluginInterface).Exists)
            {
                Gatherer !.FishManager.SaveFishRecords(_pluginInterface);
            }
Exemplo n.º 17
0
        public SkipCutscene()
        {
            if (Interface.GetPluginConfig() is not Config configuration || configuration.Version == 0)
            {
                configuration = new Config {
                    IsEnabled = true, Version = 1
                }
            }
            ;

            _config = configuration;

            Address = new CutsceneAddressResolver();

            Address.Setup(SigScanner);

            if (Address.Offset1 != IntPtr.Zero && Address.Offset2 != IntPtr.Zero)
            {
                PluginLog.Information("Cutscene Offset Found.");
                if (_config.IsEnabled)
                {
                    SetEnabled(true);
                }
            }
            else
            {
                PluginLog.Error("Cutscene Offset Not Found.");
                PluginLog.Warning("Plugin Disabling...");
                Dispose();
                return;
            }

            _csp = new RNGCryptoServiceProvider();

            CommandManager.AddHandler("/sc", new CommandInfo(OnCommand)
            {
                HelpMessage = "/sc: Roll your sanity check dice."
            });
        }
Exemplo n.º 18
0
        // Set the mod base directory.
        // If its not the first time, check if it is the same directory as before.
        // Also checks if the directory is available and tries to create it if it is not.
        private void SetBaseDirectory(string newPath, bool firstTime)
        {
            if (!firstTime && string.Equals(newPath, Penumbra.Config.ModDirectory, StringComparison.InvariantCultureIgnoreCase))
            {
                return;
            }

            if (newPath.Length == 0)
            {
                Valid    = false;
                BasePath = new DirectoryInfo(".");
            }
            else
            {
                var newDir = new DirectoryInfo(newPath);
                if (!newDir.Exists)
                {
                    try
                    {
                        Directory.CreateDirectory(newDir.FullName);
                        newDir.Refresh();
                    }
                    catch (Exception e)
                    {
                        PluginLog.Error($"Could not create specified mod directory {newDir.FullName}:\n{e}");
                    }
                }

                BasePath = newDir;
                Valid    = Directory.Exists(newDir.FullName);
                if (Penumbra.Config.ModDirectory != BasePath.FullName)
                {
                    PluginLog.Information("Set new mod base directory from {OldDirectory:l} to {NewDirectory:l}.", Penumbra.Config.ModDirectory, BasePath.FullName);
                    Penumbra.Config.ModDirectory = BasePath.FullName;
                    Penumbra.Config.Save();
                }
            }
        }
        private void FrameworkOnOnUpdateEvent(Framework framework)
        {
            if (this.hasLoading != true)
            {
                return;
            }

            var unitBase = (AtkUnitBase *)this._pi.Framework.Gui.GetUiObjectByName("_LocationTitle", 1);

            if (unitBase != null)
            {
                var loadingImage = unitBase->UldManager.NodeList[4];
                var imgNode      = (AtkImageNode *)loadingImage;

                if (loadingImage == null)
                {
                    return;
                }

                var asset = imgNode->PartsList->Parts[imgNode->PartId].UldAsset;

                if (loadingImage->Type == NodeType.Image && imgNode != null && asset != null)
                {
                    var resource = asset->AtkTexture.Resource;
                    if (resource == null)
                    {
                        return;
                    }

                    var name = resource->TexFileResourceHandle->ResourceHandle.FileName;

                    if (name == null)
                    {
                        return;
                    }

                    var texName = Marshal.PtrToStringAnsi(new IntPtr(name));

                    if (!texName.Contains("loadingimage"))
                    {
                        var t = unitBase->UldManager.NodeList[4];
                        unitBase->UldManager.NodeList[4] = unitBase->UldManager.NodeList[5];
                        unitBase->UldManager.NodeList[5] = t;

                        t->Flags_2 |= 0x1;

                        loadingImage = unitBase->UldManager.NodeList[4];

                        PluginLog.Information("Swapped!");
                    }
                }

                loadingImage->Width    = (ushort)this.width;
                loadingImage->Height   = (ushort)this.height;
                loadingImage->ScaleX   = this.scaleX;
                loadingImage->ScaleY   = this.scaleY;
                loadingImage->X        = this.X;
                loadingImage->Y        = this.Y;
                loadingImage->Priority = 0;

                loadingImage->Flags_2 |= 0x1;

                this.hasLoading = false;
            }
        }
Exemplo n.º 20
0
        private void Chat_OnChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled)
        {
            if (!Config.Recording)
            {
                return;
            }
            bool           hasMapLink     = false;
            float          coordX         = 0;
            float          coordY         = 0;
            float          scale          = 100;
            MapLinkPayload maplinkPayload = null;

            foreach (var payload in message.Payloads)
            {
                if (payload is MapLinkPayload mapLinkload)
                {
                    maplinkPayload = mapLinkload;
                    hasMapLink     = true;
                    // float fudge = 0.05f;
                    scale = mapLinkload.TerritoryType.Map.Value.SizeFactor;
                    // coordX = ConvertRawPositionToMapCoordinate(mapLinkload.RawX, scale) - fudge;
                    // coordY = ConvertRawPositionToMapCoordinate(mapLinkload.RawY, scale) - fudge;
                    coordX = mapLinkload.XCoord;
                    coordY = mapLinkload.YCoord;
                    Log($"TerritoryId: {mapLinkload.TerritoryType.RowId} {mapLinkload.PlaceName} ({coordX} ,{coordY})");
                }
            }
            string messageText = message.TextValue;

            if (hasMapLink)
            {
                var newMapLinkMessage = new MapLinkMessage(
                    (ushort)type,
                    sender.TextValue,
                    messageText,
                    coordX,
                    coordY,
                    scale,
                    maplinkPayload.TerritoryType.RowId,
                    maplinkPayload.PlaceName,
                    DateTime.Now
                    );
                bool filteredOut = false;
                if (sender.TextValue.ToLower() == "sonar")
                {
                    filteredOut = true;
                }
                bool alreadyInList = Config.MapLinkMessageList.Any(w => {
                    bool sameText  = w.Text == newMapLinkMessage.Text;
                    var timeoutMin = new TimeSpan(0, Config.FilterDupTimeout, 0);
                    if (newMapLinkMessage.RecordTime < w.RecordTime + timeoutMin)
                    {
                        bool sameX         = (int)(w.X * 10) == (int)(newMapLinkMessage.X * 10);
                        bool sameY         = (int)(w.Y * 10) == (int)(newMapLinkMessage.Y * 10);
                        bool sameTerritory = w.TerritoryId == newMapLinkMessage.TerritoryId;
                        return(sameTerritory && sameX && sameY);
                    }
                    return(sameText);
                });
                if (Config.FilterDuplicates && alreadyInList)
                {
                    filteredOut = true;
                }
                if (!filteredOut && Config.FilteredChannels.IndexOf((ushort)type) != -1)
                {
                    filteredOut = true;
                }
                if (!filteredOut)
                {
                    Config.MapLinkMessageList.Add(newMapLinkMessage);
                    if (Config.MapLinkMessageList.Count > Config.MaxRecordings)
                    {
                        var tempList = Config.MapLinkMessageList.OrderBy(e => e.RecordTime);
                        Config.MapLinkMessageList.RemoveRange(0, Config.MapLinkMessageList.Count - Config.MaxRecordings);
                        var infoMsg = $"There are too many records, truncated to the latest {Config.MaxRecordings} records";
                        PluginLog.Information(infoMsg);
                    }
                    Config.Save();
                }
            }
        }
Exemplo n.º 21
0
        // private TitleScreenMenu _titleScreenMenu;

        public TitleEditPlugin(
            [RequiredVersion("1.0")] DalamudPluginInterface pluginInterface,
            [RequiredVersion("1.0")] CommandManager commandManager,
            [RequiredVersion("1.0")] DataManager dataManager,
            [RequiredVersion("1.0")] ClientState clientState,
            [RequiredVersion("1.0")] Framework framework,
            [RequiredVersion("1.0")] KeyState keyState,
            [RequiredVersion("1.0")] SigScanner sigScanner,
            [RequiredVersion("1.0")] GameGui gameGui,
            [RequiredVersion("1.0")] TitleScreenMenu titleScreenMenu)
        {
            PluginLog.Log("===== T I T L E E D I T =====");
            _pluginInterface = pluginInterface;
            _commandManager  = commandManager;
            _dataManager     = dataManager;
            _clientState     = clientState;
            _framework       = framework;
            _keyState        = keyState;

            // Load menu_icon.png from dll resources
            var assembly       = Assembly.GetExecutingAssembly();
            var resourceStream = assembly.GetManifestResourceStream("TitleEdit.menu_icon.png");

            if (resourceStream != null)
            {
                var imageBytes = new byte[resourceStream.Length];
                resourceStream.Read(imageBytes, 0, (int)resourceStream.Length);
                PluginLog.Information($"image is {imageBytes.Length} bytes");
                try
                {
                    var image = pluginInterface.UiBuilder.LoadImage(imageBytes);
                    titleScreenMenu.AddEntry("Title Edit Menu", image, () => { _isImguiTitleEditOpen = true; });
                }
                catch (Exception e)
                {
                    PluginLog.Error(e, "Title Edit encountered an error loading menu icon");
                }
            }

            _commandManager.AddHandler(TitleEditCommand, new CommandInfo(OnTitleEditCommand)
            {
                HelpMessage = "Display the Title Edit configuration interface."
            });

            _configuration = pluginInterface.GetPluginConfig() as TitleEditConfiguration ?? new TitleEditConfiguration();
            _configuration.Initialize(pluginInterface);

            _titleScreenFolder = _pluginInterface.GetPluginConfigDirectory();
            if (!Directory.Exists(_titleScreenFolder))
            {
                Directory.CreateDirectory(_titleScreenFolder);
            }
            PrepareAssets();
            EnumerateTitleScreenFiles();

            _territoryPaths = dataManager.GetExcelSheet <TerritoryType>()
                              .ToDictionary(row => row.RowId, row => row);
            _weathers = dataManager.GetExcelSheet <Weather>()
                        .ToDictionary(row => row.RowId, row => row.Name.ToString());
            var bgms = dataManager.GetExcelSheet <BGM>()
                       .ToDictionary(row => (ushort)row.RowId, row => row.File.ToString());

            _bgmSheet = new BgmSheetManager(_titleScreenFolder, bgms);

            _titleEdit = new TitleEdit(sigScanner, clientState, gameGui, dataManager, _pluginInterface, _configuration, _titleScreenFolder);
            _titleEdit.Enable();

            _pluginInterface.UiBuilder.Draw += UiBuilder_OnBuildUi;
            _framework.Update += CheckHotkey;
            _pluginInterface.UiBuilder.OpenConfigUi += () => _isImguiTitleEditOpen = true;
            PluginLog.Log("Init complete.");
        }
 private byte HandleTerriChangeDetour(IntPtr a1, uint a2, byte a3, byte a4, IntPtr a5)
 {
     this.toLoadingTerri = (int)a2;
     PluginLog.Information($"toLoadingTerri: {this.toLoadingTerri}");
     return(this.handleTerriChangeHook.Original(a1, a2, a3, a4, a5));
 }
Exemplo n.º 23
0
 protected override void OnMessage(MessageEventArgs e)
 {
     queue.Enqueue(e.Data);
     PluginLog.Information("Enqueue: {0}", e.Data);
 }
Exemplo n.º 24
0
        public void Load(bool invertOrder = false)
        {
            // find the collection json
            var collectionPath = Path.Combine(_basePath.FullName, "collection.json");

            if (File.Exists(collectionPath))
            {
                try
                {
                    ModSettings = JsonConvert.DeserializeObject <List <ModInfo> >(File.ReadAllText(collectionPath));
                    ModSettings = ModSettings.OrderBy(x => x.Priority).ToList();
                }
                catch (Exception e)
                {
                    PluginLog.Error($"failed to read log collection information, failed path: {collectionPath}, err: {e.Message}");
                }
            }

#if DEBUG
            if (ModSettings != null)
            {
                foreach (var ms in ModSettings)
                {
                    PluginLog.Information(
                        "mod: {ModName} Enabled: {Enabled} Priority: {Priority}",
                        ms.FolderName, ms.Enabled, ms.Priority
                        );
                }
            }
#endif

            ModSettings ??= new();
            var foundMods = new List <string>();

            foreach (var modDir in _basePath.EnumerateDirectories())
            {
                var metaFile = modDir.EnumerateFiles().FirstOrDefault(f => f.Name == "meta.json");

                if (metaFile == null)
                {
                    PluginLog.LogError("mod meta is missing for resource mod: {ResourceModLocation}", modDir);
                    continue;
                }

                var meta = JsonConvert.DeserializeObject <ModMeta>(File.ReadAllText(metaFile.FullName));

                var mod = new ResourceMod
                {
                    Meta        = meta,
                    ModBasePath = modDir
                };

                var modEntry = FindOrCreateModSettings(mod);
                foundMods.Add(modDir.Name);
                mod.RefreshModFiles();
            }

            // remove any mods from the collection we didn't find
            ModSettings = ModSettings.Where(
                x =>
                foundMods.Any(
                    fm => string.Equals(x.FolderName, fm, StringComparison.InvariantCultureIgnoreCase)
                    )
                ).ToList();

            // if anything gets removed above, the priority ordering gets f****d, so we need to resort and reindex them otherwise BAD THINGS HAPPEN
            ModSettings = ModSettings.OrderBy(x => x.Priority).ToList();
            var p = 0;
            foreach (var modSetting in ModSettings)
            {
                modSetting.Priority = p++;
            }

            // reorder the resourcemods list so we can just directly iterate
            EnabledMods = GetOrderedAndEnabledModList(invertOrder).ToArray();

            // write the collection metadata back to disk
            Save();
        }
Exemplo n.º 25
0
    public Penumbra(CharacterUtility characterUtility)
    {
        CharacterUtility = characterUtility;

        Framework = new FrameworkManager();
        Backup.CreateBackup(PenumbraBackupFiles());
        Config = Configuration.Load();

        MusicManager = new MusicManager();
        if (Config.DisableSoundStreaming)
        {
            MusicManager.DisableStreaming();
        }

        ResidentResources = new ResidentResourceManager();
        Redirects         = new SimpleRedirectManager();
        MetaFileManager   = new MetaFileManager();
        ResourceLoader    = new ResourceLoader(this);
        ResourceLogger    = new ResourceLogger(ResourceLoader);
        ModManager        = new Mod.Manager(Config.ModDirectory);
        ModManager.DiscoverMods();
        CollectionManager = new ModCollection.Manager(ModManager);
        ModFileSystem     = ModFileSystem.Load();
        ObjectReloader    = new ObjectReloader();
        PathResolver      = new PathResolver(ResourceLoader);

        Dalamud.Commands.AddHandler(CommandName, new CommandInfo(OnCommand)
        {
            HelpMessage = "/penumbra - toggle ui\n/penumbra reload - reload mod file lists & discover any new mods",
        });

        ResidentResources.Reload();

        SetupInterface(out _configWindow, out _launchButton, out _windowSystem);

        if (Config.EnableHttpApi)
        {
            CreateWebServer();
        }

        ResourceLoader.EnableHooks();
        if (Config.EnableMods)
        {
            ResourceLoader.EnableReplacements();
            PathResolver.Enable();
        }

        if (Config.DebugMode)
        {
            ResourceLoader.EnableDebug();
            _configWindow.IsOpen = true;
        }

        if (Config.EnableFullResourceLogging)
        {
            ResourceLoader.EnableFullLogging();
        }

        ResidentResources.Reload();

        Api = new PenumbraApi(this);
        Ipc = new PenumbraIpc(Dalamud.PluginInterface, Api);
        SubscribeItemLinks();
        if (ImcExceptions > 0)
        {
            PluginLog.Error($"{ImcExceptions} IMC Exceptions thrown. Please repair your game files.");
        }
        else
        {
            PluginLog.Information($"Penumbra Version {Version}, Commit #{CommitHash} successfully Loaded.");
        }
    }
Exemplo n.º 26
0
        private async Task SocketClientOnMessageReceived(SocketMessage message)
        {
            if (message.Author.IsBot || message.Author.IsWebhook)
            {
                return;
            }

            var args = message.Content.Split();

            // if it doesn't start with the bot prefix, ignore it.
            if (!args[0].StartsWith(this.plugin.Config.DiscordBotPrefix))
            {
                return;
            }

            /*
             * // this is only needed for debugging purposes.
             * foreach (var s in args)
             * {
             *  PluginLog.Verbose(s);
             * }
             */

            PluginLog.Verbose("Received command: {0}", args[0]);

            try
            {
                if (args[0] == this.plugin.Config.DiscordBotPrefix + "setchannel" &&
                    await EnsureOwner(message.Author, message.Channel))
                {
                    // Are there parameters?
                    if (args.Length == 1)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"You need to specify some chat kinds to use.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    var kinds = args[1].Split(',').Select(x => x.ToLower());

                    // Is there any chat type that's not recognized?
                    if (kinds
                        .Any(x =>
                             XivChatTypeExtensions.TypeInfoDict.All(y => y.Value.Slug != x) && x != "any"))
                    {
                        PluginLog.Verbose("Could not find kinds");
                        await SendGenericEmbed(message.Channel,
                                               $"One or more of the chat kinds you specified could not be found.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    if (!this.plugin.Config.ChannelConfigs.TryGetValue(message.Channel.Id, out var config))
                    {
                        config = new DiscordChannelConfig();
                    }

                    foreach (var selectedKind in kinds)
                    {
                        PluginLog.Verbose(selectedKind);

                        if (selectedKind == "any")
                        {
                            config.SetUnique(DefaultChatTypes);
                        }
                        else
                        {
                            var chatType = XivChatTypeExtensions.GetBySlug(selectedKind);
                            config.SetUnique(chatType);
                        }
                    }

                    this.plugin.Config.ChannelConfigs[message.Channel.Id] = config;
                    this.plugin.Config.Save();

                    await SendGenericEmbed(message.Channel,
                                           $"OK! This channel has been set to receive the following chat kinds:\n\n```\n{config.ChatTypes.Select(x => $"{x.GetFancyName()}").Aggregate((x, y) => x + "\n" + y)}```",
                                           "Chat kinds set", EmbedColorFine);

                    return;
                }

                if (args[0] == this.plugin.Config.DiscordBotPrefix + "unsetchannel" &&
                    await EnsureOwner(message.Author, message.Channel))
                {
                    // Are there parameters?
                    if (args.Length == 1)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"You need to specify some chat kinds to use.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    var kinds = args[1].Split(',').Select(x => x.ToLower());

                    // Is there any chat type that's not recognized?
                    if (kinds.Any(x =>
                                  XivChatTypeExtensions.TypeInfoDict.All(y => y.Value.Slug != x) && x != "any"))
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"One or more of the chat kinds you specified could not be found.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    if (!this.plugin.Config.ChannelConfigs.TryGetValue(message.Channel.Id, out var config))
                    {
                        config = new DiscordChannelConfig();
                    }

                    foreach (var selectedKind in kinds)
                    {
                        if (selectedKind == "any")
                        {
                            config.UnsetUnique(DefaultChatTypes);
                        }
                        else
                        {
                            var chatType = XivChatTypeExtensions.GetBySlug(selectedKind);
                            config.UnsetUnique(chatType);
                        }
                    }

                    this.plugin.Config.ChannelConfigs[message.Channel.Id] = config;
                    this.plugin.Config.Save();

                    if (config.ChatTypes.Count() == 0)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"All chat kinds have been removed from this channel.",
                                               "Chat Kinds unset", EmbedColorFine);
                    }
                    await SendGenericEmbed(message.Channel,
                                           $"OK! This channel will still receive the following chat kinds:\n\n```\n{config.ChatTypes.Select(x => $"{x.GetSlug()} - {x.GetFancyName()}").Aggregate((x, y) => x + "\n" + y)}```",
                                           "Chat kinds unset", EmbedColorFine);

                    return;
                }

                if (args[0] == this.plugin.Config.DiscordBotPrefix + "setprefix" &&
                    await EnsureOwner(message.Author, message.Channel))
                {
                    // Are there parameters?
                    if (args.Length < 3)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"You need to specify some chat kinds and a prefix to use.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    var kinds = args[1].Split(',').Select(x => x.ToLower());

                    // Is there any chat type that's not recognized?
                    if (kinds.Any(x =>
                                  XivChatTypeExtensions.TypeInfoDict.All(y => y.Value.Slug != x) && x != "any"))
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"One or more of the chat kinds you specified could not be found.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    if (args[2] == "none")
                    {
                        args[2] = string.Empty;
                    }

                    foreach (var selectedKind in kinds)
                    {
                        var type = XivChatTypeExtensions.GetBySlug(selectedKind);
                        this.plugin.Config.PrefixConfigs[type] = args[2];
                    }

                    this.plugin.Config.Save();


                    await SendGenericEmbed(message.Channel,
                                           $"OK! The following prefixes are set:\n\n```\n{this.plugin.Config.PrefixConfigs.Select(x => $"{x.Key.GetFancyName()} - {x.Value}").Aggregate((x, y) => x + "\n" + y)}```",
                                           "Prefix set", EmbedColorFine);

                    return;
                }



                if (args[0] == this.plugin.Config.DiscordBotPrefix + "unsetprefix" &&
                    await EnsureOwner(message.Author, message.Channel))
                {
                    // Are there parameters?
                    if (args.Length < 2)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"You need to specify some chat kinds and a prefix to use.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    var kinds = args[1].Split(',').Select(x => x.ToLower());

                    // Is there any chat type that's not recognized?
                    if (kinds.Any(x =>
                                  XivChatTypeExtensions.TypeInfoDict.All(y => y.Value.Slug != x) && x != "any"))
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"One or more of the chat kinds you specified could not be found.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    foreach (var selectedKind in kinds)
                    {
                        var type = XivChatTypeExtensions.GetBySlug(selectedKind);
                        this.plugin.Config.PrefixConfigs.Remove(type);
                    }

                    this.plugin.Config.Save();

                    if (this.plugin.Config.PrefixConfigs.Count() == 0)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"All prefixes have been removed.",
                                               "Prefix unset", EmbedColorFine);
                    }
                    else // this doesn't seem to trigger when there's only one entry left. I don't know why.
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"OK! The prefix for {XivChatTypeExtensions.GetBySlug(args[2])} has been removed.\n\n"
                                               + $"The following prefixes are still set:\n\n```\n{this.plugin.Config.PrefixConfigs.Select(x => $"{x.Key.GetFancyName()} - {x.Value}").Aggregate((x, y) => x + "\n" + y)}```",
                                               "Prefix unset", EmbedColorFine);
                    }

                    return;
                }

                if (args[0] == this.plugin.Config.DiscordBotPrefix + "setchattypename" &&
                    await EnsureOwner(message.Author, message.Channel))
                {
                    // Are there parameters?
                    if (args.Length < 3)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"You need to specify one or more chat kinds and a custom name.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }



                    var kinds = args[1].Split(',').Select(x => x.ToLower());
                    var chatChannelOverride = string.Join(" ", args.Skip(2)).Trim('"');

                    // PluginLog.Information($"arg1: {args[1]}; arg2: {chatChannelOverride}");

                    // Is there any chat type that's not recognized?
                    if (kinds.Any(x =>
                                  XivChatTypeExtensions.TypeInfoDict.All(y => y.Value.Slug != x) && x != "any"))
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"One or more of the chat kinds you specified could not be found.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    if (chatChannelOverride == "none")
                    {
                        foreach (var selectedKind in kinds)
                        {
                            var type = XivChatTypeExtensions.GetBySlug(selectedKind);
                            this.plugin.Config.CustomSlugsConfigs[type] = type.GetSlug();
                        }

                        await SendGenericEmbed(message.Channel,
                                               $"OK! The following custom chat type names have been set:\n\n```\n{this.plugin.Config.CustomSlugsConfigs.Select(x => $"{x.Key.GetFancyName()} - {x.Value}").Aggregate((x, y) => x + "\n" + y)}```",
                                               "Custom chat type set", EmbedColorFine);
                    }
                    else
                    {
                        foreach (var selectedKind in kinds)
                        {
                            var type = XivChatTypeExtensions.GetBySlug(selectedKind);
                            this.plugin.Config.CustomSlugsConfigs[type] = chatChannelOverride;
                        }

                        await SendGenericEmbed(message.Channel,
                                               $"OK! The following custom chat type names have been set:\n\n```\n{this.plugin.Config.CustomSlugsConfigs.Select(x => $"{x.Key.GetFancyName()} - {x.Value}").Aggregate((x, y) => x + "\n" + y)}```",
                                               "Custom chat type set", EmbedColorFine);
                    }

                    this.plugin.Config.Save();

                    return;
                }

                if (args[0] == this.plugin.Config.DiscordBotPrefix + "unsetchattypename" &&
                    await EnsureOwner(message.Author, message.Channel))
                {
                    // Are there parameters?
                    if (args.Length < 2)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"One or more of the chat kinds you specified could not be found.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }



                    var kinds = args[1].Split(',').Select(x => x.ToLower());

                    PluginLog.Information($"Unsetting custom type name for arg1: {args[1]}");

                    // Is there any chat type that's not recognized?
                    if (kinds.Any(x =>
                                  XivChatTypeExtensions.TypeInfoDict.All(y => y.Value.Slug != x) && x != "any"))
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"One or more of the chat kinds you specified could not be found.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }


                    foreach (var selectedKind in kinds)
                    {
                        var type = XivChatTypeExtensions.GetBySlug(selectedKind);
                        this.plugin.Config.CustomSlugsConfigs[type] = type.GetSlug();
                    }

                    await SendGenericEmbed(message.Channel,
                                           $"OK! The following custom chat type names have been set:\n\n```\n{this.plugin.Config.CustomSlugsConfigs.Select(x => $"{x.Key.GetFancyName()} - {x.Value}").Aggregate((x, y) => x + "\n" + y)}```",
                                           "Custom chat type unset", EmbedColorFine);


                    this.plugin.Config.Save();

                    return;
                }

                if (args[0] == this.plugin.Config.DiscordBotPrefix + "setduplicatems" &&
                    await EnsureOwner(message.Author, message.Channel))
                {
                    // Are there parameters?
                    if (args.Length == 1)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"You need to specify a number in milliseconds to use.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    var kinds = args[1].Split(',').Select(x => x.ToLower());

                    // Make sure that it's a number (or assume it is)
                    int newDelay = int.Parse(args[1]);

                    this.plugin.Config.DuplicateCheckMS = newDelay;
                    this.plugin.Config.Save();

                    await SendGenericEmbed(message.Channel,
                                           $"OK! Any messages with the same content within the last **{newDelay}** milliseconds will be skipped, preventing duplicate posts.",
                                           "Duplicate Message Check", EmbedColorFine);

                    return;
                }

                if (args[0] == this.plugin.Config.DiscordBotPrefix + "toggledf" &&
                    await EnsureOwner(message.Author, message.Channel))
                {
                    if (!this.plugin.Config.ChannelConfigs.TryGetValue(message.Channel.Id, out var config))
                    {
                        config = new DiscordChannelConfig();
                    }

                    config.IsContentFinder = !config.IsContentFinder;

                    this.plugin.Config.ChannelConfigs[message.Channel.Id] = config;
                    this.plugin.Config.Save();

                    await SendGenericEmbed(message.Channel,
                                           $"OK! This channel has been {(config.IsContentFinder ? "enabled" : "disabled")} from receiving Duty Finder notifications.",
                                           "Duty Finder set", EmbedColorFine);

                    return;
                }

                if (args[0] == this.plugin.Config.DiscordBotPrefix + "setcfprefix" &&
                    await EnsureOwner(message.Author, message.Channel))
                {
                    // Are there parameters?
                    if (args.Length < 2)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"You need to specify a prefix to use, or type \"none\" if you want to remove it.\nCheck the ``{this.plugin.Config.DiscordBotPrefix}help`` command for more information.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    if (args[1] == "none")
                    {
                        args[1] = string.Empty;
                    }

                    this.plugin.Config.CFPrefixConfig = args[1];

                    this.plugin.Config.Save();


                    await SendGenericEmbed(message.Channel,
                                           $"OK! The following prefix was set:\n\n```\n{this.plugin.Config.CFPrefixConfig}```",
                                           "Prefix set", EmbedColorFine);

                    return;
                }

                if (args[0] == this.plugin.Config.DiscordBotPrefix + "listchannel" &&
                    await EnsureOwner(message.Author, message.Channel))
                {
                    if (!this.plugin.Config.ChannelConfigs.TryGetValue(message.Channel.Id, out var config))
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"You didn't set up any channel kinds for this channel yet.\nPlease use the ``{this.plugin.Config.DiscordBotPrefix}setchannel`` command to do this.",
                                               "Error", EmbedColorError);

                        return;
                    }

                    if (config == null || config.ChatTypes.Count == 0)
                    {
                        await SendGenericEmbed(message.Channel,
                                               $"There are no channel kinds set for this channel right now.\nPlease use the ``{this.plugin.Config.DiscordBotPrefix}setchannel`` command to do this.",
                                               "Error", EmbedColorFine);

                        return;
                    }

                    await SendGenericEmbed(message.Channel,
                                           $"OK! This channel has been set to receive the following chat kinds:\n\n```\n{config.ChatTypes.Select(x => $"{x.GetFancyName()}").Aggregate((x, y) => x + "\n" + y)}```",
                                           "Chat kinds set", EmbedColorFine);

                    return;
                }

                if (args[0] == this.plugin.Config.DiscordBotPrefix + "help")
                {
                    PluginLog.Verbose("Help time");

                    var builder = new EmbedBuilder()
                                  .WithTitle("Discord Bridge Help")
                                  .WithDescription("You can use the following commands to set up the Discord bridge.")
                                  .WithColor(new Color(EmbedColorFine))
                                  .AddField($"{this.plugin.Config.DiscordBotPrefix}setchannel", "Select, which kinds of chat should arrive in this channel.\n" +
                                            $"Format: ``{this.plugin.Config.DiscordBotPrefix}setchannel <kind1,kind2,...>``\n\n" +
                                            $"See [this link for a list of all available chat kinds]({Constant.KindListLink}) or type ``any`` to enable it for all regular chat messages.")
                                  //$"The following chat kinds are available:\n```all - All regular chat\n{XivChatTypeExtensions.TypeInfoDict.Select(x => $"{x.Value.Slug} - {x.Value.FancyName}").Aggregate((x, y) => x + "\n" + y)}```")
                                  .AddField($"{this.plugin.Config.DiscordBotPrefix}unsetchannel", "Works like the previous command, but removes kinds of chat from the list of kinds that are sent to this channel.")
                                  .AddField($"{this.plugin.Config.DiscordBotPrefix}listchannel", "List all chat kinds that are sent to this channel.")
                                  .AddField($"{this.plugin.Config.DiscordBotPrefix}toggledf", "Enable or disable sending duty finder updates to this channel.")
                                  .AddField($"{this.plugin.Config.DiscordBotPrefix}setduplicatems", "Set time in milliseconds that the bot will check to see if any past messages were the same. Default is 0 ms.")
                                  .AddField($"{this.plugin.Config.DiscordBotPrefix}setprefix", "Set a prefix for chat kinds. "
                                            + $"This can be an emoji or a string that will be prepended to every chat message that will arrive with this chat kind. "
                                            + $"You can also set it to `none` if you want to remove it.\n"
                                            + $"Format: ``{this.plugin.Config.DiscordBotPrefix}setchannel <kind1,kind2,...> <prefix>``")
                                  .AddField($"{this.plugin.Config.DiscordBotPrefix}setcfprefix", "Set a prefix for duty finder posts. "
                                            + $"You can also set it to `none` if you want to remove it.\n"
                                            + $"Format: ``{this.plugin.Config.DiscordBotPrefix}setcfprefix <prefix>``")
                                  .AddField($"{this.plugin.Config.DiscordBotPrefix}setchattypename ", "Set custom text for chat kinds. "
                                            + $"This can be an emoji or a string that will replace the short name of a chat kind for every chat message that will arrive with this chat kind. "
                                            + $"You can also set it to `none` if you want to remove it.\n"
                                            + $"Format: ``{this.plugin.Config.DiscordBotPrefix}setchattypename  <kind1,kind2,...> <custom text>``")
                                  .AddField($"{this.plugin.Config.DiscordBotPrefix}unsetprefix", "Remove prefix set for a chat kind. \n"
                                            + $"Format: ``{this.plugin.Config.DiscordBotPrefix}unsetprefix <kind>``")
                                  .AddField($"{this.plugin.Config.DiscordBotPrefix}unsetchattypename", "Remove custom name for a chat kind. \n"
                                            + $"Format: ``{this.plugin.Config.DiscordBotPrefix}unsetchattypename <kind>``")
                                  .AddField("Need more help?",
                                            $"You can [read the full step-by-step guide]({Constant.HelpLink}) or [join our Discord server]({Constant.DiscordJoinLink}) to ask for help.")
                                  .WithFooter(footer =>
                    {
                        footer
                        .WithText("Dalamud Discord Bridge")
                        .WithIconUrl(Constant.LogoLink);
                    })
                                  .WithThumbnailUrl(Constant.LogoLink);
                    var embed = builder.Build();

                    var m = await message.Channel.SendMessageAsync(
                        null,
                        embed : embed)
                            .ConfigureAwait(false);

                    ;
                    PluginLog.Verbose(m.Id.ToString());

                    return;
                }
            }
            catch (Exception ex)
            {
                PluginLog.Error(ex, "Could not handle incoming Discord message.");
            }
        }
        private int PrintIconPathDetour(IntPtr pathPtr, int iconId, int hq, int lang)
        {
            var r = this.printIconHook.Original(pathPtr, iconId, hq, lang);

            var terriRegion = this.terris.FirstOrDefault(x => x.PlaceNameRegionIcon == iconId);

            if (terriRegion != null)
            {
                PluginLog.Information($"LoadIcon: {iconId} detected for r:{terriRegion.RowId} with toLoadingTerri:{this.toLoadingTerri}");

                try
                {
                    if (this.toLoadingTerri == -1)
                    {
                        PluginLog.Information($"toLoadingImage not set!");
                        this.hasLoading = false;
                        return(r);
                    }

                    if (this.cfcs.Any(x => x.ContentLinkType == 1 && x.TerritoryType.Row == this.toLoadingTerri))
                    {
                        PluginLog.Information("Is InstanceContent zone!");
                        this.hasLoading = false;
                        return(r);
                    }

                    var terriZone = this.terris.FirstOrDefault(x => x.RowId == this.toLoadingTerri);

                    if (terriZone == null)
                    {
                        PluginLog.Information($"terriZone null!");
                        this.hasLoading = false;
                        return(r);
                    }

                    if (terriZone.PlaceNameRegionIcon != terriRegion.PlaceNameRegionIcon)
                    {
                        PluginLog.Information($"Mismatch: {terriZone.RowId} {terriRegion.RowId}");
                        this.hasLoading = false;
                        return(r);
                    }

                    var loading = this.loadings.FirstOrDefault(x => x.RowId == terriZone.LoadingImage);

                    if (loading == null)
                    {
                        PluginLog.Information($"LoadingImage null!");
                        this.hasLoading = false;
                        return(r);
                    }

                    SafeMemory.WriteString(pathPtr, $"ui/loadingimage/{loading.Name}_hr1.tex");
                    PluginLog.Information($"Replacing icon for territory {terriRegion.RowId}");

                    this.hasLoading = true;
                }
                catch (Exception ex)
                {
                    PluginLog.Error(ex, "Could not replace loading image.");
                }
            }

            return(r);
        }