From 1f46f253dba48f7d3a5ba6befe083f3178df48e4 Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Tue, 30 Dec 2025 16:53:52 -0600 Subject: [PATCH 01/13] SSFW finalize --- .../MultiServerLibrary/HTTP/HTTPProcessor.cs | 7 + .../DataMigrator/DataMigrator.cs} | 32 +- Servers/SSFWServer/Helpers/Misc.cs | 290 ++++++++++++++++++ .../{SSFWGetFileList.cs => GetFileList.cs} | 4 +- .../{ => Helpers}/ScenelistParser.cs | 7 +- Servers/SSFWServer/Program.cs | 7 +- Servers/SSFWServer/SSFWMisc.cs | 90 ------ Servers/SSFWServer/SSFWProcessor.cs | 203 ++++++++---- Servers/SSFWServer/SSFWUserSessionManager.cs | 224 ++++++++++++++ ...ObjectService.cs => AdminObjectService.cs} | 24 +- Servers/SSFWServer/Services/AuditService.cs | 101 ++++++ ...ayoutService.cs => AvatarLayoutService.cs} | 21 +- Servers/SSFWServer/Services/AvatarService.cs | 12 + .../{SSFWClanService.cs => ClanService.cs} | 58 ++-- Servers/SSFWServer/Services/FriendsService.cs | 38 +++ .../IdentityService.cs} | 198 +++--------- .../SSFWServer/Services/KeepAliveService.cs | 32 ++ ...{SSFWLayoutService.cs => LayoutService.cs} | 11 +- .../Services/PlayerLookupService.cs | 17 + ...SFWRewardsService.cs => RewardsService.cs} | 245 +++++++++------ .../SSFWServer/Services/SSFWAuditService.cs | 41 --- Servers/SSFWServer/Services/TradingService.cs | 247 +++++++++++++++ 22 files changed, 1397 insertions(+), 512 deletions(-) rename Servers/SSFWServer/{SSFWDataMigrator.cs => Helpers/DataMigrator/DataMigrator.cs} (65%) create mode 100644 Servers/SSFWServer/Helpers/Misc.cs rename Servers/SSFWServer/Helpers/SaveDataHelper/{SSFWGetFileList.cs => GetFileList.cs} (93%) rename Servers/SSFWServer/{ => Helpers}/ScenelistParser.cs (96%) delete mode 100644 Servers/SSFWServer/SSFWMisc.cs create mode 100644 Servers/SSFWServer/SSFWUserSessionManager.cs rename Servers/SSFWServer/Services/{SSFWAdminObjectService.cs => AdminObjectService.cs} (51%) create mode 100644 Servers/SSFWServer/Services/AuditService.cs rename Servers/SSFWServer/Services/{SSFWAvatarLayoutService.cs => AvatarLayoutService.cs} (83%) create mode 100644 Servers/SSFWServer/Services/AvatarService.cs rename Servers/SSFWServer/Services/{SSFWClanService.cs => ClanService.cs} (51%) create mode 100644 Servers/SSFWServer/Services/FriendsService.cs rename Servers/SSFWServer/{SSFWLogin.cs => Services/IdentityService.cs} (74%) create mode 100644 Servers/SSFWServer/Services/KeepAliveService.cs rename Servers/SSFWServer/Services/{SSFWLayoutService.cs => LayoutService.cs} (96%) create mode 100644 Servers/SSFWServer/Services/PlayerLookupService.cs rename Servers/SSFWServer/Services/{SSFWRewardsService.cs => RewardsService.cs} (66%) delete mode 100644 Servers/SSFWServer/Services/SSFWAuditService.cs create mode 100644 Servers/SSFWServer/Services/TradingService.cs diff --git a/BackendServices/MultiServerLibrary/HTTP/HTTPProcessor.cs b/BackendServices/MultiServerLibrary/HTTP/HTTPProcessor.cs index 84278e85a..3fc2e7649 100644 --- a/BackendServices/MultiServerLibrary/HTTP/HTTPProcessor.cs +++ b/BackendServices/MultiServerLibrary/HTTP/HTTPProcessor.cs @@ -802,6 +802,13 @@ public static string DecodeUrl(string url) return newUrl; } + public static Uri ParseUriFromAbsolutePath(string input) + { + const string fakeDomain = "http://test.com"; + + return new Uri(fakeDomain + input); + } + // HTTP requires that responses contain the proper MIME type. This quick mapping list below // contains many more mimetypes than System.Web.MimeMapping diff --git a/Servers/SSFWServer/SSFWDataMigrator.cs b/Servers/SSFWServer/Helpers/DataMigrator/DataMigrator.cs similarity index 65% rename from Servers/SSFWServer/SSFWDataMigrator.cs rename to Servers/SSFWServer/Helpers/DataMigrator/DataMigrator.cs index dcc2d1db5..69ddf3178 100644 --- a/Servers/SSFWServer/SSFWDataMigrator.cs +++ b/Servers/SSFWServer/Helpers/DataMigrator/DataMigrator.cs @@ -1,8 +1,8 @@ using MultiServerLibrary.Extension; -namespace SSFWServer +namespace SSFWServer.Helpers.DataMigrator { - public class SSFWDataMigrator + public class DataMigrator { public static void MigrateSSFWData(string ssfwrootDirectory, string oldStr, string? newStr) { @@ -11,25 +11,23 @@ public static void MigrateSSFWData(string ssfwrootDirectory, string oldStr, stri foreach (string directory in new string[] { "/AvatarLayoutService", "/LayoutService", "/RewardsService", "/SaveDataService" }) { - foreach (FileSystemInfo item in FileSystemUtils.AllFilesAndFoldersLinq(new DirectoryInfo(ssfwrootDirectory + directory)).Where(item => item.FullName.Contains(oldStr))) + foreach (FileSystemInfo item in new DirectoryInfo(ssfwrootDirectory + directory).AllFilesAndFoldersLinq().Where(item => item.FullName.Contains(oldStr))) { // Construct the full path for the new file/folder in the target directory - string newFilePath = item.FullName.Replace(oldStr, newStr); + string newPath = item.FullName.Replace(oldStr, newStr); // Check if it's a file or directory and copy accordingly - if ((item is FileInfo fileInfo) && !File.Exists(newFilePath)) + if (item is FileInfo fileInfo && !File.Exists(newPath)) { - string? directoryPath = Path.GetDirectoryName(newFilePath); + string? directoryPath = Path.GetDirectoryName(newPath); if (!string.IsNullOrEmpty(directoryPath)) Directory.CreateDirectory(directoryPath); - File.Copy(item.FullName, newFilePath); - - FileSystemUtils.SetFileReadWrite(newFilePath); + File.Copy(item.FullName, newPath); } - else if ((item is DirectoryInfo directoryInfo) && !Directory.Exists(newFilePath)) - CopyDirectory(directoryInfo.FullName, newFilePath); + else if (item is DirectoryInfo directoryInfo && !Directory.Exists(newPath)) + CopyDirectory(directoryInfo.FullName, newPath); } } } @@ -41,17 +39,15 @@ private static void CopyDirectory(string source, string target) foreach (string file in Directory.GetFiles(source)) { - string newFilePath = Path.Combine(target, Path.GetFileName(file)); - if (!File.Exists(newFilePath)) + string destinationFile = Path.Combine(target, Path.GetFileName(file)); + if (!File.Exists(destinationFile)) { - string? directoryPath = Path.GetDirectoryName(newFilePath); + string? directoryPath = Path.GetDirectoryName(destinationFile); if (!string.IsNullOrEmpty(directoryPath)) Directory.CreateDirectory(directoryPath); - File.Copy(file, newFilePath); - - FileSystemUtils.SetFileReadWrite(newFilePath); + File.Copy(file, destinationFile); } } @@ -59,7 +55,9 @@ private static void CopyDirectory(string source, string target) { string destinationDirectory = Path.Combine(target, Path.GetFileName(directory)); if (!Directory.Exists(destinationDirectory)) + { CopyDirectory(directory, destinationDirectory); + } } } } diff --git a/Servers/SSFWServer/Helpers/Misc.cs b/Servers/SSFWServer/Helpers/Misc.cs new file mode 100644 index 000000000..f62cbd72c --- /dev/null +++ b/Servers/SSFWServer/Helpers/Misc.cs @@ -0,0 +1,290 @@ +namespace SSFWServer.Helpers +{ + public class Misc + { + public static readonly string LegacyLayoutTemplate = "[{\"00000000-00000000-00000000-00000004\":{\"version\":3,\"wallpaper\":2,\"furniture\":" + + "[{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000010\",\"instanceId\":\"4874595585\",\"itemId\":0,\"" + + "positionX\":-4.287144660949707,\"positionY\":2.9999580383300781,\"positionZ\":-2.3795166015625,\"rotationX\":2.6903744583" + + "250955E-06,\"rotationY\":0.70767402648925781,\"rotationZ\":-2.1571504476014525E-06,\"rotationW\":0.70653915405273438,\"ti" + + "me\":1686384673},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000002\",\"instanceId\":\"4874595586\"" + + ",\"itemId\":1,\"positionX\":-3.7360246181488037,\"positionY\":2.9999902248382568,\"positionZ\":-0.93418246507644653,\"rot" + + "ationX\":1.5251726836140733E-05,\"rotationY\":0.92014747858047485,\"rotationZ\":-0.00032892703893594444,\"rotationW\":0.3" + + "9157184958457947,\"time\":1686384699},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000002\",\"instan" + + "ceId\":\"4874595587\",\"itemId\":2,\"positionX\":-4.2762022018432617,\"positionY\":2.9999568462371826,\"positionZ\":-4.15" + + "23990631103516,\"rotationX\":1.4554960570123399E-09,\"rotationY\":0.4747755229473114,\"rotationZ\":-1.4769816480963982E-0" + + "8,\"rotationW\":0.88010692596435547,\"time\":1686384723},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-" + + "00000002\",\"instanceId\":\"4874595588\",\"itemId\":3,\"positionX\":-2.8646721839904785,\"positionY\":2.9999570846557617," + + "\"positionZ\":-3.0560495853424072,\"rotationX\":0.00010053320875158533,\"rotationY\":-0.26336261630058289,\"rotationZ\":-" + + "3.8589099858654663E-05,\"rotationW\":0.96469688415527344,\"time\":1686384751},{\"flags\":0,\"furnitureObjectId\":\"000000" + + "00-00000000-00000002-00000001\",\"instanceId\":\"4874595589\",\"itemId\":4,\"positionX\":3.9096813201904297,\"positionY\"" + + ":2.9995136260986328,\"positionZ\":-4.2813630104064941,\"rotationX\":4.3287433072691783E-05,\"rotationY\":-0.5309971570968" + + "6279,\"rotationZ\":-3.9187150832731277E-05,\"rotationW\":0.8473736047744751,\"time\":1686384774},{\"flags\":0,\"furniture" + + "ObjectId\":\"00000000-00000000-00000002-00000004\",\"instanceId\":\"4874595590\",\"itemId\":5,\"positionX\":1.84187448024" + + "74976,\"positionY\":3.0001647472381592,\"positionZ\":-3.2746503353118896,\"rotationX\":-5.4990476201055571E-05,\"rotation" + + "Y\":-0.53177982568740845,\"rotationZ\":-1.335094293608563E-05,\"rotationW\":0.84688264131546021,\"time\":1686384795},{\"f" + + "lags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000008\",\"instanceId\":\"4874595591\",\"itemId\":6,\"posit" + + "ionX\":3.4726400375366211,\"positionY\":3.0000433921813965,\"positionZ\":4.783566951751709,\"rotationX\":6.13473239354789" + + "26E-05,\"rotationY\":0.99999260902404785,\"rotationZ\":-1.7070769899873994E-05,\"rotationW\":0.0038405421655625105,\"time" + + "\":1686384822},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000008\",\"instanceId\":\"4874595592\"," + + "\"itemId\":7,\"positionX\":3.4952659606933594,\"positionY\":3.0000007152557373,\"positionZ\":0.2776024341583252,\"rotatio" + + "nX\":-1.2929040167364292E-05,\"rotationY\":-0.0061355167999863625,\"rotationZ\":-4.4378830352798104E-05,\"rotationW\":0.9" + + "99981164932251,\"time\":1686384834},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000001\",\"instance" + + "Id\":\"4874595593\",\"itemId\":8,\"positionX\":1.3067165613174438,\"positionY\":2.9994897842407227,\"positionZ\":2.546649" + + "694442749,\"rotationX\":2.8451957405195571E-05,\"rotationY\":0.70562022924423218,\"rotationZ\":-8.0827621786738746E-06,\"" + + "rotationW\":0.70859026908874512,\"time\":1686384862},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-0000" + + "0003\",\"instanceId\":\"4874595594\",\"itemId\":9,\"positionX\":3.4803681373596191,\"positionY\":2.9999568462371826,\"pos" + + "itionZ\":2.5385856628417969,\"rotationX\":3.1659130428352E-08,\"rotationY\":-0.70712763071060181,\"rotationZ\":8.14428204" + + "87609424E-08,\"rotationW\":0.70708584785461426,\"time\":1686384884},{\"flags\":0,\"furnitureObjectId\":\"00000000-0000000" + + "0-00000002-00000009\",\"instanceId\":\"4874595595\",\"itemId\":10,\"positionX\":-3.5043892860412598,\"positionY\":2.99995" + + "68462371826,\"positionZ\":-9.527653694152832,\"rotationX\":-1.7184934222314041E-06,\"rotationY\":0.00023035785125102848,\"" + + "rotationZ\":2.5227839728358958E-07,\"rotationW\":0.99999994039535522,\"time\":1686384912},{\"flags\":0,\"furnitureObjectI" + + "d\":\"00000000-00000000-00000002-00000009\",\"instanceId\":\"4874595596\",\"itemId\":11,\"positionX\":3.6248698234558105," + + "\"positionY\":2.9999566078186035,\"positionZ\":-9.5347089767456055,\"rotationX\":-2.1324558474589139E-07,\"rotationY\":2." + + "0361580027383752E-05,\"rotationZ\":-4.7822368287597783E-08,\"rotationW\":1,\"time\":1686384931},{\"flags\":0,\"furnitureO" + + "bjectId\":\"00000000-00000000-00000002-00000005\",\"instanceId\":\"4874595597\",\"itemId\":12,\"positionX\":-3.5068926811" + + "218262,\"positionY\":3.4883472919464111,\"positionZ\":-9.5313901901245117,\"rotationX\":-0.00091801158851012588,\"rotatio" + + "nY\":0.006055513396859169,\"rotationZ\":0.000585820700507611,\"rotationW\":0.99998104572296143,\"time\":1686384961,\"phot" + + "o\":\"/Furniture/Modern2/lampOutputcube.dds\"},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000005\"" + + ",\"instanceId\":\"4874595598\",\"itemId\":13,\"positionX\":3.6171293258666992,\"positionY\":3.4891724586486816,\"position" + + "Z\":-9.53490161895752,\"rotationX\":0.00042979296995326877,\"rotationY\":-0.0092521701008081436,\"rotationZ\":-0.00027207" + + "753737457097,\"rotationW\":0.99995702505111694,\"time\":1686385008,\"photo\":\"/Furniture/Modern2/lampOutputcube.dds\"}]}}]"; + + public static readonly string HarbourStudioLayout = "{\r\n" + + " \"version\": 3,\r\n" + + " \"wallpaper\": 2,\r\n" + + " \"furniture\": [\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000010\",\r\n" + + " \"instanceId\": \"4874595585\",\r\n" + + " \"itemId\": 0,\r\n" + + " \"positionX\": -4.287144660949707,\r\n" + + " \"positionY\": 2.999958038330078,\r\n" + + " \"positionZ\": -2.3795166015625,\r\n" + + " \"rotationX\": 2.6903744583250955E-06,\r\n" + + " \"rotationY\": 0.7076740264892578,\r\n" + + " \"rotationZ\": -2.1571504476014525E-06,\r\n" + + " \"rotationW\": 0.7065391540527344,\r\n" + + " \"time\": 1686384673\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000002\",\r\n" + + " \"instanceId\": \"4874595586\",\r\n" + + " \"itemId\": 1,\r\n" + + " \"positionX\": -3.7360246181488037,\r\n" + + " \"positionY\": 2.999990224838257,\r\n" + + " \"positionZ\": -0.9341824650764465,\r\n" + + " \"rotationX\": 1.5251726836140733E-05,\r\n" + + " \"rotationY\": 0.9201474785804749,\r\n" + + " \"rotationZ\": -0.00032892703893594444,\r\n" + + " \"rotationW\": 0.39157184958457947,\r\n" + + " \"time\": 1686384699\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000002\",\r\n" + + " \"instanceId\": \"4874595587\",\r\n" + + " \"itemId\": 2,\r\n" + + " \"positionX\": -4.276202201843262,\r\n" + + " \"positionY\": 2.9999568462371826,\r\n" + + " \"positionZ\": -4.152399063110352,\r\n" + + " \"rotationX\": 1.4554960570123399E-09,\r\n" + + " \"rotationY\": 0.4747755229473114,\r\n" + + " \"rotationZ\": -1.4769816480963982E-08,\r\n" + + " \"rotationW\": 0.8801069259643555,\r\n" + + " \"time\": 1686384723\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000002\",\r\n" + + " \"instanceId\": \"4874595588\",\r\n" + + " \"itemId\": 3,\r\n" + + " \"positionX\": -2.8646721839904785,\r\n" + + " \"positionY\": 2.9999570846557617,\r\n" + + " \"positionZ\": -3.0560495853424072,\r\n" + + " \"rotationX\": 0.00010053320875158533,\r\n" + + " \"rotationY\": -0.2633626163005829,\r\n" + + " \"rotationZ\": -3.858909985865466E-05,\r\n" + + " \"rotationW\": 0.9646968841552734,\r\n" + + " \"time\": 1686384751\r\n },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000001\",\r\n" + + " \"instanceId\": \"4874595589\",\r\n" + + " \"itemId\": 4,\r\n" + + " \"positionX\": 3.9096813201904297,\r\n" + + " \"positionY\": 2.999513626098633,\r\n" + + " \"positionZ\": -4.281363010406494,\r\n" + + " \"rotationX\": 4.328743307269178E-05,\r\n" + + " \"rotationY\": -0.5309971570968628,\r\n" + + " \"rotationZ\": -3.918715083273128E-05,\r\n" + + " \"rotationW\": 0.8473736047744751,\r\n" + + " \"time\": 1686384774\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000004\",\r\n" + + " \"instanceId\": \"4874595590\",\r\n" + + " \"itemId\": 5,\r\n" + + " \"positionX\": 1.8418744802474976,\r\n" + + " \"positionY\": 3.000164747238159,\r\n" + + " \"positionZ\": -3.2746503353118896,\r\n" + + " \"rotationX\": -5.499047620105557E-05,\r\n" + + " \"rotationY\": -0.5317798256874084,\r\n" + + " \"rotationZ\": -1.335094293608563E-05,\r\n" + + " \"rotationW\": 0.8468826413154602,\r\n" + + " \"time\": 1686384795\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000008\",\r\n" + + " \"instanceId\": \"4874595591\",\r\n" + + " \"itemId\": 6,\r\n" + + " \"positionX\": 3.472640037536621,\r\n" + + " \"positionY\": 3.0000433921813965,\r\n" + + " \"positionZ\": 4.783566951751709,\r\n" + + " \"rotationX\": 6.134732393547893E-05,\r\n" + + " \"rotationY\": 0.9999926090240479,\r\n" + + " \"rotationZ\": -1.7070769899873994E-05,\r\n" + + " \"rotationW\": 0.0038405421655625105,\r\n" + + " \"time\": 1686384822\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000008\",\r\n" + + " \"instanceId\": \"4874595592\",\r\n \"itemId\": 7,\r\n" + + " \"positionX\": 3.4952659606933594,\r\n" + + " \"positionY\": 3.0000007152557373,\r\n" + + " \"positionZ\": 0.2776024341583252,\r\n" + + " \"rotationX\": -1.2929040167364292E-05,\r\n" + + " \"rotationY\": -0.0061355167999863625,\r\n" + + " \"rotationZ\": -4.4378830352798104E-05,\r\n" + + " \"rotationW\": 0.999981164932251,\r\n" + + " \"time\": 1686384834\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000001\",\r\n" + + " \"instanceId\": \"4874595593\",\r\n" + + " \"itemId\": 8,\r\n" + + " \"positionX\": 1.3067165613174438,\r\n" + + " \"positionY\": 2.9994897842407227,\r\n" + + " \"positionZ\": 2.546649694442749,\r\n" + + " \"rotationX\": 2.845195740519557E-05,\r\n" + + " \"rotationY\": 0.7056202292442322,\r\n" + + " \"rotationZ\": -8.082762178673875E-06,\r\n" + + " \"rotationW\": 0.7085902690887451,\r\n" + + " \"time\": 1686384862\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000003\",\r\n" + + " \"instanceId\": \"4874595594\",\r\n" + + " \"itemId\": 9,\r\n \"positionX\": 3.480368137359619,\r\n" + + " \"positionY\": 2.9999568462371826,\r\n" + + " \"positionZ\": 2.538585662841797,\r\n" + + " \"rotationX\": 3.1659130428352E-08,\r\n" + + " \"rotationY\": -0.7071276307106018,\r\n" + + " \"rotationZ\": 8.144282048760942E-08,\r\n" + + " \"rotationW\": 0.7070858478546143,\r\n" + + " \"time\": 1686384884\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000009\",\r\n" + + " \"instanceId\": \"4874595595\",\r\n" + + " \"itemId\": 10,\r\n" + + " \"positionX\": -3.5043892860412598,\r\n" + + " \"positionY\": 2.9999568462371826,\r\n" + + " \"positionZ\": -9.527653694152832,\r\n" + + " \"rotationX\": -1.7184934222314041E-06,\r\n" + + " \"rotationY\": 0.00023035785125102848,\r\n" + + " \"rotationZ\": 2.522783972835896E-07,\r\n" + + " \"rotationW\": 0.9999999403953552,\r\n" + + " \"time\": 1686384912\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000009\",\r\n" + + " \"instanceId\": \"4874595596\",\r\n" + + " \"itemId\": 11,\r\n" + + " \"positionX\": 3.6248698234558105,\r\n" + + " \"positionY\": 2.9999566078186035,\r\n" + + " \"positionZ\": -9.534708976745605,\r\n" + + " \"rotationX\": -2.132455847458914E-07,\r\n" + + " \"rotationY\": 2.0361580027383752E-05,\r\n" + + " \"rotationZ\": -4.782236828759778E-08,\r\n" + + " \"rotationW\": 1,\r\n" + + " \"time\": 1686384931\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000005\",\r\n" + + " \"instanceId\": \"4874595597\",\r\n" + + " \"itemId\": 12,\r\n" + + " \"positionX\": -3.506892681121826,\r\n" + + " \"positionY\": 3.488347291946411,\r\n" + + " \"positionZ\": -9.531390190124512,\r\n" + + " \"rotationX\": -0.0009180115885101259,\r\n" + + " \"rotationY\": 0.006055513396859169,\r\n" + + " \"rotationZ\": 0.000585820700507611,\r\n" + + " \"rotationW\": 0.9999810457229614,\r\n" + + " \"time\": 1686384961,\r\n" + + " \"photo\": \"/Furniture/Modern2/lampOutputcube.dds\"\r\n" + + " },\r\n" + + " {\r\n" + + " \"flags\": 0,\r\n" + + " \"furnitureObjectId\": \"00000000-00000000-00000002-00000005\",\r\n" + + " \"instanceId\": \"4874595598\",\r\n" + + " \"itemId\": 13,\r\n" + + " \"positionX\": 3.617129325866699,\r\n" + + " \"positionY\": 3.4891724586486816,\r\n" + + " \"positionZ\": -9.53490161895752,\r\n" + + " \"rotationX\": 0.00042979296995326877,\r\n" + + " \"rotationY\": -0.009252170100808144,\r\n" + + " \"rotationZ\": -0.00027207753737457097,\r\n" + + " \"rotationW\": 0.9999570250511169,\r\n" + + " \"time\": 1686385008,\r\n" + + " \"photo\": \"/Furniture/Modern2/lampOutputcube.dds\"\r\n" + + " }\r\n" + + " ]\r\n" + + "}"; + + // Sandbox Environments + public static List homeEnvs = new() + { + "cprod", "cprodts", "cpreprod", "cpreprodb", + "rc-qa", "rcdev", "rc-dev", "cqa-e", + "cqa-a", "cqa-j", "cqa-h", "cqab-e", + "cqab-a", "cqab-j", "cqab-h", "qcqa-e", + "qcqa-a", "qcqa-j", "qcqa-h", "qcpreprod", + "qcqab-e", "qcqab-a", "qcqab-j", "qcqab-h", + "qcpreprodb", "coredev", "core-dev", "core-qa", + "cdev", "cdev2", "cdev3", "cdeva", "cdevb", "cdevc" + }; + + /// + /// Extract a portion of a string winthin boundaries. + /// Extrait une portion d'un string entre des limites. + /// + /// The input string. + /// The amount of characters to remove from the left to the right. + /// The amount of characters to remove from the right to the left. + /// A string. + public static string? ExtractPortion(string input, int startToRemove, int endToRemove) + { + if (input.Length < startToRemove + endToRemove) + return null; + + return input[startToRemove..][..^endToRemove]; + } + } + + public class SSFWUserData + { + public string? Username { get; set; } + public int LogonCount { get; set; } + public int IGA { get; set; } + } +} diff --git a/Servers/SSFWServer/Helpers/SaveDataHelper/SSFWGetFileList.cs b/Servers/SSFWServer/Helpers/SaveDataHelper/GetFileList.cs similarity index 93% rename from Servers/SSFWServer/Helpers/SaveDataHelper/SSFWGetFileList.cs rename to Servers/SSFWServer/Helpers/SaveDataHelper/GetFileList.cs index 438de9734..b560da01b 100644 --- a/Servers/SSFWServer/Helpers/SaveDataHelper/SSFWGetFileList.cs +++ b/Servers/SSFWServer/Helpers/SaveDataHelper/GetFileList.cs @@ -3,9 +3,9 @@ namespace SSFWServer.SaveDataHelper { - public static class SSFWGetFileList + public static class GetFileList { - public static string? SSFWSaveDataDebugGetFileList(string directoryPath, string? segment) + public static string? SaveDataDebugGetFileList(string directoryPath, string? segment) { try { diff --git a/Servers/SSFWServer/ScenelistParser.cs b/Servers/SSFWServer/Helpers/ScenelistParser.cs similarity index 96% rename from Servers/SSFWServer/ScenelistParser.cs rename to Servers/SSFWServer/Helpers/ScenelistParser.cs index de84a11e5..2772bf086 100644 --- a/Servers/SSFWServer/ScenelistParser.cs +++ b/Servers/SSFWServer/Helpers/ScenelistParser.cs @@ -2,7 +2,7 @@ using System.Collections.Concurrent; using System.Xml; -namespace SSFWServer +namespace SSFWServer.Helpers { public class ScenelistParser { @@ -34,7 +34,8 @@ public static void UpdateSceneDictionary(object? state) if (sceneNodes != null) { // Extract ChannelID and SCENE ID from each SCENE element - Parallel.ForEach(sceneNodes.Cast(), sceneNode => { + Parallel.ForEach(sceneNodes.Cast(), sceneNode => + { if (sceneNode.Attributes != null) { string? ID = sceneNode.Attributes["ID"]?.Value; @@ -77,7 +78,7 @@ public static void UpdateSceneDictionary(object? state) } } } - catch + catch (Exception) { // Not Important } diff --git a/Servers/SSFWServer/Program.cs b/Servers/SSFWServer/Program.cs index f9108a641..8420c9fac 100644 --- a/Servers/SSFWServer/Program.cs +++ b/Servers/SSFWServer/Program.cs @@ -4,10 +4,13 @@ using System.Reflection; using System.Runtime; using System.Security.Cryptography; -using MultiServerLibrary.SNMP; -using MultiServerLibrary; using Microsoft.Extensions.Logging; +using SSFWServer.Helpers; using MultiServerLibrary.Extension; +using MultiServerLibrary.HTTP; +using MultiServerLibrary.SSL; +using MultiServerLibrary; +using MultiServerLibrary.SNMP; public static class SSFWServerConfiguration { diff --git a/Servers/SSFWServer/SSFWMisc.cs b/Servers/SSFWServer/SSFWMisc.cs deleted file mode 100644 index c26a0ed73..000000000 --- a/Servers/SSFWServer/SSFWMisc.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace SSFWServer -{ - public class SSFWMisc - { - public static readonly string LegacyLayoutTemplate = "[{\"00000000-00000000-00000000-00000004\":{\"version\":3,\"wallpaper\":2,\"furniture\":" + - "[{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000010\",\"instanceId\":\"4874595585\",\"itemId\":0,\"" + - "positionX\":-4.287144660949707,\"positionY\":2.9999580383300781,\"positionZ\":-2.3795166015625,\"rotationX\":2.6903744583" + - "250955E-06,\"rotationY\":0.70767402648925781,\"rotationZ\":-2.1571504476014525E-06,\"rotationW\":0.70653915405273438,\"ti" + - "me\":1686384673},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000002\",\"instanceId\":\"4874595586\"" + - ",\"itemId\":1,\"positionX\":-3.7360246181488037,\"positionY\":2.9999902248382568,\"positionZ\":-0.93418246507644653,\"rot" + - "ationX\":1.5251726836140733E-05,\"rotationY\":0.92014747858047485,\"rotationZ\":-0.00032892703893594444,\"rotationW\":0.3" + - "9157184958457947,\"time\":1686384699},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000002\",\"instan" + - "ceId\":\"4874595587\",\"itemId\":2,\"positionX\":-4.2762022018432617,\"positionY\":2.9999568462371826,\"positionZ\":-4.15" + - "23990631103516,\"rotationX\":1.4554960570123399E-09,\"rotationY\":0.4747755229473114,\"rotationZ\":-1.4769816480963982E-0" + - "8,\"rotationW\":0.88010692596435547,\"time\":1686384723},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-" + - "00000002\",\"instanceId\":\"4874595588\",\"itemId\":3,\"positionX\":-2.8646721839904785,\"positionY\":2.9999570846557617," + - "\"positionZ\":-3.0560495853424072,\"rotationX\":0.00010053320875158533,\"rotationY\":-0.26336261630058289,\"rotationZ\":-" + - "3.8589099858654663E-05,\"rotationW\":0.96469688415527344,\"time\":1686384751},{\"flags\":0,\"furnitureObjectId\":\"000000" + - "00-00000000-00000002-00000001\",\"instanceId\":\"4874595589\",\"itemId\":4,\"positionX\":3.9096813201904297,\"positionY\"" + - ":2.9995136260986328,\"positionZ\":-4.2813630104064941,\"rotationX\":4.3287433072691783E-05,\"rotationY\":-0.5309971570968" + - "6279,\"rotationZ\":-3.9187150832731277E-05,\"rotationW\":0.8473736047744751,\"time\":1686384774},{\"flags\":0,\"furniture" + - "ObjectId\":\"00000000-00000000-00000002-00000004\",\"instanceId\":\"4874595590\",\"itemId\":5,\"positionX\":1.84187448024" + - "74976,\"positionY\":3.0001647472381592,\"positionZ\":-3.2746503353118896,\"rotationX\":-5.4990476201055571E-05,\"rotation" + - "Y\":-0.53177982568740845,\"rotationZ\":-1.335094293608563E-05,\"rotationW\":0.84688264131546021,\"time\":1686384795},{\"f" + - "lags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000008\",\"instanceId\":\"4874595591\",\"itemId\":6,\"posit" + - "ionX\":3.4726400375366211,\"positionY\":3.0000433921813965,\"positionZ\":4.783566951751709,\"rotationX\":6.13473239354789" + - "26E-05,\"rotationY\":0.99999260902404785,\"rotationZ\":-1.7070769899873994E-05,\"rotationW\":0.0038405421655625105,\"time" + - "\":1686384822},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000008\",\"instanceId\":\"4874595592\"," + - "\"itemId\":7,\"positionX\":3.4952659606933594,\"positionY\":3.0000007152557373,\"positionZ\":0.2776024341583252,\"rotatio" + - "nX\":-1.2929040167364292E-05,\"rotationY\":-0.0061355167999863625,\"rotationZ\":-4.4378830352798104E-05,\"rotationW\":0.9" + - "99981164932251,\"time\":1686384834},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000001\",\"instance" + - "Id\":\"4874595593\",\"itemId\":8,\"positionX\":1.3067165613174438,\"positionY\":2.9994897842407227,\"positionZ\":2.546649" + - "694442749,\"rotationX\":2.8451957405195571E-05,\"rotationY\":0.70562022924423218,\"rotationZ\":-8.0827621786738746E-06,\"" + - "rotationW\":0.70859026908874512,\"time\":1686384862},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-0000" + - "0003\",\"instanceId\":\"4874595594\",\"itemId\":9,\"positionX\":3.4803681373596191,\"positionY\":2.9999568462371826,\"pos" + - "itionZ\":2.5385856628417969,\"rotationX\":3.1659130428352E-08,\"rotationY\":-0.70712763071060181,\"rotationZ\":8.14428204" + - "87609424E-08,\"rotationW\":0.70708584785461426,\"time\":1686384884},{\"flags\":0,\"furnitureObjectId\":\"00000000-0000000" + - "0-00000002-00000009\",\"instanceId\":\"4874595595\",\"itemId\":10,\"positionX\":-3.5043892860412598,\"positionY\":2.99995" + - "68462371826,\"positionZ\":-9.527653694152832,\"rotationX\":-1.7184934222314041E-06,\"rotationY\":0.00023035785125102848,\"" + - "rotationZ\":2.5227839728358958E-07,\"rotationW\":0.99999994039535522,\"time\":1686384912},{\"flags\":0,\"furnitureObjectI" + - "d\":\"00000000-00000000-00000002-00000009\",\"instanceId\":\"4874595596\",\"itemId\":11,\"positionX\":3.6248698234558105," + - "\"positionY\":2.9999566078186035,\"positionZ\":-9.5347089767456055,\"rotationX\":-2.1324558474589139E-07,\"rotationY\":2." + - "0361580027383752E-05,\"rotationZ\":-4.7822368287597783E-08,\"rotationW\":1,\"time\":1686384931},{\"flags\":0,\"furnitureO" + - "bjectId\":\"00000000-00000000-00000002-00000005\",\"instanceId\":\"4874595597\",\"itemId\":12,\"positionX\":-3.5068926811" + - "218262,\"positionY\":3.4883472919464111,\"positionZ\":-9.5313901901245117,\"rotationX\":-0.00091801158851012588,\"rotatio" + - "nY\":0.006055513396859169,\"rotationZ\":0.000585820700507611,\"rotationW\":0.99998104572296143,\"time\":1686384961,\"phot" + - "o\":\"/Furniture/Modern2/lampOutputcube.dds\"},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000005\"" + - ",\"instanceId\":\"4874595598\",\"itemId\":13,\"positionX\":3.6171293258666992,\"positionY\":3.4891724586486816,\"position" + - "Z\":-9.53490161895752,\"rotationX\":0.00042979296995326877,\"rotationY\":-0.0092521701008081436,\"rotationZ\":-0.00027207" + - "753737457097,\"rotationW\":0.99995702505111694,\"time\":1686385008,\"photo\":\"/Furniture/Modern2/lampOutputcube.dds\"}]}}]"; - - public static readonly string HarbourStudioLayout = "{\r\n \"version\": 3,\r\n \"wallpaper\": 2,\r\n \"furniture\": [\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000010\",\r\n \"instanceId\": \"4874595585\",\r\n \"itemId\": 0,\r\n \"positionX\": -4.287144660949707,\r\n \"positionY\": 2.999958038330078,\r\n \"positionZ\": -2.3795166015625,\r\n \"rotationX\": 2.6903744583250955E-06,\r\n \"rotationY\": 0.7076740264892578,\r\n \"rotationZ\": -2.1571504476014525E-06,\r\n \"rotationW\": 0.7065391540527344,\r\n \"time\": 1686384673\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000002\",\r\n \"instanceId\": \"4874595586\",\r\n \"itemId\": 1,\r\n \"positionX\": -3.7360246181488037,\r\n \"positionY\": 2.999990224838257,\r\n \"positionZ\": -0.9341824650764465,\r\n \"rotationX\": 1.5251726836140733E-05,\r\n \"rotationY\": 0.9201474785804749,\r\n \"rotationZ\": -0.00032892703893594444,\r\n \"rotationW\": 0.39157184958457947,\r\n \"time\": 1686384699\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000002\",\r\n \"instanceId\": \"4874595587\",\r\n \"itemId\": 2,\r\n \"positionX\": -4.276202201843262,\r\n \"positionY\": 2.9999568462371826,\r\n \"positionZ\": -4.152399063110352,\r\n \"rotationX\": 1.4554960570123399E-09,\r\n \"rotationY\": 0.4747755229473114,\r\n \"rotationZ\": -1.4769816480963982E-08,\r\n \"rotationW\": 0.8801069259643555,\r\n \"time\": 1686384723\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000002\",\r\n \"instanceId\": \"4874595588\",\r\n \"itemId\": 3,\r\n \"positionX\": -2.8646721839904785,\r\n \"positionY\": 2.9999570846557617,\r\n \"positionZ\": -3.0560495853424072,\r\n \"rotationX\": 0.00010053320875158533,\r\n \"rotationY\": -0.2633626163005829,\r\n \"rotationZ\": -3.858909985865466E-05,\r\n \"rotationW\": 0.9646968841552734,\r\n \"time\": 1686384751\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000001\",\r\n \"instanceId\": \"4874595589\",\r\n \"itemId\": 4,\r\n \"positionX\": 3.9096813201904297,\r\n \"positionY\": 2.999513626098633,\r\n \"positionZ\": -4.281363010406494,\r\n \"rotationX\": 4.328743307269178E-05,\r\n \"rotationY\": -0.5309971570968628,\r\n \"rotationZ\": -3.918715083273128E-05,\r\n \"rotationW\": 0.8473736047744751,\r\n \"time\": 1686384774\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000004\",\r\n \"instanceId\": \"4874595590\",\r\n \"itemId\": 5,\r\n \"positionX\": 1.8418744802474976,\r\n \"positionY\": 3.000164747238159,\r\n \"positionZ\": -3.2746503353118896,\r\n \"rotationX\": -5.499047620105557E-05,\r\n \"rotationY\": -0.5317798256874084,\r\n \"rotationZ\": -1.335094293608563E-05,\r\n \"rotationW\": 0.8468826413154602,\r\n \"time\": 1686384795\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000008\",\r\n \"instanceId\": \"4874595591\",\r\n \"itemId\": 6,\r\n \"positionX\": 3.472640037536621,\r\n \"positionY\": 3.0000433921813965,\r\n \"positionZ\": 4.783566951751709,\r\n \"rotationX\": 6.134732393547893E-05,\r\n \"rotationY\": 0.9999926090240479,\r\n \"rotationZ\": -1.7070769899873994E-05,\r\n \"rotationW\": 0.0038405421655625105,\r\n \"time\": 1686384822\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000008\",\r\n \"instanceId\": \"4874595592\",\r\n \"itemId\": 7,\r\n \"positionX\": 3.4952659606933594,\r\n \"positionY\": 3.0000007152557373,\r\n \"positionZ\": 0.2776024341583252,\r\n \"rotationX\": -1.2929040167364292E-05,\r\n \"rotationY\": -0.0061355167999863625,\r\n \"rotationZ\": -4.4378830352798104E-05,\r\n \"rotationW\": 0.999981164932251,\r\n \"time\": 1686384834\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000001\",\r\n \"instanceId\": \"4874595593\",\r\n \"itemId\": 8,\r\n \"positionX\": 1.3067165613174438,\r\n \"positionY\": 2.9994897842407227,\r\n \"positionZ\": 2.546649694442749,\r\n \"rotationX\": 2.845195740519557E-05,\r\n \"rotationY\": 0.7056202292442322,\r\n \"rotationZ\": -8.082762178673875E-06,\r\n \"rotationW\": 0.7085902690887451,\r\n \"time\": 1686384862\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000003\",\r\n \"instanceId\": \"4874595594\",\r\n \"itemId\": 9,\r\n \"positionX\": 3.480368137359619,\r\n \"positionY\": 2.9999568462371826,\r\n \"positionZ\": 2.538585662841797,\r\n \"rotationX\": 3.1659130428352E-08,\r\n \"rotationY\": -0.7071276307106018,\r\n \"rotationZ\": 8.144282048760942E-08,\r\n \"rotationW\": 0.7070858478546143,\r\n \"time\": 1686384884\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000009\",\r\n \"instanceId\": \"4874595595\",\r\n \"itemId\": 10,\r\n \"positionX\": -3.5043892860412598,\r\n \"positionY\": 2.9999568462371826,\r\n \"positionZ\": -9.527653694152832,\r\n \"rotationX\": -1.7184934222314041E-06,\r\n \"rotationY\": 0.00023035785125102848,\r\n \"rotationZ\": 2.522783972835896E-07,\r\n \"rotationW\": 0.9999999403953552,\r\n \"time\": 1686384912\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000009\",\r\n \"instanceId\": \"4874595596\",\r\n \"itemId\": 11,\r\n \"positionX\": 3.6248698234558105,\r\n \"positionY\": 2.9999566078186035,\r\n \"positionZ\": -9.534708976745605,\r\n \"rotationX\": -2.132455847458914E-07,\r\n \"rotationY\": 2.0361580027383752E-05,\r\n \"rotationZ\": -4.782236828759778E-08,\r\n \"rotationW\": 1,\r\n \"time\": 1686384931\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000005\",\r\n \"instanceId\": \"4874595597\",\r\n \"itemId\": 12,\r\n \"positionX\": -3.506892681121826,\r\n \"positionY\": 3.488347291946411,\r\n \"positionZ\": -9.531390190124512,\r\n \"rotationX\": -0.0009180115885101259,\r\n \"rotationY\": 0.006055513396859169,\r\n \"rotationZ\": 0.000585820700507611,\r\n \"rotationW\": 0.9999810457229614,\r\n \"time\": 1686384961,\r\n \"photo\": \"/Furniture/Modern2/lampOutputcube.dds\"\r\n },\r\n {\r\n \"flags\": 0,\r\n \"furnitureObjectId\": \"00000000-00000000-00000002-00000005\",\r\n \"instanceId\": \"4874595598\",\r\n \"itemId\": 13,\r\n \"positionX\": 3.617129325866699,\r\n \"positionY\": 3.4891724586486816,\r\n \"positionZ\": -9.53490161895752,\r\n \"rotationX\": 0.00042979296995326877,\r\n \"rotationY\": -0.009252170100808144,\r\n \"rotationZ\": -0.00027207753737457097,\r\n \"rotationW\": 0.9999570250511169,\r\n \"time\": 1686385008,\r\n \"photo\": \"/Furniture/Modern2/lampOutputcube.dds\"\r\n }\r\n ]\r\n}"; - - // Sandbox Environments - public static List homeEnvs = new() - { - "cprod", "cprodts", "cpreprod", "cpreprodb", - "rc-qa", "rcdev", "rc-dev", "cqa-e", - "cqa-a", "cqa-j", "cqa-h", "cqab-e", - "cqab-a", "cqab-j", "cqab-h", "qcqa-e", - "qcqa-a", "qcqa-j", "qcqa-h", "qcpreprod", - "qcqab-e", "qcqab-a", "qcqab-j", "qcqab-h", - "qcpreprodb", "coredev", "core-dev", "core-qa", - "cdev", "cdev2", "cdev3", "cdeva", "cdevb", "cdevc" - }; - - /// - /// Extract a portion of a string winthin boundaries. - /// Extrait une portion d'un string entre des limites. - /// - /// The input string. - /// The amount of characters to remove from the left to the right. - /// The amount of characters to remove from the right to the left. - /// A string. - public static string? ExtractPortion(string input, int startToRemove, int endToRemove) - { - if (input.Length < startToRemove + endToRemove) - return null; - - return input[startToRemove..][..^endToRemove]; - } - } - - public class SSFWUserData - { - public string? Username { get; set; } - public int LogonCount { get; set; } - public int IGA { get; set; } - } -} diff --git a/Servers/SSFWServer/SSFWProcessor.cs b/Servers/SSFWServer/SSFWProcessor.cs index eb9fcdc26..e9efa9846 100644 --- a/Servers/SSFWServer/SSFWProcessor.cs +++ b/Servers/SSFWServer/SSFWProcessor.cs @@ -4,6 +4,8 @@ using MultiServerLibrary.SSL; using NetCoreServer; using NetCoreServer.CustomServers; +using Org.BouncyCastle.Ocsp; +using SSFWServer.Helpers; using SSFWServer.Helpers.FileHelper; using SSFWServer.SaveDataHelper; using SSFWServer.Services; @@ -15,7 +17,6 @@ using System.Security.Cryptography.X509Certificates; using System.Text.Json; using System.Text.RegularExpressions; -using Tpm2Lib; namespace SSFWServer { @@ -37,7 +38,7 @@ public class SSFWProcessor private readonly string _certpath; private readonly string _certpass; - private static string? _legacykey; + public static string? _legacykey; public SSFWProcessor(string certpath, string certpass, string? locallegacykey) { @@ -176,15 +177,20 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse string? env = ExtractBeforeFirstDot(host); sessionid = GetHeaderValue(Headers, "X-Home-Session-Id"); - if (string.IsNullOrEmpty(env) || !SSFWMisc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) env = "cprod"; // Instantiate services - SSFWAuditService auditService = new(sessionid, env, _legacykey); - SSFWRewardsService rewardSvc = new(_legacykey); - SSFWLayoutService layout = new(_legacykey); - SSFWAvatarLayoutService avatarLayout = new(sessionid, _legacykey); - SSFWClanService clanService = new(sessionid); + AuditService auditService = new(sessionid, env, _legacykey); + AvatarService avatarService = new(); + FriendsService friendsService = new(sessionid, env, _legacykey); + KeepAliveService keepAliveService = new(); + RewardsService rewardSvc = new(_legacykey); + LayoutService layout = new(_legacykey); + AvatarLayoutService avatarLayout = new(sessionid, _legacykey); + ClanService clanService = new(sessionid); + PlayerLookupService playerLookupService = new(); + TradingService tradingService = new(sessionid, env, _legacykey); switch (request.Method) { @@ -216,6 +222,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } else Response.MakeGetResponse(res, "application/json"); + break; } #endregion @@ -223,27 +230,59 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse else if (absolutepath.Contains("/AdminObjectService/start")) { Response.Clear(); - if (new SSFWAdminObjectService(sessionid, _legacykey).HandleAdminObjectService(UserAgent)) + if (new AdminObjectService(sessionid, _legacykey).HandleAdminObjectService(UserAgent)) Response.SetBegin((int)HttpStatusCode.OK); else Response.SetBegin((int)HttpStatusCode.Forbidden); Response.SetBody(); + break; } #endregion #region SaveDataService else if (absolutepath.Contains($"/SaveDataService/{env}/{segments.LastOrDefault()}")) { - string? res = SSFWGetFileList.SSFWSaveDataDebugGetFileList(directoryPath, segments.LastOrDefault()); + string? res = GetFileList.SaveDataDebugGetFileList(directoryPath, segments.LastOrDefault()); if (res != null) Response.MakeGetResponse(res, "application/json"); else Response.MakeErrorResponse(); + break; } #endregion - else + #region PlayerLookup Service + + //Doesn't pass in SessionId!! + else if (absolutepath.Contains($"/{LoginGUID}/person/byDisplayName")) + { + var res = playerLookupService.HandlePlayerLookupService(absolutepath); + if (!string.IsNullOrEmpty(res)) + Response.MakeGetResponse(res, "application/json"); + else + Response.MakeErrorResponse(404, "Not Found"); + break; + } + #endregion + + #region DEBUG AuditService + if (absolutepath.Contains($"/AuditService/log/{env}/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}/counts") + || absolutepath.Contains($"/AuditService/log/{env}/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}/object") + && IsSSFWRegistered(sessionid)) { + var res = auditService.HandleAuditService(absolutepath, new byte[] { }, request); + + if (!string.IsNullOrEmpty(res)) + Response.MakeGetResponse(res, "application/json"); + else + Response.MakeErrorResponse(404, "Not Found"); + break; + } + #endregion + + else if (IsSSFWRegistered(sessionid)) + { + #region RewardService Inventory System //First check if this is a Inventory request if (absolutepath.Contains($"/RewardsService/") && absolutepath.Contains("counts")) { @@ -264,6 +303,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } else //fallback default Response.MakeGetResponse(@"{ ""00000000-00000000-00000000-00000001"": 1 } ", "application/json"); + break; } //Check for specifically the Tracking GUID else if (absolutepath.Contains($"/RewardsService/") && absolutepath.Contains("object/00000000-00000000-00000000-00000001")) @@ -318,11 +358,17 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse }", "application/json"); #endregion } - + break; } } + #endregion + + #region ClanService else if (absolutepath.Contains($"/ClanService/{env}/clan/")) clanService.HandleClanDetailsService(request, Response, absolutepath); + #endregion + + #region File return JSON, bin, jpeg else if (File.Exists(filePath + ".json")) { string? res = FileHelper.ReadAllText(filePath + ".json", _legacykey); @@ -362,8 +408,11 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse Response.SetBegin((int)HttpStatusCode.NotFound); Response.SetBody(); } + #endregion } } + + #region SaveData AvatarService else if (absolutepath.Contains($"/SaveDataService/avatar/{env}/") && absolutepath.EndsWith(".jpg")) { if (File.Exists(filePath)) @@ -389,11 +438,13 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse Response.SetBody(); } break; + #endregion + case "POST": if (request.BodyLength <= Array.MaxLength) { - #region SSFW Login + #region IdentityService Login byte[] postbuffer = request.BodyBytes; if (absolutepath == $"/{LoginGUID}/login/token/psn") { @@ -402,7 +453,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse if (!string.IsNullOrEmpty(XHomeClientVersion) && !string.IsNullOrEmpty(generalsecret)) { - SSFWLogin login = new(XHomeClientVersion, generalsecret, XHomeClientVersion.Replace(".", string.Empty).PadRight(6, '0'), GetHeaderValue(Headers, "x-signature"), _legacykey); + IdentityService login = new(XHomeClientVersion, generalsecret, XHomeClientVersion.Replace(".", string.Empty).PadRight(6, '0'), GetHeaderValue(Headers, "x-signature"), _legacykey); string? result = login.HandleLogin(postbuffer, env); if (!string.IsNullOrEmpty(result)) { @@ -413,6 +464,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } else Response.MakeErrorResponse(); + login.Dispose(); } else { @@ -423,32 +475,13 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } #endregion - #region PING + #region PING KeepAlive Service else if (absolutepath.Contains("/morelife") && !string.IsNullOrEmpty(GetHeaderValue(Headers, "x-signature"))) { - const byte GuidLength = 36; - int index = absolutepath.IndexOf("/morelife"); - - if (index != -1 && index > GuidLength) // Makes sure we have at least 36 chars available beforehand. - { - // Extract the substring between the last '/' and the morelife separator. - string resultSessionId = absolutepath.Substring(index - GuidLength, GuidLength); - - if (Regex.IsMatch(resultSessionId, @"^[{(]?([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})[)}]?$") && IsSSFWRegistered(resultSessionId)) - { - SSFWUserSessionManager.UpdateKeepAliveTime(resultSessionId); - Response.MakeGetResponse("{}", "application/json"); - break; - } - else - { - Response.Clear(); - Response.SetBegin((int)HttpStatusCode.Forbidden); - Response.SetBody(); - } - } + if (keepAliveService.UpdateKeepAliveForClient(absolutepath, Response)) + Response.MakeGetResponse("{}", "application/json"); // This doesn't even need a empty array, simply 200 Status is enough. else - Response.MakeErrorResponse(); + Response.MakeErrorResponse(403); } #endregion @@ -497,8 +530,25 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse || absolutepath.Contains($"/RewardsService/p4t-{env}/")) Response.MakeGetResponse(rewardSvc.HandleRewardServiceInvPOST(postbuffer, directoryPath, filePath, absolutepath), "application/json"); #endregion + + #region ClanService else if (absolutepath.Contains($"/ClanService/{env}/clan/")) clanService.HandleClanDetailsService(request, Response, absolutepath); + #endregion + + #region TradingService + else if (absolutepath.Contains($"/TradingService/pmcards/trade") && IsSSFWRegistered(sessionid)) + { + string? res = tradingService.HandleTradingService(request, sessionid, absolutepath); + + if (res != null) + Response.MakeGetResponse(res, "application/json"); + else + Response.MakeErrorResponse(); + break; + } + #endregion + else { LoggerAccessor.LogWarn($"[SSFWProcessor] : Host requested a POST method I don't know about! - Report it to GITHUB with the request : {absolutepath}"); @@ -564,12 +614,16 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse Response.MakeErrorResponse(); break; case "application/json": + + #region Event Log AuditService if (absolutepath.Equals("/AuditService/log")) { - auditService.HandleAuditService(absolutepath, putbuffer); - //Audit doesn't care we send ok! + auditService.HandleAuditService(absolutepath, putbuffer, request); + //Audit doesn't care so we send ok! Response.MakeOkResponse(); } + #endregion + else { File.WriteAllBytes($"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutepath}.json", putbuffer); @@ -627,8 +681,37 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } } #endregion + + #region ClanService else if (absolutepath.Contains($"/ClanService/{env}/clan/")) + { clanService.HandleClanDetailsService(request, Response, absolutepath); + } + #endregion + + #region RewardsService Inventory DEBUG + // RewardsService Inventory DEBUG - WipeInventory + else if (absolutepath.Contains($"/RewardsService/pmcards/rewards/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}") + && IsSSFWRegistered(sessionid)) + { + var res = rewardSvc.HandleRewardServiceInvDELETE(directoryPath, filePath, absolutepath, UserAgent, sessionid); + if (res != null) + Response.MakeOkResponse(); + else + Response.MakeErrorResponse(500, "Failed to Delete Rewards Inventory!"); + } + // RewardsService Inventory DEBUG - DebugClearCardTrackingData + else if (absolutepath.Contains($"/RewardsService/pmcards/rewards/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}/00000000-00000000-00000000-00000001") + && IsSSFWRegistered(sessionid)) + { + var res = rewardSvc.HandleRewardServiceInvCardTrackingDataDELETE(directoryPath, filePath, absolutepath, UserAgent, sessionid); + if (res != null) + Response.MakeOkResponse(); + else + Response.MakeErrorResponse(500, "Failed to Delete Rewards Card Tracking Data!"); + } + #endregion + else { if (File.Exists(filePath + ".json")) @@ -669,7 +752,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse break; } } - //SoundShapes + #region SoundShapes else if (UserAgent.Contains("PS3Application")) { isApiRequest = true; @@ -684,34 +767,44 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse { if (request.BodyLength <= Array.MaxLength) { - #region SSFW Login + #region IdentityService Login byte[] postbuffer = request.BodyBytes; //SoundShapes Login if (absolutepath == "/identity/login/token/psn") { - SSFWLogin login = new("1.00", "SoundShapes", "1.14", "$ound$h@pesi$C00l", _legacykey); + IdentityService login = new("1.00", "SoundShapes", "1.14", "$ound$h@pesi$C00l", _legacykey); string? result = login.HandleLoginSS(postbuffer, "cprod"); if (!string.IsNullOrEmpty(result)) { Response.Clear(); Response.SetBegin(201); // Replace with URL or proper Server IP Response.SetHeader("location", $"http://{IPAddress.Any}/_dentity/api/service/{LoginGUID}/proxy/login/token/psn/api/client/sessions/f59306bd-3e25-4a34-a41c-ae6c0744c57e"); - Response.SetHeader("X-OTG-Identity-SessionId", "f59306bd-3e25-4a34-a41c-ae6c0744c57e"); + Response.SetHeader("X-OTG-Identity-SessionId", sessionid); Response.SetContentType("application/json"); Response.SetBody(result, encoding); } else Response.MakeErrorResponse(); + login.Dispose(); } - else if (absolutepath.Contains("/identity/person")) + #endregion + + #region FriendService + else if (absolutepath.Contains($"/identity/person/{sessionid}/data/psn/friends-list")) + { + FriendsService friendsService = new FriendsService(sessionid, "prod", _legacykey); + var res = friendsService.HandleFriendsService(absolutepath, postbuffer); Response.MakeOkResponse(); + } #endregion } break; } } } + #endregion + } if (!isApiRequest) @@ -801,7 +894,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse string sceneId = GetHeaderValue(Headers, "sceneId", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !SSFWMisc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) env = "cprod"; Response.Clear(); @@ -850,7 +943,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } if (!string.IsNullOrEmpty(matchingDirectory)) - res = new SSFWLayoutService(_legacykey).HandleLayoutServiceGET(matchingDirectory, sceneId); + res = new LayoutService(_legacykey).HandleLayoutServiceGET(matchingDirectory, sceneId); } // if the dir not exists, we return 403. @@ -920,7 +1013,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse sessionId = GetHeaderValue(Headers, "sessionid", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !SSFWMisc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) env = "cprod"; userId = SSFWUserSessionManager.GetIdBySessionId(sessionId); @@ -964,7 +1057,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse sessionId = GetHeaderValue(Headers, "sessionid", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !SSFWMisc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) env = "cprod"; userId = SSFWUserSessionManager.GetIdBySessionId(sessionId); @@ -977,7 +1070,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse { try { - new SSFWRewardsService(_legacykey).AddMiniEntry(uuid, InventoryEntryType, $"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/trunks-{env}/trunks/{userId}.json", env, userId); + new RewardsService(_legacykey).AddMiniEntry(uuid, InventoryEntryType, $"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/trunks-{env}/trunks/{userId}.json", env, userId); Response.Clear(); Response.SetBegin((int)HttpStatusCode.OK); Response.SetBody($"UUID: {uuid} successfully added to the Mini rewards list.", encoding, GetHeaderValue(Headers, "Origin")); @@ -1010,7 +1103,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse sessionId = GetHeaderValue(Headers, "sessionid", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !SSFWMisc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) env = "cprod"; userId = SSFWUserSessionManager.GetIdBySessionId(sessionId); @@ -1030,7 +1123,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse try { - new SSFWRewardsService(_legacykey).AddMiniEntries(entriesToAdd, $"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/trunks-{env}/trunks/{userId}.json", env, userId); + new RewardsService(_legacykey).AddMiniEntries(entriesToAdd, $"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/trunks-{env}/trunks/{userId}.json", env, userId); Response.Clear(); Response.SetBegin((int)HttpStatusCode.OK); Response.SetBody($"UUIDs: {string.Join(",", uuids)} successfully added to the Mini rewards list.", encoding, GetHeaderValue(Headers, "Origin")); @@ -1063,7 +1156,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse sessionId = GetHeaderValue(Headers, "sessionid", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !SSFWMisc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) env = "cprod"; userId = SSFWUserSessionManager.GetIdBySessionId(sessionId); @@ -1076,7 +1169,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse { try { - new SSFWRewardsService(_legacykey).RemoveMiniEntry(uuid, InventoryEntryType, $"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/trunks-{env}/trunks/{userId}.json", env, userId); + new RewardsService(_legacykey).RemoveMiniEntry(uuid, InventoryEntryType, $"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/trunks-{env}/trunks/{userId}.json", env, userId); Response.Clear(); Response.SetBegin((int)HttpStatusCode.OK); Response.SetBody($"UUID: {uuid} successfully removed in the Mini rewards list.", encoding, GetHeaderValue(Headers, "Origin")); @@ -1109,7 +1202,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse sessionId = GetHeaderValue(Headers, "sessionid", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !SSFWMisc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) env = "cprod"; userId = SSFWUserSessionManager.GetIdBySessionId(sessionId); @@ -1129,7 +1222,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse try { - new SSFWRewardsService(_legacykey).RemoveMiniEntries(entriesToRemove, $"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/trunks-{env}/trunks/{userId}.json", env, userId); + new RewardsService(_legacykey).RemoveMiniEntries(entriesToRemove, $"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/trunks-{env}/trunks/{userId}.json", env, userId); Response.Clear(); Response.SetBegin((int)HttpStatusCode.OK); Response.SetBody($"UUIDs: {string.Join(",", uuids)} removed in the Mini rewards list.", encoding, GetHeaderValue(Headers, "Origin")); diff --git a/Servers/SSFWServer/SSFWUserSessionManager.cs b/Servers/SSFWServer/SSFWUserSessionManager.cs new file mode 100644 index 000000000..b3063a71c --- /dev/null +++ b/Servers/SSFWServer/SSFWUserSessionManager.cs @@ -0,0 +1,224 @@ +using CustomLogger; +using System.Collections.Concurrent; + +namespace SSFWServer +{ + public class SSFWUserSessionManager + { + private static ConcurrentDictionary userSessions = new(); + + public static void RegisterUser(string userName, string sessionid, string id, int realuserNameSize) + { + if (userSessions.TryGetValue(sessionid, out (int, UserSession, DateTime) sessionEntry)) + UpdateKeepAliveTime(sessionid, sessionEntry); + else if (userSessions.TryAdd(sessionid, (realuserNameSize, new UserSession { Username = userName, Id = id }, DateTime.Now.AddMinutes(SSFWServerConfiguration.SSFWTTL)))) + LoggerAccessor.LogInfo($"[UserSessionManager] - User '{userName}' successfully registered with SessionId '{sessionid}'."); + else + LoggerAccessor.LogError($"[UserSessionManager] - Failed to register User '{userName}' with SessionId '{sessionid}'."); + } + + public static string? GetSessionIdByUsername(string? userName, bool rpcn) + { + if (string.IsNullOrEmpty(userName)) + return null; + + foreach (var kvp in userSessions) + { + string sessionId = kvp.Key; + var (realSize, session, _) = kvp.Value; + + string? realUsername = session.Username?.Substring(0, realSize); + + if (string.Equals(realUsername + (rpcn ? "@RPCN" : string.Empty), userName, StringComparison.Ordinal)) + return sessionId; + } + + return null; + } + + public static string? GetUsernameBySessionId(string? sessionId) + { + if (string.IsNullOrEmpty(sessionId)) + return null; + + if (userSessions.TryGetValue(sessionId, out (int, UserSession, DateTime) sessionEntry)) + return sessionEntry.Item2.Username; + + return null; + } + + public static string? GetFormatedUsernameBySessionId(string? sessionId) + { + if (string.IsNullOrEmpty(sessionId)) + return null; + + if (userSessions.TryGetValue(sessionId, out (int, UserSession, DateTime) sessionEntry)) + { + string? userName = sessionEntry.Item2.Username; + + if (!string.IsNullOrEmpty(userName) && userName.Length > sessionEntry.Item1) + userName = userName.Substring(0, sessionEntry.Item1); + + return userName; + } + + return null; + } + + /// + /// Retrieves the Id of a user by their Username, if they have an active session. + /// + /// The username to search for (case-sensitive). + /// The user's Id if found and session is active, otherwise null. + public static string? GetIdByUsername(string? userName) + { + if (string.IsNullOrEmpty(userName)) + return null; + + foreach (var entry in userSessions.Values) + { + if (!string.IsNullOrEmpty(entry.Item2.Username)) + + // Check if session is still valid and username matches + if (entry.Item3 > DateTime.Now + && entry.Item2.Username.StartsWith(userName) + && entry.Item2.Username.Contains("018609")) + { + return entry.Item2.Id; + } + } + + return null; + } + + /// + /// Retrieves the full UserSession object by Username, if they have an active session. + /// + /// The username to search for (case-sensitive). + /// The UserSession if found and active, otherwise null. + public static UserSession? GetUserSessionByUsername(string? userName) + { + if (string.IsNullOrEmpty(userName)) + return null; + + foreach (var entry in userSessions.Values) + { + if (!string.IsNullOrEmpty(entry.Item2.Username)) + + if (entry.Item3 > DateTime.Now + && entry.Item2.Username.StartsWith(userName) + && entry.Item2.Username.Contains("018609")) + { + return entry.Item2; + } + } + + return null; + } + + + public static string? GetIdBySessionId(string? sessionId) + { + if (string.IsNullOrEmpty(sessionId)) + return null; + + (bool, string?) sessionTuple = IsSessionValid(sessionId, false); + + if (sessionTuple.Item1) + return sessionTuple.Item2; + + return null; + } + + public static bool UpdateKeepAliveTime(string sessionid, (int, UserSession, DateTime) sessionEntry = default) + { + if (sessionEntry == default) + { + if (!userSessions.TryGetValue(sessionid, out sessionEntry)) + return false; + } + + DateTime KeepAliveTime = DateTime.Now.AddMinutes(SSFWServerConfiguration.SSFWTTL); + + sessionEntry.Item3 = KeepAliveTime; + + if (userSessions.ContainsKey(sessionid)) + { + LoggerAccessor.LogInfo($"[SSFWUserSessionManager] - Updating: {sessionEntry.Item2?.Username} session with id: {sessionEntry.Item2?.Id} keep-alive time to:{KeepAliveTime}."); + userSessions[sessionid] = sessionEntry; + return true; + } + + LoggerAccessor.LogError($"[SSFWUserSessionManager] - Failed to update: {sessionEntry.Item2?.Username} session with id: {sessionEntry.Item2?.Id} keep-alive time."); + return false; + } + + + + public static (bool, string?) IsSessionValidByDisplayName(string? userName, bool cleanupDeadSessions) + { + if (string.IsNullOrEmpty(userName)) + return (false, null); + + var userSession = userSessions.FirstOrDefault(x => x.Value.Item2.Username == userName); + + if (!string.IsNullOrEmpty(userSession.Value.Item2.Id)) + + if (userSessions.TryGetValue(userSession.Value.Item2.Id, out (int, UserSession, DateTime) sessionEntry)) + { + if (sessionEntry.Item3 > DateTime.Now) + return (true, sessionEntry.Item2.Id); + else if (cleanupDeadSessions) + { + // Clean up expired entry. + if (userSessions.TryRemove(userSession.Value.Item2.Id, out sessionEntry)) + LoggerAccessor.LogWarn($"[SSFWUserSessionManager] - Cleaned: {sessionEntry.Item2.Username} session with id: {sessionEntry.Item2.Id}..."); + else + LoggerAccessor.LogError($"[SSFWUserSessionManager] - Failed to clean: {sessionEntry.Item2.Username} session with id: {sessionEntry.Item2.Id}..."); + } + } + + return (false, null); + } + + public static (bool, string?) IsSessionValid(string? sessionId, bool cleanupDeadSessions) + { + if (string.IsNullOrEmpty(sessionId)) + return (false, null); + + if (userSessions.TryGetValue(sessionId, out (int, UserSession, DateTime) sessionEntry)) + { + if (sessionEntry.Item3 > DateTime.Now) + return (true, sessionEntry.Item2.Id); + else if (cleanupDeadSessions) + { + // Clean up expired entry. + if (userSessions.TryRemove(sessionId, out sessionEntry)) + LoggerAccessor.LogWarn($"[SSFWUserSessionManager] - Cleaned: {sessionEntry.Item2.Username} session with id: {sessionEntry.Item2.Id}..."); + else + LoggerAccessor.LogError($"[SSFWUserSessionManager] - Failed to clean: {sessionEntry.Item2.Username} session with id: {sessionEntry.Item2.Id}..."); + } + } + + return (false, null); + } + + public static void SessionCleanupLoop(object? state) + { + lock (userSessions) + { + foreach (var sessionId in userSessions.Keys) + { + IsSessionValid(sessionId, true); + } + } + } + } + + public class UserSession + { + public string? Username { get; set; } + public string? Id { get; set; } + } + +} \ No newline at end of file diff --git a/Servers/SSFWServer/Services/SSFWAdminObjectService.cs b/Servers/SSFWServer/Services/AdminObjectService.cs similarity index 51% rename from Servers/SSFWServer/Services/SSFWAdminObjectService.cs rename to Servers/SSFWServer/Services/AdminObjectService.cs index 035e251ea..b269fa954 100644 --- a/Servers/SSFWServer/Services/SSFWAdminObjectService.cs +++ b/Servers/SSFWServer/Services/AdminObjectService.cs @@ -1,15 +1,16 @@ using CustomLogger; using Newtonsoft.Json; +using SSFWServer.Helpers; using SSFWServer.Helpers.FileHelper; namespace SSFWServer.Services { - public class SSFWAdminObjectService + public class AdminObjectService { private string? sessionid; private string? key; - public SSFWAdminObjectService(string sessionid, string? key) + public AdminObjectService(string sessionid, string? key) { this.sessionid = sessionid; this.key = key; @@ -17,11 +18,18 @@ public SSFWAdminObjectService(string sessionid, string? key) public bool HandleAdminObjectService(string UserAgent) { - string? username = SSFWUserSessionManager.GetUsernameBySessionId(sessionid); + return IsAdminVerified(UserAgent); + } + + //Helper function for other uses in SSFW services + public bool IsAdminVerified(string userAgent) + { + string? userName = SSFWUserSessionManager.GetUsernameBySessionId(sessionid); + string accountFilePath = $"{SSFWServerConfiguration.SSFWStaticFolder}/SSFW_Accounts/{userName}.json"; - if (!string.IsNullOrEmpty(username) && File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/SSFW_Accounts/{username}.json")) + if (!string.IsNullOrEmpty(userName) && File.Exists(accountFilePath)) { - string? userprofiledata = FileHelper.ReadAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/SSFW_Accounts/{username}.json", key); + string? userprofiledata = FileHelper.ReadAllText(accountFilePath, key); if (!string.IsNullOrEmpty(userprofiledata)) { @@ -30,11 +38,11 @@ public bool HandleAdminObjectService(string UserAgent) if (userData != null) { - LoggerAccessor.LogInfo($"[SSFW] - IGA Request from : {UserAgent}/{username} - IGA status : {userData.IGA}"); + LoggerAccessor.LogInfo($"[SSFW] - IsAdminVerified : IGA Request from : {userAgent}/{userName} - IGA status : {userData.IGA}"); if (userData.IGA == 1) { - LoggerAccessor.LogInfo($"[SSFW] - Admin role confirmed for : {UserAgent}/{username}"); + LoggerAccessor.LogInfo($"[SSFW] - IsAdminVerified : Admin role confirmed for : {userAgent}/{userName}"); return true; } @@ -42,7 +50,7 @@ public bool HandleAdminObjectService(string UserAgent) } } - LoggerAccessor.LogError($"[SSFW] - {UserAgent} requested IGA access, but no access allowed so we forbid!"); + LoggerAccessor.LogError($"[SSFW] - IsAdminVerified : IGA Access denied for {userAgent}!"); return false; } diff --git a/Servers/SSFWServer/Services/AuditService.cs b/Servers/SSFWServer/Services/AuditService.cs new file mode 100644 index 000000000..ceade3e29 --- /dev/null +++ b/Servers/SSFWServer/Services/AuditService.cs @@ -0,0 +1,101 @@ +using CustomLogger; +using NetCoreServer; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SSFWServer.Helpers; +using System.Text; +using System.Text.Json.Nodes; + +namespace SSFWServer.Services +{ + public class AuditService + { + private string? sessionid; + private string? env; + private string? key; + + public AuditService(string sessionid, string env, string? key) + { + this.sessionid = sessionid; + this.env = env; + this.key = key; + } + + public string HandleAuditService(string absolutepath, byte[] buffer, HttpRequest request) + { + string fileNameGUID = GuidGen.SSFWGenerateGuid(sessionid, env); + string? personIdToCompare = SSFWUserSessionManager.GetIdBySessionId(sessionid); + string auditLogPath = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutepath}"; + +#if DEBUG + LoggerAccessor.LogInfo(auditLogPath); +#endif + + switch (request.Method) + { + case "PUT": + try + { + Directory.CreateDirectory(auditLogPath); + + File.WriteAllText($"{auditLogPath}/{fileNameGUID}.json", Encoding.UTF8.GetString(buffer)); + LoggerAccessor.LogInfo($"[SSFW] AuditService - HandleAuditService Audit event log posted: {fileNameGUID}"); + return $"{{ \"result\": 0 }}"; + } + catch (Exception ex) + { + LoggerAccessor.LogError($"[SSFW] AuditService - HandleAuditService ERROR caught: \n{ex}"); + return $"{{ \"result\": -1 }}"; + } + case "GET": + + if(absolutepath.Contains("counts")) + { + var files = Directory.GetFiles(auditLogPath.Replace("/counts", "")); + + string newFileMatchingEntry = string.Empty; + + List listOfEventsByUser = new List(); + int userEventTotal = 1; + int idxTotal = 0; + foreach (string fileToRead in files) + { + string fileContents = File.ReadAllText(fileToRead); + JObject? jsonContents = JsonConvert.DeserializeObject(fileContents); + if(fileContents != null ) + { + JObject mainFile = JObject.Parse(fileContents); + + var userNameInEvent = mainFile["owner"]; + + if (personIdToCompare == (string?)userNameInEvent) + { + string fileName = Path.GetFileNameWithoutExtension(fileToRead); + if(files.Length == userEventTotal) + { + newFileMatchingEntry = $"\"{fileName}\""; + } else + newFileMatchingEntry = $"\"{fileName}\","; + } + listOfEventsByUser.Add(newFileMatchingEntry); + idxTotal++; + } + } + + LoggerAccessor.LogError($"[SSFW] AuditService - HandleAuditService returning count list of logs for player {personIdToCompare}"); + return $"{{ \"count\": {idxTotal}, \"events\": {{ {string.Join("", listOfEventsByUser)} }} }}"; + } else if(absolutepath.Contains("object")) + { + LoggerAccessor.LogInfo("[SSFW] AuditService - HandleAuditService Event log get " + auditLogPath.Replace("/object", "") + ".json"); + return File.ReadAllText(auditLogPath.Replace("/object", "") + ".json"); + } + break; + default: + LoggerAccessor.LogError($"[SSFW] AuditService - HandleAuditService Method {request.Method} unhandled"); + return $"{{ \"result\": -1 }}"; + } + + return string.Empty; + } + } +} \ No newline at end of file diff --git a/Servers/SSFWServer/Services/SSFWAvatarLayoutService.cs b/Servers/SSFWServer/Services/AvatarLayoutService.cs similarity index 83% rename from Servers/SSFWServer/Services/SSFWAvatarLayoutService.cs rename to Servers/SSFWServer/Services/AvatarLayoutService.cs index 99abbdf69..52f5f1505 100644 --- a/Servers/SSFWServer/Services/SSFWAvatarLayoutService.cs +++ b/Servers/SSFWServer/Services/AvatarLayoutService.cs @@ -5,11 +5,11 @@ namespace SSFWServer.Services { - public class SSFWAvatarLayoutService + public class AvatarLayoutService { private string? key; - public SSFWAvatarLayoutService(string sessionid, string? key) + public AvatarLayoutService(string sessionid, string? key) { this.key = key; } @@ -22,13 +22,10 @@ public bool HandleAvatarLayout(byte[] buffer, string directorypath, string filep // Check if the string ends with a number if (regex.IsMatch(absolutepath)) { - // Get the matched number as a string - string numberString = regex.Match(absolutepath).Value; - // Check if the number is valid - if (int.TryParse(numberString, out int number)) + if (int.TryParse(regex.Match(absolutepath).Value, out int number)) { - SSFWUpdateAvatar(directorypath + "/list.json", number, delete); + SSFWUpdateAvatarLayout(directorypath + "/list.json", number, delete); if (delete && File.Exists(filepath + ".json")) { @@ -47,12 +44,12 @@ public bool HandleAvatarLayout(byte[] buffer, string directorypath, string filep return false; } - public void SSFWUpdateAvatar(string filePath, int contentToUpdate, bool delete) + public void SSFWUpdateAvatarLayout(string filePath, int contentToUpdate, bool delete) { + string? json = null; + try { - string? json = null; - if (File.Exists(filePath)) json = FileHelper.ReadAllText(filePath, key); @@ -88,8 +85,8 @@ public void SSFWUpdateAvatar(string filePath, int contentToUpdate, bool delete) } catch (Exception ex) { - LoggerAccessor.LogError($"[SSFW] - SSFWUpdateAvatar errored out with this exception - {ex}"); + LoggerAccessor.LogError($"[SSFW] - SSFWUpdateAvatarLayout errored out with this exception - {ex}"); } } } -} +} \ No newline at end of file diff --git a/Servers/SSFWServer/Services/AvatarService.cs b/Servers/SSFWServer/Services/AvatarService.cs new file mode 100644 index 000000000..6a528ccf5 --- /dev/null +++ b/Servers/SSFWServer/Services/AvatarService.cs @@ -0,0 +1,12 @@ +using SSFWServer.Helpers.FileHelper; + +namespace SSFWServer.Services +{ + public class AvatarService + { + public byte[]? HandleAvatarService(string absolutepath, string? key) + { + return FileHelper.ReadAllBytes(absolutepath, key); + } + } +} \ No newline at end of file diff --git a/Servers/SSFWServer/Services/SSFWClanService.cs b/Servers/SSFWServer/Services/ClanService.cs similarity index 51% rename from Servers/SSFWServer/Services/SSFWClanService.cs rename to Servers/SSFWServer/Services/ClanService.cs index 80162d686..0558965e9 100644 --- a/Servers/SSFWServer/Services/SSFWClanService.cs +++ b/Servers/SSFWServer/Services/ClanService.cs @@ -1,48 +1,52 @@ -using System.Security.Cryptography; +using NetCoreServer; +using NetHasher; +using MultiServerLibrary.HTTP; using System.Text; using System.Text.Json; -using NetCoreServer; -using NetHasher; namespace SSFWServer.Services { - public class SSFWClanService + public class ClanService { private readonly string? _sessionid; - public SSFWClanService(string sessionid) + public ClanService(string sessionid) { _sessionid = sessionid; } - // Handles both GET/POST on this + // Handles GET,POST,DELETE on this function public HttpResponse HandleClanDetailsService(HttpRequest req, HttpResponse res, string absolutepath) { - string filePath = $"{SSFWServerConfiguration.SSFWStaticFolder}{new Uri(absolutepath).AbsolutePath}" + ".json"; + string filePath = $"{SSFWServerConfiguration.SSFWStaticFolder}{HTTPProcessor.ParseUriFromAbsolutePath(absolutepath).AbsolutePath}.json"; if (req.Method == HttpMethod.Post.ToString()) { try { - using JsonDocument doc = JsonDocument.Parse(req.Body); - - if (doc.RootElement.TryGetProperty("sceneObjectId", out JsonElement idElement)) + if (JsonDocument.Parse(req.Body).RootElement.TryGetProperty("sceneObjectId", out JsonElement idElement)) { - Directory.CreateDirectory(absolutepath); + string? psnClanId = absolutepath.Split("/").LastOrDefault(); + string? directoryPath = Path.GetDirectoryName(filePath); - string jsonToWrite = $@"{{ - ""region"":""en-US"", - ""message"":""OK"", - ""result"":0, - ""psnClanId"":{absolutepath.Split("/").LastOrDefault()}, - ""sceneObjectId"":""{idElement.GetString()!}"", - ""personId"":""{_sessionid}"", - ""clanId"":""{DotNetHasher.ComputeMD5String(Encoding.UTF8.GetBytes(absolutepath.Split("/").LastOrDefault()!))}"" - }}"; + if (string.IsNullOrEmpty(psnClanId) || string.IsNullOrEmpty(directoryPath)) + throw new Exception(); + + Directory.CreateDirectory(directoryPath); + // TODO, extract the proper region. + string jsonToWrite = $@"{{ +""region"":""en-US"", +""message"":""OK"", +""result"":0, +""psnClanId"":{psnClanId}, +""sceneObjectId"":""{idElement.GetString()!}"", +""personId"":""{_sessionid}"", +""clanId"":""{DotNetHasher.ComputeMD5String(Encoding.UTF8.GetBytes(psnClanId))}"" +}}"; File.WriteAllText(filePath, jsonToWrite); - return res.MakeGetResponse($@"{jsonToWrite}", "application/json"); + return res.MakeGetResponse(jsonToWrite, "application/json"); } } @@ -55,9 +59,9 @@ public HttpResponse HandleClanDetailsService(HttpRequest req, HttpResponse res, } else if (req.Method == HttpMethod.Get.ToString()) // GET ONLY { - // If clanid exist, we check json and return that back, otherwise not found so Home POST default + // If clanId exist, we check json and return that back, otherwise not found so Home POST default if (File.Exists(filePath)) - return res.MakeGetResponse($@"{File.ReadAllText(filePath)}", "application/json"); + return res.MakeGetResponse(File.ReadAllText(filePath), "application/json"); return res.MakeErrorResponse(404, "Not Found"); } @@ -67,12 +71,8 @@ public HttpResponse HandleClanDetailsService(HttpRequest req, HttpResponse res, { if (File.Exists(filePath)) { - using JsonDocument doc = JsonDocument.Parse(File.ReadAllText(filePath)); - - string? sceneObjectId = doc.RootElement.GetProperty("sceneObjectId").GetString() ?? string.Empty; - + string? sceneObjectId = JsonDocument.Parse(File.ReadAllText(filePath)).RootElement.GetProperty("sceneObjectId").GetString() ?? string.Empty; File.Delete(filePath); - return res.MakeGetResponse($"{{\"sceneObjectIds\":[\"{sceneObjectId}\"]}}", "application/json"); } } @@ -84,4 +84,4 @@ public HttpResponse HandleClanDetailsService(HttpRequest req, HttpResponse res, return res.MakeErrorResponse(); } } -} +} \ No newline at end of file diff --git a/Servers/SSFWServer/Services/FriendsService.cs b/Servers/SSFWServer/Services/FriendsService.cs new file mode 100644 index 000000000..a82c7e3e0 --- /dev/null +++ b/Servers/SSFWServer/Services/FriendsService.cs @@ -0,0 +1,38 @@ +using CustomLogger; +using System.Text; + +namespace SSFWServer.Services +{ + public class FriendsService + { + private string? sessionid; + private string? env; + private string? key; + + public FriendsService(string sessionid, string env, string? key) + { + this.sessionid = sessionid; + this.env = env; + this.key = key; + } + + public string HandleFriendsService(string absolutepath, byte[] buffer) + { + string? userName = SSFWUserSessionManager.GetUsernameBySessionId(sessionid); + string auditLogPath = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutepath}/{env}"; + try + { + Directory.CreateDirectory(auditLogPath); + + File.WriteAllText($"{auditLogPath}/{userName}.txt", Encoding.UTF8.GetString(buffer)); + LoggerAccessor.LogInfo($"[SSFW] FriendsService - HandleFriendsService Friends list posted: {userName}"); + return "Success"; + } + catch (Exception ex) + { + LoggerAccessor.LogError($"[SSFW] FriendsService - HandleFriendsService ERROR caught: \n{ex}"); + return ex.Message; + } + } + } +} \ No newline at end of file diff --git a/Servers/SSFWServer/SSFWLogin.cs b/Servers/SSFWServer/Services/IdentityService.cs similarity index 74% rename from Servers/SSFWServer/SSFWLogin.cs rename to Servers/SSFWServer/Services/IdentityService.cs index bee848b1d..989fe0b06 100644 --- a/Servers/SSFWServer/SSFWLogin.cs +++ b/Servers/SSFWServer/Services/IdentityService.cs @@ -1,159 +1,24 @@ +using CastleLibrary.Sony.XI5; using CustomLogger; -using System.Text; -using System.Collections.Concurrent; using NetHasher; +using MultiServerLibrary.Extension; +using SSFWServer.Helpers; +using SSFWServer.Helpers.DataMigrator; +using System.Text; using CastleLibrary.Sony.SSFW; -using CastleLibrary.Sony.XI5; -namespace SSFWServer +namespace SSFWServer.Services { - public class SSFWUserSessionManager - { - private static ConcurrentDictionary userSessions = new(); - - public static void RegisterUser(string userName, string sessionid, string id, int realuserNameSize) - { - if (userSessions.TryGetValue(sessionid, out (int, UserSession, DateTime) sessionEntry)) - UpdateKeepAliveTime(sessionid, sessionEntry); - else if (userSessions.TryAdd(sessionid, (realuserNameSize, new UserSession { Username = userName, Id = id }, DateTime.Now.AddMinutes(SSFWServerConfiguration.SSFWTTL)))) - LoggerAccessor.LogInfo($"[UserSessionManager] - User '{userName}' successfully registered with SessionId '{sessionid}'."); - else - LoggerAccessor.LogError($"[UserSessionManager] - Failed to register User '{userName}' with SessionId '{sessionid}'."); - } - - public static string? GetSessionIdByUsername(string? userName, bool rpcn) - { - if (string.IsNullOrEmpty(userName)) - return null; - - foreach (var kvp in userSessions) - { - string sessionId = kvp.Key; - var (realSize, session, _) = kvp.Value; - - string? realUsername = session.Username?.Substring(0, realSize); - - if (string.Equals(realUsername + (rpcn ? "@RPCN" : string.Empty), userName, StringComparison.Ordinal)) - return sessionId; - } - - return null; - } - - public static string? GetUsernameBySessionId(string? sessionId) - { - if (string.IsNullOrEmpty(sessionId)) - return null; - - if (userSessions.TryGetValue(sessionId, out (int, UserSession, DateTime) sessionEntry)) - return sessionEntry.Item2.Username; - - return null; - } - - public static string? GetFormatedUsernameBySessionId(string? sessionId) - { - if (string.IsNullOrEmpty(sessionId)) - return null; - - if (userSessions.TryGetValue(sessionId, out (int, UserSession, DateTime) sessionEntry)) - { - string? userName = sessionEntry.Item2.Username; - - if (!string.IsNullOrEmpty(userName) && userName.Length > sessionEntry.Item1) - userName = userName.Substring(0, sessionEntry.Item1); - - return userName; - } - - return null; - } - - public static string? GetIdBySessionId(string? sessionId) - { - if (string.IsNullOrEmpty(sessionId)) - return null; - - (bool, string?) sessionTuple = IsSessionValid(sessionId, false); - - if (sessionTuple.Item1) - return sessionTuple.Item2; - - return null; - } - - public static bool UpdateKeepAliveTime(string sessionid, (int, UserSession, DateTime) sessionEntry = default) - { - if (sessionEntry == default) - { - if (!userSessions.TryGetValue(sessionid, out sessionEntry)) - return false; - } - - DateTime KeepAliveTime = DateTime.Now.AddMinutes(SSFWServerConfiguration.SSFWTTL); - - sessionEntry.Item3 = KeepAliveTime; - - if (userSessions.ContainsKey(sessionid)) - { - LoggerAccessor.LogInfo($"[SSFWUserSessionManager] - Updating: {sessionEntry.Item2?.Username} session with id: {sessionEntry.Item2?.Id} keep-alive time to:{KeepAliveTime}."); - userSessions[sessionid] = sessionEntry; - return true; - } - - LoggerAccessor.LogError($"[SSFWUserSessionManager] - Failed to update: {sessionEntry.Item2?.Username} session with id: {sessionEntry.Item2?.Id} keep-alive time."); - return false; - } - - public static (bool, string?) IsSessionValid(string? sessionId, bool cleanupDeadSessions) - { - if (string.IsNullOrEmpty(sessionId)) - return (false, null); - - if (userSessions.TryGetValue(sessionId, out (int, UserSession, DateTime) sessionEntry)) - { - if (sessionEntry.Item3 > DateTime.Now) - return (true, sessionEntry.Item2.Id); - else if (cleanupDeadSessions) - { - // Clean up expired entry. - if (userSessions.TryRemove(sessionId, out sessionEntry)) - LoggerAccessor.LogWarn($"[SSFWUserSessionManager] - Cleaned: {sessionEntry.Item2.Username} session with id: {sessionEntry.Item2.Id}..."); - else - LoggerAccessor.LogError($"[SSFWUserSessionManager] - Failed to clean: {sessionEntry.Item2.Username} session with id: {sessionEntry.Item2.Id}..."); - } - } - - return (false, null); - } - - public static void SessionCleanupLoop(object? state) - { - lock (userSessions) - { - foreach (var sessionId in userSessions.Keys) - { - IsSessionValid(sessionId, true); - } - } - } - } - - public class UserSession - { - public string? Username { get; set; } - public string? Id { get; set; } - } - - public class SSFWLogin + public class IdentityService : IDisposable { private string? XHomeClientVersion; private string? generalsecret; private string? homeClientVersion; private string? xsignature; private string? key; + private bool disposedValue; - public SSFWLogin(string XHomeClientVersion, string generalsecret, string homeClientVersion, string? xsignature, string? key) + public IdentityService(string XHomeClientVersion, string generalsecret, string homeClientVersion, string? xsignature, string? key) { this.XHomeClientVersion = XHomeClientVersion; this.generalsecret = generalsecret; @@ -293,7 +158,7 @@ public SSFWLogin(string XHomeClientVersion, string generalsecret, string homeCli } if (IsRPCN && Directory.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/AvatarLayoutService/{env}/{ResultStrings.Item2}") && !Directory.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/AvatarLayoutService/{env}/{ResultStrings.Item1}")) - SSFWDataMigrator.MigrateSSFWData(SSFWServerConfiguration.SSFWStaticFolder, ResultStrings.Item2, ResultStrings.Item1); + DataMigrator.MigrateSSFWData(SSFWServerConfiguration.SSFWStaticFolder, ResultStrings.Item2, ResultStrings.Item1); string? resultString = IsRPCN ? ResultStrings.Item1 : ResultStrings.Item2; @@ -317,7 +182,7 @@ public SSFWLogin(string XHomeClientVersion, string generalsecret, string homeCli if (File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json")) // Migrate data. { // Parsing each value in the dictionary - foreach (var kvp in new Services.SSFWLayoutService(key).SSFWGetLegacyFurnitureLayouts($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json")) + foreach (var kvp in new Services.LayoutService(key).SSFWGetLegacyFurnitureLayouts($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json")) { if (kvp.Key == "00000000-00000000-00000000-00000004") { @@ -326,7 +191,7 @@ public SSFWLogin(string XHomeClientVersion, string generalsecret, string homeCli } else { - string scenename = scenemap.FirstOrDefault(x => x.Value == SSFWMisc.ExtractPortion(kvp.Key, 13, 18)).Key; + string scenename = scenemap.FirstOrDefault(x => x.Value == Misc.ExtractPortion(kvp.Key, 13, 18)).Key; if (!string.IsNullOrEmpty(scenename)) { if (File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/{kvp.Key}.json")) // SceneID now mapped, so SceneID based file has become obsolete. @@ -346,12 +211,12 @@ public SSFWLogin(string XHomeClientVersion, string generalsecret, string homeCli File.Delete($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json"); } else if (!File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/HarborStudio.json")) - File.WriteAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/HarborStudio.json", SSFWMisc.HarbourStudioLayout); + File.WriteAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/HarborStudio.json", Misc.HarbourStudioLayout); } else { if (!File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json")) - File.WriteAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json", SSFWMisc.LegacyLayoutTemplate); + File.WriteAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json", Misc.LegacyLayoutTemplate); } if (!File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/{env}/rewards/{resultString}/mini.json")) @@ -505,10 +370,43 @@ public SSFWLogin(string XHomeClientVersion, string generalsecret, string homeCli return null; } - return $"{{\"session\": {{\"expires: 3097114741746 ,\"id\":\"{(IsRPCN ? SessionIDs.Item1 : SessionIDs.Item2)}\",\"person\":{{\"id\":\"{(IsRPCN ? SessionIDs.Item1 : SessionIDs.Item2)}\",\"display_name\":\"{resultString}\"}},\"service\":{{\"id\":\"{(IsRPCN ? SessionIDs.Item1 : SessionIDs.Item2)}\",\"display_name\":\"{resultString}\"}} }} }}"; + return $"{{\"session\": {{\"expires\": \"3097114741746\" ,\"id\":\"{(IsRPCN ? SessionIDs.Item1 : SessionIDs.Item2)}\",\"person\":{{\"id\":\"{(IsRPCN ? SessionIDs.Item1 : SessionIDs.Item2)}\",\"display_name\":\"{resultString}\"}},\"service\":{{\"id\":\"{(IsRPCN ? SessionIDs.Item1 : SessionIDs.Item2)}\",\"display_name\":\"{resultString}\"}} }} }} }}"; } return null; } + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + XHomeClientVersion = null; + generalsecret = null; + homeClientVersion = null; + xsignature = null; + key = null; + } + + // TODO: libérer les ressources non managées (objets non managés) et substituer le finaliseur + // TODO: affecter aux grands champs une valeur null + disposedValue = true; + } + } + + // // TODO: substituer le finaliseur uniquement si 'Dispose(bool disposing)' a du code pour libérer les ressources non managées + // ~SSFWLogin() + // { + // // Ne changez pas ce code. Placez le code de nettoyage dans la méthode 'Dispose(bool disposing)' + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Ne changez pas ce code. Placez le code de nettoyage dans la méthode 'Dispose(bool disposing)' + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } } \ No newline at end of file diff --git a/Servers/SSFWServer/Services/KeepAliveService.cs b/Servers/SSFWServer/Services/KeepAliveService.cs new file mode 100644 index 000000000..21a93e1e3 --- /dev/null +++ b/Servers/SSFWServer/Services/KeepAliveService.cs @@ -0,0 +1,32 @@ +using NetCoreServer; +using System.Text.RegularExpressions; + +namespace SSFWServer.Services +{ + public class KeepAliveService + { + public bool UpdateKeepAliveForClient(string absolutePath, HttpResponse res) + { + const byte GuidLength = 36; + int index = absolutePath.IndexOf("/morelife"); + + if (index != -1 && index > GuidLength) // Makes sure we have at least 36 chars available beforehand. + { + // Extract the substring between the last '/' and the morelife separator. + string resultSessionId = absolutePath.Substring(index - GuidLength, GuidLength); + + if (Regex.IsMatch(resultSessionId, @"^[{(]?([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})[)}]?$")) + { + SSFWUserSessionManager.UpdateKeepAliveTime(resultSessionId); + return true; + } + else + { + return false; + } + } + else + return false; + } + } +} \ No newline at end of file diff --git a/Servers/SSFWServer/Services/SSFWLayoutService.cs b/Servers/SSFWServer/Services/LayoutService.cs similarity index 96% rename from Servers/SSFWServer/Services/SSFWLayoutService.cs rename to Servers/SSFWServer/Services/LayoutService.cs index 9cdf1acad..bf2f0970f 100644 --- a/Servers/SSFWServer/Services/SSFWLayoutService.cs +++ b/Servers/SSFWServer/Services/LayoutService.cs @@ -1,5 +1,6 @@ using CustomLogger; using Newtonsoft.Json.Linq; +using SSFWServer.Helpers; using SSFWServer.Helpers.FileHelper; using System.Text; using System.Text.RegularExpressions; @@ -7,11 +8,11 @@ namespace SSFWServer.Services { - public partial class SSFWLayoutService + public class LayoutService { private string? key; - public SSFWLayoutService(string? key) + public LayoutService(string? key) { this.key = key; } @@ -46,7 +47,7 @@ public bool HandleLayoutServicePOST(byte[] buffer, string directorypath, string } else { - string scenename = scenemap.FirstOrDefault(x => x.Value == SSFWMisc.ExtractPortion(kvp.Key, 13, 18)).Key; + string scenename = scenemap.FirstOrDefault(x => x.Value == Misc.ExtractPortion(kvp.Key, 13, 18)).Key; if (!string.IsNullOrEmpty(scenename)) { if (File.Exists(directorypath + $"/{kvp.Key}.json")) // SceneID now mapped, so SceneID based file has become obsolete. @@ -74,7 +75,7 @@ public bool HandleLayoutServicePOST(byte[] buffer, string directorypath, string } else { - string scenename = scenemap.FirstOrDefault(x => x.Value == SSFWMisc.ExtractPortion(sceneid, 13, 18)).Key; + string scenename = scenemap.FirstOrDefault(x => x.Value == Misc.ExtractPortion(sceneid, 13, 18)).Key; if (!string.IsNullOrEmpty(scenename)) { if (File.Exists(directorypath + $"/{sceneid}.json")) // SceneID now mapped, so SceneID based file has become obsolete. @@ -123,7 +124,7 @@ public bool HandleLayoutServicePOST(byte[] buffer, string directorypath, string } else { - string scenename = ScenelistParser.sceneDictionary.FirstOrDefault(x => x.Value == SSFWMisc.ExtractPortion(sceneid, 13, 18)).Key; + string scenename = ScenelistParser.sceneDictionary.FirstOrDefault(x => x.Value == Misc.ExtractPortion(sceneid, 13, 18)).Key; if (!string.IsNullOrEmpty(scenename)) { string filepath = directorypath + $"/{scenename}.json"; diff --git a/Servers/SSFWServer/Services/PlayerLookupService.cs b/Servers/SSFWServer/Services/PlayerLookupService.cs new file mode 100644 index 000000000..a4a913659 --- /dev/null +++ b/Servers/SSFWServer/Services/PlayerLookupService.cs @@ -0,0 +1,17 @@ +using CustomLogger; + +namespace SSFWServer.Services +{ + public class PlayerLookupService + { + public string HandlePlayerLookupService(string url) + { + string byDisplayName = url.Split("=")[1]; + string? userId = SSFWUserSessionManager.GetIdByUsername(byDisplayName); +#if DEBUG + LoggerAccessor.LogInfo($"[SSFW] PlayerLookupService - Requesting {byDisplayName}'s id, successfully returned userId {userId}"); +#endif + return $"{{\"@id\": {userId} }}"; + } + } +} \ No newline at end of file diff --git a/Servers/SSFWServer/Services/SSFWRewardsService.cs b/Servers/SSFWServer/Services/RewardsService.cs similarity index 66% rename from Servers/SSFWServer/Services/SSFWRewardsService.cs rename to Servers/SSFWServer/Services/RewardsService.cs index 03799a6c0..ad2c1cf7e 100644 --- a/Servers/SSFWServer/Services/SSFWRewardsService.cs +++ b/Servers/SSFWServer/Services/RewardsService.cs @@ -1,5 +1,4 @@ using CustomLogger; -using MultiServerLibrary.Extension; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SSFWServer.Helpers.FileHelper; @@ -9,11 +8,11 @@ namespace SSFWServer.Services { - public class SSFWRewardsService + public class RewardsService { private string? key; - public SSFWRewardsService(string? key) + public RewardsService(string? key) { this.key = key; } @@ -29,11 +28,35 @@ public byte[] HandleRewardServicePOST(byte[] buffer, string directorypath, strin return buffer; } - public byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, string filepath, string absoultepath) + public byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, string filepath, string absolutepath) { Directory.CreateDirectory(directorypath); - return SSFWRewardServiceInventoryPOST(buffer, directorypath, filepath, absoultepath); + return RewardServiceInventory(buffer, directorypath, filepath, absolutepath, false, false); + } + + public byte[]? HandleRewardServiceInvCardTrackingDataDELETE(string directorypath, string filepath, string absolutepath, string userAgent, string sessionId) + { + AdminObjectService adminObjectService = new AdminObjectService(sessionId, key); + if (adminObjectService.IsAdminVerified(userAgent)) + { + return RewardServiceInventory(null, directorypath, filepath, absolutepath, false, true); + } else { + LoggerAccessor.LogError($"[SSFW] - HandleRewardServiceInvCardTrackingDataDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to delete Tracking data!"); + return null; + } + } + + public byte[]? HandleRewardServiceInvDELETE(string directorypath, string filepath, string absolutepath, string userAgent, string sessionId) + { + AdminObjectService adminObjectService = new AdminObjectService(sessionId, key); + if(adminObjectService.IsAdminVerified(userAgent)) + { + return RewardServiceInventory(null, directorypath, filepath, absolutepath, true, false); + } else { + LoggerAccessor.LogError($"[SSFW] - HandleRewardServiceInvDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to delete Tracking data!"); + return null; + } } public void HandleRewardServiceTrunksPOST(byte[] buffer, string directorypath, string filepath, string absolutepath, string env, string? userId) @@ -42,7 +65,7 @@ public void HandleRewardServiceTrunksPOST(byte[] buffer, string directorypath, s File.WriteAllBytes($"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutepath}.json", buffer); - SSFWTrunkServiceProcess(filepath.Replace("/setpartial", string.Empty) + ".json", Encoding.UTF8.GetString(buffer), env, userId); + TrunkServiceProcess(filepath.Replace("/setpartial", string.Empty) + ".json", Encoding.UTF8.GetString(buffer), env, userId); } public void HandleRewardServiceTrunksEmergencyPOST(byte[] buffer, string directorypath, string absolutepath) @@ -77,8 +100,14 @@ public void SSFWUpdateMini(string filePath, string postData, bool delete) foreach (var reward in rewardsObject) { string rewardKey = reward.Key; - if (string.IsNullOrEmpty(rewardKey) || reward.Value == null) + JToken? rewardValue = reward.Value; + if (string.IsNullOrEmpty(rewardKey) || rewardValue == null) continue; + if (rewardValue.Type != JTokenType.Integer) + { + LoggerAccessor.LogInfo($"[RewardsService] - Reward:{rewardValue} earned, adding to mini file:{filePath}."); + rewardValue = 1; + } // Check if the reward exists in the JSON array JToken? existingReward = jsonArray.FirstOrDefault(r => r[rewardKey] != null); @@ -92,13 +121,13 @@ public void SSFWUpdateMini(string filePath, string postData, bool delete) { if (existingReward != null) // Update the value of the reward - existingReward[rewardKey] = DateTime.UtcNow.ToUnixTime(); + existingReward[rewardKey] = rewardValue; else { // Add the new reward to the JSON array jsonArray.Add(new JObject { - { rewardKey, DateTime.UtcNow.ToUnixTime() } + { rewardKey, rewardValue } }); } } @@ -114,7 +143,7 @@ public void SSFWUpdateMini(string filePath, string postData, bool delete) } } - public void SSFWTrunkServiceProcess(string filePath, string request, string env, string? userId) + public void TrunkServiceProcess(string filePath, string request, string env, string? userId) { try { @@ -142,7 +171,7 @@ public void SSFWTrunkServiceProcess(string filePath, string request, string env, foreach (JObject addObject in addArray) { mainArray.Add(addObject); - if (addObject.TryGetValue("objectId", out JToken? objectIdToken) && objectIdToken != null + if (addObject.TryGetValue("objectId", out JToken? objectIdToken) && objectIdToken != null && addObject.TryGetValue("type", out JToken? typeToken) && typeToken != null && int.TryParse(typeToken.ToString(), out int typeTokenInt) && typeTokenInt != 0) entriesToAddInMini.TryAdd(objectIdToken.ToString(), typeToken.ToString()); } @@ -224,24 +253,25 @@ public void SSFWTrunkServiceProcess(string filePath, string request, string env, } catch (Exception ex) { - LoggerAccessor.LogError($"[SSFW] - SSFWTrunkServiceProcess errored out with this exception - {ex}"); + LoggerAccessor.LogError($"[SSFW] - TrunkServiceProcess errored out with this exception - {ex}"); } } - public byte[] SSFWRewardServiceInventoryPOST(byte[] buffer, string directorypath, string filepath, string absolutePath) + public byte[] RewardServiceInventory(byte[]? buffer, string directorypath, string filepath, string absolutePath, bool deleteInv, bool deleteOnlyTracking) { - //Tracking Inventory + //Tracking Inventory GUID const string trackingGuid = "00000000-00000000-00000000-00000001"; // fallback/hardcoded tracking GUID // File paths based on the provided format string countsStoreDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}/"; + Directory.CreateDirectory(Path.GetDirectoryName(countsStoreDir)); string countsStore = $"{countsStoreDir}/counts.json"; - string trackingFileDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}/object/"; - string trackingFile = $"{trackingFileDir}/{trackingGuid}.json"; - Directory.CreateDirectory(Path.GetDirectoryName(countsStoreDir)); + string trackingFileDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}/object"; Directory.CreateDirectory(Path.GetDirectoryName(trackingFileDir)); + string trackingFile = $"{trackingFileDir}/{trackingGuid}.json"; + //Parse Buffer string fixedJsonPayload = GUIDValidator.FixJsonValues(Encoding.UTF8.GetString(buffer)); try { @@ -249,132 +279,151 @@ public byte[] SSFWRewardServiceInventoryPOST(byte[] buffer, string directorypath JsonElement root = document.RootElement; if (!root.TryGetProperty("rewards", out JsonElement rewardsElement) || rewardsElement.ValueKind != JsonValueKind.Array) { - LoggerAccessor.LogError("[SSFW] - SSFWRewardServiceInventoryPOST: Invalid payload - 'rewards' must be an array."); + LoggerAccessor.LogError("[SSFW] - RewardServiceInventoryPOST: Invalid payload - 'rewards' must be an array."); return Encoding.UTF8.GetBytes("{\"idList\": [\"00000000-00000000-00000000-00000001\"] }"); } var rewards = rewardsElement.EnumerateArray(); if (!rewards.MoveNext()) { - LoggerAccessor.LogError("[SSFW] - SSFWRewardServiceInventoryPOST: Invalid payload - 'rewards' array is empty."); + LoggerAccessor.LogError("[SSFW] - RewardServiceInventoryPOST: Invalid payload - 'rewards' array is empty."); return Encoding.UTF8.GetBytes("{\"idList\": [\"00000000-00000000-00000000-00000001\"] }"); } - Dictionary counts; + Dictionary counts = new(); if (File.Exists(countsStore)) { - string countsJson = File.ReadAllText(countsStore); - counts = System.Text.Json.JsonSerializer.Deserialize>(countsJson) ?? new Dictionary(); + if(deleteInv) + { + File.Delete(countsStore); + LoggerAccessor.LogInfo($"[SSFW] - RewardServiceInventory: Deleting Inventory counts at {countsStore}"); + } else + { + string countsJson = File.ReadAllText(countsStore); + counts = System.Text.Json.JsonSerializer.Deserialize>(countsJson) ?? new Dictionary(); + } } else { counts = new Dictionary(); } - Dictionary> existingTrackingData = null; + Dictionary>? existingTrackingData = null; if (File.Exists(trackingFile)) { - string existingTrackingJson = File.ReadAllText(trackingFile); - using JsonDocument trackingDoc = JsonDocument.Parse(existingTrackingJson); - JsonElement trackingRoot = trackingDoc.RootElement; - if (trackingRoot.TryGetProperty("rewards", out JsonElement trackingRewardsElement) && - trackingRewardsElement.ValueKind == JsonValueKind.Object) + if (deleteInv || deleteOnlyTracking) { - existingTrackingData = System.Text.Json.JsonSerializer.Deserialize>>( - trackingRewardsElement.GetRawText()); + File.Delete(trackingFile); + LoggerAccessor.LogInfo($"[SSFW] - RewardServiceInventory: Deleting Tracking file at {trackingFile}"); + return Encoding.UTF8.GetBytes(""); } - } - - foreach (JsonElement reward in rewards) - { - if (!reward.TryGetProperty("objectId", out JsonElement objectIdElement) || - objectIdElement.ValueKind != JsonValueKind.String) + else { - LoggerAccessor.LogError("[SSFW] - SSFWRewardServiceInventoryPOST: Invalid reward - 'objectId' missing or not a string."); - continue; - } + string existingTrackingJson = File.ReadAllText(trackingFile); + using JsonDocument trackingDoc = JsonDocument.Parse(existingTrackingJson); + JsonElement trackingRoot = trackingDoc.RootElement; + if (trackingRoot.TryGetProperty("rewards", out JsonElement trackingRewardsElement) && + trackingRewardsElement.ValueKind == JsonValueKind.Object) + { + existingTrackingData = System.Text.Json.JsonSerializer.Deserialize>>( + trackingRewardsElement.GetRawText()); + } - if (objectIdElement.ValueKind != JsonValueKind.String) - { - LoggerAccessor.LogError($"[SSFW] - SSFWRewardServiceInventoryPOST: 'objectId' must be a string, got {objectIdElement.ValueKind}."); - continue; - } + foreach (JsonElement reward in rewards) + { + if (!reward.TryGetProperty("objectId", out JsonElement objectIdElement) || + objectIdElement.ValueKind != JsonValueKind.String) + { + LoggerAccessor.LogError("[SSFW] - RewardServiceInventoryPOST: Invalid reward - 'objectId' missing or not a string."); + continue; + } - string objectId = objectIdElement.GetString(); + if (objectIdElement.ValueKind != JsonValueKind.String) + { + LoggerAccessor.LogError($"[SSFW] - RewardServiceInventoryPOST: 'objectId' must be a string, got {objectIdElement.ValueKind}."); + continue; + } - // Update counts - if (counts.ContainsKey(objectId)) - { - counts[objectId]++; - } - else - { - counts[objectId] = 1; - } + string? objectId = objectIdElement.GetString(); - // Check if this is a tracking object (has metadata or matches tracking GUID) - bool hasMetadata = reward.TryGetProperty("_id", out _) || - reward.TryGetProperty("scene", out _) || - reward.TryGetProperty("boost", out _) || - reward.TryGetProperty("game", out _) || - reward.TryGetProperty("migrated", out _); - if (hasMetadata || objectId == trackingGuid || objectId != string.Empty) - { - var trackingRewards = existingTrackingData ?? new Dictionary>(); - var metadata = new Dictionary(); + // Update counts + if (counts.ContainsKey(objectId)) + { + counts[objectId]++; + } + else + { + counts[objectId] = 1; + } - foreach (JsonProperty prop in reward.EnumerateObject()) - { - if (prop.Name != "objectId") + // Check if this is a tracking object (has metadata or matches tracking GUID) + bool hasMetadata = reward.TryGetProperty("_id", out _) || + reward.TryGetProperty("scene", out _) || + reward.TryGetProperty("boost", out _) || + reward.TryGetProperty("game", out _) || + reward.TryGetProperty("migrated", out _); + if (hasMetadata || objectId == trackingGuid || objectId != string.Empty) { - switch (prop.Value.ValueKind) + var trackingRewards = existingTrackingData ?? new Dictionary>(); + var metadata = new Dictionary(); + + foreach (JsonProperty prop in reward.EnumerateObject()) { - case JsonValueKind.String: - metadata[prop.Name] = prop.Value.GetString(); - break; - case JsonValueKind.Number: - metadata[prop.Name] = prop.Value.GetInt32(); - break; - case JsonValueKind.True: - case JsonValueKind.False: - metadata[prop.Name] = prop.Value.GetBoolean(); - break; - default: - metadata[prop.Name] = prop.Value.ToString(); - break; + if (prop.Name != "objectId") + { + switch (prop.Value.ValueKind) + { + case JsonValueKind.String: + metadata[prop.Name] = prop.Value.GetString() ?? ""; + break; + case JsonValueKind.Number: + metadata[prop.Name] = prop.Value.GetInt32(); + break; + case JsonValueKind.True: + case JsonValueKind.False: + metadata[prop.Name] = prop.Value.GetBoolean(); + break; + default: + metadata[prop.Name] = prop.Value.ToString(); + break; + } + } } - } - } - trackingRewards[objectId] = metadata; + trackingRewards[objectId] = metadata; - // Write tracking data - var trackingData = new Dictionary + // Write tracking data + var trackingData = new Dictionary { { "result", 0 }, { "rewards", trackingRewards } }; - string trackingJson = System.Text.Json.JsonSerializer.Serialize(trackingData, new JsonSerializerOptions { WriteIndented = true }); - File.WriteAllText(trackingFile, trackingJson); + string trackingJson = System.Text.Json.JsonSerializer.Serialize(trackingData, new JsonSerializerOptions { WriteIndented = true }); + File.WriteAllText(trackingFile, trackingJson); #if DEBUG - LoggerAccessor.LogInfo($"[SSFW] - SSFWRewardServiceInventoryPOST: Updated tracking file: {trackingFile}"); + LoggerAccessor.LogInfo($"[SSFW] - RewardServiceInventoryPOST: Updated tracking file: {trackingFile}"); #endif - } - } + } + } - string updatedCountsJson = System.Text.Json.JsonSerializer.Serialize(counts, new JsonSerializerOptions { WriteIndented = true }); - File.WriteAllText(countsStore, updatedCountsJson); + string updatedCountsJson = System.Text.Json.JsonSerializer.Serialize(counts, new JsonSerializerOptions { WriteIndented = true }); + File.WriteAllText(countsStore, updatedCountsJson); #if DEBUG - LoggerAccessor.LogInfo($"[SSFW] - SSFWRewardServiceInventoryPOST: Updated counts file: {countsStore}"); + LoggerAccessor.LogInfo($"[SSFW] - RewardServiceInventoryPOST: Updated counts file: {countsStore}"); #endif + } + + } + + } catch (System.Text.Json.JsonException ex) { - LoggerAccessor.LogError($"[SSFW] - SSFWRewardServiceInventoryPOST: Error parsing JSON payload: {ex.Message}"); + LoggerAccessor.LogError($"[SSFW] - RewardServiceInventoryPOST: Error parsing JSON payload: {ex.Message}"); } catch (Exception ex) { - LoggerAccessor.LogError($"[SSFW] - SSFWRewardServiceInventoryPOST: Error processing POST request: {ex.Message}"); + LoggerAccessor.LogError($"[SSFW] - RewardServiceInventoryPOST: Error processing POST request: {ex.Message}"); } return Encoding.UTF8.GetBytes(@"{ ""idList"": [""00000000-00000000-00000000-00000001""]}"); @@ -449,7 +498,7 @@ private void ProcessTrunkObjectUpdate(string trunkFilePath, Dictionary entries, Dicti return JsonConvert.SerializeObject(jsonObject); } } -} +} \ No newline at end of file diff --git a/Servers/SSFWServer/Services/SSFWAuditService.cs b/Servers/SSFWServer/Services/SSFWAuditService.cs deleted file mode 100644 index f826dd476..000000000 --- a/Servers/SSFWServer/Services/SSFWAuditService.cs +++ /dev/null @@ -1,41 +0,0 @@ -using CastleLibrary.Sony.SSFW; -using CustomLogger; -using Newtonsoft.Json; -using System.Text; - -namespace SSFWServer.Services -{ - public class SSFWAuditService - { - private string? sessionid; - private string? env; - private string? key; - - public SSFWAuditService(string sessionid, string env, string? key) - { - this.sessionid = sessionid; - this.env = env; - this.key = key; - } - - public void HandleAuditService(string absolutepath, byte[] buffer) - { - string fileNameGUID = GuidGenerator.SSFWGenerateGuid(sessionid, env); - string auditLogPath = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutepath}/{env}"; - try - { - Directory.CreateDirectory(auditLogPath); - - var obj = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(buffer)); - File.WriteAllText($"{auditLogPath}/{fileNameGUID}.json", JsonConvert.SerializeObject(obj, Formatting.Indented)); -#if DEBUG - LoggerAccessor.LogInfo($"[SSFW] : Audit event posted: {fileNameGUID}"); -#endif - } - catch (Exception ex) - { - LoggerAccessor.LogError($"[SSFW] - SSFWAuditService HandleAuditService ERROR: \n{ex}"); - } - } - } -} \ No newline at end of file diff --git a/Servers/SSFWServer/Services/TradingService.cs b/Servers/SSFWServer/Services/TradingService.cs new file mode 100644 index 000000000..5e528ec5b --- /dev/null +++ b/Servers/SSFWServer/Services/TradingService.cs @@ -0,0 +1,247 @@ +using CustomLogger; +using NetCoreServer; +using Newtonsoft.Json; +using System; + +namespace SSFWServer.Services +{ + public class TradingService + { + private string? sessionid; + private string? env; + private string? key; + + public TradingService(string sessionid, string env, string? key) + { + this.sessionid = sessionid; + this.env = env; + this.key = key; + } + + private static List tradeTransactions = new(); + + // + public class RootObject + { + public List? members { get; set; } + } + + public class TradeTransaction + { + public string tradeRequester = string.Empty; + public string tradePartner = string.Empty; + public int transId = 0; + public int seqNumb = 0; + + public int tradeAmount = 0; + //itemList is a dictionary list of pairs. + public Dictionary tradeRequesterItemList = new Dictionary(); + public Dictionary tradePartnerItemList = new Dictionary(); + + public Status status { get; set; } + //cards to trade etc + } + + public enum Status : int + { + Active = 0, + Commited = 1, + PartiallyCommited = 2, + Cancelled = 3 + } + + + public string HandleTradingService(HttpRequest req, string sessionid, string absolutepath) + { + TradeTransaction newTradeRequest = new TradeTransaction(); + + int existingCardTradingTransactionId = 0; + int sequenceNum = 0; + var absoPathArray = absolutepath.Split("/"); + + string? currentUserId = SSFWUserSessionManager.GetIdBySessionId(sessionid); + if (string.IsNullOrEmpty(currentUserId)) + return $" {{ \"result\": -1, \"id\": -1 }} "; + + LoggerAccessor.LogInfo(absoPathArray.Count()); + + //if this is a existing Trade Transaction, assign! + if (absoPathArray.Length > 3) + { + existingCardTradingTransactionId = Convert.ToInt32(absolutepath.Split("/")[3]); + } + + //CommitTrade sends SequenceNumber + if (absoPathArray.Length > 4) + { + sequenceNum = Convert.ToInt32(absolutepath.Split("/")[4]); + } + + if (req.Method == "POST") + { + //If we DO have a existing trade transaction in the process, handle it! + if (existingCardTradingTransactionId > 0) { + + foreach (var transaction in tradeTransactions) + { + //ADDTRADEITEMS + //If a existing transaction was created, update it here! + if (transaction.transId == existingCardTradingTransactionId) + { + transaction.transId = existingCardTradingTransactionId; + transaction.seqNumb = sequenceNum; //Set to 0 initially till set later + + try + { + // Deserialize directly into Dictionary using Newtonsoft.Json + var reqitemList = JsonConvert.DeserializeObject>(req.Body); + + if (reqitemList == null || reqitemList.Count == 0) + { + LoggerAccessor.LogInfo($"[SSFW] TradingService - Existing transaction {transaction.transId} failed to update, request contained no Items to add!"); + return $" {{ \"result\": -1, \"id\": -1 }} "; + } + + if(transaction.tradePartner == currentUserId) + { + transaction.tradePartnerItemList = reqitemList; + } else //transaction.tradeRequester == curentUserId + { + transaction.tradeRequesterItemList = reqitemList; + } + + LoggerAccessor.LogInfo($"[SSFW] TradingService - Existing transaction {transaction.transId} has been updated"); + tradeTransactions.Add(transaction); + return $" {{ \"result\": 0, \"id\": -1 }} "; + } catch (Exception ex) + { + LoggerAccessor.LogError($"[SSFW] TradingService - Exception caught attempting to remove existing trade transaction id {existingCardTradingTransactionId} with error {ex}"); + + } + + + } + } + + } else // otherwise create new transaction! + { + + string CreateTransactionBody = req.Body; + RootObject? result = JsonConvert.DeserializeObject(CreateTransactionBody); + string memberValue = result.members[0]; + + int index = 1; + foreach (var transaction in tradeTransactions) + { + newTradeRequest.tradeRequester = currentUserId; + newTradeRequest.tradePartner = memberValue; + + //If a existing transaction was created, update it here! + if (transaction.transId == existingCardTradingTransactionId) + { + //index = transaction.transId + 1; + + //newTradeRequest.transId = index; + //tradeTransactions.Add(newTradeRequest); + } + //Initial first Transaction starts at index 1 + else if (tradeTransactions.Count() == 0) + { + newTradeRequest.transId = index; + newTradeRequest.status = Status.Active; + tradeTransactions.Add(newTradeRequest); + + } + } + return $"{{ \"result\": 0, \"id\": {index} }}"; + + } + } + else if (req.Method == "GET") + { + #region Request Trade Status - Potentially unused + //Return current status of transaction + if (req.Url.Contains("status")) + { + var existingTrade = tradeTransactions.FirstOrDefault(x => x.transId == existingCardTradingTransactionId); + + + if (existingTrade != null) + { + LoggerAccessor.LogInfo($"[SSFW] TradingService - Checking current status of transactionId {existingTrade.transId} between Requester {existingTrade.tradeRequester} & Partner {existingTrade.tradePartner}: {existingTrade.status}"); + + return $@" {{ + ""result"" : 0, + ""message"" : ""Success"", + ""sequence"" : sequenceNumber, + ""ownerId"" :""{existingTrade.tradeRequester}"", + ""joinerId"" :""{existingTrade.tradePartner}"""" + ""status"" : {existingTrade.status}"" + }}"; + } else + { + LoggerAccessor.LogInfo($"[SSFW] TradingService - Checking current status of transactionId {existingTrade.transId} between Requester {existingTrade.tradeRequester} & Partner {existingTrade.tradePartner}: {existingTrade.status}"); + return $@" {{ + ""result"" : 0, + ""message"" : ""Success"", + ""sequence"" : sequenceNumber, + ""ownerId"" :""{existingTrade.tradeRequester}"", + ""joinerId"" :""{existingTrade.tradePartner}"""" + ""status"" : {existingTrade.status}"" + }}"; + } + } else + { + + if(existingCardTradingTransactionId > 0) + { + foreach (var transaction in tradeTransactions) + { + //ADDTRADEITEMS + //If a existing transaction was created, update it here! + if (transaction.transId == existingCardTradingTransactionId) + { + return $@"{{ + ""result"": 0, + ""sequence"": {transaction.seqNumb}, + ""{transaction.tradeRequester}"": {{ +{string.Join("", transaction.tradeRequesterItemList)} + }}, + ""{transaction.tradePartner}"": {{ +{string.Join("", transaction.tradePartnerItemList)} + }} +}}"; + } + } + + } + + + } + #endregion + } + #region Cancel Trade Transaction + else if (req.Method == "DELETE") + { + if (tradeTransactions.Count() > 0) + { + try + { + TradeTransaction? tradeRemovalRequest = tradeTransactions.FirstOrDefault(x => x.transId == existingCardTradingTransactionId) ?? null; + tradeTransactions.Remove(tradeRemovalRequest); + LoggerAccessor.LogError($"[SSFW] TradingService - Successfully cancelled existing trade transaction id {existingCardTradingTransactionId}"); + } + catch (Exception e) { + LoggerAccessor.LogError($"[SSFW] TradingService - Exception caught attempting to remove existing trade transaction id {existingCardTradingTransactionId} with error {e}"); + return $" {{ \"result\": 0, \"id\": -1 }} "; + } + + } + + } + #endregion + return $" {{ \"result\": 0, \"id\": -1 }} "; + + } + } +} \ No newline at end of file From 6080b1e71559f379e83189cd1d22f4c610e77d38 Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Tue, 30 Dec 2025 17:01:32 -0600 Subject: [PATCH 02/13] Restores DataMigrator in MS4 --- .../Helpers/DataMigrator/DataMigrator.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/Servers/SSFWServer/Helpers/DataMigrator/DataMigrator.cs b/Servers/SSFWServer/Helpers/DataMigrator/DataMigrator.cs index 69ddf3178..1f0d828c0 100644 --- a/Servers/SSFWServer/Helpers/DataMigrator/DataMigrator.cs +++ b/Servers/SSFWServer/Helpers/DataMigrator/DataMigrator.cs @@ -11,23 +11,25 @@ public static void MigrateSSFWData(string ssfwrootDirectory, string oldStr, stri foreach (string directory in new string[] { "/AvatarLayoutService", "/LayoutService", "/RewardsService", "/SaveDataService" }) { - foreach (FileSystemInfo item in new DirectoryInfo(ssfwrootDirectory + directory).AllFilesAndFoldersLinq().Where(item => item.FullName.Contains(oldStr))) + foreach (FileSystemInfo item in FileSystemUtils.AllFilesAndFoldersLinq(new DirectoryInfo(ssfwrootDirectory + directory)).Where(item => item.FullName.Contains(oldStr))) { // Construct the full path for the new file/folder in the target directory - string newPath = item.FullName.Replace(oldStr, newStr); + string newFilePath = item.FullName.Replace(oldStr, newStr); // Check if it's a file or directory and copy accordingly - if (item is FileInfo fileInfo && !File.Exists(newPath)) + if ((item is FileInfo fileInfo) && !File.Exists(newFilePath)) { - string? directoryPath = Path.GetDirectoryName(newPath); + string? directoryPath = Path.GetDirectoryName(newFilePath); if (!string.IsNullOrEmpty(directoryPath)) Directory.CreateDirectory(directoryPath); - File.Copy(item.FullName, newPath); + File.Copy(item.FullName, newFilePath); + + FileSystemUtils.SetFileReadWrite(newFilePath); } - else if (item is DirectoryInfo directoryInfo && !Directory.Exists(newPath)) - CopyDirectory(directoryInfo.FullName, newPath); + else if ((item is DirectoryInfo directoryInfo) && !Directory.Exists(newFilePath)) + CopyDirectory(directoryInfo.FullName, newFilePath); } } } @@ -39,15 +41,17 @@ private static void CopyDirectory(string source, string target) foreach (string file in Directory.GetFiles(source)) { - string destinationFile = Path.Combine(target, Path.GetFileName(file)); - if (!File.Exists(destinationFile)) + string newFilePath = Path.Combine(target, Path.GetFileName(file)); + if (!File.Exists(newFilePath)) { - string? directoryPath = Path.GetDirectoryName(destinationFile); + string? directoryPath = Path.GetDirectoryName(newFilePath); if (!string.IsNullOrEmpty(directoryPath)) Directory.CreateDirectory(directoryPath); - File.Copy(file, destinationFile); + File.Copy(file, newFilePath); + + FileSystemUtils.SetFileReadWrite(newFilePath); } } @@ -55,9 +59,7 @@ private static void CopyDirectory(string source, string target) { string destinationDirectory = Path.Combine(target, Path.GetFileName(directory)); if (!Directory.Exists(destinationDirectory)) - { CopyDirectory(directory, destinationDirectory); - } } } } From 792adf1e5d0aa7f2387165fce557905633d2f60b Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:24:51 -0600 Subject: [PATCH 03/13] - Optimizes Misc.cs by externalizing the two layouts into the repo upon build, additionally adds 4 new envs. - Move the KeepAlive Regex pattern to constant for clarity. - Optimizes TradingService json serializing and inheritance for responding appropriately. - Adjusts Status endpoint for bullet point 3. - Adjusts RewardServiceInventory error to return TrackingGUID variable simply. - Fix some .cs missing usings. --- Servers/SSFWServer/Helpers/Misc.cs | 253 +----------------- Servers/SSFWServer/Program.cs | 1 + Servers/SSFWServer/SSFWAccountManagement.cs | 1 + Servers/SSFWServer/Services/AuditService.cs | 7 +- .../SSFWServer/Services/IdentityService.cs | 6 +- .../SSFWServer/Services/KeepAliveService.cs | 4 +- Servers/SSFWServer/Services/RewardsService.cs | 7 +- Servers/SSFWServer/Services/TradingService.cs | 96 ++++--- .../static/layouts/HarborStudio.json | 204 ++++++++++++++ .../static/layouts/LegacyLayout.json | 208 ++++++++++++++ 10 files changed, 492 insertions(+), 295 deletions(-) create mode 100644 Servers/SSFWServer/static/layouts/HarborStudio.json create mode 100644 Servers/SSFWServer/static/layouts/LegacyLayout.json diff --git a/Servers/SSFWServer/Helpers/Misc.cs b/Servers/SSFWServer/Helpers/Misc.cs index f62cbd72c..2d10a6769 100644 --- a/Servers/SSFWServer/Helpers/Misc.cs +++ b/Servers/SSFWServer/Helpers/Misc.cs @@ -2,255 +2,7 @@ namespace SSFWServer.Helpers { public class Misc { - public static readonly string LegacyLayoutTemplate = "[{\"00000000-00000000-00000000-00000004\":{\"version\":3,\"wallpaper\":2,\"furniture\":" + - "[{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000010\",\"instanceId\":\"4874595585\",\"itemId\":0,\"" + - "positionX\":-4.287144660949707,\"positionY\":2.9999580383300781,\"positionZ\":-2.3795166015625,\"rotationX\":2.6903744583" + - "250955E-06,\"rotationY\":0.70767402648925781,\"rotationZ\":-2.1571504476014525E-06,\"rotationW\":0.70653915405273438,\"ti" + - "me\":1686384673},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000002\",\"instanceId\":\"4874595586\"" + - ",\"itemId\":1,\"positionX\":-3.7360246181488037,\"positionY\":2.9999902248382568,\"positionZ\":-0.93418246507644653,\"rot" + - "ationX\":1.5251726836140733E-05,\"rotationY\":0.92014747858047485,\"rotationZ\":-0.00032892703893594444,\"rotationW\":0.3" + - "9157184958457947,\"time\":1686384699},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000002\",\"instan" + - "ceId\":\"4874595587\",\"itemId\":2,\"positionX\":-4.2762022018432617,\"positionY\":2.9999568462371826,\"positionZ\":-4.15" + - "23990631103516,\"rotationX\":1.4554960570123399E-09,\"rotationY\":0.4747755229473114,\"rotationZ\":-1.4769816480963982E-0" + - "8,\"rotationW\":0.88010692596435547,\"time\":1686384723},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-" + - "00000002\",\"instanceId\":\"4874595588\",\"itemId\":3,\"positionX\":-2.8646721839904785,\"positionY\":2.9999570846557617," + - "\"positionZ\":-3.0560495853424072,\"rotationX\":0.00010053320875158533,\"rotationY\":-0.26336261630058289,\"rotationZ\":-" + - "3.8589099858654663E-05,\"rotationW\":0.96469688415527344,\"time\":1686384751},{\"flags\":0,\"furnitureObjectId\":\"000000" + - "00-00000000-00000002-00000001\",\"instanceId\":\"4874595589\",\"itemId\":4,\"positionX\":3.9096813201904297,\"positionY\"" + - ":2.9995136260986328,\"positionZ\":-4.2813630104064941,\"rotationX\":4.3287433072691783E-05,\"rotationY\":-0.5309971570968" + - "6279,\"rotationZ\":-3.9187150832731277E-05,\"rotationW\":0.8473736047744751,\"time\":1686384774},{\"flags\":0,\"furniture" + - "ObjectId\":\"00000000-00000000-00000002-00000004\",\"instanceId\":\"4874595590\",\"itemId\":5,\"positionX\":1.84187448024" + - "74976,\"positionY\":3.0001647472381592,\"positionZ\":-3.2746503353118896,\"rotationX\":-5.4990476201055571E-05,\"rotation" + - "Y\":-0.53177982568740845,\"rotationZ\":-1.335094293608563E-05,\"rotationW\":0.84688264131546021,\"time\":1686384795},{\"f" + - "lags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000008\",\"instanceId\":\"4874595591\",\"itemId\":6,\"posit" + - "ionX\":3.4726400375366211,\"positionY\":3.0000433921813965,\"positionZ\":4.783566951751709,\"rotationX\":6.13473239354789" + - "26E-05,\"rotationY\":0.99999260902404785,\"rotationZ\":-1.7070769899873994E-05,\"rotationW\":0.0038405421655625105,\"time" + - "\":1686384822},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000008\",\"instanceId\":\"4874595592\"," + - "\"itemId\":7,\"positionX\":3.4952659606933594,\"positionY\":3.0000007152557373,\"positionZ\":0.2776024341583252,\"rotatio" + - "nX\":-1.2929040167364292E-05,\"rotationY\":-0.0061355167999863625,\"rotationZ\":-4.4378830352798104E-05,\"rotationW\":0.9" + - "99981164932251,\"time\":1686384834},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000001\",\"instance" + - "Id\":\"4874595593\",\"itemId\":8,\"positionX\":1.3067165613174438,\"positionY\":2.9994897842407227,\"positionZ\":2.546649" + - "694442749,\"rotationX\":2.8451957405195571E-05,\"rotationY\":0.70562022924423218,\"rotationZ\":-8.0827621786738746E-06,\"" + - "rotationW\":0.70859026908874512,\"time\":1686384862},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-0000" + - "0003\",\"instanceId\":\"4874595594\",\"itemId\":9,\"positionX\":3.4803681373596191,\"positionY\":2.9999568462371826,\"pos" + - "itionZ\":2.5385856628417969,\"rotationX\":3.1659130428352E-08,\"rotationY\":-0.70712763071060181,\"rotationZ\":8.14428204" + - "87609424E-08,\"rotationW\":0.70708584785461426,\"time\":1686384884},{\"flags\":0,\"furnitureObjectId\":\"00000000-0000000" + - "0-00000002-00000009\",\"instanceId\":\"4874595595\",\"itemId\":10,\"positionX\":-3.5043892860412598,\"positionY\":2.99995" + - "68462371826,\"positionZ\":-9.527653694152832,\"rotationX\":-1.7184934222314041E-06,\"rotationY\":0.00023035785125102848,\"" + - "rotationZ\":2.5227839728358958E-07,\"rotationW\":0.99999994039535522,\"time\":1686384912},{\"flags\":0,\"furnitureObjectI" + - "d\":\"00000000-00000000-00000002-00000009\",\"instanceId\":\"4874595596\",\"itemId\":11,\"positionX\":3.6248698234558105," + - "\"positionY\":2.9999566078186035,\"positionZ\":-9.5347089767456055,\"rotationX\":-2.1324558474589139E-07,\"rotationY\":2." + - "0361580027383752E-05,\"rotationZ\":-4.7822368287597783E-08,\"rotationW\":1,\"time\":1686384931},{\"flags\":0,\"furnitureO" + - "bjectId\":\"00000000-00000000-00000002-00000005\",\"instanceId\":\"4874595597\",\"itemId\":12,\"positionX\":-3.5068926811" + - "218262,\"positionY\":3.4883472919464111,\"positionZ\":-9.5313901901245117,\"rotationX\":-0.00091801158851012588,\"rotatio" + - "nY\":0.006055513396859169,\"rotationZ\":0.000585820700507611,\"rotationW\":0.99998104572296143,\"time\":1686384961,\"phot" + - "o\":\"/Furniture/Modern2/lampOutputcube.dds\"},{\"flags\":0,\"furnitureObjectId\":\"00000000-00000000-00000002-00000005\"" + - ",\"instanceId\":\"4874595598\",\"itemId\":13,\"positionX\":3.6171293258666992,\"positionY\":3.4891724586486816,\"position" + - "Z\":-9.53490161895752,\"rotationX\":0.00042979296995326877,\"rotationY\":-0.0092521701008081436,\"rotationZ\":-0.00027207" + - "753737457097,\"rotationW\":0.99995702505111694,\"time\":1686385008,\"photo\":\"/Furniture/Modern2/lampOutputcube.dds\"}]}}]"; - - public static readonly string HarbourStudioLayout = "{\r\n" + - " \"version\": 3,\r\n" + - " \"wallpaper\": 2,\r\n" + - " \"furniture\": [\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000010\",\r\n" + - " \"instanceId\": \"4874595585\",\r\n" + - " \"itemId\": 0,\r\n" + - " \"positionX\": -4.287144660949707,\r\n" + - " \"positionY\": 2.999958038330078,\r\n" + - " \"positionZ\": -2.3795166015625,\r\n" + - " \"rotationX\": 2.6903744583250955E-06,\r\n" + - " \"rotationY\": 0.7076740264892578,\r\n" + - " \"rotationZ\": -2.1571504476014525E-06,\r\n" + - " \"rotationW\": 0.7065391540527344,\r\n" + - " \"time\": 1686384673\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000002\",\r\n" + - " \"instanceId\": \"4874595586\",\r\n" + - " \"itemId\": 1,\r\n" + - " \"positionX\": -3.7360246181488037,\r\n" + - " \"positionY\": 2.999990224838257,\r\n" + - " \"positionZ\": -0.9341824650764465,\r\n" + - " \"rotationX\": 1.5251726836140733E-05,\r\n" + - " \"rotationY\": 0.9201474785804749,\r\n" + - " \"rotationZ\": -0.00032892703893594444,\r\n" + - " \"rotationW\": 0.39157184958457947,\r\n" + - " \"time\": 1686384699\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000002\",\r\n" + - " \"instanceId\": \"4874595587\",\r\n" + - " \"itemId\": 2,\r\n" + - " \"positionX\": -4.276202201843262,\r\n" + - " \"positionY\": 2.9999568462371826,\r\n" + - " \"positionZ\": -4.152399063110352,\r\n" + - " \"rotationX\": 1.4554960570123399E-09,\r\n" + - " \"rotationY\": 0.4747755229473114,\r\n" + - " \"rotationZ\": -1.4769816480963982E-08,\r\n" + - " \"rotationW\": 0.8801069259643555,\r\n" + - " \"time\": 1686384723\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000002\",\r\n" + - " \"instanceId\": \"4874595588\",\r\n" + - " \"itemId\": 3,\r\n" + - " \"positionX\": -2.8646721839904785,\r\n" + - " \"positionY\": 2.9999570846557617,\r\n" + - " \"positionZ\": -3.0560495853424072,\r\n" + - " \"rotationX\": 0.00010053320875158533,\r\n" + - " \"rotationY\": -0.2633626163005829,\r\n" + - " \"rotationZ\": -3.858909985865466E-05,\r\n" + - " \"rotationW\": 0.9646968841552734,\r\n" + - " \"time\": 1686384751\r\n },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000001\",\r\n" + - " \"instanceId\": \"4874595589\",\r\n" + - " \"itemId\": 4,\r\n" + - " \"positionX\": 3.9096813201904297,\r\n" + - " \"positionY\": 2.999513626098633,\r\n" + - " \"positionZ\": -4.281363010406494,\r\n" + - " \"rotationX\": 4.328743307269178E-05,\r\n" + - " \"rotationY\": -0.5309971570968628,\r\n" + - " \"rotationZ\": -3.918715083273128E-05,\r\n" + - " \"rotationW\": 0.8473736047744751,\r\n" + - " \"time\": 1686384774\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000004\",\r\n" + - " \"instanceId\": \"4874595590\",\r\n" + - " \"itemId\": 5,\r\n" + - " \"positionX\": 1.8418744802474976,\r\n" + - " \"positionY\": 3.000164747238159,\r\n" + - " \"positionZ\": -3.2746503353118896,\r\n" + - " \"rotationX\": -5.499047620105557E-05,\r\n" + - " \"rotationY\": -0.5317798256874084,\r\n" + - " \"rotationZ\": -1.335094293608563E-05,\r\n" + - " \"rotationW\": 0.8468826413154602,\r\n" + - " \"time\": 1686384795\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000008\",\r\n" + - " \"instanceId\": \"4874595591\",\r\n" + - " \"itemId\": 6,\r\n" + - " \"positionX\": 3.472640037536621,\r\n" + - " \"positionY\": 3.0000433921813965,\r\n" + - " \"positionZ\": 4.783566951751709,\r\n" + - " \"rotationX\": 6.134732393547893E-05,\r\n" + - " \"rotationY\": 0.9999926090240479,\r\n" + - " \"rotationZ\": -1.7070769899873994E-05,\r\n" + - " \"rotationW\": 0.0038405421655625105,\r\n" + - " \"time\": 1686384822\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000008\",\r\n" + - " \"instanceId\": \"4874595592\",\r\n \"itemId\": 7,\r\n" + - " \"positionX\": 3.4952659606933594,\r\n" + - " \"positionY\": 3.0000007152557373,\r\n" + - " \"positionZ\": 0.2776024341583252,\r\n" + - " \"rotationX\": -1.2929040167364292E-05,\r\n" + - " \"rotationY\": -0.0061355167999863625,\r\n" + - " \"rotationZ\": -4.4378830352798104E-05,\r\n" + - " \"rotationW\": 0.999981164932251,\r\n" + - " \"time\": 1686384834\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000001\",\r\n" + - " \"instanceId\": \"4874595593\",\r\n" + - " \"itemId\": 8,\r\n" + - " \"positionX\": 1.3067165613174438,\r\n" + - " \"positionY\": 2.9994897842407227,\r\n" + - " \"positionZ\": 2.546649694442749,\r\n" + - " \"rotationX\": 2.845195740519557E-05,\r\n" + - " \"rotationY\": 0.7056202292442322,\r\n" + - " \"rotationZ\": -8.082762178673875E-06,\r\n" + - " \"rotationW\": 0.7085902690887451,\r\n" + - " \"time\": 1686384862\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000003\",\r\n" + - " \"instanceId\": \"4874595594\",\r\n" + - " \"itemId\": 9,\r\n \"positionX\": 3.480368137359619,\r\n" + - " \"positionY\": 2.9999568462371826,\r\n" + - " \"positionZ\": 2.538585662841797,\r\n" + - " \"rotationX\": 3.1659130428352E-08,\r\n" + - " \"rotationY\": -0.7071276307106018,\r\n" + - " \"rotationZ\": 8.144282048760942E-08,\r\n" + - " \"rotationW\": 0.7070858478546143,\r\n" + - " \"time\": 1686384884\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000009\",\r\n" + - " \"instanceId\": \"4874595595\",\r\n" + - " \"itemId\": 10,\r\n" + - " \"positionX\": -3.5043892860412598,\r\n" + - " \"positionY\": 2.9999568462371826,\r\n" + - " \"positionZ\": -9.527653694152832,\r\n" + - " \"rotationX\": -1.7184934222314041E-06,\r\n" + - " \"rotationY\": 0.00023035785125102848,\r\n" + - " \"rotationZ\": 2.522783972835896E-07,\r\n" + - " \"rotationW\": 0.9999999403953552,\r\n" + - " \"time\": 1686384912\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000009\",\r\n" + - " \"instanceId\": \"4874595596\",\r\n" + - " \"itemId\": 11,\r\n" + - " \"positionX\": 3.6248698234558105,\r\n" + - " \"positionY\": 2.9999566078186035,\r\n" + - " \"positionZ\": -9.534708976745605,\r\n" + - " \"rotationX\": -2.132455847458914E-07,\r\n" + - " \"rotationY\": 2.0361580027383752E-05,\r\n" + - " \"rotationZ\": -4.782236828759778E-08,\r\n" + - " \"rotationW\": 1,\r\n" + - " \"time\": 1686384931\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000005\",\r\n" + - " \"instanceId\": \"4874595597\",\r\n" + - " \"itemId\": 12,\r\n" + - " \"positionX\": -3.506892681121826,\r\n" + - " \"positionY\": 3.488347291946411,\r\n" + - " \"positionZ\": -9.531390190124512,\r\n" + - " \"rotationX\": -0.0009180115885101259,\r\n" + - " \"rotationY\": 0.006055513396859169,\r\n" + - " \"rotationZ\": 0.000585820700507611,\r\n" + - " \"rotationW\": 0.9999810457229614,\r\n" + - " \"time\": 1686384961,\r\n" + - " \"photo\": \"/Furniture/Modern2/lampOutputcube.dds\"\r\n" + - " },\r\n" + - " {\r\n" + - " \"flags\": 0,\r\n" + - " \"furnitureObjectId\": \"00000000-00000000-00000002-00000005\",\r\n" + - " \"instanceId\": \"4874595598\",\r\n" + - " \"itemId\": 13,\r\n" + - " \"positionX\": 3.617129325866699,\r\n" + - " \"positionY\": 3.4891724586486816,\r\n" + - " \"positionZ\": -9.53490161895752,\r\n" + - " \"rotationX\": 0.00042979296995326877,\r\n" + - " \"rotationY\": -0.009252170100808144,\r\n" + - " \"rotationZ\": -0.00027207753737457097,\r\n" + - " \"rotationW\": 0.9999570250511169,\r\n" + - " \"time\": 1686385008,\r\n" + - " \"photo\": \"/Furniture/Modern2/lampOutputcube.dds\"\r\n" + - " }\r\n" + - " ]\r\n" + - "}"; - + // Sandbox Environments public static List homeEnvs = new() { @@ -261,7 +13,8 @@ public class Misc "qcqa-a", "qcqa-j", "qcqa-h", "qcpreprod", "qcqab-e", "qcqab-a", "qcqab-j", "qcqab-h", "qcpreprodb", "coredev", "core-dev", "core-qa", - "cdev", "cdev2", "cdev3", "cdeva", "cdevb", "cdevc" + "cdev", "cdev2", "cdev3", "cdeva", "cdevb", "cdevc", + "nonprod1", "nonprod2", "nonprod3", "prodsp" }; /// diff --git a/Servers/SSFWServer/Program.cs b/Servers/SSFWServer/Program.cs index 8420c9fac..a5fd67551 100644 --- a/Servers/SSFWServer/Program.cs +++ b/Servers/SSFWServer/Program.cs @@ -20,6 +20,7 @@ public static class SSFWServerConfiguration public static string SSFWMinibase { get; set; } = "[]"; public static string SSFWLegacyKey { get; set; } = "**NoNoNoYouCantHaxThis****69"; public static string SSFWSessionIdKey { get; set; } = StringUtils.GenerateRandomBase64KeyAsync().Result; + public static string SSFWLayoutsFolder { get; set; } = $"{Directory.GetCurrentDirectory()}/static/layouts"; public static string SSFWStaticFolder { get; set; } = $"{Directory.GetCurrentDirectory()}/static/wwwssfwroot"; public static string HTTPSCertificateFile { get; set; } = $"{Directory.GetCurrentDirectory()}/static/SSL/SSFW.pfx"; public static string HTTPSCertificatePassword { get; set; } = "qwerty"; diff --git a/Servers/SSFWServer/SSFWAccountManagement.cs b/Servers/SSFWServer/SSFWAccountManagement.cs index 05a711e5c..3c315565b 100644 --- a/Servers/SSFWServer/SSFWAccountManagement.cs +++ b/Servers/SSFWServer/SSFWAccountManagement.cs @@ -1,5 +1,6 @@ using CustomLogger; using Newtonsoft.Json; +using SSFWServer.Helpers; using SSFWServer.Helpers.FileHelper; using System.Text; diff --git a/Servers/SSFWServer/Services/AuditService.cs b/Servers/SSFWServer/Services/AuditService.cs index ceade3e29..1bd9eb9cf 100644 --- a/Servers/SSFWServer/Services/AuditService.cs +++ b/Servers/SSFWServer/Services/AuditService.cs @@ -1,10 +1,9 @@ -using CustomLogger; +using CastleLibrary.Sony.SSFW; +using CustomLogger; using NetCoreServer; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using SSFWServer.Helpers; using System.Text; -using System.Text.Json.Nodes; namespace SSFWServer.Services { @@ -23,7 +22,7 @@ public AuditService(string sessionid, string env, string? key) public string HandleAuditService(string absolutepath, byte[] buffer, HttpRequest request) { - string fileNameGUID = GuidGen.SSFWGenerateGuid(sessionid, env); + string fileNameGUID = GuidGenerator.SSFWGenerateGuid(sessionid, env); string? personIdToCompare = SSFWUserSessionManager.GetIdBySessionId(sessionid); string auditLogPath = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutepath}"; diff --git a/Servers/SSFWServer/Services/IdentityService.cs b/Servers/SSFWServer/Services/IdentityService.cs index 989fe0b06..f70489f3b 100644 --- a/Servers/SSFWServer/Services/IdentityService.cs +++ b/Servers/SSFWServer/Services/IdentityService.cs @@ -211,12 +211,14 @@ public IdentityService(string XHomeClientVersion, string generalsecret, string h File.Delete($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json"); } else if (!File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/HarborStudio.json")) - File.WriteAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/HarborStudio.json", Misc.HarbourStudioLayout); + File.WriteAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/HarborStudio.json", + $"{SSFWServerConfiguration.SSFWLayoutsFolder}/HarborStudio.json"); } else { if (!File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json")) - File.WriteAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json", Misc.LegacyLayoutTemplate); + File.WriteAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json", + $"{SSFWServerConfiguration.SSFWLayoutsFolder}/LegacyLayout.json"); } if (!File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/{env}/rewards/{resultString}/mini.json")) diff --git a/Servers/SSFWServer/Services/KeepAliveService.cs b/Servers/SSFWServer/Services/KeepAliveService.cs index 21a93e1e3..363df6d9f 100644 --- a/Servers/SSFWServer/Services/KeepAliveService.cs +++ b/Servers/SSFWServer/Services/KeepAliveService.cs @@ -7,6 +7,8 @@ public class KeepAliveService { public bool UpdateKeepAliveForClient(string absolutePath, HttpResponse res) { + Regex regex = new Regex(@"^[{(]?([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})[)}]?$"); + const byte GuidLength = 36; int index = absolutePath.IndexOf("/morelife"); @@ -15,7 +17,7 @@ public bool UpdateKeepAliveForClient(string absolutePath, HttpResponse res) // Extract the substring between the last '/' and the morelife separator. string resultSessionId = absolutePath.Substring(index - GuidLength, GuidLength); - if (Regex.IsMatch(resultSessionId, @"^[{(]?([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})[)}]?$")) + if (regex.IsMatch(resultSessionId)) { SSFWUserSessionManager.UpdateKeepAliveTime(resultSessionId); return true; diff --git a/Servers/SSFWServer/Services/RewardsService.cs b/Servers/SSFWServer/Services/RewardsService.cs index ad2c1cf7e..3efe03693 100644 --- a/Servers/SSFWServer/Services/RewardsService.cs +++ b/Servers/SSFWServer/Services/RewardsService.cs @@ -277,17 +277,20 @@ public byte[] RewardServiceInventory(byte[]? buffer, string directorypath, strin { using JsonDocument document = JsonDocument.Parse(fixedJsonPayload); JsonElement root = document.RootElement; + + //Only return trackingGuid on error + var errorPayload = Encoding.UTF8.GetBytes($"{{\"idList\": [\"{trackingGuid}\"] }}"); if (!root.TryGetProperty("rewards", out JsonElement rewardsElement) || rewardsElement.ValueKind != JsonValueKind.Array) { LoggerAccessor.LogError("[SSFW] - RewardServiceInventoryPOST: Invalid payload - 'rewards' must be an array."); - return Encoding.UTF8.GetBytes("{\"idList\": [\"00000000-00000000-00000000-00000001\"] }"); + return errorPayload; } var rewards = rewardsElement.EnumerateArray(); if (!rewards.MoveNext()) { LoggerAccessor.LogError("[SSFW] - RewardServiceInventoryPOST: Invalid payload - 'rewards' array is empty."); - return Encoding.UTF8.GetBytes("{\"idList\": [\"00000000-00000000-00000000-00000001\"] }"); + return errorPayload; } Dictionary counts = new(); diff --git a/Servers/SSFWServer/Services/TradingService.cs b/Servers/SSFWServer/Services/TradingService.cs index 5e528ec5b..3ada7cd6e 100644 --- a/Servers/SSFWServer/Services/TradingService.cs +++ b/Servers/SSFWServer/Services/TradingService.cs @@ -18,7 +18,14 @@ public TradingService(string sessionid, string env, string? key) this.key = key; } - private static List tradeTransactions = new(); + + public class TradeResponse + { + public int result = -1; + public int id = -1; + public string message = string.Empty; + + } // public class RootObject @@ -26,12 +33,13 @@ public class RootObject public List? members { get; set; } } - public class TradeTransaction + private static List tradeTransactions = new(); + public class TradeTransaction : TradeResponse { - public string tradeRequester = string.Empty; - public string tradePartner = string.Empty; + public string ownerId = string.Empty; + public string joinerId = string.Empty; public int transId = 0; - public int seqNumb = 0; + public int sequence = 0; public int tradeAmount = 0; //itemList is a dictionary list of pairs. @@ -39,7 +47,6 @@ public class TradeTransaction public Dictionary tradePartnerItemList = new Dictionary(); public Status status { get; set; } - //cards to trade etc } public enum Status : int @@ -54,6 +61,7 @@ public enum Status : int public string HandleTradingService(HttpRequest req, string sessionid, string absolutepath) { TradeTransaction newTradeRequest = new TradeTransaction(); + TradeResponse tradeResponse = new(); int existingCardTradingTransactionId = 0; int sequenceNum = 0; @@ -61,10 +69,10 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs string? currentUserId = SSFWUserSessionManager.GetIdBySessionId(sessionid); if (string.IsNullOrEmpty(currentUserId)) - return $" {{ \"result\": -1, \"id\": -1 }} "; - + return JsonConvert.SerializeObject(tradeResponse); +#if DEBUG LoggerAccessor.LogInfo(absoPathArray.Count()); - +#endif //if this is a existing Trade Transaction, assign! if (absoPathArray.Length > 3) { @@ -89,7 +97,7 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs if (transaction.transId == existingCardTradingTransactionId) { transaction.transId = existingCardTradingTransactionId; - transaction.seqNumb = sequenceNum; //Set to 0 initially till set later + transaction.sequence = sequenceNum; //Set to 0 initially till set later try { @@ -99,10 +107,10 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs if (reqitemList == null || reqitemList.Count == 0) { LoggerAccessor.LogInfo($"[SSFW] TradingService - Existing transaction {transaction.transId} failed to update, request contained no Items to add!"); - return $" {{ \"result\": -1, \"id\": -1 }} "; + return JsonConvert.SerializeObject(tradeResponse); } - if(transaction.tradePartner == currentUserId) + if(transaction.joinerId == currentUserId) { transaction.tradePartnerItemList = reqitemList; } else //transaction.tradeRequester == curentUserId @@ -112,10 +120,13 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs LoggerAccessor.LogInfo($"[SSFW] TradingService - Existing transaction {transaction.transId} has been updated"); tradeTransactions.Add(transaction); - return $" {{ \"result\": 0, \"id\": -1 }} "; + + tradeResponse.result = 0; + return JsonConvert.SerializeObject(tradeResponse); } catch (Exception ex) { LoggerAccessor.LogError($"[SSFW] TradingService - Exception caught attempting to remove existing trade transaction id {existingCardTradingTransactionId} with error {ex}"); + return JsonConvert.SerializeObject(tradeResponse); } @@ -133,8 +144,8 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs int index = 1; foreach (var transaction in tradeTransactions) { - newTradeRequest.tradeRequester = currentUserId; - newTradeRequest.tradePartner = memberValue; + newTradeRequest.ownerId = currentUserId; + newTradeRequest.joinerId = memberValue; //If a existing transaction was created, update it here! if (transaction.transId == existingCardTradingTransactionId) @@ -153,7 +164,10 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs } } - return $"{{ \"result\": 0, \"id\": {index} }}"; + + tradeResponse.result = 0; + tradeResponse.id = index; + return JsonConvert.SerializeObject(tradeResponse); } } @@ -165,30 +179,34 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs { var existingTrade = tradeTransactions.FirstOrDefault(x => x.transId == existingCardTradingTransactionId); - if (existingTrade != null) { - LoggerAccessor.LogInfo($"[SSFW] TradingService - Checking current status of transactionId {existingTrade.transId} between Requester {existingTrade.tradeRequester} & Partner {existingTrade.tradePartner}: {existingTrade.status}"); + LoggerAccessor.LogInfo($"[SSFW] TradingService - Checking current status of transactionId {existingTrade.transId} between Requester {existingTrade.ownerId} & Partner {existingTrade.joinerId}: {existingTrade.status}"); + + //RootObject? result = JsonConvert.DeserializeObject(CreateTransactionBody); + + newTradeRequest.result = 0; + newTradeRequest.message = "Success"; + newTradeRequest.status = existingTrade.status; + newTradeRequest.sequence = existingTrade.sequence; + newTradeRequest.joinerId = existingTrade.joinerId; + return JsonConvert.SerializeObject(newTradeRequest); + + /* return $@" {{ ""result"" : 0, ""message"" : ""Success"", - ""sequence"" : sequenceNumber, + ""sequence"" : {existingTrade.seqNumb}, ""ownerId"" :""{existingTrade.tradeRequester}"", - ""joinerId"" :""{existingTrade.tradePartner}"""" + ""joinerId"" :""{existingTrade.tradePartner}"" ""status"" : {existingTrade.status}"" - }}"; + }}";*/ + } else { - LoggerAccessor.LogInfo($"[SSFW] TradingService - Checking current status of transactionId {existingTrade.transId} between Requester {existingTrade.tradeRequester} & Partner {existingTrade.tradePartner}: {existingTrade.status}"); - return $@" {{ - ""result"" : 0, - ""message"" : ""Success"", - ""sequence"" : sequenceNumber, - ""ownerId"" :""{existingTrade.tradeRequester}"", - ""joinerId"" :""{existingTrade.tradePartner}"""" - ""status"" : {existingTrade.status}"" - }}"; + LoggerAccessor.LogInfo($"[SSFW] TradingService - Checking current status of transactionId {existingTrade.transId} between Requester {existingTrade.ownerId} & Partner {existingTrade.joinerId}: {existingTrade.status}"); + return JsonConvert.SerializeObject(tradeResponse); } } else { @@ -203,11 +221,11 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs { return $@"{{ ""result"": 0, - ""sequence"": {transaction.seqNumb}, - ""{transaction.tradeRequester}"": {{ + ""sequence"": {transaction.sequence}, + ""{transaction.ownerId}"": {{ {string.Join("", transaction.tradeRequesterItemList)} }}, - ""{transaction.tradePartner}"": {{ + ""{transaction.joinerId}"": {{ {string.Join("", transaction.tradePartnerItemList)} }} }}"; @@ -216,7 +234,6 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs } - } #endregion } @@ -230,17 +247,24 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs TradeTransaction? tradeRemovalRequest = tradeTransactions.FirstOrDefault(x => x.transId == existingCardTradingTransactionId) ?? null; tradeTransactions.Remove(tradeRemovalRequest); LoggerAccessor.LogError($"[SSFW] TradingService - Successfully cancelled existing trade transaction id {existingCardTradingTransactionId}"); + + tradeResponse.result = 0; + return JsonConvert.SerializeObject(tradeResponse); } catch (Exception e) { LoggerAccessor.LogError($"[SSFW] TradingService - Exception caught attempting to remove existing trade transaction id {existingCardTradingTransactionId} with error {e}"); - return $" {{ \"result\": 0, \"id\": -1 }} "; + return JsonConvert.SerializeObject(tradeResponse); } } } #endregion - return $" {{ \"result\": 0, \"id\": -1 }} "; + + + tradeResponse.result = 0; + return JsonConvert.SerializeObject(tradeResponse); + } } diff --git a/Servers/SSFWServer/static/layouts/HarborStudio.json b/Servers/SSFWServer/static/layouts/HarborStudio.json new file mode 100644 index 000000000..f54d70d61 --- /dev/null +++ b/Servers/SSFWServer/static/layouts/HarborStudio.json @@ -0,0 +1,204 @@ +{ + "version": 3, + "wallpaper": 2, + "furniture": [ + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000010", + "instanceId": "4874595585", + "itemId": 0, + "positionX": -4.287144660949707, + "positionY": 2.999958038330078, + "positionZ": -2.3795166015625, + "rotationX": 2.6903744583250955E-06, + "rotationY": 0.7076740264892578, + "rotationZ": -2.1571504476014525E-06, + "rotationW": 0.7065391540527344, + "time": 1686384673 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000002", + "instanceId": "4874595586", + "itemId": 1, + "positionX": -3.7360246181488037, + "positionY": 2.999990224838257, + "positionZ": -0.9341824650764465, + "rotationX": 1.5251726836140733E-05, + "rotationY": 0.9201474785804749, + "rotationZ": -0.00032892703893594444, + "rotationW": 0.39157184958457947, + "time": 1686384699 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000002", + "instanceId": "4874595587", + "itemId": 2, + "positionX": -4.276202201843262, + "positionY": 2.9999568462371826, + "positionZ": -4.152399063110352, + "rotationX": 1.4554960570123399E-09, + "rotationY": 0.4747755229473114, + "rotationZ": -1.4769816480963982E-08, + "rotationW": 0.8801069259643555, + "time": 1686384723 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000002", + "instanceId": "4874595588", + "itemId": 3, + "positionX": -2.8646721839904785, + "positionY": 2.9999570846557617, + "positionZ": -3.0560495853424072, + "rotationX": 0.00010053320875158533, + "rotationY": -0.2633626163005829, + "rotationZ": -3.858909985865466E-05, + "rotationW": 0.9646968841552734, + "time": 1686384751 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000001", + "instanceId": "4874595589", + "itemId": 4, + "positionX": 3.9096813201904297, + "positionY": 2.999513626098633, + "positionZ": -4.281363010406494, + "rotationX": 4.328743307269178E-05, + "rotationY": -0.5309971570968628, + "rotationZ": -3.918715083273128E-05, + "rotationW": 0.8473736047744751, + "time": 1686384774 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000004", + "instanceId": "4874595590", + "itemId": 5, + "positionX": 1.8418744802474976, + "positionY": 3.000164747238159, + "positionZ": -3.2746503353118896, + "rotationX": -5.499047620105557E-05, + "rotationY": -0.5317798256874084, + "rotationZ": -1.335094293608563E-05, + "rotationW": 0.8468826413154602, + "time": 1686384795 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000008", + "instanceId": "4874595591", + "itemId": 6, + "positionX": 3.472640037536621, + "positionY": 3.0000433921813965, + "positionZ": 4.783566951751709, + "rotationX": 6.134732393547893E-05, + "rotationY": 0.9999926090240479, + "rotationZ": -1.7070769899873994E-05, + "rotationW": 0.0038405421655625105, + "time": 1686384822 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000008", + "instanceId": "4874595592", + "itemId": 7, + "positionX": 3.4952659606933594, + "positionY": 3.0000007152557373, + "positionZ": 0.2776024341583252, + "rotationX": -1.2929040167364292E-05, + "rotationY": -0.0061355167999863625, + "rotationZ": -4.4378830352798104E-05, + "rotationW": 0.999981164932251, + "time": 1686384834 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000001", + "instanceId": "4874595593", + "itemId": 8, + "positionX": 1.3067165613174438, + "positionY": 2.9994897842407227, + "positionZ": 2.546649694442749, + "rotationX": 2.845195740519557E-05, + "rotationY": 0.7056202292442322, + "rotationZ": -8.082762178673875E-06, + "rotationW": 0.7085902690887451, + "time": 1686384862 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000003", + "instanceId": "4874595594", + "itemId": 9, + "positionX": 3.480368137359619, + "positionY": 2.9999568462371826, + "positionZ": 2.538585662841797, + "rotationX": 3.1659130428352E-08, + "rotationY": -0.7071276307106018, + "rotationZ": 8.144282048760942E-08, + "rotationW": 0.7070858478546143, + "time": 1686384884 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000009", + "instanceId": "4874595595", + "itemId": 10, + "positionX": -3.5043892860412598, + "positionY": 2.9999568462371826, + "positionZ": -9.527653694152832, + "rotationX": -1.7184934222314041E-06, + "rotationY": 0.00023035785125102848, + "rotationZ": 2.522783972835896E-07, + "rotationW": 0.9999999403953552, + "time": 1686384912 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000009", + "instanceId": "4874595596", + "itemId": 11, + "positionX": 3.6248698234558105, + "positionY": 2.9999566078186035, + "positionZ": -9.534708976745605, + "rotationX": -2.132455847458914E-07, + "rotationY": 2.0361580027383752E-05, + "rotationZ": -4.782236828759778E-08, + "rotationW": 1, + "time": 1686384931 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000005", + "instanceId": "4874595597", + "itemId": 12, + "positionX": -3.506892681121826, + "positionY": 3.488347291946411, + "positionZ": -9.531390190124512, + "rotationX": -0.0009180115885101259, + "rotationY": 0.006055513396859169, + "rotationZ": 0.000585820700507611, + "rotationW": 0.9999810457229614, + "time": 1686384961, + "photo": "/Furniture/Modern2/lampOutputcube.dds" + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000005", + "instanceId": "4874595598", + "itemId": 13, + "positionX": 3.617129325866699, + "positionY": 3.4891724586486816, + "positionZ": -9.53490161895752, + "rotationX": 0.00042979296995326877, + "rotationY": -0.009252170100808144, + "rotationZ": -0.00027207753737457097, + "rotationW": 0.9999570250511169, + "time": 1686385008, + "photo": "/Furniture/Modern2/lampOutputcube.dds" + } + ] +} \ No newline at end of file diff --git a/Servers/SSFWServer/static/layouts/LegacyLayout.json b/Servers/SSFWServer/static/layouts/LegacyLayout.json new file mode 100644 index 000000000..01688f728 --- /dev/null +++ b/Servers/SSFWServer/static/layouts/LegacyLayout.json @@ -0,0 +1,208 @@ +[ + { + "00000000-00000000-00000000-00000004": { + "version": 3, + "wallpaper": 2, + "furniture": [ + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000010", + "instanceId": "4874595585", + "itemId": 0, + "positionX": -4.287144660949707, + "positionY": 2.9999580383300781, + "positionZ": -2.3795166015625, + "rotationX": 2.6903744583250955E-06, + "rotationY": 0.70767402648925781, + "rotationZ": -2.1571504476014525E-06, + "rotationW": 0.70653915405273438, + "time": 1686384673 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000002", + "instanceId": "4874595586", + "itemId": 1, + "positionX": -3.7360246181488037, + "positionY": 2.9999902248382568, + "positionZ": -0.93418246507644653, + "rotationX": 1.5251726836140733E-05, + "rotationY": 0.92014747858047485, + "rotationZ": -0.00032892703893594444, + "rotationW": 0.39157184958457947, + "time": 1686384699 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000002", + "instanceId": "4874595587", + "itemId": 2, + "positionX": -4.2762022018432617, + "positionY": 2.9999568462371826, + "positionZ": -4.1523990631103516, + "rotationX": 1.4554960570123399E-09, + "rotationY": 0.4747755229473114, + "rotationZ": -1.4769816480963982E-08, + "rotationW": 0.88010692596435547, + "time": 1686384723 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000002", + "instanceId": "4874595588", + "itemId": 3, + "positionX": -2.8646721839904785, + "positionY": 2.9999570846557617, + "positionZ": -3.0560495853424072, + "rotationX": 0.00010053320875158533, + "rotationY": -0.26336261630058289, + "rotationZ": -3.8589099858654663E-05, + "rotationW": 0.96469688415527344, + "time": 1686384751 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000001", + "instanceId": "4874595589", + "itemId": 4, + "positionX": 3.9096813201904297, + "positionY": 2.9995136260986328, + "positionZ": -4.2813630104064941, + "rotationX": 4.3287433072691783E-05, + "rotationY": -0.53099715709686279, + "rotationZ": -3.9187150832731277E-05, + "rotationW": 0.8473736047744751, + "time": 1686384774 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000004", + "instanceId": "4874595590", + "itemId": 5, + "positionX": 1.8418744802474976, + "positionY": 3.0001647472381592, + "positionZ": -3.2746503353118896, + "rotationX": -5.4990476201055571E-05, + "rotationY": -0.53177982568740845, + "rotationZ": -1.335094293608563E-05, + "rotationW": 0.84688264131546021, + "time": 1686384795 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000008", + "instanceId": "4874595591", + "itemId": 6, + "positionX": 3.4726400375366211, + "positionY": 3.0000433921813965, + "positionZ": 4.783566951751709, + "rotationX": 6.1347323935478926E-05, + "rotationY": 0.99999260902404785, + "rotationZ": -1.7070769899873994E-05, + "rotationW": 0.0038405421655625105, + "time": 1686384822 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000008", + "instanceId": "4874595592", + "itemId": 7, + "positionX": 3.4952659606933594, + "positionY": 3.0000007152557373, + "positionZ": 0.2776024341583252, + "rotationX": -1.2929040167364292E-05, + "rotationY": -0.0061355167999863625, + "rotationZ": -4.4378830352798104E-05, + "rotationW": 0.999981164932251, + "time": 1686384834 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000001", + "instanceId": "4874595593", + "itemId": 8, + "positionX": 1.3067165613174438, + "positionY": 2.9994897842407227, + "positionZ": 2.546649694442749, + "rotationX": 2.8451957405195571E-05, + "rotationY": 0.70562022924423218, + "rotationZ": -8.0827621786738746E-06, + "rotationW": 0.70859026908874512, + "time": 1686384862 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000003", + "instanceId": "4874595594", + "itemId": 9, + "positionX": 3.4803681373596191, + "positionY": 2.9999568462371826, + "positionZ": 2.5385856628417969, + "rotationX": 3.1659130428352E-08, + "rotationY": -0.70712763071060181, + "rotationZ": 8.1442820487609424E-08, + "rotationW": 0.70708584785461426, + "time": 1686384884 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000009", + "instanceId": "4874595595", + "itemId": 10, + "positionX": -3.5043892860412598, + "positionY": 2.9999568462371826, + "positionZ": -9.527653694152832, + "rotationX": -1.7184934222314041E-06, + "rotationY": 0.00023035785125102848, + "rotationZ": 2.5227839728358958E-07, + "rotationW": 0.99999994039535522, + "time": 1686384912 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000009", + "instanceId": "4874595596", + "itemId": 11, + "positionX": 3.6248698234558105, + "positionY": 2.9999566078186035, + "positionZ": -9.5347089767456055, + "rotationX": -2.1324558474589139E-07, + "rotationY": 2.0361580027383752E-05, + "rotationZ": -4.7822368287597783E-08, + "rotationW": 1, + "time": 1686384931 + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000005", + "instanceId": "4874595597", + "itemId": 12, + "positionX": -3.5068926811218262, + "positionY": 3.4883472919464111, + "positionZ": -9.5313901901245117, + "rotationX": -0.00091801158851012588, + "rotationY": 0.006055513396859169, + "rotationZ": 0.000585820700507611, + "rotationW": 0.99998104572296143, + "time": 1686384961, + "photo": "/Furniture/Modern2/lampOutputcube.dds" + }, + { + "flags": 0, + "furnitureObjectId": "00000000-00000000-00000002-00000005", + "instanceId": "4874595598", + "itemId": 13, + "positionX": 3.6171293258666992, + "positionY": 3.4891724586486816, + "positionZ": -9.53490161895752, + "rotationX": 0.00042979296995326877, + "rotationY": -0.0092521701008081436, + "rotationZ": -0.00027207753737457097, + "rotationW": 0.99995702505111694, + "time": 1686385008, + "photo": "/Furniture/Modern2/lampOutputcube.dds" + } + ] + } + } +] \ No newline at end of file From 42213f380ddaf9ead5e1d7909dd79faf15732df8 Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Wed, 31 Dec 2025 07:29:53 -0600 Subject: [PATCH 04/13] Move KeepAlive Regex GUID to own constant in respective helper class for use elsewhere in SSFW. Further optimize TradingService Status json serializer response. --- .../Helpers/RegexHelper/GUIDValidator.cs | 2 + .../SSFWServer/Services/KeepAliveService.cs | 5 +- Servers/SSFWServer/Services/TradingService.cs | 62 +++++++++++-------- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs b/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs index b1d89be72..c924a982d 100644 --- a/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs +++ b/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs @@ -6,6 +6,8 @@ namespace SSFWServer.Helpers.RegexHelper { public class GUIDValidator { + public static Regex RegexSessionValidator = new Regex(@"^[{(]?([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})[)}]?$"); + public static string FixJsonValues(string json) { // Match GUID portion with 8-8-8-8 format (fix unquoted GUIDs) diff --git a/Servers/SSFWServer/Services/KeepAliveService.cs b/Servers/SSFWServer/Services/KeepAliveService.cs index 363df6d9f..36c7788f6 100644 --- a/Servers/SSFWServer/Services/KeepAliveService.cs +++ b/Servers/SSFWServer/Services/KeepAliveService.cs @@ -1,4 +1,5 @@ using NetCoreServer; +using SSFWServer.Helpers.RegexHelper; using System.Text.RegularExpressions; namespace SSFWServer.Services @@ -7,8 +8,6 @@ public class KeepAliveService { public bool UpdateKeepAliveForClient(string absolutePath, HttpResponse res) { - Regex regex = new Regex(@"^[{(]?([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})[)}]?$"); - const byte GuidLength = 36; int index = absolutePath.IndexOf("/morelife"); @@ -17,7 +16,7 @@ public bool UpdateKeepAliveForClient(string absolutePath, HttpResponse res) // Extract the substring between the last '/' and the morelife separator. string resultSessionId = absolutePath.Substring(index - GuidLength, GuidLength); - if (regex.IsMatch(resultSessionId)) + if (GUIDValidator.RegexSessionValidator.IsMatch(resultSessionId)) { SSFWUserSessionManager.UpdateKeepAliveTime(resultSessionId); return true; diff --git a/Servers/SSFWServer/Services/TradingService.cs b/Servers/SSFWServer/Services/TradingService.cs index 3ada7cd6e..464091be4 100644 --- a/Servers/SSFWServer/Services/TradingService.cs +++ b/Servers/SSFWServer/Services/TradingService.cs @@ -1,7 +1,10 @@ using CustomLogger; +using MaxMind.GeoIP2.Responses; using NetCoreServer; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using System; +using System.Transactions; namespace SSFWServer.Services { @@ -19,12 +22,14 @@ public TradingService(string sessionid, string env, string? key) } - public class TradeResponse + public class BaseResponse { - public int result = -1; - public int id = -1; - public string message = string.Empty; - + [JsonProperty("result")] + public int result { get; set; } + [JsonProperty("id")] + public int id { get; set; } = -1; + [JsonProperty("message")] + public string message { get; set; } = string.Empty; } // @@ -33,13 +38,17 @@ public class RootObject public List? members { get; set; } } - private static List tradeTransactions = new(); - public class TradeTransaction : TradeResponse + private static List tradeTransactions = new(); + public class TradeTransactionResponse : BaseResponse { + [JsonProperty("ownerId")] public string ownerId = string.Empty; + [JsonProperty("joinerId")] public string joinerId = string.Empty; + [JsonProperty("transactionId")] public int transId = 0; - public int sequence = 0; + [JsonProperty("sequence")] + public long sequence { get; set; } = 0; public int tradeAmount = 0; //itemList is a dictionary list of pairs. @@ -60,8 +69,8 @@ public enum Status : int public string HandleTradingService(HttpRequest req, string sessionid, string absolutepath) { - TradeTransaction newTradeRequest = new TradeTransaction(); - TradeResponse tradeResponse = new(); + BaseResponse tradeResponse = new(); + TradeTransactionResponse newTradeTransactionResponse = new TradeTransactionResponse(); int existingCardTradingTransactionId = 0; int sequenceNum = 0; @@ -127,16 +136,13 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs { LoggerAccessor.LogError($"[SSFW] TradingService - Exception caught attempting to remove existing trade transaction id {existingCardTradingTransactionId} with error {ex}"); return JsonConvert.SerializeObject(tradeResponse); - } - } } } else // otherwise create new transaction! - { - + { string CreateTransactionBody = req.Body; RootObject? result = JsonConvert.DeserializeObject(CreateTransactionBody); string memberValue = result.members[0]; @@ -144,8 +150,8 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs int index = 1; foreach (var transaction in tradeTransactions) { - newTradeRequest.ownerId = currentUserId; - newTradeRequest.joinerId = memberValue; + newTradeTransactionResponse.ownerId = currentUserId; + newTradeTransactionResponse.joinerId = memberValue; //If a existing transaction was created, update it here! if (transaction.transId == existingCardTradingTransactionId) @@ -158,9 +164,9 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs //Initial first Transaction starts at index 1 else if (tradeTransactions.Count() == 0) { - newTradeRequest.transId = index; - newTradeRequest.status = Status.Active; - tradeTransactions.Add(newTradeRequest); + newTradeTransactionResponse.transId = index; + newTradeTransactionResponse.status = Status.Active; + tradeTransactions.Add(newTradeTransactionResponse); } } @@ -185,13 +191,15 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs //RootObject? result = JsonConvert.DeserializeObject(CreateTransactionBody); - newTradeRequest.result = 0; - newTradeRequest.message = "Success"; - newTradeRequest.status = existingTrade.status; - newTradeRequest.sequence = existingTrade.sequence; - newTradeRequest.joinerId = existingTrade.joinerId; + JObject jsonResponse = JObject.FromObject(existingTrade); + + jsonResponse[newTradeTransactionResponse.result] = 0; + jsonResponse[newTradeTransactionResponse.message] = "Sucess"; + jsonResponse[newTradeTransactionResponse.status] = Convert.ToInt32(existingTrade.status); + jsonResponse[newTradeTransactionResponse.sequence] = existingTrade.sequence; + jsonResponse[newTradeTransactionResponse.joinerId] = existingTrade.joinerId; - return JsonConvert.SerializeObject(newTradeRequest); + return jsonResponse.ToString(Formatting.Indented); /* return $@" {{ @@ -244,8 +252,8 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs { try { - TradeTransaction? tradeRemovalRequest = tradeTransactions.FirstOrDefault(x => x.transId == existingCardTradingTransactionId) ?? null; - tradeTransactions.Remove(tradeRemovalRequest); + TradeTransactionResponse? tradeRemovalResp = tradeTransactions.FirstOrDefault(x => x.transId == existingCardTradingTransactionId) ?? null; + tradeTransactions.Remove(tradeRemovalResp); LoggerAccessor.LogError($"[SSFW] TradingService - Successfully cancelled existing trade transaction id {existingCardTradingTransactionId}"); tradeResponse.result = 0; From c3ab21bf5db95bfb8bd3f5324db9d031e3145fea Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Thu, 1 Jan 2026 09:35:43 -0600 Subject: [PATCH 05/13] Further cleaning - Moved functions out of Misc.cs and eliminated it from the csproj as the helper functions and classes were better positioned for consistency. - Moved GetFileList debug endpoints to "new" SaveDataService for consistency. - Moved Scenelist parser to FileHelper - Fixes typo in status response message along with some small code tweaks for JSON serializer, added missing ownerId entry, fix the error response for null existing trades. - Fixes double slash in counts folder path along with function/log edits for consistency to what they do. - Further adjusts AvatarService to move the code from Processor to inside the Service itself. - Move HomeEnvs to Program.cs along with ExtractPortion - UserData now is in SSFWAccountManagement. --- .../{ => FileHelper}/ScenelistParser.cs | 2 +- Servers/SSFWServer/Helpers/Misc.cs | 43 ------------------- Servers/SSFWServer/Program.cs | 32 +++++++++++++- Servers/SSFWServer/SSFWAccountManagement.cs | 8 +++- Servers/SSFWServer/SSFWProcessor.cs | 41 +++++++----------- Servers/SSFWServer/Services/AvatarService.cs | 10 ++++- Servers/SSFWServer/Services/ClanService.cs | 18 ++++---- .../SSFWServer/Services/IdentityService.cs | 2 +- Servers/SSFWServer/Services/LayoutService.cs | 7 ++- Servers/SSFWServer/Services/RewardsService.cs | 17 ++++---- .../SaveDataService.cs} | 10 ++--- Servers/SSFWServer/Services/TradingService.cs | 43 ++++++++----------- 12 files changed, 108 insertions(+), 125 deletions(-) rename Servers/SSFWServer/Helpers/{ => FileHelper}/ScenelistParser.cs (99%) delete mode 100644 Servers/SSFWServer/Helpers/Misc.cs rename Servers/SSFWServer/{Helpers/SaveDataHelper/GetFileList.cs => Services/SaveDataService.cs} (86%) diff --git a/Servers/SSFWServer/Helpers/ScenelistParser.cs b/Servers/SSFWServer/Helpers/FileHelper/ScenelistParser.cs similarity index 99% rename from Servers/SSFWServer/Helpers/ScenelistParser.cs rename to Servers/SSFWServer/Helpers/FileHelper/ScenelistParser.cs index 2772bf086..68089b707 100644 --- a/Servers/SSFWServer/Helpers/ScenelistParser.cs +++ b/Servers/SSFWServer/Helpers/FileHelper/ScenelistParser.cs @@ -2,7 +2,7 @@ using System.Collections.Concurrent; using System.Xml; -namespace SSFWServer.Helpers +namespace SSFWServer.Helpers.FileHelper { public class ScenelistParser { diff --git a/Servers/SSFWServer/Helpers/Misc.cs b/Servers/SSFWServer/Helpers/Misc.cs deleted file mode 100644 index 2d10a6769..000000000 --- a/Servers/SSFWServer/Helpers/Misc.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace SSFWServer.Helpers -{ - public class Misc - { - - // Sandbox Environments - public static List homeEnvs = new() - { - "cprod", "cprodts", "cpreprod", "cpreprodb", - "rc-qa", "rcdev", "rc-dev", "cqa-e", - "cqa-a", "cqa-j", "cqa-h", "cqab-e", - "cqab-a", "cqab-j", "cqab-h", "qcqa-e", - "qcqa-a", "qcqa-j", "qcqa-h", "qcpreprod", - "qcqab-e", "qcqab-a", "qcqab-j", "qcqab-h", - "qcpreprodb", "coredev", "core-dev", "core-qa", - "cdev", "cdev2", "cdev3", "cdeva", "cdevb", "cdevc", - "nonprod1", "nonprod2", "nonprod3", "prodsp" - }; - - /// - /// Extract a portion of a string winthin boundaries. - /// Extrait une portion d'un string entre des limites. - /// - /// The input string. - /// The amount of characters to remove from the left to the right. - /// The amount of characters to remove from the right to the left. - /// A string. - public static string? ExtractPortion(string input, int startToRemove, int endToRemove) - { - if (input.Length < startToRemove + endToRemove) - return null; - - return input[startToRemove..][..^endToRemove]; - } - } - - public class SSFWUserData - { - public string? Username { get; set; } - public int LogonCount { get; set; } - public int IGA { get; set; } - } -} diff --git a/Servers/SSFWServer/Program.cs b/Servers/SSFWServer/Program.cs index a5fd67551..82e560cb4 100644 --- a/Servers/SSFWServer/Program.cs +++ b/Servers/SSFWServer/Program.cs @@ -5,12 +5,12 @@ using System.Runtime; using System.Security.Cryptography; using Microsoft.Extensions.Logging; -using SSFWServer.Helpers; using MultiServerLibrary.Extension; using MultiServerLibrary.HTTP; using MultiServerLibrary.SSL; using MultiServerLibrary; using MultiServerLibrary.SNMP; +using SSFWServer.Helpers.FileHelper; public static class SSFWServerConfiguration { @@ -50,6 +50,20 @@ public static class SSFWServerConfiguration "nonprod4.homeserverservices.online.scee.com", }; + // Sandbox Environments + public static List homeEnvs = new() + { + "cprod", "cprodts", "cpreprod", "cpreprodb", + "rc-qa", "rcdev", "rc-dev", "cqa-e", + "cqa-a", "cqa-j", "cqa-h", "cqab-e", + "cqab-a", "cqab-j", "cqab-h", "qcqa-e", + "qcqa-a", "qcqa-j", "qcqa-h", "qcpreprod", + "qcqab-e", "qcqab-a", "qcqab-j", "qcqab-h", + "qcpreprodb", "coredev", "core-dev", "core-qa", + "cdev", "cdev2", "cdev3", "cdeva", "cdevb", "cdevc", + "nonprod1", "nonprod2", "nonprod3", "prodsp" + }; + /// /// Tries to load the specified configuration file. /// Throws an exception if it fails to find the file. @@ -294,4 +308,20 @@ static void Main() Thread.Sleep(Timeout.Infinite); } } + + /// + /// Extract a portion of a string winthin boundaries. + /// Extrait une portion d'un string entre des limites. + /// + /// The input string. + /// The amount of characters to remove from the left to the right. + /// The amount of characters to remove from the right to the left. + /// A string. + public static string? ExtractPortion(string input, int startToRemove, int endToRemove) + { + if (input.Length < startToRemove + endToRemove) + return null; + + return input[startToRemove..][..^endToRemove]; + } } \ No newline at end of file diff --git a/Servers/SSFWServer/SSFWAccountManagement.cs b/Servers/SSFWServer/SSFWAccountManagement.cs index 3c315565b..957299b07 100644 --- a/Servers/SSFWServer/SSFWAccountManagement.cs +++ b/Servers/SSFWServer/SSFWAccountManagement.cs @@ -1,11 +1,17 @@ using CustomLogger; using Newtonsoft.Json; -using SSFWServer.Helpers; using SSFWServer.Helpers.FileHelper; using System.Text; namespace SSFWServer { + public class SSFWUserData + { + public string? Username { get; set; } + public int LogonCount { get; set; } + public int IGA { get; set; } + } + public class SSFWAccountManagement { public static int ReadOrMigrateAccount(byte[] extractedData, string? username, string? sessionid, string? key) diff --git a/Servers/SSFWServer/SSFWProcessor.cs b/Servers/SSFWServer/SSFWProcessor.cs index e9efa9846..31566a9ee 100644 --- a/Servers/SSFWServer/SSFWProcessor.cs +++ b/Servers/SSFWServer/SSFWProcessor.cs @@ -4,10 +4,7 @@ using MultiServerLibrary.SSL; using NetCoreServer; using NetCoreServer.CustomServers; -using Org.BouncyCastle.Ocsp; -using SSFWServer.Helpers; using SSFWServer.Helpers.FileHelper; -using SSFWServer.SaveDataHelper; using SSFWServer.Services; using System.Collections.Concurrent; using System.Net; @@ -177,7 +174,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse string? env = ExtractBeforeFirstDot(host); sessionid = GetHeaderValue(Headers, "X-Home-Session-Id"); - if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) env = "cprod"; // Instantiate services @@ -190,6 +187,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse AvatarLayoutService avatarLayout = new(sessionid, _legacykey); ClanService clanService = new(sessionid); PlayerLookupService playerLookupService = new(); + SaveDataService saveDataService = new(); TradingService tradingService = new(sessionid, env, _legacykey); switch (request.Method) @@ -242,7 +240,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse #region SaveDataService else if (absolutepath.Contains($"/SaveDataService/{env}/{segments.LastOrDefault()}")) { - string? res = GetFileList.SaveDataDebugGetFileList(directoryPath, segments.LastOrDefault()); + string? res = saveDataService.DebugGetFileList(directoryPath, segments.LastOrDefault()); ; if (res != null) Response.MakeGetResponse(res, "application/json"); else @@ -415,21 +413,12 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse #region SaveData AvatarService else if (absolutepath.Contains($"/SaveDataService/avatar/{env}/") && absolutepath.EndsWith(".jpg")) { - if (File.Exists(filePath)) - { - byte[]? res = FileHelper.ReadAllBytes(filePath, _legacykey); + byte[]? res = avatarService.HandleAvatarService(absolutepath, filePath, _legacykey); - if (res != null) - Response.MakeGetResponse(res, "image/jpg"); - else - Response.MakeErrorResponse(); - } + if (res != null) + Response.MakeGetResponse(res, "image/jpg"); else - { - Response.Clear(); - Response.SetBegin((int)HttpStatusCode.NotFound); - Response.SetBody(); - } + Response.MakeErrorResponse(404, "Not Found"); } else { @@ -525,7 +514,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse Response.MakeOkResponse(); } else if ( - absolutepath.Contains($"/RewardsService/pm_{env}_inv/") + absolutepath.Contains($"/RewardsService/pm_{env}_cards/") || absolutepath.Contains($"/RewardsService/pmcards/") || absolutepath.Contains($"/RewardsService/p4t-{env}/")) Response.MakeGetResponse(rewardSvc.HandleRewardServiceInvPOST(postbuffer, directoryPath, filePath, absolutepath), "application/json"); @@ -694,7 +683,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse else if (absolutepath.Contains($"/RewardsService/pmcards/rewards/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}") && IsSSFWRegistered(sessionid)) { - var res = rewardSvc.HandleRewardServiceInvDELETE(directoryPath, filePath, absolutepath, UserAgent, sessionid); + var res = rewardSvc.HandleRewardServiceWipeInvDELETE(directoryPath, filePath, absolutepath, UserAgent, sessionid); if (res != null) Response.MakeOkResponse(); else @@ -894,7 +883,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse string sceneId = GetHeaderValue(Headers, "sceneId", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) env = "cprod"; Response.Clear(); @@ -1013,7 +1002,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse sessionId = GetHeaderValue(Headers, "sessionid", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) env = "cprod"; userId = SSFWUserSessionManager.GetIdBySessionId(sessionId); @@ -1057,7 +1046,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse sessionId = GetHeaderValue(Headers, "sessionid", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) env = "cprod"; userId = SSFWUserSessionManager.GetIdBySessionId(sessionId); @@ -1103,7 +1092,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse sessionId = GetHeaderValue(Headers, "sessionid", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) env = "cprod"; userId = SSFWUserSessionManager.GetIdBySessionId(sessionId); @@ -1156,7 +1145,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse sessionId = GetHeaderValue(Headers, "sessionid", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) env = "cprod"; userId = SSFWUserSessionManager.GetIdBySessionId(sessionId); @@ -1202,7 +1191,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse sessionId = GetHeaderValue(Headers, "sessionid", false); env = GetHeaderValue(Headers, "env", false); - if (string.IsNullOrEmpty(env) || !Misc.homeEnvs.Contains(env)) + if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) env = "cprod"; userId = SSFWUserSessionManager.GetIdBySessionId(sessionId); diff --git a/Servers/SSFWServer/Services/AvatarService.cs b/Servers/SSFWServer/Services/AvatarService.cs index 6a528ccf5..799bc2363 100644 --- a/Servers/SSFWServer/Services/AvatarService.cs +++ b/Servers/SSFWServer/Services/AvatarService.cs @@ -4,9 +4,15 @@ namespace SSFWServer.Services { public class AvatarService { - public byte[]? HandleAvatarService(string absolutepath, string? key) + public byte[]? HandleAvatarService(string absolutepath, string filePath, string? key) { - return FileHelper.ReadAllBytes(absolutepath, key); + if (File.Exists(filePath)) + { + return FileHelper.ReadAllBytes(absolutepath, key); + } else + { + return null; + } } } } \ No newline at end of file diff --git a/Servers/SSFWServer/Services/ClanService.cs b/Servers/SSFWServer/Services/ClanService.cs index 0558965e9..2148292b1 100644 --- a/Servers/SSFWServer/Services/ClanService.cs +++ b/Servers/SSFWServer/Services/ClanService.cs @@ -36,13 +36,13 @@ public HttpResponse HandleClanDetailsService(HttpRequest req, HttpResponse res, // TODO, extract the proper region. string jsonToWrite = $@"{{ -""region"":""en-US"", -""message"":""OK"", -""result"":0, -""psnClanId"":{psnClanId}, -""sceneObjectId"":""{idElement.GetString()!}"", -""personId"":""{_sessionid}"", -""clanId"":""{DotNetHasher.ComputeMD5String(Encoding.UTF8.GetBytes(psnClanId))}"" +""region"": ""en-US"", +""message"": ""OK"", +""result"": 0, +""psnClanId"": {psnClanId}, +""sceneObjectId"": ""{idElement.GetString()!}"", +""personId"": ""{_sessionid}"", +""clanId"": ""{DotNetHasher.ComputeMD5String(Encoding.UTF8.GetBytes(psnClanId))}"" }}"; File.WriteAllText(filePath, jsonToWrite); @@ -66,14 +66,14 @@ public HttpResponse HandleClanDetailsService(HttpRequest req, HttpResponse res, return res.MakeErrorResponse(404, "Not Found"); } - // Delete clan details + // Delete clan details if clan requested DELETE method try { if (File.Exists(filePath)) { string? sceneObjectId = JsonDocument.Parse(File.ReadAllText(filePath)).RootElement.GetProperty("sceneObjectId").GetString() ?? string.Empty; File.Delete(filePath); - return res.MakeGetResponse($"{{\"sceneObjectIds\":[\"{sceneObjectId}\"]}}", "application/json"); + return res.MakeGetResponse($"{{\"sceneObjectIds\": [\"{sceneObjectId}\"] }}", "application/json"); } } catch diff --git a/Servers/SSFWServer/Services/IdentityService.cs b/Servers/SSFWServer/Services/IdentityService.cs index f70489f3b..0b0731896 100644 --- a/Servers/SSFWServer/Services/IdentityService.cs +++ b/Servers/SSFWServer/Services/IdentityService.cs @@ -2,10 +2,10 @@ using CustomLogger; using NetHasher; using MultiServerLibrary.Extension; -using SSFWServer.Helpers; using SSFWServer.Helpers.DataMigrator; using System.Text; using CastleLibrary.Sony.SSFW; +using SSFWServer.Helpers.FileHelper; namespace SSFWServer.Services { diff --git a/Servers/SSFWServer/Services/LayoutService.cs b/Servers/SSFWServer/Services/LayoutService.cs index bf2f0970f..be21d56d0 100644 --- a/Servers/SSFWServer/Services/LayoutService.cs +++ b/Servers/SSFWServer/Services/LayoutService.cs @@ -1,6 +1,5 @@ using CustomLogger; using Newtonsoft.Json.Linq; -using SSFWServer.Helpers; using SSFWServer.Helpers.FileHelper; using System.Text; using System.Text.RegularExpressions; @@ -47,7 +46,7 @@ public bool HandleLayoutServicePOST(byte[] buffer, string directorypath, string } else { - string scenename = scenemap.FirstOrDefault(x => x.Value == Misc.ExtractPortion(kvp.Key, 13, 18)).Key; + string scenename = scenemap.FirstOrDefault(x => x.Value == Program.ExtractPortion(kvp.Key, 13, 18)).Key; if (!string.IsNullOrEmpty(scenename)) { if (File.Exists(directorypath + $"/{kvp.Key}.json")) // SceneID now mapped, so SceneID based file has become obsolete. @@ -75,7 +74,7 @@ public bool HandleLayoutServicePOST(byte[] buffer, string directorypath, string } else { - string scenename = scenemap.FirstOrDefault(x => x.Value == Misc.ExtractPortion(sceneid, 13, 18)).Key; + string scenename = scenemap.FirstOrDefault(x => x.Value == Program.ExtractPortion(sceneid, 13, 18)).Key; if (!string.IsNullOrEmpty(scenename)) { if (File.Exists(directorypath + $"/{sceneid}.json")) // SceneID now mapped, so SceneID based file has become obsolete. @@ -124,7 +123,7 @@ public bool HandleLayoutServicePOST(byte[] buffer, string directorypath, string } else { - string scenename = ScenelistParser.sceneDictionary.FirstOrDefault(x => x.Value == Misc.ExtractPortion(sceneid, 13, 18)).Key; + string scenename = ScenelistParser.sceneDictionary.FirstOrDefault(x => x.Value == Program.ExtractPortion(sceneid, 13, 18)).Key; if (!string.IsNullOrEmpty(scenename)) { string filepath = directorypath + $"/{scenename}.json"; diff --git a/Servers/SSFWServer/Services/RewardsService.cs b/Servers/SSFWServer/Services/RewardsService.cs index 3efe03693..c5a719ef6 100644 --- a/Servers/SSFWServer/Services/RewardsService.cs +++ b/Servers/SSFWServer/Services/RewardsService.cs @@ -42,19 +42,19 @@ public byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, st { return RewardServiceInventory(null, directorypath, filepath, absolutepath, false, true); } else { - LoggerAccessor.LogError($"[SSFW] - HandleRewardServiceInvCardTrackingDataDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to delete Tracking data!"); + LoggerAccessor.LogError($"[SSFW] - HandleRewardServiceInvCardTrackingDataDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to delete Card Tracking data!"); return null; } } - public byte[]? HandleRewardServiceInvDELETE(string directorypath, string filepath, string absolutepath, string userAgent, string sessionId) + public byte[]? HandleRewardServiceWipeInvDELETE(string directorypath, string filepath, string absolutepath, string userAgent, string sessionId) { AdminObjectService adminObjectService = new AdminObjectService(sessionId, key); if(adminObjectService.IsAdminVerified(userAgent)) { return RewardServiceInventory(null, directorypath, filepath, absolutepath, true, false); } else { - LoggerAccessor.LogError($"[SSFW] - HandleRewardServiceInvDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to delete Tracking data!"); + LoggerAccessor.LogError($"[SSFW] - HandleRewardServiceWipeInvDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to wipe inventory data!"); return null; } } @@ -105,7 +105,7 @@ public void SSFWUpdateMini(string filePath, string postData, bool delete) continue; if (rewardValue.Type != JTokenType.Integer) { - LoggerAccessor.LogInfo($"[RewardsService] - Reward:{rewardValue} earned, adding to mini file:{filePath}."); + LoggerAccessor.LogInfo($"[SSFW] SSFWUpdateMini - Reward: {rewardValue} earned, adding to mini file: {filePath}."); rewardValue = 1; } @@ -257,20 +257,21 @@ public void TrunkServiceProcess(string filePath, string request, string env, str } } - public byte[] RewardServiceInventory(byte[]? buffer, string directorypath, string filepath, string absolutePath, bool deleteInv, bool deleteOnlyTracking) + public byte[] RewardServiceInventory(byte[] buffer, string directorypath, string filepath, string absolutePath, bool deleteInv, bool deleteOnlyTracking) { //Tracking Inventory GUID const string trackingGuid = "00000000-00000000-00000000-00000001"; // fallback/hardcoded tracking GUID // File paths based on the provided format - string countsStoreDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}/"; - Directory.CreateDirectory(Path.GetDirectoryName(countsStoreDir)); + string countsStoreDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}"; string countsStore = $"{countsStoreDir}/counts.json"; string trackingFileDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}/object"; - Directory.CreateDirectory(Path.GetDirectoryName(trackingFileDir)); string trackingFile = $"{trackingFileDir}/{trackingGuid}.json"; + Directory.CreateDirectory(Path.GetDirectoryName(countsStoreDir)); + Directory.CreateDirectory(Path.GetDirectoryName(trackingFileDir)); + //Parse Buffer string fixedJsonPayload = GUIDValidator.FixJsonValues(Encoding.UTF8.GetString(buffer)); try diff --git a/Servers/SSFWServer/Helpers/SaveDataHelper/GetFileList.cs b/Servers/SSFWServer/Services/SaveDataService.cs similarity index 86% rename from Servers/SSFWServer/Helpers/SaveDataHelper/GetFileList.cs rename to Servers/SSFWServer/Services/SaveDataService.cs index b560da01b..f4f97e776 100644 --- a/Servers/SSFWServer/Helpers/SaveDataHelper/GetFileList.cs +++ b/Servers/SSFWServer/Services/SaveDataService.cs @@ -1,11 +1,11 @@ -using CustomLogger; +using CustomLogger; using Newtonsoft.Json; -namespace SSFWServer.SaveDataHelper +namespace SSFWServer.Services { - public static class GetFileList + public class SaveDataService { - public static string? SaveDataDebugGetFileList(string directoryPath, string? segment) + public string? DebugGetFileList(string directoryPath, string? segment) { try { @@ -19,7 +19,7 @@ public static class GetFileList } catch (Exception e) { - LoggerAccessor.LogError($"[SSFW] - SaveDataDebug GetFileList ERROR: \n{e}"); + LoggerAccessor.LogError($"[SSFW] - DebugGetFileList ERROR: \n{e}"); } return null; diff --git a/Servers/SSFWServer/Services/TradingService.cs b/Servers/SSFWServer/Services/TradingService.cs index 464091be4..7d3ac6eda 100644 --- a/Servers/SSFWServer/Services/TradingService.cs +++ b/Servers/SSFWServer/Services/TradingService.cs @@ -1,10 +1,7 @@ using CustomLogger; -using MaxMind.GeoIP2.Responses; using NetCoreServer; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using System; -using System.Transactions; namespace SSFWServer.Services { @@ -21,7 +18,6 @@ public TradingService(string sessionid, string env, string? key) this.key = key; } - public class BaseResponse { [JsonProperty("result")] @@ -32,9 +28,9 @@ public class BaseResponse public string message { get; set; } = string.Empty; } - // public class RootObject { + [JsonProperty("members")] public List? members { get; set; } } @@ -42,18 +38,18 @@ public class RootObject public class TradeTransactionResponse : BaseResponse { [JsonProperty("ownerId")] - public string ownerId = string.Empty; + public string ownerId { get; set; } = string.Empty; [JsonProperty("joinerId")] - public string joinerId = string.Empty; + public string joinerId { get; set; } = string.Empty; [JsonProperty("transactionId")] - public int transId = 0; + public int transId { get; set; } = 0; [JsonProperty("sequence")] public long sequence { get; set; } = 0; - public int tradeAmount = 0; + public int tradeAmount { get; set; } = 0; //itemList is a dictionary list of pairs. - public Dictionary tradeRequesterItemList = new Dictionary(); - public Dictionary tradePartnerItemList = new Dictionary(); + public Dictionary tradeRequesterItemList { get; set; } = new Dictionary(); + public Dictionary tradePartnerItemList { get; set; } = new Dictionary(); public Status status { get; set; } } @@ -135,6 +131,8 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs } catch (Exception ex) { LoggerAccessor.LogError($"[SSFW] TradingService - Exception caught attempting to remove existing trade transaction id {existingCardTradingTransactionId} with error {ex}"); + + tradeResponse.result = -1; return JsonConvert.SerializeObject(tradeResponse); } @@ -143,8 +141,7 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs } else // otherwise create new transaction! { - string CreateTransactionBody = req.Body; - RootObject? result = JsonConvert.DeserializeObject(CreateTransactionBody); + RootObject? result = JsonConvert.DeserializeObject(req.Body); string memberValue = result.members[0]; int index = 1; @@ -174,7 +171,6 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs tradeResponse.result = 0; tradeResponse.id = index; return JsonConvert.SerializeObject(tradeResponse); - } } else if (req.Method == "GET") @@ -194,26 +190,28 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs JObject jsonResponse = JObject.FromObject(existingTrade); jsonResponse[newTradeTransactionResponse.result] = 0; - jsonResponse[newTradeTransactionResponse.message] = "Sucess"; - jsonResponse[newTradeTransactionResponse.status] = Convert.ToInt32(existingTrade.status); + jsonResponse[newTradeTransactionResponse.message] = "Success"; jsonResponse[newTradeTransactionResponse.sequence] = existingTrade.sequence; + jsonResponse[newTradeTransactionResponse.ownerId] = existingTrade.ownerId; jsonResponse[newTradeTransactionResponse.joinerId] = existingTrade.joinerId; + jsonResponse[newTradeTransactionResponse.status] = Convert.ToInt32(existingTrade.status); return jsonResponse.ToString(Formatting.Indented); - /* + /* Original return $@" {{ ""result"" : 0, ""message"" : ""Success"", - ""sequence"" : {existingTrade.seqNumb}, + ""sequence"" : {existingTrade.sequence}, ""ownerId"" :""{existingTrade.tradeRequester}"", - ""joinerId"" :""{existingTrade.tradePartner}"" + ""joinerId"" :""{existingTrade.joinerId}"" ""status"" : {existingTrade.status}"" }}";*/ - } else + } + else { - LoggerAccessor.LogInfo($"[SSFW] TradingService - Checking current status of transactionId {existingTrade.transId} between Requester {existingTrade.ownerId} & Partner {existingTrade.joinerId}: {existingTrade.status}"); + LoggerAccessor.LogInfo($"[SSFW] TradingService - GET method failed to find existing trade transaction!"); return JsonConvert.SerializeObject(tradeResponse); } } else @@ -269,11 +267,8 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs } #endregion - tradeResponse.result = 0; return JsonConvert.SerializeObject(tradeResponse); - - } } } \ No newline at end of file From 3919afd1bea0a758b7dbaee7ee3bbb3a96e47ca7 Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Thu, 1 Jan 2026 13:16:02 -0600 Subject: [PATCH 06/13] Compile Fix + Code optimizing - Fixed reference on ExtractPortion in IdentityService on compile - Made various function and variable methods static and readonly, static if not accessing instanced data, and readonly if data isn't being written too, only instatiated when the class is created. - Moves SSFWServerConfiguration to seperate class for clarity. - Optimize Jsonproperty declaration based on Visual Studio info messages. - added null checks for cases where it should NOT be null to reduce warnings. - Moves more Regex GUID checks to GUIDValidator. --- .../Helpers/RegexHelper/GUIDValidator.cs | 15 ++ Servers/SSFWServer/Program.cs | 151 +----------------- Servers/SSFWServer/SSFWServerConfiguration.cs | 144 +++++++++++++++++ .../SSFWServer/Services/IdentityService.cs | 2 +- Servers/SSFWServer/Services/LayoutService.cs | 19 +-- Servers/SSFWServer/Services/RewardsService.cs | 61 +++---- Servers/SSFWServer/Services/TradingService.cs | 80 ++++++---- 7 files changed, 246 insertions(+), 226 deletions(-) create mode 100644 Servers/SSFWServer/SSFWServerConfiguration.cs diff --git a/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs b/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs index c924a982d..584da9b90 100644 --- a/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs +++ b/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs @@ -8,6 +8,21 @@ public class GUIDValidator { public static Regex RegexSessionValidator = new Regex(@"^[{(]?([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})[)}]?$"); +#if NET7_0_OR_GREATER + [GeneratedRegex("[0-9a-fA-F]{8}-[0-9a-fA-F]{8}-[0-9a-fA-F]{8}-[0-9a-fA-F]{8}")] + private static partial Regex UUIDRegex(); +#endif + + public static Match RegexSceneIdValidMatch(string sceneId) + { +#if NET7_0_OR_GREATER + Match match = UUIDRegex().Match(sceneid); +#else + Match match = new Regex(@"[0-9a-fA-F]{8}-[0-9a-fA-F]{8}-[0-9a-fA-F]{8}-[0-9a-fA-F]{8}").Match(sceneId); +#endif + return match; + } + public static string FixJsonValues(string json) { // Match GUID portion with 8-8-8-8 format (fix unquoted GUIDs) diff --git a/Servers/SSFWServer/Program.cs b/Servers/SSFWServer/Program.cs index 82e560cb4..bb6ead680 100644 --- a/Servers/SSFWServer/Program.cs +++ b/Servers/SSFWServer/Program.cs @@ -1,162 +1,17 @@ using CustomLogger; -using Newtonsoft.Json.Linq; using SSFWServer; using System.Reflection; using System.Runtime; -using System.Security.Cryptography; using Microsoft.Extensions.Logging; -using MultiServerLibrary.Extension; -using MultiServerLibrary.HTTP; -using MultiServerLibrary.SSL; using MultiServerLibrary; using MultiServerLibrary.SNMP; using SSFWServer.Helpers.FileHelper; -public static class SSFWServerConfiguration -{ - public static bool SSFWCrossSave { get; set; } = true; - public static bool EnableHTTPCompression { get; set; } = false; - public static int SSFWTTL { get; set; } = 180; - public static string SSFWMinibase { get; set; } = "[]"; - public static string SSFWLegacyKey { get; set; } = "**NoNoNoYouCantHaxThis****69"; - public static string SSFWSessionIdKey { get; set; } = StringUtils.GenerateRandomBase64KeyAsync().Result; - public static string SSFWLayoutsFolder { get; set; } = $"{Directory.GetCurrentDirectory()}/static/layouts"; - public static string SSFWStaticFolder { get; set; } = $"{Directory.GetCurrentDirectory()}/static/wwwssfwroot"; - public static string HTTPSCertificateFile { get; set; } = $"{Directory.GetCurrentDirectory()}/static/SSL/SSFW.pfx"; - public static string HTTPSCertificatePassword { get; set; } = "qwerty"; - public static HashAlgorithmName HTTPSCertificateHashingAlgorithm { get; set; } = HashAlgorithmName.SHA384; - public static string ScenelistFile { get; set; } = $"{Directory.GetCurrentDirectory()}/static/wwwssfwroot/SceneList.xml"; - public static string[]? HTTPSDNSList { get; set; } = { - "cprod.homerewards.online.scee.com", - "cprod.homeidentity.online.scee.com", - "cprod.homeserverservices.online.scee.com", - "cdev.homerewards.online.scee.com", - "cdev.homeidentity.online.scee.com", - "cdev.homeserverservices.online.scee.com", - "cdevb.homerewards.online.scee.com", - "cdevb.homeidentity.online.scee.com", - "cdevb.homeserverservices.online.scee.com", - "nonprod1.homerewards.online.scee.com", - "nonprod1.homeidentity.online.scee.com", - "nonprod1.homeserverservices.online.scee.com", - "nonprod2.homerewards.online.scee.com", - "nonprod2.homeidentity.online.scee.com", - "nonprod2.homeserverservices.online.scee.com", - "nonprod3.homerewards.online.scee.com", - "nonprod3.homeidentity.online.scee.com", - "nonprod3.homeserverservices.online.scee.com", - "nonprod4.homerewards.online.scee.com", - "nonprod4.homeidentity.online.scee.com", - "nonprod4.homeserverservices.online.scee.com", - }; - - // Sandbox Environments - public static List homeEnvs = new() - { - "cprod", "cprodts", "cpreprod", "cpreprodb", - "rc-qa", "rcdev", "rc-dev", "cqa-e", - "cqa-a", "cqa-j", "cqa-h", "cqab-e", - "cqab-a", "cqab-j", "cqab-h", "qcqa-e", - "qcqa-a", "qcqa-j", "qcqa-h", "qcpreprod", - "qcqab-e", "qcqab-a", "qcqab-j", "qcqab-h", - "qcpreprodb", "coredev", "core-dev", "core-qa", - "cdev", "cdev2", "cdev3", "cdeva", "cdevb", "cdevc", - "nonprod1", "nonprod2", "nonprod3", "prodsp" - }; - - /// - /// Tries to load the specified configuration file. - /// Throws an exception if it fails to find the file. - /// - /// - /// - public static void RefreshVariables(string configPath) - { - // Make sure the file exists - if (!File.Exists(configPath)) - { - LoggerAccessor.LogWarn($"Could not find the configuration file:{configPath}, writing and using server's default."); - - Directory.CreateDirectory(Path.GetDirectoryName(configPath) ?? Directory.GetCurrentDirectory() + "/static"); - - // Write the JObject to a file - File.WriteAllText(configPath, new JObject( - new JProperty("config_version", (ushort)2), - new JProperty("minibase", SSFWMinibase), - new JProperty("legacyKey", SSFWLegacyKey), - new JProperty("sessionidKey", SSFWSessionIdKey), - new JProperty("time_to_live", SSFWTTL), - new JProperty("cross_save", SSFWCrossSave), - new JProperty("enable_http_compression", EnableHTTPCompression), - new JProperty("static_folder", SSFWStaticFolder), - new JProperty("https_dns_list", HTTPSDNSList ?? Array.Empty()), - new JProperty("certificate_file", HTTPSCertificateFile), - new JProperty("certificate_password", HTTPSCertificatePassword), - new JProperty("certificate_hashing_algorithm", HTTPSCertificateHashingAlgorithm.Name), - new JProperty("scenelist_file", ScenelistFile) - ).ToString()); - - return; - } - - try - { - // Parse the JSON configuration - dynamic config = JObject.Parse(File.ReadAllText(configPath)); - - ushort config_version = GetValueOrDefault(config, "config_version", (ushort)0); - if (config_version >= 2) - EnableHTTPCompression = GetValueOrDefault(config, "enable_http_compression", EnableHTTPCompression); - SSFWMinibase = GetValueOrDefault(config, "minibase", SSFWMinibase); - SSFWTTL = GetValueOrDefault(config, "time_to_live", SSFWTTL); - SSFWLegacyKey = GetValueOrDefault(config, "legacyKey", SSFWLegacyKey); - SSFWSessionIdKey = GetValueOrDefault(config, "sessionidKey", SSFWSessionIdKey); - SSFWCrossSave = GetValueOrDefault(config, "cross_save", SSFWCrossSave); - SSFWStaticFolder = GetValueOrDefault(config, "static_folder", SSFWStaticFolder); - HTTPSCertificateFile = GetValueOrDefault(config, "certificate_file", HTTPSCertificateFile); - HTTPSCertificatePassword = GetValueOrDefault(config, "certificate_password", HTTPSCertificatePassword); - HTTPSCertificateHashingAlgorithm = new HashAlgorithmName(GetValueOrDefault(config, "certificate_hashing_algorithm", HTTPSCertificateHashingAlgorithm.Name)); - HTTPSDNSList = GetValueOrDefault(config, "https_dns_list", HTTPSDNSList); - ScenelistFile = GetValueOrDefault(config, "scenelist_file", ScenelistFile); - } - catch (Exception ex) - { - LoggerAccessor.LogWarn($"{configPath} file is malformed (exception: {ex}), using server's default."); - } - } - - // Helper method to get a value or default value if not present - private static T GetValueOrDefault(dynamic obj, string propertyName, T defaultValue) - { - try - { - if (obj != null) - { - if (obj is JObject jObject) - { - if (jObject.TryGetValue(propertyName, out JToken? value)) - { - T? returnvalue = value.ToObject(); - if (returnvalue != null) - return returnvalue; - } - } - } - } - catch (Exception ex) - { - LoggerAccessor.LogError($"[Program] - GetValueOrDefault thrown an exception: {ex}"); - } - - return defaultValue; - } -} - class Program { - private static string configDir = Directory.GetCurrentDirectory() + "/static/"; - private static string configPath = configDir + "SSFWServer.json"; - private static string configMultiServerLibraryPath = configDir + "MultiServerLibrary.json"; + private static readonly string configDir = Directory.GetCurrentDirectory() + "/static/"; + private static readonly string configPath = configDir + "SSFWServer.json"; + private static readonly string configMultiServerLibraryPath = configDir + "MultiServerLibrary.json"; private static SnmpTrapSender? trapSender = null; private static Timer? SceneListTimer; private static Timer? SessionTimer; diff --git a/Servers/SSFWServer/SSFWServerConfiguration.cs b/Servers/SSFWServer/SSFWServerConfiguration.cs new file mode 100644 index 000000000..13b3755b6 --- /dev/null +++ b/Servers/SSFWServer/SSFWServerConfiguration.cs @@ -0,0 +1,144 @@ +using CustomLogger; +using Newtonsoft.Json.Linq; +using System.Security.Cryptography; +using MultiServerLibrary.Extension; + +public static class SSFWServerConfiguration +{ + public static bool SSFWCrossSave { get; set; } = true; + public static bool EnableHTTPCompression { get; set; } = false; + public static int SSFWTTL { get; set; } = 180; + public static string SSFWMinibase { get; set; } = "[]"; + public static string SSFWLegacyKey { get; set; } = "**NoNoNoYouCantHaxThis****69"; + public static string SSFWSessionIdKey { get; set; } = StringUtils.GenerateRandomBase64KeyAsync().Result; + public static string SSFWLayoutsFolder { get; set; } = $"{Directory.GetCurrentDirectory()}/static/layouts"; + public static string SSFWStaticFolder { get; set; } = $"{Directory.GetCurrentDirectory()}/static/wwwssfwroot"; + public static string HTTPSCertificateFile { get; set; } = $"{Directory.GetCurrentDirectory()}/static/SSL/SSFW.pfx"; + public static string HTTPSCertificatePassword { get; set; } = "qwerty"; + public static HashAlgorithmName HTTPSCertificateHashingAlgorithm { get; set; } = HashAlgorithmName.SHA384; + public static string ScenelistFile { get; set; } = $"{Directory.GetCurrentDirectory()}/static/wwwssfwroot/SceneList.xml"; + public static string[]? HTTPSDNSList { get; set; } = { + "cprod.homerewards.online.scee.com", + "cprod.homeidentity.online.scee.com", + "cprod.homeserverservices.online.scee.com", + "cdev.homerewards.online.scee.com", + "cdev.homeidentity.online.scee.com", + "cdev.homeserverservices.online.scee.com", + "cdevb.homerewards.online.scee.com", + "cdevb.homeidentity.online.scee.com", + "cdevb.homeserverservices.online.scee.com", + "nonprod1.homerewards.online.scee.com", + "nonprod1.homeidentity.online.scee.com", + "nonprod1.homeserverservices.online.scee.com", + "nonprod2.homerewards.online.scee.com", + "nonprod2.homeidentity.online.scee.com", + "nonprod2.homeserverservices.online.scee.com", + "nonprod3.homerewards.online.scee.com", + "nonprod3.homeidentity.online.scee.com", + "nonprod3.homeserverservices.online.scee.com", + "nonprod4.homerewards.online.scee.com", + "nonprod4.homeidentity.online.scee.com", + "nonprod4.homeserverservices.online.scee.com", + }; + + // Sandbox Environments + public static List homeEnvs = new() + { + "cprod", "cprodts", "cpreprod", "cpreprodb", + "rc-qa", "rcdev", "rc-dev", "cqa-e", + "cqa-a", "cqa-j", "cqa-h", "cqab-e", + "cqab-a", "cqab-j", "cqab-h", "qcqa-e", + "qcqa-a", "qcqa-j", "qcqa-h", "qcpreprod", + "qcqab-e", "qcqab-a", "qcqab-j", "qcqab-h", + "qcpreprodb", "coredev", "core-dev", "core-qa", + "cdev", "cdev2", "cdev3", "cdeva", "cdevb", "cdevc", + "nonprod1", "nonprod2", "nonprod3", "prodsp" + }; + + /// + /// Tries to load the specified configuration file. + /// Throws an exception if it fails to find the file. + /// + /// + /// + public static void RefreshVariables(string configPath) + { + // Make sure the file exists + if (!File.Exists(configPath)) + { + LoggerAccessor.LogWarn($"Could not find the configuration file:{configPath}, writing and using server's default."); + + Directory.CreateDirectory(Path.GetDirectoryName(configPath) ?? Directory.GetCurrentDirectory() + "/static"); + + // Write the JObject to a file + File.WriteAllText(configPath, new JObject( + new JProperty("config_version", (ushort)2), + new JProperty("minibase", SSFWMinibase), + new JProperty("legacyKey", SSFWLegacyKey), + new JProperty("sessionidKey", SSFWSessionIdKey), + new JProperty("time_to_live", SSFWTTL), + new JProperty("cross_save", SSFWCrossSave), + new JProperty("enable_http_compression", EnableHTTPCompression), + new JProperty("static_folder", SSFWStaticFolder), + new JProperty("https_dns_list", HTTPSDNSList ?? Array.Empty()), + new JProperty("certificate_file", HTTPSCertificateFile), + new JProperty("certificate_password", HTTPSCertificatePassword), + new JProperty("certificate_hashing_algorithm", HTTPSCertificateHashingAlgorithm.Name), + new JProperty("scenelist_file", ScenelistFile) + ).ToString()); + + return; + } + + try + { + // Parse the JSON configuration + dynamic config = JObject.Parse(File.ReadAllText(configPath)); + + ushort config_version = GetValueOrDefault(config, "config_version", (ushort)0); + if (config_version >= 2) + EnableHTTPCompression = GetValueOrDefault(config, "enable_http_compression", EnableHTTPCompression); + SSFWMinibase = GetValueOrDefault(config, "minibase", SSFWMinibase); + SSFWTTL = GetValueOrDefault(config, "time_to_live", SSFWTTL); + SSFWLegacyKey = GetValueOrDefault(config, "legacyKey", SSFWLegacyKey); + SSFWSessionIdKey = GetValueOrDefault(config, "sessionidKey", SSFWSessionIdKey); + SSFWCrossSave = GetValueOrDefault(config, "cross_save", SSFWCrossSave); + SSFWStaticFolder = GetValueOrDefault(config, "static_folder", SSFWStaticFolder); + HTTPSCertificateFile = GetValueOrDefault(config, "certificate_file", HTTPSCertificateFile); + HTTPSCertificatePassword = GetValueOrDefault(config, "certificate_password", HTTPSCertificatePassword); + HTTPSCertificateHashingAlgorithm = new HashAlgorithmName(GetValueOrDefault(config, "certificate_hashing_algorithm", HTTPSCertificateHashingAlgorithm.Name)); + HTTPSDNSList = GetValueOrDefault(config, "https_dns_list", HTTPSDNSList); + ScenelistFile = GetValueOrDefault(config, "scenelist_file", ScenelistFile); + } + catch (Exception ex) + { + LoggerAccessor.LogWarn($"{configPath} file is malformed (exception: {ex}), using server's default."); + } + } + + // Helper method to get a value or default value if not present + private static T GetValueOrDefault(dynamic obj, string propertyName, T defaultValue) + { + try + { + if (obj != null) + { + if (obj is JObject jObject) + { + if (jObject.TryGetValue(propertyName, out JToken? value)) + { + T? returnvalue = value.ToObject(); + if (returnvalue != null) + return returnvalue; + } + } + } + } + catch (Exception ex) + { + LoggerAccessor.LogError($"[Program] - GetValueOrDefault thrown an exception: {ex}"); + } + + return defaultValue; + } +} diff --git a/Servers/SSFWServer/Services/IdentityService.cs b/Servers/SSFWServer/Services/IdentityService.cs index 0b0731896..fc3f54c10 100644 --- a/Servers/SSFWServer/Services/IdentityService.cs +++ b/Servers/SSFWServer/Services/IdentityService.cs @@ -191,7 +191,7 @@ public IdentityService(string XHomeClientVersion, string generalsecret, string h } else { - string scenename = scenemap.FirstOrDefault(x => x.Value == Misc.ExtractPortion(kvp.Key, 13, 18)).Key; + string scenename = scenemap.FirstOrDefault(x => x.Value == Program.ExtractPortion(kvp.Key, 13, 18)).Key; if (!string.IsNullOrEmpty(scenename)) { if (File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/{kvp.Key}.json")) // SceneID now mapped, so SceneID based file has become obsolete. diff --git a/Servers/SSFWServer/Services/LayoutService.cs b/Servers/SSFWServer/Services/LayoutService.cs index be21d56d0..348b25fed 100644 --- a/Servers/SSFWServer/Services/LayoutService.cs +++ b/Servers/SSFWServer/Services/LayoutService.cs @@ -1,9 +1,8 @@ using CustomLogger; using Newtonsoft.Json.Linq; using SSFWServer.Helpers.FileHelper; +using SSFWServer.Helpers.RegexHelper; using System.Text; -using System.Text.RegularExpressions; - namespace SSFWServer.Services { @@ -71,9 +70,7 @@ public bool HandleLayoutServicePOST(byte[] buffer, string directorypath, string { File.WriteAllText(directorypath + "/HarborStudio.json", Encoding.UTF8.GetString(buffer)); handled = true; - } - else - { + } else { string scenename = scenemap.FirstOrDefault(x => x.Value == Program.ExtractPortion(sceneid, 13, 18)).Key; if (!string.IsNullOrEmpty(scenename)) { @@ -106,13 +103,7 @@ public bool HandleLayoutServicePOST(byte[] buffer, string directorypath, string if (words.Length > 0) sceneid = words[^1]; -#if NET7_0_OR_GREATER - Match match = UUIDRegex().Match(sceneid); -#else - Match match = new Regex(@"[0-9a-fA-F]{8}-[0-9a-fA-F]{8}-[0-9a-fA-F]{8}-[0-9a-fA-F]{8}").Match(sceneid); -#endif - - if (match.Success) // If is UUID Ok. + if (GUIDValidator.RegexSceneIdValidMatch(sceneid).Success) // If is UUID Ok. { if (File.Exists(SSFWServerConfiguration.ScenelistFile)) { @@ -250,9 +241,5 @@ public Dictionary SSFWGetLegacyFurnitureLayouts(string filePath) return outputDictionary; } -#if NET7_0_OR_GREATER - [GeneratedRegex("[0-9a-fA-F]{8}-[0-9a-fA-F]{8}-[0-9a-fA-F]{8}-[0-9a-fA-F]{8}")] - private static partial Regex UUIDRegex(); -#endif } } diff --git a/Servers/SSFWServer/Services/RewardsService.cs b/Servers/SSFWServer/Services/RewardsService.cs index c5a719ef6..0102f04a7 100644 --- a/Servers/SSFWServer/Services/RewardsService.cs +++ b/Servers/SSFWServer/Services/RewardsService.cs @@ -10,7 +10,7 @@ namespace SSFWServer.Services { public class RewardsService { - private string? key; + private readonly string? key; public RewardsService(string? key) { @@ -28,7 +28,7 @@ public byte[] HandleRewardServicePOST(byte[] buffer, string directorypath, strin return buffer; } - public byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, string filepath, string absolutepath) + public static byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, string filepath, string absolutepath) { Directory.CreateDirectory(directorypath); @@ -37,10 +37,10 @@ public byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, st public byte[]? HandleRewardServiceInvCardTrackingDataDELETE(string directorypath, string filepath, string absolutepath, string userAgent, string sessionId) { - AdminObjectService adminObjectService = new AdminObjectService(sessionId, key); + AdminObjectService adminObjectService = new(sessionId, key); if (adminObjectService.IsAdminVerified(userAgent)) { - return RewardServiceInventory(null, directorypath, filepath, absolutepath, false, true); + return RewardServiceInventory(Array.Empty(), directorypath, filepath, absolutepath, false, true); } else { LoggerAccessor.LogError($"[SSFW] - HandleRewardServiceInvCardTrackingDataDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to delete Card Tracking data!"); return null; @@ -49,10 +49,10 @@ public byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, st public byte[]? HandleRewardServiceWipeInvDELETE(string directorypath, string filepath, string absolutepath, string userAgent, string sessionId) { - AdminObjectService adminObjectService = new AdminObjectService(sessionId, key); + AdminObjectService adminObjectService = new(sessionId, key); if(adminObjectService.IsAdminVerified(userAgent)) { - return RewardServiceInventory(null, directorypath, filepath, absolutepath, true, false); + return RewardServiceInventory(Array.Empty(), directorypath, filepath, absolutepath, true, false); } else { LoggerAccessor.LogError($"[SSFW] - HandleRewardServiceWipeInvDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to wipe inventory data!"); return null; @@ -68,7 +68,7 @@ public void HandleRewardServiceTrunksPOST(byte[] buffer, string directorypath, s TrunkServiceProcess(filepath.Replace("/setpartial", string.Empty) + ".json", Encoding.UTF8.GetString(buffer), env, userId); } - public void HandleRewardServiceTrunksEmergencyPOST(byte[] buffer, string directorypath, string absolutepath) + public static void HandleRewardServiceTrunksEmergencyPOST(byte[] buffer, string directorypath, string absolutepath) { Directory.CreateDirectory(directorypath); @@ -166,7 +166,7 @@ public void TrunkServiceProcess(string filePath, string request, string env, str JArray? mainArray = (JArray?)mainFile["objects"]; if (mainArray != null) { - Dictionary entriesToAddInMini = new Dictionary(); + Dictionary entriesToAddInMini = new(); foreach (JObject addObject in addArray) { @@ -216,7 +216,7 @@ public void TrunkServiceProcess(string filePath, string request, string env, str JArray? mainArray = (JArray?)mainFile["objects"]; if (mainArray != null) { - List entriesToRemoveInMini = new List(); + List entriesToRemoveInMini = new(); foreach (JObject deleteObj in deleteArray) { @@ -257,7 +257,7 @@ public void TrunkServiceProcess(string filePath, string request, string env, str } } - public byte[] RewardServiceInventory(byte[] buffer, string directorypath, string filepath, string absolutePath, bool deleteInv, bool deleteOnlyTracking) + public static byte[] RewardServiceInventory(byte[] buffer, string directorypath, string filepath, string absolutePath, bool deleteInv, bool deleteOnlyTracking) { //Tracking Inventory GUID const string trackingGuid = "00000000-00000000-00000000-00000001"; // fallback/hardcoded tracking GUID @@ -269,8 +269,8 @@ public byte[] RewardServiceInventory(byte[] buffer, string directorypath, string string trackingFileDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}/object"; string trackingFile = $"{trackingFileDir}/{trackingGuid}.json"; - Directory.CreateDirectory(Path.GetDirectoryName(countsStoreDir)); - Directory.CreateDirectory(Path.GetDirectoryName(trackingFileDir)); + Directory.CreateDirectory(Path.GetDirectoryName(countsStoreDir) ?? countsStoreDir); + Directory.CreateDirectory(Path.GetDirectoryName(trackingFileDir) ?? trackingFileDir); //Parse Buffer string fixedJsonPayload = GUIDValidator.FixJsonValues(Encoding.UTF8.GetString(buffer)); @@ -349,15 +349,17 @@ public byte[] RewardServiceInventory(byte[] buffer, string directorypath, string } string? objectId = objectIdElement.GetString(); - - // Update counts - if (counts.ContainsKey(objectId)) - { - counts[objectId]++; - } - else + if (!string.IsNullOrEmpty(objectId)) { - counts[objectId] = 1; + // Update counts + if (counts.ContainsKey(objectId)) + { + counts[objectId]++; + } + else + { + counts[objectId] = 1; + } } // Check if this is a tracking object (has metadata or matches tracking GUID) @@ -394,14 +396,17 @@ public byte[] RewardServiceInventory(byte[] buffer, string directorypath, string } } - trackingRewards[objectId] = metadata; + if (!string.IsNullOrEmpty(objectId)) + { + trackingRewards[objectId] = metadata; + } - // Write tracking data + // Write tracking data var trackingData = new Dictionary - { - { "result", 0 }, - { "rewards", trackingRewards } - }; + { + { "result", 0 }, + { "rewards", trackingRewards } + }; string trackingJson = System.Text.Json.JsonSerializer.Serialize(trackingData, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(trackingFile, trackingJson); #if DEBUG @@ -511,7 +516,7 @@ private void ProcessTrunkObjectUpdate(string trunkFilePath, Dictionary entries, int startIndex) + private static string BuildAddSetPartialJson(Dictionary entries, int startIndex) { // Create the object to build the JSON structure var jsonObject = new @@ -540,7 +545,7 @@ private string BuildAddSetPartialJson(Dictionary entries, int star return JsonConvert.SerializeObject(jsonObject); } - private string BuildDeleteSetPartialJson(Dictionary entries, Dictionary indexToItem) + private static string BuildDeleteSetPartialJson(Dictionary entries, Dictionary indexToItem) { // Create the object to build the JSON structure var jsonObject = new diff --git a/Servers/SSFWServer/Services/TradingService.cs b/Servers/SSFWServer/Services/TradingService.cs index 7d3ac6eda..3c9079baf 100644 --- a/Servers/SSFWServer/Services/TradingService.cs +++ b/Servers/SSFWServer/Services/TradingService.cs @@ -7,9 +7,9 @@ namespace SSFWServer.Services { public class TradingService { - private string? sessionid; - private string? env; - private string? key; + private readonly string? sessionid; + private readonly string? env; + private readonly string? key; public TradingService(string sessionid, string env, string? key) { @@ -20,30 +20,30 @@ public TradingService(string sessionid, string env, string? key) public class BaseResponse { - [JsonProperty("result")] - public int result { get; set; } - [JsonProperty("id")] + [JsonProperty(nameof(result))] + public int result { get; set; } = -1; + [JsonProperty(nameof(id))] public int id { get; set; } = -1; - [JsonProperty("message")] + [JsonProperty(nameof(message))] public string message { get; set; } = string.Empty; } public class RootObject { - [JsonProperty("members")] + [JsonProperty(nameof(members))] public List? members { get; set; } } private static List tradeTransactions = new(); public class TradeTransactionResponse : BaseResponse { - [JsonProperty("ownerId")] + [JsonProperty(nameof(ownerId))] public string ownerId { get; set; } = string.Empty; - [JsonProperty("joinerId")] + [JsonProperty(nameof(joinerId))] public string joinerId { get; set; } = string.Empty; - [JsonProperty("transactionId")] - public int transId { get; set; } = 0; - [JsonProperty("sequence")] + [JsonProperty(nameof(transactionId))] + public int transactionId { get; set; } = 0; + [JsonProperty(nameof(sequence))] public long sequence { get; set; } = 0; public int tradeAmount { get; set; } = 0; @@ -63,10 +63,10 @@ public enum Status : int } - public string HandleTradingService(HttpRequest req, string sessionid, string absolutepath) + public static string HandleTradingService(HttpRequest req, string sessionid, string absolutepath) { BaseResponse tradeResponse = new(); - TradeTransactionResponse newTradeTransactionResponse = new TradeTransactionResponse(); + TradeTransactionResponse newTradeTransactionResponse = new(); int existingCardTradingTransactionId = 0; int sequenceNum = 0; @@ -76,7 +76,7 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs if (string.IsNullOrEmpty(currentUserId)) return JsonConvert.SerializeObject(tradeResponse); #if DEBUG - LoggerAccessor.LogInfo(absoPathArray.Count()); + LoggerAccessor.LogInfo(absoPathArray.Length); #endif //if this is a existing Trade Transaction, assign! if (absoPathArray.Length > 3) @@ -99,9 +99,9 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs { //ADDTRADEITEMS //If a existing transaction was created, update it here! - if (transaction.transId == existingCardTradingTransactionId) + if (transaction.transactionId == existingCardTradingTransactionId) { - transaction.transId = existingCardTradingTransactionId; + transaction.transactionId = existingCardTradingTransactionId; transaction.sequence = sequenceNum; //Set to 0 initially till set later try @@ -111,7 +111,7 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs if (reqitemList == null || reqitemList.Count == 0) { - LoggerAccessor.LogInfo($"[SSFW] TradingService - Existing transaction {transaction.transId} failed to update, request contained no Items to add!"); + LoggerAccessor.LogInfo($"[SSFW] TradingService - Existing transaction {transaction.transactionId} failed to update, request contained no Items to add!"); return JsonConvert.SerializeObject(tradeResponse); } @@ -123,7 +123,7 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs transaction.tradeRequesterItemList = reqitemList; } - LoggerAccessor.LogInfo($"[SSFW] TradingService - Existing transaction {transaction.transId} has been updated"); + LoggerAccessor.LogInfo($"[SSFW] TradingService - Existing transaction {transaction.transactionId} has been updated"); tradeTransactions.Add(transaction); tradeResponse.result = 0; @@ -142,7 +142,13 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs } else // otherwise create new transaction! { RootObject? result = JsonConvert.DeserializeObject(req.Body); - string memberValue = result.members[0]; + string memberValue = string.Empty; + if (result != null && result.members != null) + { + memberValue = result.members[0]; + } else { + return JsonConvert.SerializeObject(tradeResponse); + } int index = 1; foreach (var transaction in tradeTransactions) @@ -151,7 +157,7 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs newTradeTransactionResponse.joinerId = memberValue; //If a existing transaction was created, update it here! - if (transaction.transId == existingCardTradingTransactionId) + if (transaction.transactionId == existingCardTradingTransactionId) { //index = transaction.transId + 1; @@ -159,9 +165,9 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs //tradeTransactions.Add(newTradeRequest); } //Initial first Transaction starts at index 1 - else if (tradeTransactions.Count() == 0) + else if (tradeTransactions.Count == 0) { - newTradeTransactionResponse.transId = index; + newTradeTransactionResponse.transactionId = index; newTradeTransactionResponse.status = Status.Active; tradeTransactions.Add(newTradeTransactionResponse); @@ -179,11 +185,11 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs //Return current status of transaction if (req.Url.Contains("status")) { - var existingTrade = tradeTransactions.FirstOrDefault(x => x.transId == existingCardTradingTransactionId); + var existingTrade = tradeTransactions.FirstOrDefault(x => x.transactionId == existingCardTradingTransactionId); if (existingTrade != null) { - LoggerAccessor.LogInfo($"[SSFW] TradingService - Checking current status of transactionId {existingTrade.transId} between Requester {existingTrade.ownerId} & Partner {existingTrade.joinerId}: {existingTrade.status}"); + LoggerAccessor.LogInfo($"[SSFW] TradingService - Checking current status of transactionId {existingTrade.transactionId} between Requester {existingTrade.ownerId} & Partner {existingTrade.joinerId}: {existingTrade.status}"); //RootObject? result = JsonConvert.DeserializeObject(CreateTransactionBody); @@ -223,7 +229,7 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs { //ADDTRADEITEMS //If a existing transaction was created, update it here! - if (transaction.transId == existingCardTradingTransactionId) + if (transaction.transactionId == existingCardTradingTransactionId) { return $@"{{ ""result"": 0, @@ -246,16 +252,24 @@ public string HandleTradingService(HttpRequest req, string sessionid, string abs #region Cancel Trade Transaction else if (req.Method == "DELETE") { - if (tradeTransactions.Count() > 0) + if (tradeTransactions.Count > 0) { try { - TradeTransactionResponse? tradeRemovalResp = tradeTransactions.FirstOrDefault(x => x.transId == existingCardTradingTransactionId) ?? null; - tradeTransactions.Remove(tradeRemovalResp); - LoggerAccessor.LogError($"[SSFW] TradingService - Successfully cancelled existing trade transaction id {existingCardTradingTransactionId}"); + TradeTransactionResponse? tradeRemovalResp = tradeTransactions.FirstOrDefault(x => x.transactionId == existingCardTradingTransactionId) ?? null; + if (tradeRemovalResp != null) + { + tradeTransactions.Remove(tradeRemovalResp); + tradeResponse.result = 0; + + LoggerAccessor.LogError($"[SSFW] TradingService - Successfully cancelled existing trade transaction id {existingCardTradingTransactionId}"); + return JsonConvert.SerializeObject(tradeResponse); + } + else + { + LoggerAccessor.LogError($"[SSFW] TradingService - Unable to determine existing trade transaction to delete!"); + } - tradeResponse.result = 0; - return JsonConvert.SerializeObject(tradeResponse); } catch (Exception e) { LoggerAccessor.LogError($"[SSFW] TradingService - Exception caught attempting to remove existing trade transaction id {existingCardTradingTransactionId} with error {e}"); From e0b3c8a87bc5f33fd3dc79a1be47672331b509f3 Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Thu, 1 Jan 2026 13:53:42 -0600 Subject: [PATCH 07/13] Compile Fix #2 - Fix SSFWProcessor compile error on static instanced methods. - Added DEBUG AchievementService GET endpoint. --- Servers/SSFWServer/SSFWProcessor.cs | 13 +++++++++ .../SSFWServer/Services/AchievementService.cs | 28 +++++++++++++++++++ Servers/SSFWServer/Services/RewardsService.cs | 4 +-- Servers/SSFWServer/Services/TradingService.cs | 2 +- 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 Servers/SSFWServer/Services/AchievementService.cs diff --git a/Servers/SSFWServer/SSFWProcessor.cs b/Servers/SSFWServer/SSFWProcessor.cs index 31566a9ee..9b3f56232 100644 --- a/Servers/SSFWServer/SSFWProcessor.cs +++ b/Servers/SSFWServer/SSFWProcessor.cs @@ -178,6 +178,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse env = "cprod"; // Instantiate services + AchievementService achievementService = new(sessionid, env, _legacykey); AuditService auditService = new(sessionid, env, _legacykey); AvatarService avatarService = new(); FriendsService friendsService = new(sessionid, env, _legacykey); @@ -263,6 +264,18 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } #endregion + #region DEBUG AchievementService + else if (absolutepath.Contains($"/AchievementService/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}")) + { + var res = achievementService.HandleAchievementService(absolutepath); + if (!string.IsNullOrEmpty(res)) + Response.MakeGetResponse(res, "application/json"); + else + Response.MakeErrorResponse(404, "Not Found"); + break; + } + #endregion + #region DEBUG AuditService if (absolutepath.Contains($"/AuditService/log/{env}/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}/counts") || absolutepath.Contains($"/AuditService/log/{env}/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}/object") diff --git a/Servers/SSFWServer/Services/AchievementService.cs b/Servers/SSFWServer/Services/AchievementService.cs new file mode 100644 index 000000000..89a18e0e0 --- /dev/null +++ b/Servers/SSFWServer/Services/AchievementService.cs @@ -0,0 +1,28 @@ +using CustomLogger; + +namespace SSFWServer.Services +{ + public class AchievementService + { + private string? sessionid; + private string? env; + private string? key; + + public AchievementService(string sessionid, string env, string? key) + { + this.sessionid = sessionid; + this.env = env; + this.key = key; + } + + public string HandleAchievementService(string absolutePath) + { + string? userName = SSFWUserSessionManager.GetUsernameBySessionId(sessionid); +#if DEBUG + LoggerAccessor.LogInfo($"[SSFW] AchievementService - Requesting {userName}'s achievements"); +#endif + //We send empty response as status 200 for now + return $"{{}}"; + } + } +} diff --git a/Servers/SSFWServer/Services/RewardsService.cs b/Servers/SSFWServer/Services/RewardsService.cs index 0102f04a7..e77d43216 100644 --- a/Servers/SSFWServer/Services/RewardsService.cs +++ b/Servers/SSFWServer/Services/RewardsService.cs @@ -28,7 +28,7 @@ public byte[] HandleRewardServicePOST(byte[] buffer, string directorypath, strin return buffer; } - public static byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, string filepath, string absolutepath) + public byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, string filepath, string absolutepath) { Directory.CreateDirectory(directorypath); @@ -68,7 +68,7 @@ public void HandleRewardServiceTrunksPOST(byte[] buffer, string directorypath, s TrunkServiceProcess(filepath.Replace("/setpartial", string.Empty) + ".json", Encoding.UTF8.GetString(buffer), env, userId); } - public static void HandleRewardServiceTrunksEmergencyPOST(byte[] buffer, string directorypath, string absolutepath) + public void HandleRewardServiceTrunksEmergencyPOST(byte[] buffer, string directorypath, string absolutepath) { Directory.CreateDirectory(directorypath); diff --git a/Servers/SSFWServer/Services/TradingService.cs b/Servers/SSFWServer/Services/TradingService.cs index 3c9079baf..7ea613da1 100644 --- a/Servers/SSFWServer/Services/TradingService.cs +++ b/Servers/SSFWServer/Services/TradingService.cs @@ -63,7 +63,7 @@ public enum Status : int } - public static string HandleTradingService(HttpRequest req, string sessionid, string absolutepath) + public string HandleTradingService(HttpRequest req, string sessionid, string absolutepath) { BaseResponse tradeResponse = new(); TradeTransactionResponse newTradeTransactionResponse = new(); From ddd0bf33350fae6e2ba9456f84c41359fc78fc51 Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Thu, 1 Jan 2026 17:57:18 -0600 Subject: [PATCH 08/13] Config issue hotfix - Add missing entries to generate the SSFWLayoutsFolder in the serverconfig - Updated WebService SessionId header to match Home Client for consistency. --- Servers/SSFWServer/SSFWProcessor.cs | 24 +++++++++++++------ Servers/SSFWServer/SSFWServerConfiguration.cs | 6 +++-- Servers/SSFWServer/Services/FriendsService.cs | 10 ++++---- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Servers/SSFWServer/SSFWProcessor.cs b/Servers/SSFWServer/SSFWProcessor.cs index 9b3f56232..3278e80cd 100644 --- a/Servers/SSFWServer/SSFWProcessor.cs +++ b/Servers/SSFWServer/SSFWProcessor.cs @@ -13,6 +13,7 @@ using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.RegularExpressions; namespace SSFWServer @@ -551,6 +552,14 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } #endregion + #region FriendsService + else if (absolutepath.Contains($"/identity/person/{sessionid}/data/psn/friends-list") && IsSSFWRegistered(sessionid)) + { + var res = friendsService.HandleFriendsService(absolutepath, postbuffer); + Response.MakeOkResponse(); + } + #endregion + else { LoggerAccessor.LogWarn($"[SSFWProcessor] : Host requested a POST method I don't know about! - Report it to GITHUB with the request : {absolutepath}"); @@ -754,6 +763,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse break; } } + #region SoundShapes else if (UserAgent.Contains("PS3Application")) { @@ -891,7 +901,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } break; case "/WebService/ApplyLayoutOverride/": - sessionId = GetHeaderValue(Headers, "sessionid", false); + sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); string targetUserName = GetHeaderValue(Headers, "targetUserName", false); string sceneId = GetHeaderValue(Headers, "sceneId", false); env = GetHeaderValue(Headers, "env", false); @@ -988,7 +998,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } break; case "/WebService/R3moveLayoutOverride/": - sessionId = GetHeaderValue(Headers, "sessionid", false); + sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); Response.Clear(); @@ -1012,7 +1022,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } break; case "/WebService/GetMini/": - sessionId = GetHeaderValue(Headers, "sessionid", false); + sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); env = GetHeaderValue(Headers, "env", false); if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) @@ -1056,7 +1066,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse break; case "/WebService/AddMiniItem/": uuid = GetHeaderValue(Headers, "uuid", false); - sessionId = GetHeaderValue(Headers, "sessionid", false); + sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); env = GetHeaderValue(Headers, "env", false); if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) @@ -1102,7 +1112,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse break; case "/WebService/AddMiniItems/": uuids = GetHeaderValue(Headers, "uuids", false).Split(','); - sessionId = GetHeaderValue(Headers, "sessionid", false); + sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); env = GetHeaderValue(Headers, "env", false); if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) @@ -1155,7 +1165,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse break; case "/WebService/RemoveMiniItem/": uuid = GetHeaderValue(Headers, "uuid", false); - sessionId = GetHeaderValue(Headers, "sessionid", false); + sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); env = GetHeaderValue(Headers, "env", false); if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) @@ -1201,7 +1211,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse break; case "/WebService/RemoveMiniItems/": uuids = GetHeaderValue(Headers, "uuids", false).Split(','); - sessionId = GetHeaderValue(Headers, "sessionid", false); + sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); env = GetHeaderValue(Headers, "env", false); if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) diff --git a/Servers/SSFWServer/SSFWServerConfiguration.cs b/Servers/SSFWServer/SSFWServerConfiguration.cs index 13b3755b6..674397ecf 100644 --- a/Servers/SSFWServer/SSFWServerConfiguration.cs +++ b/Servers/SSFWServer/SSFWServerConfiguration.cs @@ -72,7 +72,7 @@ public static void RefreshVariables(string configPath) // Write the JObject to a file File.WriteAllText(configPath, new JObject( - new JProperty("config_version", (ushort)2), + new JProperty("config_version", (ushort)3), new JProperty("minibase", SSFWMinibase), new JProperty("legacyKey", SSFWLegacyKey), new JProperty("sessionidKey", SSFWSessionIdKey), @@ -84,7 +84,8 @@ public static void RefreshVariables(string configPath) new JProperty("certificate_file", HTTPSCertificateFile), new JProperty("certificate_password", HTTPSCertificatePassword), new JProperty("certificate_hashing_algorithm", HTTPSCertificateHashingAlgorithm.Name), - new JProperty("scenelist_file", ScenelistFile) + new JProperty("scenelist_file", ScenelistFile), + new JProperty("layouts_folder", SSFWLayoutsFolder) ).ToString()); return; @@ -109,6 +110,7 @@ public static void RefreshVariables(string configPath) HTTPSCertificateHashingAlgorithm = new HashAlgorithmName(GetValueOrDefault(config, "certificate_hashing_algorithm", HTTPSCertificateHashingAlgorithm.Name)); HTTPSDNSList = GetValueOrDefault(config, "https_dns_list", HTTPSDNSList); ScenelistFile = GetValueOrDefault(config, "scenelist_file", ScenelistFile); + SSFWLayoutsFolder = GetValueOrDefault(config, "layouts_folder", SSFWLayoutsFolder); } catch (Exception ex) { diff --git a/Servers/SSFWServer/Services/FriendsService.cs b/Servers/SSFWServer/Services/FriendsService.cs index a82c7e3e0..72fabd698 100644 --- a/Servers/SSFWServer/Services/FriendsService.cs +++ b/Servers/SSFWServer/Services/FriendsService.cs @@ -18,14 +18,14 @@ public FriendsService(string sessionid, string env, string? key) public string HandleFriendsService(string absolutepath, byte[] buffer) { - string? userName = SSFWUserSessionManager.GetUsernameBySessionId(sessionid); - string auditLogPath = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutepath}/{env}"; + string? userName = SSFWUserSessionManager.GetIdBySessionId(sessionid); + string friendsStorePath = $"{SSFWServerConfiguration.SSFWStaticFolder}/FriendsService/{env}"; try { - Directory.CreateDirectory(auditLogPath); + Directory.CreateDirectory(friendsStorePath); - File.WriteAllText($"{auditLogPath}/{userName}.txt", Encoding.UTF8.GetString(buffer)); - LoggerAccessor.LogInfo($"[SSFW] FriendsService - HandleFriendsService Friends list posted: {userName}"); + File.WriteAllText($"{friendsStorePath}/{userName}.txt", Encoding.UTF8.GetString(buffer)); + LoggerAccessor.LogInfo($"[SSFW] FriendsService - HandleFriendsService Friends list posted: {userName} at {$"{friendsStorePath}/{userName}.txt"}"); return "Success"; } catch (Exception ex) From c9e4a00359b4f4088b55756b617d9b33478d44ee Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Sun, 4 Jan 2026 11:33:51 -0600 Subject: [PATCH 09/13] Addresses Review Comments - Adjusts some Log comments to be enabled only on DEBUG compile - Removed identityService login dispose - Some new() code simplification - Added ReadOnly ConcurrentBag declaration for homeEnvs - Added a version check function filter for specific filtering if a player is on two different home clients at same time with SSFW.. If so this helps somewhat mitigate that collision. --- .../Helpers/FileHelper/ScenelistParser.cs | 2 +- .../Helpers/RegexHelper/GUIDValidator.cs | 3 +- Servers/SSFWServer/SSFWProcessor.cs | 227 +++++++++--------- Servers/SSFWServer/SSFWServerConfiguration.cs | 25 +- Servers/SSFWServer/SSFWUserSessionManager.cs | 15 +- Servers/SSFWServer/Services/AuditService.cs | 17 +- .../SSFWServer/Services/IdentityService.cs | 36 +-- .../SSFWServer/Services/KeepAliveService.cs | 30 +-- Servers/SSFWServer/Services/RewardsService.cs | 24 +- .../SSFWServer/Services/SaveDataService.cs | 7 +- 10 files changed, 173 insertions(+), 213 deletions(-) diff --git a/Servers/SSFWServer/Helpers/FileHelper/ScenelistParser.cs b/Servers/SSFWServer/Helpers/FileHelper/ScenelistParser.cs index 68089b707..6ea05b95c 100644 --- a/Servers/SSFWServer/Helpers/FileHelper/ScenelistParser.cs +++ b/Servers/SSFWServer/Helpers/FileHelper/ScenelistParser.cs @@ -78,7 +78,7 @@ public static void UpdateSceneDictionary(object? state) } } } - catch (Exception) + catch { // Not Important } diff --git a/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs b/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs index 584da9b90..3cf11d14d 100644 --- a/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs +++ b/Servers/SSFWServer/Helpers/RegexHelper/GUIDValidator.cs @@ -6,7 +6,8 @@ namespace SSFWServer.Helpers.RegexHelper { public class GUIDValidator { - public static Regex RegexSessionValidator = new Regex(@"^[{(]?([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})[)}]?$"); + public static Regex RegexSessionValidator = new(@"^[{(]?([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})[)}]?$"); + public static Regex VersionFilter = new(@"\d{6}"); #if NET7_0_OR_GREATER [GeneratedRegex("[0-9a-fA-F]{8}-[0-9a-fA-F]{8}-[0-9a-fA-F]{8}-[0-9a-fA-F]{8}")] diff --git a/Servers/SSFWServer/SSFWProcessor.cs b/Servers/SSFWServer/SSFWProcessor.cs index 3278e80cd..3da7d8508 100644 --- a/Servers/SSFWServer/SSFWProcessor.cs +++ b/Servers/SSFWServer/SSFWProcessor.cs @@ -13,7 +13,6 @@ using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Text.Json; -using System.Text.Json.Nodes; using System.Text.RegularExpressions; namespace SSFWServer @@ -23,7 +22,7 @@ public class SSFWProcessor private const string LoginGUID = "bb88aea9-6bf8-4201-a6ff-5d1f8da0dd37"; // Defines a list of web-related file extensions - private static readonly HashSet allowedWebExtensions = new HashSet(StringComparer.InvariantCultureIgnoreCase) + private static readonly HashSet allowedWebExtensions = new(StringComparer.InvariantCultureIgnoreCase) { ".html", ".htm", ".cgi", ".css", ".js", ".svg", ".gif", ".ico", ".woff", ".woff2", ".ttf", ".eot" }; @@ -36,7 +35,7 @@ public class SSFWProcessor private readonly string _certpath; private readonly string _certpass; - public static string? _legacykey; + private static string? _legacykey; public SSFWProcessor(string certpath, string certpass, string? locallegacykey) { @@ -252,7 +251,6 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse #endregion #region PlayerLookup Service - //Doesn't pass in SessionId!! else if (absolutepath.Contains($"/{LoginGUID}/person/byDisplayName")) { @@ -279,10 +277,9 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse #region DEBUG AuditService if (absolutepath.Contains($"/AuditService/log/{env}/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}/counts") - || absolutepath.Contains($"/AuditService/log/{env}/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}/object") - && IsSSFWRegistered(sessionid)) + || absolutepath.Contains($"/AuditService/log/{env}/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}/object")) { - var res = auditService.HandleAuditService(absolutepath, new byte[] { }, request); + var res = auditService.HandleAuditService(absolutepath, Array.Empty(), request); if (!string.IsNullOrEmpty(res)) Response.MakeGetResponse(res, "application/json"); @@ -292,58 +289,56 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } #endregion - else if (IsSSFWRegistered(sessionid)) + #region RewardService Inventory System + //First check if this is a Inventory request + if (absolutepath.Contains($"/RewardsService/") && absolutepath.Contains("counts")) { - #region RewardService Inventory System - //First check if this is a Inventory request - if (absolutepath.Contains($"/RewardsService/") && absolutepath.Contains("counts")) + //Detect if existing inv exists + if (File.Exists(filePath + ".json")) { - //Detect if existing inv exists - if (File.Exists(filePath + ".json")) - { - string? res = FileHelper.ReadAllText(filePath + ".json", _legacykey); + string? res = FileHelper.ReadAllText(filePath + ".json", _legacykey); - if (!string.IsNullOrEmpty(res)) - { - if (GetHeaderValue(Headers, "Accept") == "application/json") - Response.MakeGetResponse(res, "application/json"); - else - Response.MakeGetResponse(res); - } + if (!string.IsNullOrEmpty(res)) + { + if (GetHeaderValue(Headers, "Accept") == "application/json") + Response.MakeGetResponse(res, "application/json"); else - Response.MakeErrorResponse(); + Response.MakeGetResponse(res); } - else //fallback default - Response.MakeGetResponse(@"{ ""00000000-00000000-00000000-00000001"": 1 } ", "application/json"); - break; + else + Response.MakeErrorResponse(); } - //Check for specifically the Tracking GUID - else if (absolutepath.Contains($"/RewardsService/") && absolutepath.Contains("object/00000000-00000000-00000000-00000001")) + else //fallback default + Response.MakeGetResponse(@"{ ""00000000-00000000-00000000-00000001"": 1 } ", "application/json"); + break; + } + //Check for specifically the Tracking GUID + else if (absolutepath.Contains($"/RewardsService/") && absolutepath.Contains("object/00000000-00000000-00000000-00000001")) + { + //Detect if existing inv exists + if (File.Exists(filePath + ".json")) { - //Detect if existing inv exists - if (File.Exists(filePath + ".json")) - { - string? res = FileHelper.ReadAllText(filePath + ".json", _legacykey); + string? res = FileHelper.ReadAllText(filePath + ".json", _legacykey); - if (!string.IsNullOrEmpty(res)) - { - if (GetHeaderValue(Headers, "Accept") == "application/json") - Response.MakeGetResponse(res, "application/json"); - else - Response.MakeGetResponse(res); - } + if (!string.IsNullOrEmpty(res)) + { + if (GetHeaderValue(Headers, "Accept") == "application/json") + Response.MakeGetResponse(res, "application/json"); else - Response.MakeErrorResponse(); + Response.MakeGetResponse(res); } - else //fallback default - { + else + Response.MakeErrorResponse(); + } + else //fallback default + { #if DEBUG - LoggerAccessor.LogWarn($"[SSFWProcessor] : {UserAgent} Non-existent inventories detected, using defaults!"); + LoggerAccessor.LogWarn($"[SSFWProcessor] : {UserAgent} Non-existent inventories detected, using defaults!"); #endif - if (absolutepath.Contains("p4t-cprod")) - { - #region Quest for Greatness - Response.MakeGetResponse(@"{ + if (absolutepath.Contains("p4t-cprod")) + { + #region Quest for Greatness + Response.MakeGetResponse(@"{ ""result"": 0, ""rewards"": { ""00000000-00000000-00000000-00000001"": { @@ -352,12 +347,12 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } } }", "application/json"); - #endregion - } - else - { - #region Pottermore - Response.MakeGetResponse(@"{ + #endregion + } + else + { + #region Pottermore + Response.MakeGetResponse(@"{ ""result"": 0, ""rewards"": [ { @@ -368,67 +363,65 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } ] }", "application/json"); - #endregion - } - break; + #endregion } + break; } - #endregion + } + #endregion - #region ClanService - else if (absolutepath.Contains($"/ClanService/{env}/clan/")) - clanService.HandleClanDetailsService(request, Response, absolutepath); - #endregion + #region ClanService + else if (absolutepath.Contains($"/ClanService/{env}/clan/")) + clanService.HandleClanDetailsService(request, Response, absolutepath); + #endregion - #region File return JSON, bin, jpeg - else if (File.Exists(filePath + ".json")) - { - string? res = FileHelper.ReadAllText(filePath + ".json", _legacykey); + #region File return JSON, bin, jpeg + else if (File.Exists(filePath + ".json")) + { + string? res = FileHelper.ReadAllText(filePath + ".json", _legacykey); - if (!string.IsNullOrEmpty(res)) - { - if (GetHeaderValue(Headers, "Accept") == "application/json") - Response.MakeGetResponse(res, "application/json"); - else - Response.MakeGetResponse(res); - } - else - Response.MakeErrorResponse(); - } - else if (File.Exists(filePath + ".bin")) + if (!string.IsNullOrEmpty(res)) { - byte[]? res = FileHelper.ReadAllBytes(filePath + ".bin", _legacykey); - - if (res != null) - Response.MakeGetResponse(res, "application/octet-stream"); + if (GetHeaderValue(Headers, "Accept") == "application/json") + Response.MakeGetResponse(res, "application/json"); else - Response.MakeErrorResponse(); + Response.MakeGetResponse(res); } - else if (File.Exists(filePath + ".jpeg")) - { - byte[]? res = FileHelper.ReadAllBytes(filePath + ".jpeg", _legacykey); + else + Response.MakeErrorResponse(); + } + else if (File.Exists(filePath + ".bin")) + { + byte[]? res = FileHelper.ReadAllBytes(filePath + ".bin", _legacykey); - if (res != null) - Response.MakeGetResponse(res, "image/jpeg"); - else - Response.MakeErrorResponse(); - } + if (res != null) + Response.MakeGetResponse(res, "application/octet-stream"); else - { - LoggerAccessor.LogWarn($"[SSFWProcessor] : {UserAgent} Requested a non-existent file - {filePath}"); - Response.Clear(); - Response.SetBegin((int)HttpStatusCode.NotFound); - Response.SetBody(); - } - #endregion + Response.MakeErrorResponse(); } + else if (File.Exists(filePath + ".jpeg")) + { + byte[]? res = FileHelper.ReadAllBytes(filePath + ".jpeg", _legacykey); + + if (res != null) + Response.MakeGetResponse(res, "image/jpeg"); + else + Response.MakeErrorResponse(); + } + else + { + LoggerAccessor.LogWarn($"[SSFWProcessor] : {UserAgent} Requested a non-existent file - {filePath}"); + Response.Clear(); + Response.SetBegin((int)HttpStatusCode.NotFound); + Response.SetBody(); + } + #endregion } #region SaveData AvatarService else if (absolutepath.Contains($"/SaveDataService/avatar/{env}/") && absolutepath.EndsWith(".jpg")) { byte[]? res = avatarService.HandleAvatarService(absolutepath, filePath, _legacykey); - if (res != null) Response.MakeGetResponse(res, "image/jpg"); else @@ -467,7 +460,6 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } else Response.MakeErrorResponse(); - login.Dispose(); } else { @@ -481,8 +473,9 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse #region PING KeepAlive Service else if (absolutepath.Contains("/morelife") && !string.IsNullOrEmpty(GetHeaderValue(Headers, "x-signature"))) { - if (keepAliveService.UpdateKeepAliveForClient(absolutepath, Response)) - Response.MakeGetResponse("{}", "application/json"); // This doesn't even need a empty array, simply 200 Status is enough. + if (KeepAliveService.UpdateKeepAliveForClient(absolutepath)) + Response.MakeOkResponse(); // This doesn't even need a empty array, simply 200 Status is enough. + //Response.MakeGetResponse("{}", "application/json"); else Response.MakeErrorResponse(403); } @@ -540,7 +533,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse #endregion #region TradingService - else if (absolutepath.Contains($"/TradingService/pmcards/trade") && IsSSFWRegistered(sessionid)) + else if (absolutepath.Contains($"/TradingService/pmcards/trade")) { string? res = tradingService.HandleTradingService(request, sessionid, absolutepath); @@ -553,7 +546,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse #endregion #region FriendsService - else if (absolutepath.Contains($"/identity/person/{sessionid}/data/psn/friends-list") && IsSSFWRegistered(sessionid)) + else if (absolutepath.Contains($"/identity/person/{sessionid}/data/psn/friends-list")) { var res = friendsService.HandleFriendsService(absolutepath, postbuffer); Response.MakeOkResponse(); @@ -702,8 +695,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse #region RewardsService Inventory DEBUG // RewardsService Inventory DEBUG - WipeInventory - else if (absolutepath.Contains($"/RewardsService/pmcards/rewards/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}") - && IsSSFWRegistered(sessionid)) + else if (absolutepath.Contains($"/RewardsService/pmcards/rewards/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}")) { var res = rewardSvc.HandleRewardServiceWipeInvDELETE(directoryPath, filePath, absolutepath, UserAgent, sessionid); if (res != null) @@ -712,8 +704,7 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse Response.MakeErrorResponse(500, "Failed to Delete Rewards Inventory!"); } // RewardsService Inventory DEBUG - DebugClearCardTrackingData - else if (absolutepath.Contains($"/RewardsService/pmcards/rewards/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}/00000000-00000000-00000000-00000001") - && IsSSFWRegistered(sessionid)) + else if (absolutepath.Contains($"/RewardsService/pmcards/rewards/{SSFWUserSessionManager.GetIdBySessionId(sessionid)}/00000000-00000000-00000000-00000001")) { var res = rewardSvc.HandleRewardServiceInvCardTrackingDataDELETE(directoryPath, filePath, absolutepath, UserAgent, sessionid); if (res != null) @@ -798,14 +789,13 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } else Response.MakeErrorResponse(); - login.Dispose(); } #endregion #region FriendService else if (absolutepath.Contains($"/identity/person/{sessionid}/data/psn/friends-list")) { - FriendsService friendsService = new FriendsService(sessionid, "prod", _legacykey); + FriendsService friendsService = new(sessionid, "prod", _legacykey); var res = friendsService.HandleFriendsService(absolutepath, postbuffer); Response.MakeOkResponse(); } @@ -901,7 +891,8 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } break; case "/WebService/ApplyLayoutOverride/": - sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); + sessionId = GetHeaderValue(Headers, "sessionid", false); + if (sessionId == string.Empty) sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); string targetUserName = GetHeaderValue(Headers, "targetUserName", false); string sceneId = GetHeaderValue(Headers, "sceneId", false); env = GetHeaderValue(Headers, "env", false); @@ -998,7 +989,8 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } break; case "/WebService/R3moveLayoutOverride/": - sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); + sessionId = GetHeaderValue(Headers, "sessionid", false); + if (sessionId == string.Empty) sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); Response.Clear(); @@ -1022,7 +1014,8 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } break; case "/WebService/GetMini/": - sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); + sessionId = GetHeaderValue(Headers, "sessionid", false); + if (sessionId == string.Empty) sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); env = GetHeaderValue(Headers, "env", false); if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) @@ -1066,7 +1059,8 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse break; case "/WebService/AddMiniItem/": uuid = GetHeaderValue(Headers, "uuid", false); - sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); + sessionId = GetHeaderValue(Headers, "sessionid", false); + if (sessionId == string.Empty) sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); env = GetHeaderValue(Headers, "env", false); if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) @@ -1112,7 +1106,8 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse break; case "/WebService/AddMiniItems/": uuids = GetHeaderValue(Headers, "uuids", false).Split(','); - sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); + sessionId = GetHeaderValue(Headers, "sessionid", false); + if (sessionId == string.Empty) sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); env = GetHeaderValue(Headers, "env", false); if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) @@ -1165,7 +1160,8 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse break; case "/WebService/RemoveMiniItem/": uuid = GetHeaderValue(Headers, "uuid", false); - sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); + sessionId = GetHeaderValue(Headers, "sessionid", false); + if (sessionId == string.Empty) sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); env = GetHeaderValue(Headers, "env", false); if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) @@ -1211,7 +1207,8 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse break; case "/WebService/RemoveMiniItems/": uuids = GetHeaderValue(Headers, "uuids", false).Split(','); - sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); + sessionId = GetHeaderValue(Headers, "sessionid", false); + if (sessionId == string.Empty) sessionId = GetHeaderValue(Headers, "X-Home-Session-Id", false); env = GetHeaderValue(Headers, "env", false); if (string.IsNullOrEmpty(env) || !SSFWServerConfiguration.homeEnvs.Contains(env)) diff --git a/Servers/SSFWServer/SSFWServerConfiguration.cs b/Servers/SSFWServer/SSFWServerConfiguration.cs index 674397ecf..60cea7bcd 100644 --- a/Servers/SSFWServer/SSFWServerConfiguration.cs +++ b/Servers/SSFWServer/SSFWServerConfiguration.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json.Linq; using System.Security.Cryptography; using MultiServerLibrary.Extension; +using System.Collections.Concurrent; public static class SSFWServerConfiguration { @@ -42,18 +43,18 @@ public static class SSFWServerConfiguration }; // Sandbox Environments - public static List homeEnvs = new() - { - "cprod", "cprodts", "cpreprod", "cpreprodb", - "rc-qa", "rcdev", "rc-dev", "cqa-e", - "cqa-a", "cqa-j", "cqa-h", "cqab-e", - "cqab-a", "cqab-j", "cqab-h", "qcqa-e", - "qcqa-a", "qcqa-j", "qcqa-h", "qcpreprod", - "qcqab-e", "qcqab-a", "qcqab-j", "qcqab-h", - "qcpreprodb", "coredev", "core-dev", "core-qa", - "cdev", "cdev2", "cdev3", "cdeva", "cdevb", "cdevc", - "nonprod1", "nonprod2", "nonprod3", "prodsp" - }; + public static readonly ConcurrentBag homeEnvs = new() + { + "cprod", "cprodts", "cpreprod", "cpreprodb", + "rc-qa", "rcdev", "rc-dev", "cqa-e", + "cqa-a", "cqa-j", "cqa-h", "cqab-e", + "cqab-a", "cqab-j", "cqab-h", "qcqa-e", + "qcqa-a", "qcqa-j", "qcqa-h", "qcpreprod", + "qcqab-e", "qcqab-a", "qcqab-j", "qcqab-h", + "qcpreprodb", "coredev", "core-dev", "core-qa", + "cdev", "cdev2", "cdev3", "cdeva", "cdevb", "cdevc", + "nonprod1", "nonprod2", "nonprod3", "prodsp" + }; /// /// Tries to load the specified configuration file. diff --git a/Servers/SSFWServer/SSFWUserSessionManager.cs b/Servers/SSFWServer/SSFWUserSessionManager.cs index b3063a71c..231f440d3 100644 --- a/Servers/SSFWServer/SSFWUserSessionManager.cs +++ b/Servers/SSFWServer/SSFWUserSessionManager.cs @@ -1,4 +1,5 @@ using CustomLogger; +using SSFWServer.Helpers.RegexHelper; using System.Collections.Concurrent; namespace SSFWServer @@ -82,7 +83,7 @@ public static void RegisterUser(string userName, string sessionid, string id, in // Check if session is still valid and username matches if (entry.Item3 > DateTime.Now && entry.Item2.Username.StartsWith(userName) - && entry.Item2.Username.Contains("018609")) + && HasMinimumClientVersion(userName)) { return entry.Item2.Id; } @@ -107,7 +108,7 @@ public static void RegisterUser(string userName, string sessionid, string id, in if (entry.Item3 > DateTime.Now && entry.Item2.Username.StartsWith(userName) - && entry.Item2.Username.Contains("018609")) + && HasMinimumClientVersion(userName)) { return entry.Item2; } @@ -213,6 +214,16 @@ public static void SessionCleanupLoop(object? state) } } } + + private static bool HasMinimumClientVersion(string username, int minimumVersion = 016531) + { + var match = GUIDValidator.VersionFilter.Match(username); + if (match.Success && int.TryParse(match.Value, out int version)) + { + return version >= minimumVersion; + } + return false; + } } public class UserSession diff --git a/Servers/SSFWServer/Services/AuditService.cs b/Servers/SSFWServer/Services/AuditService.cs index 1bd9eb9cf..601a912c6 100644 --- a/Servers/SSFWServer/Services/AuditService.cs +++ b/Servers/SSFWServer/Services/AuditService.cs @@ -26,10 +26,6 @@ public string HandleAuditService(string absolutepath, byte[] buffer, HttpRequest string? personIdToCompare = SSFWUserSessionManager.GetIdBySessionId(sessionid); string auditLogPath = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutepath}"; -#if DEBUG - LoggerAccessor.LogInfo(auditLogPath); -#endif - switch (request.Method) { case "PUT": @@ -38,7 +34,9 @@ public string HandleAuditService(string absolutepath, byte[] buffer, HttpRequest Directory.CreateDirectory(auditLogPath); File.WriteAllText($"{auditLogPath}/{fileNameGUID}.json", Encoding.UTF8.GetString(buffer)); +#if DEBUG LoggerAccessor.LogInfo($"[SSFW] AuditService - HandleAuditService Audit event log posted: {fileNameGUID}"); +#endif return $"{{ \"result\": 0 }}"; } catch (Exception ex) @@ -54,7 +52,7 @@ public string HandleAuditService(string absolutepath, byte[] buffer, HttpRequest string newFileMatchingEntry = string.Empty; - List listOfEventsByUser = new List(); + List listOfEventsByUser = new(); int userEventTotal = 1; int idxTotal = 0; foreach (string fileToRead in files) @@ -80,17 +78,20 @@ public string HandleAuditService(string absolutepath, byte[] buffer, HttpRequest idxTotal++; } } - - LoggerAccessor.LogError($"[SSFW] AuditService - HandleAuditService returning count list of logs for player {personIdToCompare}"); +#if DEBUG + LoggerAccessor.LogInfo($"[SSFW] AuditService - HandleAuditService returning count list of logs for player {personIdToCompare}"); +#endif return $"{{ \"count\": {idxTotal}, \"events\": {{ {string.Join("", listOfEventsByUser)} }} }}"; } else if(absolutepath.Contains("object")) { +#if DEBUG LoggerAccessor.LogInfo("[SSFW] AuditService - HandleAuditService Event log get " + auditLogPath.Replace("/object", "") + ".json"); +#endif return File.ReadAllText(auditLogPath.Replace("/object", "") + ".json"); } break; default: - LoggerAccessor.LogError($"[SSFW] AuditService - HandleAuditService Method {request.Method} unhandled"); + LoggerAccessor.LogError($"[SSFW] AuditService - HandleAuditService Method {request.Method} unhandled!"); return $"{{ \"result\": -1 }}"; } diff --git a/Servers/SSFWServer/Services/IdentityService.cs b/Servers/SSFWServer/Services/IdentityService.cs index fc3f54c10..fdb654c10 100644 --- a/Servers/SSFWServer/Services/IdentityService.cs +++ b/Servers/SSFWServer/Services/IdentityService.cs @@ -1,7 +1,6 @@ using CastleLibrary.Sony.XI5; using CustomLogger; using NetHasher; -using MultiServerLibrary.Extension; using SSFWServer.Helpers.DataMigrator; using System.Text; using CastleLibrary.Sony.SSFW; @@ -9,14 +8,13 @@ namespace SSFWServer.Services { - public class IdentityService : IDisposable + public class IdentityService { private string? XHomeClientVersion; private string? generalsecret; private string? homeClientVersion; private string? xsignature; private string? key; - private bool disposedValue; public IdentityService(string XHomeClientVersion, string generalsecret, string homeClientVersion, string? xsignature, string? key) { @@ -377,38 +375,6 @@ public IdentityService(string XHomeClientVersion, string generalsecret, string h return null; } - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - XHomeClientVersion = null; - generalsecret = null; - homeClientVersion = null; - xsignature = null; - key = null; - } - - // TODO: libérer les ressources non managées (objets non managés) et substituer le finaliseur - // TODO: affecter aux grands champs une valeur null - disposedValue = true; - } - } - - // // TODO: substituer le finaliseur uniquement si 'Dispose(bool disposing)' a du code pour libérer les ressources non managées - // ~SSFWLogin() - // { - // // Ne changez pas ce code. Placez le code de nettoyage dans la méthode 'Dispose(bool disposing)' - // Dispose(disposing: false); - // } - - public void Dispose() - { - // Ne changez pas ce code. Placez le code de nettoyage dans la méthode 'Dispose(bool disposing)' - Dispose(disposing: true); - GC.SuppressFinalize(this); - } } } \ No newline at end of file diff --git a/Servers/SSFWServer/Services/KeepAliveService.cs b/Servers/SSFWServer/Services/KeepAliveService.cs index 36c7788f6..86acaed79 100644 --- a/Servers/SSFWServer/Services/KeepAliveService.cs +++ b/Servers/SSFWServer/Services/KeepAliveService.cs @@ -1,33 +1,15 @@ -using NetCoreServer; -using SSFWServer.Helpers.RegexHelper; -using System.Text.RegularExpressions; +using SSFWServer.Helpers.RegexHelper; namespace SSFWServer.Services { public class KeepAliveService { - public bool UpdateKeepAliveForClient(string absolutePath, HttpResponse res) + public static bool UpdateKeepAliveForClient(string absolutePath) { - const byte GuidLength = 36; - int index = absolutePath.IndexOf("/morelife"); - - if (index != -1 && index > GuidLength) // Makes sure we have at least 36 chars available beforehand. - { - // Extract the substring between the last '/' and the morelife separator. - string resultSessionId = absolutePath.Substring(index - GuidLength, GuidLength); - - if (GUIDValidator.RegexSessionValidator.IsMatch(resultSessionId)) - { - SSFWUserSessionManager.UpdateKeepAliveTime(resultSessionId); - return true; - } - else - { - return false; - } - } - else - return false; + string resultSessionId = absolutePath.Split("/")[3]; + if (GUIDValidator.RegexSessionValidator.IsMatch(resultSessionId)) + return SSFWUserSessionManager.UpdateKeepAliveTime(resultSessionId); + return false; } } } \ No newline at end of file diff --git a/Servers/SSFWServer/Services/RewardsService.cs b/Servers/SSFWServer/Services/RewardsService.cs index e77d43216..9ab283a18 100644 --- a/Servers/SSFWServer/Services/RewardsService.cs +++ b/Servers/SSFWServer/Services/RewardsService.cs @@ -1,4 +1,5 @@ using CustomLogger; +using MultiServerLibrary.Extension; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SSFWServer.Helpers.FileHelper; @@ -42,7 +43,7 @@ public byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, st { return RewardServiceInventory(Array.Empty(), directorypath, filepath, absolutepath, false, true); } else { - LoggerAccessor.LogError($"[SSFW] - HandleRewardServiceInvCardTrackingDataDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to delete Card Tracking data!"); + LoggerAccessor.LogWarn($"[SSFW] - HandleRewardServiceInvCardTrackingDataDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to delete Card Tracking data!"); return null; } } @@ -54,7 +55,7 @@ public byte[] HandleRewardServiceInvPOST(byte[] buffer, string directorypath, st { return RewardServiceInventory(Array.Empty(), directorypath, filepath, absolutepath, true, false); } else { - LoggerAccessor.LogError($"[SSFW] - HandleRewardServiceWipeInvDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to wipe inventory data!"); + LoggerAccessor.LogWarn($"[SSFW] - HandleRewardServiceWipeInvDELETE : {SSFWUserSessionManager.GetIdBySessionId(sessionId)} Unauthorized to wipe inventory data!"); return null; } } @@ -103,11 +104,6 @@ public void SSFWUpdateMini(string filePath, string postData, bool delete) JToken? rewardValue = reward.Value; if (string.IsNullOrEmpty(rewardKey) || rewardValue == null) continue; - if (rewardValue.Type != JTokenType.Integer) - { - LoggerAccessor.LogInfo($"[SSFW] SSFWUpdateMini - Reward: {rewardValue} earned, adding to mini file: {filePath}."); - rewardValue = 1; - } // Check if the reward exists in the JSON array JToken? existingReward = jsonArray.FirstOrDefault(r => r[rewardKey] != null); @@ -121,13 +117,13 @@ public void SSFWUpdateMini(string filePath, string postData, bool delete) { if (existingReward != null) // Update the value of the reward - existingReward[rewardKey] = rewardValue; + existingReward[rewardKey] = DateTime.UtcNow.ToUnixTime(); else { // Add the new reward to the JSON array jsonArray.Add(new JObject { - { rewardKey, rewardValue } + { rewardKey, DateTime.UtcNow.ToUnixTime() } }); } } @@ -186,7 +182,7 @@ public void TrunkServiceProcess(string filePath, string request, string env, str foreach (var entry in entriesToAddInMini) { - SSFWUpdateMini(miniPath, $"{{\"rewards\":{{\"{entry.Key}\": {entry.Value}}}}}", false); + SSFWUpdateMini(miniPath, $"{{ \"rewards\": {{ \"{entry.Key}\": {entry.Value} }} }}", false); } } } @@ -239,7 +235,7 @@ public void TrunkServiceProcess(string filePath, string request, string env, str { foreach (string entry in entriesToRemoveInMini) { - SSFWUpdateMini(miniPath, $"{{\"rewards\":{{\"{entry}\": -1}}}}", true); + SSFWUpdateMini(miniPath, $"{{ \"rewards\": {{ \"{entry}\": -1 }} }}", true); } } } @@ -300,7 +296,9 @@ public static byte[] RewardServiceInventory(byte[] buffer, string directorypath, if(deleteInv) { File.Delete(countsStore); - LoggerAccessor.LogInfo($"[SSFW] - RewardServiceInventory: Deleting Inventory counts at {countsStore}"); +#if DEBUG + LoggerAccessor.LogInfo($"[SSFW] - RewardServiceInventory: Successfully deleted Inventory counts at {countsStore}"); +#endif } else { string countsJson = File.ReadAllText(countsStore); @@ -318,7 +316,9 @@ public static byte[] RewardServiceInventory(byte[] buffer, string directorypath, if (deleteInv || deleteOnlyTracking) { File.Delete(trackingFile); +#if DEBUG LoggerAccessor.LogInfo($"[SSFW] - RewardServiceInventory: Deleting Tracking file at {trackingFile}"); +#endif return Encoding.UTF8.GetBytes(""); } else diff --git a/Servers/SSFWServer/Services/SaveDataService.cs b/Servers/SSFWServer/Services/SaveDataService.cs index f4f97e776..bcef7ac0c 100644 --- a/Servers/SSFWServer/Services/SaveDataService.cs +++ b/Servers/SSFWServer/Services/SaveDataService.cs @@ -1,5 +1,6 @@ using CustomLogger; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.RegularExpressions; namespace SSFWServer.Services { @@ -14,12 +15,12 @@ public class SaveDataService List? files = GetFilesInfo(directoryPath + "/" + segment); if (files != null) - return JsonConvert.SerializeObject(new FilesContainer() { files = files }, Formatting.Indented); + return JsonSerializer.Serialize(new FilesContainer() { files = files }); } } catch (Exception e) { - LoggerAccessor.LogError($"[SSFW] - DebugGetFileList ERROR: \n{e}"); + LoggerAccessor.LogError($"[SSFW] - DebugGetFileList ERROR: \n{e}"); } return null; From 708cf35267154f2d4166fdcb1896de6e697da26d Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Mon, 5 Jan 2026 08:08:51 -0600 Subject: [PATCH 10/13] Review changes - Fixed reading the layouts in IdentityService. - Added Null checks for the counts and tracking dirs. - Removed unused IsSessionValidByDisplayName function, as verify status was missing. However, since it is ultimately unused, it's getting removed. - Add IF DEBUG check to FriendsService Log - Change absoultePath to FilePath, verify this doesn't break. --- Servers/SSFWServer/SSFWProcessor.cs | 5 ++-- Servers/SSFWServer/SSFWUserSessionManager.cs | 27 ------------------- Servers/SSFWServer/Services/AvatarService.cs | 4 +-- Servers/SSFWServer/Services/FriendsService.cs | 2 ++ .../SSFWServer/Services/IdentityService.cs | 4 +-- Servers/SSFWServer/Services/RewardsService.cs | 10 +++++-- 6 files changed, 17 insertions(+), 35 deletions(-) diff --git a/Servers/SSFWServer/SSFWProcessor.cs b/Servers/SSFWServer/SSFWProcessor.cs index 3da7d8508..a32af20d5 100644 --- a/Servers/SSFWServer/SSFWProcessor.cs +++ b/Servers/SSFWServer/SSFWProcessor.cs @@ -419,9 +419,10 @@ private static HttpResponse SSFWRequestProcess(HttpRequest request, HttpResponse } #region SaveData AvatarService - else if (absolutepath.Contains($"/SaveDataService/avatar/{env}/") && absolutepath.EndsWith(".jpg")) + else if (absolutepath.Contains($"/SaveDataService/avatar/{env}/") + && absolutepath.EndsWith(".jpg")) { - byte[]? res = avatarService.HandleAvatarService(absolutepath, filePath, _legacykey); + byte[]? res = avatarService.HandleAvatarService(filePath, _legacykey); if (res != null) Response.MakeGetResponse(res, "image/jpg"); else diff --git a/Servers/SSFWServer/SSFWUserSessionManager.cs b/Servers/SSFWServer/SSFWUserSessionManager.cs index 231f440d3..e1c52b440 100644 --- a/Servers/SSFWServer/SSFWUserSessionManager.cs +++ b/Servers/SSFWServer/SSFWUserSessionManager.cs @@ -155,33 +155,6 @@ public static bool UpdateKeepAliveTime(string sessionid, (int, UserSession, Date } - - public static (bool, string?) IsSessionValidByDisplayName(string? userName, bool cleanupDeadSessions) - { - if (string.IsNullOrEmpty(userName)) - return (false, null); - - var userSession = userSessions.FirstOrDefault(x => x.Value.Item2.Username == userName); - - if (!string.IsNullOrEmpty(userSession.Value.Item2.Id)) - - if (userSessions.TryGetValue(userSession.Value.Item2.Id, out (int, UserSession, DateTime) sessionEntry)) - { - if (sessionEntry.Item3 > DateTime.Now) - return (true, sessionEntry.Item2.Id); - else if (cleanupDeadSessions) - { - // Clean up expired entry. - if (userSessions.TryRemove(userSession.Value.Item2.Id, out sessionEntry)) - LoggerAccessor.LogWarn($"[SSFWUserSessionManager] - Cleaned: {sessionEntry.Item2.Username} session with id: {sessionEntry.Item2.Id}..."); - else - LoggerAccessor.LogError($"[SSFWUserSessionManager] - Failed to clean: {sessionEntry.Item2.Username} session with id: {sessionEntry.Item2.Id}..."); - } - } - - return (false, null); - } - public static (bool, string?) IsSessionValid(string? sessionId, bool cleanupDeadSessions) { if (string.IsNullOrEmpty(sessionId)) diff --git a/Servers/SSFWServer/Services/AvatarService.cs b/Servers/SSFWServer/Services/AvatarService.cs index 799bc2363..da1640139 100644 --- a/Servers/SSFWServer/Services/AvatarService.cs +++ b/Servers/SSFWServer/Services/AvatarService.cs @@ -4,11 +4,11 @@ namespace SSFWServer.Services { public class AvatarService { - public byte[]? HandleAvatarService(string absolutepath, string filePath, string? key) + public byte[]? HandleAvatarService(string filePath, string? key) { if (File.Exists(filePath)) { - return FileHelper.ReadAllBytes(absolutepath, key); + return FileHelper.ReadAllBytes(filePath, key); } else { return null; diff --git a/Servers/SSFWServer/Services/FriendsService.cs b/Servers/SSFWServer/Services/FriendsService.cs index 72fabd698..df607c6c2 100644 --- a/Servers/SSFWServer/Services/FriendsService.cs +++ b/Servers/SSFWServer/Services/FriendsService.cs @@ -25,7 +25,9 @@ public string HandleFriendsService(string absolutepath, byte[] buffer) Directory.CreateDirectory(friendsStorePath); File.WriteAllText($"{friendsStorePath}/{userName}.txt", Encoding.UTF8.GetString(buffer)); +#if DEBUG LoggerAccessor.LogInfo($"[SSFW] FriendsService - HandleFriendsService Friends list posted: {userName} at {$"{friendsStorePath}/{userName}.txt"}"); +#endif return "Success"; } catch (Exception ex) diff --git a/Servers/SSFWServer/Services/IdentityService.cs b/Servers/SSFWServer/Services/IdentityService.cs index fdb654c10..2faca6026 100644 --- a/Servers/SSFWServer/Services/IdentityService.cs +++ b/Servers/SSFWServer/Services/IdentityService.cs @@ -210,13 +210,13 @@ public IdentityService(string XHomeClientVersion, string generalsecret, string h } else if (!File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/HarborStudio.json")) File.WriteAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/HarborStudio.json", - $"{SSFWServerConfiguration.SSFWLayoutsFolder}/HarborStudio.json"); + File.ReadAllText($"{SSFWServerConfiguration.SSFWLayoutsFolder}/HarborStudio.json")); } else { if (!File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json")) File.WriteAllText($"{SSFWServerConfiguration.SSFWStaticFolder}/LayoutService/{env}/person/{resultString}/mylayout.json", - $"{SSFWServerConfiguration.SSFWLayoutsFolder}/LegacyLayout.json"); + File.ReadAllText($"{SSFWServerConfiguration.SSFWLayoutsFolder}/LegacyLayout.json")); } if (!File.Exists($"{SSFWServerConfiguration.SSFWStaticFolder}/RewardsService/{env}/rewards/{resultString}/mini.json")) diff --git a/Servers/SSFWServer/Services/RewardsService.cs b/Servers/SSFWServer/Services/RewardsService.cs index 9ab283a18..22bb1ca73 100644 --- a/Servers/SSFWServer/Services/RewardsService.cs +++ b/Servers/SSFWServer/Services/RewardsService.cs @@ -265,8 +265,14 @@ public static byte[] RewardServiceInventory(byte[] buffer, string directorypath, string trackingFileDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}/object"; string trackingFile = $"{trackingFileDir}/{trackingGuid}.json"; - Directory.CreateDirectory(Path.GetDirectoryName(countsStoreDir) ?? countsStoreDir); - Directory.CreateDirectory(Path.GetDirectoryName(trackingFileDir) ?? trackingFileDir); + if (!string.IsNullOrEmpty(countsStoreDir) && !string.IsNullOrEmpty(trackingFileDir)) { + Directory.CreateDirectory(Path.GetDirectoryName(countsStoreDir)); + Directory.CreateDirectory(Path.GetDirectoryName(trackingFileDir)); + } + else + { + LoggerAccessor.LogError("Fatal error in RewardService Inventory System! CountsStorDir or TrackingFileDir should NOT be null!"); + } //Parse Buffer string fixedJsonPayload = GUIDValidator.FixJsonValues(Encoding.UTF8.GetString(buffer)); From b0e73af5b9003ad4a3322def640ba4b0e76c6219 Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Mon, 5 Jan 2026 08:47:49 -0600 Subject: [PATCH 11/13] FIx log name convention. --- Servers/SSFWServer/Services/RewardsService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Servers/SSFWServer/Services/RewardsService.cs b/Servers/SSFWServer/Services/RewardsService.cs index 22bb1ca73..7e472985f 100644 --- a/Servers/SSFWServer/Services/RewardsService.cs +++ b/Servers/SSFWServer/Services/RewardsService.cs @@ -271,7 +271,7 @@ public static byte[] RewardServiceInventory(byte[] buffer, string directorypath, } else { - LoggerAccessor.LogError("Fatal error in RewardService Inventory System! CountsStorDir or TrackingFileDir should NOT be null!"); + LoggerAccessor.LogError("[SSFW] - RewardServiceInventoryPOST: Fatal error in RewardService Inventory System! CountsStorDir or TrackingFileDir should NOT be null!"); } //Parse Buffer From d73c1d934d4b0b0fbc0125ef174d1614952e1750 Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Mon, 5 Jan 2026 08:48:38 -0600 Subject: [PATCH 12/13] Adjust Null Check and typo --- Servers/SSFWServer/Services/RewardsService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Servers/SSFWServer/Services/RewardsService.cs b/Servers/SSFWServer/Services/RewardsService.cs index 7e472985f..a49862322 100644 --- a/Servers/SSFWServer/Services/RewardsService.cs +++ b/Servers/SSFWServer/Services/RewardsService.cs @@ -265,13 +265,13 @@ public static byte[] RewardServiceInventory(byte[] buffer, string directorypath, string trackingFileDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}/object"; string trackingFile = $"{trackingFileDir}/{trackingGuid}.json"; - if (!string.IsNullOrEmpty(countsStoreDir) && !string.IsNullOrEmpty(trackingFileDir)) { + if (!string.IsNullOrEmpty(countsStoreDir) || !string.IsNullOrEmpty(trackingFileDir)) { Directory.CreateDirectory(Path.GetDirectoryName(countsStoreDir)); Directory.CreateDirectory(Path.GetDirectoryName(trackingFileDir)); } else { - LoggerAccessor.LogError("[SSFW] - RewardServiceInventoryPOST: Fatal error in RewardService Inventory System! CountsStorDir or TrackingFileDir should NOT be null!"); + LoggerAccessor.LogError("[SSFW] - RewardServiceInventoryPOST: Fatal error in RewardService Inventory System! CountsStoreDir or TrackingFileDir should NOT be null!"); } //Parse Buffer From 8cc069ea9b7a2e8196bbe28656e6f1dabce1a2a1 Mon Sep 17 00:00:00 2001 From: JumpSuit <26616362+Jump-Suit@users.noreply.github.com> Date: Mon, 5 Jan 2026 22:03:14 -0600 Subject: [PATCH 13/13] Hotfix DirectoryPathCheck --- Servers/SSFWServer/Services/RewardsService.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Servers/SSFWServer/Services/RewardsService.cs b/Servers/SSFWServer/Services/RewardsService.cs index a49862322..8c46c8e6d 100644 --- a/Servers/SSFWServer/Services/RewardsService.cs +++ b/Servers/SSFWServer/Services/RewardsService.cs @@ -258,6 +258,9 @@ public static byte[] RewardServiceInventory(byte[] buffer, string directorypath, //Tracking Inventory GUID const string trackingGuid = "00000000-00000000-00000000-00000001"; // fallback/hardcoded tracking GUID + //Only return trackingGuid on error + var errorPayload = Encoding.UTF8.GetBytes($"{{\"idList\": [\"{trackingGuid}\"] }}"); + // File paths based on the provided format string countsStoreDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}"; string countsStore = $"{countsStoreDir}/counts.json"; @@ -265,13 +268,14 @@ public static byte[] RewardServiceInventory(byte[] buffer, string directorypath, string trackingFileDir = $"{SSFWServerConfiguration.SSFWStaticFolder}/{absolutePath}/object"; string trackingFile = $"{trackingFileDir}/{trackingGuid}.json"; - if (!string.IsNullOrEmpty(countsStoreDir) || !string.IsNullOrEmpty(trackingFileDir)) { + if (!string.IsNullOrEmpty(countsStoreDir) && !string.IsNullOrEmpty(trackingFileDir)) { Directory.CreateDirectory(Path.GetDirectoryName(countsStoreDir)); Directory.CreateDirectory(Path.GetDirectoryName(trackingFileDir)); } else { LoggerAccessor.LogError("[SSFW] - RewardServiceInventoryPOST: Fatal error in RewardService Inventory System! CountsStoreDir or TrackingFileDir should NOT be null!"); + return errorPayload; } //Parse Buffer @@ -281,8 +285,6 @@ public static byte[] RewardServiceInventory(byte[] buffer, string directorypath, using JsonDocument document = JsonDocument.Parse(fixedJsonPayload); JsonElement root = document.RootElement; - //Only return trackingGuid on error - var errorPayload = Encoding.UTF8.GetBytes($"{{\"idList\": [\"{trackingGuid}\"] }}"); if (!root.TryGetProperty("rewards", out JsonElement rewardsElement) || rewardsElement.ValueKind != JsonValueKind.Array) { LoggerAccessor.LogError("[SSFW] - RewardServiceInventoryPOST: Invalid payload - 'rewards' must be an array.");