Began working on a graphical playground for testing.

Improved SDL exception class.

Engine code changes.

General progress.
This commit is contained in:
Harrison Deng 2020-05-27 00:20:41 -05:00
parent 0f0395fd63
commit a013c476e7
13 changed files with 191 additions and 43 deletions

View File

@ -14,18 +14,21 @@ namespace SlatedGameToolkit.Framework.Exceptions
/// Creates an SDL exception. /// Creates an SDL exception.
/// </summary> /// </summary>
/// <param name="Fetch">Whether or not to fetch the last error message that occurred in SDL.</param> /// <param name="Fetch">Whether or not to fetch the last error message that occurred in SDL.</param>
public SDLException(bool autoFlush = true) : base() { public SDLException(bool autoFlush = true) : base(autoFlush ? SDL.SDL_GetError() : "SDL error has occurred.") {
if (autoFlush) { if (autoFlush) {
SDLMessage = SDL.SDL_GetError(); SDLMessage = SDL.SDL_GetError();
SDL.SDL_ClearError(); 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) { if (autoFlush) {
SDLMessage = SDL.SDL_GetError(); SDLMessage = SDL.SDL_GetError();
SDL.SDL_ClearError(); SDL.SDL_ClearError();

View File

@ -17,47 +17,69 @@ namespace SlatedGameToolkit.Framework {
CreateLogger(); CreateLogger();
private static readonly object ignitionLock = new object(); private static readonly object ignitionLock = new object();
private static Thread thread; private static Thread thread;
private static volatile bool exit = false, loopCompleted = true; private static volatile bool exit = false, stopped = true;
private static long updateDeltaTime = 50, frameDeltaTime = 0; private static volatile bool deltaChanged = true;
private static long updateDeltaTime = 25, frameDeltaTime = 16;
/// <summary> /// <summary>
/// The amount of updates per second. /// The amount of updates per second.
/// Is floored to milleseconds. /// Is floored to milleseconds.
/// Maximum updates per second is 200.
/// </summary> /// </summary>
/// <value>The updates per second.</value> /// <value>The updates per second.</value>
public static double UpdatesPerSecond { public static double UpdatesPerSecond {
get { get {
return TimeSpan.FromMilliseconds(1 / updateDeltaTime).TotalSeconds; return 1 / TimeSpan.FromMilliseconds(updateDeltaTime).TotalSeconds;
} }
set { 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;
} }
} }
/// <summary> /// <summary>
/// The target frames per second. Will not go above this number, but may dip below this number. /// 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.
/// </summary> /// </summary>
/// <value>The target frames per second.</value> /// <value>The target frames per second.</value>
public static double targetFPS { public static double targetFPS {
get { get {
return TimeSpan.FromMilliseconds(1 / frameDeltaTime).TotalSeconds; if (frameDeltaTime == 0) return 0;
return 1 / TimeSpan.FromMilliseconds(frameDeltaTime).TotalSeconds;
} }
set { set {
if (value == 0) Interlocked.Exchange(ref frameDeltaTime, 0);
Interlocked.Exchange(ref frameDeltaTime, (long) TimeSpan.FromSeconds(1 / value).TotalMilliseconds); 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) { private static void Loop(Object o) {
if (!(o is Manager)) throw new InternalFrameworkException(String.Format("Expected manager object for asynchronous loop. Got {0}", o)); if (!(o is Manager)) throw new InternalFrameworkException(String.Format("Expected manager object for asynchronous loop. Got {0}", o));
Manager manager = (Manager) o; Manager manager = (Manager) o;
manager.initialize();
long currentTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); long currentTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
long timePassedFromLastUpdate = 0; long timePassedFromLastUpdate = 0;
long timePassedFromLastRender = 0; long timePassedFromLastRender = 0;
loopCompleted = false; long updateDeltaTime = 0;
long frameDeltaTime = 0;
stopped = false;
logger.Information("Game engine initiated.");
while (!exit) { while (!exit) {
long updateDeltaTime = Interlocked.Read(ref GameEngine.updateDeltaTime); if (deltaChanged) {
long frameDeltaTime = Interlocked.Read(ref GameEngine.frameDeltaTime); updateDeltaTime = Interlocked.Read(ref GameEngine.updateDeltaTime);
frameDeltaTime = Interlocked.Read(ref GameEngine.frameDeltaTime);
deltaChanged = false;
}
long frameStart = DateTimeOffset.Now.ToUnixTimeMilliseconds(); long frameStart = DateTimeOffset.Now.ToUnixTimeMilliseconds();
long difference = frameStart - currentTime; long difference = frameStart - currentTime;
currentTime = frameStart; currentTime = frameStart;
@ -74,8 +96,10 @@ namespace SlatedGameToolkit.Framework {
timePassedFromLastRender = 0; timePassedFromLastRender = 0;
} }
} }
loopCompleted = true; manager.Dispose();
stopped = true;
SDL.SDL_Quit(); SDL.SDL_Quit();
logger.Information("Game engine has stopped.");
} }
/// <summary> /// <summary>
@ -96,28 +120,25 @@ namespace SlatedGameToolkit.Framework {
/// Requests to start the game engine. /// Requests to start the game engine.
/// </summary> /// </summary>
/// <returns>True iff the engine is not already running.</returns> /// <returns>True iff the engine is not already running.</returns>
private static bool Ignite(Manager manager) { public static bool Ignite(Manager manager) {
if (manager == null) throw new ArgumentNullException("manager"); if (manager == null) throw new ArgumentNullException("manager");
SDL.SDL_version SDLVersion; SDL.SDL_version SDLVersion;
SDL.SDL_version SDLBuiltVersion; SDL.SDL_version SDLBuiltVersion;
SDL.SDL_GetVersion(out SDLVersion); SDL.SDL_GetVersion(out SDLVersion);
SDL.SDL_VERSION(out SDLBuiltVersion); SDL.SDL_VERSION(out SDLBuiltVersion);
logger.Information(String.Format("Attempting to initiate game engine with SDL version: {0}", SDLVersion.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)) { 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.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) { if (SDLVersion.major < 2) {
logger.Error("This engine was designed to work with SDL2. The version you're currently running is severely outdated."); 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."); throw new FrameworkUsageException("Outdated SDL binaries.");
} }
} }
lock (ignitionLock) { lock (ignitionLock) {
if (!loopCompleted) return false; if (!stopped) {
loopCompleted = false; logger.Warning("Engine is already running.");
return 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();
exit = false; exit = false;
if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO) != 0) { if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO) != 0) {
throw new FrameworkSDLException(); throw new FrameworkSDLException();
@ -125,6 +146,11 @@ namespace SlatedGameToolkit.Framework {
if (SDL.SDL_Init(SDL.SDL_INIT_AUDIO) != 0) { if (SDL.SDL_Init(SDL.SDL_INIT_AUDIO) != 0) {
throw new FrameworkSDLException(); 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 = new Thread(Loop);
thread.Start(manager); thread.Start(manager);
return true; return true;

View File

@ -207,9 +207,13 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
/// Makes this window the window that is currently being drawn to. /// 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 /// More specifically, it sets the OpenGL Context associated with this window to be the one
/// that is actively receiving all OpenGL calls. /// that is actively receiving all OpenGL calls.
///
/// If the current context is already of this window, nothing happens.
/// </summary> /// </summary>
public void DrawToWindow() { public void DrawToWindow() {
SDL.SDL_GL_MakeCurrent(window, glContext); if (SDL.SDL_GL_GetCurrentContext() != glContext) {
SDL.SDL_GL_MakeCurrent(window, glContext);
}
} }
/// <summary> /// <summary>

View File

@ -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) {
}
}
}
}

View File

@ -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."); 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) { internal void update(double delta) {
if (nextState != null) { if (nextState != null) {
if (currentState.Deactivate() & nextState.Activate()) { if (currentState.Deactivate() & nextState.Activate()) {

View File

@ -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();
}
}
}

View File

@ -52,5 +52,10 @@ namespace SlatedGameToolkit.Tools.Commands
{ {
return invokers; return invokers;
} }
public void Dispose()
{
}
} }
} }

View File

@ -27,5 +27,9 @@ namespace SlatedGameToolkit.Tools.Commands
{ {
return invokers; return invokers;
} }
public void Dispose()
{
}
} }
} }

