diff --git a/src/SlatedGameToolkit.Framework/Exceptions/SDLException.cs b/src/SlatedGameToolkit.Framework/Exceptions/SDLException.cs index afe4ff4..cb7eb89 100644 --- a/src/SlatedGameToolkit.Framework/Exceptions/SDLException.cs +++ b/src/SlatedGameToolkit.Framework/Exceptions/SDLException.cs @@ -14,18 +14,21 @@ namespace SlatedGameToolkit.Framework.Exceptions /// Creates an SDL exception. /// /// Whether or not to fetch the last error message that occurred in SDL. - public SDLException(bool autoFlush = true) : base() { + public SDLException(bool autoFlush = true) : base(autoFlush ? SDL.SDL_GetError() : "SDL error has occurred.") { if (autoFlush) { SDLMessage = SDL.SDL_GetError(); SDL.SDL_ClearError(); } } - public SDLException(string message, Exception inner) : base(message, inner) { - + public SDLException(string message, bool autoFlush = true) : base(message + " (" + (autoFlush ? SDL.SDL_GetError() : "SDL error has occurred.") + ")") { + if (autoFlush) { + SDLMessage = SDL.SDL_GetError(); + SDL.SDL_ClearError(); + } } - public SDLException(string message, bool autoFlush = true) : base(message) { + public SDLException(string message, Exception inner, bool autoFlush = true) : base(message + " (" + (autoFlush ? SDL.SDL_GetError() : "SDL error has occurred.") + ")", inner) { if (autoFlush) { SDLMessage = SDL.SDL_GetError(); SDL.SDL_ClearError(); diff --git a/src/SlatedGameToolkit.Framework/GameEngine.cs b/src/SlatedGameToolkit.Framework/GameEngine.cs index a7c564d..7fbe809 100644 --- a/src/SlatedGameToolkit.Framework/GameEngine.cs +++ b/src/SlatedGameToolkit.Framework/GameEngine.cs @@ -17,47 +17,69 @@ namespace SlatedGameToolkit.Framework { CreateLogger(); private static readonly object ignitionLock = new object(); private static Thread thread; - private static volatile bool exit = false, loopCompleted = true; - private static long updateDeltaTime = 50, frameDeltaTime = 0; + private static volatile bool exit = false, stopped = true; + private static volatile bool deltaChanged = true; + private static long updateDeltaTime = 25, frameDeltaTime = 16; /// /// The amount of updates per second. /// Is floored to milleseconds. + /// Maximum updates per second is 200. /// /// The updates per second. public static double UpdatesPerSecond { get { - return TimeSpan.FromMilliseconds(1 / updateDeltaTime).TotalSeconds; + return 1 / TimeSpan.FromMilliseconds(updateDeltaTime).TotalSeconds; } set { - Interlocked.Exchange(ref updateDeltaTime, (long) TimeSpan.FromSeconds(1 / value).TotalMilliseconds); + Interlocked.Exchange(ref updateDeltaTime, Math.Min(5, (long) TimeSpan.FromSeconds(1 / value).TotalMilliseconds)); + deltaChanged = true; } } /// /// The target frames per second. Will not go above this number, but may dip below this number. - /// This value is floored to millesconds. + /// This value is floored to millesconds units. + /// Setting this to 0 renders as many frames as the system can handle. + /// Default is 60. /// /// The target frames per second. public static double targetFPS { get { - return TimeSpan.FromMilliseconds(1 / frameDeltaTime).TotalSeconds; + if (frameDeltaTime == 0) return 0; + return 1 / TimeSpan.FromMilliseconds(frameDeltaTime).TotalSeconds; } set { + if (value == 0) Interlocked.Exchange(ref frameDeltaTime, 0); Interlocked.Exchange(ref frameDeltaTime, (long) TimeSpan.FromSeconds(1 / value).TotalMilliseconds); + deltaChanged = true; + } + } + + public static bool Running { + get { + return !stopped; } } private static void Loop(Object o) { if (!(o is Manager)) throw new InternalFrameworkException(String.Format("Expected manager object for asynchronous loop. Got {0}", o)); Manager manager = (Manager) o; + manager.initialize(); long currentTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); long timePassedFromLastUpdate = 0; long timePassedFromLastRender = 0; - loopCompleted = false; + long updateDeltaTime = 0; + long frameDeltaTime = 0; + stopped = false; + logger.Information("Game engine initiated."); while (!exit) { - long updateDeltaTime = Interlocked.Read(ref GameEngine.updateDeltaTime); - long frameDeltaTime = Interlocked.Read(ref GameEngine.frameDeltaTime); + if (deltaChanged) { + updateDeltaTime = Interlocked.Read(ref GameEngine.updateDeltaTime); + frameDeltaTime = Interlocked.Read(ref GameEngine.frameDeltaTime); + deltaChanged = false; + } + long frameStart = DateTimeOffset.Now.ToUnixTimeMilliseconds(); long difference = frameStart - currentTime; currentTime = frameStart; @@ -74,8 +96,10 @@ namespace SlatedGameToolkit.Framework { timePassedFromLastRender = 0; } } - loopCompleted = true; + manager.Dispose(); + stopped = true; SDL.SDL_Quit(); + logger.Information("Game engine has stopped."); } /// @@ -96,28 +120,25 @@ namespace SlatedGameToolkit.Framework { /// Requests to start the game engine. /// /// True iff the engine is not already running. - private static bool Ignite(Manager manager) { + public static bool Ignite(Manager manager) { if (manager == null) throw new ArgumentNullException("manager"); SDL.SDL_version SDLVersion; SDL.SDL_version SDLBuiltVersion; SDL.SDL_GetVersion(out SDLVersion); SDL.SDL_VERSION(out SDLBuiltVersion); - logger.Information(String.Format("Attempting to initiate game engine with SDL version: {0}", SDLVersion.ToString())); - if (SDL.SDL_VERSION_ATLEAST(SDLBuiltVersion.major, SDLBuiltVersion.minor, SDLBuiltVersion.patch)) { - logger.Warning(String.Format("Engine was designed with SDL version {0}, currently running on {1}", SDLVersion.ToString(), SDLBuiltVersion.ToString())); + logger.Information(String.Format("Attempting to initiate game engine with SDL version: {0}.{1}.{2}", SDLVersion.major, SDLVersion.minor, SDLVersion.patch)); + if (!SDL.SDL_VERSION_ATLEAST(SDLBuiltVersion.major, SDLBuiltVersion.minor, SDLBuiltVersion.patch)) { + logger.Warning(String.Format("Engine was designed with SDL version {0}.{1}.{2}, currently running on {3}.{4}.{5}", SDLBuiltVersion.major, SDLBuiltVersion.minor, SDLBuiltVersion.patch, SDLVersion.major, SDLVersion.minor, SDLVersion.patch)); if (SDLVersion.major < 2) { logger.Error("This engine was designed to work with SDL2. The version you're currently running is severely outdated."); throw new FrameworkUsageException("Outdated SDL binaries."); } } lock (ignitionLock) { - if (!loopCompleted) return false; - loopCompleted = false; - - if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 0) throw new FrameworkSDLException(); - if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 1) < 0) throw new FrameworkSDLException(); - if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE) < 0) throw new FrameworkSDLException(); - + if (!stopped) { + logger.Warning("Engine is already running."); + return false; + } exit = false; if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO) != 0) { throw new FrameworkSDLException(); @@ -125,6 +146,11 @@ namespace SlatedGameToolkit.Framework { if (SDL.SDL_Init(SDL.SDL_INIT_AUDIO) != 0) { throw new FrameworkSDLException(); } + + if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 0) throw new FrameworkSDLException(); + if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 1) < 0) throw new FrameworkSDLException(); + if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE) < 0) throw new FrameworkSDLException(); + thread = new Thread(Loop); thread.Start(manager); return true; diff --git a/src/SlatedGameToolkit.Framework/Graphics/Window/WindowHandle.cs b/src/SlatedGameToolkit.Framework/Graphics/Window/WindowHandle.cs index 56ec6f1..eea8dfe 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Window/WindowHandle.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Window/WindowHandle.cs @@ -207,9 +207,13 @@ namespace SlatedGameToolkit.Framework.Graphics.Window /// Makes this window the window that is currently being drawn to. /// More specifically, it sets the OpenGL Context associated with this window to be the one /// that is actively receiving all OpenGL calls. + /// + /// If the current context is already of this window, nothing happens. /// public void DrawToWindow() { - SDL.SDL_GL_MakeCurrent(window, glContext); + if (SDL.SDL_GL_GetCurrentContext() != glContext) { + SDL.SDL_GL_MakeCurrent(window, glContext); + } } /// diff --git a/src/SlatedGameToolkit.Framework/Input/InputMatrix.cs b/src/SlatedGameToolkit.Framework/Input/InputMatrix.cs deleted file mode 100644 index 37c322f..0000000 --- a/src/SlatedGameToolkit.Framework/Input/InputMatrix.cs +++ /dev/null @@ -1,13 +0,0 @@ -using SDL2; - -namespace SlatedGameToolkit.Framework.Input -{ - internal class InputMatrix { - public void update() { - SDL.SDL_Event inputEvent; - while (SDL.SDL_PollEvent(out inputEvent) != 0) { - - } - } - } -} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/StateSystem/Manager.cs b/src/SlatedGameToolkit.Framework/StateSystem/Manager.cs index 29f38ff..0bcff20 100644 --- a/src/SlatedGameToolkit.Framework/StateSystem/Manager.cs +++ b/src/SlatedGameToolkit.Framework/StateSystem/Manager.cs @@ -26,6 +26,10 @@ namespace SlatedGameToolkit.Framework.StateSystem if (!this.states.TryGetValue(initialState, out currentState)) throw new ArgumentException("The requested initial state name does not exist in the provided list of states."); } + internal void initialize() { + thread = Thread.CurrentThread; + } + internal void update(double delta) { if (nextState != null) { if (currentState.Deactivate() & nextState.Activate()) { diff --git a/src/SlatedGameToolkit.Tools/Commands/GraphicalPlaygroundCommand.cs b/src/SlatedGameToolkit.Tools/Commands/GraphicalPlaygroundCommand.cs new file mode 100644 index 0000000..705f3a7 --- /dev/null +++ b/src/SlatedGameToolkit.Tools/Commands/GraphicalPlaygroundCommand.cs @@ -0,0 +1,60 @@ +using SlatedGameToolkit.Framework; +using SlatedGameToolkit.Framework.StateSystem; +using SlatedGameToolkit.Tools.System; +using SlatedGameToolkit.Tools.System.Interaction; +using SlatedGameToolkit.Tools.Utilities.GraphicalPlayground; + +namespace SlatedGameToolkit.Tools.Commands +{ + public class GraphicalPlaygroundCommand : IInvocable + { + private readonly string[] invokers = new string[] {"playground"}; + + public bool Execute(IInteractable interactable, string[] args) + { + if (args.Length != 1) return false; + if (args[0].Equals("start")) { + if (GameEngine.Running) { + interactable.Tell("Engine is already running!"); + return true; + } + Manager manager = new Manager("main state", new MainState()); + GameEngine.Ignite(manager); + return true; + } else if (args[0].Equals("stop")) { + if (!GameEngine.Running) { + interactable.Tell("Engine was never running!"); + return true; + } + GameEngine.Stop(); + return true; + } else if (args[0].Equals("status")) { + interactable.Tell("Running: " + GameEngine.Running); + interactable.Tell("Target FPS: " + GameEngine.targetFPS); + interactable.Tell("Target Update Rate: " + GameEngine.UpdatesPerSecond); + return true; + } + return false; + } + + public string getDescription() + { + return "Starts and stops the graphical playground."; + } + + public string[] GetInvokers() + { + return invokers; + } + + public string getUsage(string arg) + { + return "Usage: \"playground [start | stop | status]\" the required argument being whether to start, stop or get the current status of the game engine."; + } + + public void Dispose() + { + GameEngine.Stop(); + } + } +} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Tools/Commands/HelpCommand.cs b/src/SlatedGameToolkit.Tools/Commands/HelpCommand.cs index fe25e82..f3a23f5 100644 --- a/src/SlatedGameToolkit.Tools/Commands/HelpCommand.cs +++ b/src/SlatedGameToolkit.Tools/Commands/HelpCommand.cs @@ -52,5 +52,10 @@ namespace SlatedGameToolkit.Tools.Commands { return invokers; } + + public void Dispose() + { + + } } } \ No newline at end of file diff --git a/src/SlatedGameToolkit.Tools/Commands/StopCommand.cs b/src/SlatedGameToolkit.Tools/Commands/StopCommand.cs index 02609d1..b189519 100644 --- a/src/SlatedGameToolkit.Tools/Commands/StopCommand.cs +++ b/src/SlatedGameToolkit.Tools/Commands/StopCommand.cs @@ -27,5 +27,9 @@ namespace SlatedGameToolkit.Tools.Commands { return invokers; } + + public void Dispose() + { + } } } \ No newline at end of file diff --git a/src/SlatedGameToolkit.Tools/Program.cs b/src/SlatedGameToolkit.Tools/Program.cs index 03dcb02..acc9ac5 100644 --- a/src/SlatedGameToolkit.Tools/Program.cs +++ b/src/SlatedGameToolkit.Tools/Program.cs @@ -11,8 +11,10 @@ namespace SlatedGameToolkit.Tools static private bool running; static void Main(string[] args) { - CommandMap commands = new CommandMap(new StopCommand()); + CommandMap commands = new CommandMap(); + commands.Add(new StopCommand()); commands.Add(new HelpCommand(commands)); + commands.Add(new GraphicalPlaygroundCommand()); CommandProcessor processor = new CommandProcessor("The command \"{input}\" was not understood. Please type \"help\" for more information.", commands); AssemblyName name = Assembly.GetExecutingAssembly().GetName(); ConsoleInteraction consoleInteracter = new ConsoleInteraction("Tools"); @@ -24,6 +26,7 @@ namespace SlatedGameToolkit.Tools processor.Process(consoleInteracter); } consoleInteracter.Tell("Exiting tool."); + commands.Dispose(); } public static void Stop() { diff --git a/src/SlatedGameToolkit.Tools/System/CommandMap.cs b/src/SlatedGameToolkit.Tools/System/CommandMap.cs index bb35897..f62a3da 100644 --- a/src/SlatedGameToolkit.Tools/System/CommandMap.cs +++ b/src/SlatedGameToolkit.Tools/System/CommandMap.cs @@ -5,7 +5,7 @@ using SlatedGameToolkit.Tools.System.Interaction; namespace SlatedGameToolkit.Tools.System { - public class CommandMap : ICollection { + public class CommandMap : ICollection, IDisposable { Dictionary invocations; HashSet values; @@ -79,5 +79,17 @@ namespace SlatedGameToolkit.Tools.System values.Remove(item); return true; } + + public void Dispose() + { + foreach (IInvocable invocable in this) + { + invocable.Dispose(); + } + } + + ~CommandMap() { + Dispose(); + } } } \ No newline at end of file diff --git a/src/SlatedGameToolkit.Tools/System/IInvocable.cs b/src/SlatedGameToolkit.Tools/System/IInvocable.cs index 6beb0c0..f27f63c 100644 --- a/src/SlatedGameToolkit.Tools/System/IInvocable.cs +++ b/src/SlatedGameToolkit.Tools/System/IInvocable.cs @@ -1,8 +1,9 @@ +using System; using SlatedGameToolkit.Tools.System.Interaction; namespace SlatedGameToolkit.Tools.System { - public interface IInvocable + public interface IInvocable : IDisposable { /// /// Invokers are the strings that should invoke this command. diff --git a/src/SlatedGameToolkit.Tools/System/Interaction/IInteractable.cs b/src/SlatedGameToolkit.Tools/System/Interaction/IInteractable.cs index 0aa362c..1530574 100644 --- a/src/SlatedGameToolkit.Tools/System/Interaction/IInteractable.cs +++ b/src/SlatedGameToolkit.Tools/System/Interaction/IInteractable.cs @@ -4,7 +4,6 @@ namespace SlatedGameToolkit.Tools.System.Interaction { void Tell(string message); void Separate(); - string Listen(); } } \ No newline at end of file diff --git a/src/SlatedGameToolkit.Tools/Utilities/GraphicalPlayground/MainState.cs b/src/SlatedGameToolkit.Tools/Utilities/GraphicalPlayground/MainState.cs new file mode 100644 index 0000000..0fd08f5 --- /dev/null +++ b/src/SlatedGameToolkit.Tools/Utilities/GraphicalPlayground/MainState.cs @@ -0,0 +1,40 @@ +using SlatedGameToolkit.Framework.StateSystem; +using SlatedGameToolkit.Framework.StateSystem.States; + +namespace SlatedGameToolkit.Tools.Utilities.GraphicalPlayground +{ + public class MainState : IState + { + public bool Activate() + { + return true; + } + + public bool Deactivate() + { + return true; + } + + public void Dispose() + { + } + + public string getName() + { + return "main state"; + } + + public void Initialize(Manager manager) + { + + } + + public void Render(double delta) + { + } + + public void Update(double delta) + { + } + } +} \ No newline at end of file