Closed totallyeviljake closed 11 years ago
BMFontConfiguration uses the ReflectiveReader, which does not make it into the binary on a device build.
this error for this issue is from the file name being ".fnt" instead of without the suffix.
these menu and label tests work on the simulator.
we are also building for the device with linking disabled.
This is for Xamarin:
Microsoft.Xna.Framework.Content.ReflectiveReader`1[[cocos2d.CCBMFontConfiguration, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]]
Ran this (label) test just now, third test (Atlas3.cs) loads an FNT file, which is caught in ContentTypeReaderManager.cs (MonoGame) line 161.
The exception here is that it can not find the type listed above.
here is the code excerpt:
else
{
//System.Diagnostics.Debug.WriteLine(originalReaderTypeString);
// Need to resolve namespace differences
string readerTypeString = originalReaderTypeString;
readerTypeString = PrepareType(readerTypeString);
var l_readerType = Type.GetType(readerTypeString);
if (l_readerType != null)
{
try
{
contentReaders[i] = l_readerType.GetDefaultConstructor().Invoke(null) as ContentTypeReader;
}
catch (TargetInvocationException ex)
{
// If you are getting here, the Mono runtime is most likely not able to JIT the type.
// In particular, MonoTouch needs help instantiating types that are only defined in strings in Xnb files.
throw new InvalidOperationException(
"Failed to get default constructor for ContentTypeReader. To work around, add a creation function to ContentTypeReaderManager.AddTypeCreator() " +
"with the following failed type string: " + originalReaderTypeString);
}
}
else
throw new ContentLoadException("Could not find matching content reader of type " + originalReaderTypeString + " (" + readerTypeString + ")");
}
In my program.cs I added the following to bypass the AOT optimization:
public override void FinishedLaunching (UIApplication app)
{
// More shameless hacking to bypass AOT
var hHack = new ReflectiveReader<CCBMFontConfiguration>();
var hFoo = new PlistDocument.PlistDocumentReader ();
// Fun begins..
game = new Game1();
game.Run();
}
This works on the simulator, does not work on the device. I have the linker setting set to "do not link".
This is on an iPad G1 running iOS 5.1
In the above, here is the code that throws the exception:
else
throw new ContentLoadException("Could not find matching content reader of type " + originalReaderTypeString + " (" + readerTypeString + ")");
There are no other errors in the output console. Just the exception associated with the error described above.
What is the value of readerTypeString before the call to Type.GetType?
You mean - what happens here:
readerTypeString = PrepareType(readerTypeString);
Will have to capture that again in another debug session.
readerTypeString = Microsoft.Xna.Framework.Content.ReflectiveReader`1[[cocos2d.CCBMFontConfiguration, cocos2d-xna]]
ha, Sebastien was right - cocos2d-xna.iOS (and I fixed that once already). so now I get this error:
System.InvalidOperationException: Failed to get default constructor for ContentTypeReader. To work around, add a creation function to ContentTypeReaderManager.AddTypeCreator() with the following failed type string: Microsoft.Xna.Framework.Content.DictionaryReader2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[cocos2d.CCBMFontConfiguration+ccBMFontDef, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]] at Microsoft.Xna.Framework.Content.ContentTypeReaderManager.LoadAssetReaders () [0x0019f] in /Users/admin/MonoGame/MonoGame.Framework/Content/ContentTypeReaderManager.cs:155 at Microsoft.Xna.Framework.Content.ContentReader.InitializeTypeReaders () [0x0000c] in /Users/admin/MonoGame/MonoGame.Framework/Content/ContentReader.cs:111 at Microsoft.Xna.Framework.Content.ContentReader.ReadAsset[CCBMFontConfiguration] () [0x00000] in <filename unknown>:0 at Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[CCBMFontConfiguration] (System.String assetName, System.Action
1 recordDisposableObject) [0x00000] in 1 pTouches, cocos2d.CCEvent pEvent, Int32 uIndex) [0x00112] in /Users/admin/cocos2d-xna/cocos2d/touch_dispatcher/CCTouchDispatcher.cs:225 at cocos2d.CCTouchDispatcher.TouchesEnded (System.Collections.Generic.List
1 touches, cocos2d.CCEvent pEvent) [0x0000b] in /Users/admin/cocos2d-xna/cocos2d/touch_dispatcher/CCTouchDispatcher.cs:51
at cocos2d.CCApplication.ProcessTouch () [0x002a7] in /Users/admin/cocos2d-xna/cocos2d/Platform/CCApplication.cs:514
at cocos2d.CCApplication.Update (Microsoft.Xna.Framework.GameTime gameTime) [0x00025] in /Users/admin/cocos2d-xna/cocos2d/Platform/CCApplication.cs:185
at Microsoft.Xna.Framework.Game.1[Microsoft.Xna.Framework.IUpdateable].ForEachFilteredItem[GameTime] (System.Action
2 action, Microsoft.Xna.Framework.GameTime userData) [0x00000] in
i ran it again in the simulator and sure enough it works there.
Question: is there a chance that we can just move the font loader to Cocos2D and forget about using MonoGame for it?
From what I am gathering it is an AOT issue and not a linker issue with iOS. http://docs.xamarin.com/guides/ios/advanced_topics/limitations
Specifically from this line here right?
var l_readerType = Type.GetType(readerTypeString);
That is nasty. Have never looked at that part of the code in MG nor run into this issue before so will have to read about it. Never really ran into the issue so never occurred to me.
yes the font and other cocos2d resources can be loaded from embedded resources instead of using the content manager. the FNT file loader will need to revert back to the cocos2d-x variety of its implementation though.
there is an embedded resource content loader ... maybe we could use that instead.
Microsoft.Xna.Framework.Content.DictionaryReader2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[cocos2d.CCBMFontConfiguration+ccBMFontDef, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]]
^ there should not be a reference on "mscorlib, Version=4.0.0.0" on an iOS project.
ahha! So we have to build the iOS content on a windows box, and that must be where the mscorlib reference originates.
@tomspilman does the MGCB work on iOS yet, or do we still have to build on Windows?
It was working for textures and audio files. We add SpriteFont support using pure .NET APIs, but i don't think it has been tested on Mac yet.
@RayBatts @dellis1972 - Have we tested this?
I get new exception message now when loading FNT files. I think this is from @raybatts recent changes:
around, add a creation function to ContentTypeReaderManager.AddTypeCreator() with the following failed type string: Microsoft.Xna.Framework.Content.DictionaryReader`2[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[cocos2d.CCBMFontConfiguration+ccBMFontDef, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null] at Microsoft.Xna.Framework.Content.ContentTypeReaderManager.LoadAssetReaders () [0x0019f]
cool!
Besides the Parallax test that uses GetData, this is the last issue on iOS as far as I can tell.
Have any ideas how we can tackle this one? The CCBMFontConfiguration is not a Reader like the PList is.
Also am thinking that MGCB is not going to fix this issue. In the loading code all the version information is stripped out and I think it is need to actually load the class dynamically. PrepareType strips out some of the information on the class.
this works on the simulator, just not after AOT takes over. I'll verify again, but that appears to be the problem. if we can't get around AOT on this one, then we will need to (1) not allow the ReflectiveReader on iOS in MonoGame, and (2) rewrite this font reader to NOT use reflection...
I started adding
#if IOS
[MonoTouch.Foundation.Preserve (AllMembers = true)]
#endif
To the members of CCBMFontConfiguration and for ccBMFontDef and it is now crashing with reflective reader of CCRect. Added the same there and it is still having the problem.
there's an assembly level preserve attribute, right? I think that I added that once too.
Armed uses the reflective reader - @raybatts had posted a PR fix for that - I wonder if they are having a similar problem with reflecting on generics...
I have it this far:
Microsoft.Xna.Framework.Content.ReflectiveReader`1[[cocos2d.CCRect, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]]
2013-03-14 07:06:42.251 cocos2dtests[15397:907] Unhandled managed exception: Failed to get default constructor for ContentTypeReader. To work around, add a creation function to ContentTypeReaderManager.AddTypeCreator() with the following failed type string: Microsoft.Xna.Framework.Content.ReflectiveReader`1[cocos2d.CCRect, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]
at Microsoft.Xna.Framework.Content.ContentTypeReaderManager.LoadAssetReaders () [0x0019f] in /Users/Jimmy/Public/Share/MonoMacSource/kjpgit/MonoGame/MonoGame.Framework/Content/ContentTypeReaderManager.cs:155
at Microsoft.Xna.Framework.Content.ContentReader.InitializeTypeReaders () [0x0000c] in /Users/Jimmy/Public/Share/MonoMacSource/kjpgit/MonoGame/MonoGame.Framework/Content/ContentReader.cs:111
at Microsoft.Xna.Framework.Content.ContentReader.ReadAsset[CCBMFontConfiguration]() [0x00000] in
CCRect is a struct with no default ctor. After CCRect you'll have to do CCSize and CCPoint. That'll take you to the primitive ordinal types. CCRect, CCSize, and CCpoint are all structs.
So, from the .NET MSDN help:
Structs cannot contain explicit parameterless constructors. Struct members are automatically initialized to their default values.
http://msdn.microsoft.com/en-us/library/aa288208(v=vs.71).aspx
Hmmmmm
Yeah have almost got them. Am on tKerningElement at the moment. It is hobbling along.
I have created ContentReaders for all of those.
Having problems with tKerningHashElement with it being nested.
Ok I think it is all working. I do not have access to an actual device, oh by the way as of last night I am able to test on an actual device, because I am remoted in. It looks like my machine went down which is weird.
I was in the middle of git add for all the classes when the connection failed so do not have the commits done yet. Let me see when or if the machine comes back.
FYI. I'm going to keep out of this conversation because I have absolutely no clue what anyone is discussing here. :)
Same as Tom. I've never even heard of a .fnt file before this.
Armed uses the reflective reader - @raybatts had posted a PR fix for that - I wonder if they are having a similar problem with reflecting on generics.
Yes, there are some problems with using generics on MonoTouch. As discussed though, the [MonoTouch.Foundation.Preserve (AllMembers = true)] attribute prevents it from being stripped and crashing at runtime. The fix I posted for the ReflectiveReader didn't have to do with generics though, nor do I think it caused this issue.
@RayBatts @tomspilman - FNT is a bitmap font description (like a poor mans version of spritefont)
We read in the FNT key (tells us where the characters are at in the bitmap) using the ReflectiveReader. The members of the FNT data structure use a generic dictionary and some struct members. The problem appears to be that the content loader needs to create instances of the structs using the default ctor, but there is no default ctor on a struct. So @kjpou1 has been adding custom type creators for these members.
I was just curious if you had seen the same problem with Armed on an iOS device (not the simulator).
I tried using Preserve but that didn't solve the problem for us. This one is a case of not being able to find the default ctor (TargetInvocation) rather than a missing type. @RayBatts changes to MonoGame helped to pick up on that detailed exception coming out of MonoGame.
@RayBatts By default (which is "Link SDK assemblies" for devices) the linker does not remove anything from non-SDK assemblies. IOW it only removes code from Xamarin-shipping assemblies.
So unless you are using "Link all assemblies" using [Preserve]
on in your own code won't change anything (as far as the linker is concerned).
If you ever doubt that the linker is an issue for your project you can: a) turn it off (i.e. Don't link) on a device build; or b) turn it on (i.e. Link SDK) on a simulator build (which is off by default); and compare results.
@spouliot I think the culprit of all this woe has been AOT and not the final linker. there must be some step in AOT that removes some generic or unused classes, as far as it can tell anyway. I dunno, I am having a hard time understanding why this works on the simulator and not on the device. We don't have the linker enabled on our device build of cocos2d-xna and it still doesn't work the same.
It could be the AOT compiler. It sometime needs a bit of help, e.g. for nested generics, to detect (and compile) all the generic code that is required at runtime (not always easy to detect).
The fact that it works on the simulator (with the same linking options) makes it even more likely to be an AOT issue as the simulator uses the JIT (not the AOT) compiler.
OTOH you generally get a EngineExecutionException when, at runtime, the application finds out the required code is missing (i.e. if it was not compiled). If uncatched that exception can cause a crash too (or it could be catched and not reported, wrapped, inside another exception). If a runtime crash occurs then the crash report should hint about this.
Hey guys
Here is a "Potential" fix for this issue for now. It work on the physical device as well as simulator.
https://github.com/totallyevil/cocos2d-xna/pull/56
Edit:
Also, am not saying I like this mod at all but what @RayBatts did was the perfect solution to at least get it rolling for now until we get a definitive resolution. Thanks Ray because I was looking at doing the same type of modification when I found your code.
Jake
Which one? I have it up and running right now on my iPhone.
I created 5 new readers.
CCPointReader CCRectReader CCSizeReader KerningHashElement CCBMFontPaddingReader
The others were for DictionaryReader.
Testing on iPhone 4 iOS 6.1.2
They should be in the platform/ContentReaders
ContentTypeReaderManager.AddTypeCreator (
"Microsoft.Xna.Framework.Content.DictionaryReader`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[cocos2d.CCBMFontConfiguration+ccBMFontDef, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]]",
( ) => new DictionaryReader<Int32, CCBMFontConfiguration.ccBMFontDef> ()
);
ContentTypeReaderManager.AddTypeCreator (
"Microsoft.Xna.Framework.Content.DictionaryReader`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[cocos2d.CCBMFontConfiguration+tKerningHashElement, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]]",
( ) => new DictionaryReader<Int32, CCBMFontConfiguration.tKerningHashElement> ()
);
ContentTypeReaderManager.AddTypeCreator (
"Microsoft.Xna.Framework.Content.ReflectiveReader`1[[cocos2d.CCRect, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]]",
( ) => new CCRectReader ()
);
ContentTypeReaderManager.AddTypeCreator (
"Microsoft.Xna.Framework.Content.ReflectiveReader`1[[cocos2d.CCPoint, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]]",
( ) => new CCPointReader ()
);
ContentTypeReaderManager.AddTypeCreator (
"Microsoft.Xna.Framework.Content.ReflectiveReader`1[[cocos2d.CCSize, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]]",
( ) => new CCSizeReader ()
);
ContentTypeReaderManager.AddTypeCreator (
"Microsoft.Xna.Framework.Content.ReflectiveReader`1[[cocos2d.CCBMFontConfiguration+tKerningHashElement, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]]",
( ) => new KerningHashElementReader ()
);
ContentTypeReaderManager.AddTypeCreator (
"Microsoft.Xna.Framework.Content.ReflectiveReader`1[[cocos2d.CCBMFontConfiguration+ccBMFontPadding, cocos2d-xna, Version=2.0.3.0, Culture=neutral, PublicKeyToken=null]]",
( ) => new CCBMFontPaddingtReader ()
);
Jake
Did not push the main mod last night. Thought it was already done but I guess it was not. Will send the PR.
no worries. with your mac dying, I figured something was lost ..
MenuTest and Label Test both work.
Loading of FNT content files does not work.
Mar 3 10:40:24 jacobs-iPhone cocos2dtests[477]: Unhandled managed exception: Could not load fonts/konqa32.fnt asset as a non-content file! (Microsoft.Xna.Framework.Content.ContentLoadException)
at Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[CCBMFontConfiguration](System.String assetName, System.Action`1 recordDisposableObject) [0x00000] in :0
at Microsoft.Xna.Framework.Content.ContentManager.Load[T](System.String assetName) [0x00000] in :0
at cocos2d.CCBMFontConfiguration.Create (System.String fntFile) [0x00000] in :0
at cocos2d.CCLabelBMFont.FNTConfigLoadFile (System.String file) [0x00000] in :0
at cocos2d.CCLabelBMFont.InitWithString (System.String theString, System.String fntFile, Single width, CCTextAlignment alignment, CCPoint imageOffset) [0x00000] in :0
at cocos2d.CCLabelBMFont.Create (System.String str, System.String fntFile, Single width, CCTextAlignment alignment, CCPoint imageOffset) [0x00000] in :0
at cocos2d.CCLabelBMFont.Create (System.String str, System.String fntFile) [0x00000] in :0
at tests.LayerScaleTest.OnEnter () [0x00000] in :0
at cocos2d.CCNode.OnEnter () [0x00000] in :0
at tests.TestScene.OnEnter () [0x00000] in :0
at cocos2d.CCDirector.SetNextScene () [0x00000] in :0