/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /*~~~~~~~~~~~~~~~~~~(Separate library's .h file, compiled with /clr)~~~~~~~~~~~~~~~~~~~~~~*/ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ #pragma once #pragma warning(disable: 4002) #pragma warning(disable: 4003) #pragma warning(disable: 4005) #pragma warning(disable: 4302) #pragma warning(disable: 4311) #pragma warning(disable: 4312) #pragma warning(disable: 4996) //warning C4996: 'mbstowcs': This function or variable may be unsafe. Consider using mbstowcs_s instead. #define var auto #define null nullptr typedef unsigned char byte; typedef const wchar_t* ustring; #ifdef CPPCLI //#include //#include //#include typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32; typedef unsigned long long uint64; typedef char int8; typedef short int16; typedef int int32; typedef long long int64; typedef char ANSICHAR; typedef wchar_t WIDECHAR; typedef wchar_t TCHAR; typedef unsigned char UTF8CHAR; typedef unsigned short UCS2CHAR; typedef unsigned short UTF16CHAR; typedef unsigned int UTF32CHAR; #define GETMGDSTR(str) ANSI_TO_TCHAR( static_cast(Marshal::StringToHGlobalAnsi(str).ToPointer()) ) static wchar_t* ANSI_TO_TCHAR(const char* text) { const size_t size = strlen(text) + 1; wchar_t* wText = new wchar_t[size]; mbstowcs(wText, text, size); System::Runtime::InteropServices::Marshal::FreeHGlobal(System::IntPtr((char*)text)); return wText; } typedef unsigned int UPTRINT; typedef unsigned int PTRINT; #include "enums.h" #endif #define IMPL_MACROS 1 #include "includes.h" #define IMPL_MACROS 0 typedef void (*fnptr_gPrintMessage)(ustring msg, int timeVisible_s); typedef void (*fnptr_gPrintClassName)(class _AActor* actor, int timeVisible_s); #define MACRO_FUNCTION_TYPEDEF_PARAMS(xtype, xname, xparams) typedef xtype (* fn_##xclass##_##xname )( class xclass##* actor, xparams ); #define MACRO_FUNCTION_TYPEDEF_PURE(xtype, xname) typedef xtype (* fn_##xclass##_##xname )( class xclass##* actor ); AACTOR_FUNCTION_TYPEDEF_PURE(float*, GetActorLocation); AACTOR_FUNCTION_TYPEDEF_PARAMS(void, SetActorLocation, float*, newLocation); class Vtable_methods { public: #define MACRO_FIELD_FNPTR_IMPL(xtype, xname) get_##xclass##_##xname m_get_##xclass##_##xname; set_##xclass##_##xname m_set_##xclass##_##xname; AACTOR_FUNCTION_FNPTR_IMPL(float*, GetActorLocation); AACTOR_FUNCTION_FNPTR_IMPL(void, SetActorLocation); }; class _AActor { public: void* nativeActorObj = 0; void* nextActorInLinkedList = 0; Vtable_methods* methods; public: fnptr_gPrintMessage gPrintMessage; fnptr_gPrintClassName gPrintClassName; }; #ifdef CPPCLI namespace CppCli { using namespace System; using namespace System::Collections::Generic; using namespace System::IO; using namespace System::Runtime::InteropServices; ref class AActor { private: _AActor* native; void _printMsg(std::wstring msg, int time_s) { native->gPrintMessage(msg.c_str(), time_s); } public: AActor(_AActor* native) { this->native = native; } public: void PrintMessage(String^ msg) { var p = GETMGDSTR(msg); std::wstring str = p; _printMsg(str, 9); delete p; } void PrintClassName() { native->gPrintClassName(native, 9); } public: #define MACRO_FUNCTION_WRAPPER_GETVEC3_PURE(xtype, xname) array^ xname() { var ptr = (native->methods->##m_fn_##xclass##_##xname##(native)); var v = gcnew array(3){ptr[0], ptr[1], ptr[2]}; delete[] ptr; return v; } #define MACRO_FUNCTION_WRAPPER_SET_1PARAM_P1ISVEC(xtype, xname, x11, x12) void xname(array^ floats) { var ptr = new float[3]; ptr[0]=*floats[0]; ptr[1]=*floats[1]; ptr[2]=*floats[2]; (native->methods->##m_fn_##xclass##_##xname##(native, ptr)); } AACTOR_FUNCTION_WRAPPER_GETVEC3_PURE(float*, GetActorLocation); AACTOR_FUNCTION_WRAPPER_SET_1PARAM_P1ISVEC(void, SetActorLocation, float*, newLocation); }; } #endif #define string const TCHAR* #define var auto #define null nullptr namespace CppCli { #ifdef CPPCLI using namespace System; using namespace System::Collections::Generic; using namespace System::IO; using namespace System::Runtime::InteropServices; using namespace System::CodeDom; using namespace System::Globalization; using namespace System::CodeDom::Compiler; using namespace System::Reflection; using namespace System::Linq; using namespace Microsoft::CSharp; #include #define gcroot msclr::gcroot static gcroot assembly; static gcroot Game1; static gcroot Init_Method; static gcroot Update_Method; static gcroot gameInst; ref class Mygame { List^ actors; public: static Assembly^ CompileSourceCodeDom(List^ lines2) { CodeDomProvider^ cpd = gcnew CSharpCodeProvider(); var cp = gcnew CompilerParameters(); cp->ReferencedAssemblies->Add("System.dll"); cp->ReferencedAssemblies->Add("System.Core.dll"); cp->GenerateExecutable = false; cp->GenerateInMemory = true; var di = gcnew DirectoryInfo("D:\\dev\\MyGameCs"); var _files = di->GetFiles("*.cs", SearchOption::AllDirectories); var files = gcnew List(); for each (var f in _files) { if (!f->FullName->Contains("Program.cs")) files->Add(f->FullName); } CompilerResults^ cr = cpd->CompileAssemblyFromFile(cp, files->ToArray()); if (cr->Errors->Count > 0) for each (var error in cr->Errors) { lines2->Add(error->ToString()); } return cr->CompiledAssembly; } static void _Init(Assembly^ assembly, List^ lines2, List^ sceneActors) { Game1 = assembly->GetType("game1.Game1"); Init_Method = Game1->GetMethod("Init"); Update_Method = Game1->GetMethod("Update"); lines2->Add("got Init method.."); gameInst = assembly->CreateInstance("game1.Game1"); lines2->Add("got inst.."); Init_Method->Invoke(gameInst, BindingFlags::InvokeMethod, null, gcnew array(1) { sceneActors }, CultureInfo::CurrentCulture); lines2->Add("Init() call happened"); } int Init(_AActor* root) { actors = gcnew List(); var _root = gcnew AActor(root); actors->Add(_root); var node = root; while (node->nextActorInLinkedList != null) { node = (_AActor*)node->nextActorInLinkedList; actors->Add(gcnew AActor(node)); } List^ lines2 = gcnew List(); lines2->Add("Run().."); try { assembly = CompileSourceCodeDom(lines2); _Init(assembly, lines2, actors); } catch(Exception^ e) { if(e->InnerException != null) lines2->Add(String::Concat(e->InnerException->StackTrace, e->InnerException->Message)); } File::WriteAllLines("-----------------\\out"+".txt", lines2->ToArray()); return 0; } int Update() { List^ lines2 = gcnew List(); lines2->Add("Update().."); try { Update_Method->Invoke(gameInst, BindingFlags::InvokeMethod, null, null, CultureInfo::CurrentCulture); } catch (Exception^ e) { if (e->InnerException != null) lines2->Add(String::Concat(e->InnerException->StackTrace, e->InnerException->Message)); } File::WriteAllLines("--------------\\out4" + ".txt", lines2->ToArray()); return 0; } }; static gcroot mygame; extern "C" int __stdcall Init(_AActor * root) { mygame = gcnew Mygame; return mygame->Init(root); } extern "C" int __stdcall Update() { return mygame->Update(); } #else extern "C" int __stdcall Init(_AActor* root); extern "C" int __stdcall Update(); #endif } #ifndef CPPCLI void _gPrintMessage(const TCHAR* msg, int timeVisible_s) { engine->AddOnScreenDebugMessage(-1, timeVisible_s, FColorList::Green, msg); } void _gPrintClassName(_AActor* actor, int timeVisible_s) { engine->AddOnScreenDebugMessage(-1, timeVisible_s, FColorList::Green, *GetNameSafe((UObjectBaseUtility*)actor->nativeActorObj)); } void* _ustringlist_get_array_fname(ustringlist* v) { TArray* arr = new TArray(); var node = v; while (node->next) { arr->Add(node->current); node = node->next; } return (void*)arr; } ustringlist* _ustringlist_converted(TArray& sourceList) { var node = new ustringlist(_ustringlist_get_array_fname); var rootNode = node; for (auto item : sourceList) { node->current = *(item.GetPlainNameString()); node->next = new ustringlist(_ustringlist_get_array_fname); node = node->next; } return rootNode; } #define MACRO_FUNCTION_GETVEC3_PURE(xtype, xname) float* _fn_##xclass##_##xname##(##xclass##* actor) { float* ptr = new float[3]; if(!((##uclass##*)(actor->nativeActorObj))->GetRootComponent()) { ptr[0]=-1.f; ptr[1]=-1.f; ptr[2]=-1.f; return ptr; } var v = ((##uclass##*)(actor->nativeActorObj))->##xname##(); ptr[0] = v.X; ptr[1] = v.Y; ptr[2] = v.Z; return ptr; } #define MACRO_FUNCTION_SET_1PARAM_P1ISVEC(xtype, xname, x11, x12) void _fn_##xclass##_##xname##(##xclass##* actor, x11 x12) { /*cli -> c++ land*/ float* ptr = x12; FVector vec(ptr[0], ptr[1], ptr[2]); delete[] ptr; ((##uclass##*)(actor->nativeActorObj))->##xname##(vec); } AACTOR_FUNCTION_GETVEC3_PURE(float*, GetActorLocation); AACTOR_FUNCTION_SET_1PARAM_P1ISVEC(void, SetActorLocation, float*, newLocation); #endif //#undef string //#undef var #define null 0 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(Main game class file)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ #include "AGame.h" //#include #include "Engine/Engine.h" #include "UObject/ConstructorHelpers.h" #include "Engine/Blueprint.h" #include "D:\Epic Games\UE_4.23\Engine\Source\Runtime\Engine\Public\EngineUtils.h" #include #include #include #include #include UEngine* engine; #include "../CppCli/CppCli.h" _AActor* root = null; Vtable_methods* vtable_methods = null; std::list<_AActor*> actorRefList; void AAGame::InitCSharp() { if (vtable_methods == null) { vtable_methods = new Vtable_methods(); #define VTABLE_MACRO_FUNCTION(xname) vtable_methods->m_fn_##xclass##_##xname = _fn_##xclass##_##xname##; VTABLE_AACTOR_FUNCTION(GetActorLocation); VTABLE_AACTOR_FUNCTION(SetActorLocation); } if (root == null) { root = new _AActor(); root->methods = vtable_methods; root->gPrintMessage = _gPrintMessage; root->gPrintClassName = _gPrintClassName; } root->nativeActorObj = this; //delete data and get it fresh from the new BeginPlay() session for (auto a : actorRefList) { delete a; } actorRefList.clear(); _AActor* next = root; for (TActorIterator Itr(GetWorld()); Itr; ++Itr) { var item = *Itr; _AActor* a = new _AActor(); a->nativeActorObj = item; a->methods = vtable_methods; a->gPrintMessage = _gPrintMessage; a->gPrintClassName = _gPrintClassName; next->nextActorInLinkedList = a; next = (_AActor*)a; actorRefList.push_back((_AActor*)a); } CppCli::Init(root); } // Sets default values AAGame::AAGame() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; } // Called when the game starts or when spawned void AAGame::BeginPlay() { Super::BeginPlay(); engine = GetWorld()->GetGameInstance()->GetEngine(); InitCSharp(); } // Called every frame void AAGame::Tick(float DeltaTime) { Super::Tick(DeltaTime); CppCli::Update(); } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(C# class file)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ using System; using System.Reflection; using System.Globalization; using System.Linq; using System.Collections.Generic; namespace game1 { public partial class Game1 { //TODO public class UActorComponent{} public class USceneComponent{} public class AActor { public object refclass; public int id; public void PrintMessage(string msg){ _PrintMessage_Method.Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{msg}, CultureInfo.CurrentCulture); } public void PrintClassName(){ _PrintClassName_Method.Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); } public static MethodInfo _PrintMessage_Method; public static MethodInfo _PrintClassName_Method; public static bool actors_methodinfo_valid = false; public static Dictionary getters; public static Dictionary setters; public static Dictionary functions; public static string[] fields = { /*~~~~~~~~~~~~~~~~~~~~~~~~~~fields~~~~~~~~~~~~~~~~~~~~~~~~~*/ "AutoReceiveInput","bActorSeamlessTraveled","bAllowTickBeforeBeginPlay","bAlwaysRelevant","bAutoDestroyWhenFinished", "bBlockInput","bCanBeDamaged","bCollideWhenPlacing","bEnableAutoLODGeneration","bExchangedRoles","bFindCameraComponentWhenViewTarget","bHidden", "bHiddenEd","bHiddenEdLayer","bHiddenEdLevel","bIgnoresOriginShifting","bIsEditorOnlyActor","bIsEditorPreviewActor","bLockLocation", "BlueprintCreatedComponents","bActorLabelEditable","bAllowReceiveTickEventOnDedicatedServer","bEditable","bCanBeInCluster", "bListedInSceneOutliner","bNetCheckedInitialPhysicsState","bOptimizeBPComponentData","bRelevantForLevelBounds","bRelevantForNetworkReplays", "bReplayRewindable","bReplicateMovement","bReplicates","ControllingMatineeActors","NetDriverName","bNetLoadOnClient","bNetStartup", "bNetTemporary","bOnlyRelevantToOwner","bOnlyRelevantToOwner","bRunConstructionScriptOnDrag","Children","CreationTime","CurrentTransactionAnnotation", "CustomTimeDilation","DetachFence","GroupActor","HiddenEditorViews","InitialLifeSpan","InputComponent","InputPriority", "Instigator","Layers","MinNetUpdateFrequency","NetCullDistanceSquared","NetDormancy","NetPriority","NetTag","NetUpdateFrequency", /*--------------------------------------------------------------------------------------------------------------------------------*/ "ReplicatedMovement","Role","SpawnCollisionHandlingMethod","SpriteScale","Tags","PivotOffset","ReplicatedComponents","RootComponent","TimerHandle_LifeSpanExpired", /*~~~~~~~~~~~~~~~~~~~~~~~~~~functions~~~~~~~~~~~~~~~~~~~~~~~~~*/ /*"ActorHasTag",*/ "GetActorForwardVector","GetActorLocation","GetActorQuat","GetActorRelativeScale3D","GetActorRightVector","GetActorRotation","GetActorScale","GetActorScale3D", "SetActorLocation", //TODO: W }; public void writeToFile(MethodInfo[] methods) { List lines = new List(); foreach(var m in methods) lines.Add("method: " + m.Name); System.IO.File.WriteAllLines(@"-----------\out2.txt", lines); } public AActor(object refclass, int id) { this.id = id; this.refclass = refclass; actors.Add(this); if(!actors_methodinfo_valid) { actors_methodinfo_valid = true; getters = new Dictionary(); setters = new Dictionary(); functions = new Dictionary(); var methods = refclass.GetType().GetMethods(); _PrintMessage_Method = methods.Where(x=>x.Name == "PrintMessage").FirstOrDefault(); _PrintClassName_Method = methods.Where(x=>x.Name == "PrintClassName").FirstOrDefault(); writeToFile(methods); foreach(var m in methods) foreach(var f in fields) if(m.Name == f){functions[f] = m; break;} foreach(var m in methods) foreach(var f in fields) if(m.Name == "get_"+f){getters[f] = m; break;} foreach(var m in methods) foreach(var f in fields) if(m.Name == "set_"+f){setters[f] = m; break;} } PrintClassName(); //this only works if there's a RootComponent if(GetActorLocation().Z == -1) PrintMessage("GetActorLocation(): { No RootComponent }"); else PrintMessage("GetActorLocation(): " + GetActorLocation()); } public void Update() { if(id == 1) { var mightBeNoRootComponent = GetActorLocation(); if(mightBeNoRootComponent.Z != -1) SetActorLocation(GetActorLocation() + new FVector(-1, 0, 0)); } } /*~~~~~~~~~~~~~~~~~~~~~~~~~~functions~~~~~~~~~~~~~~~~~~~~~~~~~*/ public void SetActorLocation(FVector vec) { functions["SetActorLocation"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{vec.vt}, CultureInfo.CurrentCulture); } public FVector GetActorLocation() { return new FVector((ValueType[])functions["GetActorLocation"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture)); } //<...> /*~~~~~~~~~~~~~~~~~~~~~~~~~~fields~~~~~~~~~~~~~~~~~~~~~~~~~*/ //<...> public UInt64 HiddenEditorViews { get{ return (UInt64)getters["HiddenEditorViews"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); } set{ setters["HiddenEditorViews"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } } public float InitialLifeSpan { get{ return (float)getters["InitialLifeSpan"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); } set{ setters["InitialLifeSpan"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } } public Int32 InputPriority { get{ return (Int32)getters["InputPriority"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); } set{ setters["InputPriority"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } } public float MinNetUpdateFrequency { get{ return (float)getters["MinNetUpdateFrequency"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); } set{ setters["MinNetUpdateFrequency"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } } public ENetDormancy NetDormancy { get{ return (ENetDormancy)getters["NetDormancy"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); } set{ setters["NetDormancy"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } } public ENetRole Role { get{ return (ENetRole)getters["Role"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); } set{ setters["Role"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } } public ESpawnActorCollisionHandlingMethod SpawnCollisionHandlingMethod { get{ return (ESpawnActorCollisionHandlingMethod)getters["SpawnCollisionHandlingMethod"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); } set{ setters["SpawnCollisionHandlingMethod"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } } public float SpriteScale { get{ return (float)getters["SpriteScale"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); } set{ setters["SpriteScale"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } } //<...> } } }