diff options
| author | AShifter <[email protected]> | 2017-06-05 09:49:46 -0600 |
|---|---|---|
| committer | AShifter <[email protected]> | 2017-06-05 09:49:46 -0600 |
| commit | 61c906e596145bbedd60725c6dcee68c34a27907 (patch) | |
| tree | cd7a00d501affe96028bfb21a8dec90c2ee63f2c /ShiftOS_TheReturn | |
| parent | 66ea2cf2fdeeaa025bd22961a0400423233c505d (diff) | |
| parent | 3e11eca70481841b6e2f2253d667944779cfd5fb (diff) | |
| download | shiftos_thereturn-61c906e596145bbedd60725c6dcee68c34a27907.tar.gz shiftos_thereturn-61c906e596145bbedd60725c6dcee68c34a27907.tar.bz2 shiftos_thereturn-61c906e596145bbedd60725c6dcee68c34a27907.zip | |
Merge remote-tracking branch 'refs/remotes/shiftos-game/master'
Diffstat (limited to 'ShiftOS_TheReturn')
29 files changed, 2405 insertions, 1553 deletions
diff --git a/ShiftOS_TheReturn/AppearanceManager.cs b/ShiftOS_TheReturn/AppearanceManager.cs index 4c1754e..7cf06c9 100644 --- a/ShiftOS_TheReturn/AppearanceManager.cs +++ b/ShiftOS_TheReturn/AppearanceManager.cs @@ -38,9 +38,7 @@ using static ShiftOS.Engine.SaveSystem; namespace ShiftOS.Engine { - /// <summary> - /// Provides functionality for managing windows within ShiftOS. - /// </summary> + // Provides functionality for managing windows within ShiftOS. public static class AppearanceManager { [Obsolete("Please use Localization.GetAllLanguages().")] @@ -49,12 +47,7 @@ namespace ShiftOS.Engine return Localization.GetAllLanguages(); } - /// <summary> - /// Sets the title text of the specified window. - /// </summary> - /// <param name="window">The window to modify</param> - /// <param name="title">The title text to use</param> - /// <exception cref="ArgumentNullException">Thrown if the window is null.</exception> + // Sets the title text of the specified window. public static void SetWindowTitle(IShiftOSWindow window, string title) { if (window == null) @@ -62,11 +55,13 @@ namespace ShiftOS.Engine winmgr.SetTitle(window, title); } + //HEY LETS FIND THE WINDOWS public static IEnumerable<Type> GetAllWindowTypes() { List<Type> types = new List<Type>(); foreach(var file in System.IO.Directory.GetFiles(Environment.CurrentDirectory)) { + // hey if a thing is an exe or a dll show up plz kthx if(file.EndsWith(".exe") || file.EndsWith(".dll")) { try @@ -84,12 +79,7 @@ namespace ShiftOS.Engine return types; } - /// <summary> - /// Returns the default window title for a specified <see cref="IShiftOSWindow"/>-inheriting type. - /// </summary> - /// <param name="winType">The type to scan</param> - /// <returns>The default title</returns> - /// <exception cref="ArgumentNullException">Thrown if <paramref name="winType"/> is null.</exception> + // hey you know that window we just made appear? well give it its title public static string GetDefaultTitle(Type winType) { if (winType == null) @@ -104,257 +94,155 @@ namespace ShiftOS.Engine return winType.Name; } - /// <summary> - /// Current cursor position of the console - /// </summary> + // Current cursor position of the console public static int CurrentPosition { get; set; } - /// <summary> - /// We don't know what this does. It may be gone if it does nothing. - /// </summary> + // We don't know what this does. It may be gone if it does nothing. public static int LastLength { get; set; } - /// <summary> - /// Minimize a window. - /// </summary> - /// <param name="form">The window border to minimize.</param> - /// <exception cref="ArgumentNullException">Thrown if <paramref name="form"/> is null.</exception> - /// <exception cref="EngineModuleDisabledException">Thrown if this part of the engine hasn't been enabled.</exception> + // Minimize a window. public static void Minimize(IWindowBorder form) { if (form == null) + //FUCK WHY THE FUCK IS THIS NULL throw new ArgumentNullException("form"); if (winmgr == null) + //FUCK THIS PART OF THE ENGINE WASNT TURNED ON YET throw new EngineModuleDisabledException(); winmgr.Minimize(form); } - /// <summary> - /// Maximizes a window. - /// </summary> - /// <param name="form">The window border to maximize.</param> - /// <exception cref="ArgumentNullException">Thrown if <paramref name="form"/> is null.</exception> - /// <exception cref="EngineModuleDisabledException">Thrown if this engine module hasn't been enabled.</exception> + // Maximizes a window! :D public static void Maximize(IWindowBorder form) { if (form == null) + //AHHHH SHOULDNT BE NULLLLLL throw new ArgumentNullException("form"); if (winmgr == null) + //WHY ARE YOU DOING THIS PART OF THE ENGINE IT WASNT ENABLED FUCK throw new EngineModuleDisabledException(); winmgr.Maximize(form); } - /// <summary> - /// Provides a list of all open ShiftOS windows. - /// </summary> + // Provides a list of all open ShiftOS windows. public static List<IWindowBorder> OpenForms = new List<IWindowBorder>(); - /// <summary> - /// Decorates a window with a border, then shows the window. - /// </summary> - /// <param name="form">The window to decorate and show.</param> - /// <exception cref="ArgumentNullException">Thrown if <paramref name="form"/> is null. </exception> - /// <exception cref="EngineModuleDisabledException">Thrown if this engine module has not been initiated yet.</exception> + // Decorates a window with a border, then shows the window. public static void SetupWindow(IShiftOSWindow form) { if (form == null) + //YOU GET THE POINT THIS REALLY SHOULDNT BE NULL throw new ArgumentNullException("form"); if (winmgr == null) + //SAME HERE throw new EngineModuleDisabledException(); winmgr.SetupWindow(form); Desktop.ResetPanelButtons(); } - /// <summary> - /// Closes the specified window. - /// </summary> - /// <param name="win">The window to close.</param> - /// <exception cref="ArgumentNullException">Thrown if <paramref name="win"/> is null. </exception> - /// <exception cref="EngineModuleDisabledException">Thrown if this engine module has not been initiated yet.</exception> + // Closes the specified window. SHOCKED YOU ARE I KNOW, HOW COULD YOU HAVE GUESSED public static void Close(IShiftOSWindow win) { if (win == null) + //NOPE SHOULDNT BE NULL throw new ArgumentNullException("win"); if (winmgr == null) + //WHY IS THIS NULL throw new EngineModuleDisabledException(); winmgr.Close(win); Desktop.ResetPanelButtons(); } - /// <summary> - /// Decorates a window with a border, then shows the window, as a dialog box. - /// </summary> - /// <param name="form">The window to decorate and show.</param> - /// <exception cref="ArgumentNullException">Thrown if <paramref name="form"/> is null. </exception> - /// <exception cref="EngineModuleDisabledException">Thrown if this engine module has not been initiated yet.</exception> + + // Decorates a window with a border, then shows the window, as a dialog box. public static void SetupDialog(IShiftOSWindow form) { if (form == null) + //NULLLLLLLLL throw new ArgumentNullException("form"); if (winmgr == null) + //ASGDFHASDGF throw new EngineModuleDisabledException(); winmgr.SetupDialog(form); Desktop.ResetPanelButtons(); } - /// <summary> - /// The underlying window manager for this engine module - /// </summary> + // The underlying window manager for this engine module private static WindowManager winmgr = null; - /// <summary> - /// Initiate this engine module, and perform mandatory configuration. - /// </summary> - /// <param name="mgr">A working, configured <see cref="WindowManager"/> to use as a backend for this module </param> + // Initiate this engine module, and perform mandatory configuration. public static void Initiate(WindowManager mgr) { - winmgr = mgr; + winmgr = mgr; // A working, configured window manager to use as a backend for this module } - /// <summary> - /// Raised when the engine is entering its shutdown phase. Save your work! - /// </summary> + + // Raised when the engine is entering its shutdown phase. Save your work! public static event EmptyEventHandler OnExit; - /// <summary> - /// Starts the engine's exit routine, firing the OnExit event. - /// </summary> + // Starts the engine's exit routine, firing the OnExit event. internal static void Exit() { OnExit?.Invoke(); //disconnect from MUD ServerManager.Disconnect(); + Desktop.InvokeOnWorkerThread(() => + { + Process.GetCurrentProcess().Kill(); //bye bye + }); } - /// <summary> - /// The current terminal body control. - /// </summary> + // The current terminal body control. public static ITerminalWidget ConsoleOut { get; set; } - /// <summary> - /// Redirects the .NET <see cref="Console"/> to a new <see cref="TerminalTextWriter"/> instance. - /// </summary> + // Redirects the .NET to a new TerminalTextWriter instance. public static void StartConsoleOut() { - Console.SetOut(new TerminalTextWriter()); + Console.SetOut(new TerminalTextWriter()); //"plz start writing text .NET kthx" } - /// <summary> - /// Invokes an action on the window management thread. - /// </summary> - /// <param name="act">The action to invoke</param> + // Invokes an action on the window management thread. public static void Invoke(Action act) { winmgr.InvokeAction(act); } } - /// <summary> - /// Provides the base functionality for a ShiftOS terminal. - /// </summary> + // Provides the base functionality for a ShiftOS terminal. public interface ITerminalWidget { - /// <summary> - /// Write text to this Terminal. - /// </summary> - /// <param name="text">Text to write</param> - void Write(string text); - /// <summary> - /// Write text to this Terminal, followed by a newline. - /// </summary> - /// <param name="text">Text to write.</param> - void WriteLine(string text); - /// <summary> - /// Clear the contents of this Terminal. - /// </summary> - void Clear(); - /// <summary> - /// Move the cursor to the last character in the Terminal. - /// </summary> - void SelectBottom(); + void Write(string text); // Actually write text to this Terminal! :D:D:D:D + void WriteLine(string text); // Write text to this Terminal, followed by a newline. + void Clear(); // Clear the contents of this Terminal, i bet you wouldve never guessed that + void SelectBottom(); // Move the cursor to the last character in the Terminal. } - /// <summary> - /// Provides the base functionality for a ShiftOS window manager. - /// </summary> + // makes the window manager actually do its job public abstract class WindowManager { - /// <summary> - /// Minimizes a window - /// </summary> - /// <param name="border">The window border to minimize</param> - public abstract void Minimize(IWindowBorder border); - - /// <summary> - /// Maximizes a window - /// </summary> - /// <param name="border">The window border to maximize</param> - public abstract void Maximize(IWindowBorder border); - - /// <summary> - /// Closes a window - /// </summary> - /// <param name="win">The window to close</param> - public abstract void Close(IShiftOSWindow win); - - /// <summary> - /// Decorates a window with a window border, then shows it to the user. - /// </summary> - /// <param name="win">The window to decorate.</param> - public abstract void SetupWindow(IShiftOSWindow win); - - /// <summary> - /// Decorates a window with a border, then shows it to the user as a dialog box. - /// </summary> - /// <param name="win">The window to decorate</param> - public abstract void SetupDialog(IShiftOSWindow win); - - /// <summary> - /// Invokes an action on the window management thread. - /// </summary> - /// <param name="act">The action to invoke.</param> - public abstract void InvokeAction(Action act); - - /// <summary> - /// Sets the title text of a window. - /// </summary> - /// <param name="win">The window to modify.</param> - /// <param name="title">The new title text.</param> - public abstract void SetTitle(IShiftOSWindow win, string title); + public abstract void Minimize(IWindowBorder border); // guess what this does + public abstract void Maximize(IWindowBorder border); // ooh this too + public abstract void Close(IShiftOSWindow win); // omg this probably does something + public abstract void SetupWindow(IShiftOSWindow win); // i cant think of what this does + public abstract void SetupDialog(IShiftOSWindow win); // how about this??????? + public abstract void InvokeAction(Action act); // i wonder what this invokes + public abstract void SetTitle(IShiftOSWindow win, string title); // what is a title again } - /// <summary> - /// Provides the base functionality for a typical ShiftOS window border. - /// </summary> + // Provides the base functionality for a typical ShiftOS window border, what did you expect public interface IWindowBorder { - /// <summary> - /// Closes the border along with its window. Unload events should be invoked here. - /// </summary> - void Close(); - - /// <summary> - /// Gets or sets the title text for the window border. - /// </summary> - string Text { get; set; } - - /// <summary> - /// Gets or sets the underlying <see cref="IShiftOSWindow"/> for this border. - /// </summary> - IShiftOSWindow ParentWindow { get; set; } + void Close(); // CLOSES THE BORDER ALONG WITH ITS WINDOW!!!!!!! HOLY SHIT I DIDNT EXPECT THAT + string Text { get; set; } // title text exists now + IShiftOSWindow ParentWindow { get; set; } // Gets or sets the underlying for this border. } - - /// <summary> - /// Provides a way of setting default title text for <see cref="IShiftOSWindow"/> classes. - /// </summary> + + // Provides a way of setting default title text for classes. public class DefaultTitleAttribute : Attribute { - /// <summary> - /// Creates a new instance of the <see cref="DefaultTitleAttribute"/>. - /// </summary> - /// <param name="title">A default title to associate with this attribute.</param> + // oy if you cant find a title this is the one you should use public DefaultTitleAttribute(string title) { Title = title; @@ -363,17 +251,13 @@ namespace ShiftOS.Engine public string Title { get; private set; } } - /// <summary> - /// An exception that is thrown when mandatory configuration to run a specific method or module hasn't been done yet. - /// </summary> + // An exception that is thrown when mandatory configuration to run a specific method or module hasn't been done yet. public class EngineModuleDisabledException : Exception { - /// <summary> - /// Initializes a new instance of the <see cref="EngineModuleDisabledException"/>. - /// </summary> + // FUCK WE DIDNT ORDER THINGS RIGHT public EngineModuleDisabledException() : base("This engine module has not yet been enabled.") { - + //FUCK } } } diff --git a/ShiftOS_TheReturn/AudioManager.cs b/ShiftOS_TheReturn/AudioManager.cs index a636497..0a1a210 100644 --- a/ShiftOS_TheReturn/AudioManager.cs +++ b/ShiftOS_TheReturn/AudioManager.cs @@ -47,9 +47,12 @@ namespace ShiftOS.Engine /// </summary> public static void Stop() { - _out?.Stop(); - _reader?.Dispose(); - _out?.Dispose(); + Desktop.InvokeOnWorkerThread(() => + { + _out?.Stop(); + _reader?.Dispose(); + _out?.Dispose(); + }); } /// <summary> @@ -80,16 +83,29 @@ namespace ShiftOS.Engine /// <param name="file">The file to play.</param> public static void Play(string file) { - try + bool play = true; + float volume = 1f; + if (SaveSystem.CurrentSave != null) { - _reader = new AudioFileReader(file); - _out = new WaveOut(); - _out.Init(_reader); - _out.Volume = _provider.Volume; - _out.Play(); - _out.PlaybackStopped += (o, a) => { PlayCompleted?.Invoke(); }; + play = (SaveSystem.CurrentSave.SoundEnabled); + volume = (float)SaveSystem.CurrentSave.MusicVolume / 100f; + } + if (play) + { + try + { + _reader = new AudioFileReader(file); + _out = new WaveOut(); + _out.Init(_reader); + _out.Volume = volume; + _out.Play(); + _out.PlaybackStopped += (o, a) => { PlayCompleted?.Invoke(); }; + } + catch (Exception ex) + { + Console.WriteLine("Audio error: " + ex.Message); + } } - catch { } } /// <summary> @@ -98,15 +114,27 @@ namespace ShiftOS.Engine /// <param name="str">The stream to read from.</param> public static void PlayStream(Stream str) { - var bytes = new byte[str.Length]; - str.Read(bytes, 0, bytes.Length); - ShiftOS.Engine.AudioManager.Stop(); - if (File.Exists("snd.wav")) - File.Delete("snd.wav"); - File.WriteAllBytes("snd.wav", bytes); - - ShiftOS.Engine.AudioManager.Play("snd.wav"); - + try + { + bool play = true; + float volume = 1f; + if (SaveSystem.CurrentSave != null) + { + play = (SaveSystem.CurrentSave.SoundEnabled); + volume = (float)SaveSystem.CurrentSave.MusicVolume / 100f; + } + if (play) + { + ShiftOS.Engine.AudioManager.Stop(); + _out = new WaveOut(); + var mp3 = new WaveFileReader(str); + _out.Init(mp3); + _out.Volume = volume; + _out.Play(); + _out.PlaybackStopped += (o, a) => { PlayCompleted?.Invoke(); }; + } + } + catch { } } public static event Action PlayCompleted; diff --git a/ShiftOS_TheReturn/Commands.cs b/ShiftOS_TheReturn/Commands.cs index 57d1d34..f37bcb3 100644 --- a/ShiftOS_TheReturn/Commands.cs +++ b/ShiftOS_TheReturn/Commands.cs @@ -95,7 +95,7 @@ namespace ShiftOS.Engine { TerminalBackend.IsForwardingConsoleWrites = forwarding; TerminalBackend.ForwardGUID = (forwarding == true) ? fGuid : null; - Console.WriteLine($"{SaveSystem.CurrentSave.Username} says \"{result}\"."); + Console.WriteLine($"{SaveSystem.CurrentUser.Username} says \"{result}\"."); TerminalBackend.IsForwardingConsoleWrites = false; }; Desktop.InvokeOnWorkerThread(new Action(() => @@ -233,6 +233,23 @@ namespace ShiftOS.Engine [Namespace("dev")] public static class ShiftOSDevCommands { + [Command("buy")] + public static bool UnlockUpgrade(Dictionary<string, object> args) + { + string upg = args["id"].ToString(); + try + { + SaveSystem.CurrentSave.Upgrades[upg] = true; + Shiftorium.InvokeUpgradeInstalled(); + SaveSystem.SaveGame(); + } + catch + { + Console.WriteLine("Upgrade not found."); + } + return true; + } + [Command("rock", description = "A little surprise for unstable builds...")] public static bool ThrowASandwichingRock() { @@ -276,6 +293,17 @@ namespace ShiftOS.Engine return true; } + [Command("restart")] + public static bool Restart() + { + SaveSystem.CurrentSave.Upgrades = new Dictionary<string, bool>(); + SaveSystem.CurrentSave.Codepoints = 0; + SaveSystem.CurrentSave.StoriesExperienced.Clear(); + SaveSystem.CurrentSave.StoriesExperienced.Add("mud_fundamentals"); + SaveSystem.SaveGame(); + Shiftorium.InvokeUpgradeInstalled(); + return true; + } [Command("freecp")] public static bool FreeCodepoints(Dictionary<string, object> args) @@ -283,8 +311,8 @@ namespace ShiftOS.Engine if (args.ContainsKey("amount")) try { - Int64 codepointsToAdd = Convert.ToInt64(args["amount"].ToString()); - SaveSystem.TransferCodepointsFrom("dev", codepointsToAdd); + ulong codepointsToAdd = Convert.ToUInt64(args["amount"].ToString()); + SaveSystem.CurrentSave.Codepoints += codepointsToAdd; return true; } catch (Exception ex) @@ -293,7 +321,7 @@ namespace ShiftOS.Engine return true; } - SaveSystem.TransferCodepointsFrom("dev", 1000); + SaveSystem.CurrentSave.Codepoints += 1000; return true; } @@ -302,8 +330,13 @@ namespace ShiftOS.Engine { foreach (var upg in Shiftorium.GetDefaults()) { - Shiftorium.Buy(upg.ID, 0); + if (!SaveSystem.CurrentSave.Upgrades.ContainsKey(upg.ID)) + SaveSystem.CurrentSave.Upgrades.Add(upg.ID, true); + else + SaveSystem.CurrentSave.Upgrades[upg.ID] = true; } + Shiftorium.InvokeUpgradeInstalled(); + SkinEngine.LoadSkin(); return true; } @@ -350,12 +383,67 @@ namespace ShiftOS.Engine [Namespace("sos")] public static class ShiftOSCommands { + + [Command("setsfxenabled", description = "Set whether or not sound effects are enabled in the system.")] + [RequiresArgument("value")] + public static bool SetSfxEnabled(Dictionary<string, object> args) + { + try + { + bool value = Convert.ToBoolean(args["value"].ToString()); + SaveSystem.CurrentSave.SoundEnabled = value; + SaveSystem.SaveGame(); + } + catch + { + Console.WriteLine("Error: Value must be either true or false."); + } + return true; + } + + + + [Command("setmusicenabled", description = "Set whether or not music is enabled in the system.")] + [RequiresArgument("value")] + public static bool SetMusicEnabled(Dictionary<string, object> args) + { + try + { + bool value = Convert.ToBoolean(args["value"].ToString()); + SaveSystem.CurrentSave.MusicEnabled = value; + SaveSystem.SaveGame(); + } + catch + { + Console.WriteLine("Error: Value must be either true or false."); + } + return true; + } + + + + [Command("setsfxvolume", description ="Set the system sound volume to a value between 1 and 100.")] + [RequiresArgument("value")] + public static bool SetSfxVolume(Dictionary<string, object> args) + { + int value = int.Parse(args["value"].ToString()); + if(value >= 0 && value <= 100) + { + SaveSystem.CurrentSave.MusicVolume = value; + SaveSystem.SaveGame(); + } + else + { + Console.WriteLine("Volume must be between 0 and 100!"); + } + return true; + } + [RemoteLock] [Command("shutdown")] public static bool Shutdown() { TerminalBackend.InvokeCommand("sos.save"); - SaveSystem.ShuttingDown = true; AppearanceManager.Exit(); return true; } @@ -389,85 +477,43 @@ namespace ShiftOS.Engine } } - [Command("help", "{COMMAND_HELP_USAGE}", "{COMMAND_HELP_DESCRIPTION}")] - public static bool Help() + [Command("help", "{COMMAND_HELP_USAGE", "{COMMAND_HELP_DESCRIPTION}")] + public static bool Help(Dictionary<string, object> args) { - foreach (var exec in System.IO.Directory.GetFiles(Environment.CurrentDirectory)) + var sb = new StringBuilder(); + sb.AppendLine("Retrieving help data."); + + if (args.ContainsKey("ns")) { - if (exec.EndsWith(".exe") || exec.EndsWith(".dll")) + string ns = args["ns"].ToString(); + //First let's check for a command that has this namespace. + var cmdtest = TerminalBackend.Commands.FirstOrDefault(x => x.NamespaceInfo.name == ns); + if (cmdtest == null) //Namespace not found. + sb.AppendLine("Error retrieving help for namespace \"" + ns + "\". Namespace not found."); + else { - try + //Now do the actual scan. + sb.AppendLine("Namespace: " + ns); + foreach(var cmd in TerminalBackend.Commands.Where(x => x.NamespaceInfo.name == ns)) { - var asm = Assembly.LoadFile(exec); - - var types = asm.GetTypes(); - - foreach (var type in types) - { - if (Shiftorium.UpgradeAttributesUnlocked(type)) - { - foreach (var a in type.GetCustomAttributes(false)) - { - if (a is Namespace) - { - var ns = a as Namespace; - - if (!ns.hide) - { - string descp = "{NAMESPACE_" + ns.name.ToUpper() + "_DESCRIPTION}"; - if (descp == Localization.Parse(descp)) - descp = ""; - else - descp = Shiftorium.UpgradeInstalled("help_description") ? Localization.Parse("{SEPERATOR}" + descp) : ""; - - Console.WriteLine($"{{NAMESPACE}}{ns.name}" + descp); - - foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Static)) - { - if (Shiftorium.UpgradeAttributesUnlocked(method)) - { - foreach (var ma in method.GetCustomAttributes(false)) - { - if (ma is Command) - { - var cmd = ma as Command; - - if (!cmd.hide) - { - string descriptionparse = "{COMMAND_" + ns.name.ToUpper() + "_" + cmd.name.ToUpper() + "_DESCRIPTION}"; - string usageparse = "{COMMAND_" + ns.name.ToUpper() + "_" + cmd.name.ToUpper() + "_USAGE}"; - if (descriptionparse == Localization.Parse(descriptionparse)) - descriptionparse = ""; - else - descriptionparse = Shiftorium.UpgradeInstalled("help_description") ? Localization.Parse("{SEPERATOR}" + descriptionparse) : ""; - - if (usageparse == Localization.Parse(usageparse)) - usageparse = ""; - else - usageparse = Shiftorium.UpgradeInstalled("help_usage") ? Localization.Parse("{SEPERATOR}" + usageparse, new Dictionary<string, string>() { - {"%ns", ns.name}, - {"%cmd", cmd.name} - }) : ""; - - Console.WriteLine($"{{COMMAND}}{ns.name}.{cmd.name}" + usageparse + descriptionparse); - } - } - } - } - - } - } - - } - } - } - } - + string str = cmd.ToString(); + str = str.Replace(str.Substring(str.LastIndexOf("|")), ""); + sb.AppendLine(str); } - catch { } + } + } + else + { + + //print all unique namespaces. + foreach(var n in TerminalBackend.Commands.Select(x => x.NamespaceInfo.name).Distinct()) + { + sb.AppendLine("sos.help{ns:\"" + n + "\"}"); } } + Console.WriteLine(sb.ToString()); + return true; } @@ -487,11 +533,33 @@ namespace ShiftOS.Engine Codepoints: {SaveSystem.CurrentSave.Codepoints} Upgrades: {SaveSystem.CurrentSave.CountUpgrades()} installed, - {Shiftorium.GetAvailable().Length} available"; + {Shiftorium.GetAvailable().Length} available + +"; - if (Shiftorium.UpgradeInstalled("mud_control_centre")) - status += Environment.NewLine + $"Reputation: {SaveSystem.CurrentSave.RawReputation} ({SaveSystem.CurrentSave.Reputation})"; Console.WriteLine(status); + Console.WriteLine("Objectives:"); + try + { + if (Story.CurrentObjectives.Count > 0) + { + foreach (var obj in Story.CurrentObjectives) + { + Console.WriteLine(obj.Name); + Console.WriteLine("-------------------------------"); + Console.WriteLine(); + Console.WriteLine(obj.Description); + } + } + else + { + Console.WriteLine(" - No objectives are running. Check back later!"); + } + } + catch + { + Console.WriteLine(" - No objectives are running. Check back later!"); + } return true; } } @@ -615,7 +683,7 @@ shiftorium.buy{{upgrade:""{upg.ID}""}}"); cat = args["cat"].ToString(); } - Dictionary<string, long> upgrades = new Dictionary<string, long>(); + Dictionary<string, ulong> upgrades = new Dictionary<string, ulong>(); int maxLength = 5; IEnumerable<ShiftoriumUpgrade> upglist = Shiftorium.GetAvailable(); diff --git a/ShiftOS_TheReturn/ConsoleEx.cs b/ShiftOS_TheReturn/ConsoleEx.cs index 90f9cc0..74483dc 100644 --- a/ShiftOS_TheReturn/ConsoleEx.cs +++ b/ShiftOS_TheReturn/ConsoleEx.cs @@ -48,5 +48,12 @@ namespace ShiftOS.Engine /// Gets or sets whether text in the Terminal is underlined. /// </summary> public static bool Underline { get; set; } + + internal static void Flush() + { + OnFlush?.Invoke(); + } + + public static Action OnFlush; } } diff --git a/ShiftOS_TheReturn/CrashHandler.cs b/ShiftOS_TheReturn/CrashHandler.cs index ed42ea5..48eaf1f 100644 --- a/ShiftOS_TheReturn/CrashHandler.cs +++ b/ShiftOS_TheReturn/CrashHandler.cs @@ -41,38 +41,41 @@ namespace ShiftOS.Engine { public class GetHardwareInfo { + // returns the processor's name for the crash public static string GetProcessorName() { - string ProcessorName = ""; + string ProcessorName = ""; // put the processors name in here sometime later ManagementObjectSearcher mos - = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Processor"); + = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Processor"); // OI CPU TELL ME YOUR NAME PLZ foreach (ManagementObject mo in mos.Get()) - ProcessorName = mo["Name"].ToString(); + ProcessorName = mo["Name"].ToString(); // see told you it puts in it there return ProcessorName; } + // same as above but instead for the gpu's name for the crash as well public static string GetGPUName() { string GPUName = ""; ManagementObjectSearcher mos - = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_VideoController"); + = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_VideoController"); //now can you tell me your name cpu kthx foreach (ManagementObject mo in mos.Get()) GPUName = mo["Name"].ToString(); return GPUName; } + // oh wow even more same, but this time its RAM AMOUNT oooh nice public static string GetRAMAmount() { var RAMAmount = ""; ManagementObjectSearcher mos - = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PhysicalMemory"); + = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PhysicalMemory"); //ram you too how much of you exists foreach (ManagementObject mo in mos.Get()) RAMAmount = mo["Capacity"].ToString(); - RAMAmount = (RAMAmount + " B"); + RAMAmount = (RAMAmount + " B"); // ooh and now we add "bytes" to the end return RAMAmount; } @@ -81,22 +84,24 @@ namespace ShiftOS.Engine public partial class CrashHandler : Form { + //fuck it crashed public CrashHandler() { InitializeComponent(); - //Send the bug to Debugle - // or alternatively, send to [email protected] OR [email protected] + //Send the bug to Unite as a bug report + // or alternatively, send to [email protected] + // or just on the discord that works too } - public static Exception HandledException = null; + public static Exception HandledException = null; // this value determines if we can try to set the game back on track or we cant do anything about it public static void Start(Exception e) { if(SaveSystem.CurrentSave != null) - TerminalBackend.InvokeCommand("sos.save"); + TerminalBackend.InvokeCommand("sos.save"); // SAVE BEFORE CRASHING ServerManager.Disconnect(); while (Application.OpenForms.Count > 0) @@ -108,6 +113,7 @@ namespace ShiftOS.Engine System.IO.FileInfo fileInfo = new System.IO.FileInfo(assembly.Location); DateTime lastModified = fileInfo.LastWriteTime; + // put all this in a text document string rtbcrash_Text = $@" === {AssemblyName} has crashed. === Game: {AssemblyName} @@ -179,11 +185,11 @@ Stack trace: } - File.WriteAllText("crash.txt", rtbcrash_Text); + File.WriteAllText("crash.txt", rtbcrash_Text); // make that text document and put above super long string in it var result = MessageBox.Show(caption: "ShiftOS - Fatal error", text: "ShiftOS has encountered a fatal error and has been shut down. Info about the error has been saved to a file called crash.txt in the same folder as the active executable. Would you like to try and recover the game session?", buttons: MessageBoxButtons.YesNo); if(result == DialogResult.Yes) { - Application.Restart(); + Application.Restart(); // tries to restart if user clicks yes, who wouldve guessed } } @@ -197,18 +203,20 @@ Stack trace: this.Close(); Application.Restart(); } - + + // make both of those variables that appear in the long string above public static string AssemblyName { get; private set; } public static string AssemblyDescription { get; private set; } + // get info about the game itself public static void SetGameMetadata(Assembly assembly) { - AssemblyName = assembly.GetName().Name; + AssemblyName = assembly.GetName().Name; // name of game foreach(var attr in assembly.GetCustomAttributes(true)) { if(attr is AssemblyDescriptionAttribute) { - AssemblyDescription = (attr as AssemblyDescriptionAttribute).Description; + AssemblyDescription = (attr as AssemblyDescriptionAttribute).Description; // description of the game } } diff --git a/ShiftOS_TheReturn/Desktop.cs b/ShiftOS_TheReturn/Desktop.cs index bc17a8e..a5e7f43 100644 --- a/ShiftOS_TheReturn/Desktop.cs +++ b/ShiftOS_TheReturn/Desktop.cs @@ -102,7 +102,14 @@ namespace ShiftOS.Engine /// Gets the name of the desktop. /// </summary> string DesktopName { get; } - + + /// <summary> + /// Show a notification on the desktop. + /// </summary> + /// <param name="app">An application ID (for determining what system icon to show the notification alongside)</param> + /// <param name="title">The title of the notification.</param> + /// <param name="message">Isn't this.... self explanatory?</param> + void PushNotification(string app, string title, string message); /// <summary> /// Performs most of the skinning and layout handling for the desktop. @@ -266,6 +273,14 @@ namespace ShiftOS.Engine { _desktop.HideAppLauncher(); } + + public static void PushNotification(string app, string title, string msg) + { + InvokeOnWorkerThread(() => + { + _desktop.PushNotification(app, title, msg); + }); + } } // sorry i almost killed everything :P } diff --git a/ShiftOS_TheReturn/IStatusIcon.cs b/ShiftOS_TheReturn/IStatusIcon.cs new file mode 100644 index 0000000..f32d1c1 --- /dev/null +++ b/ShiftOS_TheReturn/IStatusIcon.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShiftOS.Engine +{ + public interface IStatusIcon + { + void Setup(); + + } +} diff --git a/ShiftOS_TheReturn/KernelWatchdog.cs b/ShiftOS_TheReturn/KernelWatchdog.cs index 430d36a..0608c46 100644 --- a/ShiftOS_TheReturn/KernelWatchdog.cs +++ b/ShiftOS_TheReturn/KernelWatchdog.cs @@ -10,6 +10,7 @@ namespace ShiftOS.Engine { public static class KernelWatchdog { + //store logs into a file public static void Log(string e, string desc) { string line = $"[{DateTime.Now}] <{e}> {desc}"; @@ -36,12 +37,13 @@ namespace ShiftOS.Engine } set { - if(value == false) + if(value == false) // hey game if you want to disconnect from mud do this: { foreach(var win in AppearanceManager.OpenForms) { foreach(var attr in win.ParentWindow.GetType().GetCustomAttributes(true)) { + // prevents disconnect from mud if an application that needs a connection is open if(attr is MultiplayerOnlyAttribute) { ConsoleEx.Bold = true; @@ -59,63 +61,44 @@ namespace ShiftOS.Engine } } - _mudConnected = value; + _mudConnected = value; // connects or disconnects from mud Desktop.PopulateAppLauncher(); } } - public static bool IsSafe(Type type) + public static bool IsSafe(TerminalBackend.TerminalCommand cmd) { - if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Root) + if (!cmd.RequiresElevation) return true; - - foreach (var attrib in type.GetCustomAttributes(false)) + else { - if (attrib is KernelModeAttribute) - { - if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Root) - return true; + if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Root) + return true; + else return false; - } } - return true; } - public static bool IsSafe(MethodInfo type) - { - if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Root) - return true; - - foreach (var attrib in type.GetCustomAttributes(false)) - { - if (attrib is KernelModeAttribute) - { - if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Root) - return true; - return false; - } - } - return true; - } - static string regularUsername = ""; + static string regularUsername = ""; //put regular username in here later public static void EnterKernelMode() { - regularUsername = SaveSystem.CurrentUser.Username; - SaveSystem.CurrentUser = SaveSystem.Users.FirstOrDefault(x => x.Username == "root"); + regularUsername = SaveSystem.CurrentUser.Username; // k for now put user's username in here for the time being + SaveSystem.CurrentUser = SaveSystem.Users.FirstOrDefault(x => x.Username == "root"); // now their username is root } public static void LeaveKernelMode() { - var user = SaveSystem.Users.FirstOrDefault(x => x.Username == regularUsername); - if (user == null) - throw new Exception("User not in root mode."); + var user = SaveSystem.Users.FirstOrDefault(x => x.Username == regularUsername); //finds username + if (user == null) + throw new Exception("User not in root mode."); // fuck this means the user isnt root quick throw error SaveSystem.CurrentUser = user; } + //determines if you can disconnect from mud if there are no applications that currently need to internal static bool CanRunOffline(Type method) { if (MudConnected) @@ -128,7 +111,8 @@ namespace ShiftOS.Engine } return true; } - + + //same as above but this time for methods internal static bool CanRunOffline(MethodInfo method) { if (MudConnected) diff --git a/ShiftOS_TheReturn/Localization.cs b/ShiftOS_TheReturn/Localization.cs index c1a6bd6..8adfa5a 100644 --- a/ShiftOS_TheReturn/Localization.cs +++ b/ShiftOS_TheReturn/Localization.cs @@ -33,6 +33,7 @@ using System.Threading.Tasks; namespace ShiftOS.Engine { + //define a whole bunch of things that are needed public interface ILanguageProvider { List<string> GetJSONTranscripts(); @@ -51,14 +52,15 @@ namespace ShiftOS.Engine { if(_provider == null) { - return JsonConvert.DeserializeObject<string[]>(Properties.Resources.languages); + return JsonConvert.DeserializeObject<string[]>(Properties.Resources.languages); //collect all the languages availible } else { - return _provider.GetAllLanguages(); + return _provider.GetAllLanguages(); //also collect all the languages avalible but from a specific provider this time } } + //if no local selected, english will be loaded public static void SetupTHETRUEDefaultLocals() { if (_provider == null) @@ -79,6 +81,7 @@ namespace ShiftOS.Engine } } + // ignore this not really setup of default no no zone public static void SetupDefaultLocals(string lines, string path) { Utils.WriteAllText(Paths.GetPath(path), lines); @@ -86,13 +89,8 @@ namespace ShiftOS.Engine } - /// <summary> - /// Takes in a string and parses localization blocks into text blocks in the current language. - /// </summary> - /// <example>"{CODEPOINTS}: 0" will come out as "Codepoints: 0" if the current language is english.</example> - /// <param name="original">The string to parse</param> - /// <returns>The parsed string.</returns> - /// + // Takes in a string and parses localization blocks into text blocks in the current language. + // example: "{CODEPOINTS}: 0" will come out as "Codepoints: 0" if the current language is english public static string Parse(string original) { return Parse(original, new Dictionary<string, string>()); @@ -104,103 +102,85 @@ namespace ShiftOS.Engine Dictionary<string, string> localizationStrings = new Dictionary<string, string>(); - try { localizationStrings = JsonConvert.DeserializeObject<Dictionary<string, string>>(_provider.GetCurrentTranscript()); } catch { - localizationStrings = JsonConvert.DeserializeObject<Dictionary<string, string>>(Utils.ReadAllText(Paths.GetPath("english.local"))); - } - - foreach (var kv in localizationStrings) - { - original = original.Replace(kv.Key, kv.Value); + localizationStrings = JsonConvert.DeserializeObject<Dictionary<string, string>>(Utils.ReadAllText(Paths.GetPath("english.local"))); //if no provider fall back to english } - List<string> orphaned = new List<string>(); - if (Utils.FileExists("0:/dev_orphaned_lang.txt")) + foreach (var kv in localizationStrings.Where(x=>original.Contains(x.Key))) { - orphaned = JsonConvert.DeserializeObject<List<string>>(Utils.ReadAllText("0:/dev_orphaned_lang.txt")); + original = original.Replace(kv.Key, kv.Value); // goes through and replaces all the localization blocks } + //string original2 = Parse(original); - int start_index = 0; - int length = 0; - bool indexing = false; + string usernameReplace = ""; + string domainReplace = ""; - foreach (var c in original) + // if the user has saved then store their username and systemname in these string variables please + if (SaveSystem.CurrentSave != null) { - if (c == '{') + try { - start_index = original.IndexOf(c); - indexing = true; + usernameReplace = SaveSystem.CurrentUser.Username; } - - if (indexing == true) + catch { - length++; - if (c == '}') - { - indexing = false; - string o = original.Substring(start_index, length); - if (!orphaned.Contains(o)) - { - orphaned.Add(o); - } - start_index = 0; - length = 0; - } + usernameReplace = "user"; } - } - - if (orphaned.Count > 0) - { - Utils.WriteAllText("0:/dev_orphaned_lang.txt", JsonConvert.SerializeObject(orphaned, Formatting.Indented)); - } - - //string original2 = Parse(original); - - string usernameReplace = ""; - string domainReplace = ""; - if (SaveSystem.CurrentSave != null) - { - usernameReplace = SaveSystem.CurrentSave.Username; - domainReplace = SaveSystem.CurrentSave.SystemName; + try + { + domainReplace = SaveSystem.CurrentSave.SystemName; + } + catch + { + domainReplace = "system"; + } + } string namespaceReplace = ""; string commandReplace = ""; + // if the user did a command in the terminal and it had a period in it then split it up into the part before the period and the part after and then store them into these two string variables please if (TerminalBackend.latestCommmand != "" && TerminalBackend.latestCommmand.IndexOf('.') > -1) { namespaceReplace = TerminalBackend.latestCommmand.Split('.')[0]; commandReplace = TerminalBackend.latestCommmand.Split('.')[1]; } + // if you see these then replace them with what you need to Dictionary<string, string> defaultReplace = new Dictionary<string, string>() { {"%username", usernameReplace}, {"%domain", domainReplace}, {"%ns", namespaceReplace}, {"%cmd", commandReplace}, - {"%cp", SaveSystem.CurrentSave?.Codepoints.ToString() }, +#if LOCALIZE_CODEPOINTS + { "%cp", SaveSystem.CurrentSave?.Codepoints.ToString() }, +#endif }; - foreach (KeyValuePair<string, string> replacement in replace) + // actually do the replacement + foreach (KeyValuePair<string, string> replacement in replace.Where(x => original.Contains(x.Key))) { original = original.Replace(replacement.Key, Parse(replacement.Value)); } - foreach (KeyValuePair<string, string> replacement in defaultReplace) + // do the replacement but default + foreach (KeyValuePair<string, string> replacement in defaultReplace.Where(x => original.Contains(x.Key))) { original = original.Replace(replacement.Key, replacement.Value); } - return original; + return original; // returns the now replaced string } + // a few things are defined here public static void RegisterProvider(ILanguageProvider p) { _provider = p; diff --git a/ShiftOS_TheReturn/LoginManager.cs b/ShiftOS_TheReturn/LoginManager.cs new file mode 100644 index 0000000..d326f2c --- /dev/null +++ b/ShiftOS_TheReturn/LoginManager.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Objects; + +namespace ShiftOS.Engine +{ + public static class LoginManager + { + private static ILoginFrontend _login = null; + + public static void Init(ILoginFrontend login) + { + _login = login; + } + + public static void PromptForLogin() + { + _login.LoginComplete += (user) => + { + LoginComplete?.Invoke(user); + }; + _login.Login(); + } + + public static bool ShouldUseGUILogin + { + get + { + if (_login == null) + return false; + return _login.UseGUILogin; + } + } + + public static event Action<ClientSave> LoginComplete; + } + + /// <summary> + /// Interface for GUI-based logins. + /// </summary> + public interface ILoginFrontend + { + /// <summary> + /// When implemented, shows the login UI. + /// </summary> + void Login(); + + /// <summary> + /// Gets whether the ShiftOS engine should use a GUI-based login system or the default one. + /// </summary> + bool UseGUILogin { get; } + + + /// <summary> + /// Occurs when the login is complete. + /// </summary> + event Action<ClientSave> LoginComplete; + + + + } +} diff --git a/ShiftOS_TheReturn/NotificationDaemon.cs b/ShiftOS_TheReturn/NotificationDaemon.cs index 77a31fc..0725782 100644 --- a/ShiftOS_TheReturn/NotificationDaemon.cs +++ b/ShiftOS_TheReturn/NotificationDaemon.cs @@ -25,6 +25,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; @@ -34,6 +35,36 @@ namespace ShiftOS.Engine { public static class NotificationDaemon { + /// <summary> + /// Gets a list of all <see cref="IStatusIcon"/> objects that meet their Shiftorium dependencies. + /// </summary> + /// <returns>An array of <see cref="Type"/>s containing the found objects.</returns> + public static Type[] GetAllStatusIcons() + { + List<Type> lst = new List<Type>(); + foreach(var exec in System.IO.Directory.GetFiles(Environment.CurrentDirectory)) + { + if(exec.ToLower().EndsWith(".exe") || exec.ToLower().EndsWith(".dll")) + { + try + { + var asm = Assembly.LoadFile(exec); + foreach(var type in asm.GetTypes().Where(x => x.GetInterfaces().Contains(typeof(IStatusIcon)))) + { + if (Shiftorium.UpgradeAttributesUnlocked(type)) + { + lst.Add(type); + } + } + } + catch { } + } + } + return lst.ToArray(); + } + + + //if the notifications file already exists then get them public static Notification[] GetAllFromFile() { Notification[] notes = { }; @@ -44,23 +75,25 @@ namespace ShiftOS.Engine return notes; } + //tells the computer how it likes it to be written in the file internal static void WriteNotes(Notification[] notes) { - Utils.WriteAllText(Paths.GetPath("notifications.dat"), JsonConvert.SerializeObject(notes, Formatting.Indented)); + Utils.WriteAllText(Paths.GetPath("notifications.dat"), JsonConvert.SerializeObject(notes, Formatting.Indented)); //"write it in there indented pls" } - public static event Action<Notification> NotificationMade; - + public static event Action<Notification> NotificationMade; //use this if you want to know when a notification has been made + public static void AddNotification(NotificationType note, object data) { - var lst = new List<Notification>(GetAllFromFile()); - lst.Add(new Engine.Notification(note, data)); + var lst = new List<Notification>(GetAllFromFile()); //grabs all current notifications + lst.Add(new Engine.Notification(note, data)); //then adds the new one to the list WriteNotes(lst.ToArray()); - NotificationMade?.Invoke(lst[lst.Count - 1]); + NotificationMade?.Invoke(lst[lst.Count - 1]); //says to the program that a notification has indeed been made } public static event Action NotificationRead; + //for every notification that there is, mark them as read public static void MarkAllRead() { var notes = GetAllFromFile(); @@ -68,30 +101,33 @@ namespace ShiftOS.Engine MarkRead(i); } + //grabs list of notifcations and if the notification you want to mark as read actually exsists, then it assigns it as read public static void MarkRead(int note) { var notes = GetAllFromFile(); if (note >= notes.Length || note < 0) throw new ArgumentOutOfRangeException("note", new Exception("You cannot mark a notification that does not exist as read.")); - notes[note].Read = true; + notes[note].Read = true; //assigns the specific notification as read WriteNotes(notes); NotificationRead?.Invoke(); } - public static int GetUnreadCount() + public static int GetUnreadCount() //use this if you want the unread notification count, but i think you probably already knew that { int c = 0; foreach (var note in GetAllFromFile()) if (note.Read == false) - c++; //gahh I hate that programming language. + c++; //gahh I hate that programming language. //dont we all return c; } } + //actually gives the proper data for the notification public struct Notification { + //defaults for all notificaions public Notification(NotificationType t, object data) { Type = t; @@ -106,9 +142,10 @@ namespace ShiftOS.Engine public DateTime Timestamp { get; set; } } + //defines all the possible notificaions that can happen public enum NotificationType { - Generic = 0x00, + Generic = 0x00, //lets get generic MemoReceived = 0x10, MemoSent = 0x11, DownloadStarted = 0x20, diff --git a/ShiftOS_TheReturn/OutOfBoxExperience.cs b/ShiftOS_TheReturn/OutOfBoxExperience.cs index 6ed9e49..eb8e61d 100644 --- a/ShiftOS_TheReturn/OutOfBoxExperience.cs +++ b/ShiftOS_TheReturn/OutOfBoxExperience.cs @@ -45,17 +45,17 @@ namespace ShiftOS.Engine public static void Init(IOobe oobe) { - _oobe = oobe; + _oobe = oobe; // takes the oobe and makes it an IOobe } public static void Start(Save save) { - + //if its null then FUCK YOU DID THE WRONG THING if (_oobe == null) throw new InvalidOperationException("OOBE frontend not activated! This function can't be used! Please use OutOfBoxExperience.Init() passing an IOobe-implementing object to start the OOBE frontend."); - _oobe.StartShowing(save); + _oobe.StartShowing(save); //tells the save data to start showing the oobe } @@ -64,7 +64,7 @@ namespace ShiftOS.Engine { Desktop.InvokeOnWorkerThread(new Action(() => { - _oobe.PromptForLogin(); + _oobe.PromptForLogin(); //prompts for login, what did you expect })); } @@ -72,12 +72,13 @@ namespace ShiftOS.Engine { Desktop.InvokeOnWorkerThread(new Action(() => { - _oobe.ShowSaveTransfer(save); + _oobe.ShowSaveTransfer(save); //triggers save transfer if not done already })); } } + //triggers all the above events public interface IOobe { void StartShowing(Save save); diff --git a/ShiftOS_TheReturn/Paths.cs b/ShiftOS_TheReturn/Paths.cs index 10fd7d7..5b75ae6 100644 --- a/ShiftOS_TheReturn/Paths.cs +++ b/ShiftOS_TheReturn/Paths.cs @@ -35,8 +35,14 @@ using System.Threading; namespace ShiftOS.Engine { + /// <summary> + /// Management class for ShiftFS path variables. + /// </summary> public static class Paths { + /// <summary> + /// Initiate the path system. + /// </summary> public static void Init() { Locations = new Dictionary<string, string>(); @@ -88,6 +94,10 @@ namespace ShiftOS.Engine } + /// <summary> + /// Gets all full paths without their keynames. + /// </summary> + /// <returns>A string array representing all paths.</returns> public static string[] GetAllWithoutKey() { List<string> strings = new List<string>(); @@ -99,11 +109,19 @@ namespace ShiftOS.Engine } + /// <summary> + /// Get the full path using a path key. + /// </summary> + /// <param name="id">The path key (folder/filename) for the path.</param> + /// <returns>The full path.</returns> public static string GetPath(string id) { return Locations[id]; } + /// <summary> + /// Checks all directories in the path system to see if they exist, and if not, creates them. + /// </summary> private static void CheckPathExistence() { foreach(var path in Locations) @@ -119,8 +137,14 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Gets or sets a <see cref="Dictionary{string, string}"/> representing all paths in the system. + /// </summary> private static Dictionary<string, string> Locations { get; set; } + /// <summary> + /// Mounts the ShiftOS shared directory to 1:/, creating the directory if it does not exist. + /// </summary> public static void CreateAndMountSharedFolder() { if (!System.IO.Directory.Exists(SharedFolder)) @@ -201,9 +225,7 @@ namespace ShiftOS.Engine t.Start(); } - - - public static void ScanForDirectories(string folder, int mount) + private static void ScanForDirectories(string folder, int mount) { foreach (var file in System.IO.Directory.GetFiles(folder)) { @@ -220,10 +242,27 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Gets the ShiftOS shared folder. + /// </summary> public static string SharedFolder { get { return Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\ShiftOS_Shared"; } } + + /// <summary> + /// Gets the location of the ShiftOS.mfs file. + /// </summary> public static string SaveFile { get { return Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\ShiftOS.mfs"; } } + + /// <summary> + /// Gets the path of the inner save file. + /// </summary> + [Obsolete("Not used.")] public static string SaveFileInner { get { return Locations["save.json"]; } } + /// <summary> + /// Add a path to the system. + /// </summary> + /// <param name="parent">The path's parent directory.</param> + /// <param name="path">The filename for the path.</param> public static void AddPath(string parent, string path) { Locations.Add(path, Locations[parent] + "/" + path); diff --git a/ShiftOS_TheReturn/SaveSystem.cs b/ShiftOS_TheReturn/SaveSystem.cs index f29e5b8..e98a51e 100644 --- a/ShiftOS_TheReturn/SaveSystem.cs +++ b/ShiftOS_TheReturn/SaveSystem.cs @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +// #define NOSAVE //#define ONLINEMODE @@ -39,6 +40,7 @@ using static System.Net.Mime.MediaTypeNames; namespace ShiftOS.Engine { + [Obsolete("Use the servers.conf file instead.")] public class EngineConfig { public bool ConnectToMud = true; @@ -46,13 +48,34 @@ namespace ShiftOS.Engine public int MudDefaultPort = 13370; } + /// <summary> + /// Management class for the ShiftOS save system. + /// </summary> public static class SaveSystem { + /// <summary> + /// Boolean representing whether the system is shutting down. + /// </summary> public static bool ShuttingDown = false; + /// <summary> + /// Gets or sets the current logged in client-side user. + /// </summary> public static ClientSave CurrentUser { get; set; } + /// <summary> + /// Boolean representing whether the save system is ready to be used. + /// </summary> + public static bool Ready = false; + /// <summary> + /// Occurs before the save system connects to the ShiftOS Digital Society. + /// </summary> + public static event Action PreDigitalSocietyConnection; + + /// <summary> + /// Gets or sets the current server-side save file. + /// </summary> public static Save CurrentSave { get; set; } /// <summary> @@ -98,64 +121,92 @@ namespace ShiftOS.Engine } Thread.Sleep(350); - Console.WriteLine("Initiating kernel..."); + Console.WriteLine("ShiftKernel v0.4.2"); + Console.WriteLine("(MIT) DevX 2017, Very Little Rights Reserved"); + Console.WriteLine(""); + Console.WriteLine("THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR"); + Console.WriteLine("IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,"); + Console.WriteLine("FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE"); + Console.WriteLine("AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER"); + Console.WriteLine("LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,"); + Console.WriteLine("OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE"); + Console.WriteLine("SOFTWARE."); + Console.WriteLine(""); Thread.Sleep(250); - Console.WriteLine("Reading filesystem..."); + Console.WriteLine("[init] Kernel boot complete."); + Console.WriteLine("[sfs] Loading SFS driver v3"); Thread.Sleep(100); - Console.WriteLine("Reading configuration..."); + Console.WriteLine("[sfs] 4096 blocks read."); + Console.WriteLine("[simpl-conf] Reading configuration files (global-3.conf)"); + Console.WriteLine("[termdb] Building command database from filesystem..."); + TerminalBackend.PopulateTerminalCommands(); + Console.WriteLine("[inetd] Connecting to network..."); - Console.WriteLine("{CONNECTING_TO_MUD}"); + Ready = false; - if (defaultConf.ConnectToMud == true) + if (PreDigitalSocietyConnection != null) { - bool guidReceived = false; - ServerManager.GUIDReceived += (str) => + PreDigitalSocietyConnection?.Invoke(); + + while (!Ready) { + Thread.Sleep(10); + } + } + + + + bool guidReceived = false; + ServerManager.GUIDReceived += (str) => + { //Connection successful! Stop waiting! guidReceived = true; - Console.WriteLine("Connection successful."); - }; + Console.WriteLine("[inetd] Connection successful."); + }; - try - { - - ServerManager.Initiate("secondary4162.cloudapp.net", 13370); - //This haults the client until the connection is successful. - while (ServerManager.thisGuid == new Guid()) - { - Thread.Sleep(10); - } - Console.WriteLine("GUID received - bootstrapping complete."); - FinishBootstrap(); - } - catch (Exception ex) + try + { + + ServerManager.Initiate(UserConfig.Get().DigitalSocietyAddress, UserConfig.Get().DigitalSocietyPort); + //This haults the client until the connection is successful. + while (ServerManager.thisGuid == new Guid()) { - //No errors, this never gets called. - Console.WriteLine("{ERROR}: " + ex.Message); - Thread.Sleep(3000); - ServerManager.StartLANServer(); - while (ServerManager.thisGuid == new Guid()) - { - Thread.Sleep(10); - } - Console.WriteLine("GUID received - bootstrapping complete."); - FinishBootstrap(); + Thread.Sleep(10); } + Console.WriteLine("[inetd] DHCP GUID recieved, finished setup"); + FinishBootstrap(); } - else + catch (Exception ex) { - ServerManager.StartLANServer(); + //No errors, this never gets called. + Console.WriteLine("[inetd] SEVERE: " + ex.Message); + Thread.Sleep(3000); + Console.WriteLine("[sys] SEVERE: Cannot connect to server. Shutting down in 5..."); + Thread.Sleep(1000); + Console.WriteLine("[sys] 4..."); + Thread.Sleep(1000); + Console.WriteLine("[sys] 3..."); + Thread.Sleep(1000); + Console.WriteLine("[sys] 2..."); + Thread.Sleep(1000); + Console.WriteLine("[sys] 1..."); + Thread.Sleep(1000); + Console.WriteLine("[sys] Bye bye."); + System.Diagnostics.Process.GetCurrentProcess().Kill(); } //Nothing happens past this point - but the client IS connected! It shouldn't be stuck in that while loop above. - + })); thread.IsBackground = true; thread.Start(); } - public static void FinishBootstrap() + /// <summary> + /// Finish bootstrapping the engine. + /// </summary> + private static void FinishBootstrap() { KernelWatchdog.Log("mud_handshake", "handshake successful: kernel watchdog access code is \"" + ServerManager.thisGuid.ToString() + "\""); @@ -165,13 +216,21 @@ namespace ShiftOS.Engine { if (msg.Name == "mud_savefile") { - CurrentSave = JsonConvert.DeserializeObject<Save>(msg.Contents); ServerManager.MessageReceived -= savehandshake; - } + try + { + CurrentSave = JsonConvert.DeserializeObject<Save>(msg.Contents); + } + catch + { + Console.WriteLine("[system] [SEVERE] Cannot parse configuration file."); + oobe.PromptForLogin(); + } + } else if (msg.Name == "mud_login_denied") { - oobe.PromptForLogin(); ServerManager.MessageReceived -= savehandshake; + oobe.PromptForLogin(); } }; ServerManager.MessageReceived += savehandshake; @@ -196,7 +255,7 @@ namespace ShiftOS.Engine Thread.Sleep(75); Thread.Sleep(50); - Console.WriteLine("{SYSTEM_INITIATED}"); + Console.WriteLine("[usr-man] Accepting logins on local tty 1."); Sysname: bool waitingForNewSysName = false; @@ -204,7 +263,7 @@ namespace ShiftOS.Engine if (string.IsNullOrWhiteSpace(CurrentSave.SystemName)) { - Infobox.PromptText("Enter a system name", "Your system does not have a name. All systems within the digital society must have a name. Please enter one.", (name)=> + Infobox.PromptText("Enter a system name", "Your system does not have a name. All systems within the digital society must have a name. Please enter one.", (name) => { if (string.IsNullOrWhiteSpace(name)) Infobox.Show("Invalid name", "Please enter a valid name.", () => @@ -248,8 +307,34 @@ namespace ShiftOS.Engine if (CurrentSave.Users == null) CurrentSave.Users = new List<ClientSave>(); - - if(CurrentSave.Users.Count == 0) + Console.WriteLine($@" + `-:/++++::.` + .+ydNMMMMMNNMMMMMNhs/. + /yNMMmy+:-` `````.-/ohNMMms- + `oNMMh/.`:oydmNMMMMNmhs+- .+dMMm+` Welcome to ShiftOS. + `oMMmo``+dMMMMMMMMMMMMMMMMMNh/`.sNMN+ + :NMN+ -yMMMMMMMNdhyssyyhdmNMMMMNs``sMMd. SYSTEM STATUS: + oMMd.`sMMMMMMd+. `/MMMMN+ -mMN: ---------------------- + oMMh .mMMMMMM/ `-::::-.` :MMMMMMh`.mMM: + :MMd .NMMMMMMs .dMMMMMMMMMNddMMMMMMMd`.NMN. Codepoints: {SaveSystem.CurrentSave.Codepoints} + mMM. dMMMMMMMo -mMMMMMMMMMMMMMMMMMMMMs /MMy Upgrades: {SaveSystem.CurrentSave.CountUpgrades()} installed + :MMh :MMMMMMMMm` .+shmMMMMMMMMMMMMMMMN` NMN` {Shiftorium.GetAvailable().Count()} available + oMM+ sMMMMMMMMMN+` `-/smMMMMMMMMMMM: hMM: Filesystems: {Utils.Mounts.Count} filesystems mounted in memory. + sMM+ sMMMMMMMMMMMMds/-` .sMMMMMMMMM/ yMM/ + +MMs +MMMMMMMMMMMMMMMMMmhs:` +MMMMMMMM- dMM- System name: {CurrentSave.SystemName.ToUpper()} + .MMm `NMMMMMMMMMMMMMMMMMMMMMo `NMMMMMMd .MMN Users: {Users.Count()} found. + hMM+ +MMMMMMmsdNMMMMMMMMMMN/ -MMMMMMN- yMM+ + `NMN- oMMMMMd `-/+osso+- .mMMMMMN: +MMd + -NMN: /NMMMm` :yMMMMMMm- oMMd` + -mMMs``sMMMMNdhso++///+oydNMMMMMMNo .hMMh` + `yMMm/ .omMMMMMMMMMMMMMMMMMMMMd+``oNMNo + -hMMNo. -ohNMMMMMMMMMMMMmy+. -yNMNy` + .sNMMms/. `-/+++++/:-` ./yNMMmo` + :sdMMMNdyso+++ooshdNMMMdo- + `:+yhmNNMMMMNNdhs+- + ```` "); + + if (CurrentSave.Users.Count == 0) { CurrentSave.Users.Add(new ClientSave { @@ -257,76 +342,107 @@ namespace ShiftOS.Engine Password = "", Permissions = UserPermissions.Root }); - Console.WriteLine("No users found. Creating new user with username \"root\", with no password."); + Console.WriteLine("[usr-man] WARN: No users found. Creating new user with username \"root\", with no password."); } TerminalBackend.InStory = false; TerminalBackend.PrefixEnabled = false; - Login: - string username = ""; - int progress = 0; - bool goback = false; - TextSentEventHandler ev = null; - ev = (text) => + if (LoginManager.ShouldUseGUILogin) + { + Action<ClientSave> Completed = null; + Completed += (user) => + { + CurrentUser = user; + LoginManager.LoginComplete -= Completed; + }; + LoginManager.LoginComplete += Completed; + Desktop.InvokeOnWorkerThread(() => + { + LoginManager.PromptForLogin(); + }); + while (CurrentUser == null) + { + Thread.Sleep(10); + } + } + else { - if (progress == 0) + + Login: + string username = ""; + int progress = 0; + bool goback = false; + TextSentEventHandler ev = null; + ev = (text) => { - if (!string.IsNullOrWhiteSpace(text)) + if (progress == 0) { - if (CurrentSave.Users.FirstOrDefault(x => x.Username == text) == null) + string loginstr = CurrentSave.SystemName + " login: "; + string getuser = text.Remove(0, loginstr.Length); + if (!string.IsNullOrWhiteSpace(getuser)) { - Console.WriteLine("User not found."); - goback = true; + if (CurrentSave.Users.FirstOrDefault(x => x.Username == getuser) == null) + { + Console.WriteLine(); + Console.WriteLine("User not found."); + goback = true; + progress++; + TerminalBackend.TextSent -= ev; + return; + } + username = getuser; progress++; + } + else + { + Console.WriteLine(); + Console.WriteLine("Username not provided."); TerminalBackend.TextSent -= ev; - return; + goback = true; + progress++; } - username = text; - progress++; } - else + else if (progress == 1) { - Console.WriteLine("Username not provided."); + string passwordstr = "password: "; + string getpass = text.Remove(0, passwordstr.Length); + var user = CurrentSave.Users.FirstOrDefault(x => x.Username == username); + if (user.Password == getpass) + { + Console.WriteLine(); + Console.WriteLine("Welcome to ShiftOS."); + CurrentUser = user; + progress++; + } + else + { + Console.WriteLine(); + Console.WriteLine("Access denied."); + goback = true; + progress++; + } TerminalBackend.TextSent -= ev; - goback = true; - progress++; } - } - else if (progress == 1) + }; + TerminalBackend.TextSent += ev; + Console.WriteLine(); + Console.Write(CurrentSave.SystemName + " login: "); + ConsoleEx.Flush(); + while (progress == 0) { - var user = CurrentSave.Users.FirstOrDefault(x => x.Username == username); - if (user.Password == text) - { - Console.WriteLine("Welcome to ShiftOS."); - CurrentUser = user; - Thread.Sleep(2000); - progress++; - } - else - { - Console.WriteLine("Access denied."); - goback = true; - progress++; - } - TerminalBackend.TextSent -= ev; + Thread.Sleep(10); } - }; - TerminalBackend.TextSent += ev; - Console.WriteLine(CurrentSave.SystemName + " login:"); - while(progress == 0) - { - Thread.Sleep(10); + if (goback) + goto Login; + Console.WriteLine(); + Console.Write("password: "); + ConsoleEx.Flush(); + while (progress == 1) + Thread.Sleep(10); + if (goback) + goto Login; } - if (goback) - goto Login; - Console.WriteLine("password:"); - while (progress == 1) - Thread.Sleep(10); - if (goback) - goto Login; - - TerminalBackend.PrefixEnabled = true; Shiftorium.LogOrphanedUpgrades = true; Desktop.InvokeOnWorkerThread(new Action(() => @@ -337,10 +453,26 @@ namespace ShiftOS.Engine Desktop.InvokeOnWorkerThread(new Action(() => Desktop.PopulateAppLauncher())); GameReady?.Invoke(); + + if (!string.IsNullOrWhiteSpace(CurrentSave.PickupPoint)) + { + try + { + Story.Start(CurrentSave.PickupPoint); + TerminalBackend.PrintPrompt(); + } + catch { } + } } + /// <summary> + /// Delegate type for events with no caller objects or event arguments. You can use the () => {...} (C#) lambda expression with this delegate + /// </summary> public delegate void EmptyEventHandler(); + /// <summary> + /// Gets a list of all client-side users. + /// </summary> public static List<ClientSave> Users { get @@ -349,20 +481,35 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Occurs when the engine is loaded and the game can take over. + /// </summary> public static event EmptyEventHandler GameReady; - public static void TransferCodepointsToVoid(long amount) + /// <summary> + /// Deducts a set amount of Codepoints from the save file... and sends them to a place where they'll never be seen again. + /// </summary> + /// <param name="amount">The amount of Codepoints to deduct.</param> + public static void TransferCodepointsToVoid(ulong amount) { + if (amount < 0) + throw new ArgumentOutOfRangeException("We see what you did there. Trying to pull Codepoints from the void? That won't work."); CurrentSave.Codepoints -= amount; NotificationDaemon.AddNotification(NotificationType.CodepointsSent, amount); } + /// <summary> + /// Restarts the game. + /// </summary> public static void Restart() { TerminalBackend.InvokeCommand("sos.shutdown"); System.Windows.Forms.Application.Restart(); } + /// <summary> + /// Requests the save file from the server. If authentication fails, this will cause the user to be prompted for their website login and a new save will be created if none is associated with the login. + /// </summary> public static void ReadSave() { //Migrate old saves. @@ -406,6 +553,9 @@ namespace ShiftOS.Engine } + /// <summary> + /// Creates a new save, starting the Out Of Box Experience (OOBE). + /// </summary> public static void NewSave() { AppearanceManager.Invoke(new Action(() => @@ -418,31 +568,57 @@ namespace ShiftOS.Engine })); } + /// <summary> + /// Saves the game to the server, updating website stats if possible. + /// </summary> public static void SaveGame() { +#if !NOSAVE if(!Shiftorium.Silent) Console.WriteLine(""); if(!Shiftorium.Silent) Console.Write("{SE_SAVING}... "); if (SaveSystem.CurrentSave != null) { - Utils.WriteAllText(Paths.GetPath("user.dat"), CurrentSave.UniteAuthToken); - ServerManager.SendMessage("mud_save", JsonConvert.SerializeObject(CurrentSave, Formatting.Indented)); + Utils.WriteAllText(Paths.GetPath("user.dat"), CurrentSave.UniteAuthToken); + var serialisedSaveFile = JsonConvert.SerializeObject(CurrentSave, Formatting.Indented); + new Thread(() => + { + // please don't do networking on the main thread if you're just going to + // discard the response, it's extremely slow + ServerManager.SendMessage("mud_save", serialisedSaveFile); + }) + { IsBackground = false }.Start(); } if (!Shiftorium.Silent) Console.WriteLine(" ...{DONE}."); System.IO.File.WriteAllText(Paths.SaveFile, Utils.ExportMount(0)); +#endif } - public static void TransferCodepointsFrom(string who, long amount) + /// <summary> + /// Transfers codepoints from an arbitrary character to the save file. + /// </summary> + /// <param name="who">The character name</param> + /// <param name="amount">The amount of Codepoints.</param> + public static void TransferCodepointsFrom(string who, ulong amount) { + if (amount < 0) + throw new ArgumentOutOfRangeException("We see what you did there... You can't just give a fake character Codepoints like that. It's better if you transfer them to the void."); NotificationDaemon.AddNotification(NotificationType.CodepointsReceived, amount); CurrentSave.Codepoints += amount; } } + /// <summary> + /// Delegate for handling Terminal text input. + /// </summary> + /// <param name="text">The text inputted by the user (including prompt text).</param> public delegate void TextSentEventHandler(string text); + /// <summary> + /// Denotes that this Terminal command or namespace is for developers. + /// </summary> public class DeveloperAttribute : Attribute { diff --git a/ShiftOS_TheReturn/Scripting.cs b/ShiftOS_TheReturn/Scripting.cs index d96bc98..5021f50 100644 --- a/ShiftOS_TheReturn/Scripting.cs +++ b/ShiftOS_TheReturn/Scripting.cs @@ -38,31 +38,65 @@ using System.Net; namespace ShiftOS.Engine.Scripting { + /// <summary> + /// Brings some C# goodies to the Lua system. + /// </summary> [Exposed("strutils")] public class StringUtils { + /// <summary> + /// Checks if a string ends with a specified string. + /// </summary> + /// <param name="operand">The string to operate on</param> + /// <param name="value">The string to check for</param> + /// <returns>Whether <paramref name="operand"/> ends with <paramref name="value"/>.</returns> public bool endswith(string operand, string value) { return operand.EndsWith(value); } + + /// <summary> + /// Checks if a string starts with a specified string. + /// </summary> + /// <param name="operand">The string to operate on</param> + /// <param name="value">The string to check for</param> + /// <returns>Whether <paramref name="operand"/> starts with <paramref name="value"/>.</returns> public bool startswith(string operand, string value) { return operand.StartsWith(value); } + /// <summary> + /// Checks if a string contains a specified string. + /// </summary> + /// <param name="operand">The string to operate on</param> + /// <param name="value">The string to check for</param> + /// <returns>Whether <paramref name="operand"/> contains <paramref name="value"/>.</returns> public bool contains(string operand, string value) { return operand.Contains(value); } } - + /// <summary> + /// DynamicLua wrapper for the ShiftOS engine. + /// </summary> public class LuaInterpreter { + /// <summary> + /// The DynamicLua backend. + /// </summary> public dynamic Lua = new DynamicLua.DynamicLua(); + + /// <summary> + /// Boolean representing whether the script is running. + /// </summary> public bool Running = true; + /// <summary> + /// Static constructor for the <see cref="LuaInterpreter"/> class. + /// </summary> static LuaInterpreter() { ServerManager.MessageReceived += (msg) => @@ -83,12 +117,21 @@ namespace ShiftOS.Engine.Scripting }; } + /// <summary> + /// Create a .SFT representation of a Lua script. + /// </summary> + /// <param name="lua">The Lua code to convert</param> + /// <returns>Base64 SFT representation.</returns> public static string CreateSft(string lua) { byte[] bytes = Encoding.UTF8.GetBytes(lua); return Convert.ToBase64String(bytes); } + /// <summary> + /// Run a compressed .SFT file as a lua script. + /// </summary> + /// <param name="sft">The .sft file to run.</param> public static void RunSft(string sft) { if (Utils.FileExists(sft)) @@ -108,8 +151,14 @@ namespace ShiftOS.Engine.Scripting } } + /// <summary> + /// Get the current working directory of the script. + /// </summary> public static string CurrentDirectory { get; private set; } + /// <summary> + /// Creates a new instance of the <see cref="LuaInterpreter"/> class. + /// </summary> public LuaInterpreter() { Lua(@"function totable(clrlist) @@ -128,6 +177,9 @@ end"); }; } + /// <summary> + /// Scans the engine, frontend, and all mods for Lua-exposed classes and functions. + /// </summary> public void SetupAPIs() { Lua.currentdir = (string.IsNullOrWhiteSpace(CurrentDirectory)) ? "0:" : CurrentDirectory; @@ -233,7 +285,10 @@ end"); }); } - + /// <summary> + /// Executes the specified file as an uncompressed Lua script. + /// </summary> + /// <param name="file">The file to execute.</param> public void ExecuteFile(string file) { if (Utils.FileExists(file)) @@ -247,13 +302,17 @@ end"); } } + /// <summary> + /// Executes the specified string as a Lua script. + /// </summary> + /// <param name="lua">The Lua code to execute.</param> public void Execute(string lua) { try { Console.WriteLine(""); Lua(lua); - Console.WriteLine($"{SaveSystem.CurrentSave.Username}@{SaveSystem.CurrentSave.SystemName}:~$ "); + Console.WriteLine($"{SaveSystem.CurrentUser.Username}@{SaveSystem.CurrentSave.SystemName}:~$ "); } catch (Exception e) { @@ -279,24 +338,46 @@ end"); } } + /// <summary> + /// Lua functions for .sft files. + /// </summary> [Exposed("sft")] public class SFTFunctions { + /// <summary> + /// Make a .sft file from a lua code string + /// </summary> + /// <param name="lua">The Lua code</param> + /// <returns>The resulting .sft string</returns> public string make(string lua) { return LuaInterpreter.CreateSft(lua); } + /// <summary> + /// Make a .sft string and save to a specified file. + /// </summary> + /// <param name="lua">The Lua code to compress</param> + /// <param name="outpath">The path to save the compressed .sft file to.</param> public void makefile(string lua, string outpath) { Utils.WriteAllText(outpath, make(lua)); } + /// <summary> + /// Run a compressed .sft file in the <see cref="LuaInterpreter"/>. + /// </summary> + /// <param name="inpath">The .sft file to run.</param> public void run(string inpath) { LuaInterpreter.RunSft(inpath); } + /// <summary> + /// Reads the specified .sft file and decompresses to it's Lua form. + /// </summary> + /// <param name="sft">The .sft file to uncompress</param> + /// <returns>The resulting Lua code.</returns> public string unmake(string sft) { if (Utils.FileExists(sft)) @@ -310,9 +391,17 @@ end"); } } + /// <summary> + /// Network functions for Lua. + /// </summary> [Exposed("net")] public class NetFunctions { + /// <summary> + /// Submit a GET request to the specified URL. + /// </summary> + /// <param name="url">The URL to open</param> + /// <returns>The result from the server</returns> public string get(string url) { return new WebClient().DownloadString(url); @@ -320,26 +409,48 @@ end"); } + /// <summary> + /// Console functions for Lua. + /// </summary> [Exposed("console")] public class ConsoleFunctions { + /// <summary> + /// Write text to the console. + /// </summary> + /// <param name="text">The text to write.</param> public void write(dynamic text) { Console.Write(text.ToString()); } + /// <summary> + /// Write text to the console, followed by a new line. + /// </summary> + /// <param name="text">The text to write.</param> public void writeLine(dynamic text) { Console.WriteLine(text.ToString()); } } + /// <summary> + /// The main ShiftOS API. + /// </summary> [Exposed("sos")] public class SystemFunctions { - public long getCodepoints() { return SaveSystem.CurrentSave.Codepoints; } - + /// <summary> + /// Retrieves the user's Codepoints from the save file. + /// </summary> + /// <returns>The user's Codepoints.</returns> + public ulong getCodepoints() { return SaveSystem.CurrentSave.Codepoints; } + /// <summary> + /// Run a command in the Terminal. + /// </summary> + /// <param name="cmd">The command to run, using regular ShiftOS syntax.</param> + /// <returns>Whether the command was found and ran.</returns> public bool runCommand(string cmd) { var args = TerminalBackend.GetArgs(ref cmd); @@ -347,7 +458,11 @@ end"); return TerminalBackend.RunClient(cmd, args); } - public void addCodepoints(int cp) + /// <summary> + /// Adds the specified amount of Codepoints to the save flie. + /// </summary> + /// <param name="cp">The codepoints to add.</param> + public void addCodepoints(uint cp) { if (cp > 100 || cp <= 0) { @@ -361,128 +476,227 @@ end"); } } - [Exposed("mud")] - public class MUDFunctions - { - public void sendDiagnostic(string src, string cat, object val) - { - ServerManager.SendMessage("diag_log", $"[{src}] <{cat}>: {val}"); - } - } - + /// <summary> + /// User information API. + /// </summary> [Exposed("userinfo")] public class UserInfoFunctions { + /// <summary> + /// Gets the user name of the currently logged in user. + /// </summary> + /// <returns>The user's username.</returns> public string getUsername() { - return SaveSystem.CurrentSave.Username; + return SaveSystem.CurrentUser.Username; } + /// <summary> + /// Retrieves the user's system name. + /// </summary> + /// <returns>The user's system name.</returns> public string getSysname() { return SaveSystem.CurrentSave.SystemName; } + /// <summary> + /// Gets the user's ShiftOS email (username@sysname). + /// </summary> + /// <returns>The user's email.</returns> public string getEmail() { return getUsername() + "@" + getSysname(); } } - + /// <summary> + /// Infobox API for Lua. + /// </summary> [Exposed("infobox")] public class InfoboxFunctions { + /// <summary> + /// Show a message to the user in an Infobox. + /// </summary> + /// <param name="title">The title of the Infobox</param> + /// <param name="message">The infobox's message</param> + /// <param name="callback">A function to run when the user clicks "OK"</param> public void show(string title, string message, Action callback = null) { Infobox.Show(title, message, callback); } + /// <summary> + /// Ask a simple yes/no question to the user using an Infobox. + /// </summary> + /// <param name="title">The title of the Infobox</param> + /// <param name="message">The infobox's message</param> + /// <param name="callback">A function to run when they choose an option. The boolean argument will be true if the user clicks Yes, and false if they click No.</param> public void question(string title, string message, Action<bool> callback) { Infobox.PromptYesNo(title, message, callback); } - public void input(string title, string message, Action<string> callback) + /// <summary> + /// Prompt the user for text using an Infobox. + /// </summary> + /// <param name="title">The infobox's title</param> + /// <param name="message">The infobox's message</param> + /// <param name="callback">A function to run when the user clicks "OK". The string value is the text entered by the user.</param> + /// <param name="isPassword">Whether the text box should hide its characters as if it were a password box.</param> + public void input(string title, string message, Action<string> callback, bool isPassword = false) { - Infobox.PromptText(title, message, callback); + Infobox.PromptText(title, message, callback, isPassword); } } + /// <summary> + /// File Skimmer API for Lua. + /// </summary> [Exposed("fileskimmer")] public class FileSkimmerFunctions { + /// <summary> + /// Opens a File Skimmer "Open File" dialog. + /// </summary> + /// <param name="extensions">Semicolon-separated list of file extensions that the opener should let through the filter.</param> + /// <param name="callback">Function to be called when the user chooses a file. The string value is the file's path.</param> public void openFile(string extensions, Action<string> callback) { FileSkimmerBackend.GetFile(extensions.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries), FileOpenerStyle.Open, callback); } + /// <summary> + /// Opens a File Skimmer "Save File" dialog. + /// </summary> + /// <param name="extensions">Semicolon-separated list of file extensions that the opener should let through the filter.</param> + /// <param name="callback">Function to be called when the user chooses a file. The string value is the file's path.</param> public void saveFile(string extensions, Action<string> callback) { FileSkimmerBackend.GetFile(extensions.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries), FileOpenerStyle.Save, callback); } } + /// <summary> + /// ShiftFS API for Lua. + /// </summary> [Exposed("fs")] public class ShiftFSFunctions { + /// <summary> + /// Read all text in a file to a string. + /// </summary> + /// <param name="path">The file path to read</param> + /// <returns>The string containing the file's contents.</returns> public string readAllText(string path) { return Utils.ReadAllText(path); } + /// <summary> + /// Copy a file from one place to another. + /// </summary> + /// <param name="i">The source file</param> + /// <param name="o">The destination path</param> public void copy(string i, string o) { Utils.WriteAllBytes(o, Utils.ReadAllBytes(i)); } + /// <summary> + /// Gets all files in the specified directory. + /// </summary> + /// <param name="dir">The directory to search</param> + /// <returns>A string array containing all file paths in the directory.</returns> public string[] getFiles(string dir) { return Utils.GetFiles(dir); } + /// <summary> + /// Gets all directories inside a directory. + /// </summary> + /// <param name="dir">The directory to search</param> + /// <returns>A string array containing all directory paths in the directory.</returns> public string[] getDirectories(string dir) { return Utils.GetDirectories(dir); } + /// <summary> + /// Read the binary contents of a file to a <see cref="byte"/> array. + /// </summary> + /// <param name="path">The file path to read.</param> + /// <returns>The resulting byte array.</returns> public byte[] readAllBytes(string path) { return Utils.ReadAllBytes(path); } + /// <summary> + /// Writes the specified text to a file. + /// </summary> + /// <param name="path">The file path</param> + /// <param name="contents">The text to write</param> public void writeAllText(string path, string contents) { Utils.WriteAllText(path, contents); } + /// <summary> + /// Writes the specified binary data to a file. + /// </summary> + /// <param name="path">The file path.</param> + /// <param name="contents">The binary data</param> public void writeAllBytes(string path, byte[] contents) { Utils.WriteAllBytes(path, contents); } + /// <summary> + /// Determines whether the specified path exists and is a file. + /// </summary> + /// <param name="path">The path to search</param> + /// <returns>The result of the search.</returns> public bool fileExists(string path) { return Utils.FileExists(path); } + /// <summary> + /// Determines whether the specified path exists and is a directory. + /// </summary> + /// <param name="path">The path to search</param> + /// <returns>The result of the search.</returns> public bool directoryExists(string path) { return Utils.DirectoryExists(path); } + /// <summary> + /// Deletes the file/directory at the specified path. + /// </summary> + /// <param name="path">The path to delete</param> public void delete(string path) { Utils.Delete(path); } + /// <summary> + /// Creates a new directory at the specified path. + /// </summary> + /// <param name="path">The path to create</param> public void createDirectory(string path) { Utils.CreateDirectory(path); } } - + /// <summary> + /// Marks the specified class as a Lua API object. + /// </summary> + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class ExposedAttribute : Attribute { /// <summary> @@ -494,6 +708,9 @@ end"); Name = name; } + /// <summary> + /// The API object's name + /// </summary> public string Name { get; private set; } } } diff --git a/ShiftOS_TheReturn/Server.cs b/ShiftOS_TheReturn/Server.cs new file mode 100644 index 0000000..ddbd15b --- /dev/null +++ b/ShiftOS_TheReturn/Server.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Objects; + +namespace ShiftOS.Engine +{ + public interface Server + { + /// <summary> + /// Occurs when someone sends a message to the server. + /// </summary> + /// <param name="msg">The message from the client.</param> + void MessageReceived(ServerMessage msg); + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] + public class ServerAttribute : Attribute + { + public ServerAttribute(string name, int port) + { + Name = name; + Port = port; + } + + + /// <summary> + /// Gets the name of the server. + /// </summary> + public string Name { get; } + + /// <summary> + /// Gets the port of the server. + /// </summary> + public int Port { get; } + + } +} diff --git a/ShiftOS_TheReturn/ServerManager.cs b/ShiftOS_TheReturn/ServerManager.cs index 792b38d..1439c0d 100644 --- a/ShiftOS_TheReturn/ServerManager.cs +++ b/ShiftOS_TheReturn/ServerManager.cs @@ -36,31 +36,55 @@ using static ShiftOS.Engine.SaveSystem; using Newtonsoft.Json; using System.Net.Sockets; using System.Diagnostics; +using System.IO; +using System.Reflection; namespace ShiftOS.Engine { + /// <summary> + /// Digital Society connection management class. + /// </summary> public static class ServerManager { + /// <summary> + /// Print connection diagnostic information. + /// </summary> public static void PrintDiagnostics() { Console.WriteLine($@"{{CLIENT_DIAGNOSTICS}} {{GUID}}: {thisGuid} +Ping: {ServerManager.DigitalSocietyPing} ms {{CLIENT_DATA}}: {JsonConvert.SerializeObject(client, Formatting.Indented)}"); } + /// <summary> + /// Gets the unique identifier for this Digital Society connection. This can be used for peer-to-peer communication between two clients. + /// </summary> public static Guid thisGuid { get; private set; } - private static NetObjectClient client { get; set; } + + /// <summary> + /// Gets the underlying NetSockets client for this connection. + /// </summary> + public static NetObjectClient client { get; private set; } + + private static bool UserDisconnect = false; - public static TimeSpan DigitalSocietyPing + /// <summary> + /// Gets or sets the server response time for the last request made by this client. + /// </summary> + public static long DigitalSocietyPing { get; private set; } + /// <summary> + /// Disconnect from the digital society intentionally. + /// </summary> public static void Disconnect() { UserDisconnect = true; @@ -72,38 +96,59 @@ namespace ShiftOS.Engine } + /// <summary> + /// Occurs when you are disconnected from the Digital Society. + /// </summary> public static event EmptyEventHandler Disconnected; - - public static void InitiateMUDHack() + + /// <summary> + /// Occurs when the unique ID for this client is sent by the server. + /// </summary> + public static event Action<string> GUIDReceived; + + private static void delegateToServer(ServerMessage msg) { - MessageReceived += ServerManager_MessageReceived; - SendMessage("mudhack_init", ""); + string[] split = msg.GUID.Split('|'); + bool finished = false; + foreach (var exec in Directory.GetFiles(Environment.CurrentDirectory)) + { + if(exec.ToLower().EndsWith(".exe") || exec.ToLower().EndsWith(".dll")) + { + try + { + var asm = Assembly.LoadFile(exec); + foreach(var type in asm.GetTypes().Where(x => x.GetInterfaces().Contains(typeof(Server)))) + { + var attrib = type.GetCustomAttributes().FirstOrDefault(x => x is ServerAttribute) as ServerAttribute; + if(attrib != null) + { + if(split[0] == SaveSystem.CurrentSave.SystemName && split[1] == attrib.Port.ToString()) + { + if (Shiftorium.UpgradeAttributesUnlocked(type)) + { + type.GetMethods(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(x => x.Name == "MessageReceived")?.Invoke(Activator.CreateInstance(type), null); + finished = true; + } + } + } + } + } + catch { } + } + } + if (finished == false) + { + Forward(split[2], "Error", $"{split[0]}:{split[1]}: connection refused"); + } } - public static event Action<string> ServerPasswordGenerated; - public static event EmptyEventHandler ServerAccessGranted; - public static event EmptyEventHandler ServerAccessDenied; - public static event Action<string> GUIDReceived; - public static event Action<List<OnlineUser>> UsersReceived; private static void ServerManager_MessageReceived(ServerMessage msg) { switch(msg.Name) { - case "mudhack_users": - UsersReceived?.Invoke(JsonConvert.DeserializeObject<List<OnlineUser>>(msg.Contents)); - break; - case "mudhack_init": - ServerPasswordGenerated?.Invoke(msg.Contents); - break; - case "mudhack_denied": - ServerAccessDenied?.Invoke(); - break; - case "mudhack_granted": - ServerAccessGranted?.Invoke(); - break; case "getguid_fromserver": - if(SaveSystem.CurrentSave.Username == msg.Contents) + if(SaveSystem.CurrentUser.Username == msg.Contents) { client.Send(new NetObject("yes_i_am", new ServerMessage { @@ -113,17 +158,45 @@ namespace ShiftOS.Engine })); } break; + case "msgtosys": + try + { + var m = JsonConvert.DeserializeObject<ServerMessage>(msg.Contents); + if(m.GUID.Split('|')[2] != thisGuid.ToString()) + { + delegateToServer(m); + } + } + catch { } + break; case "getguid_reply": GUIDReceived?.Invoke(msg.Contents); break; } } + public static void SendMessageToIngameServer(string sysname, int port, string title, string contents) + { + var smsg = new ServerMessage + { + Name = title, + GUID = $"{sysname}|{port}|{thisGuid.ToString()}", + Contents = contents + }; + Forward("all", "msgtosys", JsonConvert.SerializeObject(smsg)); + + } + public static void Detach_ServerManager_MessageReceived() { MessageReceived -= new ServerMessageReceived(ServerManager_MessageReceived); } + /// <summary> + /// Initiate a new Digital Society connection. + /// </summary> + /// <param name="mud_address">The IP address or hostname of the target server</param> + /// <param name="port">The target port.</param> public static void Initiate(string mud_address, int port) { client = new NetObjectClient(); @@ -131,6 +204,7 @@ namespace ShiftOS.Engine { if (!UserDisconnect) { + Desktop.PushNotification("digital_society_connection", "Disconnected from Digital Society.", "The ShiftOS kernel has been disconnected from the Digital Society. We are attempting to re-connect you."); TerminalBackend.PrefixEnabled = true; ConsoleEx.ForegroundColor = ConsoleColor.Red; ConsoleEx.Bold = true; @@ -148,7 +222,7 @@ namespace ShiftOS.Engine { if (PingTimer.IsRunning) { - DigitalSocietyPing = PingTimer.Elapsed; + DigitalSocietyPing = PingTimer.ElapsedMilliseconds; PingTimer.Reset(); } var msg = a.Data.Object as ServerMessage; @@ -170,9 +244,9 @@ namespace ShiftOS.Engine else if(msg.Name == "update_your_cp") { var args = JsonConvert.DeserializeObject<Dictionary<string, object>>(msg.Contents); - if(args["username"] as string == SaveSystem.CurrentSave.Username) + if(args["username"] as string == SaveSystem.CurrentUser.Username) { - SaveSystem.CurrentSave.Codepoints += (long)args["amount"]; + SaveSystem.CurrentSave.Codepoints += (ulong)args["amount"]; Desktop.InvokeOnWorkerThread(new Action(() => { Infobox.Show($"MUD Control Centre", $"Someone bought an item in your shop, and they have paid {args["amount"]}, and as such, you have been granted these Codepoints."); @@ -221,6 +295,11 @@ namespace ShiftOS.Engine private static Stopwatch PingTimer = new Stopwatch(); + /// <summary> + /// Send a message to the server. + /// </summary> + /// <param name="name">The message name</param> + /// <param name="contents">The message body</param> public static void SendMessage(string name, string contents) { var sMsg = new ServerMessage @@ -237,38 +316,33 @@ namespace ShiftOS.Engine private static bool singleplayer = false; public static bool IsSingleplayer { get { return singleplayer; } } - public static void StartLANServer() - { - singleplayer = true; - ShiftOS.Server.Program.ServerStarted += (address) => - { - Console.WriteLine($"Connecting to {address}..."); - Initiate(address, 13370); - }; - Disconnected += () => - { - ShiftOS.Server.Program.Stop(); - }; - ShiftOS.Server.Program.Main(new[] { "" }); - - - } - - + /// <summary> + /// Occurs when the server sends a message to this client. + /// </summary> public static event ServerMessageReceived MessageReceived; - public static void Forward(string targetGUID, string v, string message) + /// <summary> + /// Send a message to another client. + /// </summary> + /// <param name="targetGUID">The target client GUID.</param> + /// <param name="title">The message title</param> + /// <param name="message">The message contents</param> + public static void Forward(string targetGUID, string title, string message) { var smsg = new ServerMessage { GUID = targetGUID, - Name = v, + Name = title, Contents = message }; ServerManager.SendMessage("mud_forward", JsonConvert.SerializeObject(smsg)); } } + /// <summary> + /// Delegate for handling server messages + /// </summary> + /// <param name="msg">A server message containing the protocol message name, GUID of the sender, and the contents of the message.</param> public delegate void ServerMessageReceived(ServerMessage msg); public class MultiplayerOnlyAttribute : Attribute diff --git a/ShiftOS_TheReturn/ShiftOS.Engine.csproj b/ShiftOS_TheReturn/ShiftOS.Engine.csproj index fb33dc5..f70c41e 100644 --- a/ShiftOS_TheReturn/ShiftOS.Engine.csproj +++ b/ShiftOS_TheReturn/ShiftOS.Engine.csproj @@ -109,8 +109,10 @@ <Compile Include="FileSkimmerBackend.cs" /> <Compile Include="Infobox.cs" /> <Compile Include="IShiftOSWindow.cs" /> + <Compile Include="IStatusIcon.cs" /> <Compile Include="KernelWatchdog.cs" /> <Compile Include="Localization.cs" /> + <Compile Include="LoginManager.cs" /> <Compile Include="NotificationDaemon.cs" /> <Compile Include="OutOfBoxExperience.cs" /> <Compile Include="Paths.cs" /> @@ -123,6 +125,7 @@ </Compile> <Compile Include="SaveSystem.cs" /> <Compile Include="Scripting.cs" /> + <Compile Include="Server.cs" /> <Compile Include="ServerManager.cs" /> <Compile Include="ShiftnetSite.cs" /> <Compile Include="Shiftorium.cs" /> @@ -131,10 +134,7 @@ <Compile Include="TerminalBackend.cs" /> <Compile Include="TerminalTextWriter.cs" /> <Compile Include="TutorialManager.cs" /> - <Compile Include="UniteClient.cs" /> - <Compile Include="UniteTestCommands.cs" /> <Compile Include="UserManagementCommands.cs" /> - <Compile Include="VirusEngine.cs" /> <Compile Include="WinOpenAttribute.cs" /> <EmbeddedResource Include="Infobox.resx"> <DependentUpon>Infobox.cs</DependentUpon> @@ -163,10 +163,6 @@ <Project>{A069089A-8962-4607-B2B2-4CF4A371066E}</Project> <Name>ShiftOS.Objects</Name> </ProjectReference> - <ProjectReference Include="..\ShiftOS.Server\ShiftOS.Server.csproj"> - <Project>{226C63B4-E60D-4949-B4E7-7A2DDBB96776}</Project> - <Name>ShiftOS.Server</Name> - </ProjectReference> </ItemGroup> <ItemGroup> <COMReference Include="MediaPlayer"> diff --git a/ShiftOS_TheReturn/ShiftnetSite.cs b/ShiftOS_TheReturn/ShiftnetSite.cs index 5460171..07b4698 100644 --- a/ShiftOS_TheReturn/ShiftnetSite.cs +++ b/ShiftOS_TheReturn/ShiftnetSite.cs @@ -6,13 +6,34 @@ using System.Threading.Tasks; namespace ShiftOS.Engine { + /// <summary> + /// Interface for creating a Shiftnet website. + /// </summary> public interface IShiftnetSite { + /// <summary> + /// Called when the page is loaded. Perform data population here. + /// </summary> void Setup(); + + /// <summary> + /// Occurs when a ShiftOS skin is loaded. + /// </summary> void OnSkinLoad(); + + /// <summary> + /// Occurs when a Shiftorium upgrade is installed. + /// </summary> void OnUpgrade(); + /// <summary> + /// Invoke this to navigate the parent browser to a specified Shiftnet URL. + /// </summary> event Action<string> GoToUrl; + + /// <summary> + /// Invoke this to tell the parent browser to navigate to the previous page. + /// </summary> event Action GoBack; } @@ -24,14 +45,35 @@ namespace ShiftOS.Engine } + /// <summary> + /// Interface for creating a Shiftnet client. + /// </summary> public interface IShiftnetClient { + /// <summary> + /// Navigates the client to a specified Shiftnet URL. + /// </summary> + /// <param name="url">The URL to navigate to.</param> void NavigateToUrl(string url); + + /// <summary> + /// Refreshes the current page. + /// </summary> void RefreshSite(); } + /// <summary> + /// Marks this class as a Shiftnet website. + /// </summary> + [AttributeUsage(AttributeTargets.Class, AllowMultiple =false)] public class ShiftnetSiteAttribute : Attribute { + /// <summary> + /// Creates a new instance of the <see cref="ShiftnetSiteAttribute"/> class. + /// </summary> + /// <param name="url">The URL that links to this site</param> + /// <param name="name">The name of this site</param> + /// <param name="description">The description of this site</param> public ShiftnetSiteAttribute(string url, string name, string description) { Url = url; @@ -39,8 +81,19 @@ namespace ShiftOS.Engine Description = description; } + /// <summary> + /// Gets the Shiftnet URL for this site. + /// </summary> public string Url { get; private set; } + + /// <summary> + /// Gets the name of this website. + /// </summary> public string Name { get; private set; } + + /// <summary> + /// Gets the description of this website. + /// </summary> public string Description { get; private set; } } } diff --git a/ShiftOS_TheReturn/Shiftorium.cs b/ShiftOS_TheReturn/Shiftorium.cs index ad60134..975939f 100644 --- a/ShiftOS_TheReturn/Shiftorium.cs +++ b/ShiftOS_TheReturn/Shiftorium.cs @@ -34,6 +34,9 @@ using System.Diagnostics; namespace ShiftOS.Engine { + /// <summary> + /// Backend class for the Shiftorium. + /// </summary> public static class Shiftorium { /// <summary> @@ -49,11 +52,11 @@ namespace ShiftOS.Engine public static string[] GetCategories(bool onlyAvailable = true) { List<string> cats = new List<string>(); - IEnumerable < ShiftoriumUpgrade > upgrades = GetDefaults(); + IEnumerable<ShiftoriumUpgrade> upgrades = GetDefaults(); if (onlyAvailable) upgrades = new List<ShiftoriumUpgrade>(GetAvailable()); - foreach(var upg in upgrades) + foreach (var upg in upgrades) { if (!cats.Contains(upg.Category)) cats.Add(upg.Category); @@ -62,11 +65,19 @@ namespace ShiftOS.Engine return cats.ToArray(); } + /// <summary> + /// Causes the engine to alert the frontend of a new Shiftorium upgrade install. + /// </summary> public static void InvokeUpgradeInstalled() { Installed?.Invoke(); } + /// <summary> + /// Gets the category of an upgrade. + /// </summary> + /// <param name="id">The upgrade ID to check</param> + /// <returns>"Other" if the upgrade is not found, else, the upgrade category.</returns> public static string GetCategory(string id) { var upg = GetDefaults().FirstOrDefault(x => x.ID == id); @@ -75,19 +86,35 @@ namespace ShiftOS.Engine return (upg.Category == null) ? "Other" : upg.Category; } + /// <summary> + /// Gets all upgrades in a given category. + /// </summary> + /// <param name="cat">The category name to search</param> + /// <returns>The upgrades in the category.</returns> public static IEnumerable<ShiftoriumUpgrade> GetAllInCategory(string cat) { return GetDefaults().Where(x => x.Category == cat); } + /// <summary> + /// Gets whether or not the user has installed all upgrades in a category. + /// </summary> + /// <param name="cat">The category to search.</param> + /// <returns>Boolean value representing whether the user has installed all upgrades in the category.</returns> public static bool IsCategoryEmptied(string cat) { return GetDefaults().Where(x => x.Category == cat).FirstOrDefault(x => x.Installed == false) == null; } - public static bool Buy(string id, long cost) + /// <summary> + /// Buy an upgrade, deducting the specified amount of Codepoints. + /// </summary> + /// <param name="id">The upgrade ID to buy</param> + /// <param name="cost">The amount of Codepoints to deduct</param> + /// <returns>True if the upgrade was installed successfully, false if the user didn't have enough Codepoints or the upgrade wasn' found.</returns> + public static bool Buy(string id, ulong cost) { - if(SaveSystem.CurrentSave.Codepoints >= cost) + if (SaveSystem.CurrentSave.Codepoints >= cost) { SaveSystem.CurrentSave.Upgrades[id] = true; TerminalBackend.InvokeCommand("sos.save"); @@ -99,17 +126,22 @@ namespace ShiftOS.Engine } else { - if(!Silent) + if (!Silent) Console.WriteLine($"{{SHIFTORIUM_NOTENOUGHCP}}: {cost} > {SaveSystem.CurrentSave.Codepoints}"); return false; } } + /// <summary> + /// Determines whether all Shiftorium upgrade attributes for this type have been installed. + /// </summary> + /// <param name="type">The type to scan</param> + /// <returns>Boolean value representing the result of this function.</returns> public static bool UpgradeAttributesUnlocked(Type type) { - foreach(var attr in type.GetCustomAttributes(true)) + foreach (var attr in type.GetCustomAttributes(true)) { - if(attr is RequiresUpgradeAttribute) + if (attr is RequiresUpgradeAttribute) { var rAttr = attr as RequiresUpgradeAttribute; return rAttr.Installed; @@ -119,6 +151,11 @@ namespace ShiftOS.Engine return true; } + /// <summary> + /// Determines whether all Shiftorium upgrade attributes for this method have been installed. + /// </summary> + /// <param name="type">The method to scan</param> + /// <returns>Boolean value representing the result of this function.</returns> public static bool UpgradeAttributesUnlocked(MethodInfo type) { foreach (var attr in type.GetCustomAttributes(true)) @@ -133,6 +170,11 @@ namespace ShiftOS.Engine return true; } + /// <summary> + /// Determines whether all Shiftorium upgrade attributes for this property have been installed. + /// </summary> + /// <param name="type">The property to scan</param> + /// <returns>Boolean value representing the result of this function.</returns> public static bool UpgradeAttributesUnlocked(PropertyInfo type) { foreach (var attr in type.GetCustomAttributes(true)) @@ -147,6 +189,11 @@ namespace ShiftOS.Engine return true; } + /// <summary> + /// Determines whether all Shiftorium upgrade attributes for this field have been installed. + /// </summary> + /// <param name="type">The field to scan</param> + /// <returns>Boolean value representing the result of this function.</returns> public static bool UpgradeAttributesUnlocked(FieldInfo type) { foreach (var attr in type.GetCustomAttributes(true)) @@ -161,15 +208,140 @@ namespace ShiftOS.Engine return true; } + private static List<ShiftoriumUpgrade> upgDb = null; + + public static void CreateUpgradeDatabase() + { + upgDb = new List<ShiftoriumUpgrade>(); + //Now we probe for ShiftoriumUpgradeAttributes for mods. + foreach (var file in System.IO.Directory.GetFiles(Environment.CurrentDirectory)) + { + if (file.EndsWith(".exe") || file.EndsWith(".dll")) + { + try + { + var asm = Assembly.LoadFile(file); + foreach (var type in asm.GetTypes()) + { + if (type.GetInterfaces().Contains(typeof(IShiftoriumProvider))) + { + if (type.GetCustomAttributes().FirstOrDefault(x => x is ShiftoriumProviderAttribute) != null) + { + var _p = Activator.CreateInstance(type, null) as IShiftoriumProvider; + upgDb.AddRange(_p.GetDefaults()); + } + } + + + ShiftoriumUpgradeAttribute attrib = type.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute; + if (attrib != null) + { + if (upgDb.FirstOrDefault(x => x.ID == attrib.Upgrade) != null) + throw new ShiftoriumConflictException(attrib.Upgrade); + upgDb.Add(new ShiftoriumUpgrade + { + Id = attrib.Upgrade, + Name = attrib.Name, + Cost = attrib.Cost, + Description = attrib.Description, + Dependencies = attrib.Dependencies, + Category = attrib.Category + }); + } + + foreach (var mth in type.GetMethods()) + { + attrib = mth.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute; + if (attrib != null) + { + if (upgDb.FirstOrDefault(x => x.ID == attrib.Upgrade) != null) + throw new ShiftoriumConflictException(attrib.Upgrade); + upgDb.Add(new ShiftoriumUpgrade + { + Id = attrib.Upgrade, + Name = attrib.Name, + Cost = attrib.Cost, + Description = attrib.Description, + Dependencies = attrib.Dependencies, + Category = attrib.Category + }); + + } + } + + foreach (var mth in type.GetFields()) + { + attrib = mth.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute; + if (attrib != null) + { + if (upgDb.FirstOrDefault(x => x.ID == attrib.Upgrade) != null) + throw new ShiftoriumConflictException(attrib.Upgrade); + upgDb.Add(new ShiftoriumUpgrade + { + Id = attrib.Upgrade, + Name = attrib.Name, + Cost = attrib.Cost, + Description = attrib.Description, + Dependencies = attrib.Dependencies, + Category = attrib.Category + }); + + } + } + + foreach (var mth in type.GetProperties()) + { + attrib = mth.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute; + if (attrib != null) + { + if (upgDb.FirstOrDefault(x => x.ID == attrib.Upgrade) != null) + throw new ShiftoriumConflictException(attrib.Upgrade); + upgDb.Add(new ShiftoriumUpgrade + { + Id = attrib.Upgrade, + Name = attrib.Name, + Cost = attrib.Cost, + Description = attrib.Description, + Dependencies = attrib.Dependencies, + Category = attrib.Category + }); + + } + } + + } + } + catch { } + } + } + + + + foreach (var item in upgDb) + { + if (upgDb.Where(x => x.ID == item.ID).Count() > 1) + throw new ShiftoriumConflictException(item.Id); + } + } + + + /// <summary> + /// Gets or sets whether the Shiftorium has been initiated. + /// </summary> public static bool IsInitiated { get; private set; } + + /// <summary> + /// Initiates the Shiftorium. + /// </summary> public static void Init() { if (IsInitiated == false) { IsInitiated = true; //Let the crash handler deal with this one... - var dict = GetDefaults(); + CreateUpgradeDatabase(); + var dict = upgDb; foreach (var itm in dict) { if (!SaveSystem.CurrentSave.Upgrades.ContainsKey(itm.ID)) @@ -188,9 +360,14 @@ namespace ShiftOS.Engine } - public static long GetCPValue(string id) + /// <summary> + /// Get the codepoint value for an upgrade. + /// </summary> + /// <param name="id">The upgrade ID to search</param> + /// <returns>The codepoint value.</returns> + public static ulong GetCPValue(string id) { - foreach(var upg in GetDefaults()) + foreach (var upg in GetDefaults()) { if (upg.ID == id) return upg.Cost; @@ -198,10 +375,14 @@ namespace ShiftOS.Engine return 0; } + /// <summary> + /// Gets all available upgrades. + /// </summary> + /// <returns></returns> public static ShiftoriumUpgrade[] GetAvailable() { List<ShiftoriumUpgrade> available = new List<ShiftoriumUpgrade>(); - foreach(var defaultupg in GetDefaults()) + foreach (var defaultupg in GetDefaults()) { if (!UpgradeInstalled(defaultupg.ID) && DependenciesInstalled(defaultupg)) available.Add(defaultupg); @@ -209,6 +390,11 @@ namespace ShiftOS.Engine return available.ToArray(); } + /// <summary> + /// Determines whether all dependencies of a given upgrade have been installed. + /// </summary> + /// <param name="upg">The upgrade to scan</param> + /// <returns>Boolean representing the result of this function.</returns> public static bool DependenciesInstalled(ShiftoriumUpgrade upg) { if (string.IsNullOrEmpty(upg.Dependencies)) @@ -218,23 +404,33 @@ namespace ShiftOS.Engine else if (upg.Dependencies.Contains(";")) { string[] dependencies = upg.Dependencies.Split(';'); - foreach(var dependency in dependencies) + foreach (var dependency in dependencies) { if (!UpgradeInstalled(dependency)) return false; } return true; - } + } else { return UpgradeInstalled(upg.Dependencies); } } + /// <summary> + /// Fired when an upgrade is installed. + /// </summary> public static event EmptyEventHandler Installed; + /// <summary> + /// Determines if an upgrade is installed. + /// </summary> + /// <param name="id">The upgrade ID to scan.</param> + /// <returns>Whether the upgrade is installed.</returns> public static bool UpgradeInstalled(string id) { + if (string.IsNullOrWhiteSpace(id)) + return true; if (SaveSystem.CurrentSave != null) { if (!IsInitiated) @@ -250,7 +446,7 @@ namespace ShiftOS.Engine if (id.Contains(';')) { - foreach(var u in id.Split(';')) + foreach (var u in id.Split(';')) { if (UpgradeInstalled(u) == false) return false; @@ -259,10 +455,10 @@ namespace ShiftOS.Engine } bool upgInstalled = false; - if(SaveSystem.CurrentSave.Upgrades.ContainsKey(id)) + if (SaveSystem.CurrentSave.Upgrades.ContainsKey(id)) upgInstalled = SaveSystem.CurrentSave.Upgrades[id]; - if(upgInstalled == false) + if (upgInstalled == false) return SaveSystem.CurrentSave.StoriesExperienced.Contains(id); return true; } @@ -286,125 +482,22 @@ namespace ShiftOS.Engine _provider = p; } - //Bless the newer NEWER engine. + /// <summary> + /// Gets every upgrade inside the frontend and all mods. + /// </summary> + /// <returns>Every single found Shiftorium upgrade.</returns> public static List<ShiftoriumUpgrade> GetDefaults() { - List<ShiftoriumUpgrade> list = new List<ShiftoriumUpgrade>(); - //Now we probe for ShiftoriumUpgradeAttributes for mods. - foreach(var file in System.IO.Directory.GetFiles(Environment.CurrentDirectory)) - { - if(file.EndsWith(".exe") || file.EndsWith(".dll")) - { - try - { - var asm = Assembly.LoadFile(file); - foreach (var type in asm.GetTypes()) - { - if (type.GetInterfaces().Contains(typeof(IShiftoriumProvider))) - { - if(type.GetCustomAttributes().FirstOrDefault(x=> x is ShiftoriumProviderAttribute) != null) - { - var _p = Activator.CreateInstance(type, null) as IShiftoriumProvider; - list.AddRange(_p.GetDefaults()); - } - } - - - ShiftoriumUpgradeAttribute attrib = type.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute; - if (attrib != null) - { - if (list.FirstOrDefault(x => x.ID == attrib.Upgrade) != null) - throw new ShiftoriumConflictException(attrib.Upgrade); - list.Add(new ShiftoriumUpgrade - { - Id = attrib.Upgrade, - Name = attrib.Name, - Cost = attrib.Cost, - Description = attrib.Description, - Dependencies = attrib.Dependencies, - Category = attrib.Category - }); - } - - foreach (var mth in type.GetMethods()) - { - attrib = mth.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute; - if (attrib != null) - { - if (list.FirstOrDefault(x => x.ID == attrib.Upgrade) != null) - throw new ShiftoriumConflictException(attrib.Upgrade); - list.Add(new ShiftoriumUpgrade - { - Id = attrib.Upgrade, - Name = attrib.Name, - Cost = attrib.Cost, - Description = attrib.Description, - Dependencies = attrib.Dependencies, - Category = attrib.Category - }); - - } - } - - foreach (var mth in type.GetFields()) - { - attrib = mth.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute; - if (attrib != null) - { - if (list.FirstOrDefault(x => x.ID == attrib.Upgrade) != null) - throw new ShiftoriumConflictException(attrib.Upgrade); - list.Add(new ShiftoriumUpgrade - { - Id = attrib.Upgrade, - Name = attrib.Name, - Cost = attrib.Cost, - Description = attrib.Description, - Dependencies = attrib.Dependencies, - Category = attrib.Category - }); - - } - } - - foreach (var mth in type.GetProperties()) - { - attrib = mth.GetCustomAttributes(false).FirstOrDefault(x => x is ShiftoriumUpgradeAttribute) as ShiftoriumUpgradeAttribute; - if (attrib != null) - { - if (list.FirstOrDefault(x => x.ID == attrib.Upgrade) != null) - throw new ShiftoriumConflictException(attrib.Upgrade); - list.Add(new ShiftoriumUpgrade - { - Id = attrib.Upgrade, - Name = attrib.Name, - Cost = attrib.Cost, - Description = attrib.Description, - Dependencies = attrib.Dependencies, - Category = attrib.Category - }); - - } - } - - } - } - catch { } - } - } - - - - foreach(var item in list) - { - if (list.Where(x => x.ID == item.ID).Count() > 1) - throw new ShiftoriumConflictException(item.Id); - } - return list; + return upgDb; } } public interface IShiftoriumProvider { + /// <summary> + /// Retrieves all frontend upgrades. + /// </summary> + /// <returns></returns> List<ShiftoriumUpgrade> GetDefaults(); } @@ -427,7 +520,7 @@ namespace ShiftOS.Engine { public string Name { get; set; } public string Description { get; set; } - public long Cost { get; set; } + public ulong Cost { get; set; } public string ID { get { return (this.Id != null ? this.Id : (Name.ToLower().Replace(" ", "_"))); } } public string Id { get; set; } public string Category { get; set; } @@ -443,7 +536,7 @@ namespace ShiftOS.Engine public class ShiftoriumUpgradeAttribute : RequiresUpgradeAttribute { - public ShiftoriumUpgradeAttribute(string name, long cost, string desc, string dependencies, string category) : base(name.ToLower().Replace(" ", "_")) + public ShiftoriumUpgradeAttribute(string name, ulong cost, string desc, string dependencies, string category) : base(name.ToLower().Replace(" ", "_")) { Name = name; Description = desc; @@ -454,7 +547,7 @@ namespace ShiftOS.Engine public string Name { get; private set; } public string Description { get; private set; } - public long Cost { get; private set; } + public ulong Cost { get; private set; } public string Dependencies { get; private set; } public string Category { get; private set; } } diff --git a/ShiftOS_TheReturn/Skinning.cs b/ShiftOS_TheReturn/Skinning.cs index 4cc9bbd..d5114c5 100644 --- a/ShiftOS_TheReturn/Skinning.cs +++ b/ShiftOS_TheReturn/Skinning.cs @@ -37,35 +37,71 @@ using System.Reflection; using ShiftOS.Engine.Scripting; namespace ShiftOS.Engine { - + /// <summary> + /// Skinning API for Lua. + /// </summary> [Exposed("skinning")] public class SkinFunctions { + /// <summary> + /// Reload the current skin. + /// </summary> public void loadSkin() { SkinEngine.LoadSkin(); } + /// <summary> + /// Get the current skin info. + /// </summary> + /// <returns>A proxy object containing all skin variables.</returns> public dynamic getSkin() { return SkinEngine.LoadedSkin; } + /// <summary> + /// Set the current skin to the specified <see cref="Skin"/> class. + /// </summary> + /// <param name="skn">The <see cref="Skin"/> class to load.</param> public void setSkin(Skin skn) { Utils.WriteAllText(Paths.GetPath("skin.json"), JsonConvert.SerializeObject(skn)); SkinEngine.LoadSkin(); } + /// <summary> + /// Retrieves an image from the skin file. + /// </summary> + /// <param name="id">The skin image ID</param> + /// <returns>The loaded image, null (nil in Lua) if none is found.</returns> public dynamic getImage(string id) { return SkinEngine.GetImage(id); } } - + /// <summary> + /// Skin engine management class. + /// </summary> public static class SkinEngine { + private static ISkinPostProcessor processor = null; + + /// <summary> + /// Load a new skin postprocessor into the engine. + /// </summary> + /// <param name="_processor">The postprocessor to load.</param> + public static void SetPostProcessor(ISkinPostProcessor _processor) + { + processor = _processor; + } + + /// <summary> + /// Retrieve the user-specified image layout of a skin image. + /// </summary> + /// <param name="img">The skin image ID.</param> + /// <returns>The <see cref="ImageLayout"/> for the image.</returns> public static ImageLayout GetImageLayout(string img) { if (LoadedSkin.SkinImageLayouts.ContainsKey(img)) @@ -79,6 +115,11 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Retrieves an image from the skin after postprocessing it. + /// </summary> + /// <param name="img">The image ID to search.</param> + /// <returns>The post-processed image, or null if none was found.</returns> public static System.Drawing.Image GetImage(string img) { var type = typeof(Skin); @@ -93,6 +134,8 @@ namespace ShiftOS.Engine if (iattr.Name == img) { byte[] image = (byte[])field.GetValue(LoadedSkin); + if (processor != null) + image = processor.ProcessImage(image); return ImageFromBinary(image); } } @@ -102,11 +145,20 @@ namespace ShiftOS.Engine return null; } + /// <summary> + /// Set the engine's current icon prober. + /// </summary> + /// <param name="prober">The icon prober to use.</param> public static void SetIconProber(IIconProber prober) { _iconProber = prober; } + /// <summary> + /// Load a <see cref="Image"/> from a <see cref="byte"/> array. + /// </summary> + /// <param name="image">The array to convert</param> + /// <returns>The resulting image.</returns> public static Image ImageFromBinary(byte[] image) { if (image == null) @@ -117,6 +169,9 @@ namespace ShiftOS.Engine private static Skin loadedSkin = new Skin(); + /// <summary> + /// Gets the currently loaded skin. + /// </summary> public static Skin LoadedSkin { get @@ -129,6 +184,9 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Initiates the skin engine. + /// </summary> public static void Init() { Application.ApplicationExit += (o, a) => @@ -151,8 +209,14 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Occurs when the skin is loaded. + /// </summary> public static event EmptyEventHandler SkinLoaded; + /// <summary> + /// Reload the current skin. + /// </summary> public static void LoadSkin() { LoadedSkin = JsonConvert.DeserializeObject<Skin>(Utils.ReadAllText(Paths.GetPath("skin.json"))); @@ -161,6 +225,9 @@ namespace ShiftOS.Engine Desktop.PopulateAppLauncher(); } + /// <summary> + /// Save the skin loaded in memory to the filesystem. + /// </summary> public static void SaveSkin() { Utils.WriteAllText(Paths.GetPath("skin.json"), JsonConvert.SerializeObject(LoadedSkin, Formatting.Indented)); @@ -168,6 +235,11 @@ namespace ShiftOS.Engine private static IIconProber _iconProber = null; + /// <summary> + /// Retrieves the default icon for a given icon ID. + /// </summary> + /// <param name="id">The icon ID to search.</param> + /// <returns>The resulting icon image.</returns> public static Image GetDefaultIcon(string id) { if (_iconProber == null) @@ -204,13 +276,26 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Retrieves the user-defined icon for a specified icon ID. + /// </summary> + /// <param name="id">The icon ID to search.</param> + /// <returns>The resulting icon image.</returns> public static Image GetIcon(string id) { if (!LoadedSkin.AppIcons.ContainsKey(id)) LoadedSkin.AppIcons.Add(id, null); if (LoadedSkin.AppIcons[id] == null) - return GetDefaultIcon(id); + { + var img = GetDefaultIcon(id); + using (var mstr = new MemoryStream()) + { + img.Save(mstr, System.Drawing.Imaging.ImageFormat.Png); + LoadedSkin.AppIcons[id] = mstr.ToArray(); + } + return img; + } else { using (var sr = new MemoryStream(LoadedSkin.AppIcons[id])) @@ -222,11 +307,23 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Interface for probing app icons. + /// </summary> public interface IIconProber { + /// <summary> + /// Retrieve the icon image from a <see cref="DefaultIconAttribute"/>. + /// </summary> + /// <param name="attr">The attribute data</param> + /// <returns>The resulting image.</returns> Image GetIcon(DefaultIconAttribute attr); } + /// <summary> + /// Sets the default icon ID for a <see cref="IShiftOSWindow"/>. + /// </summary> + [AttributeUsage(AttributeTargets.Class, AllowMultiple =false)] public class DefaultIconAttribute : Attribute { public DefaultIconAttribute(string id) @@ -237,21 +334,24 @@ namespace ShiftOS.Engine public string ID { get; private set; } } + /// <summary> + /// The data stored in any .skn file. + /// </summary> public class Skin { //borrowing from the discourse theme for the default skin - private static readonly Color DefaultBackground = Color.FromArgb(0, 0x44, 0x00); + private static readonly Color DefaultBackground = Color.FromArgb(0x11, 0x11, 0x11); private static readonly Color DefaultForeground = Color.FromArgb(0xDD, 0xDD, 0xDD); private static readonly Color Accent1 = Color.FromArgb(0x66, 0x66, 0x66); - private static readonly Color Accent2 = Color.FromArgb(0x80, 0, 0); - private static readonly Color DesktopBG = Color.FromArgb(0x22, 0x22, 0x22); + private static readonly Color Accent2 = Color.FromArgb(0x0, 0x80, 0); + private static readonly Color DesktopBG = Color.FromArgb(0x00, 0x00, 0x00); private static readonly Font SysFont = new Font("Tahoma", 9F); private static readonly Font SysFont2 = new Font("Tahoma", 10F, FontStyle.Bold); - private static readonly Font Header1 = new Font("Helvetica", 20F, FontStyle.Bold); - private static readonly Font Header2 = new Font("Helvetica", 17.5F, FontStyle.Bold); - private static readonly Font Header3 = new Font("Tahoma", 15F, FontStyle.Bold); + private static readonly Font Header1 = new Font("Courier New", 20F, FontStyle.Bold); + private static readonly Font Header2 = new Font("Courier New", 17.5F, FontStyle.Bold); + private static readonly Font Header3 = new Font("Courier New", 15F, FontStyle.Bold); - private static readonly Color TitleBG = Color.FromArgb(0x11, 0x11, 0x11); + private static readonly Color TitleBG = Color.FromArgb(0x11, 0x55, 0x11); private static readonly Color TitleFG = Color.FromArgb(0xaa, 0xaa, 0xaa); //Todo: When making Shifter GUI we need to label all these with proper Shifter attributes to get 'em displaying in the right places. @@ -267,6 +367,151 @@ namespace ShiftOS.Engine [ShifterHidden] public Dictionary<string, byte[]> AppIcons = new Dictionary<string, byte[]>(); + [ShifterMeta("System")] + [ShifterCategory("Progress Bar")] + [RequiresUpgrade("shift_progress_bar;skinning")] + [Image("progressbarbg")] + [ShifterName("Progress Bar Background Image")] + [ShifterDescription("Set an image for the background of a progress bar.")] + public byte[] ProgressBarBG = null; + + + [ShifterMeta("System")] + [ShifterCategory("Progress Bar")] + [RequiresUpgrade("shift_progress_bar;skinning")] + [Image("progress")] + [ShifterName("Progress Image")] + [ShifterDescription("Set the image for the progress inside a progress bar.")] + public byte[] Progress = null; + + + [ShifterMeta("System")] + [ShifterCategory("Progress Bar")] + [RequiresUpgrade("shift_progress_bar")] + [ShifterName("Progress bar foreground color")] + [ShifterDescription("Set the color of the progress indicator.")] + public Color ProgressColor = Accent1; + + + [ShifterMeta("System")] + [ShifterCategory("Progress Bar")] + [RequiresUpgrade("shift_progress_bar")] + [ShifterName("Progress bar background color")] + [ShifterDescription("The background color of the progress bar.")] + public Color ProgressBarBackgroundColor = Color.Black; + + + [ShifterMeta("System")] + [ShifterCategory("Progress Bar")] + [RequiresUpgrade("shift_progress_bar")] + [ShifterName("Progress bar block size")] + [ShifterDescription("If the progress bar style is set to Blocks, this determines how wide each block should be.")] + public int ProgressBarBlockSize = 15; + + + [ShifterMeta("System")] + [ShifterCategory("Progress Bar")] + [RequiresUpgrade("shift_progress_bar")] + [ShifterDescription("Set the style of a progress bar.\r\nMarquee: The progress bar will render a box that moves from the left to the right in a loop.\r\nContinuous: Progress is shown by a single, continuous box.\r\nBlocks: Just like Continuous, but the box is split into even smaller boxes of a set width.")] + [ShifterName("Progress bar style")] + public ProgressBarStyle ProgressBarStyle = ProgressBarStyle.Continuous; + + + + + + + [ShifterMeta("System")] + [ShifterCategory("Buttons")] + [RequiresUpgrade("shift_buttons")] + [ShifterName("Button background color")] + [ShifterDescription("Set the background color for each button's Idle state.")] + public Color ButtonBackgroundColor = Skin.DefaultBackground; + + [ShifterMeta("System")] + [ShifterCategory("Buttons")] + [RequiresUpgrade("shift_buttons;skinning")] + [Image("buttonhover")] + [ShifterName("Button hover image")] + [ShifterDescription("Set the image that's displayed when the mouse hovers over a button.")] + public byte[] ButtonHoverImage = null; + + [ShifterMeta("System")] + [ShifterCategory("Buttons")] + [RequiresUpgrade("skinning;shift_buttons")] + [Image("buttonpressed")] + [ShifterName("Button pressed image")] + [ShifterDescription("Select an image to show when the user presses a button.")] + public byte[] ButtonPressedImage = null; + + [ShifterMeta("System")] + [ShifterCategory("Buttons")] + [RequiresUpgrade("shift_buttons")] + [ShifterName("Button hover color")] + [ShifterDescription("Choose the color that displays on a button when the mouse hovers over it.")] + public Color ButtonHoverColor = Skin.Accent1; + + [ShifterMeta("System")] + [ShifterCategory("Buttons")] + [RequiresUpgrade("shift_buttons")] + [ShifterName("Button pressed color")] + [ShifterDescription("Select the background color for the button when the mouse clicks it.")] + public Color ButtonPressedColor = Skin.Accent2; + + [ShifterMeta("System")] + [ShifterCategory("Buttons")] + [RequiresUpgrade("shift_buttons")] + [ShifterName("Button foreground color")] + [ShifterDescription("Select the text and border color for each button.")] + public Color ButtonForegroundColor = Skin.DefaultForeground; + + [ShifterMeta("System")] + [ShifterCategory("Buttons")] + [RequiresUpgrade("shift_buttons")] + [ShifterName("Button border width")] + [ShifterDescription("Set the width, in pixels, of the button's border.")] + public int ButtonBorderWidth = 2; + + [ShifterMeta("System")] + [ShifterCategory("Buttons")] + [RequiresUpgrade("shift_buttons")] + [ShifterName("Button font")] + [ShifterDescription("Select the font for the button's text.")] + public Font ButtonTextFont = Skin.SysFont; + + [ShifterMeta("System")] + [ShifterCategory("Buttons")] + [RequiresUpgrade("shift_buttons;skinning")] + [Image("buttonidle")] + [ShifterName("Button background color")] + [ShifterDescription("Select an image to show as the button's Idle state.")] + public byte[] ButtonBG = null; + + + [Image("panelclockbg")] + [ShifterMeta("Desktop")] + [ShifterCategory("Panel Clock")] + [ShifterName("Panel Clock Background Image")] + [ShifterDescription("Set the background image of the panel clock.")] + [RequiresUpgrade("skinning;shift_panel_clock")] + public byte[] PanelClockBG = null; + + [ShifterMeta("System")] + [ShifterCategory("Login Screen")] + [RequiresUpgrade("gui_based_login_screen")] + [ShifterName("Login Screen Background Color")] + [ShifterDescription("Change the background color of the login screen.")] + public Color LoginScreenColor = Skin.DesktopBG; + + [ShifterMeta("System")] + [ShifterCategory("Login Screen")] + [RequiresUpgrade("skinning;gui_based_login_screen")] + [ShifterName("Login Screen Background Image")] + [ShifterDescription("Set an image as your login screen!")] + [Image("login")] + public byte[] LoginScreenBG = null; + + [RequiresUpgrade("shift_screensaver")] [ShifterMeta("System")] [ShifterCategory("Screen saver")] @@ -405,7 +650,7 @@ namespace ShiftOS.Engine [ShifterName("Close Button Color")] [RequiresUpgrade("shift_title_buttons")] [ShifterDescription("The close button color")] - public Color CloseButtonColor = Accent2; + public Color CloseButtonColor = Color.FromArgb(0x80,0,0); [ShifterMeta("Windows")] [ShifterCategory("Title Buttons")] @@ -1218,6 +1463,9 @@ namespace ShiftOS.Engine public Font AdvALItemFont = SysFont2; } + /// <summary> + /// Marks a skin spec field as hidden from the Shifter. + /// </summary> public class ShifterHiddenAttribute : Attribute { @@ -1307,6 +1555,16 @@ namespace ShiftOS.Engine public string Category { get; set; } } + public interface ISkinPostProcessor + { + /// <summary> + /// Perform algorithmic operations at the bit level on a ShiftOS skin image. + /// </summary> + /// <param name="original">The image, as loaded by the engine, as a 32-bit ARGB byte array.</param> + /// <returns>The processed image.</returns> + byte[] ProcessImage(byte[] original); + } + public class ShifterMetaAttribute : Attribute { diff --git a/ShiftOS_TheReturn/Story.cs b/ShiftOS_TheReturn/Story.cs index 8c726ed..f473f89 100644 --- a/ShiftOS_TheReturn/Story.cs +++ b/ShiftOS_TheReturn/Story.cs @@ -35,8 +35,95 @@ using Newtonsoft.Json; namespace ShiftOS.Engine { - public class Story + public class StoryContext { + public string Id { get; set; } + public MethodInfo Method { get; set; } + public bool AutoComplete = false; + + public StoryContext() + { + AutoComplete = true; + } + + public void MarkComplete() + { + SaveSystem.CurrentSave.StoriesExperienced.Add(Id); + OnComplete?.Invoke(); + } + + public event Action OnComplete; + } + + public class Objective + { + private Func<bool> _completeFunc = null; + + public string Name { get; set; } + public string Description { get; set; } + + public bool IsComplete + { + get + { + return (bool)_completeFunc?.Invoke(); + } + } + + public Objective(string name, string desc, Func<bool> completeFunc, Action onComplete) + { + _completeFunc = completeFunc; + Name = name; + Description = desc; + this.onComplete = onComplete; + } + + private Action onComplete = null; + + public void Complete() + { + onComplete?.Invoke(); + } + } + + /// <summary> + /// Storyline management class. + /// </summary> + public static class Story + { + public static StoryContext Context { get; private set; } + public static event Action<string> StoryComplete; + public static List<Objective> CurrentObjectives { get; private set; } + + public static void PushObjective(string name, string desc, Func<bool> completeFunc, Action onComplete) + { + if (CurrentObjectives == null) + CurrentObjectives = new List<Objective>(); + + var currentObjective = new Objective(name, desc, completeFunc, onComplete); + CurrentObjectives.Add(currentObjective); + var t = new Thread(() => + { + var obj = currentObjective; + while (!obj.IsComplete) + { + Thread.Sleep(5000); + } + CurrentObjectives.Remove(obj); + obj.Complete(); + }); + t.IsBackground = true; + t.Start(); + + Console.WriteLine("NEW OBJECTIVE:"); + Console.WriteLine("A new objective has been added to your system. Run sos.status to find out what you need to do."); + } + + + /// <summary> + /// Starts the storyline with the specified Storyline ID. + /// </summary> + /// <param name="stid">The storyline ID to start.</param> public static void Start(string stid) { foreach (var exec in System.IO.Directory.GetFiles(Environment.CurrentDirectory)) @@ -59,8 +146,26 @@ namespace ShiftOS.Engine var story = attrib as StoryAttribute; if(story.StoryID == stid) { - mth.Invoke(null, null); - SaveSystem.CurrentSave.StoriesExperienced.Add(stid); + new Thread(() => + { + Context = new Engine.StoryContext + { + Id = stid, + Method = mth, + AutoComplete = true, + }; + SaveSystem.CurrentSave.PickupPoint = Context.Id; + Context.OnComplete += () => + { + StoryComplete?.Invoke(stid); + SaveSystem.CurrentSave.PickupPoint = null; + }; + mth.Invoke(null, null); + if (Context.AutoComplete) + { + Context.MarkComplete(); + } + }).Start(); return; } } @@ -78,231 +183,12 @@ namespace ShiftOS.Engine #endif } - + [Obsolete("Please use Story.Start() in tandem with [StoryAttribute].")] public static void RunFromInternalResource(string resource_id) { - var t = typeof(Properties.Resources); - - foreach(var prop in t.GetProperties(System.Reflection.BindingFlags.NonPublic | BindingFlags.Static)) - { - if(prop.Name == resource_id) - { - if(prop.PropertyType == typeof(string)) - { - WriteStory(prop.GetValue(null) as string); - - return; - } - } - } - throw new ArgumentException("Couldn't find resource ID inside the engine: " + resource_id); - } - - - public string Character { get; set; } - public List<string> Lines { get; set; } - - public static void Start() - { - Console.WriteLine(); - if(SaveSystem.CurrentSave.StoryPosition == 5) - { - StartDevXLies(); - } - } - - public static void StartDevXLies() - { - int chatProgress = 0; - //bool LoopStuck = false; - string textToWrite = ""; - const int TYPE_SPEED_MS = 45; - bool done = false; - bool write = true; - - while (done == false) { - write = true; - switch (chatProgress) - { - case 0: - textToWrite = "User joined: @" + SaveSystem.CurrentSave.Username; - break; - case 1: - textToWrite = $"Hello, {SaveSystem.CurrentSave.Username}."; - break; - case 2: //If C:\ShiftOS doesn't exist the player won't notice this is here. - if (Directory.Exists(Paths.GetPath("classic"))) - { - textToWrite = "I see you've participated in my previous ShiftOS experiment. Welcome back, Shifter. I assume you know lots about ShiftOS, but there are some updates I have to tell you."; - } - else - { - write = false; - } - break; - case 3: //DevX hates ShiftOS-Next secretly. - if (Directory.Exists(Paths.GetPath("classic") + "-Next")) - { - textToWrite = "Hmmmm.... looking at my sentience records, I see you've participated in ShiftOS-Next. This is gonna be difficult."; - } - else - { - write = false; - } - break; - case 4: - textToWrite = "There's a lot that has changed within ShiftOS."; - break; - case 5: - textToWrite = "First off, I want to tell you a bit about myself in case you don't already know."; - break; - case 6: - textToWrite = "My name is DevX. I am the architect of ShiftOS. I have chosen you to take part in helping me out with it."; - break; - case 7: - textToWrite = "You see, in my past attempts it has all been about an evolving operating system and seeing how the users work with it..."; - break; - case 8: - textToWrite = "Almost one hundred percent of the time, people have found out it was an experiment and they could simply return to their regular system with a specific upgrade."; - break; - case 9: - textToWrite = "But now, I want to try something different - something unique."; - break; - case 10: - textToWrite = "ShiftOS is the same as it has been in my previous attempts, but now, your goal is to gain as much wealth and power as possible."; - break; - case 11: - textToWrite = "Right now you are inside my segregation LAN. Only you and me exist within this domain. You are free from other users unless I create them."; - break; - case 12: - textToWrite = "Since you have proved your sentience, I have a task for you outside the segregation LAN."; - break; - case 13: - textToWrite = "But first... you need to be taught a few things."; - break; - case 14: - textToWrite = "First off, when I bring you into my multi-user domain, you'll first want to establish as much wealth as possible."; - break; - case 15: - textToWrite = "Wealth comes in the form of Codepoints - a currency used among users of the multi-user domain."; - break; - case 16: - textToWrite = @"You can get Codepoints by doing the following: - - - Stealing them from other users - - Extracting them from inactive/unverified sentiences - - Using specific scripts/programs within ShiftOS - - Creating paid scripts/applications within ShiftOS"; - break; - case 17: - textToWrite = "You can use Codepoints to buy upgrades using the 'shiftorium.buy' command, or you can use them to pay other users, or scripts."; - break; - case 18: - textToWrite = "Within the multi-user domain you are free to do whatever you want. Larcany, theft, deceiving, lies, and distribution of malware is permitted under my watch."; - break; - case 19: - textToWrite = "Do whatever you have to to get Codepoints."; - break; - case 20: - textToWrite = "Then use them to make yourself stronger by buying upgrades at the shiftorium."; - break; - case 21: - textToWrite = "If you want to get a bit devious within the multi-user domain, look around for scripts that will expose user account information."; - break; - case 22: - textToWrite = "Or just spread a virus around the mud."; - break; - case 23: - textToWrite = "Or you can be the 'good' guy and stop these attacks and gain the trust of other users."; - break; - case 24: - textToWrite = "It's up to you. Just, don't mess with my system. You won't want me coming to you after that. I'm watching."; - break; - case 25: - textToWrite = "User left chat: @" + SaveSystem.CurrentSave.Username; - done = true; - SaveSystem.CurrentSave.StoryPosition++; - TerminalBackend.InvokeCommand("sos.save"); - break; - - } - - if (write == true) - { - Console.WriteLine(); - Console.Write("DevX: "); - foreach(char c in textToWrite) - { - Console.Beep(750, TYPE_SPEED_MS); - if (c == '\n') - { - - } - else if (c == '\r') - { - Console.WriteLine(); - } - else - { - Console.Write(c); - } - } - Thread.Sleep(1000); - } - chatProgress += 1; - } } - public static void WriteStory(string json) - { - var thread = new Thread(new ThreadStart(() => - { - var story = JsonConvert.DeserializeObject<Story>(json); - - const int TYPESPEED = 45; - - foreach (var line in story.Lines) - { - var localized = Localization.Parse(line); - - - if (line.StartsWith("cmd:")) - { - string[] cmdsplit = line.Replace("cmd:", "").Split(' '); - switch (cmdsplit[0]) - { - case "givecp": - SaveSystem.TransferCodepointsFrom(story.Character, Convert.ToInt32(cmdsplit[1])); - break; - } - } - else - { - Console.Write(story.Character + ": "); - - foreach (var c in localized) - { - Console.Beep(1024, TYPESPEED); - if (c == '\r') - { - - } - else if (c == '\n') - Console.WriteLine(); - else - Console.Write(c); - } - - Console.WriteLine(); - Thread.Sleep(1000); - } - } - Console.Write($"{SaveSystem.CurrentSave.Username}@{SaveSystem.CurrentSave.SystemName}:~$ "); - })); - thread.IsBackground = true; - thread.Start(); - } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] @@ -322,6 +208,9 @@ namespace ShiftOS.Engine StoryID = id; } + /// <summary> + /// Gets the storyline ID stored in this attribute. + /// </summary> public string StoryID { get; private set; } } diff --git a/ShiftOS_TheReturn/TerminalBackend.cs b/ShiftOS_TheReturn/TerminalBackend.cs index 9c57aa8..09ef3d6 100644 --- a/ShiftOS_TheReturn/TerminalBackend.cs +++ b/ShiftOS_TheReturn/TerminalBackend.cs @@ -24,6 +24,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; @@ -34,12 +35,26 @@ using static ShiftOS.Engine.SaveSystem; namespace ShiftOS.Engine { + /// <summary> + /// Backend for the ShiftOS terminal. + /// </summary> public static class TerminalBackend { + /// <summary> + /// Occurs when a command is processed. + /// </summary> public static event Action<string, string> CommandProcessed; + /// <summary> + /// Gets or sets whether the current command is elevated. + /// </summary> public static bool Elevated { get; set; } + /// <summary> + /// Parses command-line arguments using the ShiftOS syntax and stores them in a <see cref="Dictionary{string, string}"/>, removing the parsed text from the original string. + /// </summary> + /// <param name="text">The text to parse.</param> + /// <returns><see cref="Dictionary{string, string}"/> containing the parsed arguments.</returns> public static Dictionary<string, string> GetArgs(ref string text) { bool shouldParse = false; @@ -64,8 +79,23 @@ namespace ShiftOS.Engine return JsonConvert.DeserializeObject<Dictionary<string, string>>(args); } + /// <summary> + /// String representing the last command entered by the user. + /// </summary> public static string LastCommand = ""; + /// <summary> + /// Gets the output of the last command. + /// </summary> + public static string LastCommandBuffer { get; private set; } + + /// <summary> + /// Invokes a ShiftOS terminal command. + /// </summary> + /// <param name="ns">The command's namespace.</param> + /// <param name="command">The command name.</param> + /// <param name="arguments">The command arguments.</param> + /// <param name="isRemote">Whether the command should be sent through Remote Terminal Session (RTS).</param> public static void InvokeCommand(string ns, string command, Dictionary<string, string> arguments, bool isRemote = false) { try @@ -78,13 +108,7 @@ namespace ShiftOS.Engine if (!commandWasClient && !string.IsNullOrWhiteSpace(ns)) { - PrefixEnabled = false; - - ServerManager.SendMessage("script", $@"{{ - user: ""{ns}"", - script: ""{command}"", - args: ""{GetSentArgs(arguments)}"" -}}"); + Console.WriteLine("Error: Command not found."); } CommandProcessed?.Invoke(ns + "." + command, JsonConvert.SerializeObject(arguments)); @@ -97,6 +121,11 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Transforms a <see cref="Dictionary{String, String}"/> of arguments to a <see cref="Dictionary{String, Object}"/>. + /// </summary> + /// <param name="argss">The original argument dictionary to convert.</param> + /// <returns>The converted dictionary.</returns> public static string GetSentArgs(Dictionary<string, string> argss) { Dictionary<string, object> args = new Dictionary<string, object>(); @@ -107,26 +136,239 @@ namespace ShiftOS.Engine return JsonConvert.SerializeObject(args); } - public static void InvokeCommand(string text, bool isRemote = false) + public class TerminalCommand { - try + public override int GetHashCode() + { + int hash = 0; + foreach (char c in ToString()) + { + hash += (int)c; + } + return hash; + } + + public Namespace NamespaceInfo { get; set; } + public Command CommandInfo { get; set; } + + public List<string> RequiredArguments { get; set; } + public string Dependencies { get; set; } + + public MethodInfo CommandHandler; + + public Type CommandType; + + public override string ToString() { - if (string.IsNullOrWhiteSpace(text)) + StringBuilder sb = new StringBuilder(); + sb.Append(this.NamespaceInfo.name); + sb.Append("."); + sb.Append(this.CommandInfo.name); + if (this.RequiredArguments.Count > 0) + { + sb.Append("{"); + foreach (var arg in RequiredArguments) + { + sb.Append(arg); + sb.Append(":"); + if (RequiredArguments.IndexOf(arg) < RequiredArguments.Count - 1) + sb.Append(','); + } + sb.Append("}"); + } + sb.Append("|"); + sb.Append(CommandHandler.Name + "()"); + return sb.ToString(); + } + + public bool RequiresElevation { get; set; } + + public void Invoke(Dictionary<string, object> args) + { + List<string> errors = new List<string>(); + bool requiresAuth = false; + if (!KernelWatchdog.IsSafe(this)) + { + if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Admin) + requiresAuth = true; + else + errors.Add("You can't run this command - you do not have permission."); + } + if (errors.Count > 0) + { + foreach (var error in errors) + { + Console.WriteLine("Command error: " + error); + } return; + } + if (requiresAuth) + { + Infobox.PromptText("Enter your password.", "This command requires you to have elevated permissions. Please enter your password to confirm this action.", (pass) => + { + if (pass == SaveSystem.CurrentUser.Password) + { + var uname = SaveSystem.CurrentUser.Username; + SaveSystem.CurrentUser = SaveSystem.CurrentSave.Users.FirstOrDefault(x => x.Username == "root"); + try + { + var h = CommandHandler; + h.Invoke(null, new[] { args }); + } + catch + { + var h = CommandHandler; + h.Invoke(null, null); + } + SaveSystem.CurrentUser = SaveSystem.CurrentSave.Users.FirstOrDefault(x => x.Username == uname); + } + else + { + Infobox.Show("Access denied.", "Incorrect password provided. The command will not run."); + } + }, true); + } + + try + { + CommandHandler.Invoke(null, new[] { args }); + } + catch + { + CommandHandler.Invoke(null, null); + } + } + } + + public class MemoryTextWriter : System.IO.TextWriter + { + public override Encoding Encoding + { + get + { + return Encoding.Unicode; + } + } + + private StringBuilder sb = null; + + public MemoryTextWriter() + { + sb = new StringBuilder(); + } + + public override string ToString() + { + return sb.ToString(); + } + + public override void Write(char value) + { + sb.Append(value); + } + + public override void WriteLine() + { + sb.AppendLine(); + } + + public override void Write(string value) + { + sb.Append(value); + } + + public override void Close() + { + sb.Clear(); + sb = null; + base.Close(); + } + + public override void WriteLine(string value) + { + sb.AppendLine(value); + } + } + + public static List<TerminalCommand> Commands { get; private set; } + + public static void PopulateTerminalCommands() + { + Commands = new List<TerminalCommand>(); + foreach(var exec in System.IO.Directory.GetFiles(Environment.CurrentDirectory)) + { + if(exec.ToLower().EndsWith(".exe") || exec.ToLower().EndsWith(".dll")) + { + try + { + var asm = Assembly.LoadFile(exec); + foreach(var type in asm.GetTypes()) + { + var ns = type.GetCustomAttributes(false).FirstOrDefault(x => x is Namespace) as Namespace; + if(ns != null) + { + foreach(var mth in type.GetMethods(BindingFlags.Public | BindingFlags.Static)) + { + var cmd = mth.GetCustomAttributes(false).FirstOrDefault(x => x is Command); + if(cmd != null) + { + var tc = new TerminalCommand(); + tc.RequiresElevation = !(type.GetCustomAttributes(false).FirstOrDefault(x => x is KernelModeAttribute) == null); + tc.NamespaceInfo = ns; + + tc.CommandInfo = cmd as Command; + tc.RequiresElevation = tc.RequiresElevation || !(mth.GetCustomAttributes(false).FirstOrDefault(x => x is KernelModeAttribute) == null); + tc.RequiredArguments = new List<string>(); + foreach (var arg in mth.GetCustomAttributes(false).Where(x=>x is RequiresArgument)) + { + var rarg = arg as RequiresArgument; + tc.RequiredArguments.Add(rarg.argument); + } + var rupg = mth.GetCustomAttributes(false).FirstOrDefault(x => x is RequiresUpgradeAttribute) as RequiresUpgradeAttribute; + if (rupg != null) + tc.Dependencies = rupg.Upgrade; + else + tc.Dependencies = ""; + tc.CommandType = type; + tc.CommandHandler = mth; + if (!Commands.Contains(tc)) + Commands.Add(tc); + } + } + } + } + } + catch(Exception e) + { + Console.WriteLine("[termdb] Error: " + e.ToString()); + } + } + } + Console.WriteLine("[termdb] " + Commands.Count + " commands found."); + } + + /// <summary> + /// Invokes a ShiftOS terminal command. + /// </summary> + /// <param name="text">The full command text in regular ShiftOS syntax</param> + /// <param name="isRemote">Whether the command should be sent through Remote Terminal Session (RTS).</param> + public static void InvokeCommand(string text, bool isRemote = false) + { + if (string.IsNullOrWhiteSpace(text)) + return; + var tw = new MemoryTextWriter(); + Console.SetOut(tw); + try + { var args = GetArgs(ref text); bool commandWasClient = RunClient(text, args, isRemote); if (!commandWasClient) { - PrefixEnabled = false; - - ServerManager.SendMessage("script", $@"{{ - user: ""{text.Split('.')[0]}"", - script: ""{text.Split('.')[1]}"", - args: ""{GetSentArgs(args)}"" -}}"); + Console.WriteLine("Error: Command not found."); + } CommandProcessed?.Invoke(text, GetSentArgs(args)); } @@ -136,21 +378,48 @@ namespace ShiftOS.Engine PrefixEnabled = true; } + string buffer = tw.ToString(); + LastCommandBuffer = buffer; + Console.SetOut(new TerminalTextWriter()); + if(!isRemote) + Console.Write(buffer); + } + /// <summary> + /// Gets or sets whether the user prefix is printed after a command completes. + /// </summary> public static bool PrefixEnabled { get; set; } + /// <summary> + /// Gets or sets whether the user is in a story plot, and thus, the terminal input should be disabled. + /// </summary> public static bool InStory { get; set; } + /// <summary> + /// Another latest command string. + /// </summary> public static string latestCommmand = ""; + /// <summary> + /// Occurs when the engine requests a Terminal to be open. + /// </summary> public static event EmptyEventHandler TerminalRequested; + /// <summary> + /// Opens a Terminal. + /// </summary> internal static void OpenTerminal() { TerminalRequested?.Invoke(); } + /// <summary> + /// Determines if the specified command method can be ran in RTS + /// </summary> + /// <param name="mth">The method to scan</param> + /// <param name="isRemote">Is the user in an RTS session?</param> + /// <returns>Whether the command can be run.</returns> public static bool CanRunRemotely(MethodInfo mth, bool isRemote) { if (!isRemote) @@ -165,12 +434,26 @@ namespace ShiftOS.Engine return true; } + /// <summary> + /// Runs a command on the client-side. + /// </summary> + /// <param name="ns">The command's namespace.</param> + /// <param name="cmd">The command name.</param> + /// <param name="args">The command's arguments.</param> + /// <param name="isRemote">Whether the command should be ran through RTS.</param> + /// <returns>Whether the command ran successfully.</returns> public static bool RunClient(string ns, string cmd, Dictionary<string, string> args, bool isRemote = false) { return RunClient(ns + "." + cmd, args, isRemote); } - + /// <summary> + /// Runs a command on the client. + /// </summary> + /// <param name="text">The command text.</param> + /// <param name="argss">The command arguments.</param> + /// <param name="isRemote">Whether the command should be ran through RTS.</param> + /// <returns>Whether the command ran successfully.</returns> public static bool RunClient(string text, Dictionary<string, string> argss, bool isRemote = false) { Dictionary<string, object> args = new Dictionary<string, object>(); @@ -181,291 +464,93 @@ namespace ShiftOS.Engine return RunClient(text, args, isRemote); } + /// <summary> + /// Runs a command on the client. + /// </summary> + /// <param name="text">The command text.</param> + /// <param name="args">The command arguments.</param> + /// <param name="isRemote">Whether the command should be run in RTS.</param> + /// <returns>Whether the command ran successfully.</returns> public static bool RunClient(string text, Dictionary<string, object> args, bool isRemote = false) { latestCommmand = text; //Console.WriteLine(text + " " + "{" + string.Join(",", args.Select(kv => kv.Key + "=" + kv.Value).ToArray()) + "}" + " " + isRemote); - foreach (var asmExec in System.IO.Directory.GetFiles(Environment.CurrentDirectory)) + + string[] split = text.Split('.'); + var cmd = Commands.FirstOrDefault(x => x.NamespaceInfo.name == split[0] && x.CommandInfo.name == split[1]); + if (cmd == null) + return false; + if (!Shiftorium.UpgradeInstalled(cmd.Dependencies)) + return false; + bool res = false; + foreach (var arg in cmd.RequiredArguments) { - try + if (!args.ContainsKey(arg)) { - var asm = Assembly.LoadFile(asmExec); - - var types = asm.GetTypes(); - foreach (var type in types) - { - if (Shiftorium.UpgradeAttributesUnlocked(type)) - { - foreach (var a in type.GetCustomAttributes(false)) - { - if (a is Namespace) - { - var ns = a as Namespace; - if (text.Split('.')[0] == ns.name) - { - if (KernelWatchdog.IsSafe(type)) - { - if (KernelWatchdog.CanRunOffline(type)) - { - foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Static)) - { - if (Shiftorium.UpgradeAttributesUnlocked(method)) - { - if (CanRunRemotely(method, isRemote)) - { - foreach (var ma in method.GetCustomAttributes(false)) - { - if (ma is Command) - { - var cmd = ma as Command; - if (text.Split('.')[1] == cmd.name) - { - if (KernelWatchdog.IsSafe(method)) - { - if (KernelWatchdog.CanRunOffline(method)) - { - var attr = method.GetCustomAttribute<CommandObsolete>(); - - if (attr != null) - { - string newcommand = attr.newcommand; - if (attr.warn) - { - Console.WriteLine(Localization.Parse((newcommand == "" ? "{ERROR}" : "{WARN}") + attr.reason, new Dictionary<string, string>() { - {"%newcommand", newcommand} - })); - } - if (newcommand != "") - { - // redo the entire process running newcommand - - return RunClient(newcommand, args); - } - } - - var requiresArgs = method.GetCustomAttributes<RequiresArgument>(); - bool error = false; - bool providedusage = false; - - foreach (RequiresArgument argument in requiresArgs) - { - if (!args.ContainsKey(argument.argument)) - { - - if (!providedusage) - { - string usageparse = "{COMMAND_" + ns.name.ToUpper() + "_" + cmd.name.ToUpper() + "_USAGE}"; - if (usageparse == Localization.Parse(usageparse)) - usageparse = ""; - else - usageparse = Shiftorium.UpgradeInstalled("help_usage") ? Localization.Parse("{ERROR}{USAGE}" + usageparse, new Dictionary<string, string>() { - {"%ns", ns.name}, - {"%cmd", cmd.name} - }) : ""; - - Console.WriteLine(usageparse); - - providedusage = true; - } - if (Shiftorium.UpgradeInstalled("help_usage")) - { - Console.WriteLine(Localization.Parse("{ERROR_ARGUMENT_REQUIRED}", new Dictionary<string, string>() { - {"%argument", argument.argument} - })); - } - else - { - Console.WriteLine(Localization.Parse("{ERROR_ARGUMENT_REQUIRED_NO_USAGE}")); - } - - error = true; - } - } - - if (error) - { - throw new Exception("{ERROR_COMMAND_WRONG}"); - } - - try - { - return (bool)method.Invoke(null, new[] { args }); - } - catch (TargetInvocationException e) - { - Console.WriteLine(Localization.Parse("{ERROR_EXCEPTION_THROWN_IN_METHOD}")); - Console.WriteLine(e.InnerException.Message); - Console.WriteLine(e.InnerException.StackTrace); - return true; - } - catch - { - return (bool)method.Invoke(null, new object[] { }); - } - } - else - { - Console.Write("<"); - ConsoleEx.Bold = true; - ConsoleEx.ForegroundColor = ConsoleColor.DarkRed; - Console.Write("session_mgr"); - ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC; - ConsoleEx.Bold = false; - Console.Write(">"); - ConsoleEx.Italic = true; - ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine(" You cannot run this command while disconnected from the multi-user domain.."); - return true; - - } - } - else - { - if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Admin) - { - Infobox.PromptText("Elevate to root mode", "This command cannot be run as a regular user. To run this command, please enter your password to elevate to root mode temporarily.", (pass) => - { - if (pass == SaveSystem.CurrentUser.Password) - { - KernelWatchdog.EnterKernelMode(); - RunClient(text, args, isRemote); - KernelWatchdog.LeaveKernelMode(); - } - else - { - Infobox.Show("Access denied.", "You did not type in the correct password."); - } - }, true); - return true; - } - Console.Write("<"); - ConsoleEx.Bold = true; - ConsoleEx.ForegroundColor = ConsoleColor.DarkRed; - Console.Write("watchdog"); - ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC; - ConsoleEx.Bold = false; - Console.Write(">"); - ConsoleEx.Italic = true; - ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine(" You cannot run this command. You do not have permission. Incident reported."); - KernelWatchdog.Log("potential_sys_breach", "user attempted to run kernel mode command " + text + " - watchdog has prevented this, good sir."); - return true; - } - } - } - - - } - } - } - else - { - Console.WriteLine(text + " cannot be ran in a remote session"); - return true; - } - } - - } - - - else - { - Console.Write("<"); - ConsoleEx.Bold = true; - ConsoleEx.ForegroundColor = ConsoleColor.DarkRed; - Console.Write("session_mgr"); - ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC; - ConsoleEx.Bold = false; - Console.Write(">"); - ConsoleEx.Italic = true; - ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine(" You cannot run this command while disconnected from the multi-user domain.."); - return true; - - } - - } - else - { - if (SaveSystem.CurrentUser.Permissions == Objects.UserPermissions.Admin) - { - Infobox.PromptText("Elevate to root mode", "This command cannot be run as a regular user. To run this command, please enter your password to elevate to root mode temporarily.", (pass) => - { - if (pass == SaveSystem.CurrentUser.Password) - { - KernelWatchdog.EnterKernelMode(); - RunClient(text, args, isRemote); - KernelWatchdog.LeaveKernelMode(); - } - else - { - Infobox.Show("Access denied.", "You did not type in the correct password."); - } - }, true); - return true; - } - Console.Write("<"); - ConsoleEx.Bold = true; - ConsoleEx.ForegroundColor = ConsoleColor.DarkRed; - Console.Write("watchdog"); - ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC; - ConsoleEx.Bold = false; - Console.Write(">"); - ConsoleEx.Italic = true; - ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine(" You cannot run this command. You do not have permission. Incident reported."); - KernelWatchdog.Log("potential_sys_breach", "user attempted to run kernel mode command " + text + " - watchdog has prevented this, good sir."); - return true; - } - } - } - } - } - } + res = true; + Console.WriteLine("You are missing an argument with the key \"" + arg + "\"."); } - catch { } } - return false; + if (res == true) + return true; + try + { + cmd.Invoke(args); + } + catch(Exception ex) + { + Console.WriteLine("Command error: " + ex.Message); + } + + return true; } + + /// <summary> + /// Prints the user prompt to the terminal. + /// </summary> public static void PrintPrompt() { + Console.WriteLine(); if (SaveSystem.CurrentSave != null && CurrentUser != null) { - ConsoleEx.BackgroundColor = SkinEngine.LoadedSkin.TerminalBackColorCC; - ConsoleEx.Italic = false; - ConsoleEx.Underline = false; - - ConsoleEx.ForegroundColor = ConsoleColor.Magenta; - ConsoleEx.Bold = true; - - Console.Write(SaveSystem.CurrentUser.Username); - ConsoleEx.Bold = false; - ConsoleEx.ForegroundColor = ConsoleColor.Gray; - Console.Write("@"); - ConsoleEx.Italic = true; - ConsoleEx.Bold = true; - ConsoleEx.ForegroundColor = ConsoleColor.Yellow; - Console.Write(SaveSystem.CurrentSave.SystemName); - ConsoleEx.Italic = false; - ConsoleEx.Bold = false; - ConsoleEx.ForegroundColor = ConsoleColor.Gray; - Console.Write(":~"); - Console.ForegroundColor = ConsoleColor.White; - ConsoleEx.Italic = true; - if (KernelWatchdog.InKernelMode == true) - Console.Write("#"); - else - Console.Write("$"); - ConsoleEx.Italic = false; - ConsoleEx.Bold = false; - ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC; - Console.Write(" "); + ConsoleEx.BackgroundColor = SkinEngine.LoadedSkin.TerminalBackColorCC; + ConsoleEx.Italic = false; + ConsoleEx.Underline = false; + + ConsoleEx.ForegroundColor = ConsoleColor.Magenta; + ConsoleEx.Bold = true; + + Console.Write(SaveSystem.CurrentUser.Username); + ConsoleEx.Bold = false; + ConsoleEx.ForegroundColor = ConsoleColor.Gray; + Console.Write("@"); + ConsoleEx.Italic = true; + ConsoleEx.Bold = true; + ConsoleEx.ForegroundColor = ConsoleColor.Yellow; + Console.Write(SaveSystem.CurrentSave.SystemName); + ConsoleEx.Italic = false; + ConsoleEx.Bold = false; + ConsoleEx.ForegroundColor = ConsoleColor.Gray; + Console.Write(":~"); + Console.ForegroundColor = ConsoleColor.White; + ConsoleEx.Italic = true; + if (KernelWatchdog.InKernelMode == true) + Console.Write("#"); + else + Console.Write("$"); + ConsoleEx.Italic = false; + ConsoleEx.Bold = false; + ConsoleEx.ForegroundColor = SkinEngine.LoadedSkin.TerminalForeColorCC; + Console.Write(" "); + ConsoleEx.Flush(); } } - + /// <summary> + /// Static constructor for <see cref="TerminalBackend"/>. + /// </summary> static TerminalBackend() { ServerMessageReceived onMessageReceived = (msg) => @@ -477,7 +562,7 @@ namespace ShiftOS.Engine if (TerminalBackend.PrefixEnabled) { - text3 = text4.Remove(0, $"{SaveSystem.CurrentSave.Username}@{SaveSystem.CurrentSave.SystemName}:~$ ".Length); + text3 = text4.Remove(0, $"{SaveSystem.CurrentUser.Username}@{SaveSystem.CurrentSave.SystemName}:~$ ".Length); } IsForwardingConsoleWrites = true; if (TerminalBackend.InStory == false) @@ -486,7 +571,7 @@ namespace ShiftOS.Engine } if (TerminalBackend.PrefixEnabled) { - Console.Write($"{SaveSystem.CurrentSave.Username}@{SaveSystem.CurrentSave.SystemName}:~$ "); + Console.Write($"{SaveSystem.CurrentUser.Username}@{SaveSystem.CurrentSave.SystemName}:~$ "); } IsForwardingConsoleWrites = false; } @@ -501,7 +586,7 @@ namespace ShiftOS.Engine string pass = a["password"] as string; string sys = a["sysname"] as string; string guid = msg.GUID; - if (SaveSystem.CurrentSave.Username == uName && SaveSystem.CurrentSave.Password == pass && CurrentSave.SystemName == sys) + if (SaveSystem.CurrentUser.Username == uName && SaveSystem.CurrentSave.Password == pass && CurrentSave.SystemName == sys) { ForwardGUID = guid; ServerManager.SendMessage("trm_handshake_accept", $@"{{ @@ -511,7 +596,7 @@ namespace ShiftOS.Engine IsForwardingConsoleWrites = true; InvokeCommand("sos.status"); - Console.Write($"{SaveSystem.CurrentSave.Username}@{SaveSystem.CurrentSave.SystemName}:~$ "); + Console.Write($"{SaveSystem.CurrentUser.Username}@{SaveSystem.CurrentSave.SystemName}:~$ "); IsForwardingConsoleWrites = false; } } @@ -520,11 +605,25 @@ namespace ShiftOS.Engine ServerManager.MessageReceived += onMessageReceived; } + /// <summary> + /// Gets whether the terminal backend is forwarding console write requests through RTS to a remote client. + /// </summary> public static bool IsForwardingConsoleWrites { get; internal set; } + + /// <summary> + /// Gets the RTS forward GUID. + /// </summary> public static string ForwardGUID { get; internal set; } + /// <summary> + /// Occurs when the user inputs text in a Terminal. + /// </summary> public static event TextSentEventHandler TextSent; + /// <summary> + /// Fakes the user inputting text to a Terminal. + /// </summary> + /// <param name="text">The text to input.</param> public static void SendText(string text) { TextSent?.Invoke(text); diff --git a/ShiftOS_TheReturn/TerminalTextWriter.cs b/ShiftOS_TheReturn/TerminalTextWriter.cs index 55e27cf..63e88eb 100644 --- a/ShiftOS_TheReturn/TerminalTextWriter.cs +++ b/ShiftOS_TheReturn/TerminalTextWriter.cs @@ -32,12 +32,28 @@ using System.Windows.Forms; namespace ShiftOS.Engine { + /// <summary> + /// Backend class for forwarding <see cref="System.Console"/> to the ShiftOS terminal. + /// </summary> public class TerminalTextWriter : TextWriter { - [System.Runtime.InteropServices.DllImport("user32.dll")] - public static extern bool LockWindowUpdate(IntPtr hWndLock); + public TerminalTextWriter() + { + ConsoleEx.OnFlush = () => + { + System.Diagnostics.Debug.WriteLine("[terminal] " + buffer); + Desktop.InvokeOnWorkerThread(() => + { + UnderlyingControl?.Write(buffer); + buffer = ""; + }); + }; + } + /// <summary> + /// Gets the encoding format for this <see cref="TextWriter"/>. God bless the Unicode Consortiem. + /// </summary> public override Encoding Encoding { get @@ -46,6 +62,9 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Gets the underlying <see cref="ITerminalWidget"/> that this <see cref="TerminalTextWriter"/> is forwarding to. + /// </summary> public ITerminalWidget UnderlyingControl { get @@ -54,6 +73,9 @@ namespace ShiftOS.Engine } } + /// <summary> + /// Moves the caret to the last character in the textbox. + /// </summary> public void select() { Desktop.InvokeOnWorkerThread(new Action(() => @@ -63,6 +85,10 @@ namespace ShiftOS.Engine })); } + /// <summary> + /// Write a character to the Terminal. + /// </summary> + /// <param name="value">The character to write.</param> public override void Write(char value) { if (TerminalBackend.IsForwardingConsoleWrites) @@ -74,14 +100,24 @@ namespace ShiftOS.Engine } else { - Desktop.InvokeOnWorkerThread(new Action(() => - { - UnderlyingControl?.Write(value.ToString()); - select(); - })); + buffer += value; + } + } + + private string buffer = ""; + + public string Buffer + { + get + { + return buffer; } } + /// <summary> + /// Write text to the Terminal, followed by a newline. + /// </summary> + /// <param name="value">The text to write.</param> public override void WriteLine(string value) { if (TerminalBackend.IsForwardingConsoleWrites) @@ -94,18 +130,20 @@ namespace ShiftOS.Engine else { - Desktop.InvokeOnWorkerThread(new Action(() => - { - UnderlyingControl?.WriteLine(value); - select(); - })); + buffer += value + Environment.NewLine; + ConsoleEx.Flush(); } } + [Obsolete("Stub.")] public void SetLastText() { } + /// <summary> + /// Write text to the Terminal. + /// </summary> + /// <param name="value">The text to write.</param> public override void Write(string value) { if (TerminalBackend.IsForwardingConsoleWrites) @@ -120,8 +158,7 @@ namespace ShiftOS.Engine Desktop.InvokeOnWorkerThread(new Action(() => { - UnderlyingControl?.Write(value.ToString()); - select(); + buffer += value; })); } } diff --git a/ShiftOS_TheReturn/TutorialManager.cs b/ShiftOS_TheReturn/TutorialManager.cs index ea78cd8..13df153 100644 --- a/ShiftOS_TheReturn/TutorialManager.cs +++ b/ShiftOS_TheReturn/TutorialManager.cs @@ -30,10 +30,18 @@ using System.Threading.Tasks; namespace ShiftOS.Engine { + [Obsolete("This isn't used... I don't think...")] public static class TutorialManager { + /// <summary> + /// The tutorial frontend. + /// </summary> private static ITutorial _tut = null; + /// <summary> + /// Registers a tutorial frontend to the backend. + /// </summary> + /// <param name="tut"></param> public static void RegisterTutorial(ITutorial tut) { IsInTutorial = false; diff --git a/ShiftOS_TheReturn/UniteClient.cs b/ShiftOS_TheReturn/UniteClient.cs deleted file mode 100644 index 1136b5c..0000000 --- a/ShiftOS_TheReturn/UniteClient.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json; - -namespace ShiftOS.Unite -{ - public class UniteClient - { - public string Token { get; private set; } - public string BaseURL { get; private set; } - - public string GetDisplayNameId(string id) - { - return MakeCall("/API/GetDisplayName/" + id); - } - - public PongHighscoreModel GetPongHighscores() - { - return JsonConvert.DeserializeObject<PongHighscoreModel>(MakeCall("/API/GetPongHighscores")); - } - - public UniteClient(string baseurl, string usertoken) - { - BaseURL = baseurl; - Token = usertoken; - } - - internal string MakeCall(string url) - { - var webrequest = WebRequest.Create(BaseURL + url); - webrequest.Headers.Add("Authentication: Token " + Token); - using (var response = webrequest.GetResponse()) - { - using (var stream = response.GetResponseStream()) - { - using (var reader = new System.IO.StreamReader(stream)) - { - return reader.ReadToEnd(); - } - } - } - } - - public int GetPongCP() - { - return Convert.ToInt32(MakeCall("/API/GetPongCP")); - } - - public int GetPongLevel() - { - return Convert.ToInt32(MakeCall("/API/GetPongLevel")); - } - - public void SetPongLevel(int value) - { - MakeCall("/API/SetPongLevel/" + value.ToString()); - } - - public void SetPongCP(int value) - { - MakeCall("/API/SetPongCP/" + value.ToString()); - } - - public string GetEmail() - { - return MakeCall("/API/GetEmail"); - } - - public string GetSysName() - { - return MakeCall("/API/GetSysName"); - } - - public void SetSysName(string value) - { - MakeCall("/API/SetSysName/" + value); - } - - public string GetDisplayName() - { - return MakeCall("/API/GetDisplayName"); - } - - public void SetDisplayName(string value) - { - MakeCall("/API/SetDisplayName/" + value.ToString()); - } - - public string GetFullName() - { - return MakeCall("/API/GetFullName"); - } - - public void SetFullName(string value) - { - MakeCall("/API/SetFullName/" + value.ToString()); - } - - - public long GetCodepoints() - { - return Convert.ToInt64(MakeCall("/API/GetCodepoints")); - } - - public void SetCodepoints(long value) - { - MakeCall("/API/SetCodepoints/" + value.ToString()); - } - } - - public class PongHighscoreModel - { - public int Pages { get; set; } - public PongHighscore[] Highscores { get; set; } - } - - public class PongHighscore - { - public string UserId { get; set; } - public int Level { get; set; } - public long CodepointsCashout { get; set; } - } -} diff --git a/ShiftOS_TheReturn/UniteTestCommands.cs b/ShiftOS_TheReturn/UniteTestCommands.cs deleted file mode 100644 index 7a83e44..0000000 --- a/ShiftOS_TheReturn/UniteTestCommands.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; - -namespace ShiftOS.Engine -{ - [Namespace("unite")] - public static class UniteTestCommands - { - [Command("setdisplayname")] - [RequiresArgument("name")] - public static bool SetDisplayName(Dictionary<string, object> args) - { - string dname = args["name"].ToString(); - var unite = new ShiftOS.Unite.UniteClient("http://getshiftos.ml", SaveSystem.CurrentSave.UniteAuthToken); - unite.SetDisplayName(dname); - return true; - } - - [Command("login")] - [RequiresArgument("username")] - [RequiresArgument("password")] - public static bool LoginTest(Dictionary<string, object> args) - { - string u = args["username"].ToString(); - string p = args["password"].ToString(); - var webrequest = HttpWebRequest.Create("http://getshiftos.ml/Auth/Login?appname=ShiftOS&appdesc=ShiftOS+client&version=1_0_beta_2_4"); - string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{u}:{p}")); - webrequest.Headers.Add("Authentication: Basic " + base64); - var response = webrequest.GetResponse(); - var str = response.GetResponseStream(); - var reader = new System.IO.StreamReader(str); - string result = reader.ReadToEnd(); - Console.WriteLine("Server returned: " + result); - reader.Close(); - str.Close(); - str.Dispose(); - response.Dispose(); - - return true; - } - - [Command("linklogin")] - [RequiresArgument("username")] - [RequiresArgument("password")] - public static bool LinkLogin(Dictionary<string, object> args) - { - string u = args["username"].ToString(); - string p = args["password"].ToString(); - var webrequest = HttpWebRequest.Create("http://getshiftos.ml/Auth/Login?appname=ShiftOS&appdesc=ShiftOS+client&version=1_0_beta_2_4"); - string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{u}:{p}")); - webrequest.Headers.Add("Authentication: Basic " + base64); - var response = webrequest.GetResponse(); - var str = response.GetResponseStream(); - var reader = new System.IO.StreamReader(str); - string result = reader.ReadToEnd(); - //If we get this far, the token is OURS. :D - SaveSystem.CurrentSave.UniteAuthToken = result; - Console.WriteLine("Unite says \"Access Granted!\""); - SaveSystem.SaveGame(); - reader.Close(); - str.Close(); - str.Dispose(); - response.Dispose(); - - return true; - } - - - } -} diff --git a/ShiftOS_TheReturn/UserManagementCommands.cs b/ShiftOS_TheReturn/UserManagementCommands.cs index 1c3c0ed..a64c99c 100644 --- a/ShiftOS_TheReturn/UserManagementCommands.cs +++ b/ShiftOS_TheReturn/UserManagementCommands.cs @@ -7,11 +7,19 @@ using ShiftOS.Objects; namespace ShiftOS.Engine { + /// <summary> + /// Administrative user management terminal commands. + /// </summary> [Namespace("admin")] [KernelMode] [RequiresUpgrade("mud_fundamentals")] public static class AdminUserManagementCommands { + /// <summary> + /// Add a user to the system. + /// </summary> + /// <param name="args">Command arguments.</param> + /// <returns>Command result.</returns> [Command("add", description = "Add a user to the system.", usage ="name:")] [RequiresArgument("name")] public static bool AddUser(Dictionary<string, object> args) @@ -35,6 +43,12 @@ namespace ShiftOS.Engine return true; } + /// <summary> + /// Remove a user from the system. + /// </summary> + /// <param name="args">Command arguments.</param> + /// <returns>Command result.</returns> + [Command("remove", description = "Remove a user from the system.", usage = "name:")] [RequiresArgument("name")] public static bool RemoveUser(Dictionary<string, object> args) @@ -47,12 +61,25 @@ namespace ShiftOS.Engine } var user = SaveSystem.CurrentSave.Users.FirstOrDefault(x => x.Username == name); + if(user.Username != SaveSystem.CurrentUser.Username) + { + Console.WriteLine("Error: Cannot remove yourself."); + return true; + } SaveSystem.CurrentSave.Users.Remove(user); Console.WriteLine($"Removing user \"{name}\" from system..."); SaveSystem.SaveGame(); return true; } + + + /// <summary> + /// Set access control level for a user. + /// </summary> + /// <param name="args">Command arguments.</param> + /// <returns>Command result.</returns> + [Command("set_acl")] [RequiresArgument("user")] [RequiresArgument("val")] @@ -116,14 +143,76 @@ namespace ShiftOS.Engine return true; } + /// <summary> + /// List all users in the system. + /// </summary> + /// <param name="args">Command arguments.</param> + /// <returns>Command result.</returns> + + [Command("users", description = "Get a list of all users on the system.")] + public static bool GetUsers() + { + foreach (var u in SaveSystem.CurrentSave.Users) + { + if (u.Username == SaveSystem.CurrentUser.Username) + { + ConsoleEx.ForegroundColor = ConsoleColor.Magenta; + ConsoleEx.Bold = true; + } + else + { + ConsoleEx.ForegroundColor = ConsoleColor.Gray; + ConsoleEx.Bold = false; + } + Console.WriteLine(u.Username); + } + return true; + } } + /// <summary> + /// Non-administrative user management terminal commands. + /// </summary> [Namespace("user")] [RequiresUpgrade("mud_fundamentals")] public static class UserManagementCommands { + /// <summary> + /// Log in as another user. + /// </summary> + /// <param name="args">Command arguments.</param> + /// <returns>Command result.</returns> + [Command("login", description = "Log in as another user.")] + [RequiresArgument("user")] + [RequiresArgument("pass")] + public static bool Login(Dictionary<string, object> args) + { + string user = args["user"].ToString(); + string pass = args["pass"].ToString(); + + var usr = SaveSystem.CurrentSave.Users.FirstOrDefault(x => x.Username == user); + if(usr==null) + { + Console.WriteLine("Error: No such user."); + return true; + } + if (usr.Password != pass) + { + Console.WriteLine("Access denied."); + return true; + } + + SaveSystem.CurrentUser = usr; + Console.WriteLine("Access granted."); + return true; + } + /// <summary> + /// Set the password for the current user. + /// </summary> + /// <param name="args">Command arguments.</param> + /// <returns>Command result.</returns> [Command("setpass", description ="Allows you to set your password to a new value.", usage ="old:,new:")] [RequiresArgument("old")] [RequiresArgument("new")] diff --git a/ShiftOS_TheReturn/VirusEngine.cs b/ShiftOS_TheReturn/VirusEngine.cs deleted file mode 100644 index 650db92..0000000 --- a/ShiftOS_TheReturn/VirusEngine.cs +++ /dev/null @@ -1,106 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json; -using static ShiftOS.Objects.ShiftFS.Utils; - - -namespace ShiftOS.Engine -{ - public static class VirusEngine - { - public static void InfectFile(string file, string virusid) - { - var infected = new List<string>(); - var hData = GetHeaderText(file); - - if (hData == "") - { - infected.Add(virusid); - } - else - { - infected = JsonConvert.DeserializeObject<List<string>>(hData); - if (!infected.Contains(virusid)) - infected.Add(virusid); - } - - SetHeaderText(file, JsonConvert.SerializeObject(infected)); - } - - public static void DisinfectFile(string file, int threatlevel) - { - var infected = new List<string>(); - var hData = GetHeaderText(file); - - if (hData != "") - { - infected = JsonConvert.DeserializeObject<List<string>>(hData); - for (int i = 0; i < infected.Count; i++) - { - string[] splitID = infected[i].Split('.'); - int th = Convert.ToInt32(splitID[splitID.Length - 1]); - if (th <= threatlevel) - { - infected.RemoveAt(i); - } - } - } - - SetHeaderText(file, JsonConvert.SerializeObject(infected)); - } - - internal static string[] FindAllVirusesInFile(string file) - { - string hdata = GetHeaderText(file); - return (hdata != "") ? new string[0] : JsonConvert.DeserializeObject<string[]>(hdata); - } - - } - - public abstract class Virus - { - /// <summary> - /// Inject the virus into system memory by running it. - /// </summary> - public abstract void Activate(); - - /// <summary> - /// Terminate the virus. - /// </summary> - public abstract void Deactivate(); - - public abstract int ThreatLevel { get; } - - public abstract string Signature { get; } - - public abstract string Type { get; } - } -} |
