Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .idea/.idea.Penumbra/.idea/indexLayout.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

142 changes: 142 additions & 0 deletions HTTP_API.postman_collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
{
"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&collection=Khal",
"protocol": "http",
"host": [
"localhost"
],
"port": "42069",
"path": [
"api",
"mods"
],
"query": [
{
"key": "activeOnly",
"value": "true"
},
{
"key": "collection",
"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"
}
]
}
},
"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": []
}
]
}
1 change: 1 addition & 0 deletions Penumbra.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
7 changes: 7 additions & 0 deletions Penumbra/API/Models/NewMod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Penumbra.Api.Models
{
public class NewMod
{
public string name { get; set; }
}
}
163 changes: 143 additions & 20 deletions Penumbra/API/ModsController.cs
Original file line number Diff line number Diff line change
@@ -1,45 +1,168 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dalamud.Logging;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using Lumina.Data.Parsing;
using Lumina.Excel.GeneratedSheets;
using Penumbra.Api.Models;
using Penumbra.Mods;
using Penumbra.Util;

namespace Penumbra.Api
{
public class ModsController : WebApiController
{
private readonly Penumbra _penumbra;
private readonly Penumbra _penumbra;
private readonly PenumbraApi _api;
private readonly ModManager _modManager;

public ModsController( Penumbra penumbra )
=> _penumbra = penumbra;
public ModsController( Penumbra penumbra, ModManager modManager, PenumbraApi api )
{
_penumbra = penumbra;
_modManager = modManager;
_api = api;
}

/// <summary>
/// Returns a list of all mods. Query params can be supplied to refine this list
/// </summary>
/// <param name="activeOnly">Returns only enabled mods if true</param>
/// <param name="collection">Restricts the results to the specified collection if not null</param>
[Route( HttpVerbs.Get, "/mods" )]
public object? GetMods()
public object? GetMods([QueryField] string collection, [QueryField] bool activeOnly)
{
var modManager = Service< ModManager >.Get();
return modManager.Collections.CurrentCollection.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 ),
} )
?? null;
var requestedCollection = string.IsNullOrWhiteSpace(collection)
? _modManager.Collections.CurrentCollection
: _modManager.GetModCollection(collection);
if( requestedCollection is null )
{
PluginLog.LogError("Unable to find any collections. Please ensure penumbra has at least one collection.");
return false;
}

var mods = requestedCollection.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( activeOnly )
{
mods = mods?.Where( m => m.Enabled );
}

return mods;
}

/// <summary>
/// Creates an empty mod based on the form data posted to this endpoint
/// </summary>
/// <returns>Name of the created mod</returns>
[Route( HttpVerbs.Post, "/mods" )]
public object CreateMod()
=> new { };
public async Task< string > CreateMod()
{
var requestData = await HttpContext.GetRequestDataAsync<NewMod>();
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;
}

/// <summary>
/// Deletes a mod from the user's mod folder
/// </summary>
/// <param name="name">Name of the mod to be deleted</param>
/// <returns>Boolean reflecting if the mod was deleted successfully</returns>
[Route(HttpVerbs.Post, "/mods/delete")]
public bool DeleteMod([QueryField] string name)
{
if( string.IsNullOrWhiteSpace(name) )
{
return false;
}

PluginLog.Log($"Attempting to delete mod: {name}");
try
{
var mod = _modManager.Mods[ name ];
_modManager.DeleteMod( mod.BasePath );
ModFileSystem.InvokeChange();
return true;
}
catch
{
return false;
}
}

/// <summary>
/// Obtains a list of items that have been modded. By default this will search the active collections only.
/// </summary>
/// <param name="collections">
/// Name of the collection(s) you would like to limit the search to. Search multiple collections by adding commas
/// </param>
[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;
}

/// <summary>
/// Get a list of files that have been modified by Penumbra
/// </summary>
[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
)
Expand Down
7 changes: 6 additions & 1 deletion Penumbra/Api/IPenumbraApi.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using Dalamud.Game.ClientState.Objects.Types;
using Lumina.Data;
using Penumbra.GameData.Enums;
using Penumbra.Mods;

namespace Penumbra.Api
{
Expand All @@ -20,7 +22,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 );

Expand All @@ -43,5 +45,8 @@ public interface IPenumbraApi : IPenumbraApiBase

// Try to load a given gamePath with the resolved path from Penumbra.
public T? GetFile<T>( string gamePath, string characterName ) where T : FileResource;

// Gets a dictionary of effected items from a collection
public Dictionary< string, ushort > GetChangedItemsForCollection(ModCollection collection);
}
}
Loading