Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
d5ec7f97e2 | |||
96e7a01087 | |||
2bae14c46b | |||
c13789eac2 | |||
9bc04099a6 | |||
2c60413622 |
15 changed files with 1212 additions and 271 deletions
|
@ -21,17 +21,16 @@ namespace GamecraftScripting.Commands
|
|||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
private bool isEnabledIDE = false;
|
||||
public bool isRemovable => false;
|
||||
|
||||
private bool isEnabledIDE = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CommandRegistrationHelper.Unregister("PythonVersion");
|
||||
CommandRegistrationHelper.Unregister("PythonSearchPathes");
|
||||
CommandRegistrationHelper.Unregister("ToggleIDE");
|
||||
if (isEnabledIDE)
|
||||
{
|
||||
toggleIDE();
|
||||
}
|
||||
isEnabledIDE = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
|
|
206
GamecraftScripting/Commands/ExecuteCommandEngine.cs
Normal file
206
GamecraftScripting/Commands/ExecuteCommandEngine.cs
Normal file
|
@ -0,0 +1,206 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
using GamecraftModdingAPI.Commands;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using Svelto.ECS;
|
||||
|
||||
using Microsoft.Scripting.Hosting;
|
||||
|
||||
using GamecraftScripting.Environment;
|
||||
using GamecraftScripting.Serialization;
|
||||
|
||||
namespace GamecraftScripting.Commands
|
||||
{
|
||||
public class ExecuteCommandEngine : ICustomCommandEngine
|
||||
{
|
||||
public const string URI_IMMEDIATE = "immediate://";
|
||||
public const string URI_HTTP = "http://";
|
||||
public const string URI_HTTPS = "https://";
|
||||
public const string URI_INTERNAL = "internal://";
|
||||
public const string URI_BUILTIN = "exmods://";
|
||||
public const string URI_FILE = "file://";
|
||||
public const string URI_ANSWER = "42://";
|
||||
|
||||
public string Description { get; } = "Execute some Python, intelligently guessing how to execute it";
|
||||
|
||||
public string Name { get; } = "ExecutePython";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CommandRegistrationHelper.Unregister(Name);
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
// TODO command registration
|
||||
CommandRegistrationHelper.Register<string>(Name, ExecutePythonCommand, Description);
|
||||
// create insecure Python namespace
|
||||
ScriptEngine pyEngine = PythonEnvironment.CreateEngine();
|
||||
ScriptScope pyScope = PythonEnvironment.CreateScope(pyEngine);
|
||||
PythonEnvironment.CurrentEngine = pyEngine;
|
||||
PythonEnvironment.CurrentScope = pyScope;
|
||||
}
|
||||
|
||||
private void ExecutePythonCommand(string name)
|
||||
{
|
||||
string uriName = urifyName(name.Trim());
|
||||
if (uriName == null)
|
||||
{
|
||||
Logging.CommandLogError("Unable to guess script type. Please add a scheme to the start of the command argument to avoid this. \nValid schemes include: \n" + URI_IMMEDIATE.Replace('/', '\\') + " (for Python code) \n" + URI_FILE.Replace('/', '\\') + " (for Python file) ");
|
||||
return;
|
||||
}
|
||||
string scriptContents = getScript(uriName);
|
||||
if (scriptContents == null)
|
||||
{
|
||||
Logging.CommandLogError("Invalid script or URI scheme. Please replace the scheme at the start of the command argument. Valid schemes include: \n" + URI_IMMEDIATE.Replace('/', '\\') + "\\\\ (for Python code) \n" + URI_FILE.Replace('/', '\\') + "\\\\ (for Python file) ");
|
||||
return;
|
||||
}
|
||||
Execute(uriName, scriptContents);
|
||||
}
|
||||
|
||||
private string urifyName(string name)
|
||||
{
|
||||
if (name.Contains("://")) return name; // already is a uri, hopefully a valid uri
|
||||
if (name.Contains(":\\\\")) return name.Replace(":\\\\", "://"); // :\\ is easier to input in CLI
|
||||
if (!name.EndsWith(".py", StringComparison.InvariantCultureIgnoreCase))
|
||||
return URI_IMMEDIATE + name; // probably not a script file, hopefully it's some code
|
||||
name = name.TrimEnd('\\', '/'); // I still hate that Windows filenames use the escape character as a separator
|
||||
// search scripts included in game save
|
||||
EntityCollection<ScriptStruct> integratedScripts = entitiesDB.QueryEntities<ScriptStruct>(ScriptBuilder.ScriptGroup);
|
||||
for (int i = 0; i < integratedScripts.count; i++)
|
||||
{
|
||||
if (integratedScripts[i].name == name)
|
||||
{
|
||||
// matching script found in game save
|
||||
return URI_INTERNAL + name;
|
||||
}
|
||||
}
|
||||
// search scripts on disk
|
||||
try
|
||||
{
|
||||
FileInfo file = new FileInfo(name);
|
||||
if (file.Exists)
|
||||
{
|
||||
return URI_FILE + name;
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (
|
||||
e is System.Security.SecurityException
|
||||
|| e is ArgumentException
|
||||
|| e is UnauthorizedAccessException
|
||||
|| e is PathTooLongException
|
||||
|| e is NotSupportedException)
|
||||
{ }
|
||||
return null;
|
||||
}
|
||||
|
||||
private string getScheme(string uriName)
|
||||
{
|
||||
string[] splitName = uriName.Split(new string[] { @"://" }, StringSplitOptions.None);
|
||||
if (splitName.Length == 2)
|
||||
{
|
||||
return splitName[0] + "://";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private string getPath(string uriName)
|
||||
{
|
||||
string[] splitName = uriName.Split(new string[] { @"://" }, StringSplitOptions.None);
|
||||
if (splitName.Length == 2)
|
||||
{
|
||||
return splitName[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private string getScript(string uriName)
|
||||
{
|
||||
string uri_scheme = getScheme(uriName);
|
||||
if (uri_scheme == null) return null;
|
||||
string uri_path = getPath(uriName);
|
||||
if (uri_path == null) return null;
|
||||
switch (uri_scheme)
|
||||
{
|
||||
case URI_IMMEDIATE:
|
||||
return uri_path;
|
||||
case URI_HTTP: // download from web
|
||||
case URI_HTTPS:
|
||||
#if DEBUG
|
||||
Logging.CommandLogWarning("Executing code from the Internet is dangerous! \nWeb scripts are only available in DEVELOPMENT versions of GamecraftScripting.");
|
||||
try
|
||||
{
|
||||
WebRequest req = WebRequest.Create(uriName);
|
||||
req.Method = "GET";
|
||||
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
|
||||
byte[] body = new byte[int.Parse(resp.GetResponseHeader("Content-Length"))];
|
||||
resp.GetResponseStream().Read(body, 0, body.Length);
|
||||
return Encoding.ASCII.GetString(body);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
Logging.CommandLogError("Executing code from the Internet is dangerous! \nWeb scripts are only available in DEVELOPMENT versions of GamecraftScripting.");
|
||||
return null;
|
||||
#endif
|
||||
case URI_INTERNAL: // found in game file (already deserialized to memory)
|
||||
EntityCollection<ScriptStruct> internalScripts = entitiesDB.QueryEntities<ScriptStruct>(ScriptBuilder.ScriptGroup);
|
||||
for (int i = 0; i < internalScripts.count; i++)
|
||||
{
|
||||
if (internalScripts[i].name == uri_path)
|
||||
{
|
||||
return internalScripts[i].script;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
case URI_BUILTIN:
|
||||
// TODO: for future implementation
|
||||
return null;
|
||||
case URI_FILE:
|
||||
try
|
||||
{
|
||||
return File.ReadAllText(uri_path);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case URI_ANSWER:
|
||||
return "print 42";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void Execute(string uriName, string script, bool logError = true)
|
||||
{
|
||||
ScriptSource src = PythonEnvironment.CurrentEngine.CreateScriptSourceFromString(script);
|
||||
try
|
||||
{
|
||||
src.Execute(PythonEnvironment.CurrentScope);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (logError)
|
||||
{
|
||||
Logging.CommandLogError($"Python error in '{uriName}': \n{e.Message}");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ using Svelto.ECS;
|
|||
using Microsoft.Scripting.Hosting;
|
||||
|
||||
using GamecraftScripting.Environment;
|
||||
using GamecraftScripting.Serialization;
|
||||
|
||||
namespace GamecraftScripting.Commands
|
||||
{
|
||||
|
@ -19,17 +20,23 @@ namespace GamecraftScripting.Commands
|
|||
public string Name { get; } = "PythonRunner";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public void Dispose()
|
||||
public void Dispose()
|
||||
{
|
||||
CommandRegistrationHelper.Unregister("RunPythonScript");
|
||||
CommandRegistrationHelper.Unregister("RunPython");
|
||||
CommandRegistrationHelper.Unregister("RunGamePython");
|
||||
CommandRegistrationHelper.Unregister("ListGameScripts");
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
CommandRegistrationHelper.Register<string>("RunPythonScript", RunPythonScript, "Run a python script stored on your computer");
|
||||
CommandRegistrationHelper.Register<string>("RunPython", RunPythonString, "Run a python argument");
|
||||
CommandRegistrationHelper.Register<string>("RunGamePython", RunIntegratedScript, "Run a python script stored in the game file");
|
||||
CommandRegistrationHelper.Register("ListGameScripts", ListIntegratedScripts, "List python scripts stored in the game file");
|
||||
// create insecure Python namespace
|
||||
ScriptEngine pyEngine = PythonEnvironment.CreateEngine();
|
||||
ScriptScope pyScope = PythonEnvironment.CreateScope(pyEngine);
|
||||
|
@ -38,6 +45,37 @@ namespace GamecraftScripting.Commands
|
|||
//Logging.MetaLog(string.Join(", ", pyEngine.GetSearchPaths().ToArray()));
|
||||
}
|
||||
|
||||
private void ListIntegratedScripts()
|
||||
{
|
||||
string result = "";
|
||||
EntityCollection<ScriptStruct> scripts = entitiesDB.QueryEntities<ScriptStruct>(ScriptBuilder.ScriptGroup);
|
||||
for (uint i = 0u; i < scripts.count; i++)
|
||||
{
|
||||
result += scripts[i].name + " \n";
|
||||
}
|
||||
Logging.CommandLog($"Found {scripts.count} integrated script(s) \n{result}");
|
||||
}
|
||||
|
||||
private void RunIntegratedScript(string scriptName)
|
||||
{
|
||||
if (scriptName == null) return;
|
||||
if (scriptName.StartsWith("exmods://", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// TODO: Lookup for built-in scripts
|
||||
return;
|
||||
}
|
||||
EntityCollection<ScriptStruct> scripts = entitiesDB.QueryEntities<ScriptStruct>(ScriptBuilder.ScriptGroup);
|
||||
for (uint i = 0u; i < scripts.count; i++)
|
||||
{
|
||||
if (scripts[i].name == scriptName)
|
||||
{
|
||||
ExecuteScript(scripts[i].script);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Logging.CommandLogError("Script not found");
|
||||
}
|
||||
|
||||
private void RunPythonScript(string scriptPath)
|
||||
{
|
||||
string script = File.ReadAllText(scriptPath);
|
||||
|
|
38
GamecraftScripting/Commands/SerializationCommandEngine.cs
Normal file
38
GamecraftScripting/Commands/SerializationCommandEngine.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Svelto.ECS;
|
||||
using GamecraftModdingAPI.Commands;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftScripting.Commands
|
||||
{
|
||||
public class SerializationCommandEngine : ICustomCommandEngine
|
||||
{
|
||||
public string Description => "Save a script into a Gamecraft game file";
|
||||
|
||||
public string Name => "IntegrateScript";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
internal static IEntityFactory entityFactory = null;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CommandRegistrationHelper.Unregister(Name);
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
CommandRegistrationHelper.Register<string>(Name, persistScript, Description);
|
||||
}
|
||||
|
||||
private void persistScript(string filepath)
|
||||
{
|
||||
if (entityFactory == null) return;
|
||||
EGID scriptId = Serialization.ScriptBuilder.BuildScriptEntity(filepath, File.ReadAllText(filepath), entityFactory);
|
||||
Logging.MetaLog($"Created persistent script with id {scriptId.entityID}");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -9,6 +9,7 @@ using IllusionPlugin;
|
|||
using UnityEngine;
|
||||
using GamecraftModdingAPI;
|
||||
using GamecraftModdingAPI.Commands;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace GamecraftScripting
|
||||
{
|
||||
|
@ -17,26 +18,35 @@ namespace GamecraftScripting
|
|||
public string[] Filter { get; } = new string[] { "Gamecraft" };
|
||||
|
||||
public string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
|
||||
|
||||
|
||||
public string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
|
||||
|
||||
private Harmony harmony = null;
|
||||
|
||||
public void OnApplicationQuit()
|
||||
{
|
||||
var currentAssembly = Assembly.GetExecutingAssembly();
|
||||
harmony.UnpatchAll(currentAssembly.GetName().Name);
|
||||
harmony = null;
|
||||
Main.Shutdown();
|
||||
}
|
||||
|
||||
public void OnApplicationStart()
|
||||
{
|
||||
Main.Init();
|
||||
var currentAssembly = Assembly.GetExecutingAssembly();
|
||||
harmony = new Harmony(currentAssembly.GetName().Name);
|
||||
harmony.PatchAll(currentAssembly);
|
||||
// register development commands
|
||||
#if DEBUG
|
||||
//CommandManager.AddCommand(new SimpleCustomCommandEngine(() => { Application.Quit(); }, "Exit", "Exit the game immediately"));
|
||||
|
||||
#endif
|
||||
// debugging commands
|
||||
CommandManager.AddCommand(new Commands.DebugCommandEngine());
|
||||
// functional commands
|
||||
CommandManager.AddCommand(new Commands.PythonRunnerCommandEngine());
|
||||
CommandManager.AddCommand(new Commands.ExecuteCommandEngine());
|
||||
CommandManager.AddCommand(new Commands.SerializationCommandEngine());
|
||||
}
|
||||
|
||||
public void OnFixedUpdate() { }
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Serialization;
|
||||
|
||||
namespace GamecraftScripting.Serialization
|
||||
{
|
||||
[HarmonyPatch]
|
||||
class DeserializeFromDiskEntitiesEnginePatch
|
||||
{
|
||||
internal static IEntityFactory entityFactory = null;
|
||||
|
||||
private static readonly byte[] frameStart = (new UTF8Encoding()).GetBytes("GamecraftScripting");
|
||||
|
||||
public static void Prefix(ref ISerializationData ____serializationData, ref FasterList<byte> ____bytesStream, ref IEntitySerialization ____entitySerializer)
|
||||
{
|
||||
____entitySerializer.RegisterSerializationFactory<ScriptEntityDescriptor>(new ScriptDeserializationFactory(entityFactory));
|
||||
uint scriptDataStart = ____serializationData.dataPos;
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"dataPos: {scriptDataStart}");
|
||||
BinaryBufferReader bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out uint count), ____serializationData.dataPos);
|
||||
byte[] frameBuffer = new byte[frameStart.Length];
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"serial data count: {____serializationData.data.count} capacity: {____serializationData.data.capacity}");
|
||||
int i = 0;
|
||||
// match frame start
|
||||
while (frameBuffer != frameStart && bbr.Position < count-frameStart.Length)
|
||||
{
|
||||
i = 0;
|
||||
frameBuffer[0] = bbr.ReadByte();
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"Buffer byte 0: {(new UTF8Encoding()).GetString(new byte[]{frameBuffer[0]})}");
|
||||
while (frameBuffer[i] == frameStart[i])
|
||||
{
|
||||
i++;
|
||||
if (i == frameStart.Length) break;
|
||||
frameBuffer[i] = bbr.ReadByte();
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"Buffer byte {i}: {(new UTF8Encoding()).GetString(new byte[] { frameBuffer[i] })}");
|
||||
}
|
||||
if (i == frameStart.Length) break;
|
||||
}
|
||||
// abort if at end of file
|
||||
if (bbr.Position >= count - frameStart.Length)
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog("Skipping script deserialization (no frame found)");
|
||||
return;
|
||||
}
|
||||
____serializationData.dataPos = bbr.Position;
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"dataPos (after frame): {____serializationData.dataPos}");
|
||||
uint scriptCount = bbr.ReadUint();
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"scriptCount: {scriptCount}");
|
||||
uint potentialStart = bbr.ReadUint();
|
||||
if (potentialStart == ____serializationData.dataPos - frameStart.Length) return;
|
||||
____serializationData.dataPos += 4u;
|
||||
for (uint j = 0; j < scriptCount; j++)
|
||||
{
|
||||
EGID newScriptId = new EGID(j, ScriptBuilder.ScriptGroup);
|
||||
// why doesn't \/this\/ do anything?
|
||||
____entitySerializer.DeserializeNewEntity(newScriptId, ____serializationData, (int)SerializationType.Storage);
|
||||
}
|
||||
bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out count), ____serializationData.dataPos);
|
||||
uint actualStart = bbr.ReadUint();
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"actualStart: {actualStart}");
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"Deserialized {scriptCount} scripts starting at {scriptDataStart} ({actualStart})");
|
||||
____serializationData.dataPos = scriptDataStart; // change back to original end point (just in case)
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.Method("RobocraftX.SaveAndLoad.DeserializeFromDiskEntitiesEngine:LoadingFinished");//AccessTools.TypeByName("RobocraftX.SaveAndLoad.DeserializeFromDiskEntities")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.SaveAndLoad;
|
||||
using Svelto.ECS;
|
||||
namespace GamecraftScripting.Serialization
|
||||
{
|
||||
[HarmonyPatch(typeof(SaveAndLoadCompositionRoot), "Compose")]
|
||||
class SaveAndLoadCompositionRootPatch
|
||||
{
|
||||
public static void Prefix(EnginesRoot enginesRoot)
|
||||
{
|
||||
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
||||
DeserializeFromDiskEntitiesEnginePatch.entityFactory = factory;
|
||||
Commands.SerializationCommandEngine.entityFactory = factory;
|
||||
}
|
||||
}
|
||||
}
|
57
GamecraftScripting/Serialization/SaveGameEnginePatch.cs
Normal file
57
GamecraftScripting/Serialization/SaveGameEnginePatch.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.SaveAndLoad;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Serialization;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftScripting.Serialization
|
||||
{
|
||||
[HarmonyPatch]
|
||||
class SaveGameEnginePatch
|
||||
{
|
||||
private static readonly byte[] frameStart = (new UTF8Encoding()).GetBytes("GamecraftScripting");
|
||||
|
||||
public static void Postfix(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer)
|
||||
{
|
||||
Logging.MetaLog("Running Postfix on game save serializer");
|
||||
serializationData.data.ExpandBy((uint)frameStart.Length);
|
||||
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint buffLen), serializationData.dataPos);
|
||||
EntityCollection<ScriptStruct> scripts = entitiesDB.QueryEntities<ScriptStruct>(ScriptBuilder.ScriptGroup);
|
||||
uint scriptDataStart = serializationData.dataPos;
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"dataPos: {scriptDataStart}");
|
||||
for (int i = 0; i < frameStart.Length; i++)
|
||||
{
|
||||
bbw.Write(frameStart[i]);
|
||||
}
|
||||
serializationData.dataPos += (uint)frameStart.Length;
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"dataPos (after frame start): {serializationData.dataPos}");
|
||||
serializationData.data.ExpandBy(4u);
|
||||
Logging.MetaLog($"scriptCount: {scripts.count}");
|
||||
bbw.Write(scripts.count);
|
||||
serializationData.dataPos += 4u;
|
||||
//foreach (byte b in BitConverter.GetBytes(count)) serializationData.data.Add(b);
|
||||
for (uint i = 0; i < scripts.count; i++)
|
||||
{
|
||||
EGID scriptId = new EGID(i, ScriptBuilder.ScriptGroup);
|
||||
entitySerializer.SerializeEntity(scriptId, serializationData, (int)SerializationType.Storage);
|
||||
}
|
||||
bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out buffLen), serializationData.dataPos);
|
||||
serializationData.data.ExpandBy(4u);
|
||||
bbw.Write(scriptDataStart);
|
||||
serializationData.dataPos += 4u;
|
||||
//foreach (byte b in BitConverter.GetBytes(scriptDataStart)) serializationData.data.Add(b);
|
||||
serializationData.data.Trim();
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return typeof(SaveGameEngine).GetMethod("SerializeGameToBuffer");
|
||||
}
|
||||
}
|
||||
}
|
24
GamecraftScripting/Serialization/ScriptBuilder.cs
Normal file
24
GamecraftScripting/Serialization/ScriptBuilder.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Experimental;
|
||||
|
||||
namespace GamecraftScripting.Serialization
|
||||
{
|
||||
public static class ScriptBuilder
|
||||
{
|
||||
public static readonly ExclusiveGroup ScriptGroup = new ExclusiveGroup("SCRIPT_GROUP");
|
||||
|
||||
private static uint nextScriptId = 0u;
|
||||
|
||||
public static EGID BuildScriptEntity(string name, string script, IEntityFactory entityFactory)
|
||||
{
|
||||
EGID scriptId = new EGID(nextScriptId++, ScriptGroup);
|
||||
EntityComponentInitializer builder = entityFactory.BuildEntity<ScriptEntityDescriptor>(scriptId);
|
||||
builder.Init(new ScriptStruct {
|
||||
name = new ECSString(name),
|
||||
script = new ECSString(script),
|
||||
});
|
||||
return scriptId;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Serialization;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
namespace GamecraftScripting.Serialization
|
||||
{
|
||||
public class ScriptDeserializationFactory : IDeserializationFactory
|
||||
{
|
||||
private IEntityFactory entityFactory;
|
||||
|
||||
public ScriptDeserializationFactory(IEntityFactory entityFactory)
|
||||
{
|
||||
this.entityFactory = entityFactory;
|
||||
}
|
||||
|
||||
public EntityComponentInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor, int serializationType, IEntitySerialization entitySerialization)
|
||||
{
|
||||
Logging.MetaLog(entityFactory == null);
|
||||
EntityComponentInitializer esi = entityFactory.BuildEntity<ScriptEntityDescriptor>(egid);
|
||||
entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref esi, serializationType);
|
||||
Logging.MetaLog($"Deserialized script named {esi.Get<ScriptStruct>().name.ToString()}");
|
||||
return esi;
|
||||
}
|
||||
}
|
||||
}
|
29
GamecraftScripting/Serialization/ScriptEntityDescriptor.cs
Normal file
29
GamecraftScripting/Serialization/ScriptEntityDescriptor.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Serialization;
|
||||
|
||||
namespace GamecraftScripting.Serialization
|
||||
{
|
||||
public class ScriptEntityDescriptor : SerializableEntityDescriptor<ScriptEntityDescriptor._ScriptEntityDescriptor>
|
||||
{
|
||||
[HashName("GamecraftScriptingScriptEntityDescriptorV0")]
|
||||
public class _ScriptEntityDescriptor : IEntityDescriptor
|
||||
{
|
||||
public IComponentBuilder[] componentsToBuild => _entityBuilders;
|
||||
|
||||
private static readonly IComponentBuilder[] _entityBuilders = new IComponentBuilder[1]
|
||||
{
|
||||
new SerializableComponentBuilder<SerializationType, ScriptStruct>(((int)SerializationType.Network, new ScriptSerializer()),
|
||||
((int)SerializationType.Storage, new ScriptSerializer()))
|
||||
};
|
||||
}
|
||||
|
||||
public ScriptEntityDescriptor() : base()
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog("ScriptEntityDescriptor Initialized");
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"Entities to serialize: {entitiesToSerialize.Length}");
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"Entities to serialize: {componentsToBuild.Length}");
|
||||
}
|
||||
}
|
||||
}
|
61
GamecraftScripting/Serialization/ScriptSerializer.cs
Normal file
61
GamecraftScripting/Serialization/ScriptSerializer.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Serialization;
|
||||
|
||||
namespace GamecraftScripting.Serialization
|
||||
{
|
||||
public class ScriptSerializer : IComponentSerializer<ScriptStruct>
|
||||
{
|
||||
private static readonly UTF8Encoding utf8Encoding = new UTF8Encoding();
|
||||
|
||||
private static readonly uint padding = 42u;
|
||||
|
||||
public uint size => 0u;
|
||||
|
||||
public bool Deserialize(ref ScriptStruct value, ISerializationData serializationData)
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"dataPos: {serializationData.dataPos}");
|
||||
BinaryBufferReader bbr = new BinaryBufferReader(serializationData.data.ToArrayFast(out uint count), serializationData.dataPos);
|
||||
short nameLength = bbr.ReadShort();
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"nameLength: {nameLength}");
|
||||
byte[] serialName = new byte[nameLength];
|
||||
bbr.ReadBytes(serialName, (uint)nameLength);
|
||||
value.name.Set(utf8Encoding.GetString(serialName));
|
||||
int scriptLength = bbr.ReadInt();
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"scriptLength: {scriptLength}");
|
||||
byte[] serialScript = new byte[scriptLength];
|
||||
bbr.ReadBytes(serialScript, (uint)scriptLength);
|
||||
value.script.Set(utf8Encoding.GetString(serialScript));
|
||||
uint serialPadding = bbr.ReadUint();
|
||||
serializationData.dataPos += (uint)(2 + nameLength + 4 + scriptLength + 4);
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"Deserializing Script successful: {serialPadding == padding}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Serialize(in ScriptStruct value, ISerializationData serializationData)
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.MetaLog($"dataPos: {serializationData.dataPos}");
|
||||
byte[] serialName = utf8Encoding.GetBytes(value.name);
|
||||
byte[] serialScript = utf8Encoding.GetBytes(value.script);
|
||||
uint actualSize = (uint)(2 + serialName.Length + 4 + serialScript.Length + 4);
|
||||
serializationData.data.ExpandBy(actualSize);
|
||||
BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint count), serializationData.dataPos);
|
||||
bbw.Write((short)serialName.Length);
|
||||
for (int i = 0; i < serialName.Length; i++)
|
||||
{
|
||||
bbw.Write(serialName[i]);
|
||||
}
|
||||
bbw.Write((int)serialScript.Length);
|
||||
for (int i = 0; i < serialScript.Length; i++)
|
||||
{
|
||||
bbw.Write(serialScript[i]);
|
||||
}
|
||||
bbw.Write(padding);
|
||||
serializationData.dataPos += actualSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
13
GamecraftScripting/Serialization/ScriptStruct.cs
Normal file
13
GamecraftScripting/Serialization/ScriptStruct.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Experimental;
|
||||
|
||||
namespace GamecraftScripting.Serialization
|
||||
{
|
||||
public struct ScriptStruct : IEntityComponent
|
||||
{
|
||||
public ECSString name;
|
||||
|
||||
public ECSString script;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Serialization;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftScripting.Serialization
|
||||
{
|
||||
#if DEBUG
|
||||
[HarmonyPatch]
|
||||
public class SerializationDescriptorMapPatch
|
||||
{
|
||||
public static void Prefix(ISerializableEntityDescriptor descriptor)
|
||||
{
|
||||
if (descriptor.entitiesToSerialize.Length == 0)
|
||||
{
|
||||
if (descriptor.componentsToBuild.Length != 0)
|
||||
{
|
||||
Logging.MetaLog($"Descriptor: {descriptor.componentsToBuild[0].GetType().FullName} (hash:{descriptor.hash})");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.MetaLog($"Emtpy descriptor (hash: {descriptor.hash})");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.MetaLog($"Descriptor: {descriptor.entitiesToSerialize[0].GetType().FullName} (hash:{descriptor.hash}) (serializables: {descriptor.entitiesToSerialize.Length})");
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
//Logging.MetaLog(AccessTools.Method(AccessTools.Inner(AccessTools.TypeByName("Svelto.ECS.EnginesRoot"), "SerializationDescriptorMap"), "RegisterEntityDescriptor") == null);
|
||||
return AccessTools.Method(AccessTools.Inner(AccessTools.TypeByName("Svelto.ECS.EnginesRoot"), "SerializationDescriptorMap"), "RegisterEntityDescriptor");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
public class SerializationDescriptorMapGetPatch
|
||||
{
|
||||
public static void Prefix(uint descriptorID)
|
||||
{
|
||||
if (descriptorID == 3520129338u)
|
||||
{
|
||||
Logging.MetaLog("Got hash for ScriptStruct");
|
||||
}
|
||||
Logging.MetaLog($"GetDescriptorFromHash({descriptorID})");
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
//Logging.MetaLog(AccessTools.Method(AccessTools.Inner(AccessTools.TypeByName("Svelto.ECS.EnginesRoot"), "SerializationDescriptorMap"), "RegisterEntityDescriptor") == null);
|
||||
return AccessTools.Method(AccessTools.Inner(AccessTools.TypeByName("Svelto.ECS.EnginesRoot"), "SerializationDescriptorMap"), "GetDescriptorFromHash");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
Loading…
Add table
Reference in a new issue