View File

@ -11,8 +11,10 @@ namespace SlatedGameToolkit.Tools
static private bool running; static private bool running;
static void Main(string[] args) 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 HelpCommand(commands));
commands.Add(new GraphicalPlaygroundCommand());
CommandProcessor processor = new CommandProcessor("The command \"{input}\" was not understood. Please type \"help\" for more information.", commands); CommandProcessor processor = new CommandProcessor("The command \"{input}\" was not understood. Please type \"help\" for more information.", commands);
AssemblyName name = Assembly.GetExecutingAssembly().GetName(); AssemblyName name = Assembly.GetExecutingAssembly().GetName();
ConsoleInteraction consoleInteracter = new ConsoleInteraction("Tools"); ConsoleInteraction consoleInteracter = new ConsoleInteraction("Tools");
@ -24,6 +26,7 @@ namespace SlatedGameToolkit.Tools
processor.Process(consoleInteracter); processor.Process(consoleInteracter);
} }
consoleInteracter.Tell("Exiting tool."); consoleInteracter.Tell("Exiting tool.");
commands.Dispose();
} }
public static void Stop() { public static void Stop() {

View File

@ -5,7 +5,7 @@ using SlatedGameToolkit.Tools.System.Interaction;
namespace SlatedGameToolkit.Tools.System namespace SlatedGameToolkit.Tools.System
{ {
public class CommandMap : ICollection<IInvocable> { public class CommandMap : ICollection<IInvocable>, IDisposable {
Dictionary<string, IInvocable> invocations; Dictionary<string, IInvocable> invocations;
HashSet<IInvocable> values; HashSet<IInvocable> values;
@ -79,5 +79,17 @@ namespace SlatedGameToolkit.Tools.System
values.Remove(item); values.Remove(item);
return true; return true;
} }
public void Dispose()
{
foreach (IInvocable invocable in this)
{
invocable.Dispose();
}
}
~CommandMap() {
Dispose();
}
} }
} }

View File

@ -1,8 +1,9 @@
using System;
using SlatedGameToolkit.Tools.System.Interaction; using SlatedGameToolkit.Tools.System.Interaction;
namespace SlatedGameToolkit.Tools.System namespace SlatedGameToolkit.Tools.System
{ {
public interface IInvocable public interface IInvocable : IDisposable
{ {
/// <summary> /// <summary>
/// Invokers are the strings that should invoke this command. /// Invokers are the strings that should invoke this command.

View File

@ -4,7 +4,6 @@ namespace SlatedGameToolkit.Tools.System.Interaction
{ {
void Tell(string message); void Tell(string message);
void Separate(); void Separate();
string Listen(); string Listen();
} }
} }

View File

@ -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)
{
}
}
}