From 6fab853d5f57218b8297c11f72ba7b55c94d7147 Mon Sep 17 00:00:00 2001 From: AlanMoonbase Date: Mon, 17 Mar 2025 14:05:43 -0700 Subject: [PATCH] implement realtime communication with mmo server -also added messages for anyone on wojs or earlier for ribbons -mmo communication occurs in the ``MessagingService`` and ``BuddyService`` -for now this requires a DLL from the game client as we should not distribute the SmartFox client openly here due to it being closed sourced --- .gitignore | 3 +- src/Controllers/Common/ContentController.cs | 4 +- src/Program.cs | 3 + src/Services/AchievementService.cs | 6 +- src/Services/BuddyService.cs | 14 ++- src/Services/MMOClientService.cs | 111 ++++++++++++++++++++ src/Services/MessagingService.cs | 8 +- src/sodoff.csproj | 10 ++ 8 files changed, 153 insertions(+), 6 deletions(-) create mode 100644 src/Services/MMOClientService.cs diff --git a/.gitignore b/.gitignore index e40df02..32f91cc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ src/Properties __pycache__/ sodoff.db sodoff.db-shm -sodoff.db-wal \ No newline at end of file +sodoff.db-wal +src/Dlls/SmartFox2X.dll \ No newline at end of file diff --git a/src/Controllers/Common/ContentController.cs b/src/Controllers/Common/ContentController.cs index 4be9327..8054a7e 100644 --- a/src/Controllers/Common/ContentController.cs +++ b/src/Controllers/Common/ContentController.cs @@ -1754,6 +1754,7 @@ public class ContentController : Controller { { existingScene.XmlData = contentXml; ctx.SaveChanges(); + return Ok(true); } else @@ -1764,7 +1765,8 @@ public class ContentController : Controller { XmlData = contentXml }; viking.SceneData.Add(sceneData); - ctx.SaveChanges(); + ctx.SaveChanges(); + return Ok(true); } } diff --git a/src/Program.cs b/src/Program.cs index fb2beee..be2cb13 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -30,6 +30,7 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddScoped(); @@ -44,6 +45,8 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddHostedService(provider => provider.GetService()); + bool assetServer = builder.Configuration.GetSection("AssetServer").GetValue("Enabled"); string assetIP = builder.Configuration.GetSection("AssetServer").GetValue("ListenIP"); int assetPort = builder.Configuration.GetSection("AssetServer").GetValue("Port"); diff --git a/src/Services/AchievementService.cs b/src/Services/AchievementService.cs index f5cd2c1..3649cba 100644 --- a/src/Services/AchievementService.cs +++ b/src/Services/AchievementService.cs @@ -12,7 +12,8 @@ namespace sodoff.Services { private MessagingService messagingService; public readonly DBContext ctx; - public AchievementService(AchievementStoreSingleton achievementStore, InventoryService inventoryService, MessagingService messagingService, DBContext ctx) { + public AchievementService(AchievementStoreSingleton achievementStore, InventoryService inventoryService, MessagingService messagingService, DBContext ctx) + { this.achievementStore = achievementStore; this.inventoryService = inventoryService; this.messagingService = messagingService; @@ -149,6 +150,9 @@ namespace sodoff.Services { // TODO: check trophies, etc criteria and id need apply and add to results extra reward here + if (viking.GameVersion <= ClientVersion.WoJS) + messagingService.AddMessageToViking(null, viking, MessageType.Data, MessageTypeID.Achievement, MessageLevel.WhiteList, "[[Line1]]=[[Great Job! You've Earned A Ribbon, JumpStars, And Coins!]][[SubType]]=[[Ribbon]]", "[[Line1]]=[[Great Job! You've Earned A Ribbon, JumpStars, And Coins!]][[SubType]]=[[Ribbon]]", "[[Line1]]=[[Great Job! You've Earned A Ribbon, JumpStars, And Coins!]][[SubType]]=[[Ribbon]]", isPrivate: true); + return grantedRewards.ToArray(); } diff --git a/src/Services/BuddyService.cs b/src/Services/BuddyService.cs index 11fef4e..a8d6eef 100644 --- a/src/Services/BuddyService.cs +++ b/src/Services/BuddyService.cs @@ -14,11 +14,13 @@ public class BuddyService private readonly DBContext ctx; private readonly MessagingService messagingService; + private readonly MMOClientService mMOClientService; - public BuddyService(DBContext ctx, MessagingService messagingService) + public BuddyService(DBContext ctx, MessagingService messagingService, MMOClientService mMOClientService) { this.ctx = ctx; this.messagingService = messagingService; + this.mMOClientService = mMOClientService; } public BuddyActionResult CreateBuddyRelation(Viking viking1, Viking viking2, BuddyStatus buddyStatus1 = BuddyStatus.PendingApprovalFromOther, BuddyStatus buddyStatus2 = BuddyStatus.PendingApprovalFromSelf) @@ -53,8 +55,11 @@ public class BuddyService // if this is a buddy relationship, use MessagingService to send buddy request message to viking2 if (buddyStatus1 == BuddyStatus.PendingApprovalFromOther && buddyStatus2 == BuddyStatus.PendingApprovalFromSelf) + { messagingService.AddMessageToViking(viking1, viking2, MessageType.Data, MessageTypeID.BuddyList, MessageLevel.WhiteList, "[[Line1]]=[[{{BuddyUserName}} Wants To Add You As A Buddy!]]", "[[Line1]]=[[{{BuddyUserName}} Wants To Add You As A Buddy!]]", "[[Line1]]=[[{{BuddyUserName}} Wants To Add You As A Buddy!]]"); - + mMOClientService.SendCommandToUser(viking2.Uid.ToString(), "SBE", new string[] { "SBE", "-1", viking1.Uid.ToString(), viking2.Uid.ToString(), "1" }); + } + // return result return new BuddyActionResult { @@ -77,6 +82,9 @@ public class BuddyService buddy.BuddyStatus2 = buddyStatus2; ctx.SaveChanges(); + if ((buddyStatus1 & buddyStatus2) == BuddyStatus.Approved) + mMOClientService.SendCommandToUser(viking.Uid.ToString(), "SBE", new string[] { "SBE", "-1", buddyViking.Uid.ToString(), viking.Uid.ToString(), "4" }); + // return result return true; } else return false; @@ -161,6 +169,8 @@ public class BuddyService ctx.Buddies.Remove(buddy); ctx.SaveChanges(); + mMOClientService.SendCommandToUser(buddyViking.Uid.ToString(), "SBE", new string[] { "SBE", "-1", viking.Uid.ToString(), buddyViking.Uid.ToString(), "2" }); + return true; } else return false; diff --git a/src/Services/MMOClientService.cs b/src/Services/MMOClientService.cs new file mode 100644 index 0000000..a51a9ac --- /dev/null +++ b/src/Services/MMOClientService.cs @@ -0,0 +1,111 @@ +using System; +using Microsoft.Extensions.Options; +using Sfs2X; +using Sfs2X.Core; +using Sfs2X.Entities; +using Sfs2X.Entities.Data; +using Sfs2X.Entities.Variables; +using Sfs2X.Requests; +using Sfs2X.Util; +using sodoff.Configuration; + +namespace sodoff.Services; + +public class MMOClientService : IHostedService +{ + private readonly IOptions Config; + private ConfigData SFSConfig; + + public SmartFox SFSClient { get; private set; } + public bool IsLoggedIn { get; private set; } + public User? CurrentUser { get; private set; } + + public MMOClientService(IOptions config) + { + Config = config; + + // set SFSConfig + SFSConfig = new ConfigData(); + + SFSConfig.Host = Config.Value.MMOAdress; + SFSConfig.Port = Config.Value.MMOPort; + SFSConfig.Zone = "JumpStart"; + + // set SFSClient + SFSClient = new SmartFox(); + SFSClient.ThreadSafeMode = false; // this is set to true by default which requires Unity's thread processing stuff + } + + public Task StartAsync(CancellationToken cancellationToken) + { + // connect and login to mmo server using SFSConfig + + Console.WriteLine("Starting Connection To MMO Server..."); + + SFSClient.Connect(SFSConfig); + + SFSClient.AddEventListener(SFSEvent.CONNECTION, OnConnectionEstablished); + SFSClient.AddEventListener(SFSEvent.LOGIN, OnLogin); + + // return completed task (SmartFox seems to be completely synchronous) + return Task.CompletedTask; + } + + public Task SetRoomVarForRoom(int roomId, RoomVariable var) + { + if (IsLoggedIn) + { + Room room = SFSClient.GetRoomById(roomId); + if (room != null) room.SetVariable(var); + else return Task.CompletedTask; + } + else throw new InvalidOperationException("MMO Client Was Not Ready"); + + return Task.CompletedTask; + } + + public Task SendCommandToUser(string userId, string cmd, string[] parameters) + { + // SFSClient doesn't seem to have a method to send commands to specific users, so we'll make a custom extension endpoint that will handle it + ISFSObject obj = SFSObject.NewInstance(); + obj.PutUtfString("UID", userId); + obj.PutUtfString("CMD", cmd); + obj.PutUtfStringArray("ARR", parameters); + + SFSClient.Send(new ExtensionRequest("SUE", obj)); + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + Console.WriteLine("Stopping Connection To MMO Server..."); + + if(SFSClient.IsConnected) + { + if(IsLoggedIn) SFSClient.Send(new LogoutRequest()); + SFSClient.Disconnect(); + } + + return Task.CompletedTask; + } + + private void OnConnectionEstablished(BaseEvent evt) + { + bool success = (bool)evt.Params["success"]; + if (success) + { + Console.WriteLine("Connection Established. Sending Login Request..."); + SFSClient.Send(new LoginRequest("API")); + } + } + + private void OnLogin(BaseEvent evt) + { + CurrentUser = (User)evt.Params["user"]; + IsLoggedIn = CurrentUser != null; + Console.WriteLine($"Logged In? - {IsLoggedIn}"); + if (IsLoggedIn) Console.WriteLine("MMO Service Ready."); + else Console.WriteLine("MMO Service Not Ready."); + } +} diff --git a/src/Services/MessagingService.cs b/src/Services/MessagingService.cs index ef08233..d978e65 100644 --- a/src/Services/MessagingService.cs +++ b/src/Services/MessagingService.cs @@ -11,9 +11,11 @@ namespace sodoff.Services; public class MessagingService { private readonly DBContext ctx; - public MessagingService(DBContext ctx) + private readonly MMOClientService mMOClientService; + public MessagingService(DBContext ctx, MMOClientService mMOClientService) { this.ctx = ctx; + this.mMOClientService = mMOClientService; } public Model.Message AddMessageToViking(Viking? viking, Viking toViking, MessageType messageType, MessageTypeID messageTypeID, MessageLevel messageLevel, string data, string memberMessage = "", string nonMemberMessage = "", bool IsNew = true, bool IsDeleted = false, bool isReply = false, bool isPrivate = false, int parentMessageId = 0) @@ -74,6 +76,10 @@ public class MessagingService ctx.Messages.Add(message); ctx.SaveChanges(); + // update receiving users messages + mMOClientService.SendCommandToUser(toViking.Uid.ToString(), "SPMN", new string[] { "SPMN" }); + mMOClientService.SendCommandToUser(toViking.Uid.ToString(), "NMP", new string[] { "NMP", "-1", "1", "1" }); + // return constructed message return message; } diff --git a/src/sodoff.csproj b/src/sodoff.csproj index 9af53c7..823eb9a 100644 --- a/src/sodoff.csproj +++ b/src/sodoff.csproj @@ -159,4 +159,14 @@ PreserveNewest + + + + Dlls\SmartFox2X.dll + + + + + +