diff --git a/MonoGame.Framework/ConcreteGraphicsDeviceManager.Blazor.cs b/MonoGame.Framework/ConcreteGraphicsDeviceManager.Blazor.cs
index ee1145f168c..cb5adab290c 100644
--- a/MonoGame.Framework/ConcreteGraphicsDeviceManager.Blazor.cs
+++ b/MonoGame.Framework/ConcreteGraphicsDeviceManager.Blazor.cs
@@ -168,7 +168,7 @@ private void GraphicsDevice_DeviceReset_UpdateTouchPanel(object sender, EventArg
private void GraphicsDevice_PresentationChanged_UpdateGamePlatform(object sender, PresentationEventArgs args)
{
- base.Game.Platform.OnPresentationChanged(args.PresentationParameters);
+ base.Game.Strategy.OnPresentationChanged(args.PresentationParameters);
}
}
diff --git a/MonoGame.Framework/ConcreteGraphicsDeviceManager.SDL.cs b/MonoGame.Framework/ConcreteGraphicsDeviceManager.SDL.cs
index d9485e5f47e..ee02a9e24e8 100644
--- a/MonoGame.Framework/ConcreteGraphicsDeviceManager.SDL.cs
+++ b/MonoGame.Framework/ConcreteGraphicsDeviceManager.SDL.cs
@@ -254,7 +254,7 @@ private void GraphicsDevice_DeviceReset_UpdateTouchPanel(object sender, EventArg
private void GraphicsDevice_PresentationChanged_UpdateGamePlatform(object sender, PresentationEventArgs args)
{
- base.Game.Platform.OnPresentationChanged(args.PresentationParameters);
+ base.Game.Strategy.OnPresentationChanged(args.PresentationParameters);
}
}
diff --git a/MonoGame.Framework/ConcreteGraphicsDeviceManager.UWP.cs b/MonoGame.Framework/ConcreteGraphicsDeviceManager.UWP.cs
index 39333925267..28bc31d63c0 100644
--- a/MonoGame.Framework/ConcreteGraphicsDeviceManager.UWP.cs
+++ b/MonoGame.Framework/ConcreteGraphicsDeviceManager.UWP.cs
@@ -193,7 +193,7 @@ private void GraphicsDevice_DeviceReset_UpdateTouchPanel(object sender, EventArg
private void GraphicsDevice_PresentationChanged_UpdateGamePlatform(object sender, PresentationEventArgs args)
{
- base.Game.Platform.OnPresentationChanged(args.PresentationParameters);
+ base.Game.Strategy.OnPresentationChanged(args.PresentationParameters);
}
}
diff --git a/MonoGame.Framework/ConcreteGraphicsDeviceManager.WindowsDX11.cs b/MonoGame.Framework/ConcreteGraphicsDeviceManager.WindowsDX11.cs
index ee1145f168c..cb5adab290c 100644
--- a/MonoGame.Framework/ConcreteGraphicsDeviceManager.WindowsDX11.cs
+++ b/MonoGame.Framework/ConcreteGraphicsDeviceManager.WindowsDX11.cs
@@ -168,7 +168,7 @@ private void GraphicsDevice_DeviceReset_UpdateTouchPanel(object sender, EventArg
private void GraphicsDevice_PresentationChanged_UpdateGamePlatform(object sender, PresentationEventArgs args)
{
- base.Game.Platform.OnPresentationChanged(args.PresentationParameters);
+ base.Game.Strategy.OnPresentationChanged(args.PresentationParameters);
}
}
diff --git a/MonoGame.Framework/Game.cs b/MonoGame.Framework/Game.cs
index 24fbe2adc91..5b24e004516 100644
--- a/MonoGame.Framework/Game.cs
+++ b/MonoGame.Framework/Game.cs
@@ -1,16 +1,13 @@
// MonoGame - Copyright (C) The MonoGame Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
+
+// Copyright (C)2023 Nick Kastellanos
using System;
using System.Collections.Generic;
using System.Diagnostics;
-#if WINDOWS_UAP
-using System.Threading.Tasks;
-using Windows.ApplicationModel.Activation;
-#endif
-
using Microsoft.Xna.Platform;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
@@ -24,12 +21,9 @@ namespace Microsoft.Xna.Framework
/// This class is the entry point for most games. Handles setting up
/// a window and graphics and runs a game loop that calls and .
///
- public partial class Game : IDisposable
+ public class Game : IDisposable
{
- private GameComponentCollection _components;
- private GameServiceContainer _services;
- private ContentManager _content;
- internal GamePlatform Platform;
+ internal GameStrategy Strategy { get; private set; }
private DrawableComponents _drawableComponents = new DrawableComponents();
private UpdateableComponents _updateableComponents = new UpdateableComponents();
@@ -38,17 +32,6 @@ public partial class Game : IDisposable
private IGraphicsDeviceService _graphicsDeviceService;
private bool _initialized = false;
- private bool _isFixedTimeStep = true;
-
- private TimeSpan _targetElapsedTime = TimeSpan.FromTicks(166666); // 60fps
- private TimeSpan _inactiveSleepTime = TimeSpan.FromSeconds(0.02);
-
- private TimeSpan _maxElapsedTime = TimeSpan.FromMilliseconds(500);
-
- private bool _shouldExit;
- private bool _suppressDraw;
-
- partial void PlatformConstruct();
///
/// Create a .
@@ -58,17 +41,12 @@ public Game()
_instance = this;
LaunchParameters = new LaunchParameters();
- _services = new GameServiceContainer();
- _components = new GameComponentCollection();
- _content = new ContentManager(_services);
- Platform = GamePlatform.PlatformCreate(this);
- Platform.Activated += Platform_Activated;
- Platform.Deactivated += Platform_Deactivated;
- _services.AddService(typeof(GamePlatform), Platform);
+ Strategy = new ConcreteGame(this);
- // Allow some optional per-platform construction to occur too.
- PlatformConstruct();
+ Strategy.Activated += Platform_Activated;
+ Strategy.Deactivated += Platform_Deactivated;
+ Services.AddService(typeof(GameStrategy), Strategy);
}
@@ -80,7 +58,7 @@ public Game()
[System.Diagnostics.Conditional("DEBUG")]
internal void Log(string Message)
{
- if (Platform != null) Platform.Log(Message);
+ if (Strategy != null) Strategy.Log(Message);
}
void Platform_Activated(object sender, EventArgs e) { OnActivated(e); }
@@ -106,18 +84,18 @@ protected virtual void Dispose(bool disposing)
if (disposing)
{
// Dispose loaded game components
- for (int i = 0; i < _components.Count; i++)
+ for (int i = 0; i < Strategy._components.Count; i++)
{
- var disposable = _components[i] as IDisposable;
+ var disposable = Strategy._components[i] as IDisposable;
if (disposable != null)
disposable.Dispose();
}
- _components = null;
+ Strategy._components = null;
- if (_content != null)
+ if (Strategy._content != null)
{
- _content.Dispose();
- _content = null;
+ Strategy._content.Dispose();
+ Strategy._content = null;
}
if (_graphicsDeviceManager != null)
@@ -126,14 +104,14 @@ protected virtual void Dispose(bool disposing)
_graphicsDeviceManager = null;
}
- if (Platform != null)
+ if (Strategy != null)
{
- Platform.Activated -= Platform_Activated;
- Platform.Deactivated -= Platform_Deactivated;
- _services.RemoveService(typeof(GamePlatform));
+ Strategy.Activated -= Platform_Activated;
+ Strategy.Deactivated -= Platform_Deactivated;
+ Services.RemoveService(typeof(GameStrategy));
- Platform.Dispose();
- Platform = null;
+ Strategy.Dispose();
+ Strategy = null;
}
AudioService.Shutdown();
@@ -176,21 +154,12 @@ private void AssertNotDisposed()
///
/// A collection of game components attached to this .
///
- public GameComponentCollection Components
- {
- get { return _components; }
- }
+ public GameComponentCollection Components { get { return Strategy.Components; } }
public TimeSpan InactiveSleepTime
{
- get { return _inactiveSleepTime; }
- set
- {
- if (value < TimeSpan.Zero)
- throw new ArgumentOutOfRangeException("InactiveSleepTime must be positive.");
-
- _inactiveSleepTime = value;
- }
+ get { return Strategy.InactiveSleepTime; }
+ set { Strategy.InactiveSleepTime = value; }
}
///
@@ -199,14 +168,8 @@ public TimeSpan InactiveSleepTime
///
public TimeSpan MaxElapsedTime
{
- get { return _maxElapsedTime; }
- set
- {
- if (value < TimeSpan.FromMilliseconds(500))
- throw new ArgumentOutOfRangeException("MaxElapsedTime must be at least 0.5s");
-
- _maxElapsedTime = value;
- }
+ get { return Strategy.MaxElapsedTime; }
+ set { Strategy.MaxElapsedTime = value; }
}
///
@@ -214,13 +177,13 @@ public TimeSpan MaxElapsedTime
///
public bool IsActive
{
- get { return Platform.IsActive; }
+ get { return Strategy.IsActive; }
}
public bool IsVisible
{
- get { return Platform.IsVisible; }
+ get { return Strategy.IsVisible; }
}
///
@@ -228,8 +191,8 @@ public bool IsVisible
///
public bool IsMouseVisible
{
- get { return Platform.IsMouseVisible; }
- set { Platform.IsMouseVisible = value; }
+ get { return Strategy.IsMouseVisible; }
+ set { Strategy.IsMouseVisible = value; }
}
///
@@ -238,23 +201,8 @@ public bool IsMouseVisible
/// Target elapsed time must be strictly larger than zero.
public TimeSpan TargetElapsedTime
{
- get { return _targetElapsedTime; }
- set
- {
- // Give GamePlatform implementations an opportunity to override
- // the new value.
- value = Platform.TargetElapsedTimeChanging(value);
-
- if (value <= TimeSpan.Zero)
- throw new ArgumentOutOfRangeException(
- "TargetElapsedTime must be positive and non-zero.");
-
- if (value != _targetElapsedTime)
- {
- _targetElapsedTime = value;
- Platform.TargetElapsedTimeChanged();
- }
- }
+ get { return Strategy.TargetElapsedTime; }
+ set { Strategy.TargetElapsedTime = value; }
}
@@ -266,16 +214,14 @@ public TimeSpan TargetElapsedTime
///
public bool IsFixedTimeStep
{
- get { return _isFixedTimeStep; }
- set { _isFixedTimeStep = value; }
+ get { return Strategy.IsFixedTimeStep; }
+ set { Strategy.IsFixedTimeStep = value; }
}
///
/// Get a container holding service providers attached to this .
///
- public GameServiceContainer Services {
- get { return _services; }
- }
+ public GameServiceContainer Services { get { return Strategy.Services; } }
///
@@ -284,14 +230,8 @@ public GameServiceContainer Services {
/// If Content is set to null
.
public ContentManager Content
{
- get { return _content; }
- set
- {
- if (value == null)
- throw new ArgumentNullException();
-
- _content = value;
- }
+ get { return Strategy.Content; }
+ set { Strategy.Content = value; }
}
///
@@ -322,7 +262,7 @@ public GraphicsDevice GraphicsDevice
[CLSCompliant(false)]
public GameWindow Window
{
- get { return Platform.Window; }
+ get { return Strategy.Window; }
}
#endregion Properties
@@ -364,7 +304,7 @@ internal bool Initialized
#if WINDOWS_UAP
[CLSCompliant(false)]
- public ApplicationExecutionState PreviousExecutionState { get; internal set; }
+ public Windows.ApplicationModel.Activation.ApplicationExecutionState PreviousExecutionState { get; internal set; }
#endif
#endregion
@@ -376,11 +316,7 @@ internal bool Initialized
///
public void Exit()
{
-#if ANDROID || IOS || TVOS
- throw new InvalidOperationException("This platform's policy does not allow programmatically closing.");
-#endif
- _shouldExit = true;
- _suppressDraw = true;
+ Strategy.Exit();
}
///
@@ -388,10 +324,7 @@ public void Exit()
///
public void ResetElapsedTime()
{
- Platform.ResetElapsedTime();
-
- _accumulatedElapsedTime = TimeSpan.Zero;
- _previousElapsedTime = TimeSpan.Zero;
+ Strategy.ResetElapsedTime();
}
///
@@ -399,7 +332,7 @@ public void ResetElapsedTime()
///
public void SuppressDraw()
{
- _suppressDraw = true;
+ Strategy.SuppressDraw();
}
///
@@ -407,16 +340,16 @@ public void SuppressDraw()
///
public void RunOneFrame()
{
- if (Platform == null)
+ if (Strategy == null)
return;
- if (!Platform.ANDROID_BeforeRun())
+ if (!Strategy.ANDROID_BeforeRun())
return;
if (!Initialized)
{
DoInitialize();
- Platform.Timer = Stopwatch.StartNew();
+ Strategy.Timer = Stopwatch.StartNew();
}
BeginRun();
@@ -435,7 +368,7 @@ public void Run()
{
AssertNotDisposed();
- Platform.Run();
+ Strategy.Run();
}
///
@@ -445,27 +378,20 @@ internal void Run_UAP_XAML()
{
AssertNotDisposed();
- Platform.Run_UAP_XAML();
+ Strategy.Run_UAP_XAML();
}
internal void DoBeginRun()
{
BeginRun();
- Platform.Timer = Stopwatch.StartNew();
+ Strategy.Timer = Stopwatch.StartNew();
}
internal void DoEndRun()
{
EndRun();
}
-
- private TimeSpan _accumulatedElapsedTime;
- private TimeSpan _previousElapsedTime;
- private int _updateFrameLag;
-#if WINDOWS_UAP
- private readonly object _locker = new object();
-#endif
-
+
///
/// Run one iteration of the game loop.
///
@@ -475,116 +401,11 @@ internal void DoEndRun()
/// make exactly one call to .
///
public void Tick()
- {
- // NOTE: This code is very sensitive and can break very badly
- // with even what looks like a safe change. Be sure to test
- // any change fully in both the fixed and variable timestep
- // modes across multiple devices and platforms.
-
- // implement InactiveSleepTime to save battery life
- // and/or release CPU time to other threads and processes.
- if (!IsActive)
- {
-#if WINDOWS_UAP
- lock (_locker)
- System.Threading.Monitor.Wait(_locker, (int)InactiveSleepTime.TotalMilliseconds);
-#else
- System.Threading.Thread.Sleep((int)InactiveSleepTime.TotalMilliseconds);
-#endif
- }
-
- RetryTick:
-
- // Advance the accumulated elapsed time.
- TimeSpan elapsedTime = Platform.Timer.Elapsed;
- TimeSpan elapsedTimeDiff = TimeSpan.FromTicks(elapsedTime.Ticks - _previousElapsedTime.Ticks);
- _previousElapsedTime = elapsedTime;
-
- _accumulatedElapsedTime += elapsedTimeDiff;
-
- if (IsFixedTimeStep && _accumulatedElapsedTime < TargetElapsedTime)
- {
- // When game IsActive use CPU Spin.
- /*
- if ((TargetElapsedTime - _accumulatedElapsedTime).TotalMilliseconds >= 2.0)
- {
-#if WINDOWS || DESKTOPGL || ANDROID || IOS || TVOS
- System.Threading.Thread.Sleep(0);
-#elif WINDOWS_UAP
- lock (_locker)
- System.Threading.Monitor.Wait(_locker, 0);
-#endif
- }
- */
-
- // Keep looping until it's time to perform the next update
- goto RetryTick;
- }
+ {
+ Strategy.Tick();
- // Do not allow any update to take longer than our maximum.
- var maxElapsedTime = TimeSpan.FromTicks(Math.Max(_maxElapsedTime.Ticks, _targetElapsedTime.Ticks));
- if (_accumulatedElapsedTime > maxElapsedTime)
- _accumulatedElapsedTime = maxElapsedTime;
-
- if (IsFixedTimeStep)
- {
- Platform.Time.ElapsedGameTime = TargetElapsedTime;
- int stepCount = 0;
-
- // Perform as many full fixed length time steps as we can.
- while (_accumulatedElapsedTime >= TargetElapsedTime && !_shouldExit)
- {
- Platform.Time.TotalGameTime += TargetElapsedTime;
- _accumulatedElapsedTime -= TargetElapsedTime;
- stepCount++;
-
- DoUpdate(Platform.Time);
- }
-
- //Every update after the first accumulates lag
- _updateFrameLag += Math.Max(0, stepCount - 1);
- _updateFrameLag = Math.Min(_updateFrameLag, 5);
-
- //If we think we are running slowly, wait until the lag clears before resetting it
- if (Platform.Time.IsRunningSlowly)
- {
- if (_updateFrameLag == 0)
- Platform.Time.IsRunningSlowly = false;
- }
- else if (_updateFrameLag >= 5)
- {
- //If we lag more than 5 frames, start thinking we are running slowly
- Platform.Time.IsRunningSlowly = true;
- }
-
- //Every time we just do one update and one draw, then we are not running slowly, so decrease the lag
- if (stepCount == 1 && _updateFrameLag > 0)
- _updateFrameLag--;
-
- // Draw needs to know the total elapsed time
- // that occured for the fixed length updates.
- Platform.Time.ElapsedGameTime = TimeSpan.FromTicks(TargetElapsedTime.Ticks * stepCount);
- }
- else
- {
- // Perform a single variable length update.
- Platform.Time.ElapsedGameTime = _accumulatedElapsedTime;
- Platform.Time.TotalGameTime += _accumulatedElapsedTime;
- _accumulatedElapsedTime = TimeSpan.Zero;
-
- DoUpdate(Platform.Time);
- }
-
- // Draw unless the update suppressed it.
- if (_suppressDraw)
- _suppressDraw = false;
- else
- {
- DoDraw(Platform.Time);
- }
-
- if (_shouldExit)
- Platform.Exit();
+ if (Strategy.ShouldExit)
+ Strategy.TickExiting();
}
#endregion
@@ -606,7 +427,7 @@ public void Tick()
///
protected virtual void EndDraw()
{
- Platform.Present();
+ Strategy.EndDraw();
}
///
@@ -640,18 +461,18 @@ protected virtual void Initialize()
#if ANDROID || IOS || TVOS
// applyChanges
{
- Platform.BeginScreenDeviceChange(GraphicsDevice.PresentationParameters.IsFullScreen);
+ Strategy.BeginScreenDeviceChange(GraphicsDevice.PresentationParameters.IsFullScreen);
if (GraphicsDevice.PresentationParameters.IsFullScreen)
- Platform.EnterFullScreen();
+ Strategy.EnterFullScreen();
else
- Platform.ExitFullScreen();
+ Strategy.ExitFullScreen();
var viewport = new Viewport(0, 0,
GraphicsDevice.PresentationParameters.BackBufferWidth,
GraphicsDevice.PresentationParameters.BackBufferHeight);
GraphicsDevice.Viewport = viewport;
- Platform.EndScreenDeviceChange(string.Empty, viewport.Width, viewport.Height);
+ Strategy.EndScreenDeviceChange(string.Empty, viewport.Width, viewport.Height);
}
#endif
@@ -738,8 +559,7 @@ protected virtual void OnDeactivated(EventArgs args)
#region Event Handlers
- private void Components_ComponentAdded(
- object sender, GameComponentCollectionEventArgs e)
+ private void Components_ComponentAdded(object sender, GameComponentCollectionEventArgs e)
{
// Since we only subscribe to ComponentAdded after the graphics
// devices are set up, it is safe to just blindly call Initialize.
@@ -751,8 +571,7 @@ private void Components_ComponentAdded(
_drawableComponents.AddDrawable((IDrawable)e.GameComponent);
}
- private void Components_ComponentRemoved(
- object sender, GameComponentCollectionEventArgs e)
+ private void Components_ComponentRemoved(object sender, GameComponentCollectionEventArgs e)
{
if (e.GameComponent is IUpdateable)
_updateableComponents.RemoveUpdatable((IUpdateable)e.GameComponent);
@@ -764,15 +583,11 @@ private void Components_ComponentRemoved(
#region Internal Methods
- // FIXME: We should work toward eliminating internal methods. They
- // break entirely the possibility that additional platforms could
- // be added by third parties without changing MonoGame itself.
-
internal void DoUpdate(GameTime gameTime)
{
AssertNotDisposed();
- if (Platform.BeforeUpdate(gameTime))
+ if (Strategy.BeforeUpdate(gameTime))
{
((IFrameworkDispatcher)FrameworkDispatcher.Current).Update();
@@ -790,7 +605,7 @@ internal void DoDraw(GameTime gameTime)
// Draw and EndDraw should not be called if BeginDraw returns false.
// http://stackoverflow.com/questions/4054936/manual-control-over-when-to-redraw-the-screen/4057180#4057180
// http://stackoverflow.com/questions/4235439/xna-3-1-to-4-0-requires-constant-redraw-or-will-display-a-purple-screen
- if (Platform.BeforeDraw(gameTime) && BeginDraw())
+ if (Strategy.BeforeDraw(gameTime) && BeginDraw())
{
Draw(gameTime);
EndDraw();
@@ -804,7 +619,7 @@ internal void DoInitialize()
if (GraphicsDevice == null && graphicsDeviceManager != null)
((IGraphicsDeviceManager)graphicsDeviceManager).CreateDevice();
- Platform.BeforeInitialize();
+ Strategy.BeforeInitialize();
Initialize();
// We need to do this after virtual Initialize(...) is called.
@@ -823,8 +638,8 @@ internal void DoInitialize()
_drawableComponents.AddDrawable((IDrawable)Components[i]);
}
- _components.ComponentAdded += Components_ComponentAdded;
- _components.ComponentRemoved += Components_ComponentRemoved;
+ Components.ComponentAdded += Components_ComponentAdded;
+ Components.ComponentRemoved += Components_ComponentRemoved;
_initialized = true;
}
diff --git a/MonoGame.Framework/GamePlatform.Blazor.cs b/MonoGame.Framework/GamePlatform.Blazor.cs
deleted file mode 100644
index 928fc301a00..00000000000
--- a/MonoGame.Framework/GamePlatform.Blazor.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (C)2022 Nick Kastellanos
-
-using System;
-
-
-namespace Microsoft.Xna.Framework
-{
- partial class GamePlatform
- {
- internal static GamePlatform PlatformCreate(Game game)
- {
- return new MonoGame.Framework.BlazorGamePlatform(game);
- }
- }
-}
diff --git a/MonoGame.Framework/GamePlatform.Desktop.cs b/MonoGame.Framework/GamePlatform.Desktop.cs
deleted file mode 100644
index 6a03d7d5421..00000000000
--- a/MonoGame.Framework/GamePlatform.Desktop.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// MonoGame - Copyright (C) The MonoGame Team
-// This file is subject to the terms and conditions defined in
-// file 'LICENSE.txt', which is part of this source code package.
-
-using System;
-
-#if WINDOWS_UAP
-using Windows.UI.ViewManagement;
-#endif
-
-namespace Microsoft.Xna.Framework
-{
- partial class GamePlatform
- {
- internal static GamePlatform PlatformCreate(Game game)
- {
-#if DESKTOPGL
- return new SdlGamePlatform(game);
-#elif WINDOWS
- return new MonoGame.Framework.WinFormsGamePlatform(game);
-#elif WINDOWS_UAP
- return new UAPGamePlatform(game);
-#endif
- }
- }
-}
diff --git a/MonoGame.Framework/GamePlatform.Mobile.cs b/MonoGame.Framework/GamePlatform.Mobile.cs
deleted file mode 100644
index 96e4ed8949a..00000000000
--- a/MonoGame.Framework/GamePlatform.Mobile.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// MonoGame - Copyright (C) The MonoGame Team
-// This file is subject to the terms and conditions defined in
-// file 'LICENSE.txt', which is part of this source code package.
-
-using System;
-
-namespace Microsoft.Xna.Framework
-{
- partial class GamePlatform
- {
- internal static GamePlatform PlatformCreate(Game game)
- {
-#if IOS || TVOS
- return new iOSGamePlatform(game);
-#elif ANDROID
- return new AndroidGamePlatform(game);
-#endif
- }
- }
-}
diff --git a/MonoGame.Framework/GamePlatform.Ref.cs b/MonoGame.Framework/GamePlatform.Ref.cs
index d51a5ed288c..fad02a95150 100644
--- a/MonoGame.Framework/GamePlatform.Ref.cs
+++ b/MonoGame.Framework/GamePlatform.Ref.cs
@@ -1,16 +1,80 @@
-// Copyright (C)2022 Nick Kastellanos
+// Copyright (C)2023 Nick Kastellanos
using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
-namespace Microsoft.Xna.Framework
+namespace Microsoft.Xna.Platform
{
- partial class GamePlatform
+ sealed class ConcreteGame : GameStrategy
{
+ public ConcreteGame(Game game) : base(game)
+ {
+ }
- internal static GamePlatform PlatformCreate(Game game)
+ internal override void Run()
{
throw new PlatformNotSupportedException();
}
+ public override void Tick()
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public override void BeforeInitialize()
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public override void TickExiting()
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public override bool BeforeUpdate(GameTime gameTime)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public override bool BeforeDraw(GameTime gameTime)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public override void EnterFullScreen()
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public override void ExitFullScreen()
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ internal override void OnPresentationChanged(PresentationParameters pp)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public override void EndScreenDeviceChange(string screenDeviceName, int clientWidth, int clientHeight)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public override void BeginScreenDeviceChange(bool willBeFullScreen)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public override void Log(string message)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public override void EndDraw()
+ {
+ throw new PlatformNotSupportedException();
+ }
}
}
diff --git a/MonoGame.Framework/GamePlatform.cs b/MonoGame.Framework/GamePlatform.cs
index 50773280a2e..a9fc2335262 100644
--- a/MonoGame.Framework/GamePlatform.cs
+++ b/MonoGame.Framework/GamePlatform.cs
@@ -2,38 +2,59 @@
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
+// Copyright (C)2022 Nick Kastellanos
+
using System;
using System.Diagnostics;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
-namespace Microsoft.Xna.Framework
+namespace Microsoft.Xna.Platform
{
- abstract partial class GamePlatform : IDisposable
+ abstract class GameStrategy : IDisposable
{
#region Fields
- protected TimeSpan _inactiveSleepTime = TimeSpan.FromMilliseconds(20.0);
- protected bool _needsToResetElapsedTime = false;
- bool disposed;
+ private GameServiceContainer _services;
+ internal GameComponentCollection _components;
+ internal ContentManager _content;
+
+ private TimeSpan _targetElapsedTime = TimeSpan.FromTicks(166666); // 60fps
+ private TimeSpan _inactiveSleepTime = TimeSpan.FromMilliseconds(20.0);
+
+ private TimeSpan _maxElapsedTime = TimeSpan.FromMilliseconds(500);
+
+ private bool _isFixedTimeStep = true;
+
+ private bool _shouldExit;
+ private bool _suppressDraw;
+
+ bool _isDisposed;
+
protected bool InFullScreenMode = false;
- protected bool IsDisposed { get { return disposed; } }
+ protected bool IsDisposed { get { return _isDisposed; } }
#endregion
#region Construction/Destruction
- protected GamePlatform(Game game)
+ protected GameStrategy(Game game)
{
if (game == null)
throw new ArgumentNullException("game");
Game = game;
+
+ _services = new GameServiceContainer();
+ _components = new GameComponentCollection();
+ _content = new ContentManager(_services);
}
- ~GamePlatform()
+ ~GameStrategy()
{
Dispose(false);
}
@@ -42,14 +63,39 @@ protected GamePlatform(Game game)
#region Public Properties
+ ///
+ /// Get a container holding service providers attached to this .
+ ///
+ public GameServiceContainer Services { get { return _services; } }
+
+ ///
+ /// A collection of game components attached to this .
+ ///
+ public GameComponentCollection Components { get { return _components; } }
+
+ ///
+ /// The of this .
+ ///
+ /// If Content is set to null
.
+ public ContentManager Content
+ {
+ get { return _content; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException();
+
+ _content = value;
+ }
+ }
///
/// Gets the Game instance that owns this GamePlatform instance.
///
public readonly Game Game;
- public readonly GameTime Time = new GameTime();
- public Stopwatch Timer;
+ private readonly GameTime Time = new GameTime();
+ internal Stopwatch Timer;
private bool _isActive;
public bool IsActive
@@ -76,17 +122,10 @@ public bool IsVisible
}
private bool _isMouseVisible;
- public bool IsMouseVisible
+ public virtual bool IsMouseVisible
{
get { return _isMouseVisible; }
- set
- {
- if (_isMouseVisible != value)
- {
- _isMouseVisible = value;
- OnIsMouseVisibleChanged();
- }
- }
+ set { _isMouseVisible = value; }
}
private GameWindow _window;
@@ -105,6 +144,64 @@ protected set
}
}
+ public TimeSpan InactiveSleepTime
+ {
+ get { return _inactiveSleepTime; }
+ set
+ {
+ if (value < TimeSpan.Zero)
+ throw new ArgumentOutOfRangeException("InactiveSleepTime must be positive.");
+
+ _inactiveSleepTime = value;
+ }
+ }
+
+ ///
+ /// The maximum amount of time we will frameskip over and only perform Update calls with no Draw calls.
+ /// MonoGame extension.
+ ///
+ public TimeSpan MaxElapsedTime
+ {
+ get { return _maxElapsedTime; }
+ set
+ {
+ if (value < TimeSpan.FromMilliseconds(500))
+ throw new ArgumentOutOfRangeException("MaxElapsedTime must be at least 0.5s");
+
+ _maxElapsedTime = value;
+ }
+ }
+
+ ///
+ /// The time between frames when running with a fixed time step.
+ ///
+ /// Target elapsed time must be strictly larger than zero.
+ public virtual TimeSpan TargetElapsedTime
+ {
+ get { return _targetElapsedTime; }
+ set
+ {
+ if (value <= TimeSpan.Zero)
+ throw new ArgumentOutOfRangeException("TargetElapsedTime must be positive and non-zero.");
+
+ _targetElapsedTime = value;
+ }
+ }
+
+ ///
+ /// Indicates if this game is running with a fixed time between frames.
+ ///
+ /// When set to true
the target time between frames is
+ /// given by .
+ ///
+ public bool IsFixedTimeStep
+ {
+ get { return _isFixedTimeStep; }
+ set { _isFixedTimeStep = value; }
+ }
+
+ public bool ShouldExit { get { return _shouldExit; } }
+
#endregion
#region Events
@@ -148,7 +245,13 @@ public virtual bool ANDROID_BeforeRun()
///
/// When implemented in a derived, ends the active run loop.
///
- public abstract void Exit();
+ public virtual void Exit()
+ {
+ _shouldExit = true;
+ _suppressDraw = true;
+ }
+
+ public abstract void TickExiting();
///
/// Gives derived classes an opportunity to do work just before Update
@@ -180,25 +283,13 @@ public virtual bool ANDROID_BeforeRun()
///
public abstract void ExitFullScreen();
- ///
- /// Gives derived classes an opportunity to modify
- /// Game.TargetElapsedTime before it is set.
- ///
- /// The proposed new value of TargetElapsedTime.
- /// The new value of TargetElapsedTime that will be set.
- public virtual TimeSpan TargetElapsedTimeChanging(TimeSpan value)
- {
- return value;
- }
///
/// Starts a device transition (windowed to full screen or vice versa).
///
///
/// Specifies whether the device will be in full-screen mode upon completion of the change.
///
- public abstract void BeginScreenDeviceChange (
- bool willBeFullScreen
- );
+ public abstract void BeginScreenDeviceChange(bool willBeFullScreen);
///
/// Completes a device transition.
@@ -212,18 +303,12 @@ bool willBeFullScreen
///
/// The new height of the game's client window.
///
- public abstract void EndScreenDeviceChange (
+ public abstract void EndScreenDeviceChange(
string screenDeviceName,
int clientWidth,
int clientHeight
);
- ///
- /// Gives derived classes an opportunity to take action after
- /// Game.TargetElapsedTime has been set.
- ///
- public virtual void TargetElapsedTimeChanged() {}
-
///
/// MSDN: Use this method if your game is recovering from a slow-running state, and ElapsedGameTime is too large to be useful.
/// Frame timing is generally handled by the Game class, but some platforms still handle it elsewhere. Once all platforms
@@ -238,11 +323,20 @@ public virtual void ResetElapsedTime()
}
Time.ElapsedGameTime = TimeSpan.Zero;
- }
- public virtual void Present() { }
+ _currElapsedTime = TimeSpan.Zero;
+ _prevElapsedTime = TimeSpan.Zero;
+ }
+
+ ///
+ /// Supress calling in the game loop.
+ ///
+ public void SuppressDraw()
+ {
+ _suppressDraw = true;
+ }
- protected virtual void OnIsMouseVisibleChanged() {}
+ public virtual void EndDraw() { }
///
/// Called by the GraphicsDeviceManager to notify the platform
@@ -253,6 +347,123 @@ internal virtual void OnPresentationChanged(PresentationParameters pp)
{
}
+#if WINDOWS_UAP
+ private readonly object _locker = new object();
+#endif
+
+ private TimeSpan _currElapsedTime;
+ private TimeSpan _prevElapsedTime;
+ private int _updateFrameLag;
+
+ ///
+ /// Run one iteration of the game loop.
+ ///
+ /// Makes at least one call to
+ /// and exactly one call to if drawing is not supressed.
+ /// When is set to false
this will
+ /// make exactly one call to .
+ ///
+ public virtual void Tick()
+ {
+ // implement InactiveSleepTime to save battery life
+ // and/or release CPU time to other threads and processes.
+ if (!IsActive)
+ {
+#if WINDOWS_UAP
+ lock (_locker)
+ System.Threading.Monitor.Wait(_locker, (int)InactiveSleepTime.TotalMilliseconds);
+#else
+ System.Threading.Thread.Sleep((int)InactiveSleepTime.TotalMilliseconds);
+#endif
+ }
+
+ RetryTick:
+
+ // Advance the accumulated elapsed time.
+ TimeSpan elapsedTime = Timer.Elapsed;
+ TimeSpan dt = elapsedTime - _prevElapsedTime;
+ _currElapsedTime += dt;
+ _prevElapsedTime = elapsedTime;
+
+ if (IsFixedTimeStep && _currElapsedTime < TargetElapsedTime)
+ {
+ // When game IsActive use CPU Spin.
+ /*
+ if ((TargetElapsedTime - _accumulatedElapsedTime).TotalMilliseconds >= 2.0)
+ {
+#if WINDOWS || DESKTOPGL || ANDROID || IOS || TVOS
+ System.Threading.Thread.Sleep(0);
+#elif WINDOWS_UAP
+ lock (_locker)
+ System.Threading.Monitor.Wait(_locker, 0);
+#endif
+ }
+ */
+
+ // Keep looping until it's time to perform the next update
+ goto RetryTick;
+ }
+
+ // Do not allow any update to take longer than our maximum.
+ var maxElapsedTime = TimeSpan.FromTicks(Math.Max(MaxElapsedTime.Ticks, TargetElapsedTime.Ticks));
+ if (_currElapsedTime > maxElapsedTime)
+ _currElapsedTime = maxElapsedTime;
+
+ if (IsFixedTimeStep)
+ {
+ Time.ElapsedGameTime = TargetElapsedTime;
+ int stepCount = 0;
+
+ // Perform as many full fixed length time steps as we can.
+ while (_currElapsedTime >= TargetElapsedTime && !_shouldExit)
+ {
+ Time.TotalGameTime += TargetElapsedTime;
+ _currElapsedTime -= TargetElapsedTime;
+ stepCount++;
+
+ Game.DoUpdate(Time);
+ }
+
+ //Every update after the first accumulates lag
+ _updateFrameLag += Math.Max(0, stepCount - 1);
+ _updateFrameLag = Math.Min(_updateFrameLag, 5);
+
+ //If we think we are running slowly, wait until the lag clears before resetting it
+ if (Time.IsRunningSlowly)
+ {
+ if (_updateFrameLag == 0)
+ Time.IsRunningSlowly = false;
+ }
+ else if (_updateFrameLag >= 5)
+ {
+ //If we lag more than 5 frames, start thinking we are running slowly
+ Time.IsRunningSlowly = true;
+ }
+
+ //Every time we just do one update and one draw, then we are not running slowly, so decrease the lag
+ if (stepCount == 1 && _updateFrameLag > 0)
+ _updateFrameLag--;
+
+ // Draw needs to know the total elapsed time
+ // that occured for the fixed length updates.
+ Time.ElapsedGameTime = TimeSpan.FromTicks(TargetElapsedTime.Ticks * stepCount);
+ }
+ else
+ {
+ // Perform a single variable length update.
+ Time.ElapsedGameTime = _currElapsedTime;
+ Time.TotalGameTime += _currElapsedTime;
+ _currElapsedTime = TimeSpan.Zero;
+
+ Game.DoUpdate(Time);
+ }
+
+ // Draw unless the update suppressed it.
+ if (!_suppressDraw)
+ Game.DoDraw(Time);
+ _suppressDraw = false;
+ }
+
#endregion Methods
#region IDisposable implementation
@@ -269,12 +480,12 @@ public void Dispose()
protected virtual void Dispose(bool disposing)
{
- if (!disposed)
+ if (!_isDisposed)
{
Mouse.PrimaryWindow = null;
TouchPanel.PrimaryWindow = null;
- disposed = true;
+ _isDisposed = true;
}
}
diff --git a/MonoGame.Framework/Media/VideoPlayer.Android.cs b/MonoGame.Framework/Media/VideoPlayer.Android.cs
index 8a79e9660bb..136aad5d282 100644
--- a/MonoGame.Framework/Media/VideoPlayer.Android.cs
+++ b/MonoGame.Framework/Media/VideoPlayer.Android.cs
@@ -5,6 +5,7 @@
using System;
using Android.Widget;
using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Platform;
namespace Microsoft.Xna.Framework.Media
{
@@ -40,15 +41,15 @@ private void PlatformPlay()
{
_currentVideo.Player.SetDisplay(((AndroidGameWindow)_game.Window).GameView.Holder);
_currentVideo.Player.Start();
-
- AndroidGamePlatform.IsPlayingVdeo = true;
+
+ ConcreteGame.IsPlayingVdeo = true;
}
private void PlatformStop()
{
_currentVideo.Player.Stop();
- AndroidGamePlatform.IsPlayingVdeo = false;
+ ConcreteGame.IsPlayingVdeo = false;
_currentVideo.Player.SetDisplay(null);
}
diff --git a/MonoGame.Framework/Media/VideoPlayer.iOS.cs b/MonoGame.Framework/Media/VideoPlayer.iOS.cs
index c222095c79d..5da7ecab025 100644
--- a/MonoGame.Framework/Media/VideoPlayer.iOS.cs
+++ b/MonoGame.Framework/Media/VideoPlayer.iOS.cs
@@ -4,6 +4,7 @@
using System;
using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Platform;
using MediaPlayer;
using Foundation;
using UIKit;
@@ -13,13 +14,13 @@ namespace Microsoft.Xna.Framework.Media
public sealed partial class VideoPlayer : IDisposable
{
private Game _game;
- private iOSGamePlatform _platform;
+ private ConcreteGame _platform;
private NSObject _playbackDidFinishObserver;
private void PlatformInitialize()
{
_game = Game.Instance;
- _platform = (iOSGamePlatform)_game.Services.GetService(typeof(iOSGamePlatform));
+ _platform = (ConcreteGame)_game.Services.GetService(typeof(ConcreteGame));
if (_platform == null)
throw new InvalidOperationException("No iOSGamePlatform instance was available");
diff --git a/MonoGame.Framework/MonoGame.Framework.Android.csproj b/MonoGame.Framework/MonoGame.Framework.Android.csproj
index 79bbfa3fdb1..137fd79154e 100644
--- a/MonoGame.Framework/MonoGame.Framework.Android.csproj
+++ b/MonoGame.Framework/MonoGame.Framework.Android.csproj
@@ -51,7 +51,6 @@
-
diff --git a/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj b/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj
index 6b5adbf311f..8aeb16f4d35 100644
--- a/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj
+++ b/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj
@@ -71,7 +71,6 @@
-
diff --git a/MonoGame.Framework/MonoGame.Framework.WindowsDX.csproj b/MonoGame.Framework/MonoGame.Framework.WindowsDX.csproj
index 39da9e867fe..7ee9a5ace02 100644
--- a/MonoGame.Framework/MonoGame.Framework.WindowsDX.csproj
+++ b/MonoGame.Framework/MonoGame.Framework.WindowsDX.csproj
@@ -40,7 +40,6 @@
-
diff --git a/MonoGame.Framework/MonoGame.Framework.WindowsUniversal.csproj b/MonoGame.Framework/MonoGame.Framework.WindowsUniversal.csproj
index 38acc547808..3f46eb70bbe 100644
--- a/MonoGame.Framework/MonoGame.Framework.WindowsUniversal.csproj
+++ b/MonoGame.Framework/MonoGame.Framework.WindowsUniversal.csproj
@@ -50,7 +50,6 @@
-
diff --git a/MonoGame.Framework/MonoGame.Framework.iOS.csproj b/MonoGame.Framework/MonoGame.Framework.iOS.csproj
index 3687ff06e0f..2f63d42bd53 100644
--- a/MonoGame.Framework/MonoGame.Framework.iOS.csproj
+++ b/MonoGame.Framework/MonoGame.Framework.iOS.csproj
@@ -44,7 +44,6 @@
-
diff --git a/MonoGame.Framework/Platform/Android/AndroidGameActivity.cs b/MonoGame.Framework/Platform/Android/AndroidGameActivity.cs
index 16cd6392d91..0fd1b24f106 100644
--- a/MonoGame.Framework/Platform/Android/AndroidGameActivity.cs
+++ b/MonoGame.Framework/Platform/Android/AndroidGameActivity.cs
@@ -7,6 +7,7 @@
using Android.Content;
using Android.OS;
using Android.Views;
+using Microsoft.Xna.Platform;
namespace Microsoft.Xna.Framework
@@ -94,7 +95,7 @@ protected override void OnResume()
public override void OnWindowFocusChanged(bool hasFocus)
{
base.OnWindowFocusChanged(hasFocus);
- ((AndroidGamePlatform)Game.Platform).OnWindowFocusChanged(hasFocus);
+ ((ConcreteGame)Game.Strategy).OnWindowFocusChanged(hasFocus);
}
protected override void OnDestroy()
diff --git a/MonoGame.Framework/Platform/Android/AndroidGamePlatform.cs b/MonoGame.Framework/Platform/Android/AndroidGamePlatform.cs
index 91dc1fbc1b2..a1db078d152 100644
--- a/MonoGame.Framework/Platform/Android/AndroidGamePlatform.cs
+++ b/MonoGame.Framework/Platform/Android/AndroidGamePlatform.cs
@@ -2,17 +2,20 @@
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
+// Copyright (C)2023 Nick Kastellanos
+
using System;
using Android.Views;
+using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
-namespace Microsoft.Xna.Framework
+namespace Microsoft.Xna.Platform
{
- class AndroidGamePlatform : GamePlatform
+ sealed class ConcreteGame : GameStrategy
{
- public AndroidGamePlatform(Game game)
+ public ConcreteGame(Game game)
: base(game)
{
System.Diagnostics.Debug.Assert(Game.Activity != null, "Must set Game.Activity before creating the Game instance");
@@ -41,6 +44,11 @@ protected override void Dispose(bool disposing)
private AndroidGameWindow _gameWindow;
public override void Exit()
+ {
+ throw new InvalidOperationException("This platform's policy does not allow programmatically closing.");
+ }
+
+ public override void TickExiting()
{
// Do Nothing: Android games do not "exit" or shut down.
throw new NotImplementedException();
@@ -170,6 +178,11 @@ internal override void Run()
//Game.DoExiting();
}
+ public override void Tick()
+ {
+ base.Tick();
+ }
+
public override void Log(string Message)
{
#if LOGGING
@@ -177,15 +190,13 @@ public override void Log(string Message)
#endif
}
- public override void Present()
+ public override void EndDraw()
{
try
{
var device = Game.GraphicsDevice;
if (device != null)
- {
device.Present();
- }
_gameWindow.GameView.SwapBuffers();
}
diff --git a/MonoGame.Framework/Platform/Android/AndroidGameWindow.cs b/MonoGame.Framework/Platform/Android/AndroidGameWindow.cs
index 4343655c651..0231c3238a7 100644
--- a/MonoGame.Framework/Platform/Android/AndroidGameWindow.cs
+++ b/MonoGame.Framework/Platform/Android/AndroidGameWindow.cs
@@ -8,7 +8,9 @@
using Android.OS;
using Android.Views;
using Microsoft.Xna.Framework.Input.Touch;
-using MonoGame.OpenGL;
+using Microsoft.Xna.Platform;
+using MonoGame.OpenGL;
+
namespace Microsoft.Xna.Framework
{
@@ -76,7 +78,7 @@ private void OnTick(object sender, EventArgs args)
if (_game != null)
{
- if (!GameView.IsResuming && ((AndroidGamePlatform)_game.Platform).IsActivityActive && !ScreenReceiver.ScreenLocked) //Only call draw if an update has occured
+ if (!GameView.IsResuming && ((ConcreteGame)_game.Strategy).IsActivityActive && !ScreenReceiver.ScreenLocked) //Only call draw if an update has occured
{
_game.Tick();
}
@@ -87,7 +89,7 @@ private void OnTick(object sender, EventArgs args)
{
Resumer.Draw();
}
- _game.Platform.Present();
+ _game.Strategy.EndDraw();
}
}
diff --git a/MonoGame.Framework/Platform/Blazor/BlazorGamePlatform.cs b/MonoGame.Framework/Platform/Blazor/BlazorGamePlatform.cs
index 3ddae8d041b..1b6adc08fe6 100644
--- a/MonoGame.Framework/Platform/Blazor/BlazorGamePlatform.cs
+++ b/MonoGame.Framework/Platform/Blazor/BlazorGamePlatform.cs
@@ -2,21 +2,24 @@
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
+// Copyright (C)2023 Nick Kastellanos
+
using System;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
-namespace MonoGame.Framework
+
+namespace Microsoft.Xna.Platform
{
- class BlazorGamePlatform : GamePlatform
+ sealed class ConcreteGame : GameStrategy
{
//internal static string LaunchParameters;
private BlazorGameWindow _window;
- public BlazorGamePlatform(Game game)
+ public ConcreteGame(Game game)
: base(game)
{
IsActive = true;
@@ -43,9 +46,22 @@ internal override void Run()
//Game.DoExiting();
}
- protected override void OnIsMouseVisibleChanged()
+ public override void Tick()
+ {
+ base.Tick();
+ }
+
+ public override bool IsMouseVisible
{
- _window.MouseVisibleToggled();
+ get { return base.IsMouseVisible; }
+ set
+ {
+ if (base.IsMouseVisible != value)
+ {
+ base.IsMouseVisible = value;
+ _window.MouseVisibleToggled();
+ }
+ }
}
public override void BeforeInitialize()
@@ -64,10 +80,11 @@ public override void BeforeInitialize()
}
}
- public override void Exit()
+ public override void TickExiting()
{
if (_window != null)
_window.Dispose();
+
_window = null;
Window = null;
}
@@ -108,10 +125,10 @@ public override void Log(string message)
Debug.WriteLine(message);
}
- public override void Present()
+ public override void EndDraw()
{
var device = Game.GraphicsDevice;
- if ( device != null )
+ if (device != null)
device.Present();
}
diff --git a/MonoGame.Framework/Platform/Blazor/BlazorGameWindow.cs b/MonoGame.Framework/Platform/Blazor/BlazorGameWindow.cs
index 20f4065d3c6..b30e5761ec9 100644
--- a/MonoGame.Framework/Platform/Blazor/BlazorGameWindow.cs
+++ b/MonoGame.Framework/Platform/Blazor/BlazorGameWindow.cs
@@ -12,7 +12,7 @@
using nkast.Wasm.Canvas;
using nkast.Wasm.Dom;
-namespace MonoGame.Framework
+namespace Microsoft.Xna.Framework
{
// TODO: BlazorGameWindow should be internal
public class BlazorGameWindow : GameWindow, IDisposable
@@ -25,7 +25,7 @@ internal static BlazorGameWindow FromHandle(IntPtr handle)
}
private Window _window;
- private BlazorGamePlatform _platform;
+ private ConcreteGame _platform;
private bool _isResizable;
private bool _isBorderless;
@@ -115,7 +115,7 @@ public override bool IsBorderless
internal Canvas _canvas { get; private set; }
internal Window wasmWindow { get { return _window; } }
- internal BlazorGameWindow(BlazorGamePlatform platform)
+ internal BlazorGameWindow(ConcreteGame platform)
{
_platform = platform;
diff --git a/MonoGame.Framework/Platform/SDL/SDLGamePlatform.cs b/MonoGame.Framework/Platform/SDL/SDLGamePlatform.cs
index 57afe25968c..52b2bbfd811 100644
--- a/MonoGame.Framework/Platform/SDL/SDLGamePlatform.cs
+++ b/MonoGame.Framework/Platform/SDL/SDLGamePlatform.cs
@@ -2,19 +2,23 @@
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
+// Copyright (C)2023 Nick Kastellanos
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
+using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MonoGame.Framework.Utilities;
-namespace Microsoft.Xna.Framework
+
+namespace Microsoft.Xna.Platform
{
- internal class SdlGamePlatform : GamePlatform
+ sealed class ConcreteGame : GameStrategy
{
internal override void Run()
{
@@ -33,6 +37,11 @@ internal override void Run()
Game.DoExiting();
}
+ public override void Tick()
+ {
+ base.Tick();
+ }
+
private readonly List _keys;
private int _isExiting;
@@ -40,7 +49,7 @@ internal override void Run()
private readonly List _dropList;
- public SdlGamePlatform(Game game)
+ public ConcreteGame(Game game)
: base(game)
{
_keys = new List();
@@ -84,9 +93,17 @@ public override void BeforeInitialize()
base.BeforeInitialize();
}
- protected override void OnIsMouseVisibleChanged()
+ public override bool IsMouseVisible
{
- _view.SetCursorVisible(Game.IsMouseVisible);
+ get { return base.IsMouseVisible; }
+ set
+ {
+ if (base.IsMouseVisible != value)
+ {
+ base.IsMouseVisible = value;
+ _view.SetCursorVisible(Game.IsMouseVisible);
+ }
+ }
}
internal override void OnPresentationChanged(PresentationParameters pp)
@@ -292,7 +309,7 @@ private int UTF8ToUnicode(int utf8)
return -1;
}
- public override void Exit()
+ public override void TickExiting()
{
Interlocked.Increment(ref _isExiting);
}
@@ -330,10 +347,11 @@ public override void Log(string message)
Console.WriteLine(message);
}
- public override void Present()
+ public override void EndDraw()
{
- if (Game.GraphicsDevice != null)
- Game.GraphicsDevice.Present();
+ var device = Game.GraphicsDevice;
+ if (device != null)
+ device.Present();
}
protected override void Dispose(bool disposing)
diff --git a/MonoGame.Framework/Platform/Windows/WinFormsGameForm.cs b/MonoGame.Framework/Platform/Windows/WinFormsGameForm.cs
index 15aa1806e2b..f17d55a3f10 100644
--- a/MonoGame.Framework/Platform/Windows/WinFormsGameForm.cs
+++ b/MonoGame.Framework/Platform/Windows/WinFormsGameForm.cs
@@ -11,7 +11,6 @@
using Microsoft.Xna.Framework.Input.Touch;
using MonoGame.Framework;
-
namespace Microsoft.Xna.Framework.Windows
{
internal static class MessageExtensions
diff --git a/MonoGame.Framework/Platform/Windows/WinFormsGamePlatform.cs b/MonoGame.Framework/Platform/Windows/WinFormsGamePlatform.cs
index 76ff7bc8d5b..95ec5e767c5 100644
--- a/MonoGame.Framework/Platform/Windows/WinFormsGamePlatform.cs
+++ b/MonoGame.Framework/Platform/Windows/WinFormsGamePlatform.cs
@@ -11,16 +11,17 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
+using MonoGame.Framework;
-namespace MonoGame.Framework
+namespace Microsoft.Xna.Platform
{
- class WinFormsGamePlatform : GamePlatform
+ sealed class ConcreteGame : GameStrategy
{
//internal static string LaunchParameters;
private WinFormsGameWindow _window;
- public WinFormsGamePlatform(Game game)
+ public ConcreteGame(Game game)
: base(game)
{
_window = new WinFormsGameWindow(this);
@@ -45,9 +46,22 @@ internal override void Run()
Game.DoExiting();
}
- protected override void OnIsMouseVisibleChanged()
+ public override void Tick()
{
- _window.MouseVisibleToggled();
+ base.Tick();
+ }
+
+ public override bool IsMouseVisible
+ {
+ get { return base.IsMouseVisible; }
+ set
+ {
+ if (base.IsMouseVisible != value)
+ {
+ base.IsMouseVisible = value;
+ _window.MouseVisibleToggled();
+ }
+ }
}
public override void BeforeInitialize()
@@ -66,10 +80,11 @@ public override void BeforeInitialize()
}
}
- public override void Exit()
+ public override void TickExiting()
{
if (_window != null)
_window.Dispose();
+
_window = null;
Window = null;
}
@@ -110,10 +125,10 @@ public override void Log(string message)
Debug.WriteLine(message);
}
- public override void Present()
+ public override void EndDraw()
{
var device = Game.GraphicsDevice;
- if ( device != null )
+ if (device != null)
device.Present();
}
diff --git a/MonoGame.Framework/Platform/Windows/WinFormsGameWindow.cs b/MonoGame.Framework/Platform/Windows/WinFormsGameWindow.cs
index 50d2118f6f5..23eb9c41f43 100644
--- a/MonoGame.Framework/Platform/Windows/WinFormsGameWindow.cs
+++ b/MonoGame.Framework/Platform/Windows/WinFormsGameWindow.cs
@@ -16,6 +16,7 @@
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Windows;
+using Microsoft.Xna.Platform;
using ButtonState = Microsoft.Xna.Framework.Input.ButtonState;
using Keys = Microsoft.Xna.Framework.Input.Keys;
using Point = System.Drawing.Point;
@@ -28,7 +29,7 @@ class WinFormsGameWindow : GameWindow, IDisposable
{
internal WinFormsGameForm Form;
- private WinFormsGamePlatform _platform;
+ private ConcreteGame _platform;
private bool _isResizable;
private bool _isBorderless;
@@ -133,7 +134,7 @@ public override bool IsBorderless
#endregion
- internal WinFormsGameWindow(WinFormsGamePlatform platform)
+ internal WinFormsGameWindow(ConcreteGame platform)
{
_platform = platform;
Game = platform.Game;
diff --git a/MonoGame.Framework/Platform/WindowsUniversal/UAPFrameworkView.cs b/MonoGame.Framework/Platform/WindowsUniversal/UAPFrameworkView.cs
index 5d04926b350..e5bfda8e2b8 100644
--- a/MonoGame.Framework/Platform/WindowsUniversal/UAPFrameworkView.cs
+++ b/MonoGame.Framework/Platform/WindowsUniversal/UAPFrameworkView.cs
@@ -4,12 +4,10 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;
using Windows.ApplicationModel.Activation;
+using Microsoft.Xna.Platform;
namespace Microsoft.Xna.Framework
{
@@ -37,8 +35,8 @@ private void ViewActivated(CoreApplicationView sender, IActivatedEventArgs args)
if (args.Kind == ActivationKind.Launch)
{
// Save any launch parameters to be parsed by the platform.
- UAPGamePlatform.LaunchParameters = ((LaunchActivatedEventArgs)args).Arguments;
- UAPGamePlatform.PreviousExecutionState = ((LaunchActivatedEventArgs)args).PreviousExecutionState;
+ ConcreteGame.LaunchParameters = ((LaunchActivatedEventArgs)args).Arguments;
+ ConcreteGame.PreviousExecutionState = ((LaunchActivatedEventArgs)args).PreviousExecutionState;
// Construct the game.
_game = new T();
@@ -52,8 +50,8 @@ private void ViewActivated(CoreApplicationView sender, IActivatedEventArgs args)
{
// Save any protocol launch parameters to be parsed by the platform.
var protocolArgs = args as ProtocolActivatedEventArgs;
- UAPGamePlatform.LaunchParameters = protocolArgs.Uri.AbsoluteUri;
- UAPGamePlatform.PreviousExecutionState = protocolArgs.PreviousExecutionState;
+ ConcreteGame.LaunchParameters = protocolArgs.Uri.AbsoluteUri;
+ ConcreteGame.PreviousExecutionState = protocolArgs.PreviousExecutionState;
// Construct the game if it does not exist
// Protocol can be used to reactivate a suspended game
@@ -81,7 +79,7 @@ public void Run()
public void SetWindow(CoreWindow window)
{
// Initialize the singleton window.
- UAPGameWindow.Instance.Initialize(window, null, UAPGamePlatform.TouchQueue);
+ UAPGameWindow.Instance.Initialize(window, null, ConcreteGame.TouchQueue);
}
public void Uninitialize()
diff --git a/MonoGame.Framework/Platform/WindowsUniversal/UAPGamePlatform.cs b/MonoGame.Framework/Platform/WindowsUniversal/UAPGamePlatform.cs
index 480abd2ab20..81c6e96ae18 100644
--- a/MonoGame.Framework/Platform/WindowsUniversal/UAPGamePlatform.cs
+++ b/MonoGame.Framework/Platform/WindowsUniversal/UAPGamePlatform.cs
@@ -2,6 +2,8 @@
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
+// Copyright (C)2023 Nick Kastellanos
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -13,14 +15,16 @@
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
+using Microsoft.Xna.Framework;
//using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
-namespace Microsoft.Xna.Framework
+
+namespace Microsoft.Xna.Platform
{
- class UAPGamePlatform : GamePlatform
+ sealed class ConcreteGame : GameStrategy
{
internal static string LaunchParameters;
@@ -28,7 +32,7 @@ class UAPGamePlatform : GamePlatform
internal static ApplicationExecutionState PreviousExecutionState { get; set; }
- public UAPGamePlatform(Game game)
+ public ConcreteGame(Game game)
: base(game)
{
// Setup the game window.
@@ -139,6 +143,11 @@ internal override void Run()
Game.DoExiting();
}
+ public override void Tick()
+ {
+ base.Tick();
+ }
+
//TODO: merge Run_UAP_XAML() with Run()
internal override void Run_UAP_XAML()
{
@@ -212,7 +221,7 @@ private void OnRenderFrame(bool isQueueEmpty)
dispatcher.RunIdleAsync(OnRenderFrame);
}
- public override void Exit()
+ public override void TickExiting()
{
if (!UAPGameWindow.Instance.IsExiting)
{
@@ -277,16 +286,25 @@ public override void Log(string Message)
Debug.WriteLine(Message);
}
- public override void Present()
+ public override void EndDraw()
{
var device = Game.GraphicsDevice;
- if ( device != null )
+ if (device != null)
device.Present();
}
- protected override void OnIsMouseVisibleChanged()
+ public override bool IsMouseVisible
{
- UAPGameWindow.Instance.SetCursor(Game.IsMouseVisible);
+ get { return base.IsMouseVisible; }
+ set
+ {
+ if (base.IsMouseVisible != value)
+ {
+ base.IsMouseVisible = value;
+ UAPGameWindow.Instance.SetCursor(Game.IsMouseVisible);
+ }
+ else base.IsMouseVisible = value;
+ }
}
protected override void Dispose(bool disposing)
diff --git a/MonoGame.Framework/Platform/WindowsUniversal/UAPGameWindow.cs b/MonoGame.Framework/Platform/WindowsUniversal/UAPGameWindow.cs
index acdc0ffa57b..d86a871e273 100644
--- a/MonoGame.Framework/Platform/WindowsUniversal/UAPGameWindow.cs
+++ b/MonoGame.Framework/Platform/WindowsUniversal/UAPGameWindow.cs
@@ -18,6 +18,7 @@
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Windows.UI.Xaml.Controls;
+using Microsoft.Xna.Platform;
namespace Microsoft.Xna.Framework
{
@@ -75,7 +76,7 @@ public override DisplayOrientation CurrentOrientation
get { return _orientation; }
}
- private UAPGamePlatform Platform { get { return Game.Instance.Platform as UAPGamePlatform; } }
+ private ConcreteGame Strategy { get { return Game.Instance.Strategy as ConcreteGame; } }
protected internal override void SetSupportedOrientations(DisplayOrientation orientations)
{
@@ -148,7 +149,7 @@ internal void RegisterCoreWindowService()
void Window_VisibilityChanged(CoreWindow sender, VisibilityChangedEventArgs args)
{
- Platform.IsVisible = args.Visible;
+ Strategy.IsVisible = args.Visible;
}
private void Window_FocusChanged(CoreWindow sender, WindowActivatedEventArgs args)
@@ -167,16 +168,16 @@ private void UpdateFocus()
_isFocusChanged = false;
if (_newActivationState == CoreWindowActivationState.Deactivated)
- Platform.IsActive = false;
+ Strategy.IsActive = false;
else
- Platform.IsActive = true;
+ Strategy.IsActive = true;
}
}
private void Window_Closed(CoreWindow sender, CoreWindowEventArgs args)
{
Game.SuppressDraw();
- Game.Platform.Exit();
+ Game.Strategy.TickExiting();
}
private void SetViewBounds(double width, double height)
diff --git a/MonoGame.Framework/Platform/WindowsUniversal/XamlGame.cs b/MonoGame.Framework/Platform/WindowsUniversal/XamlGame.cs
index 1f0f55bdb46..bbc180c9eb7 100644
--- a/MonoGame.Framework/Platform/WindowsUniversal/XamlGame.cs
+++ b/MonoGame.Framework/Platform/WindowsUniversal/XamlGame.cs
@@ -3,9 +3,10 @@
// file 'LICENSE.txt', which is part of this source code package.
using System;
-using Microsoft.Xna.Framework;
using Windows.UI.Core;
using Windows.UI.Xaml.Controls;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Platform;
namespace MonoGame.Framework
@@ -50,10 +51,10 @@ static public T Create(Func gameConstructor, string launchParameters, CoreWin
throw new NullReferenceException("The swap chain panel cannot be null!");
// Save any launch parameters to be parsed by the platform.
- UAPGamePlatform.LaunchParameters = launchParameters;
+ ConcreteGame.LaunchParameters = launchParameters;
// Setup the window class.
- UAPGameWindow.Instance.Initialize(window, swapChainPanel, UAPGamePlatform.TouchQueue);
+ UAPGameWindow.Instance.Initialize(window, swapChainPanel, ConcreteGame.TouchQueue);
// Construct the game.
var game = gameConstructor();
diff --git a/MonoGame.Framework/Platform/iOS/iOSGamePlatform.cs b/MonoGame.Framework/Platform/iOS/iOSGamePlatform.cs
index 581e46b7a04..dd2ad239105 100644
--- a/MonoGame.Framework/Platform/iOS/iOSGamePlatform.cs
+++ b/MonoGame.Framework/Platform/iOS/iOSGamePlatform.cs
@@ -66,6 +66,8 @@ additional consumer rights under your local laws which this license cannot
*/
#endregion License
+// Copyright (C)2023 Nick Kastellanos
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -76,25 +78,27 @@ additional consumer rights under your local laws which this license cannot
using CoreAnimation;
using ObjCRuntime;
+using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
//using Microsoft.Xna.Framework.GamerServices;
-namespace Microsoft.Xna.Framework
+
+namespace Microsoft.Xna.Platform
{
- class iOSGamePlatform : GamePlatform
+ sealed class ConcreteGame : GameStrategy
{
private iOSGameViewController _viewController;
private UIWindow _mainWindow;
private List _applicationObservers;
private CADisplayLink _displayLink;
- public iOSGamePlatform(Game game) :
+ public ConcreteGame(Game game) :
base(game)
{
- game.Services.AddService(typeof(iOSGamePlatform), this);
+ game.Services.AddService(typeof(ConcreteGame), this);
//This also runs the TitleContainer static constructor, ensuring it is done on the main thread
Directory.SetCurrentDirectory(TitleContainer.Location);
@@ -123,9 +127,17 @@ public iOSGamePlatform(Game game) :
//Guide.Initialise(game);
}
- public override void TargetElapsedTimeChanged ()
+ public override TimeSpan TargetElapsedTime
{
- CreateDisplayLink();
+ get { return base.TargetElapsedTime; }
+ set
+ {
+ if (base.TargetElapsedTime != value)
+ {
+ base.TargetElapsedTime = value;
+ CreateDisplayLink();
+ }
+ }
}
private void CreateDisplayLink()
@@ -158,8 +170,13 @@ internal override void Run()
//Game.DoExiting();
}
+ public override void Tick()
+ {
+ base.Tick();
+ }
+
[Obsolete(
- "iOSGamePlatform.IsPlayingVideo must be removed when MonoGame " +
+ "ConcreteGame.IsPlayingVideo must be removed when MonoGame " +
"fully implements the XNA VideoPlayer contract.")]
public bool IsPlayingVideo { get; set; }
@@ -214,7 +231,7 @@ private void StartRunLoop()
CreateDisplayLink();
}
- internal void Tick()
+ internal void iOSTick()
{
if (!Game.IsActive)
return;
@@ -270,6 +287,11 @@ public override void ExitFullScreen()
}
public override void Exit()
+ {
+ throw new InvalidOperationException("This platform's policy does not allow programmatically closing.");
+ }
+
+ public override void TickExiting()
{
// Do Nothing: iOS games do not "exit" or shut down.
throw new NotImplementedException();
@@ -340,8 +362,7 @@ private void ViewController_InterfaceOrientationChanged (object sender, EventArg
var orientation = CurrentOrientation;
// FIXME: The presentation parameters for the GraphicsDevice should
- // be managed by the GraphicsDevice itself. Not by
- // iOSGamePlatform.
+ // be managed by the GraphicsDevice itself. Not by ConcreteGame.
var gdm = (GraphicsDeviceManager) Game.Services.GetService (typeof (IGraphicsDeviceManager));
TouchPanel.DisplayOrientation = orientation;
diff --git a/MonoGame.Framework/Platform/iOS/iOSGameView.cs b/MonoGame.Framework/Platform/iOS/iOSGameView.cs
index 66ed0c1eab2..f6b2d62c9d1 100644
--- a/MonoGame.Framework/Platform/iOS/iOSGameView.cs
+++ b/MonoGame.Framework/Platform/iOS/iOSGameView.cs
@@ -82,18 +82,20 @@ additional consumer rights under your local laws which this license cannot
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
+using Microsoft.Xna.Platform;
+
namespace Microsoft.Xna.Framework {
[Register("iOSGameView")]
partial class iOSGameView : UIView {
- private readonly iOSGamePlatform _platform;
+ private readonly ConcreteGame _platform;
private int _colorbuffer;
private int _depthbuffer;
private int _framebuffer;
#region Construction/Destruction
- public iOSGameView (iOSGamePlatform platform, CGRect frame)
+ public iOSGameView (ConcreteGame platform, CGRect frame)
: base(frame)
{
if (platform == null)
@@ -200,7 +202,7 @@ private void DestroyContext ()
[Export("doTick")]
void DoTick()
{
- _platform.Tick();
+ _platform.iOSTick();
}
private void CreateFramebuffer ()
@@ -325,7 +327,7 @@ private void DestroyFramebuffer ()
// FIXME: This logic belongs in GraphicsDevice.Present, not
// here. If it can someday be moved there, then the
// normal call to Present in Game.Tick should cover
- // this. For now, iOSGamePlatform will call Present
+ // this. For now, ConcreteGame will call Present
// in the Draw/Update loop handler.
public void Present ()
{
diff --git a/MonoGame.Framework/Platform/iOS/iOSGameViewController.cs b/MonoGame.Framework/Platform/iOS/iOSGameViewController.cs
index d59bd3d113b..0530d3bd452 100644
--- a/MonoGame.Framework/Platform/iOS/iOSGameViewController.cs
+++ b/MonoGame.Framework/Platform/iOS/iOSGameViewController.cs
@@ -9,6 +9,8 @@
using Foundation;
using CoreGraphics;
using ObjCRuntime;
+using Microsoft.Xna.Platform;
+
namespace Microsoft.Xna.Framework
{
@@ -19,12 +21,12 @@ class iOSGameViewController :
UIViewController
#endif
{
- iOSGamePlatform _platform;
+ ConcreteGame _platform;
#if TVOS
IPlatformBackButton platformBackButton;
#endif
- public iOSGameViewController(iOSGamePlatform platform)
+ public iOSGameViewController(ConcreteGame platform)
{
if (platform == null)
throw new ArgumentNullException("platform");
@@ -131,7 +133,7 @@ public override void ViewWillTransitionToSize(CGSize toSize, IUIViewControllerTr
UIInterfaceOrientation prevOrientation = InterfaceOrientation;
// In iOS 8+ DidRotate is no longer called after a rotation
- // But we need to notify iOSGamePlatform to update back buffer so we explicitly call it
+ // But we need to notify ConcreteGame to update back buffer so we explicitly call it
// We do this within the animateAlongside action, which at the point of calling
// will have the new InterfaceOrientation set
diff --git a/MonoGame.Framework/XNA.Framework.Android.csproj b/MonoGame.Framework/XNA.Framework.Android.csproj
index 1f44b13d9b4..6ce5a4440a8 100644
--- a/MonoGame.Framework/XNA.Framework.Android.csproj
+++ b/MonoGame.Framework/XNA.Framework.Android.csproj
@@ -153,7 +153,6 @@
-
diff --git a/MonoGame.Framework/XNA.Framework.Blazor.csproj b/MonoGame.Framework/XNA.Framework.Blazor.csproj
index 4f1c86cf3a2..dfc1348e119 100644
--- a/MonoGame.Framework/XNA.Framework.Blazor.csproj
+++ b/MonoGame.Framework/XNA.Framework.Blazor.csproj
@@ -50,7 +50,6 @@
-
diff --git a/MonoGame.Framework/XNA.Framework.DesktopGL.csproj b/MonoGame.Framework/XNA.Framework.DesktopGL.csproj
index f1985d7da85..c2efc8d0719 100644
--- a/MonoGame.Framework/XNA.Framework.DesktopGL.csproj
+++ b/MonoGame.Framework/XNA.Framework.DesktopGL.csproj
@@ -150,7 +150,6 @@
-
diff --git a/MonoGame.Framework/XNA.Framework.UAP.csproj b/MonoGame.Framework/XNA.Framework.UAP.csproj
index 8a2f02b7ea0..00292df630d 100644
--- a/MonoGame.Framework/XNA.Framework.UAP.csproj
+++ b/MonoGame.Framework/XNA.Framework.UAP.csproj
@@ -160,7 +160,6 @@
-
diff --git a/MonoGame.Framework/XNA.Framework.WindowsDX11.csproj b/MonoGame.Framework/XNA.Framework.WindowsDX11.csproj
index 9f6ac9f1df6..fc20137a2b4 100644
--- a/MonoGame.Framework/XNA.Framework.WindowsDX11.csproj
+++ b/MonoGame.Framework/XNA.Framework.WindowsDX11.csproj
@@ -168,7 +168,6 @@
-
diff --git a/MonoGame.Framework/XNA.Framework.iOS.csproj b/MonoGame.Framework/XNA.Framework.iOS.csproj
index 13c1ad75aae..b58b867cfec 100644
--- a/MonoGame.Framework/XNA.Framework.iOS.csproj
+++ b/MonoGame.Framework/XNA.Framework.iOS.csproj
@@ -197,7 +197,6 @@
-
diff --git a/Tests/Framework/TestGameBase.cs b/Tests/Framework/TestGameBase.cs
index 68b4196da39..7fd090cb39a 100644
--- a/Tests/Framework/TestGameBase.cs
+++ b/Tests/Framework/TestGameBase.cs
@@ -238,7 +238,7 @@ protected void DoExit()
// complete running all the unit tests. So we do the
// next best thing can call the interal platform code
// directly which produces the same result.
- Platform.Exit();
+ Strategy.Exit();
SuppressDraw();
#endif
}