Document App and Test additions (+ minor tweaks)
This commit is contained in:
parent
0019b7c073
commit
189c3ca2a5
9 changed files with 249 additions and 8 deletions
|
@ -5,6 +5,10 @@ using GamecraftModdingAPI.Tests;
|
|||
namespace GamecraftModdingAPI.App
|
||||
{
|
||||
#if TEST
|
||||
/// <summary>
|
||||
/// App callbacks tests.
|
||||
/// Only available in TEST builds.
|
||||
/// </summary>
|
||||
[APITestClass]
|
||||
public static class AppCallbacksTest
|
||||
{
|
||||
|
|
|
@ -6,32 +6,56 @@ using GamecraftModdingAPI.Utility;
|
|||
|
||||
namespace GamecraftModdingAPI.App
|
||||
{
|
||||
/// <summary>
|
||||
/// The Gamecraft application that is running this code right now.
|
||||
/// </summary>
|
||||
public class Client
|
||||
{
|
||||
// extensible engine
|
||||
protected static AppEngine appEngine = new AppEngine();
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires whenever the main menu is loaded.
|
||||
/// </summary>
|
||||
public static event EventHandler<MenuEventArgs> EnterMenu
|
||||
{
|
||||
add => appEngine.EnterMenu += value;
|
||||
remove => appEngine.EnterMenu -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fire whenever the main menu is exited.
|
||||
/// </summary>
|
||||
public static event EventHandler<MenuEventArgs> ExitMenu
|
||||
{
|
||||
add => appEngine.ExitMenu += value;
|
||||
remove => appEngine.ExitMenu -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gamecraft build version string.
|
||||
/// Usually this is in the form YYYY.mm.DD.HH.MM.SS
|
||||
/// </summary>
|
||||
/// <value>The version.</value>
|
||||
public string Version
|
||||
{
|
||||
get => Application.version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity version string.
|
||||
/// </summary>
|
||||
/// <value>The unity version.</value>
|
||||
public string UnityVersion
|
||||
{
|
||||
get => Application.unityVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Game saves currently visible in the menu.
|
||||
/// These take a second to completely populate after the EnterMenu event fires.
|
||||
/// </summary>
|
||||
/// <value>My games.</value>
|
||||
public Game[] MyGames
|
||||
{
|
||||
get
|
||||
|
|
|
@ -9,12 +9,17 @@ using Svelto.ECS;
|
|||
|
||||
using GamecraftModdingAPI.Tasks;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
// TODO: exceptions
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
{
|
||||
/// <summary>
|
||||
/// An in-game save.
|
||||
/// This can be a menu item for a local save or the currently loaded save.
|
||||
/// Support for Steam Workshop coming soon (hopefully).
|
||||
/// </summary>
|
||||
public class Game
|
||||
{
|
||||
// extensible engines
|
||||
protected static GameGameEngine gameEngine = new GameGameEngine();
|
||||
protected static GameMenuEngine menuEngine = new GameMenuEngine();
|
||||
protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine();
|
||||
|
@ -25,10 +30,18 @@ namespace GamecraftModdingAPI.App
|
|||
private bool menuMode = true;
|
||||
private bool hasId = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">Menu identifier.</param>
|
||||
public Game(uint id) : this(new EGID(id, MyGamesScreenExclusiveGroups.MyGames))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">Menu identifier.</param>
|
||||
public Game(EGID id)
|
||||
{
|
||||
this.Id = id.entityID;
|
||||
|
@ -38,6 +51,10 @@ namespace GamecraftModdingAPI.App
|
|||
if (!VerifyMode()) throw new AppStateException("Game cannot be created while not in a game nor in a menu (is the game in a loading screen?)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class without id.
|
||||
/// This is assumed to be the current game.
|
||||
/// </summary>
|
||||
public Game()
|
||||
{
|
||||
menuMode = false;
|
||||
|
@ -45,11 +62,21 @@ namespace GamecraftModdingAPI.App
|
|||
if (menuEngine.IsInMenu) throw new GameNotFoundException("Game not found.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently loaded game.
|
||||
/// If in a menu, manipulating the returned object may not work as intended.
|
||||
/// </summary>
|
||||
/// <returns>The current game.</returns>
|
||||
public static Game CurrentGame()
|
||||
{
|
||||
return new Game();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new game and adds it to the menu.
|
||||
/// If not in a menu, this will throw AppStateException.
|
||||
/// </summary>
|
||||
/// <returns>The new game.</returns>
|
||||
public static Game NewGame()
|
||||
{
|
||||
if (!menuEngine.IsInMenu) throw new AppStateException("New Game cannot be created while not in a menu.");
|
||||
|
@ -59,47 +86,77 @@ namespace GamecraftModdingAPI.App
|
|||
return new Game(egid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires whenever a game is switched to simulation mode (time running mode).
|
||||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Simulate
|
||||
{
|
||||
add => buildSimEventEngine.SimulationMode += value;
|
||||
remove => buildSimEventEngine.SimulationMode -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires whenever a game is switched to edit mode (time stopped mode).
|
||||
/// This does not fire when a game is loaded.
|
||||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Edit
|
||||
{
|
||||
add => buildSimEventEngine.BuildMode += value;
|
||||
remove => buildSimEventEngine.BuildMode -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires right after a game is completely loaded.
|
||||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Enter
|
||||
{
|
||||
add => gameEngine.EnterGame += value;
|
||||
remove => gameEngine.EnterGame -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires right before a game returns to the main menu.
|
||||
/// At this point, Gamecraft is transitioning state so many things are invalid/unstable here.
|
||||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Exit
|
||||
{
|
||||
add => gameEngine.ExitGame += value;
|
||||
remove => gameEngine.ExitGame -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game's unique menu identifier.
|
||||
/// </summary>
|
||||
/// <value>The identifier.</value>
|
||||
public uint Id
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game's unique menu EGID.
|
||||
/// </summary>
|
||||
/// <value>The egid.</value>
|
||||
public EGID EGID
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game is a (valid) menu item.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if menu item; otherwise, <c>false</c>.</value>
|
||||
public bool MenuItem
|
||||
{
|
||||
get => menuMode && hasId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game's name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
|
@ -123,6 +180,10 @@ namespace GamecraftModdingAPI.App
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game's description.
|
||||
/// </summary>
|
||||
/// <value>The description.</value>
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
|
@ -146,6 +207,10 @@ namespace GamecraftModdingAPI.App
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The path to the game's save folder.
|
||||
/// </summary>
|
||||
/// <value>The path.</value>
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
|
@ -170,6 +235,11 @@ namespace GamecraftModdingAPI.App
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Steam Workshop Id of the game save.
|
||||
/// In most cases this is invalid and returns 0, so this can be ignored.
|
||||
/// </summary>
|
||||
/// <value>The workshop identifier.</value>
|
||||
public ulong WorkshopId
|
||||
{
|
||||
get
|
||||
|
@ -195,6 +265,10 @@ namespace GamecraftModdingAPI.App
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game is in simulation mode.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is simulating; otherwise, <c>false</c>.</value>
|
||||
public bool IsSimulating
|
||||
{
|
||||
get
|
||||
|
@ -211,6 +285,11 @@ namespace GamecraftModdingAPI.App
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game is in time-running mode.
|
||||
/// Alias of IsSimulating.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is time running; otherwise, <c>false</c>.</value>
|
||||
public bool IsTimeRunning
|
||||
{
|
||||
get => IsSimulating;
|
||||
|
@ -221,6 +300,10 @@ namespace GamecraftModdingAPI.App
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game is in time-stopped mode.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is time stopped; otherwise, <c>false</c>.</value>
|
||||
public bool IsTimeStopped
|
||||
{
|
||||
get
|
||||
|
@ -237,6 +320,9 @@ namespace GamecraftModdingAPI.App
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the time mode.
|
||||
/// </summary>
|
||||
public void ToggleTimeMode()
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
|
@ -247,6 +333,11 @@ namespace GamecraftModdingAPI.App
|
|||
gameEngine.ToggleTimeMode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the game save.
|
||||
/// This happens asynchronously, so when this method returns the game not loaded yet.
|
||||
/// Use the Game.Enter event to perform operations after the game has completely loaded.
|
||||
/// </summary>
|
||||
public void EnterGame()
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
|
@ -258,17 +349,43 @@ namespace GamecraftModdingAPI.App
|
|||
Scheduler.Schedule(task);
|
||||
}
|
||||
|
||||
public void ExitGame()
|
||||
/// <summary>
|
||||
/// Return to the menu.
|
||||
/// Part of this always happens asynchronously, so when this method returns the game has not exited yet.
|
||||
/// Use the Client.EnterMenu event to perform operations after the game has completely exited.
|
||||
/// </summary>
|
||||
/// <param name="async">If set to <c>true</c>, do this async.</param>
|
||||
public void ExitGame(bool async = false)
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (menuMode)
|
||||
{
|
||||
throw new GameNotFoundException("Cannot exit game using menu ID");
|
||||
}
|
||||
ISchedulable task = new Once(() => { gameEngine.ExitCurrentGame(); this.menuMode = true; });
|
||||
Scheduler.Schedule(task);
|
||||
gameEngine.ExitCurrentGame(async);
|
||||
this.menuMode = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the game.
|
||||
/// Part of this happens asynchronously, so when this method returns the game has not been saved yet.
|
||||
/// </summary>
|
||||
public void SaveGame()
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (menuMode)
|
||||
{
|
||||
throw new GameNotFoundException("Cannot save game using menu ID");
|
||||
}
|
||||
gameEngine.SaveCurrentGame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add information to the in-game debug display.
|
||||
/// When this object is garbage collected, this debug info is automatically removed.
|
||||
/// </summary>
|
||||
/// <param name="id">Debug info identifier.</param>
|
||||
/// <param name="contentGetter">Content getter.</param>
|
||||
public void AddDebugInfo(string id, Func<string> contentGetter)
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
|
@ -280,6 +397,11 @@ namespace GamecraftModdingAPI.App
|
|||
debugIds.Add(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove information from the in-game debug display.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if debug info was removed, <c>false</c> otherwise.</returns>
|
||||
/// <param name="id">Debug info identifier.</param>
|
||||
public bool RemoveDebugInfo(string id)
|
||||
{
|
||||
if (!VerifyMode()) return false;
|
||||
|
|
|
@ -47,9 +47,18 @@ namespace GamecraftModdingAPI.App
|
|||
private set;
|
||||
} = false;
|
||||
|
||||
public void ExitCurrentGame()
|
||||
public void ExitCurrentGame(bool async = false)
|
||||
{
|
||||
ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING);
|
||||
if (async)
|
||||
{
|
||||
ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING);
|
||||
}
|
||||
else
|
||||
{
|
||||
entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true;
|
||||
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IEnumerator<TaskContract> ExitCurrentGameAsync()
|
||||
|
@ -62,6 +71,14 @@ namespace GamecraftModdingAPI.App
|
|||
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
}
|
||||
|
||||
public void SaveCurrentGame()
|
||||
{
|
||||
ref GameSceneEntityStruct gses = ref entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
gses.LoadAfterSaving = false;
|
||||
gses.SaveNow = true;
|
||||
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
}
|
||||
|
||||
public bool IsTimeRunningMode()
|
||||
{
|
||||
return TimeRunningModeUtil.IsTimeRunningMode(entitiesDB);
|
||||
|
|
|
@ -45,6 +45,15 @@ namespace GamecraftModdingAPI
|
|||
return playerEngine.ExistsById(player);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The amount of Players in the current game.
|
||||
/// </summary>
|
||||
/// <returns>The count.</returns>
|
||||
public static uint Count()
|
||||
{
|
||||
return playerEngine.GetAllPlayerCount();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.Player"/> class.
|
||||
/// </summary>
|
||||
|
|
|
@ -65,6 +65,26 @@ namespace GamecraftModdingAPI.Players
|
|||
return uint.MaxValue;
|
||||
}
|
||||
|
||||
public uint GetAllPlayerCount()
|
||||
{
|
||||
uint count = 0;
|
||||
foreach (ExclusiveGroupStruct eg in PlayersExclusiveGroups.AllPlayers)
|
||||
{
|
||||
count += entitiesDB.Count<PlayerIDStruct>(eg);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public uint GetLocalPlayerCount()
|
||||
{
|
||||
return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
|
||||
}
|
||||
|
||||
public uint GetRemotePlayerCount()
|
||||
{
|
||||
return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
|
||||
}
|
||||
|
||||
public bool ExistsById(uint playerId)
|
||||
{
|
||||
return entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.LocalPlayers)
|
||||
|
@ -109,7 +129,7 @@ namespace GamecraftModdingAPI.Players
|
|||
{
|
||||
return ((Quaternion) rbes.rotation).eulerAngles;
|
||||
}
|
||||
return default;
|
||||
return default(float3);
|
||||
}
|
||||
|
||||
public bool SetRotation(uint playerId, float3 value)
|
||||
|
@ -174,7 +194,7 @@ namespace GamecraftModdingAPI.Players
|
|||
{
|
||||
return rbes.physicsMass;
|
||||
}
|
||||
return default;
|
||||
return default(PhysicsMass);
|
||||
}
|
||||
|
||||
public bool SetInverseMass(uint playerId, float inverseMass)
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
using System;
|
||||
namespace GamecraftModdingAPI.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test type.
|
||||
/// When provided to APITestCaseAttribute, this dictates when the test case is called.
|
||||
/// </summary>
|
||||
public enum TestType
|
||||
{
|
||||
Menu,
|
||||
|
@ -9,6 +13,10 @@ namespace GamecraftModdingAPI.Tests
|
|||
EditMode,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API Test Class attribute.
|
||||
/// Classes without this attribute will be ignored when searching for test cases.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class APITestClassAttribute : Attribute
|
||||
{
|
||||
|
@ -20,6 +28,10 @@ namespace GamecraftModdingAPI.Tests
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API Test Case attribute.
|
||||
/// Static methods with this attribute will be called when the API test system is running.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class APITestCaseAttribute : Attribute
|
||||
{
|
||||
|
@ -31,12 +43,20 @@ namespace GamecraftModdingAPI.Tests
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API Test StartUp attribute.
|
||||
/// Static methods with this attribute will be called before any test case is run by the API test system.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class APITestStartUpAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API Test TearDown attribute.
|
||||
/// Static methods with this attribute will be called after all API test system test cases have completed (failed or succeeded).
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class APITestTearDownAttribute : Attribute
|
||||
{
|
||||
|
|
|
@ -5,6 +5,9 @@ using System.Runtime.CompilerServices;
|
|||
|
||||
namespace GamecraftModdingAPI.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// API test system assertion utilities.
|
||||
/// </summary>
|
||||
public static class Assert
|
||||
{
|
||||
private static StreamWriter logFile = null;
|
||||
|
@ -19,6 +22,11 @@ namespace GamecraftModdingAPI.Tests
|
|||
|
||||
private const string INFO = "DEBUG: ";
|
||||
|
||||
/// <summary>
|
||||
/// Log a message to the test log.
|
||||
/// </summary>
|
||||
/// <param name="msg">Message.</param>
|
||||
/// <param name="end">Message ending.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Log(string msg, string end = "\n")
|
||||
{
|
||||
|
@ -27,6 +35,16 @@ namespace GamecraftModdingAPI.Tests
|
|||
logFile.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the event receives a callback... eventually.
|
||||
/// Add the eventhandler returned by this method to the relevant event.
|
||||
/// This does not assert that the callback happens under that event's intended circumstances.
|
||||
/// Add another event handler to assert specific circumstance requirements.
|
||||
/// </summary>
|
||||
/// <returns>The callback event handler.</returns>
|
||||
/// <param name="eventName">Event name.</param>
|
||||
/// <param name="eventMsg">Event error message.</param>
|
||||
/// <typeparam name="T">The event handler callback argument object.</typeparam>
|
||||
public static EventHandler<T> CallsBack<T>(string eventName, string eventMsg = null)
|
||||
{
|
||||
if (eventMsg == null) eventMsg = $"expected callback to {eventName} but it never occurred...";
|
||||
|
|
|
@ -15,6 +15,9 @@ using GamecraftModdingAPI.Utility;
|
|||
|
||||
namespace GamecraftModdingAPI.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// API test system root class.
|
||||
/// </summary>
|
||||
public static class TestRoot
|
||||
{
|
||||
public static bool AutoShutdown = true;
|
||||
|
@ -229,6 +232,10 @@ namespace GamecraftModdingAPI.Tests
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the tests.
|
||||
/// </summary>
|
||||
/// <param name="asm">Assembly to search for tests. When set to null, this uses the GamecraftModdingAPI assembly. </param>
|
||||
public static void RunTests(Assembly asm = null)
|
||||
{
|
||||
if (asm == null) asm = Assembly.GetExecutingAssembly();
|
||||
|
|
Loading…
Reference in a new issue