Open nargacu83 opened 4 years ago
I'm facing this problem too. In your project the Assembly.LoadFile call (mentioned here: https://docs.godotengine.org/en/3.2/getting_started/workflow/export/exporting_pcks.html ) is missing but even with the assembly loaded the script cant be instanced
@xsellier Since you wrote that note in the docs, could you help us out?
I didn't had the time to test it but i think we can try to load the DLL's content in the PCK with the godot File class, try to convert it into bytes to load it using Assembly.Load(bytes[] rawAssembly)
.
Yes we wrote that in the docs when i had this problem, so maybe we can find a workaround for it soon.
Was this problem ever figured out? I'm getting the following error when trying to load a scene with a c# script attached to it: ERROR: Cannot instance script because the class 'Tutorial' could not be found.
Not yet, after some research i ended up doing all the code on the base and only do textures, scenes and other assets in the PCKs like in other engines.
I wanted to do more research before posting here. So far i found that the assembly is indeed loaded but Godot don't know it, all the code is there but can't be "loaded" in the Godot context.
Any progress on this? I'm getting the error just trying to run my project via the Editor or VSCode
@Wavertron As far as I know, nobody found a solution to this yet.
Well, at least it worked but it's really unstable and i sure i'm doing it really poorly.
So here's what i've done in the PCKLoader.cs:
private byte[] GetAssemblyBytes()
{
byte[] rawAssembly = null;
Godot.File dllFile = new Godot.File();
dllFile.Open("res://.mono/assemblies/Debug/ProjectExportPCK.dll", Godot.File.ModeFlags.Read);
rawAssembly = dllFile.GetBuffer((int) dllFile.GetLen());
dllFile.Close();
return rawAssembly;
}
private void FixDependencies()
{
Assembly asm = AppDomain.CurrentDomain.Load(GetAssemblyBytes());
foreach (string dependencyPath in ResourceLoader.GetDependencies("res://PCKScene.tscn"))
{
// Look for C# Scripts
CSharpScript csScript = ResourceLoader.Load<CSharpScript>(dependencyPath, "CSharpScript", false);
// Makes sure it's a C# script
if(csScript == null)
{
continue;
}
// Try to reload the script
Error reloadResult = csScript.Reload();
// Look for each type in the assembly of the DLC
foreach (Type type in asm.GetTypes())
{
// Look if the type name is the same as the resource name
// Potential error depending on the name
if(type.ToString().Equals(csScript.ResourceName))
{
// Try to instance the script with Activator
object scriptInstance = Activator.CreateInstance(type);
// In this case the script derives from Control type
Control control = (Control) scriptInstance;
// Try to instance the script as a control node
GetTree().CurrentScene.AddChild(control);
break;
}
}
}
}
So right now, i rather prefer to not do something like this. It is very unstable, crashes Godot one time out of two, not even the game itself.
The best we can do right now with the stable version is to do all the code in the same assembly and separate all other "heavy" assets in PCKs.
@neikeq I guess that's a use case that you hadn't thought of/tested yet. Some changes are probably needed to ensure that assemblies are properly exported in "DLC" type PCKs, and properly loaded when using ProjectSettings.LoadResourcePack
.
Same problem here.
I created a new project to test and its attached here: mono-test.zip
Steps to reproduce:
KinematicBody2D.cs
and KinematicBody2D.tscn
to outside the project and export it as executable.KinematicBody2D.cs
and KinematicBody2D.tscn
again to inside the project and export it as .pck or .zip to extract the .dllIf i export the executable game filtering the resources, but with the class present on project it works properly, but if a export the executable without the classes and after try to load new classes in runtime the error apeear.
I also found this problem today, and I hope it can be solved.
I am also facing this problem.
I believe I have found a stable work around. This example below is setup to only load one specific mod (GameMod) but can be adapted to do more than one mod.
I've attached the projects if someone wants to test further. The Game.zip would be your game itself, containing the mod loader and an example mod. And the GameMod.zip would be what someone else makes as the mod to your game.
Hopefully this helps with creating a fix in the engine but it runs as a good work around in the meantime from my tests.
public class Main : Node2D {
public const string NameSpace = "GameMod"; //Namespace of the mod
public static Assembly Assembly; //Assembly of the mod
public override void _Ready() {
base._Ready();
var dir = Directory.GetCurrentDirectory() + "\\";
foreach (var file in Directory.GetFiles("Mods")) {
if (!file.ToLower().EndsWith(".dll")) continue;
Assembly = Assembly.LoadFile(dir + file);
ProjectSettings.LoadResourcePack((dir + file).Replace(".dll", ".pck"));
}
var entryPoint = LoadPatched("res://ModEntry.tscn");
AddChild(entryPoint.Instance());
}
//Loads a PackedScene but replaces the CScript with an Assembly instantiated version
public static PackedScene LoadPatched(string scenePath) {
var x = ResourceLoader.Load<PackedScene>(scenePath);
var bundled = x.Get("_bundled") as Dictionary;
var vars = bundled["variants"] as Array;
for (var i = 0; i < vars.Count; i++) {
var o = vars[i];
if (o is CSharpScript c) {
//Try to find the path of the script by the namespace and the resource itself
var path = NameSpace + "." + c.ResourcePath.Split("res://")[1].Replace("/", ".").Replace(".cs", "");
//Attempt to instantiate that script, assuming it is a Node so we can get the script
var thing = Assembly.GetType(path).GetConstructor(Type.EmptyTypes)?.Invoke(System.Array.Empty<object>()) as Node;
//Change the script of the packed scene
vars[i] = thing.GetScript();
thing.Free();
}
}
bundled["variants"] = vars;
x.Set("_bundled", bundled);
return x;
}
}
我相信我已经找到了稳定的工作。下面的示例设置为仅加载一个特定的 mod (GameMod),但可以调整为执行多个 mod。
如果有人想进一步测试,我已经附上了这些项目。Game.zip 将是您的游戏本身,包含 mod 加载器和示例 mod。GameMod.zip 将是其他人制作的游戏模组。
希望这有助于在引擎中创建修复程序,但同时从我的测试中可以很好地解决它。
public class Main : Node2D { public const string NameSpace = "GameMod"; //Namespace of the mod public static Assembly Assembly; //Assembly of the mod public override void _Ready() { base._Ready(); var dir = Directory.GetCurrentDirectory() + "\\"; foreach (var file in Directory.GetFiles("Mods")) { if (!file.ToLower().EndsWith(".dll")) continue; Assembly = Assembly.LoadFile(dir + file); ProjectSettings.LoadResourcePack((dir + file).Replace(".dll", ".pck")); } var entryPoint = LoadPatched("res://ModEntry.tscn"); AddChild(entryPoint.Instance()); } //Loads a PackedScene but replaces the CScript with an Assembly instantiated version public static PackedScene LoadPatched(string scenePath) { var x = ResourceLoader.Load<PackedScene>(scenePath); var bundled = x.Get("_bundled") as Dictionary; var vars = bundled["variants"] as Array; for (var i = 0; i < vars.Count; i++) { var o = vars[i]; if (o is CSharpScript c) { //Try to find the path of the script by the namespace and the resource itself var path = NameSpace + "." + c.ResourcePath.Split("res://")[1].Replace("/", ".").Replace(".cs", ""); //Attempt to instantiate that script, assuming it is a Node so we can get the script var thing = Assembly.GetType(path).GetConstructor(Type.EmptyTypes)?.Invoke(System.Array.Empty<object>()) as Node; //Change the script of the packed scene vars[i] = thing.GetScript(); thing.Free(); } } bundled["variants"] = vars; x.Set("_bundled", bundled); return x; } }
Does it support IOS?
PCKLoader
godot4 error;;
Assembly.GetType(path) this objet is null
I believe I have found a stable work around. This example below is setup to only load one specific mod (GameMod) but can be adapted to do more than one mod.
I've attached the projects if someone wants to test further. The Game.zip would be your game itself, containing the mod loader and an example mod. And the GameMod.zip would be what someone else makes as the mod to your game.
Hopefully this helps with creating a fix in the engine but it runs as a good work around in the meantime from my tests.
public class Main : Node2D { public const string NameSpace = "GameMod"; //Namespace of the mod public static Assembly Assembly; //Assembly of the mod public override void _Ready() { base._Ready(); var dir = Directory.GetCurrentDirectory() + "\\"; foreach (var file in Directory.GetFiles("Mods")) { if (!file.ToLower().EndsWith(".dll")) continue; Assembly = Assembly.LoadFile(dir + file); ProjectSettings.LoadResourcePack((dir + file).Replace(".dll", ".pck")); } var entryPoint = LoadPatched("res://ModEntry.tscn"); AddChild(entryPoint.Instance()); } //Loads a PackedScene but replaces the CScript with an Assembly instantiated version public static PackedScene LoadPatched(string scenePath) { var x = ResourceLoader.Load<PackedScene>(scenePath); var bundled = x.Get("_bundled") as Dictionary; var vars = bundled["variants"] as Array; for (var i = 0; i < vars.Count; i++) { var o = vars[i]; if (o is CSharpScript c) { //Try to find the path of the script by the namespace and the resource itself var path = NameSpace + "." + c.ResourcePath.Split("res://")[1].Replace("/", ".").Replace(".cs", ""); //Attempt to instantiate that script, assuming it is a Node so we can get the script var thing = Assembly.GetType(path).GetConstructor(Type.EmptyTypes)?.Invoke(System.Array.Empty<object>()) as Node; //Change the script of the packed scene vars[i] = thing.GetScript(); thing.Free(); } } bundled["variants"] = vars; x.Set("_bundled", bundled); return x; } }
godot4 error
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. at Godot.NativeInterop.NativeFuncs.godotsharp_string_new_with_utf16_chars(Godot.NativeInterop.godot_string ByRef, Char*) at Godot.NativeInterop.Marshaling.ConvertStringToNative(System.String) at Godot.NativeInterop.NativeFuncs.godotsharp_string_name_new_from_string(System.String)
var thing = asm.GetType(path).GetConstructor(Type.EmptyTypes)?.Invoke(System.Array.Empty
this line crashed
@diybl This issue is about C# in 3.x, which uses Mono, not .NET 6. The .NET 6 issue is being tracked in https://github.com/godotengine/godot/issues/73932.
@diybl This issue is about C# in 3.x, which uses Mono, not .NET 6. The .NET 6 issue is being tracked in #73932.
Are there plans to fix this bug? I'm worried my game won't have this feature before release. Or could I use the patch method of c# to temporarily solve this bug?
Any updates on this? Still having the issue and can't seem to find a workaround.
Bugsquad note: This issue has been confirmed several times already. No need to confirm it further.
Godot version:
v3.2.stable.mono.official
OS/device including version:
Manjaro Linux with kernel 5.5.7-1
Issue description:
We are unable to fully import a scene from a
.pck
or.zip
file with C# scripts dependencies.Steps to reproduce:
Extract the downloaded MinimalProject.zip.
Open the Project_Import_PCK in Godot.
Run the project and check the Debugger.
Minimal reproduction project:
MinimalProject.zip
Notes:
.pck
and.zip
files..pck
or.zip
file.