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.
/// </summary>
/// <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) {
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();

View File

@ -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;
/// <summary>
/// The amount of updates per second.
/// Is floored to milleseconds.
/// Maximum updates per second is 200.
/// </summary>
/// <value>The updates per second.</value>
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;
}
}
/// <summary>
/// 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>
/// <value>The target frames per second.</value>
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.");
}
/// <summary>
@ -96,28 +120,25 @@ namespace SlatedGameToolkit.Framework {
/// Requests to start the game engine.
/// </summary>
/// <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");
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;

View File

@ -207,10 +207,14 @@ 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.
/// </summary>
public void DrawToWindow() {
if (SDL.SDL_GL_GetCurrentContext() != glContext) {
SDL.SDL_GL_MakeCurrent(window, glContext);
}
}
/// <summary>
/// Raises this window to be above other windows.

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.");
}
internal void initialize() {
thread = Thread.CurrentThread;
}
internal void update(double delta) {
if (nextState != null) {
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;
}
public void Dispose()
{
}
}
}

View File

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

View File

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

View File

@ -5,7 +5,7 @@ using SlatedGameToolkit.Tools.System.Interaction;
namespace SlatedGameToolkit.Tools.System
{
public class CommandMap : ICollection<IInvocable> {
public class CommandMap : ICollection<IInvocable>, IDisposable {
Dictionary<string, IInvocable> invocations;
HashSet<IInvocable> 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();
}
}
}

View File

@ -1,8 +1,9 @@
using System;
using SlatedGameToolkit.Tools.System.Interaction;
namespace SlatedGameToolkit.Tools.System
{
public interface IInvocable
public interface IInvocable : IDisposable
{
/// <summary>
/// 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 Separate();
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)
{
}
}
}