diff --git a/RPGPlugin.csproj b/RPGPlugin.csproj index efdfb3c..c519f67 100644 --- a/RPGPlugin.csproj +++ b/RPGPlugin.csproj @@ -127,17 +127,26 @@ Settings.settings True - + + - + + Hunter.xaml + + + Miner.xaml + + + Warrior.xaml + @@ -151,6 +160,7 @@ RPGPluginControl.xaml + @@ -179,6 +189,9 @@ Designer MSBuild:Compile + + + MSBuild:Compile Designer @@ -196,9 +209,6 @@ Designer - - - diff --git a/RPGPlugin/RPGPlugin.cs b/RPGPlugin/RPGPlugin.cs index 2899281..a5418cf 100644 --- a/RPGPlugin/RPGPlugin.cs +++ b/RPGPlugin/RPGPlugin.cs @@ -3,8 +3,11 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using System.Timers; using System.Windows.Controls; +using System.Windows.Threading; using System.Xml.Serialization; using RPGPlugin.PointManagementSystem; using Sandbox.Engine.Multiplayer; @@ -18,8 +21,8 @@ using Torch.Session; using VRage.GameServices; using RPGPlugin.Utils; -using Sandbox.Game.Multiplayer; -using Newtonsoft.Json; +using Torch.Collections; +using Timer = System.Timers.Timer; namespace RPGPlugin @@ -30,6 +33,8 @@ public class Roles : TorchPluginBase, IWpfPlugin public static ConcurrentDictionary PlayerManagers = new ConcurrentDictionary(); public static Dictionary classConfigs = new Dictionary(); public static Dictionary roles = new Dictionary(); + public MtObservableSortedDictionary classViews = new MtObservableSortedDictionary(); + public readonly Dispatcher MainDispatcher = Dispatcher.CurrentDispatcher; private Timer _delayManagers = new Timer(TimeSpan.FromSeconds(5).TotalMilliseconds); public PatchManager patchManager; public PatchContext patchContext; @@ -45,14 +50,12 @@ public class Roles : TorchPluginBase, IWpfPlugin private Persistent _config; public RPGPluginConfig Config => _config?.Data; - public override void Init(ITorchBase torch) + public override async void Init(ITorchBase torch) { base.Init(torch); + Roles.Log.Warn($"MainPlugin Thread => {Thread.CurrentThread.ManagedThreadId}"); Instance = this; - SetupConfig(); - - // Registration of role configuration classes - // This is called on class auto-load! + await SetupConfig(); _delayManagers.Stop(); _delayManagers.Elapsed += DelayManagersOnElapsed; @@ -62,12 +65,11 @@ public override void Init(ITorchBase torch) else Log.Warn("No session manager loaded!"); - patchManager = DependencyProviderExtensions.GetManager(torch.Managers); + patchManager = torch.Managers.GetManager(); patchContext = patchManager.AcquireContext(); DrillPatch.Patch(patchContext); - RoleAgent.LoadAllConfigs(); - RoleAgent.LoadAllClasses(); - Save(); + await Save(); + await RoleAgent.LoadAllRoles();; // Give the system time to write and release the saved config file } private void DelayManagersOnElapsed(object sender, ElapsedEventArgs e) @@ -119,8 +121,6 @@ private void SessionChanged(ITorchSession session, TorchSessionState state) private async void PlayerDisconnected(ulong steamID, MyChatMemberStateChangeEnum myChatMemberStateChangeEnum) { // Unload them from the system, free up resources. - MyPlayer player = MySession.Static.Players.TryGetPlayerBySteamId(steamID); - if (!PlayerManagers.ContainsKey(steamID)) { Log.Error($"Unable to save profile for player [SteamID:{steamID}], it was probably not loaded."); @@ -142,13 +142,12 @@ private async void SaveAllPlayersForShutDown() private static void PlayerConnected(ulong steamID, string s) { - long playerId = Sync.Players.TryGetIdentityId(steamID); PlayerManager roleManager = new PlayerManager(); roleManager.InitAsync(steamID); PlayerManagers.TryAdd(steamID, roleManager); } - private void SetupConfig() + private async Task SetupConfig() { string dataPath = Path.Combine(StoragePath, "RPGPlugin"); string playerDataPath = Path.Combine(dataPath, "Player Data"); @@ -156,13 +155,6 @@ private void SetupConfig() // create directories if they do not exist Directory.CreateDirectory(dataPath); Directory.CreateDirectory(playerDataPath); - - // set the config file path - // we prefer xml over json because it is easier to edit for the user.. - // most users are not experienced with json format. xml is more familiar. - // the class config are fine as json as they really don't need to be edited by the user. - // any issues serializing to xml can be fixed by implementing a serializable version of - // what doesnt have ISerializable implemented. string configFile = Path.Combine(StoragePath, "RPGPluginConfig.xml"); if (!File.Exists(configFile)) @@ -179,7 +171,6 @@ private void SetupConfig() catch (Exception e) { Log.Warn(e); - } if (_config?.Data == null) @@ -203,24 +194,40 @@ private void SetupConfig() { Log.Warn(e); } - Save(); + await Save(); } } } - - - public void Save() + public Task Save() { try { _config.Save(); Log.Info("Main Configuration Saved."); + return Task.CompletedTask; } catch (IOException e) { Log.Warn(e, "Main Configuration Saved during plugin loading."); + return Task.FromException(e); } } + + private async Task WaitThenSetupClasses() + { + // Had issues were roles were loaded faster than the config was created... causes issues. + await Task.Run(async () => + { + while (Config == null) + { + // If the config is null, wait a second and try again. Repeat until config is loaded. + Thread.Sleep(1000); + } + + await RoleAgent.LoadAllRoles(); + }); + + } } } diff --git a/RPGPlugin/RPGPluginCommands.cs b/RPGPlugin/RPGPluginCommands.cs index d76bf6a..57abcbe 100644 --- a/RPGPlugin/RPGPluginCommands.cs +++ b/RPGPlugin/RPGPluginCommands.cs @@ -15,7 +15,7 @@ public class RolesCommands : CommandModule [Command("setrole", "Set your role")] [Permission(MyPromoteLevel.None)] - public void SetRole(string roleName) + public async void SetRole(string roleName) { if (Context.Player == null) { @@ -36,11 +36,8 @@ public void SetRole(string roleName) } // Check if the role is valid - if (!Roles.Instance.Config.RegisteredRoles.Any(r => r.Item1.Equals(roleName, StringComparison.OrdinalIgnoreCase))) + if (Roles.Instance.Config.RegisteredRoles.Any(r => r.Item1.Equals(roleName, StringComparison.OrdinalIgnoreCase))) { - - if (roleName != Roles.Instance.Config.RegisteredRoles[index].Item1) continue; - Roles.PlayerManagers[Context.Player.SteamUserId].SetRole(roleName); await Roles.PlayerManagers[Context.Player.SteamUserId].SavePlayerData(); Context.Respond($"Your role has been updated to [{roleName}]"); @@ -95,9 +92,9 @@ public void Stats() StringBuilder reply = new StringBuilder(); reply.AppendLine("*** Information ***"); - reply.AppendLine("—————————————————————————————"); + reply.AppendLine("—————————————————————————"); reply.AppendLine($"Current Role: {currentPlayerRole}"); - reply.AppendLine("—————————————————————————————"); + reply.AppendLine("—————————————————————————"); foreach (SerializableTuple role in Roles.Instance.Config.RegisteredRoles) { if (!Roles.PlayerManagers[Context.Player.SteamUserId]._PlayerData.ClassInfo.ContainsKey(role.Item1)) @@ -109,7 +106,7 @@ public void Stats() reply.AppendLine($"{role.Item1}:"); reply.AppendLine($"Current level: {Roles.PlayerManagers[Context.Player.SteamUserId]._PlayerData.ClassInfo[role.Item1].Item1}."); reply.AppendLine($"Exp needed for next level: {Roles.roles[role.Item1 + "Class"].ExpToLevelUp(Context.Player.SteamUserId).ToString()}."); - reply.AppendLine("—————————————————————————————"); + reply.AppendLine("—————————————————————————"); } Context.Respond(reply.ToString()); diff --git a/RPGPlugin/Roles/Behavior/WarriorClass.cs b/RPGPlugin/Roles/Behavior/WarriorClass.cs index 03af568..d8b703c 100644 --- a/RPGPlugin/Roles/Behavior/WarriorClass.cs +++ b/RPGPlugin/Roles/Behavior/WarriorClass.cs @@ -23,12 +23,10 @@ public class WarriorClass : ClassesBase { /// /// Point to your classConfig ExpRatio collection - public override ObservableCollection> ExpRatio { get; set; } = - new ObservableCollection>(); + public override ObservableCollection> ExpRatio { get; set; } = new ObservableCollection>(); //test skill point system - public override ObservableCollection> SkillPoints { get; set; } = - new ObservableCollection>(); + public override ObservableCollection> SkillPoints { get; set; } = new ObservableCollection>(); /// public override void init() @@ -47,32 +45,25 @@ private void DamageHandler(object target, ref MyDamageInformation info) // Deformation damage can be from ramming such as torpedo grids, or a warship hitting another. // Will do a sample here. Be warned... This requires allot of work and will become a bit long... :) - if (target is MyCubeBlock block) - { - // Get relation between grid and attacker - if (block.IDModule.GetUserRelationToOwner(info.AttackerId) != MyRelationsBetweenPlayerAndBlock.Friends) + if (!(target is MyCubeBlock block)) return; + + // Get relation between grid and attacker + if (block.IDModule.GetUserRelationToOwner(info.AttackerId) == MyRelationsBetweenPlayerAndBlock.Friends) return; + + if (block.CubeGrid.GridSizeEnum == MyCubeSize.Small) + _ProcessQueue.Enqueue(new ExperienceAction { - switch (block.CubeGrid.GridSizeEnum) - { - case MyCubeSize.Small: - _ProcessQueue.Enqueue( new ExperienceAction - { - ownerID = info.AttackerId, - subType = "EnemySmallBlock", - amount = info.Amount // This gives points per point of damage. - }); - break; - case MyCubeSize.Large: - _ProcessQueue.Enqueue( new ExperienceAction - { - ownerID = info.AttackerId, - subType = "EnemyLargeBlock", - amount = info.Amount // This gives points per point of damage. - }); - break; - } - } - } + ownerID = info.AttackerId, + subType = "EnemySmallBlock", + amount = info.Amount // This gives points per point of damage. + }); + else if (block.CubeGrid.GridSizeEnum == MyCubeSize.Large) + _ProcessQueue.Enqueue(new ExperienceAction + { + ownerID = info.AttackerId, + subType = "EnemyLargeBlock", + amount = info.Amount // This gives points per point of damage. + }); } /// diff --git a/RPGPlugin/Roles/Config/HunterConfig.cs b/RPGPlugin/Roles/Config/HunterConfig.cs index 933aaf2..cdbdc7d 100644 --- a/RPGPlugin/Roles/Config/HunterConfig.cs +++ b/RPGPlugin/Roles/Config/HunterConfig.cs @@ -1,15 +1,23 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; +using System.Threading; using System.Threading.Tasks; +using System.Windows.Controls; using Newtonsoft.Json; using RPGPlugin.Utils; +using RPGPlugin.View; namespace RPGPlugin { public sealed class HunterConfig : configBase { // Definition of the ExpRatio property, which stores experience point values for individual minerals + + /// + public override string ViewName { get; } = "Hunter"; + /// public override ObservableCollection> ExpRatio { get; set; } = new ObservableCollection>(); @@ -25,14 +33,25 @@ public override void init() ExpRatio.Add(new KeyValuePair("Spider", 0.0013 )); ExpRatio.Add(new KeyValuePair("SmallBlock", 2 )); ExpRatio.Add(new KeyValuePair("LargeBlock", 10 )); + + StaticHelperFunctions.StaThreadWrapper(() => + { + + Roles.Log.Warn($"HunterSTA Thread => {Thread.CurrentThread.ManagedThreadId}"); + UserControl classView = new Hunter(); + Roles.Instance.classViews.Add("Hunter", classView); + + }); + } public override void RegisterClass() { + // Register Class Info SerializableTuple RoleToRegister = new SerializableTuple{Item1 = "Hunter", Item2 = "Specialized in attacking NPC ships and creatures."}; - - if (!Roles.Instance.Config.RegisteredRoles.Contains(RoleToRegister)) - Roles.Instance.Config.RegisteredRoles.Add(RoleToRegister); + if (Roles.Instance.Config.RegisteredRoles.Any(role => role.Item1.Equals(RoleToRegister.Item1, StringComparison.OrdinalIgnoreCase))) return ; + Roles.Instance.Config.RegisteredRoles.Add(RoleToRegister); + Roles.Log.Warn($"Registered New Class: {RoleToRegister.Item1}"); } public override void LoadConfig() diff --git a/RPGPlugin/Roles/Config/MinerConfig.cs b/RPGPlugin/Roles/Config/MinerConfig.cs index 7e315c5..5f2fb09 100644 --- a/RPGPlugin/Roles/Config/MinerConfig.cs +++ b/RPGPlugin/Roles/Config/MinerConfig.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; using RPGPlugin.Utils; @@ -12,12 +13,13 @@ public sealed class MinerConfig : configBase // Definition of the ExpRatio property, which stores experience point values for individual minerals /// - public override ObservableCollection> ExpRatio { get; set; } = - new ObservableCollection>(); + public override string ViewName { get; } = "Miner"; + + /// + public override ObservableCollection> ExpRatio { get; set; } = new ObservableCollection>(); //test skill point system - public override ObservableCollection> SkillPoints { get; set; } = - new ObservableCollection>(); + public override ObservableCollection> SkillPoints { get; set; } = new ObservableCollection>(); public override void init() { @@ -39,9 +41,11 @@ public override void init() public override void RegisterClass() { SerializableTuple RoleToRegister = new SerializableTuple{Item1 = "Miner", Item2 = "Specialized in resource extraction."}; + + if (Roles.Instance.Config.RegisteredRoles.Any(Role => Role.Item1.Equals(RoleToRegister.Item1, StringComparison.OrdinalIgnoreCase))) return; - if (!Roles.Instance.Config.RegisteredRoles.Contains(RoleToRegister)) - Roles.Instance.Config.RegisteredRoles.Add(RoleToRegister); + Roles.Instance.Config.RegisteredRoles.Add(RoleToRegister); + Roles.Log.Warn($"Registered New Class: {RoleToRegister.Item1}"); } diff --git a/RPGPlugin/Roles/Config/SampleConfig.cs b/RPGPlugin/Roles/Config/SampleConfig.cs index 0d6060b..1f604e5 100644 --- a/RPGPlugin/Roles/Config/SampleConfig.cs +++ b/RPGPlugin/Roles/Config/SampleConfig.cs @@ -15,6 +15,9 @@ public class SampleConfig : configBase public override ObservableCollection> SkillPoints { get; set; } = new ObservableCollection>(); + /// + public override string ViewName { get; } = "Sample"; + public override void init() { // Always run this method first! diff --git a/RPGPlugin/Roles/Config/WarriorConfig.cs b/RPGPlugin/Roles/Config/WarriorConfig.cs index 4817896..e2573c6 100644 --- a/RPGPlugin/Roles/Config/WarriorConfig.cs +++ b/RPGPlugin/Roles/Config/WarriorConfig.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; using RPGPlugin.Utils; @@ -10,14 +11,17 @@ namespace RPGPlugin public sealed class WarriorConfig : configBase { // Definition of the ExpRatio property, which stores experience point values for individual minerals + + /// - public override ObservableCollection> ExpRatio { get; set; } = - new ObservableCollection>(); + public override ObservableCollection> ExpRatio { get; set; } = new ObservableCollection>(); //test skill point system - public override ObservableCollection> SkillPoints { get; set; } = - new ObservableCollection>(); + public override ObservableCollection> SkillPoints { get; set; } = new ObservableCollection>(); + /// + public override string ViewName { get; } = "Warrior"; + public override void init() { // Initialize class with default settings, will be replaced if a config file is loaded. @@ -25,10 +29,6 @@ public override void init() ExpRatio.Add(new KeyValuePair("EnemySmallBlock", 0.2 )); ExpRatio.Add(new KeyValuePair("EnemyLargeBlock", 0.8 )); - // 0 point values can be omitted. If its not in the list, its the same as 0 - // ExpRatio.Add(new KeyValuePair("FriendlySmallBlock", 0 )); - // ExpRatio.Add(new KeyValuePair("FriendlyLargeBlock", 0 )); - //test skill point system SkillPoints.Add(new KeyValuePair(2, 1)); } @@ -36,12 +36,11 @@ public override void init() public override void RegisterClass() { SerializableTuple RoleToRegister = new SerializableTuple{Item1 = "Warrior", Item2 = "Specialized in battles and destruction of other engineers things!."}; + + if (Roles.Instance.Config.RegisteredRoles.Any(Role => Role.Item1.Equals(RoleToRegister.Item1, StringComparison.OrdinalIgnoreCase))) return; - if (!Roles.Instance.Config.RegisteredRoles.Contains(RoleToRegister)) - Roles.Instance.Config.RegisteredRoles.Add(RoleToRegister); - else - Roles.Log.Warn("Warior Role already registered!"); - + Roles.Instance.Config.RegisteredRoles.Add(RoleToRegister); + Roles.Log.Warn($"Registered New Class: {RoleToRegister.Item1}"); } public override void LoadConfig() @@ -57,6 +56,5 @@ public override async Task SaveConfig() string jsonData = JsonConvert.SerializeObject(this, Formatting.Indented); await SaveConfig(jsonData); } - } } \ No newline at end of file diff --git a/RPGPlugin/Roles/View/Hunter.xaml b/RPGPlugin/Roles/View/Hunter.xaml new file mode 100644 index 0000000..35ca962 --- /dev/null +++ b/RPGPlugin/Roles/View/Hunter.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +