From a177adf2ea2b69154cdff217ff48ef445031ccfd Mon Sep 17 00:00:00 2001 From: Brenden Reeves Date: Sun, 10 Oct 2021 19:04:19 -0500 Subject: [PATCH 1/7] Exanded HTTP API's Get Mods and Added Delete Function --- .idea/.idea.Penumbra/.idea/indexLayout.xml | 2 +- Penumbra/API/ModsController.cs | 62 ++++++++++++++++++---- Penumbra/Mods/CollectionManager.cs | 2 +- 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/.idea/.idea.Penumbra/.idea/indexLayout.xml b/.idea/.idea.Penumbra/.idea/indexLayout.xml index 27ba142e9..7b08163ce 100644 --- a/.idea/.idea.Penumbra/.idea/indexLayout.xml +++ b/.idea/.idea.Penumbra/.idea/indexLayout.xml @@ -1,6 +1,6 @@ - + diff --git a/Penumbra/API/ModsController.cs b/Penumbra/API/ModsController.cs index 277f4a3ae..347af2421 100644 --- a/Penumbra/API/ModsController.cs +++ b/Penumbra/API/ModsController.cs @@ -1,7 +1,11 @@ +using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; +using Dalamud.Logging; using EmbedIO; using EmbedIO.Routing; +using EmbedIO.Utilities; using EmbedIO.WebApi; using Penumbra.Mods; using Penumbra.Util; @@ -18,23 +22,63 @@ public ModsController( Penumbra penumbra ) [Route( HttpVerbs.Get, "/mods" )] public object? GetMods() { + // Obtain Query Data + var activeOnly = Convert.ToBoolean( Request.QueryString[ "activeOnly" ] ); + var requestedCollection = Request.QueryString[ "collection" ]; + var modManager = Service< ModManager >.Get(); - return modManager.Collections.CurrentCollection.Cache?.AvailableMods.Values.Select( x => new + var collection = modManager.Collections.CurrentCollection; + + if( !string.IsNullOrWhiteSpace(requestedCollection) ) + { + collection = modManager.Collections.Collections[ requestedCollection ]; + if( collection.Cache is null ) { - x.Settings.Enabled, - x.Settings.Priority, - x.Data.BasePath.Name, - x.Data.Meta, - BasePath = x.Data.BasePath.FullName, - Files = x.Data.Resources.ModFiles.Select( fi => fi.FullName ), - } ) - ?? null; + PluginLog.Log($"Collection {requestedCollection} has been requested but cash is null. Generating cache now...."); + modManager.Collections.AddCache(collection); + } + } + + var mods = collection.Cache?.AvailableMods.Values.Select( x => new + { + x.Settings.Enabled, + x.Settings.Priority, + x.Data.BasePath.Name, + x.Data.Meta, + BasePath = x.Data.BasePath.FullName, + Files = x.Data.Resources.ModFiles.Select( fi => fi.FullName ), + } ); + + if( Convert.ToBoolean( Request.QueryString[ "activeOnly" ] ) ) + { + mods = mods?.Where( m => m.Enabled ); + } + + return mods; } [Route( HttpVerbs.Post, "/mods" )] public object CreateMod() => new { }; + [Route(HttpVerbs.Post, "/mods/delete")] + public async Task< bool > DeleteMod() + { + var requestData = await HttpContext.GetRequestFormDataAsync(); + var modName = Request.QueryString[ "name" ]; + + if( string.IsNullOrWhiteSpace(modName) ) + { + return false; + } + + PluginLog.Log($"Attempting to delete mod: {modName}"); + var modManager = Service< ModManager >.Get(); + var mod = modManager.Mods[ modName ]; + modManager.DeleteMod(mod.BasePath); + return true; + } + [Route( HttpVerbs.Get, "/files" )] public object GetFiles() { diff --git a/Penumbra/Mods/CollectionManager.cs b/Penumbra/Mods/CollectionManager.cs index 9e6177467..a6150ce46 100644 --- a/Penumbra/Mods/CollectionManager.cs +++ b/Penumbra/Mods/CollectionManager.cs @@ -161,7 +161,7 @@ public bool RemoveCollection( string name ) return false; } - private void AddCache( ModCollection collection ) + public void AddCache( ModCollection collection ) { if( !_manager.TempWritable ) { From b9f3abee82ccde94a395573c7196ab4deb83f4fc Mon Sep 17 00:00:00 2001 From: Brenden Reeves Date: Sun, 10 Oct 2021 19:08:01 -0500 Subject: [PATCH 2/7] Injected Mod Manager Into Mods Controller --- Penumbra/API/ModsController.cs | 33 +++++++++++++++++---------------- Penumbra/Penumbra.cs | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Penumbra/API/ModsController.cs b/Penumbra/API/ModsController.cs index 347af2421..4487cd0c1 100644 --- a/Penumbra/API/ModsController.cs +++ b/Penumbra/API/ModsController.cs @@ -5,19 +5,21 @@ using Dalamud.Logging; using EmbedIO; using EmbedIO.Routing; -using EmbedIO.Utilities; using EmbedIO.WebApi; using Penumbra.Mods; -using Penumbra.Util; namespace Penumbra.Api { public class ModsController : WebApiController { - private readonly Penumbra _penumbra; + private readonly Penumbra _penumbra; + private readonly ModManager _modManager; - public ModsController( Penumbra penumbra ) - => _penumbra = penumbra; + public ModsController( Penumbra penumbra, ModManager modManager ) + { + _penumbra = penumbra; + _modManager = modManager; + } [Route( HttpVerbs.Get, "/mods" )] public object? GetMods() @@ -26,16 +28,15 @@ public ModsController( Penumbra penumbra ) var activeOnly = Convert.ToBoolean( Request.QueryString[ "activeOnly" ] ); var requestedCollection = Request.QueryString[ "collection" ]; - var modManager = Service< ModManager >.Get(); - var collection = modManager.Collections.CurrentCollection; + var collection = _modManager.Collections.CurrentCollection; if( !string.IsNullOrWhiteSpace(requestedCollection) ) { - collection = modManager.Collections.Collections[ requestedCollection ]; + collection = _modManager.Collections.Collections[ requestedCollection ]; if( collection.Cache is null ) { PluginLog.Log($"Collection {requestedCollection} has been requested but cash is null. Generating cache now...."); - modManager.Collections.AddCache(collection); + _modManager.Collections.AddCache(collection); } } @@ -58,8 +59,10 @@ public ModsController( Penumbra penumbra ) } [Route( HttpVerbs.Post, "/mods" )] - public object CreateMod() - => new { }; + public object? CreateMod() + { + return null; + } [Route(HttpVerbs.Post, "/mods/delete")] public async Task< bool > DeleteMod() @@ -73,17 +76,15 @@ public async Task< bool > DeleteMod() } PluginLog.Log($"Attempting to delete mod: {modName}"); - var modManager = Service< ModManager >.Get(); - var mod = modManager.Mods[ modName ]; - modManager.DeleteMod(mod.BasePath); + var mod = _modManager.Mods[ modName ]; + _modManager.DeleteMod(mod.BasePath); return true; } [Route( HttpVerbs.Get, "/files" )] public object GetFiles() { - var modManager = Service< ModManager >.Get(); - return modManager.Collections.CurrentCollection.Cache?.ResolvedFiles.ToDictionary( + return _modManager.Collections.CurrentCollection.Cache?.ResolvedFiles.ToDictionary( o => ( string )o.Key, o => o.Value.FullName ) diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 6ac949d61..391e71f2d 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -119,7 +119,7 @@ public void CreateWebServer() .WithMode( HttpListenerMode.EmbedIO ) ) .WithCors( prefix ) .WithWebApi( "/api", m => m - .WithController( () => new ModsController( this ) ) ); + .WithController( () => new ModsController( this, Service< ModManager >.Get() ) ) ); _webServer.StateChanged += ( s, e ) => PluginLog.Information( $"WebServer New State - {e.NewState}" ); From 12d09a4e730d50f354076d631c5b96c3259d36eb Mon Sep 17 00:00:00 2001 From: Brenden Reeves Date: Sun, 10 Oct 2021 19:40:01 -0500 Subject: [PATCH 3/7] Added Mod Creation to HTTP API --- Penumbra/API/Models/NewMod.cs | 7 +++++ Penumbra/API/ModsController.cs | 26 +++++++++++++++---- Penumbra/Mods/ModManager.cs | 23 ++++++++++++++-- .../TabInstalled/TabInstalledSelector.cs | 14 +--------- 4 files changed, 50 insertions(+), 20 deletions(-) create mode 100644 Penumbra/API/Models/NewMod.cs diff --git a/Penumbra/API/Models/NewMod.cs b/Penumbra/API/Models/NewMod.cs new file mode 100644 index 000000000..941f3f8f7 --- /dev/null +++ b/Penumbra/API/Models/NewMod.cs @@ -0,0 +1,7 @@ +namespace Penumbra.Api.Models +{ + public class NewMod + { + public string name { get; set; } + } +} \ No newline at end of file diff --git a/Penumbra/API/ModsController.cs b/Penumbra/API/ModsController.cs index 4487cd0c1..4effac847 100644 --- a/Penumbra/API/ModsController.cs +++ b/Penumbra/API/ModsController.cs @@ -6,6 +6,7 @@ using EmbedIO; using EmbedIO.Routing; using EmbedIO.WebApi; +using Penumbra.Api.Models; using Penumbra.Mods; namespace Penumbra.Api @@ -59,9 +60,15 @@ public ModsController( Penumbra penumbra, ModManager modManager ) } [Route( HttpVerbs.Post, "/mods" )] - public object? CreateMod() + public async Task< string > CreateMod() { - return null; + var requestData = await HttpContext.GetRequestDataAsync(); + PluginLog.Log($"Attempting to create mod: {requestData.name}"); + var newModDir = _modManager.GenerateEmptyMod( requestData.name ); + return _modManager.Mods + .Where( m => m.Value.BasePath.Name == newModDir.Name ) + .FirstOrDefault() + .Value.BasePath.Name; } [Route(HttpVerbs.Post, "/mods/delete")] @@ -76,9 +83,18 @@ public async Task< bool > DeleteMod() } PluginLog.Log($"Attempting to delete mod: {modName}"); - var mod = _modManager.Mods[ modName ]; - _modManager.DeleteMod(mod.BasePath); - return true; + try + { + var mod = _modManager.Mods[ modName ]; + _modManager.DeleteMod( mod.BasePath ); + ModFileSystem.InvokeChange(); + + return true; + } + catch + { + return false; + } } [Route( HttpVerbs.Get, "/files" )] diff --git a/Penumbra/Mods/ModManager.cs b/Penumbra/Mods/ModManager.cs index 4a42d7b68..88ccba1e5 100644 --- a/Penumbra/Mods/ModManager.cs +++ b/Penumbra/Mods/ModManager.cs @@ -4,6 +4,7 @@ using System.Linq; using Dalamud.Logging; using Penumbra.GameData.Util; +using Penumbra.Importer; using Penumbra.Meta; using Penumbra.Mod; @@ -268,6 +269,24 @@ public void DeleteMod( DirectoryInfo modFolder ) } } + public DirectoryInfo GenerateEmptyMod(string name) + { + var newDir = TexToolsImport.CreateModFolder( new DirectoryInfo( Penumbra.Config!.ModDirectory ), + name ); + var modMeta = new ModMeta + { + Author = "Unknown", + Name = name.Replace( '/', '\\' ), + Description = string.Empty, + }; + + var metaFile = new FileInfo( Path.Combine( newDir.FullName, "meta.json" ) ); + modMeta.SaveToFile( metaFile ); + AddMod( newDir ); + ModFileSystem.InvokeChange(); + return newDir; + } + public bool AddMod( DirectoryInfo modFolder ) { var mod = ModData.LoadMod( StructuredMods, modFolder ); @@ -375,7 +394,7 @@ public bool UpdateMod( ModData mod, bool reloadMeta = false, bool recomputeMeta // PluginLog.Log( "a loaded file has been modified - file: {FullPath}", file ); // _plugin.GameUtils.ReloadPlayerResources(); // } - // + // // private void FileSystemPasta() // { // haha spaghet @@ -388,7 +407,7 @@ public bool UpdateMod( ModData mod, bool reloadMeta = false, bool recomputeMeta // IncludeSubdirectories = true, // EnableRaisingEvents = true // }; - // + // // _fileSystemWatcher.Changed += FileSystemWatcherOnChanged; // _fileSystemWatcher.Created += FileSystemWatcherOnChanged; // _fileSystemWatcher.Deleted += FileSystemWatcherOnChanged; diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs index 5f73a2e9c..46c9261d8 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs @@ -154,19 +154,7 @@ private void DrawModAddPopup() { try { - var newDir = TexToolsImport.CreateModFolder( new DirectoryInfo( Penumbra.Config!.ModDirectory ), - newName ); - var modMeta = new ModMeta - { - Author = "Unknown", - Name = newName.Replace( '/', '\\' ), - Description = string.Empty, - }; - - var metaFile = new FileInfo( Path.Combine( newDir.FullName, "meta.json" ) ); - modMeta.SaveToFile( metaFile ); - _modManager.AddMod( newDir ); - ModFileSystem.InvokeChange(); + var newDir = _modManager.GenerateEmptyMod(newName); SelectModOnUpdate( newDir.Name ); } catch( Exception e ) From 6c9e178058aa003a1e53c41b9cb04235636651f8 Mon Sep 17 00:00:00 2001 From: Brenden Reeves Date: Sun, 10 Oct 2021 19:48:16 -0500 Subject: [PATCH 4/7] Added Postman Collection for HTTP API --- HTTP_API.postman_collection.json | 96 ++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 HTTP_API.postman_collection.json diff --git a/HTTP_API.postman_collection.json b/HTTP_API.postman_collection.json new file mode 100644 index 000000000..c5634ba85 --- /dev/null +++ b/HTTP_API.postman_collection.json @@ -0,0 +1,96 @@ +{ + "info": { + "_postman_id": "791e3480-5ea2-4366-a39a-8b8126305691", + "name": "Penumbra API", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Get Mods", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:42069/api/mods?activeOnly=true", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "42069", + "path": [ + "api", + "mods" + ], + "query": [ + { + "key": "activeOnly", + "value": "true" + }, + { + "key": "collection", + "value": "sfdgd", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Delete Mod", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "http://localhost:42069/api/mods/delete?name=Hello World", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "42069", + "path": [ + "api", + "mods", + "delete" + ], + "query": [ + { + "key": "name", + "value": "Hello World" + } + ] + } + }, + "response": [] + }, + { + "name": "Create Mod", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\": \"Hello World\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:42069/api/mods", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "42069", + "path": [ + "api", + "mods" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file From b554041b880677f6f10daedb6fa5f7b5ef631efe Mon Sep 17 00:00:00 2001 From: Brenden Reeves Date: Mon, 18 Oct 2021 23:31:29 -0500 Subject: [PATCH 5/7] Cleaned Up API and Added Comments --- Penumbra.sln | 1 + Penumbra/API/ModsController.cs | 60 +++++++++++++++++++--------------- Penumbra/Api/IPenumbraApi.cs | 3 +- Penumbra/Mods/ModManager.cs | 18 ++++++++++ 4 files changed, 54 insertions(+), 28 deletions(-) diff --git a/Penumbra.sln b/Penumbra.sln index 58df4bf55..bbde9545a 100644 --- a/Penumbra.sln +++ b/Penumbra.sln @@ -8,6 +8,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F89C9EAE-25C8-43BE-8108-5921E5A93502}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + .gitignore = .gitignore EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "Penumbra.GameData\Penumbra.GameData.csproj", "{EE551E87-FDB3-4612-B500-DC870C07C605}" diff --git a/Penumbra/API/ModsController.cs b/Penumbra/API/ModsController.cs index 4effac847..8c8fdc9c1 100644 --- a/Penumbra/API/ModsController.cs +++ b/Penumbra/API/ModsController.cs @@ -13,35 +13,33 @@ namespace Penumbra.Api { public class ModsController : WebApiController { - private readonly Penumbra _penumbra; - private readonly ModManager _modManager; + private readonly Penumbra _penumbra; + private readonly ModManager _modManager; - public ModsController( Penumbra penumbra, ModManager modManager ) + public ModsController( Penumbra penumbra, ModManager modManager) { _penumbra = penumbra; _modManager = modManager; } + /// + /// Returns a list of all mods. Query params can be supplied to refine this list + /// + /// Returns only enabled mods if true + /// Restricts the results to the specified collection if not null [Route( HttpVerbs.Get, "/mods" )] - public object? GetMods() + public object? GetMods([QueryField] string collection, [QueryField] bool activeOnly) { - // Obtain Query Data - var activeOnly = Convert.ToBoolean( Request.QueryString[ "activeOnly" ] ); - var requestedCollection = Request.QueryString[ "collection" ]; - - var collection = _modManager.Collections.CurrentCollection; - - if( !string.IsNullOrWhiteSpace(requestedCollection) ) + var requestedCollection = string.IsNullOrWhiteSpace(collection) + ? _modManager.Collections.CurrentCollection + : _modManager.GetModCollection(collection); + if( requestedCollection is null ) { - collection = _modManager.Collections.Collections[ requestedCollection ]; - if( collection.Cache is null ) - { - PluginLog.Log($"Collection {requestedCollection} has been requested but cash is null. Generating cache now...."); - _modManager.Collections.AddCache(collection); - } + PluginLog.LogError("Unable to find any collections. Please ensure penumbra has at least one collection."); + return false; } - var mods = collection.Cache?.AvailableMods.Values.Select( x => new + var mods = requestedCollection.Cache?.AvailableMods.Values.Select( x => new { x.Settings.Enabled, x.Settings.Priority, @@ -51,7 +49,7 @@ public ModsController( Penumbra penumbra, ModManager modManager ) Files = x.Data.Resources.ModFiles.Select( fi => fi.FullName ), } ); - if( Convert.ToBoolean( Request.QueryString[ "activeOnly" ] ) ) + if( activeOnly ) { mods = mods?.Where( m => m.Enabled ); } @@ -59,6 +57,10 @@ public ModsController( Penumbra penumbra, ModManager modManager ) return mods; } + /// + /// Creates an empty mod based on the form data posted to this endpoint + /// + /// Name of the created mod [Route( HttpVerbs.Post, "/mods" )] public async Task< string > CreateMod() { @@ -71,24 +73,25 @@ public async Task< string > CreateMod() .Value.BasePath.Name; } + /// + /// Deletes a mod from the user's mod folder + /// + /// Name of the mod to be deleted + /// Boolean reflecting if the mod was deleted successfully [Route(HttpVerbs.Post, "/mods/delete")] - public async Task< bool > DeleteMod() + public bool DeleteMod([QueryField] string name) { - var requestData = await HttpContext.GetRequestFormDataAsync(); - var modName = Request.QueryString[ "name" ]; - - if( string.IsNullOrWhiteSpace(modName) ) + if( string.IsNullOrWhiteSpace(name) ) { return false; } - PluginLog.Log($"Attempting to delete mod: {modName}"); + PluginLog.Log($"Attempting to delete mod: {name}"); try { - var mod = _modManager.Mods[ modName ]; + var mod = _modManager.Mods[ name ]; _modManager.DeleteMod( mod.BasePath ); ModFileSystem.InvokeChange(); - return true; } catch @@ -97,6 +100,9 @@ public async Task< bool > DeleteMod() } } + /// + /// Get a list of files that have been modified by Penumbra + /// [Route( HttpVerbs.Get, "/files" )] public object GetFiles() { diff --git a/Penumbra/Api/IPenumbraApi.cs b/Penumbra/Api/IPenumbraApi.cs index 92d9ef3d0..138da0c12 100644 --- a/Penumbra/Api/IPenumbraApi.cs +++ b/Penumbra/Api/IPenumbraApi.cs @@ -1,6 +1,7 @@ using Dalamud.Game.ClientState.Objects.Types; using Lumina.Data; using Penumbra.GameData.Enums; +using Penumbra.Mods; namespace Penumbra.Api { @@ -20,7 +21,7 @@ public interface IPenumbraApi : IPenumbraApiBase public event ChangedItemHover? ChangedItemTooltip; // Triggered when the user clicks a listed changed object in a mod tab. public event ChangedItemClick? ChangedItemClicked; - + // Queue redrawing of all actors of the given name with the given RedrawType. public void RedrawObject( string name, RedrawType setting ); diff --git a/Penumbra/Mods/ModManager.cs b/Penumbra/Mods/ModManager.cs index 97ef8d0ee..a8b351e05 100644 --- a/Penumbra/Mods/ModManager.cs +++ b/Penumbra/Mods/ModManager.cs @@ -361,6 +361,24 @@ public bool UpdateMod( ModData mod, bool reloadMeta = false, bool recomputeMeta return true; } + public ModCollection? GetModCollection( string collectionName ) + { + if ( Collections.Collections.ContainsKey(collectionName) ) + { + var requestedCollection = Collections.Collections[ collectionName ]; + if( requestedCollection.Cache is null ) + { + PluginLog.Log($"Collection {collectionName} has been requested but cache is null. Generating cache now...."); + Collections.AddCache(requestedCollection); + } + return requestedCollection; + } + else + { + return null; + } + } + public string? ResolveSwappedOrReplacementPath( GamePath gameResourcePath ) { var ret = Collections.ActiveCollection.ResolveSwappedOrReplacementPath( gameResourcePath ); From fdc8d09f5b772ccd5c5c1827a8130a330dd3947c Mon Sep 17 00:00:00 2001 From: Brenden Reeves Date: Tue, 19 Oct 2021 01:34:34 -0500 Subject: [PATCH 6/7] Added Method to Get Changed Items in HTTP API --- Penumbra/API/ModsController.cs | 58 +++++++++++++++++++++++++++++++++- Penumbra/Api/IPenumbraApi.cs | 4 +++ Penumbra/Api/PenumbraApi.cs | 37 ++++++++++++++++++++++ Penumbra/Penumbra.cs | 2 +- 4 files changed, 99 insertions(+), 2 deletions(-) diff --git a/Penumbra/API/ModsController.cs b/Penumbra/API/ModsController.cs index 8c8fdc9c1..773291563 100644 --- a/Penumbra/API/ModsController.cs +++ b/Penumbra/API/ModsController.cs @@ -6,6 +6,8 @@ using EmbedIO; using EmbedIO.Routing; using EmbedIO.WebApi; +using Lumina.Data.Parsing; +using Lumina.Excel.GeneratedSheets; using Penumbra.Api.Models; using Penumbra.Mods; @@ -14,12 +16,14 @@ namespace Penumbra.Api public class ModsController : WebApiController { private readonly Penumbra _penumbra; + private readonly PenumbraApi _api; private readonly ModManager _modManager; - public ModsController( Penumbra penumbra, ModManager modManager) + public ModsController( Penumbra penumbra, ModManager modManager, PenumbraApi api ) { _penumbra = penumbra; _modManager = modManager; + _api = api; } /// @@ -100,6 +104,58 @@ public bool DeleteMod([QueryField] string name) } } + /// + /// Obtains a list of items that have been modded. By default this will search the active collections only. + /// + /// + /// Name of the collection(s) you would like to limit the search to. Search multiple collections by adding commas + /// + [Route( HttpVerbs.Get, "/mods/items" )] + public object? GetChangedItems([QueryField] string collections) + { + Dictionary< string, ushort > changedItems = new (); + List< string > requestedCollections = new (); + + if( string.IsNullOrWhiteSpace( collections ) ) + { + requestedCollections.Add(_modManager.Collections.CurrentCollection.Name); + } + else if( collections.Contains( "," ) ) + { + requestedCollections = collections.Split( ',' ).ToList(); + } + else if( collections == "all" ) + { + foreach( var collection in _modManager.Collections.Collections ) + { + requestedCollections.Add(collection.Key); + } + } + else + { + requestedCollections.Add(collections); + } + + foreach( var collection in requestedCollections ) + { + ModCollection? requestedCollection = _modManager.GetModCollection( collection.Trim() ); + if( requestedCollection is null ) + { + continue; + } + + var items = _api.GetChangedItemsForCollection( requestedCollection ); + foreach( var item in items ) + { + if( !changedItems.ContainsKey( item.Key ) ) + { + changedItems.Add( item.Key, item.Value ); + } + } + } + return changedItems; + } + /// /// Get a list of files that have been modified by Penumbra /// diff --git a/Penumbra/Api/IPenumbraApi.cs b/Penumbra/Api/IPenumbraApi.cs index 138da0c12..f6a48487e 100644 --- a/Penumbra/Api/IPenumbraApi.cs +++ b/Penumbra/Api/IPenumbraApi.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Dalamud.Game.ClientState.Objects.Types; using Lumina.Data; using Penumbra.GameData.Enums; @@ -44,5 +45,8 @@ public interface IPenumbraApi : IPenumbraApiBase // Try to load a given gamePath with the resolved path from Penumbra. public T? GetFile( string gamePath, string characterName ) where T : FileResource; + + // Gets a dictionary of effected items from a collection + public Dictionary< string, ushort > GetChangedItemsForCollection(ModCollection collection); } } \ No newline at end of file diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 51360940b..76fba8d3f 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -1,9 +1,13 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Logging; using Lumina.Data; +using Lumina.Data.Parsing; +using Lumina.Excel.GeneratedSheets; using Penumbra.GameData.Enums; using Penumbra.GameData.Util; using Penumbra.Mods; @@ -131,5 +135,38 @@ public string ResolvePath( string path, string characterName ) public T? GetFile< T >( string gamePath, string characterName ) where T : FileResource => GetFileIntern< T >( ResolvePath( gamePath, characterName ) ); + + public Dictionary< string, ushort > GetChangedItemsForCollection(ModCollection collection) + { + var modList = collection.Cache?.AvailableMods + .Where(mod => mod.Value.Data.ChangedItems.Count > 0 && mod.Value.Settings.Enabled) + .Select( mod => mod.Value ); + + var changedItems = new Dictionary< string, ushort >(); + if(modList is not null && modList.Count() > 0) + { + foreach( var mod in modList.Select(mod => mod.Data) ) + { + foreach( var (name, data) in mod.ChangedItems.Where( item => item.Value?.GetType() == typeof(Item)) ) + { + PluginLog.Log($"Found item {name}"); + if( !changedItems.ContainsKey( name ) ) + { + try + { + changedItems.Add(name, ( ( Quad )(data as Item).ModelMain ).A); + PluginLog.Log("Added"); + } + catch( Exception e ) + { + PluginLog.Error(e, "Unable to add item"); + } + } + } + } + } + + return changedItems; + } } } \ No newline at end of file diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 391e71f2d..34be7fe66 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -119,7 +119,7 @@ public void CreateWebServer() .WithMode( HttpListenerMode.EmbedIO ) ) .WithCors( prefix ) .WithWebApi( "/api", m => m - .WithController( () => new ModsController( this, Service< ModManager >.Get() ) ) ); + .WithController( () => new ModsController( this, Service< ModManager >.Get(), Api ) ) ); _webServer.StateChanged += ( s, e ) => PluginLog.Information( $"WebServer New State - {e.NewState}" ); From cdc8165a712ccc0f58cb2efb15f610e068526a43 Mon Sep 17 00:00:00 2001 From: Brenden Reeves Date: Tue, 19 Oct 2021 01:42:57 -0500 Subject: [PATCH 7/7] Updated Postman for HTTP API --- HTTP_API.postman_collection.json | 52 ++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/HTTP_API.postman_collection.json b/HTTP_API.postman_collection.json index c5634ba85..8157a251d 100644 --- a/HTTP_API.postman_collection.json +++ b/HTTP_API.postman_collection.json @@ -11,7 +11,7 @@ "method": "GET", "header": [], "url": { - "raw": "http://localhost:42069/api/mods?activeOnly=true", + "raw": "http://localhost:42069/api/mods?activeOnly=true&collection=Khal", "protocol": "http", "host": [ "localhost" @@ -28,8 +28,54 @@ }, { "key": "collection", - "value": "sfdgd", - "disabled": true + "value": "Khal" + } + ] + } + }, + "response": [] + }, + { + "name": "Get Affected Files", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:42069/api/files", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "42069", + "path": [ + "api", + "files" + ] + } + }, + "response": [] + }, + { + "name": "Get Affected Items", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:42069/api/mods/items?collections=all", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "42069", + "path": [ + "api", + "mods", + "items" + ], + "query": [ + { + "key": "collections", + "value": "all" } ] }