@@ -0,0 +1,31 @@ | |||
using System; | |||
using GamecraftModdingAPI.Tests; | |||
namespace GamecraftModdingAPI.App | |||
{ | |||
#if TEST | |||
[APITestClass] | |||
public static class AppCallbacksTest | |||
{ | |||
[APITestStartUp] | |||
public static void StartUp() | |||
{ | |||
// this could be split into 6 separate test cases | |||
Game.Enter += Assert.CallsBack<GameEventArgs>("GameEnter"); | |||
Game.Exit += Assert.CallsBack<GameEventArgs>("GameExit"); | |||
Game.Simulate += Assert.CallsBack<GameEventArgs>("GameSimulate"); | |||
Game.Edit += Assert.CallsBack<GameEventArgs>("GameEdit"); | |||
Client.EnterMenu += Assert.CallsBack<MenuEventArgs>("MenuEnter"); | |||
Client.ExitMenu += Assert.CallsBack<MenuEventArgs>("MenuExit"); | |||
} | |||
[APITestCase(TestType.Game)] | |||
public static void Test() | |||
{ | |||
// the test is actually completely implemented in StartUp() | |||
// this is here just so it looks less weird (not required) | |||
} | |||
} | |||
#endif | |||
} |
@@ -0,0 +1,63 @@ | |||
using System; | |||
using RobocraftX.GUI.MyGamesScreen; | |||
using RobocraftX.GUI; | |||
using Svelto.ECS; | |||
using GamecraftModdingAPI.Engines; | |||
using GamecraftModdingAPI.Utility; | |||
namespace GamecraftModdingAPI.App | |||
{ | |||
public class AppEngine : IFactoryEngine | |||
{ | |||
public event EventHandler<MenuEventArgs> EnterMenu; | |||
public event EventHandler<MenuEventArgs> ExitMenu; | |||
public IEntityFactory Factory { set; private get; } | |||
public string Name => "GamecraftModdingAPIAppEngine"; | |||
public bool isRemovable => false; | |||
public EntitiesDB entitiesDB { set; private get; } | |||
public void Dispose() | |||
{ | |||
IsInMenu = false; | |||
ExceptionUtil.InvokeEvent(ExitMenu, this, new MenuEventArgs { }); | |||
} | |||
public void Ready() | |||
{ | |||
IsInMenu = true; | |||
ExceptionUtil.InvokeEvent(EnterMenu, this, new MenuEventArgs { }); | |||
} | |||
// app functionality | |||
public bool IsInMenu | |||
{ | |||
get; | |||
private set; | |||
} = false; | |||
public Game[] GetMyGames() | |||
{ | |||
EntityCollection<MyGameDataEntityStruct> mgsevs = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames); | |||
Game[] games = new Game[mgsevs.count]; | |||
for (int i = 0; i < mgsevs.count; i++) | |||
{ | |||
Utility.Logging.MetaDebugLog($"Found game named {mgsevs[i].GameName}"); | |||
games[i] = new Game(mgsevs[i].ID); | |||
} | |||
return games; | |||
} | |||
} | |||
public struct MenuEventArgs | |||
{ | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
using System; | |||
using System.Runtime.Serialization; | |||
namespace GamecraftModdingAPI.App | |||
{ | |||
public class AppException : GamecraftModdingAPIException | |||
{ | |||
public AppException() | |||
{ | |||
} | |||
public AppException(string message) : base(message) | |||
{ | |||
} | |||
public AppException(string message, Exception innerException) : base(message, innerException) | |||
{ | |||
} | |||
} | |||
public class AppStateException : AppException | |||
{ | |||
public AppStateException() | |||
{ | |||
} | |||
public AppStateException(string message) : base(message) | |||
{ | |||
} | |||
} | |||
public class GameNotFoundException : AppException | |||
{ | |||
public GameNotFoundException() | |||
{ | |||
} | |||
public GameNotFoundException(string message) : base(message) | |||
{ | |||
} | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
using System; | |||
using UnityEngine; | |||
using GamecraftModdingAPI.Utility; | |||
namespace GamecraftModdingAPI.App | |||
{ | |||
public class Client | |||
{ | |||
protected static AppEngine appEngine = new AppEngine(); | |||
public static event EventHandler<MenuEventArgs> EnterMenu | |||
{ | |||
add => appEngine.EnterMenu += value; | |||
remove => appEngine.EnterMenu -= value; | |||
} | |||
public static event EventHandler<MenuEventArgs> ExitMenu | |||
{ | |||
add => appEngine.ExitMenu += value; | |||
remove => appEngine.ExitMenu -= value; | |||
} | |||
public string Version | |||
{ | |||
get => Application.version; | |||
} | |||
public string UnityVersion | |||
{ | |||
get => Application.unityVersion; | |||
} | |||
public Game[] MyGames | |||
{ | |||
get | |||
{ | |||
if (!appEngine.IsInMenu) return new Game[0]; | |||
return appEngine.GetMyGames(); | |||
} | |||
} | |||
internal static void Init() | |||
{ | |||
MenuEngineManager.AddMenuEngine(appEngine); | |||
} | |||
} | |||
} |
@@ -0,0 +1,331 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
using RobocraftX.Common; | |||
using RobocraftX.GUI.MyGamesScreen; | |||
using RobocraftX.StateSync; | |||
using Svelto.ECS; | |||
using GamecraftModdingAPI.Tasks; | |||
using GamecraftModdingAPI.Utility; | |||
// TODO: exceptions | |||
namespace GamecraftModdingAPI.App | |||
{ | |||
public class Game | |||
{ | |||
protected static GameGameEngine gameEngine = new GameGameEngine(); | |||
protected static GameMenuEngine menuEngine = new GameMenuEngine(); | |||
protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine(); | |||
protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine(); | |||
private List<string> debugIds = new List<string>(); | |||
private bool menuMode = true; | |||
private bool hasId = false; | |||
public Game(uint id) : this(new EGID(id, MyGamesScreenExclusiveGroups.MyGames)) | |||
{ | |||
} | |||
public Game(EGID id) | |||
{ | |||
this.Id = id.entityID; | |||
this.EGID = id; | |||
this.hasId = true; | |||
menuMode = true; | |||
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?)"); | |||
} | |||
public Game() | |||
{ | |||
menuMode = false; | |||
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?)"); | |||
if (menuEngine.IsInMenu) throw new GameNotFoundException("Game not found."); | |||
} | |||
public static Game CurrentGame() | |||
{ | |||
return new Game(); | |||
} | |||
public static Game NewGame() | |||
{ | |||
if (!menuEngine.IsInMenu) throw new AppStateException("New Game cannot be created while not in a menu."); | |||
uint nextId = menuEngine.HighestID() + 1; | |||
EGID egid = new EGID(nextId, MyGamesScreenExclusiveGroups.MyGames); | |||
menuEngine.CreateMyGame(egid); | |||
return new Game(egid); | |||
} | |||
public static event EventHandler<GameEventArgs> Simulate | |||
{ | |||
add => buildSimEventEngine.SimulationMode += value; | |||
remove => buildSimEventEngine.SimulationMode -= value; | |||
} | |||
public static event EventHandler<GameEventArgs> Edit | |||
{ | |||
add => buildSimEventEngine.BuildMode += value; | |||
remove => buildSimEventEngine.BuildMode -= value; | |||
} | |||
public static event EventHandler<GameEventArgs> Enter | |||
{ | |||
add => gameEngine.EnterGame += value; | |||
remove => gameEngine.EnterGame -= value; | |||
} | |||
public static event EventHandler<GameEventArgs> Exit | |||
{ | |||
add => gameEngine.ExitGame += value; | |||
remove => gameEngine.ExitGame -= value; | |||
} | |||
public uint Id | |||
{ | |||
get; | |||
private set; | |||
} | |||
public EGID EGID | |||
{ | |||
get; | |||
private set; | |||
} | |||
public bool MenuItem | |||
{ | |||
get => menuMode && hasId; | |||
} | |||
public string Name | |||
{ | |||
get | |||
{ | |||
if (!VerifyMode()) return null; | |||
if (menuMode) return menuEngine.GetGameInfo(EGID).GameName; | |||
return GameMode.SaveGameDetails.Name; | |||
} | |||
set | |||
{ | |||
if (!VerifyMode()) return; | |||
if (menuMode) | |||
{ | |||
menuEngine.SetGameName(EGID, value); | |||
} | |||
else | |||
{ | |||
GameMode.SaveGameDetails.Name = value; | |||
} | |||
} | |||
} | |||
public string Description | |||
{ | |||
get | |||
{ | |||
if (!VerifyMode()) return null; | |||
if (menuMode) return menuEngine.GetGameInfo(EGID).GameDescription; | |||
return ""; | |||
} | |||
set | |||
{ | |||
if (!VerifyMode()) return; | |||
if (menuMode) | |||
{ | |||
menuEngine.SetGameDescription(EGID, value); | |||
} | |||
else | |||
{ | |||
// No description exists in-game | |||
} | |||
} | |||
} | |||
public string Path | |||
{ | |||
get | |||
{ | |||
if (!VerifyMode()) return null; | |||
if (menuMode) return menuEngine.GetGameInfo(EGID).SavedGamePath; | |||
return GameMode.SaveGameDetails.Folder; | |||
} | |||
set | |||
{ | |||
if (!VerifyMode()) return; | |||
if (menuMode) | |||
{ | |||
menuEngine.GetGameInfo(EGID).SavedGamePath.Set(value); | |||
} | |||
else | |||
{ | |||
// this likely breaks things | |||
GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, value, GameMode.SaveGameDetails.WorkshopId); | |||
} | |||
} | |||
} | |||
public ulong WorkshopId | |||
{ | |||
get | |||
{ | |||
if (!VerifyMode()) return 0uL; | |||
if (menuMode) return 0uL; // MyGames don't have workshop IDs | |||
return GameMode.SaveGameDetails.WorkshopId; | |||
} | |||
set | |||
{ | |||
VerifyMode(); | |||
if (menuMode) | |||
{ | |||
// MyGames don't have workshop IDs | |||
// menuEngine.GetGameInfo(EGID).GameName.Set(value); | |||
} | |||
else | |||
{ | |||
// this likely breaks things | |||
GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, GameMode.SaveGameDetails.Folder, value); | |||
} | |||
} | |||
} | |||
public bool IsSimulating | |||
{ | |||
get | |||
{ | |||
if (!VerifyMode()) return false; | |||
return !menuMode && gameEngine.IsTimeRunningMode(); | |||
} | |||
set | |||
{ | |||
if (!VerifyMode()) return; | |||
if (!menuMode && gameEngine.IsTimeRunningMode() != value) | |||
gameEngine.ToggleTimeMode(); | |||
} | |||
} | |||
public bool IsTimeRunning | |||
{ | |||
get => IsSimulating; | |||
set | |||
{ | |||
IsSimulating = value; | |||
} | |||
} | |||
public bool IsTimeStopped | |||
{ | |||
get | |||
{ | |||
if (!VerifyMode()) return false; | |||
return !menuMode && gameEngine.IsTimeStoppedMode(); | |||
} | |||
set | |||
{ | |||
if (!VerifyMode()) return; | |||
if (!menuMode && gameEngine.IsTimeStoppedMode() != value) | |||
gameEngine.ToggleTimeMode(); | |||
} | |||
} | |||
public void ToggleTimeMode() | |||
{ | |||
if (!VerifyMode()) return; | |||
if (menuMode || !gameEngine.IsInGame) | |||
{ | |||
throw new AppStateException("Game menu item cannot toggle it's time mode"); | |||
} | |||
gameEngine.ToggleTimeMode(); | |||
} | |||
public void EnterGame() | |||
{ | |||
if (!VerifyMode()) return; | |||
if (!hasId) | |||
{ | |||
throw new GameNotFoundException("Game has an invalid ID"); | |||
} | |||
ISchedulable task = new Once(() => { menuEngine.EnterGame(EGID); this.menuMode = false; }); | |||
Scheduler.Schedule(task); | |||
} | |||
public void ExitGame() | |||
{ | |||
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); | |||
} | |||
public void AddDebugInfo(string id, Func<string> contentGetter) | |||
{ | |||
if (!VerifyMode()) return; | |||
if (menuMode) | |||
{ | |||
throw new GameNotFoundException("Game object references a menu item but AddDebugInfo only works on the currently-loaded game"); | |||
} | |||
debugOverlayEngine.SetInfo(id, contentGetter); | |||
debugIds.Add(id); | |||
} | |||
public bool RemoveDebugInfo(string id) | |||
{ | |||
if (!VerifyMode()) return false; | |||
if (menuMode) | |||
{ | |||
throw new GameNotFoundException("Game object references a menu item but RemoveDebugInfo only works on the currently-loaded game"); | |||
} | |||
if (!debugIds.Contains(id)) return false; | |||
debugOverlayEngine.RemoveInfo(id); | |||
return debugIds.Remove(id); | |||
} | |||
~Game() | |||
{ | |||
foreach (string id in debugIds) | |||
{ | |||
debugOverlayEngine.RemoveInfo(id); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
private bool VerifyMode() | |||
{ | |||
if (menuMode && (!menuEngine.IsInMenu || gameEngine.IsInGame)) | |||
{ | |||
// either game loading or API is broken | |||
return false; | |||
} | |||
if (!menuMode && (menuEngine.IsInMenu || !gameEngine.IsInGame)) | |||
{ | |||
// either game loading or API is broken | |||
return false; | |||
} | |||
return true; | |||
} | |||
internal static void Init() | |||
{ | |||
GameEngineManager.AddGameEngine(gameEngine); | |||
GameEngineManager.AddGameEngine(debugOverlayEngine); | |||
MenuEngineManager.AddMenuEngine(menuEngine); | |||
} | |||
internal static void InitDeterministic(StateSyncRegistrationHelper stateSyncReg) | |||
{ | |||
stateSyncReg.AddDeterministicEngine(buildSimEventEngine); | |||
} | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
using System; | |||
using RobocraftX.Common; | |||
using RobocraftX.StateSync; | |||
using Svelto.ECS; | |||
using Unity.Jobs; | |||
using GamecraftModdingAPI.Engines; | |||
using GamecraftModdingAPI.Utility; | |||
namespace GamecraftModdingAPI.App | |||
{ | |||
public class GameBuildSimEventEngine : IApiEngine, IUnorderedInitializeOnTimeRunningModeEntered, IUnorderedInitializeOnTimeStoppedModeEntered | |||
{ | |||
public event EventHandler<GameEventArgs> SimulationMode; | |||
public event EventHandler<GameEventArgs> BuildMode; | |||
public string Name => "GamecraftModdingAPIBuildSimEventGameEngine"; | |||
public bool isRemovable => false; | |||
public EntitiesDB entitiesDB { set; private get; } | |||
public void Dispose() { } | |||
public void Ready() { } | |||
public JobHandle OnInitializeTimeRunningMode() | |||
{ | |||
ExceptionUtil.InvokeEvent(SimulationMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder }); | |||
return default(JobHandle); | |||
} | |||
public JobHandle OnInitializeTimeStoppedMode() | |||
{ | |||
ExceptionUtil.InvokeEvent(BuildMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder }); | |||
return default(JobHandle); | |||
} | |||
} | |||
public struct GameEventArgs | |||
{ | |||
public string GameName; | |||
public string GamePath; | |||
} | |||
} |
@@ -0,0 +1,80 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using HarmonyLib; | |||
using RobocraftX; | |||
using RobocraftX.Common; | |||
using RobocraftX.Schedulers; | |||
using RobocraftX.SimulationModeState; | |||
using Svelto.ECS; | |||
using Svelto.Tasks; | |||
using Svelto.Tasks.Lean; | |||
using GamecraftModdingAPI.Engines; | |||
using GamecraftModdingAPI.Utility; | |||
namespace GamecraftModdingAPI.App | |||
{ | |||
public class GameGameEngine : IApiEngine | |||
{ | |||
public event EventHandler<GameEventArgs> EnterGame; | |||
public event EventHandler<GameEventArgs> ExitGame; | |||
public string Name => "GamecraftModdingAPIGameInfoMenuEngine"; | |||
public bool isRemovable => false; | |||
public EntitiesDB entitiesDB { set; private get; } | |||
public void Dispose() | |||
{ | |||
ExceptionUtil.InvokeEvent(ExitGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder }); | |||
IsInGame = false; | |||
} | |||
public void Ready() | |||
{ | |||
ExceptionUtil.InvokeEvent(EnterGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder }); | |||
IsInGame = true; | |||
} | |||
// game functionality | |||
public bool IsInGame | |||
{ | |||
get; | |||
private set; | |||
} = false; | |||
public void ExitCurrentGame() | |||
{ | |||
ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING); | |||
} | |||
public IEnumerator<TaskContract> ExitCurrentGameAsync() | |||
{ | |||
/* | |||
while (Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING.isStopping) { yield return Yield.It; } | |||
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu").Invoke(FullGameFields.Instance, new object[0]);*/ | |||
yield return Yield.It; | |||
entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true; | |||
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID); | |||
} | |||
public bool IsTimeRunningMode() | |||
{ | |||
return TimeRunningModeUtil.IsTimeRunningMode(entitiesDB); | |||
} | |||
public bool IsTimeStoppedMode() | |||
{ | |||
return TimeRunningModeUtil.IsTimeStoppedMode(entitiesDB); | |||
} | |||
public void ToggleTimeMode() | |||
{ | |||
TimeRunningModeUtil.ToggleTimeRunningState(entitiesDB); | |||
} | |||
} | |||
} |
@@ -0,0 +1,128 @@ | |||
using System; | |||
using HarmonyLib; | |||
using RobocraftX; | |||
using RobocraftX.Common; | |||
using RobocraftX.GUI; | |||
using RobocraftX.GUI.MyGamesScreen; | |||
using Svelto.ECS; | |||
using Svelto.ECS.Experimental; | |||
using GamecraftModdingAPI.Engines; | |||
using GamecraftModdingAPI.Utility; | |||
namespace GamecraftModdingAPI.App | |||
{ | |||
public class GameMenuEngine : IFactoryEngine | |||
{ | |||
public IEntityFactory Factory { set; private get; } | |||
public string Name => "GamecraftModdingAPIGameInfoGameEngine"; | |||
public bool isRemovable => false; | |||
public EntitiesDB entitiesDB { set; private get; } | |||
public void Dispose() | |||
{ | |||
IsInMenu = false; | |||
} | |||
public void Ready() | |||
{ | |||
IsInMenu = true; | |||
} | |||
// game functionality | |||
public bool IsInMenu | |||
{ | |||
get; | |||
private set; | |||
} = false; | |||
public bool CreateMyGame(EGID id, string path = "", uint thumbnailId = 0, string gameName = "", string creatorName = "", string description = "", long createdDate = 0L) | |||
{ | |||
EntityComponentInitializer eci = Factory.BuildEntity<MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal>(id); | |||
eci.Init(new MyGameDataEntityStruct | |||
{ | |||
SavedGamePath = new ECSString(path), | |||
ThumbnailId = thumbnailId, | |||
GameName = new ECSString(gameName), | |||
CreatorName = new ECSString(creatorName), | |||
GameDescription = new ECSString(description), | |||
CreatedDate = createdDate, | |||
}); | |||
// entitiesDB.PublishEntityChange<MyGameDataEntityStruct>(id); // this will always fail | |||
return true; | |||
} | |||
public uint HighestID() | |||
{ | |||
EntityCollection<MyGameDataEntityStruct> games = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames); | |||
uint max = 0; | |||
for (int i = 0; i < games.count; i++) | |||
{ | |||
if (games[i].ID.entityID > max) | |||
{ | |||
max = games[i].ID.entityID; | |||
} | |||
} | |||
return max; | |||
} | |||
public bool EnterGame(EGID id) | |||
{ | |||
if (!ExistsGameInfo(id)) return false; | |||
ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id); | |||
return EnterGame(mgdes.GameName, mgdes.SavedGamePath); | |||
} | |||
public bool EnterGame(string gameName, string path, ulong workshopId = 0uL, bool autoEnterSim = false) | |||
{ | |||
GameMode.CurrentMode = autoEnterSim ? RCXMode.Play : RCXMode.Build; | |||
GameMode.SaveGameDetails = new SaveGameDetails(gameName, path, workshopId); | |||
// the private FullGameCompositionRoot.SwitchToGame() method gets passed to menu items for this reason | |||
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame").Invoke(FullGameFields.Instance, new object[0]); | |||
return true; | |||
} | |||
public bool SetGameName(EGID id, string name) | |||
{ | |||
if (!ExistsGameInfo(id)) return false; | |||
GetGameInfo(id).GameName.Set(name); | |||
GetGameViewInfo(id).MyGamesSlotComponent.GameName = StringUtil.SanitiseString(name); | |||
return true; | |||
} | |||
public bool SetGameDescription(EGID id, string name) | |||
{ | |||
if (!ExistsGameInfo(id)) return false; | |||
GetGameInfo(id).GameDescription.Set(name); | |||
GetGameViewInfo(id).MyGamesSlotComponent.GameDescription = StringUtil.SanitiseString(name); | |||
return true; | |||
} | |||
public bool ExistsGameInfo(EGID id) | |||
{ | |||
return entitiesDB.Exists<MyGameDataEntityStruct>(id); | |||
} | |||
public ref MyGameDataEntityStruct GetGameInfo(EGID id) | |||
{ | |||
return ref GetComponent<MyGameDataEntityStruct>(id); | |||
} | |||
public ref MyGamesSlotEntityViewStruct GetGameViewInfo(EGID id) | |||
{ | |||
return ref GetComponent<MyGamesSlotEntityViewStruct>(new EGID(id.entityID, MyGamesScreenExclusiveGroups.GameSlotGuiEntities)); | |||
} | |||
public ref T GetComponent<T>(EGID id) where T: struct, IEntityComponent | |||
{ | |||
return ref entitiesDB.QueryEntity<T>(id); | |||
} | |||
} | |||
internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { } | |||
} |
@@ -0,0 +1,26 @@ | |||
using System; | |||
using System.Reflection; | |||
using RobocraftX.CR.MainGame; | |||
using RobocraftX.StateSync; | |||
using HarmonyLib; | |||
namespace GamecraftModdingAPI.App | |||
{ | |||
[HarmonyPatch] | |||
class StateSyncRegPatch | |||
{ | |||
public static void Postfix(StateSyncRegistrationHelper stateSyncReg) | |||
{ | |||
// register sim/build events engines | |||
Game.InitDeterministic(stateSyncReg); | |||
} | |||
[HarmonyTargetMethod] | |||
public static MethodBase Target() | |||
{ | |||
return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicCompose").MakeGenericMethod(typeof(object)); | |||
} | |||
} | |||
} |
@@ -72,6 +72,8 @@ namespace GamecraftModdingAPI | |||
Block.Init(); | |||
GameClient.Init(); | |||
AsyncUtils.Init(); | |||
GamecraftModdingAPI.App.Client.Init(); | |||
GamecraftModdingAPI.App.Game.Init(); | |||
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized"); | |||
} | |||
@@ -25,8 +25,15 @@ namespace GamecraftModdingAPI.Utility | |||
/// </summary> | |||
public static class FullGameFields | |||
{ | |||
public static FullGameCompositionRoot Instance | |||
{ | |||
private set; | |||
get; | |||
} = null; | |||
public static MultiplayerInitParameters _multiplayerParams | |||
{ get | |||
{ | |||
get | |||
{ | |||
return (MultiplayerInitParameters)fgcr?.Field("_multiplayerParams").GetValue(); | |||
} | |||
@@ -157,6 +164,7 @@ namespace GamecraftModdingAPI.Utility | |||
public static void Init(FullGameCompositionRoot instance) | |||
{ | |||
fgcr = new Traverse(instance); | |||
FullGameFields.Instance = instance; | |||
} | |||
} | |||
} |