diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b8330d0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*.cs] +# Core formatting rules +indent_style = space +indent_size = 4 + +# --- LINTING RULES --- + +# IDE0005: Remove unnecessary using directives +# Options: suggestion (dots), warning (yellow squiggly), error (red squiggly/build fails) +dotnet_diagnostic.IDE0005.severity = warning + +# IDE0001: Simplify names (e.g., use 'string' instead of 'System.String') +dotnet_diagnostic.IDE0001.severity = warning + +# CS8019: Unnecessary using directive (Compiler specific rule) +dotnet_diagnostic.CS8019.severity = warning \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 313b50a..1969669 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,48 +2,31 @@ "version": "0.2.0", "configurations": [ { - "name": "πŸš€ Build, Deploy & Launch", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "[1.5] Build and Launch", - "program": "dotnet", - "args": [], - "cwd": "${workspaceFolder}", - "console": "internalConsole", - "stopAtEntry": false - }, - { - "name": "πŸ”¨ Build & Deploy 1.5", + "name": "▢️ Launch RimWorld [1.5]", "type": "coreclr", "request": "launch", - "preLaunchTask": "[1.5] Build and Deploy", - "program": "dotnet", - "args": [], + "preLaunchTask": "πŸš€ Deploy [1.5]", + "program": "${config:rimworld.paths.v15}", + "args": [ + // Tip: Add "-savedatafolder=${workspaceFolder}/.rimworld-save" here + // to keep your dev colony saves isolated from your actual game saves + ], "cwd": "${workspaceFolder}", "console": "internalConsole", - "stopAtEntry": false + "stopAtEntry": false, + "requireExactSource": false }, { - "name": "πŸ”₯ [1.6] Build, Launch", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "[1.6] Build and Launch", - "program": "dotnet", - "args": [], - "cwd": "${workspaceFolder}", - "console": "internalConsole", - "stopAtEntry": false - }, - { - "name": "πŸ”¨ [1.6] Build", + "name": "▢️ Launch RimWorld [1.6]", "type": "coreclr", "request": "launch", - "preLaunchTask": "[1.6] Build and Deploy", - "program": "dotnet", + "preLaunchTask": "πŸš€ Deploy [1.6]", + "program": "${config:rimworld.paths.v16}", "args": [], "cwd": "${workspaceFolder}", "console": "internalConsole", - "stopAtEntry": false - } + "stopAtEntry": false, + "requireExactSource": false + }, ] -} +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 965293e..7f989f2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { - "dotnet.defaultSolution": "RimApi.sln", + "dotnet.defaultSolution": "Source/RIMAPI/RimApi.sln", + "dotnet.preferCSharpExtension": true, "files.exclude": { "**/bin": true, "**/obj": true, @@ -9,5 +10,13 @@ "**/bin": true, "**/obj": true }, - "dotnet.preferCSharpExtension": true + + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + }, + + // --- RIMWORLD ENVIRONMENT VARIABLES --- + "rimworld.paths.v15": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\RimWorld\\RimWorldWin64.exe", + "rimworld.paths.v16": "E:\\Games\\RimWorld\\RimWorldWin64.exe", + "rimworld.mod.deployPath": "E:\\Program Files\\Steam\\steamapps\\common\\RimWorld\\Mods\\3593423732" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a8cedc5..074677f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,127 +1,71 @@ { "version": "2.0.0", "tasks": [ + // --- 1.5 TASKS --- { - "label": "[1.5] Build and Deploy", + "label": "πŸ”¨ Build [1.5]", "command": "dotnet", "type": "process", "args": [ "build", "${workspaceFolder}/Source/RIMAPI/RimApi.csproj", "--configuration", "Release-1.5", - "--verbosity", "normal", + "--verbosity", "minimal", "/p:CopyLocalLockFileAssemblies=true" ], "group": "build", - "presentation": { - "echo": true, - "reveal": "always", - "focus": false, - "panel": "shared", - "showReuseMessage": false, - "clear": true - }, "problemMatcher": "$msCompile" }, { - "label": "[1.6] Build and Deploy", + "label": "πŸš€ Deploy [1.5]", + "type": "shell", + "command": "xcopy", + "args": [ + "/Y", "/E", "/I", "/Q", + "${workspaceFolder}\\1.5\\Assemblies\\*", + "${config:rimworld.mod.deployPath}\\1.5\\Assemblies\\" + ], + "dependsOn": "πŸ”¨ Build [1.5]", + "presentation": { "reveal": "silent" } + }, + + // --- 1.6 TASKS --- + { + "label": "πŸ”¨ Build [1.6]", "command": "dotnet", "type": "process", "args": [ "build", "${workspaceFolder}/Source/RIMAPI/RimApi.csproj", "--configuration", "Release-1.6", - "--verbosity", "normal", + "--verbosity", "minimal", "/p:CopyLocalLockFileAssemblies=true" ], "group": "build", - "presentation": { - "echo": true, - "reveal": "always", - "focus": false, - "panel": "shared", - "showReuseMessage": false, - "clear": true - }, "problemMatcher": "$msCompile" }, { - "label": "clean-build", + "label": "πŸš€ Deploy [1.6]", + "type": "shell", + "command": "xcopy", + "args": [ + "/Y", "/E", "/I", "/Q", + "${workspaceFolder}\\1.6\\Assemblies\\*", + "${config:rimworld.mod.deployPath}\\1.6\\Assemblies\\" + ], + "dependsOn": "πŸ”¨ Build [1.6]", + "presentation": { "reveal": "silent" } + }, + + // --- UTILITY --- + { + "label": "🧹 Clean", "command": "dotnet", "type": "process", "args": [ "clean", "${workspaceFolder}/Source/RIMAPI/RimApi.csproj" - ], - "group": "build" - }, - { - "label": "[1.5] Launch RimWorld", - "type": "shell", - "command": "cmd", - "args": [ - "/c", - "start", - "\"E:\\Games\\RimWorld\\RimWorldWin64.exe\"" - ], - "group": "build", - "presentation": { - "echo": false, - "reveal": "silent" - } - }, - { - "label": "[1.6] Launch RimWorld", - "type": "process", - "command": "E:\\Program Files\\Steam\\steamapps\\common\\RimWorld\\RimWorldWin64.exe", - "group": "build", - "presentation": { - "echo": false, - "reveal": "silent" - } - }, - { - "label": "[1.5] Build and Launch", - "dependsOrder": "sequence", - "dependsOn": ["[1.5] Build and Deploy", "[1.5] Launch RimWorld"], - "group": "build", - "presentation": { - "echo": true, - "reveal": "always", - "focus": false, - "panel": "shared", - "clear": true - } - }, - { - "label": "[1.6] Copy To Mods", - "type": "shell", - "command": "xcopy", - "args": [ - "/Y", - "/E", - "/I", - "${workspaceFolder}\\1.6\\Assemblies\\*", - "E:\\Program Files\\Steam\\steamapps\\common\\RimWorld\\Mods\\3593423732\\1.6\\Assemblies\\" - ], - "group": "build", - "presentation": { - "echo": false, - "reveal": "silent" - } - }, - { - "label": "[1.6] Build and Launch", - "dependsOrder": "sequence", - "dependsOn": ["[1.6] Build and Deploy", "[1.6] Copy To Mods", "[1.6] Launch RimWorld"], - "group": "build", - "presentation": { - "echo": true, - "reveal": "always", - "focus": false, - "panel": "shared", - "clear": true - } + ] } ] } \ No newline at end of file diff --git a/Source/RIMAPI/RIMAPI_GameComponent.cs b/Source/RIMAPI/RIMAPI_GameComponent.cs index 6b18356..25bae2c 100644 --- a/Source/RIMAPI/RIMAPI_GameComponent.cs +++ b/Source/RIMAPI/RIMAPI_GameComponent.cs @@ -1,6 +1,5 @@ ο»Ώusing System; using RIMAPI.Core; -using UnityEngine; using Verse; namespace RIMAPI diff --git a/Source/RIMAPI/RimApi.sln b/Source/RIMAPI/RimApi.sln index a83ac97..57e2f95 100644 --- a/Source/RIMAPI/RimApi.sln +++ b/Source/RIMAPI/RimApi.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RimApi", "RimApi.csproj", "{A077C903-8CBC-474C-9D24-877D06ADECC8}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RimApi", "RimApi.csproj", "{7C37F0D8-B533-4F83-A559-00503E1DC46F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,18 +15,18 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Debug|x64.ActiveCfg = Debug|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Debug|x64.Build.0 = Debug|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Debug|x86.ActiveCfg = Debug|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Debug|x86.Build.0 = Debug|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Release|Any CPU.Build.0 = Release|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Release|x64.ActiveCfg = Release|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Release|x64.Build.0 = Release|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Release|x86.ActiveCfg = Release|Any CPU - {A077C903-8CBC-474C-9D24-877D06ADECC8}.Release|x86.Build.0 = Release|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Debug|x64.ActiveCfg = Debug|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Debug|x64.Build.0 = Debug|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Debug|x86.ActiveCfg = Debug|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Debug|x86.Build.0 = Debug|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Release|Any CPU.Build.0 = Release|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Release|x64.ActiveCfg = Release|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Release|x64.Build.0 = Release|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Release|x86.ActiveCfg = Release|Any CPU + {7C37F0D8-B533-4F83-A559-00503E1DC46F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/ServerCacheController.cs b/Source/RIMAPI/RimworldRestApi/BaseControllers/ServerCacheController.cs index 1d6d16a..5120c92 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/ServerCacheController.cs +++ b/Source/RIMAPI/RimworldRestApi/BaseControllers/ServerCacheController.cs @@ -2,9 +2,6 @@ using System.Net; using RIMAPI.Core; using RIMAPI.Http; -using RIMAPI.Models; -using RIMAPI.Services; -using System; namespace RIMAPI.Controllers { diff --git a/Source/RIMAPI/RimworldRestApi/Controllers/AI/.info.meta b/Source/RIMAPI/RimworldRestApi/Controllers/AI/.info.meta new file mode 100644 index 0000000..5d187c7 --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Controllers/AI/.info.meta @@ -0,0 +1,7 @@ +Domain: AI + +Description: +Handles artificial intelligence systems, group behaviors, and automated decision-making processes. + +Controllers: +- LordController: Manages 'Lords' (group AI) which dictate the behavior of raids, caravans, parties, and other coordinated entity gatherings. \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/LordController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/AI/LordController.cs similarity index 98% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/LordController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/AI/LordController.cs index 111e98c..eb1c316 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/LordController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/AI/LordController.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using System.Net; using RIMAPI.Core; -using RIMAPI.Http; using RIMAPI.Models; using RIMAPI.Services; diff --git a/Source/RIMAPI/RimworldRestApi/Controllers/Client/.info.meta b/Source/RIMAPI/RimworldRestApi/Controllers/Client/.info.meta new file mode 100644 index 0000000..3931391 --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Client/.info.meta @@ -0,0 +1,13 @@ +Domain: Client + +Description: +Handles all interactions with the local player's visual perspective, user interface, and engine rendering. + +Controllers: +- CameraController: Manipulates the viewport, zoom levels, and captures screenshots. +- ImagesController: Extracts game textures, materials, and renders visual assets. +- OverlayController: Manages drawing custom graphics, labels, and lines on top of the game world. +- UIController: Reads game alerts, messages, active windows, and menus. + +Dependencies: +Requires execution on the main Unity thread (GameThreadUtility) for all rendering and IMGUI-layer calls. \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/CameraController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Client/CameraController.cs similarity index 98% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/CameraController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Client/CameraController.cs index ade49d4..339c989 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/CameraController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Client/CameraController.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using System.Net; using System.Threading.Tasks; using RIMAPI.CameraStreamer; @@ -8,7 +6,6 @@ using RIMAPI.Models; using RIMAPI.Models.Camera; using RIMAPI.Services; -using Verse; namespace RimworldRestApi.Controllers { diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/ImagesController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Client/ImagesController.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/ImagesController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Client/ImagesController.cs diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/OverlayController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Client/OverlayController.cs similarity index 97% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/OverlayController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Client/OverlayController.cs index 0e1b2d6..1da8421 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/OverlayController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Client/OverlayController.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using System.Net; using RIMAPI.Core; -using RIMAPI.Http; using RIMAPI.Models; using RIMAPI.Services; diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/WindowController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Client/UIController.cs similarity index 77% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/WindowController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Client/UIController.cs index 01e42da..3cd7790 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/WindowController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Client/UIController.cs @@ -1,18 +1,20 @@ using System.Threading.Tasks; using System.Net; using RIMAPI.Core; -using RIMAPI.Http; -using RIMAPI.Models; +using RIMAPI.Services.Interfaces; using RIMAPI.Services; +using RIMAPI.Models; -namespace RIMAPI.Controllers +namespace RIMAPI.BaseControllers { - public class WindowController + public class UIController { + private readonly IUIService _uiService; private readonly IWindowService _windowService; - public WindowController(IWindowService windowService) + public UIController(IUIService uiService, IWindowService windowService) { + _uiService = uiService; _windowService = windowService; } diff --git a/Source/RIMAPI/RimworldRestApi/Controllers/Colony/.info.meta b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/.info.meta new file mode 100644 index 0000000..b6d3454 --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/.info.meta @@ -0,0 +1,13 @@ +Domain: Colony + +Description: +Manages abstract game mechanics, colony-wide systems, and base management tasks. + +Controllers: +- BillController: Manages crafting recipes and production rules on workbenches. +- BuilderController: Handles blueprint placement and construction logic. +- ColonistsWorkController: Manages the work tab, priorities, and allowed areas. +- GameEventsController: Triggers or monitors incidents, raids, drop pods, and quests (Storyteller interactions). +- OrderController: Issues player designations (e.g., mine, chop wood, harvest, haul). +- ResearchController: Manages the research tree, current projects, and technological unlocks. +- TradeController: Handles orbital trade ships, caravans, and economic exchanges. \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/BillController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/BillController.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/BillController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Colony/BillController.cs diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/BuilderController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/BuilderController.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/BuilderController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Colony/BuilderController.cs diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/ColonistsWorkController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/ColonistsWorkController.cs similarity index 86% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/ColonistsWorkController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Colony/ColonistsWorkController.cs index 4183d57..4beb881 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/ColonistsWorkController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/ColonistsWorkController.cs @@ -1,8 +1,4 @@ -using System; -using System.Net; -using System.Threading.Tasks; using RIMAPI.Core; -using RIMAPI.Http; using RIMAPI.Services; namespace RIMAPI.Controllers diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/GameEventsController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/GameEventsController.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/GameEventsController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Colony/GameEventsController.cs diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/OrderController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/OrderController.cs similarity index 97% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/OrderController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Colony/OrderController.cs index 1ed41b8..c65403e 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/OrderController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/OrderController.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using System.Net; using RIMAPI.Core; -using RIMAPI.Http; using RIMAPI.Models; using RIMAPI.Services; diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/ResearchController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/ResearchController.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/ResearchController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Colony/ResearchController.cs index ee26d7d..50ca1c2 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/ResearchController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/ResearchController.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using RIMAPI.Core; using RIMAPI.Http; -using RIMAPI.Models; using RIMAPI.Services; namespace RIMAPI.Controllers diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/TradeController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/TradeController.cs similarity index 96% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/TradeController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Colony/TradeController.cs index 99d5d6a..c71af51 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/TradeController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Colony/TradeController.cs @@ -1,8 +1,6 @@ using System.Threading.Tasks; using System.Net; using RIMAPI.Core; -using RIMAPI.Http; -using RIMAPI.Models; using RIMAPI.Services; using System; diff --git a/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/.info.meta b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/.info.meta new file mode 100644 index 0000000..051ddbf --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/.info.meta @@ -0,0 +1,11 @@ +Domain: Pawns + +Description: +Handles all interactions, mechanics, and data related to individual entities (colonists, animals, enemies). + +Controllers: +- PawnController: Core mechanics including health, needs, thoughts, and inventory management. +- PawnEditController: Modifies pawn traits, skills, appearance, and backstories. +- PawnInfoController: Retrieves detailed readouts, statistics, and records for specific pawns. +- PawnJobController: Monitors and interrupts the current actions/jobs a pawn is performing. +- PawnSpawnController: Handles generating and safely placing new pawns into the world. \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/PawnController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnController.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/PawnController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnController.cs index da23f57..3ca92c6 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/PawnController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnController.cs @@ -1,4 +1,3 @@ -using System; using System.Net; using System.Threading.Tasks; using RIMAPI.Core; diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/PawnEditController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnEditController.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/PawnEditController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnEditController.cs index 6e465a0..839b2d2 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/PawnEditController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnEditController.cs @@ -1,8 +1,6 @@ -using System; using System.Net; using System.Threading.Tasks; using RIMAPI.Core; -using RIMAPI.Http; using RIMAPI.Models; using RIMAPI.Services; diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/PawnInfoController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnInfoController.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/PawnInfoController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnInfoController.cs diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/PawnJobController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnJobController.cs similarity index 98% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/PawnJobController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnJobController.cs index a629510..b93a16d 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/PawnJobController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnJobController.cs @@ -1,7 +1,6 @@ using System.Net; using System.Threading.Tasks; using RIMAPI.Core; -using RIMAPI.Http; using RIMAPI.Models; using RIMAPI.Services; diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/PawnSpawnController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnSpawnController.cs similarity index 95% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/PawnSpawnController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnSpawnController.cs index b75142d..9b1fe8a 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/PawnSpawnController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/Pawns/PawnSpawnController.cs @@ -1,8 +1,6 @@ -using System; using System.Net; using System.Threading.Tasks; using RIMAPI.Core; -using RIMAPI.Http; using RIMAPI.Models; using RIMAPI.Services; diff --git a/Source/RIMAPI/RimworldRestApi/Controllers/System/.info.meta b/Source/RIMAPI/RimworldRestApi/Controllers/System/.info.meta new file mode 100644 index 0000000..0fc2bb6 --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Controllers/System/.info.meta @@ -0,0 +1,9 @@ +Domain: System + +Description: +Interfaces with core game engine states, infrastructure, and debugging tools. + +Controllers: +- DevToolsController: Provides access to debug actions, god mode, and cheat tools. +- GameController: Manages high-level state like saving/loading, time speed, and pausing/unpausing. +- ThingsController: Handles generic spawning, destroying, and management of items, buildings, and world objects (Things). \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/DevToolsController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/System/DevToolsController.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/DevToolsController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/System/DevToolsController.cs diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/GameController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/System/GameController.cs similarity index 86% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/GameController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/System/GameController.cs index c0f95d1..d3adfb0 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/GameController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/System/GameController.cs @@ -7,6 +7,7 @@ using RIMAPI.Http; using RIMAPI.Models; using RIMAPI.Services; +using RIMAPI.Services.Interfaces; using RimWorld; namespace RIMAPI.Controllers @@ -14,18 +15,30 @@ namespace RIMAPI.Controllers public class GameController { private readonly IGameStateService _gameStateService; + private readonly IGameDataService _gameDataService; private readonly RIMAPI_Settings _settings; private readonly ICachingService _cachingService; + private readonly IModService _modService; + private readonly IUIService _uiService; + private readonly ISelectionService _selectionService; public GameController( IGameStateService gameStateService, + IGameDataService gameDataService, RIMAPI_Settings settings, - ICachingService cachingService + ICachingService cachingService, + IModService modService, + IUIService uiService, + ISelectionService selectionService ) { _gameStateService = gameStateService; + _gameDataService = gameDataService; _settings = settings; _cachingService = cachingService; + _modService = modService; + _uiService = uiService; + _selectionService = selectionService; } [Get("/api/v1/version")] @@ -60,7 +73,7 @@ public async Task GetGameState(HttpListenerContext context) public async Task ConfigureMods(HttpListenerContext context) { var body = await context.Request.ReadBodyAsync(); - var result = _gameStateService.ConfigureMods(body); + var result = _modService.ConfigureMods(body); await context.SendJsonResponse(result); } @@ -72,7 +85,7 @@ public async Task GetModsInfo(HttpListenerContext context) await _cachingService.CacheAwareResponseAsync( context, "/api/v1/mods/info", - dataFactory: () => Task.FromResult(_gameStateService.GetModsInfo()), + dataFactory: () => Task.FromResult(_modService.GetModsInfo()), expiration: TimeSpan.FromMinutes(1), priority: CachePriority.Normal, expirationType: CacheExpirationType.Absolute @@ -84,7 +97,7 @@ await _cachingService.CacheAwareResponseAsync( public async Task SelectArea(HttpListenerContext context) { var body = await context.Request.ReadBodyAsync(); - var result = _gameStateService.SelectArea(body); + var result = _selectionService.SelectArea(body); await context.SendJsonResponse(result); } @@ -94,7 +107,7 @@ public async Task Select(HttpListenerContext context) { var objType = RequestParser.GetStringParameter(context, "type"); var id = RequestParser.GetIntParameter(context, "id"); - var result = _gameStateService.Select(objType, id); + var result = _selectionService.Select(objType, id); await context.SendJsonResponse(result); } @@ -102,7 +115,7 @@ public async Task Select(HttpListenerContext context) [EndpointMetadata("Clear game selection")] public async Task DeselectAll(HttpListenerContext context) { - var result = _gameStateService.DeselectAll(); + var result = _selectionService.DeselectAll(); await context.SendJsonResponse(result); } @@ -111,24 +124,7 @@ public async Task DeselectAll(HttpListenerContext context) public async Task OpenTab(HttpListenerContext context) { var tabName = RequestParser.GetStringParameter(context, "name"); - var result = _gameStateService.OpenTab(tabName); - await context.SendJsonResponse(result); - } - - [Get("/api/v1/datetime")] - [EndpointMetadata("Get in-game date and time")] - public async Task GetCurrentMapDatetime(HttpListenerContext context) - { - var result = _gameStateService.GetCurrentMapDatetime(); - await context.SendJsonResponse(result); - } - - [Get("/api/v1/datetime/tile")] - [EndpointMetadata("Get in-game date and time in global map tile")] - public async Task GetWorldTileDatetime(HttpListenerContext context) - { - var tileId = RequestParser.GetIntParameter(context, "tile_id"); - var result = _gameStateService.GetWorldTileDatetime(tileId); + var result = _uiService.OpenTab(tabName); await context.SendJsonResponse(result); } @@ -156,7 +152,7 @@ public async Task GetAllDefs(HttpListenerContext context) await _cachingService.CacheAwareResponseAsync( context, "/api/v1/def/all", - dataFactory: () => Task.FromResult(_gameStateService.GetAllDefs(body)), + dataFactory: () => Task.FromResult(_gameDataService.GetAllDefs(body)), expiration: TimeSpan.FromMinutes(5), priority: CachePriority.Normal, expirationType: CacheExpirationType.Absolute @@ -167,7 +163,7 @@ await _cachingService.CacheAwareResponseAsync( public async Task PostLetter(HttpListenerContext context) { var body = await context.Request.ReadBodyAsync(); - var result = _gameStateService.SendLetterSimple(body); + var result = _uiService.SendLetterSimple(body); await context.SendJsonResponse(result); } diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/ThingsController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/System/ThingsController.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/ThingsController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/System/ThingsController.cs diff --git a/Source/RIMAPI/RimworldRestApi/Controllers/World/.info.meta b/Source/RIMAPI/RimworldRestApi/Controllers/World/.info.meta new file mode 100644 index 0000000..333ab0d --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Controllers/World/.info.meta @@ -0,0 +1,9 @@ +Domain: World + +Description: +Handles the physical environments, grid systems, cell and planet-level map interactions. + +Controllers: +- FactionController: Manages relationships, alliances, hostility, and faction-level data. +- GlobalMapController: Interacts with the planet map (WorldLayer), world tiles, and traveling caravans. +- MapController: Manages the local play area, weather, temperature, terrain grids, and cell data. \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/FactionController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/World/FactionController.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/FactionController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/World/FactionController.cs diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/GlobalMapController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/World/GlobalMapController.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/GlobalMapController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/World/GlobalMapController.cs index c5c05b7..7ce2ce4 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/GlobalMapController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/World/GlobalMapController.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net; using System.Threading.Tasks; using RIMAPI.Core; diff --git a/Source/RIMAPI/RimworldRestApi/BaseControllers/MapController.cs b/Source/RIMAPI/RimworldRestApi/Controllers/World/MapController.cs similarity index 94% rename from Source/RIMAPI/RimworldRestApi/BaseControllers/MapController.cs rename to Source/RIMAPI/RimworldRestApi/Controllers/World/MapController.cs index 55930c7..d3756ee 100644 --- a/Source/RIMAPI/RimworldRestApi/BaseControllers/MapController.cs +++ b/Source/RIMAPI/RimworldRestApi/Controllers/World/MapController.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Net; using System.Threading.Tasks; using RIMAPI.Core; @@ -287,5 +286,23 @@ public async Task UpdateStockpile(HttpListenerContext context) var result = _mapService.UpdateStockpile(body); await context.SendJsonResponse(result); } + + [Get("/api/v1/datetime")] + [EndpointMetadata("Get in-game date and time")] + public async Task GetCurrentMapDatetime(HttpListenerContext context) + { + var result = _mapService.GetCurrentMapDatetime(); + await context.SendJsonResponse(result); + } + + [Get("/api/v1/datetime/tile")] + [EndpointMetadata("Get in-game date and time in global map tile")] + public async Task GetWorldTileDatetime(HttpListenerContext context) + { + var tileId = RequestParser.GetIntParameter(context, "tile_id"); + var result = _mapService.GetWorldTileDatetime(tileId); + await context.SendJsonResponse(result); + } + } } diff --git a/Source/RIMAPI/RimworldRestApi/Core/ApiServer.cs b/Source/RIMAPI/RimworldRestApi/Core/ApiServer.cs index 71b8693..dadecc6 100644 --- a/Source/RIMAPI/RimworldRestApi/Core/ApiServer.cs +++ b/Source/RIMAPI/RimworldRestApi/Core/ApiServer.cs @@ -113,7 +113,8 @@ private IServiceProvider CreateDefaultServiceProvider() services.AddSingleton(extensionRegistry); // Create instances first to avoid any DI issues - var gameStateService = new GameStateService(cachingService); + var gameDataService = new GameDataService(cachingService); + var gameStateService = new GameStateService(); var sseService = new SseService(gameStateService); var eventRegistry = new EventRegistry(sseService); diff --git a/Source/RIMAPI/RimworldRestApi/Core/Docs/DocumentationService.cs b/Source/RIMAPI/RimworldRestApi/Core/Docs/DocumentationService.cs index 0a9ec3d..23c2121 100644 --- a/Source/RIMAPI/RimworldRestApi/Core/Docs/DocumentationService.cs +++ b/Source/RIMAPI/RimworldRestApi/Core/Docs/DocumentationService.cs @@ -7,8 +7,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; -using RIMAPI; -using RIMAPI.Controllers; using RIMAPI.Models; namespace RIMAPI.Core diff --git a/Source/RIMAPI/RimworldRestApi/Core/Routing/ExtensionRouter.cs b/Source/RIMAPI/RimworldRestApi/Core/Routing/ExtensionRouter.cs index 2179394..71af18e 100644 --- a/Source/RIMAPI/RimworldRestApi/Core/Routing/ExtensionRouter.cs +++ b/Source/RIMAPI/RimworldRestApi/Core/Routing/ExtensionRouter.cs @@ -3,7 +3,6 @@ using System.Net; using System.Reflection; using System.Threading.Tasks; -using Verse; namespace RIMAPI.Core { diff --git a/Source/RIMAPI/RimworldRestApi/Core/SSE/EventPublisher.cs b/Source/RIMAPI/RimworldRestApi/Core/SSE/EventPublisher.cs index 904f5e7..62a328f 100644 --- a/Source/RIMAPI/RimworldRestApi/Core/SSE/EventPublisher.cs +++ b/Source/RIMAPI/RimworldRestApi/Core/SSE/EventPublisher.cs @@ -1,5 +1,4 @@ using System; -using RIMAPI.Core; namespace RIMAPI.Core { diff --git a/Source/RIMAPI/RimworldRestApi/Core/SSE/SseService.cs b/Source/RIMAPI/RimworldRestApi/Core/SSE/SseService.cs index 78e2350..55c3289 100644 --- a/Source/RIMAPI/RimworldRestApi/Core/SSE/SseService.cs +++ b/Source/RIMAPI/RimworldRestApi/Core/SSE/SseService.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Net; using System.Threading.Tasks; // Required for Task using Newtonsoft.Json; @@ -68,7 +67,6 @@ public void RegisterEventType(string eventType) } } - // --- UPDATED: Method signature changed from void to async Task --- public async Task HandleSSEConnection(HttpListenerContext context) { if (_disposed) diff --git a/Source/RIMAPI/RimworldRestApi/Helpers/BillHelper.cs b/Source/RIMAPI/RimworldRestApi/Helpers/BillHelper.cs index b28538e..0e71244 100644 --- a/Source/RIMAPI/RimworldRestApi/Helpers/BillHelper.cs +++ b/Source/RIMAPI/RimworldRestApi/Helpers/BillHelper.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using RIMAPI.Core; using RIMAPI.Models; using RimWorld; using Verse; diff --git a/Source/RIMAPI/RimworldRestApi/Helpers/ColonistsHelper.cs b/Source/RIMAPI/RimworldRestApi/Helpers/ColonistsHelper.cs index 0a6c655..17ca5f4 100644 --- a/Source/RIMAPI/RimworldRestApi/Helpers/ColonistsHelper.cs +++ b/Source/RIMAPI/RimworldRestApi/Helpers/ColonistsHelper.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using RIMAPI.Models; using RimWorld; -using UnityEngine; +using RIMAPI.Models; using Verse; namespace RIMAPI.Helpers diff --git a/Source/RIMAPI/RimworldRestApi/Helpers/FactionHelper.cs b/Source/RIMAPI/RimworldRestApi/Helpers/FactionHelper.cs index 67924cd..6d349a0 100644 --- a/Source/RIMAPI/RimworldRestApi/Helpers/FactionHelper.cs +++ b/Source/RIMAPI/RimworldRestApi/Helpers/FactionHelper.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; using System.Linq; -using RIMAPI.Models; using RimWorld; -using UnityEngine; using Verse; namespace RIMAPI.Helpers diff --git a/Source/RIMAPI/RimworldRestApi/Helpers/GamePlayHelper.cs b/Source/RIMAPI/RimworldRestApi/Helpers/GamePlayHelper.cs index df45b0a..55d2ff1 100644 --- a/Source/RIMAPI/RimworldRestApi/Helpers/GamePlayHelper.cs +++ b/Source/RIMAPI/RimworldRestApi/Helpers/GamePlayHelper.cs @@ -4,8 +4,8 @@ using RIMAPI.Models; using RimWorld; using UnityEngine; -using RimWorld.Planet; using Verse; +using RimWorld.Planet; namespace RIMAPI.Helpers { diff --git a/Source/RIMAPI/RimworldRestApi/Helpers/GlobalMapHelper.cs b/Source/RIMAPI/RimworldRestApi/Helpers/GlobalMapHelper.cs index 8f81699..6b1b06e 100644 --- a/Source/RIMAPI/RimworldRestApi/Helpers/GlobalMapHelper.cs +++ b/Source/RIMAPI/RimworldRestApi/Helpers/GlobalMapHelper.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.Linq; -using RimWorld.Planet; using RIMAPI.Models; -using UnityEngine; using Verse; namespace RIMAPI.Helpers diff --git a/Source/RIMAPI/RimworldRestApi/Helpers/PawnHelper.cs b/Source/RIMAPI/RimworldRestApi/Helpers/PawnHelper.cs index 266e2f5..905fdd7 100644 --- a/Source/RIMAPI/RimworldRestApi/Helpers/PawnHelper.cs +++ b/Source/RIMAPI/RimworldRestApi/Helpers/PawnHelper.cs @@ -3,7 +3,6 @@ using System.Linq; using RIMAPI.Models; using RimWorld; -using UnityEngine; using Verse; using Verse.AI; diff --git a/Source/RIMAPI/RimworldRestApi/Helpers/SiteHelper.cs b/Source/RIMAPI/RimworldRestApi/Helpers/SiteHelper.cs index 6f6beab..e62cfd6 100644 --- a/Source/RIMAPI/RimworldRestApi/Helpers/SiteHelper.cs +++ b/Source/RIMAPI/RimworldRestApi/Helpers/SiteHelper.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using RimWorld.Planet; using RIMAPI.Models; using Verse; diff --git a/Source/RIMAPI/RimworldRestApi/Hooks/GameStateHooks.cs b/Source/RIMAPI/RimworldRestApi/Hooks/GameStateHooks.cs index 679d092..328afa3 100644 --- a/Source/RIMAPI/RimworldRestApi/Hooks/GameStateHooks.cs +++ b/Source/RIMAPI/RimworldRestApi/Hooks/GameStateHooks.cs @@ -1,6 +1,5 @@ using System; using HarmonyLib; -using RimWorld; using Verse; namespace RimworldRestApi.Hooks diff --git a/Source/RIMAPI/RimworldRestApi/Hooks/WindowsHook.cs b/Source/RIMAPI/RimworldRestApi/Hooks/WindowsHook.cs index cec2925..2e3b8d2 100644 --- a/Source/RIMAPI/RimworldRestApi/Hooks/WindowsHook.cs +++ b/Source/RIMAPI/RimworldRestApi/Hooks/WindowsHook.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using HarmonyLib; using RIMAPI.Core; diff --git a/Source/RIMAPI/RimworldRestApi/Models/BuilderDtos.cs b/Source/RIMAPI/RimworldRestApi/Models/BuilderDtos.cs index 1f0d1da..d198219 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/BuilderDtos.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/BuilderDtos.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using RIMAPI.Models; namespace RIMAPI.Models { diff --git a/Source/RIMAPI/RimworldRestApi/Models/Def/DefDtos.cs b/Source/RIMAPI/RimworldRestApi/Models/Def/DefDtos.cs index be4905d..e2736d3 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/Def/DefDtos.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/Def/DefDtos.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using RimWorld; diff --git a/Source/RIMAPI/RimworldRestApi/Models/DesignateRequestDto.cs b/Source/RIMAPI/RimworldRestApi/Models/DesignateRequestDto.cs index 3f6eebf..861603b 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/DesignateRequestDto.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/DesignateRequestDto.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace RIMAPI.Models { public class DesignateRequestDto diff --git a/Source/RIMAPI/RimworldRestApi/Models/DevDto.cs b/Source/RIMAPI/RimworldRestApi/Models/DevDto.cs index 72c39f3..1b9a310 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/DevDto.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/DevDto.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; namespace RIMAPI.Models diff --git a/Source/RIMAPI/RimworldRestApi/Models/FactionsDto.cs b/Source/RIMAPI/RimworldRestApi/Models/FactionsDto.cs index 0a4d68e..3b1238a 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/FactionsDto.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/FactionsDto.cs @@ -1,7 +1,5 @@ -using System; using System.Collections.Generic; using RimWorld; -using Verse; namespace RIMAPI.Models { diff --git a/Source/RIMAPI/RimworldRestApi/Models/Game/GameActionsDto.cs b/Source/RIMAPI/RimworldRestApi/Models/Game/GameActionsDto.cs index 86c4c94..7882ab9 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/Game/GameActionsDto.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/Game/GameActionsDto.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; namespace RIMAPI.Models diff --git a/Source/RIMAPI/RimworldRestApi/Models/Game/GameDataSaveDto.cs b/Source/RIMAPI/RimworldRestApi/Models/Game/GameDataSaveDto.cs index 58bdf17..f9364e4 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/Game/GameDataSaveDto.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/Game/GameDataSaveDto.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace RIMAPI.Models { public class GameLoadRequestDto diff --git a/Source/RIMAPI/RimworldRestApi/Models/Game/GameEventsDto.cs b/Source/RIMAPI/RimworldRestApi/Models/Game/GameEventsDto.cs index 571d640..21d38fd 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/Game/GameEventsDto.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/Game/GameEventsDto.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Verse; using Verse.AI.Group; namespace RIMAPI.Models diff --git a/Source/RIMAPI/RimworldRestApi/Models/Game/GameStateDto.cs b/Source/RIMAPI/RimworldRestApi/Models/Game/GameStateDto.cs index 43b3583..6d7f214 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/Game/GameStateDto.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/Game/GameStateDto.cs @@ -1,5 +1,3 @@ -using System; - namespace RIMAPI.Models { public class GameStateDto diff --git a/Source/RIMAPI/RimworldRestApi/Models/Map/GlobalMapDto.cs b/Source/RIMAPI/RimworldRestApi/Models/Map/GlobalMapDto.cs index df76f86..7fe2dce 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/Map/GlobalMapDto.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/Map/GlobalMapDto.cs @@ -1,6 +1,4 @@ using System.Collections.Generic; -using RimWorld; -using Verse; namespace RIMAPI.Models { diff --git a/Source/RIMAPI/RimworldRestApi/Models/Pawns/PawnDtos.cs b/Source/RIMAPI/RimworldRestApi/Models/Pawns/PawnDtos.cs index 18435c8..b6ae70c 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/Pawns/PawnDtos.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/Pawns/PawnDtos.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace RIMAPI.Models { public class PawnDto diff --git a/Source/RIMAPI/RimworldRestApi/Models/SelectAreaRequestDto.cs b/Source/RIMAPI/RimworldRestApi/Models/SelectAreaRequestDto.cs index 1e41aec..8d0e220 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/SelectAreaRequestDto.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/SelectAreaRequestDto.cs @@ -1,5 +1,3 @@ -using RIMAPI.Models; - namespace RIMAPI.Models { public class SelectAreaRequestDto diff --git a/Source/RIMAPI/RimworldRestApi/Models/ThingsAtCellRequestDto.cs b/Source/RIMAPI/RimworldRestApi/Models/ThingsAtCellRequestDto.cs index 10b13fa..d99249d 100644 --- a/Source/RIMAPI/RimworldRestApi/Models/ThingsAtCellRequestDto.cs +++ b/Source/RIMAPI/RimworldRestApi/Models/ThingsAtCellRequestDto.cs @@ -1,5 +1,3 @@ -using RIMAPI.Models; - namespace RIMAPI.Models { public class ThingsAtCellRequestDto diff --git a/Source/RIMAPI/RimworldRestApi/Services/CameraService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/CameraService/CameraService.cs similarity index 86% rename from Source/RIMAPI/RimworldRestApi/Services/CameraService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Client/CameraService/CameraService.cs index 8f7c42b..8d78e6e 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/CameraService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Client/CameraService/CameraService.cs @@ -1,5 +1,7 @@ using System; using System.Collections; +using System.IO; +using System.Reflection; using System.Threading.Tasks; using RIMAPI.CameraStreamer; using RIMAPI.Core; @@ -30,6 +32,46 @@ public ApiResult ChangeZoom(int zoom) return ApiResult.Ok(); } + private static void SetScreenshotMode(bool state) + { + if (Find.UIRoot == null || Find.UIRoot.screenshotMode == null) return; + var handler = Find.UIRoot.screenshotMode; + +#if RIMWORLD_1_6 + handler.Active = state; +#else + // In 1.5, we must use Reflection to write to the private 'active' backing field + FieldInfo field = typeof(ScreenshotModeHandler).GetField("active", BindingFlags.NonPublic | BindingFlags.Instance); + if (field != null) + { + field.SetValue(handler, state); + } + else + { + // Fallback for auto-properties + PropertyInfo prop = typeof(ScreenshotModeHandler).GetProperty("Active", BindingFlags.Public | BindingFlags.Instance); + if (prop != null && prop.CanWrite) + { + prop.SetValue(handler, state, null); + } + } +#endif + } + + private static void TakeNativeScreenshotDirect(string fileName) + { + string folderPath = GenFilePaths.ScreenshotFolderPath; + + DirectoryInfo dir = new DirectoryInfo(folderPath); + if (!dir.Exists) + { + dir.Create(); + } + + string fullPath = Path.Combine(folderPath, fileName + ".png"); + ScreenCapture.CaptureScreenshot(fullPath); + } + public ApiResult TakeNativeScreenshot(NativeScreenshotRequestDto request) { if (Find.CurrentMap == null) @@ -62,20 +104,20 @@ private IEnumerator NativeScreenshotRoutine(NativeScreenshotRequestDto request) if (request.HideUI && Find.UIRoot != null) { originalUIState = Find.UIRoot.screenshotMode.Active; - Find.UIRoot.screenshotMode.Active = true; + SetScreenshotMode(true); } - // 3. Trigger the native screenshot tool + // Trigger the native screenshot tool string fileName = string.IsNullOrEmpty(request.FileName) ? $"RIMAPI_{DateTime.Now.Ticks}" : request.FileName; - ScreenshotTaker.TakeNonSteamShot(fileName); + TakeNativeScreenshotDirect(fileName); - // 4. WAIT for the frame to finish rendering so the screenshot actually captures! + // WAIT for the frame to finish rendering so the screenshot actually captures! yield return new WaitForEndOfFrame(); // 5. Restore UI if (request.HideUI && Find.UIRoot != null) { - Find.UIRoot.screenshotMode.Active = originalUIState; + SetScreenshotMode(originalUIState); } } @@ -301,17 +343,16 @@ private IEnumerator CaptureScreenshotCoroutine(CameraScreenshotRequestDto reques if (request.HideUI && Find.UIRoot != null) { originalUIState = Find.UIRoot.screenshotMode.Active; - Find.UIRoot.screenshotMode.Active = true; + SetScreenshotMode(true); } - // 2. THE MAGIC SAUCE: Yield to let Unity render a brand new frame WITHOUT the UI! + // Yield to let Unity render a brand new frame WITHOUT the UI! // We must do this outside the try-catch to satisfy C# compiler rules (CS1626). yield return new WaitForEndOfFrame(); - // 3. Now enter the try-catch for the risky GPU memory and encoding operations try { - // 1. Read the raw screen buffer directly! + // Read the raw screen buffer directly! // Because we waited for the end of the frame, the IMGUI (RimWorld UI) is guaranteed to be here. RenderTexture.active = null; // Ensure we are reading from the screen, not an RT Texture2D screenTex = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false); @@ -321,7 +362,7 @@ private IEnumerator CaptureScreenshotCoroutine(CameraScreenshotRequestDto reques Texture2D finalTexture = screenTex; RenderTexture resizedRT = null; - // 2. Handle Resizing (If the API client requested a smaller/larger width than the game window) + // Handle Resizing (If the API client requested a smaller/larger width than the game window) bool needsResize = (targetWidth != Screen.width || targetHeight != Screen.height); if (needsResize) { @@ -338,7 +379,6 @@ private IEnumerator CaptureScreenshotCoroutine(CameraScreenshotRequestDto reques RenderTexture.active = null; } - // 3. Encode to Image Format byte[] imageBytes; string actualFormat; @@ -374,7 +414,7 @@ private IEnumerator CaptureScreenshotCoroutine(CameraScreenshotRequestDto reques } }; - // 4. Cleanup memory to prevent massive memory leaks + // Cleanup memory to prevent massive memory leaks UnityEngine.Object.Destroy(screenTex); if (needsResize) { @@ -382,7 +422,6 @@ private IEnumerator CaptureScreenshotCoroutine(CameraScreenshotRequestDto reques RenderTexture.ReleaseTemporary(resizedRT); } - // Complete the Task! tcs.TrySetResult(ApiResult.Ok(dto)); } catch (Exception ex) @@ -392,10 +431,9 @@ private IEnumerator CaptureScreenshotCoroutine(CameraScreenshotRequestDto reques } finally { - // 4. ALWAYS Restore the UI state, even if encoding failed if (request.HideUI && Find.UIRoot != null) { - Find.UIRoot.screenshotMode.Active = originalUIState; + SetScreenshotMode(originalUIState); } } } diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/ICameraService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/CameraService/ICameraService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/ICameraService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Client/CameraService/ICameraService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IImageService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/ImageService/IImageService.cs similarity index 91% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IImageService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Client/ImageService/IImageService.cs index f3fb8b1..4a29d5a 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IImageService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Client/ImageService/IImageService.cs @@ -1,5 +1,3 @@ - -using System.Collections.Generic; using RIMAPI.Core; using RIMAPI.Models; diff --git a/Source/RIMAPI/RimworldRestApi/Services/ImageService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/ImageService/ImageService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/ImageService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Client/ImageService/ImageService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/OverlayService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/OverlayService/OverlayService.cs similarity index 98% rename from Source/RIMAPI/RimworldRestApi/Services/OverlayService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Client/OverlayService/OverlayService.cs index 57ff07d..f2aa86f 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/OverlayService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Client/OverlayService/OverlayService.cs @@ -1,4 +1,3 @@ -using System; using RIMAPI.Core; using RIMAPI.Models; using RIMAPI.UI; diff --git a/Source/RIMAPI/RimworldRestApi/Services/Client/SelectionService/ISelectionService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/SelectionService/ISelectionService.cs new file mode 100644 index 0000000..27465f4 --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Services/Client/SelectionService/ISelectionService.cs @@ -0,0 +1,12 @@ +using RIMAPI.Core; +using RIMAPI.Models; + +namespace RIMAPI.Services +{ + public interface ISelectionService + { + ApiResult SelectArea(SelectAreaRequestDto body); + ApiResult Select(string objectType, int id); + ApiResult DeselectAll(); + } +} diff --git a/Source/RIMAPI/RimworldRestApi/Services/Client/SelectionService/SelectionService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/SelectionService/SelectionService.cs new file mode 100644 index 0000000..1d5bc3d --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Services/Client/SelectionService/SelectionService.cs @@ -0,0 +1,83 @@ +using System; +using System.Linq; +using RIMAPI.Core; +using RIMAPI.Helpers; +using RIMAPI.Models; +using Verse; + +namespace RIMAPI.Services +{ + public class SelectionService : ISelectionService + { + public SelectionService() { } + + public ApiResult Select(string objectType, int id) + { + try + { + switch (objectType) + { + case "item": + var item = Find + .CurrentMap.listerThings.AllThings.Where(p => p.thingIDNumber == id) + .FirstOrDefault(); + Find.Selector.Select(item); + break; + case "pawn": + var pawn = PawnHelper.FindPawnById(id); + Find.Selector.Select(pawn); + break; + case "building": + var building = BuildingHelper.FindBuildingByID(id); + Find.Selector.Select(building); + break; + default: + return ApiResult.Fail($"Tried to select unknown object type: {objectType}"); + } + return ApiResult.Ok(); + } + catch (Exception ex) + { + return ApiResult.Fail(ex.Message); + } + } + + public ApiResult SelectArea(SelectAreaRequestDto body) + { + try + { + if (body.PositionA == null || body.PositionB == null) + { + return ApiResult.Fail("PositionA and PositionB cannot be null."); + } + + IntVec3 posA = new IntVec3(body.PositionA.X, body.PositionA.Y, body.PositionA.Z); + IntVec3 posB = new IntVec3(body.PositionB.X, body.PositionB.Y, body.PositionB.Z); + + CellRect rect = CellRect.FromLimits(posA, posB); + Find.Selector.Select(rect); + + return ApiResult.Ok(); + } + catch (Exception ex) + { + LogApi.Error($"Error selecting area: {ex}"); + return ApiResult.Fail($"Failed to select area: {ex.Message}"); + } + } + + public ApiResult DeselectAll() + { + try + { + Find.Selector.ClearSelection(); + return ApiResult.Ok(); + } + catch (Exception ex) + { + LogApi.Error($"Error deselecting all: {ex}"); + return ApiResult.Fail($"Failed to deselect: {ex.Message}"); + } + } + } +} diff --git a/Source/RIMAPI/RimworldRestApi/Services/Client/UIService/IUIService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/UIService/IUIService.cs new file mode 100644 index 0000000..8395a7e --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Services/Client/UIService/IUIService.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using RIMAPI.Core; +using RIMAPI.Models; +using RIMAPI.Models.UI; + +namespace RIMAPI.Services.Interfaces +{ + public interface IUIService + { + ApiResult OpenTab(string tabName); + ApiResult SendLetterSimple(SendLetterRequestDto body); + } +} \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/Services/Client/UIService/UIService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/UIService/UIService.cs new file mode 100644 index 0000000..a96fcb2 --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Services/Client/UIService/UIService.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using RimWorld; +using Verse; +using RIMAPI.Core; +using RIMAPI.Services.Interfaces; +using RIMAPI.Helpers; +using System.Linq; +using RIMAPI.Models; + +namespace RIMAPI.Services +{ + public class UIService : IUIService + { + private static readonly FieldInfo ActiveAlertsField = typeof(AlertsReadout).GetField( + "activeAlerts", + BindingFlags.Instance | BindingFlags.NonPublic + ); + + public ApiResult OpenTab(string tabName) + { + try + { + switch (tabName.ToLower()) + { + case "health": + InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Health)); + break; + case "character": + case "backstory": + InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Character)); + break; + case "gear": + case "equipment": + case "inventory": + InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Gear)); + break; + case "needs": + case "mood": + InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Needs)); + break; + case "training": + InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Training)); + break; + case "log": + case "combatlog": + InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Log)); + break; + case "relations": + case "social": + InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Social)); + break; + case "prisoner": + InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Prisoner)); + break; + case "slave": + InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Slave)); + break; + case "guest": + InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Guest)); + break; + default: + return ApiResult.Fail($"Tried to open unknown tab menu: {tabName}"); + } + return ApiResult.Ok(); + } + catch (Exception ex) + { + LogApi.Error($"Error opening tab {tabName}: {ex}"); + return ApiResult.Fail($"Failed to open tab: {ex.Message}"); + } + } + + public ApiResult SendLetterSimple(SendLetterRequestDto body) + { + try + { + List warnings = new List(); + const int MAX_LABEL_SIZE = 48; + const int MAX_MESSAGE_SIZE = 500; + var label = ApiSecurityHelper.SanitizeLetterInput(body.Label); + var message = ApiSecurityHelper.SanitizeLetterInput(body.Message); + + if (string.IsNullOrEmpty(message)) + { + return ApiResult.Fail("Message is empty after sanitization"); + } + + if (message.Length > MAX_MESSAGE_SIZE) + { + message = message.Substring(0, MAX_MESSAGE_SIZE) + "..."; + warnings.Add($"Message has been truncated to {MAX_MESSAGE_SIZE} characters"); + } + + if (label.Length > MAX_LABEL_SIZE) + { + message = message.Substring(0, MAX_LABEL_SIZE) + "..."; + warnings.Add($"Label has been truncated to {MAX_LABEL_SIZE} characters"); + } + + LetterDef letterDef = GameTypesHelper.StringToLetterDef(body.LetterDef); + LookTargets target = null; + Faction faction = null; + Quest quest = null; + // TODO: Support for hyperlinkThingDefs & debugInfo + List hyperlinkThingDefs = null; + string debugInfo = null; + + if (!string.IsNullOrEmpty(body.MapId)) + { + target = MapHelper.GetThingOnMapById( + int.Parse(body.MapId), + int.Parse(body.LookTargetThingId) + ); + } + + if (!string.IsNullOrEmpty(body.FactionOrderId)) + { + faction = FactionHelper.GetFactionByOrderId(int.Parse(body.FactionOrderId)); + } + + if (!string.IsNullOrEmpty(body.QuestId)) + { + int id = int.Parse(body.QuestId); + quest = Find + .QuestManager.QuestsListForReading.Where(s => s.id == id) + .FirstOrDefault(); + } + + Find.LetterStack.ReceiveLetter( + label, + message, + letterDef, + (LookTargets)target, + faction, + quest, + hyperlinkThingDefs, + debugInfo, + body.DelayTicks, + body.PlaySound + ); + + if (warnings.Count > 0) + { + return ApiResult.Partial(warnings); + } + return ApiResult.Ok(); + } + catch (Exception ex) + { + return ApiResult.Fail(ex.Message); + } + } + } +} \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IWindowService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/WindowService/IWindowService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IWindowService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Client/WindowService/IWindowService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/WindowService.cs b/Source/RIMAPI/RimworldRestApi/Services/Client/WindowService/WindowService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/WindowService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Client/WindowService/WindowService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/BillService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/BillService/BillService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/BillService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/BillService/BillService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IBillService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/BillService/IBillService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IBillService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/BillService/IBillService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IIncidentService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/IncidentService/IIncidentService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IIncidentService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/IncidentService/IIncidentService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/IncidentService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/IncidentService/IncidentService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/IncidentService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/IncidentService/IncidentService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/ILordMakerService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/LordMakerService/ILordMakerService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/ILordMakerService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/LordMakerService/ILordMakerService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/LordMakerService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/LordMakerService/LordMakerService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/LordMakerService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/LordMakerService/LordMakerService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IOrderService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/OrderService/IOrderService.cs similarity index 83% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IOrderService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/OrderService/IOrderService.cs index 91fb3cd..6c9ccd4 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IOrderService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Colony/OrderService/IOrderService.cs @@ -1,5 +1,3 @@ - -using System.Collections.Generic; using RIMAPI.Core; using RIMAPI.Models; diff --git a/Source/RIMAPI/RimworldRestApi/Services/OrderService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/OrderService/OrderService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/OrderService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/OrderService/OrderService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IResearchService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/ResearchService/IResearchService.cs similarity index 94% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IResearchService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/ResearchService/IResearchService.cs index 1de8875..f6be344 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IResearchService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Colony/ResearchService/IResearchService.cs @@ -1,5 +1,3 @@ - -using System.Collections.Generic; using RIMAPI.Core; using RIMAPI.Models; diff --git a/Source/RIMAPI/RimworldRestApi/Services/ResearchService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/ResearchService/ResearchService.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/Services/ResearchService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/ResearchService/ResearchService.cs index 8427adc..240d158 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/ResearchService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Colony/ResearchService/ResearchService.cs @@ -1,7 +1,6 @@ using RIMAPI.Core; using RIMAPI.Helpers; using RIMAPI.Models; -using Verse; namespace RIMAPI.Services { diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/ITradeService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/TradeService/ITradeService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/ITradeService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/TradeService/ITradeService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/TradeService.cs b/Source/RIMAPI/RimworldRestApi/Services/Colony/TradeService/TradeService.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/Services/TradeService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Colony/TradeService/TradeService.cs index db54b51..6643361 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/TradeService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Colony/TradeService/TradeService.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; using HarmonyLib; using RIMAPI.Core; -using RIMAPI.Helpers; using RIMAPI.Models; -using RIMAPI.Services; using RimWorld; using Verse; diff --git a/Source/RIMAPI/RimworldRestApi/Services/GameDataService.cs b/Source/RIMAPI/RimworldRestApi/Services/GameDataService.cs deleted file mode 100644 index 0c2511c..0000000 --- a/Source/RIMAPI/RimworldRestApi/Services/GameDataService.cs +++ /dev/null @@ -1,911 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using RIMAPI.Core; -using RIMAPI.Helpers; -using RIMAPI.Models; -using RimWorld; -using UnityEngine.Experimental.AI; -using Verse; - -namespace RIMAPI.Services -{ - public class GameStateService : IGameStateService - { - private readonly ICachingService _cachingService; - - public GameStateService(ICachingService cachingService) - { - _cachingService = cachingService; - } - - public ApiResult GetGameState() - { - try - { - var state = GamePlayHelper.GetGameStateDto(); - return ApiResult.Ok(state); - } - catch (Exception ex) - { - LogApi.Error($"Error getting game state: {ex}"); - return ApiResult.Fail($"Failed to get game state: {ex.Message}"); - } - } - - public ApiResult> GetModsInfo() - { - try - { - var mods = LoadedModManager - .RunningModsListForReading.Select(mod => new ModInfoDto - { - Name = mod.Name, - PackageId = mod.PackageId, - LoadOrder = mod.loadOrder, - }) - .ToList(); - - return ApiResult>.Ok(mods); - } - catch (Exception ex) - { - LogApi.Error($"Error getting mods info: {ex}"); - return ApiResult>.Fail($"Failed to get mods info: {ex.Message}"); - } - } - - public ApiResult ConfigureMods(ConfigureModsRequestDto body) - { - try - { - if (body == null || body.PackageIds == null) - { - return ApiResult.Fail("Invalid request. 'PackageIds' list is required."); - } - - // Ensure Core is always active - if (!body.PackageIds.Contains("ludeon.rimworld", StringComparer.OrdinalIgnoreCase)) - { - body.PackageIds.Insert(0, "ludeon.rimworld"); - } - - ModsConfig.SetActiveToList(body.PackageIds); - ModsConfig.Save(); - - if (body.RestartGame) - { - LogApi.Info("Restarting RimWorld to apply new mod configuration..."); - GenCommandLine.Restart(); - } - - return ApiResult.Ok(); - } - catch (Exception ex) - { - LogApi.Error($"Error configuring mods: {ex}"); - return ApiResult.Fail($"Failed to configure mods: {ex.Message}"); - } - } - - public ApiResult DeselectAll() - { - try - { - Find.Selector.ClearSelection(); - return ApiResult.Ok(); - } - catch (Exception ex) - { - LogApi.Error($"Error deselecting all: {ex}"); - return ApiResult.Fail($"Failed to deselect: {ex.Message}"); - } - } - - public ApiResult OpenTab(string tabName) - { - try - { - switch (tabName.ToLower()) - { - case "health": - InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Health)); - break; - case "character": - case "backstory": - InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Character)); - break; - case "gear": - case "equipment": - case "inventory": - InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Gear)); - break; - case "needs": - case "mood": - InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Needs)); - break; - case "training": - InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Training)); - break; - case "log": - case "combatlog": - InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Log)); - break; - case "relations": - case "social": - InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Social)); - break; - case "prisoner": - InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Prisoner)); - break; - case "slave": - InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Slave)); - break; - case "guest": - InspectPaneUtility.OpenTab(typeof(ITab_Pawn_Guest)); - break; - default: - return ApiResult.Fail($"Tried to open unknown tab menu: {tabName}"); - } - return ApiResult.Ok(); - } - catch (Exception ex) - { - LogApi.Error($"Error opening tab {tabName}: {ex}"); - return ApiResult.Fail($"Failed to open tab: {ex.Message}"); - } - } - - private void SetProperty( - DefsDto defs, - Func> valueGetter, - List warnings, - string propertyName - ) - { - try - { - // Get compiled property setter from cache (or create and cache it) - var propertySetter = _cachingService.GetPropertySetter>( - propertyName - ); - - // Get the value - var value = valueGetter(); - - // Set the property using the compiled setter - propertySetter(defs, value); - } - catch (Exception ex) - { - warnings.Add($"Failed to load {propertyName}: {ex.Message}"); - } - } - - public ApiResult GetAllDefs(AllDefsRequestDto body) - { - try - { - var warnings = new List(); - var defs = new DefsDto(); - - // Check if we should show all defs - bool showAll = - body == null - || body.Filters == null - || body.Filters.Count == 0 - || body.Filters.Contains("All", StringComparer.OrdinalIgnoreCase); - - // Create a dictionary of property setters for dynamic invocation - var propertyMap = new Dictionary - { - ["ThingsDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetThingDefDtoList, - warnings, - "ThingsDefs" - ), - ["IncidentsDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetIncidentDefDtoList, - warnings, - "IncidentsDefs" - ), - ["ConditionsDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetConditionsDefDtoList, - warnings, - "ConditionsDefs" - ), - ["PawnKindDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetPawnKindDefDtoList, - warnings, - "PawnKindDefs" - ), - ["TraitDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetTraitDefDtoList, - warnings, - "TraitDefs" - ), - ["ResearchDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetResearchProjectDefDtoList, - warnings, - "ResearchDefs" - ), - ["HediffDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetHediffDefsList, - warnings, - "HediffDefs" - ), - ["SkillDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetSkillDefDtoList, - warnings, - "SkillDefs" - ), - ["WorkTypeDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetWorkTypeDefDtoList, - warnings, - "WorkTypeDefs" - ), - ["NeedDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetNeedDefDtoList, - warnings, - "NeedDefs" - ), - ["ThoughtDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetThoughtDefDtoList, - warnings, - "ThoughtDefs" - ), - ["StatDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetStatDefDtoList, - warnings, - "StatDefs" - ), - ["WorldObjectDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetWorldObjectDefDtoList, - warnings, - "WorldObjectDefs" - ), - ["BiomeDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetBiomeDefDtoList, - warnings, - "BiomeDefs" - ), - ["TerrainDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetTerrainDefDtoList, - warnings, - "TerrainDefs" - ), - ["RecipeDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetRecipeDefDtoList, - warnings, - "RecipeDefs" - ), - ["BodyDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetBodyDefDtoList, - warnings, - "BodyDefs" - ), - ["BodyPartDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetBodyPartDefDtoList, - warnings, - "BodyPartDefs" - ), - ["FactionDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetFactionDefDtoList, - warnings, - "FactionDefs" - ), - ["SoundDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetSoundDefDtoList, - warnings, - "SoundDefs" - ), - ["DesignationCategoryDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetDesignationCategoryDefDtoList, - warnings, - "DesignationCategoryDefs" - ), - ["JoyKindDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetJoyKindDefDtoList, - warnings, - "JoyKindDefs" - ), - ["MemeDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetMemeDefDtoList, - warnings, - "MemeDefs" - ), - ["PreceptDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetPreceptDefDtoList, - warnings, - "PreceptDefs" - ), - ["AbilityDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetAbilityDefDtoList, - warnings, - "AbilityDefs" - ), - ["GeneDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetGeneDefDtoList, - warnings, - "GeneDefs" - ), - ["WeatherDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetWeatherDefDtoList, - warnings, - "WeatherDefs" - ), - ["RoomRoleDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetRoomRoleDefDtoList, - warnings, - "RoomRoleDefs" - ), - ["RoomStatDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetRoomStatDefDtoList, - warnings, - "RoomStatDefs" - ), - ["MentalStateDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetMentalStateDefDtoList, - warnings, - "MentalStateDefs" - ), - ["DrugPolicyDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetDrugPolicyDefDtoList, - warnings, - "DrugPolicyDefs" - ), - ["PlantDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetPlantDefDtoList, - warnings, - "PlantDefs" - ), - ["AnimalDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetAnimalDefDtoList, - warnings, - "AnimalDefs" - ), - ["StorytellerDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetStorytellerDefDtoList, - warnings, - "StorytellerDefs" - ), - ["DifficultyDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetDifficultyDefDtoList, - warnings, - "DifficultyDefs" - ), - ["JobDefs"] = () => - SetProperty( - defs, - DefDatabaseHelper.GetJobDefDtoList, - warnings, - "JobDefs" - ), - }; - - // Execute only the requested properties - if (showAll) - { - // Execute all property getters - foreach (var propertySetter in propertyMap.Values) - { - propertySetter(); - } - } - else - { - // Execute only filtered properties - foreach (var filter in body.Filters) - { - // Match either exact name or snake_case name - var matchedKey = propertyMap.Keys.FirstOrDefault(k => - k.Equals(filter, StringComparison.OrdinalIgnoreCase) || - ToSnakeCase(k).Equals(filter, StringComparison.OrdinalIgnoreCase)); - - if (matchedKey != null) - { - propertyMap[matchedKey](); - } - else - { - warnings.Add($"Unknown filter: {filter}"); - } - } - } - - if (warnings.Count > 0) - { - return ApiResult.Partial(defs, warnings); - } - return ApiResult.Ok(defs); - } - catch (Exception ex) - { - LogApi.Error($"Error getting all defs: {ex}"); - return ApiResult.Fail($"Failed to get defs: {ex.Message}"); - } - } - - private string ToSnakeCase(string input) - { - if (string.IsNullOrEmpty(input)) return input; - var result = new System.Text.StringBuilder(); - result.Append(char.ToLower(input[0])); - for (int i = 1; i < input.Length; i++) - { - if (char.IsUpper(input[i])) - { - result.Append('_'); - result.Append(char.ToLower(input[i])); - } - else - { - result.Append(input[i]); - } - } - return result.ToString(); - } - - public static int GetMapTileId(Map map) - { -#if RIMWORLD_1_5 - return map.Tile; -#elif RIMWORLD_1_6 - return map.Tile.tileId; -#endif - throw new Exception("Failed to get GetMapTileId for this rimworld version."); - } - - public ApiResult GetCurrentMapDatetime() - { - try - { - var map = Find.CurrentMap; - if (map == null) - return ApiResult.Fail("No current map found"); - - var time = GetDatetimeAt(GetMapTileId(Find.CurrentMap)); - return ApiResult.Ok(time); - } - catch (Exception ex) - { - LogApi.Error($"Error getting current map datetime: {ex}"); - return ApiResult.Fail($"Failed to get datetime: {ex.Message}"); - } - } - - public ApiResult GetWorldTileDatetime(int tileID) - { - try - { - var time = GetDatetimeAt(tileID); - - return ApiResult.Ok(time); - } - catch (Exception ex) - { - LogApi.Error($"Error getting world tile datetime for tile {tileID}: {ex}"); - return ApiResult.Fail($"Failed to get datetime: {ex.Message}"); - } - } - - public MapTimeDto GetDatetimeAt(int tileID) - { - MapTimeDto mapTimeDto = new MapTimeDto(); - try - { - if (Current.ProgramState != ProgramState.Playing || Find.WorldGrid == null) - { - return mapTimeDto; - } - - var vector = Find.WorldGrid.LongLatOf(tileID); - mapTimeDto.Datetime = GenDate.DateFullStringWithHourAt( - Find.TickManager.TicksAbs, - vector - ); - - return mapTimeDto; - } - catch (Exception ex) - { - LogApi.Error($"Error - {ex.Message}"); - return mapTimeDto; - } - } - - public ApiResult Select(string objectType, int id) - { - try - { - switch (objectType) - { - case "item": - var item = Find - .CurrentMap.listerThings.AllThings.Where(p => p.thingIDNumber == id) - .FirstOrDefault(); - Find.Selector.Select(item); - break; - case "pawn": - var pawn = PawnHelper.FindPawnById(id); - Find.Selector.Select(pawn); - break; - case "building": - var building = BuildingHelper.FindBuildingByID(id); - Find.Selector.Select(building); - break; - default: - return ApiResult.Fail($"Tried to select unknown object type: {objectType}"); - } - return ApiResult.Ok(); - } - catch (Exception ex) - { - return ApiResult.Fail(ex.Message); - } - } - - public ApiResult SendLetterSimple(SendLetterRequestDto body) - { - try - { - List warnings = new List(); - const int MAX_LABEL_SIZE = 48; - const int MAX_MESSAGE_SIZE = 500; - var label = ApiSecurityHelper.SanitizeLetterInput(body.Label); - var message = ApiSecurityHelper.SanitizeLetterInput(body.Message); - - if (string.IsNullOrEmpty(message)) - { - return ApiResult.Fail("Message is empty after sanitization"); - } - - if (message.Length > MAX_MESSAGE_SIZE) - { - message = message.Substring(0, MAX_MESSAGE_SIZE) + "..."; - warnings.Add($"Message has been truncated to {MAX_MESSAGE_SIZE} characters"); - } - - if (label.Length > MAX_LABEL_SIZE) - { - message = message.Substring(0, MAX_LABEL_SIZE) + "..."; - warnings.Add($"Label has been truncated to {MAX_LABEL_SIZE} characters"); - } - - LetterDef letterDef = GameTypesHelper.StringToLetterDef(body.LetterDef); - LookTargets target = null; - Faction faction = null; - Quest quest = null; - // TODO: Support for hyperlinkThingDefs & debugInfo - List hyperlinkThingDefs = null; - string debugInfo = null; - - if (!string.IsNullOrEmpty(body.MapId)) - { - target = MapHelper.GetThingOnMapById( - int.Parse(body.MapId), - int.Parse(body.LookTargetThingId) - ); - } - - if (!string.IsNullOrEmpty(body.FactionOrderId)) - { - faction = FactionHelper.GetFactionByOrderId(int.Parse(body.FactionOrderId)); - } - - if (!string.IsNullOrEmpty(body.QuestId)) - { - int id = int.Parse(body.QuestId); - quest = Find - .QuestManager.QuestsListForReading.Where(s => s.id == id) - .FirstOrDefault(); - } - - Find.LetterStack.ReceiveLetter( - label, - message, - letterDef, - (LookTargets)target, - faction, - quest, - hyperlinkThingDefs, - debugInfo, - body.DelayTicks, - body.PlaySound - ); - - if (warnings.Count > 0) - { - return ApiResult.Partial(warnings); - } - return ApiResult.Ok(); - } - catch (Exception ex) - { - return ApiResult.Fail(ex.Message); - } - } - - public ApiResult SetGameSpeed(int speed) - { - try - { - Find.TickManager.CurTimeSpeed = (TimeSpeed)speed; - return ApiResult.Ok(); - } - catch (Exception ex) - { - LogApi.Error($"Error setting game speed: {ex}"); - return ApiResult.Fail($"Failed to set game speed: {ex.Message}"); - } - } - - public ApiResult SelectArea(SelectAreaRequestDto body) - { - try - { - if (body.PositionA == null || body.PositionB == null) - { - return ApiResult.Fail("PositionA and PositionB cannot be null."); - } - - IntVec3 posA = new IntVec3(body.PositionA.X, body.PositionA.Y, body.PositionA.Z); - IntVec3 posB = new IntVec3(body.PositionB.X, body.PositionB.Y, body.PositionB.Z); - - CellRect rect = CellRect.FromLimits(posA, posB); - Find.Selector.Select(rect); - - return ApiResult.Ok(); - } - catch (Exception ex) - { - LogApi.Error($"Error selecting area: {ex}"); - return ApiResult.Fail($"Failed to select area: {ex.Message}"); - } - } - - public ApiResult GoToMainMenu() - { - LongEventHandler.ExecuteWhenFinished(() => - { - GenScene.GoToMainMenu(); - }); - return ApiResult.Ok(); - } - - public ApiResult QuitGame() - { - LongEventHandler.ExecuteWhenFinished(() => - { - Root.Shutdown(); - }); - return ApiResult.Ok(); - } - - public ApiResult GameSave(GameSaveRequestDto body) - { - string saveName; - - try - { - if (body == null) - { - return ApiResult.Fail("Request body is missing or invalid."); - } - - if (Current.Game == null) - { - return ApiResult.Fail("Cannot save: No active game is currently running."); - } - - if (GameDataSaveLoader.SavingIsTemporarilyDisabled) - { - return ApiResult.Fail("Cannot save game - saving is temporarily disabled (e.g., during a cutscene)."); - } - - if (Current.Game.Info.permadeathMode) - { - saveName = Current.Game.Info.permadeathModeUniqueName; - } - else - { - if (string.IsNullOrWhiteSpace(body.FileName)) - { - return ApiResult.Fail("The 'file_name' parameter is required to save a game."); - } - saveName = GenFile.SanitizedFileName(body.FileName); - } - - LongEventHandler.ExecuteWhenFinished(() => - { - LongEventHandler.QueueLongEvent(delegate - { - GameDataSaveLoader.SaveGame(saveName); - }, "SavingLongEvent", doAsynchronously: false, null); - - Messages.Message("Game saved as: " + saveName, MessageTypeDefOf.SilentInput); - }); - - return ApiResult.Ok(); - } - catch (Exception ex) - { - return ApiResult.Fail($"Failed to initialize game save: {ex.Message}"); - } - } - - public ApiResult GameLoad(GameLoadRequestDto body) - { - try - { - if (body == null) - { - return ApiResult.Fail("Request body is missing or invalid."); - } - - string filePath = GenFilePaths.FilePathForSavedGame(body.FileName); - if (!File.Exists(filePath)) - { - return ApiResult.Fail($"Save file not found: {body.FileName}.rws"); - } - - // Queue execution onto RimWorld's main thread. - LongEventHandler.ExecuteWhenFinished(() => - { - if (!body.CheckVersion) - { - // Immediate asynchronous queueing without version checking - GameDataSaveLoader.LoadGame(body.FileName); - } - else if (body.SkipModMismatch) - { - // Check version but forcefully skip the mod mismatch dialog - PreLoadUtility.CheckVersionAndLoad( - filePath, - ScribeMetaHeaderUtility.ScribeHeaderMode.Map, - delegate - { - GameDataSaveLoader.LoadGame(body.FileName); - }, - skipOnMismatch: true - ); - } - else - { - GameDataSaveLoader.CheckVersionAndLoadGame(body.FileName); - } - }); - - return ApiResult.Ok(); - } - catch (Exception ex) - { - return ApiResult.Fail($"Failed to initialize game load: {ex.Message}"); - } - } - - public ApiResult GameDevQuickStart() - { - try - { - LongEventHandler.QueueLongEvent(delegate - { - Root_Play.SetupForQuickTestPlay(); - PageUtility.InitGameStart(); - }, "GeneratingMap", doAsynchronously: true, GameAndMapInitExceptionHandlers.ErrorWhileGeneratingMap); - } - catch (Exception ex) - { - return ApiResult.Fail($"Failed to start quick dev game: {ex.Message}"); - } - return ApiResult.Ok(); - } - - public ApiResult GameStart(NewGameStartRequestDto request) - { - try - { - LongEventHandler.QueueLongEvent(delegate - { - GamePlayHelper.InitGameFromConfiguration(request); - PageUtility.InitGameStart(); - }, "GeneratingMap", doAsynchronously: true, GameAndMapInitExceptionHandlers.ErrorWhileGeneratingMap); - } - catch (Exception ex) - { - return ApiResult.Fail($"Failed to start game from configuration: {ex.Message}"); - } - return ApiResult.Ok(); - } - - public ApiResult GetCurrentSettings() - { - var result = GamePlayHelper.GetCurrentSettings(); - return ApiResult.Ok(result); - } - - public ApiResult ToggleRunInBackground() - { - Prefs.RunInBackground = !Prefs.RunInBackground; - Prefs.Save(); - return ApiResult.Ok(Prefs.RunInBackground); - } - - public ApiResult GetRunInBackground() - { - return ApiResult.Ok(Prefs.RunInBackground); - } - } -} diff --git a/Source/RIMAPI/RimworldRestApi/Services/ColonistService.cs b/Source/RIMAPI/RimworldRestApi/Services/Pawns/ColonistService/ColonistService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/ColonistService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Pawns/ColonistService/ColonistService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IColonistService.cs b/Source/RIMAPI/RimworldRestApi/Services/Pawns/ColonistService/IColonistService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IColonistService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Pawns/ColonistService/IColonistService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IPawnEditService.cs b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnEditService/IPawnEditService.cs similarity index 96% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IPawnEditService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnEditService/IPawnEditService.cs index 86650f8..303f68f 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IPawnEditService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnEditService/IPawnEditService.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using RIMAPI.Core; using RIMAPI.Models; diff --git a/Source/RIMAPI/RimworldRestApi/Services/PawnEditService.cs b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnEditService/PawnEditService.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/Services/PawnEditService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnEditService/PawnEditService.cs index c26b1d5..adf9b03 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/PawnEditService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnEditService/PawnEditService.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using RIMAPI.Core; using RIMAPI.Helpers; @@ -7,7 +6,6 @@ using RimWorld; using UnityEngine; using Verse; -using Verse.AI; namespace RIMAPI.Services { diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IPawnInfoService.cs b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnInfoService/IPawnInfoService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IPawnInfoService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnInfoService/IPawnInfoService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/PawnInfoService.cs b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnInfoService/PawnInfoService.cs similarity index 98% rename from Source/RIMAPI/RimworldRestApi/Services/PawnInfoService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnInfoService/PawnInfoService.cs index 0e19f21..031684d 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/PawnInfoService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnInfoService/PawnInfoService.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; using RIMAPI.Core; using RIMAPI.Helpers; using RIMAPI.Models; -using RimWorld; -using UnityEngine; using Verse; namespace RIMAPI.Services diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IPawnJobService.cs b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnJobService/IPawnJobService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IPawnJobService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnJobService/IPawnJobService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/PawnJobService.cs b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnJobService/PawnJobService.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/Services/PawnJobService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnJobService/PawnJobService.cs index 451ea26..75119d9 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/PawnJobService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnJobService/PawnJobService.cs @@ -5,7 +5,6 @@ using RIMAPI.Models; using RimWorld; using Verse; -using Verse.AI; namespace RIMAPI.Services { diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IPawnSpawnService.cs b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnSpawnService/IPawnSpawnService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IPawnSpawnService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnSpawnService/IPawnSpawnService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/PawnSpawnService.cs b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnSpawnService/PawnSpawnService.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/Services/PawnSpawnService.cs rename to Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnSpawnService/PawnSpawnService.cs index 2689192..fd9af31 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/PawnSpawnService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/Pawns/PawnSpawnService/PawnSpawnService.cs @@ -4,7 +4,6 @@ using RIMAPI.Helpers; using RIMAPI.Models; using RimWorld; -using UnityEngine; using Verse; namespace RIMAPI.Services diff --git a/Source/RIMAPI/RimworldRestApi/Services/DevToolsService.cs b/Source/RIMAPI/RimworldRestApi/Services/System/DevToolsService/DevToolsService.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/Services/DevToolsService.cs rename to Source/RIMAPI/RimworldRestApi/Services/System/DevToolsService/DevToolsService.cs index 057fb28..0621e37 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/DevToolsService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/System/DevToolsService/DevToolsService.cs @@ -3,7 +3,6 @@ using RIMAPI.Core; using RIMAPI.Helpers; using RIMAPI.Models; -using RimWorld; using Verse; namespace RIMAPI.Services diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IDevToolsService.cs b/Source/RIMAPI/RimworldRestApi/Services/System/DevToolsService/IDevToolsService.cs similarity index 91% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IDevToolsService.cs rename to Source/RIMAPI/RimworldRestApi/Services/System/DevToolsService/IDevToolsService.cs index 6db2ba0..00412f3 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IDevToolsService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/System/DevToolsService/IDevToolsService.cs @@ -1,5 +1,3 @@ - -using System.Collections.Generic; using RIMAPI.Core; using RIMAPI.Models; diff --git a/Source/RIMAPI/RimworldRestApi/Services/System/GameDataService/GameDataService.cs b/Source/RIMAPI/RimworldRestApi/Services/System/GameDataService/GameDataService.cs new file mode 100644 index 0000000..0892f88 --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Services/System/GameDataService/GameDataService.cs @@ -0,0 +1,380 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using RIMAPI.Core; +using RIMAPI.Helpers; +using RIMAPI.Models; +using Verse; + +namespace RIMAPI.Services +{ + public class GameDataService : IGameDataService + { + private readonly ICachingService _cachingService; + + public GameDataService(ICachingService cachingService) + { + _cachingService = cachingService; + } + + private string ToSnakeCase(string input) + { + if (string.IsNullOrEmpty(input)) return input; + var result = new System.Text.StringBuilder(); + result.Append(char.ToLower(input[0])); + for (int i = 1; i < input.Length; i++) + { + if (char.IsUpper(input[i])) + { + result.Append('_'); + result.Append(char.ToLower(input[i])); + } + else + { + result.Append(input[i]); + } + } + return result.ToString(); + } + + private void SetProperty( + DefsDto defs, + Func> valueGetter, + List warnings, + string propertyName + ) + { + try + { + // Get compiled property setter from cache (or create and cache it) + var propertySetter = _cachingService.GetPropertySetter>( + propertyName + ); + + // Get the value + var value = valueGetter(); + + // Set the property using the compiled setter + propertySetter(defs, value); + } + catch (Exception ex) + { + warnings.Add($"Failed to load {propertyName}: {ex.Message}"); + } + } + + public ApiResult GetAllDefs(AllDefsRequestDto body) + { + try + { + var warnings = new List(); + var defs = new DefsDto(); + + // Check if we should show all defs + bool showAll = + body == null + || body.Filters == null + || body.Filters.Count == 0 + || body.Filters.Contains("All", StringComparer.OrdinalIgnoreCase); + + // Create a dictionary of property setters for dynamic invocation + var propertyMap = new Dictionary + { + ["ThingsDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetThingDefDtoList, + warnings, + "ThingsDefs" + ), + ["IncidentsDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetIncidentDefDtoList, + warnings, + "IncidentsDefs" + ), + ["ConditionsDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetConditionsDefDtoList, + warnings, + "ConditionsDefs" + ), + ["PawnKindDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetPawnKindDefDtoList, + warnings, + "PawnKindDefs" + ), + ["TraitDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetTraitDefDtoList, + warnings, + "TraitDefs" + ), + ["ResearchDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetResearchProjectDefDtoList, + warnings, + "ResearchDefs" + ), + ["HediffDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetHediffDefsList, + warnings, + "HediffDefs" + ), + ["SkillDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetSkillDefDtoList, + warnings, + "SkillDefs" + ), + ["WorkTypeDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetWorkTypeDefDtoList, + warnings, + "WorkTypeDefs" + ), + ["NeedDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetNeedDefDtoList, + warnings, + "NeedDefs" + ), + ["ThoughtDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetThoughtDefDtoList, + warnings, + "ThoughtDefs" + ), + ["StatDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetStatDefDtoList, + warnings, + "StatDefs" + ), + ["WorldObjectDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetWorldObjectDefDtoList, + warnings, + "WorldObjectDefs" + ), + ["BiomeDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetBiomeDefDtoList, + warnings, + "BiomeDefs" + ), + ["TerrainDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetTerrainDefDtoList, + warnings, + "TerrainDefs" + ), + ["RecipeDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetRecipeDefDtoList, + warnings, + "RecipeDefs" + ), + ["BodyDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetBodyDefDtoList, + warnings, + "BodyDefs" + ), + ["BodyPartDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetBodyPartDefDtoList, + warnings, + "BodyPartDefs" + ), + ["FactionDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetFactionDefDtoList, + warnings, + "FactionDefs" + ), + ["SoundDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetSoundDefDtoList, + warnings, + "SoundDefs" + ), + ["DesignationCategoryDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetDesignationCategoryDefDtoList, + warnings, + "DesignationCategoryDefs" + ), + ["JoyKindDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetJoyKindDefDtoList, + warnings, + "JoyKindDefs" + ), + ["MemeDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetMemeDefDtoList, + warnings, + "MemeDefs" + ), + ["PreceptDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetPreceptDefDtoList, + warnings, + "PreceptDefs" + ), + ["AbilityDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetAbilityDefDtoList, + warnings, + "AbilityDefs" + ), + ["GeneDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetGeneDefDtoList, + warnings, + "GeneDefs" + ), + ["WeatherDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetWeatherDefDtoList, + warnings, + "WeatherDefs" + ), + ["RoomRoleDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetRoomRoleDefDtoList, + warnings, + "RoomRoleDefs" + ), + ["RoomStatDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetRoomStatDefDtoList, + warnings, + "RoomStatDefs" + ), + ["MentalStateDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetMentalStateDefDtoList, + warnings, + "MentalStateDefs" + ), + ["DrugPolicyDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetDrugPolicyDefDtoList, + warnings, + "DrugPolicyDefs" + ), + ["PlantDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetPlantDefDtoList, + warnings, + "PlantDefs" + ), + ["AnimalDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetAnimalDefDtoList, + warnings, + "AnimalDefs" + ), + ["StorytellerDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetStorytellerDefDtoList, + warnings, + "StorytellerDefs" + ), + ["DifficultyDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetDifficultyDefDtoList, + warnings, + "DifficultyDefs" + ), + ["JobDefs"] = () => + SetProperty( + defs, + DefDatabaseHelper.GetJobDefDtoList, + warnings, + "JobDefs" + ), + }; + + // Execute only the requested properties + if (showAll) + { + // Execute all property getters + foreach (var propertySetter in propertyMap.Values) + { + propertySetter(); + } + } + else + { + // Execute only filtered properties + foreach (var filter in body.Filters) + { + // Match either exact name or snake_case name + var matchedKey = propertyMap.Keys.FirstOrDefault(k => + k.Equals(filter, StringComparison.OrdinalIgnoreCase) || + ToSnakeCase(k).Equals(filter, StringComparison.OrdinalIgnoreCase)); + + if (matchedKey != null) + { + propertyMap[matchedKey](); + } + else + { + warnings.Add($"Unknown filter: {filter}"); + } + } + } + + if (warnings.Count > 0) + { + return ApiResult.Partial(defs, warnings); + } + return ApiResult.Ok(defs); + } + catch (Exception ex) + { + LogApi.Error($"Error getting all defs: {ex}"); + return ApiResult.Fail($"Failed to get defs: {ex.Message}"); + } + } + } +} diff --git a/Source/RIMAPI/RimworldRestApi/Services/System/GameDataService/IGameDataService.cs b/Source/RIMAPI/RimworldRestApi/Services/System/GameDataService/IGameDataService.cs new file mode 100644 index 0000000..8d35b39 --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Services/System/GameDataService/IGameDataService.cs @@ -0,0 +1,10 @@ +using RIMAPI.Core; +using RIMAPI.Models; + +namespace RIMAPI.Services +{ + public interface IGameDataService + { + ApiResult GetAllDefs(AllDefsRequestDto body); + } +} \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/Services/System/GameStateService/GameStateService.cs b/Source/RIMAPI/RimworldRestApi/Services/System/GameStateService/GameStateService.cs new file mode 100644 index 0000000..5b9a9f1 --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Services/System/GameStateService/GameStateService.cs @@ -0,0 +1,156 @@ +using System; +using RIMAPI.Core; +using RIMAPI.Helpers; +using RIMAPI.Models; +using RimWorld; +using Verse; + +namespace RIMAPI.Services +{ + public class GameStateService : IGameStateService + { + public ApiResult GetGameState() + { + try + { + return ApiResult.Ok(GamePlayHelper.GetGameStateDto()); + } + catch (Exception ex) + { + LogApi.Error($"Error getting game state: {ex}"); + return ApiResult.Fail($"Failed to get game state: {ex.Message}"); + } + } + + public ApiResult SetGameSpeed(int speed) + { + try + { + Find.TickManager.CurTimeSpeed = (TimeSpeed)speed; + return ApiResult.Ok(); + } + catch (Exception ex) + { + return ApiResult.Fail($"Failed to set game speed: {ex.Message}"); + } + } + + public ApiResult GoToMainMenu() + { + LongEventHandler.ExecuteWhenFinished(() => GenScene.GoToMainMenu()); + return ApiResult.Ok(); + } + + public ApiResult QuitGame() + { + LongEventHandler.ExecuteWhenFinished(() => Root.Shutdown()); + return ApiResult.Ok(); + } + + public ApiResult GameSave(GameSaveRequestDto body) + { + try + { + if (body == null) return ApiResult.Fail("Request body is missing."); + if (Current.Game == null) return ApiResult.Fail("No active game is currently running."); + if (GameDataSaveLoader.SavingIsTemporarilyDisabled) return ApiResult.Fail("Saving is temporarily disabled."); + + string saveName = Current.Game.Info.permadeathMode + ? Current.Game.Info.permadeathModeUniqueName + : GenFile.SanitizedFileName(body.FileName); + + if (string.IsNullOrWhiteSpace(saveName)) return ApiResult.Fail("File name is required."); + + LongEventHandler.ExecuteWhenFinished(() => + { + LongEventHandler.QueueLongEvent(() => GameDataSaveLoader.SaveGame(saveName), "SavingLongEvent", false, null); + Messages.Message("Game saved as: " + saveName, MessageTypeDefOf.SilentInput); + }); + + return ApiResult.Ok(); + } + catch (Exception ex) + { + return ApiResult.Fail($"Failed to initialize game save: {ex.Message}"); + } + } + + public ApiResult GameLoad(GameLoadRequestDto body) + { + try + { + if (body == null) return ApiResult.Fail("Request body is missing."); + string filePath = GenFilePaths.FilePathForSavedGame(body.FileName); + if (!System.IO.File.Exists(filePath)) return ApiResult.Fail($"Save file not found: {body.FileName}.rws"); + + LongEventHandler.ExecuteWhenFinished(() => + { + if (!body.CheckVersion) + { + GameDataSaveLoader.LoadGame(body.FileName); + } + else if (body.SkipModMismatch) + { + PreLoadUtility.CheckVersionAndLoad(filePath, ScribeMetaHeaderUtility.ScribeHeaderMode.Map, + () => GameDataSaveLoader.LoadGame(body.FileName), skipOnMismatch: true); + } + else + { + GameDataSaveLoader.CheckVersionAndLoadGame(body.FileName); + } + }); + + return ApiResult.Ok(); + } + catch (Exception ex) + { + return ApiResult.Fail($"Failed to load game: {ex.Message}"); + } + } + + public ApiResult GameDevQuickStart() + { + try + { + LongEventHandler.QueueLongEvent(() => + { + Root_Play.SetupForQuickTestPlay(); + PageUtility.InitGameStart(); + }, "GeneratingMap", true, GameAndMapInitExceptionHandlers.ErrorWhileGeneratingMap); + return ApiResult.Ok(); + } + catch (Exception ex) { return ApiResult.Fail(ex.Message); } + } + + public ApiResult GameStart(NewGameStartRequestDto request) + { + try + { + LongEventHandler.QueueLongEvent(() => + { + GamePlayHelper.InitGameFromConfiguration(request); + PageUtility.InitGameStart(); + }, "GeneratingMap", true, GameAndMapInitExceptionHandlers.ErrorWhileGeneratingMap); + return ApiResult.Ok(); + } + catch (Exception ex) { return ApiResult.Fail(ex.Message); } + } + + public ApiResult GetCurrentSettings() + { + return ApiResult.Ok(GamePlayHelper.GetCurrentSettings()); + } + + public ApiResult ToggleRunInBackground() + { + Prefs.RunInBackground = !Prefs.RunInBackground; + Prefs.Save(); + return ApiResult.Ok(Prefs.RunInBackground); + } + + public ApiResult GetRunInBackground() + { + return ApiResult.Ok(Prefs.RunInBackground); + } + } +} \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IGameStateService.cs b/Source/RIMAPI/RimworldRestApi/Services/System/GameStateService/IGameStateService.cs similarity index 52% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IGameStateService.cs rename to Source/RIMAPI/RimworldRestApi/Services/System/GameStateService/IGameStateService.cs index b0e7e7b..f358762 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IGameStateService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/System/GameStateService/IGameStateService.cs @@ -1,5 +1,3 @@ - -using System.Collections.Generic; using RIMAPI.Core; using RIMAPI.Models; @@ -8,16 +6,6 @@ namespace RIMAPI.Services public interface IGameStateService { ApiResult GetGameState(); - ApiResult> GetModsInfo(); - ApiResult ConfigureMods(ConfigureModsRequestDto body); - ApiResult SelectArea(SelectAreaRequestDto body); - ApiResult Select(string objectType, int id); - ApiResult DeselectAll(); - ApiResult OpenTab(string tabName); - ApiResult GetAllDefs(AllDefsRequestDto body); - ApiResult GetCurrentMapDatetime(); - ApiResult GetWorldTileDatetime(int tileID); - ApiResult SendLetterSimple(SendLetterRequestDto body); ApiResult SetGameSpeed(int speed); ApiResult GameSave(GameSaveRequestDto body); ApiResult GameLoad(GameLoadRequestDto body); diff --git a/Source/RIMAPI/RimworldRestApi/Services/System/ModService/IModService.cs b/Source/RIMAPI/RimworldRestApi/Services/System/ModService/IModService.cs new file mode 100644 index 0000000..c921d8b --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Services/System/ModService/IModService.cs @@ -0,0 +1,13 @@ + +using System.Collections.Generic; +using RIMAPI.Core; +using RIMAPI.Models; + +namespace RIMAPI.Services +{ + public interface IModService + { + ApiResult> GetModsInfo(); + ApiResult ConfigureMods(ConfigureModsRequestDto body); + } +} \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/Services/System/ModService/ModService.cs b/Source/RIMAPI/RimworldRestApi/Services/System/ModService/ModService.cs new file mode 100644 index 0000000..b9016d3 --- /dev/null +++ b/Source/RIMAPI/RimworldRestApi/Services/System/ModService/ModService.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using RIMAPI.Core; +using RIMAPI.Models; +using Verse; + +namespace RIMAPI.Services +{ + public class ModService : IModService + { + public ApiResult> GetModsInfo() + { + try + { + var mods = LoadedModManager.RunningModsListForReading.Select(mod => new ModInfoDto + { + Name = mod.Name, + PackageId = mod.PackageId, + LoadOrder = mod.loadOrder, + }).ToList(); + + return ApiResult>.Ok(mods); + } + catch (Exception ex) + { + return ApiResult>.Fail($"Failed to get mods info: {ex.Message}"); + } + } + + public ApiResult ConfigureMods(ConfigureModsRequestDto body) + { + try + { + if (body == null || body.PackageIds == null) + return ApiResult.Fail("Invalid request. 'PackageIds' list is required."); + + if (!body.PackageIds.Contains("ludeon.rimworld", StringComparer.OrdinalIgnoreCase)) + body.PackageIds.Insert(0, "ludeon.rimworld"); + + ModsConfig.SetActiveToList(body.PackageIds); + ModsConfig.Save(); + + if (body.RestartGame) + { + LogApi.Info("Restarting RimWorld to apply new mod configuration..."); + GenCommandLine.Restart(); + } + + return ApiResult.Ok(); + } + catch (Exception ex) + { + return ApiResult.Fail($"Failed to configure mods: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/Source/RIMAPI/RimworldRestApi/Services/BuilderService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/BuilderService/BuilderService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/BuilderService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/BuilderService/BuilderService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IBuilderService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/BuilderService/IBuilderService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IBuilderService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/BuilderService/IBuilderService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/BuildingService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/BuildingService/BuildingService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/BuildingService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/BuildingService/BuildingService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IDataServices.cs b/Source/RIMAPI/RimworldRestApi/Services/World/BuildingService/IBuildingService.cs similarity index 55% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IDataServices.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/BuildingService/IBuildingService.cs index 4dab645..1643368 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IDataServices.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/World/BuildingService/IBuildingService.cs @@ -1,21 +1,11 @@ - -using System.Collections.Generic; using RIMAPI.Core; using RIMAPI.Models; namespace RIMAPI.Services { - public interface IGameDataService - { - void RefreshCache(); - void UpdateGameTick(int currentTick); - } - public interface IBuildingService { ApiResult GetBuildingInfo(int buildingId); ApiResult SetBuildingPower(int buildingId, bool powerOn); } - - public interface IJobService { } } diff --git a/Source/RIMAPI/RimworldRestApi/Services/FactionService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/FactionService/FactionService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/FactionService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/FactionService/FactionService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IFactionService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/FactionService/IFactionService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IFactionService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/FactionService/IFactionService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/GlobalMapService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/GlobalMapService/GlobalMapService.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/Services/GlobalMapService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/GlobalMapService/GlobalMapService.cs index 2451d53..6b5b393 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/GlobalMapService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/World/GlobalMapService/GlobalMapService.cs @@ -3,7 +3,6 @@ using RIMAPI.Helpers; using RIMAPI.Models; using RimWorld.Planet; -using Verse; namespace RIMAPI.Services { diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IGlobalMapService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/GlobalMapService/IGlobalMapService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IGlobalMapService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/GlobalMapService/IGlobalMapService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IMapService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/MapService/IMapService.cs similarity index 94% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IMapService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/MapService/IMapService.cs index 429edff..6960165 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IMapService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/World/MapService/IMapService.cs @@ -35,5 +35,8 @@ public interface IMapService ApiResult CreateStockpile(CreateStockpileRequestDto request); ApiResult DeleteStockpile(int zoneId); ApiResult UpdateStockpile(UpdateStockpileRequestDto request); + + ApiResult GetCurrentMapDatetime(); + ApiResult GetWorldTileDatetime(int tileID); } } diff --git a/Source/RIMAPI/RimworldRestApi/Services/MapService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/MapService/MapService.cs similarity index 90% rename from Source/RIMAPI/RimworldRestApi/Services/MapService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/MapService/MapService.cs index 2a763bd..f45b221 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/MapService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/World/MapService/MapService.cs @@ -540,7 +540,7 @@ public ApiResult CreateStockpile(CreateStockpileRequestDto } var result = MapHelper.CreateStockpile(request); - + if (result.Success) { return ApiResult.Ok(result); @@ -560,5 +560,74 @@ public ApiResult CreateStockpile(CreateStockpileRequestDto public ApiResult DeleteStockpile(int zoneId) => MapHelper.DeleteStockpile(zoneId); public ApiResult UpdateStockpile(UpdateStockpileRequestDto request) => MapHelper.UpdateStockpile(request); + + + public static int GetMapTileId(Map map) + { +#if RIMWORLD_1_5 + return map.Tile; +#elif RIMWORLD_1_6 + return map.Tile.tileId; +#endif + throw new Exception("Failed to get GetMapTileId for this rimworld version."); + } + + public ApiResult GetCurrentMapDatetime() + { + try + { + var map = Find.CurrentMap; + if (map == null) + return ApiResult.Fail("No current map found"); + + var time = GetDatetimeAt(GetMapTileId(Find.CurrentMap)); + return ApiResult.Ok(time); + } + catch (Exception ex) + { + LogApi.Error($"Error getting current map datetime: {ex}"); + return ApiResult.Fail($"Failed to get datetime: {ex.Message}"); + } + } + + public ApiResult GetWorldTileDatetime(int tileID) + { + try + { + var time = GetDatetimeAt(tileID); + + return ApiResult.Ok(time); + } + catch (Exception ex) + { + LogApi.Error($"Error getting world tile datetime for tile {tileID}: {ex}"); + return ApiResult.Fail($"Failed to get datetime: {ex.Message}"); + } + } + + public MapTimeDto GetDatetimeAt(int tileID) + { + MapTimeDto mapTimeDto = new MapTimeDto(); + try + { + if (Current.ProgramState != ProgramState.Playing || Find.WorldGrid == null) + { + return mapTimeDto; + } + + var vector = Find.WorldGrid.LongLatOf(tileID); + mapTimeDto.Datetime = GenDate.DateFullStringWithHourAt( + Find.TickManager.TicksAbs, + vector + ); + + return mapTimeDto; + } + catch (Exception ex) + { + LogApi.Error($"Error - {ex.Message}"); + return mapTimeDto; + } + } } } diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IResourceService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/ResourceService/IResourceService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IResourceService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/ResourceService/IResourceService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/ResourceService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/ResourceService/ResourceService.cs similarity index 100% rename from Source/RIMAPI/RimworldRestApi/Services/ResourceService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/ResourceService/ResourceService.cs diff --git a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IThingsService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/ThingsService/IThingsService.cs similarity index 90% rename from Source/RIMAPI/RimworldRestApi/Services/Interfaces/IThingsService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/ThingsService/IThingsService.cs index 33a5f65..ddae457 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/Interfaces/IThingsService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/World/ThingsService/IThingsService.cs @@ -1,5 +1,3 @@ - -using System.Collections.Generic; using RIMAPI.Core; using RIMAPI.Models; diff --git a/Source/RIMAPI/RimworldRestApi/Services/ThingsService.cs b/Source/RIMAPI/RimworldRestApi/Services/World/ThingsService/ThingsService.cs similarity index 99% rename from Source/RIMAPI/RimworldRestApi/Services/ThingsService.cs rename to Source/RIMAPI/RimworldRestApi/Services/World/ThingsService/ThingsService.cs index ae915bb..c1ea1f4 100644 --- a/Source/RIMAPI/RimworldRestApi/Services/ThingsService.cs +++ b/Source/RIMAPI/RimworldRestApi/Services/World/ThingsService/ThingsService.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using System.Linq; using RIMAPI.Core; -using RIMAPI.Helpers; using RIMAPI.Models; -using RIMAPI.Services; using RimWorld; using Verse;