craftworkgames / MonoGame.Extended

Extensions to make MonoGame more awesome
http://www.monogameextended.net/
Other
1.44k stars 325 forks source link

NullReferenceException in Content Pipeline #739

Open obiwanjacobi opened 3 years ago

obiwanjacobi commented 3 years ago

Just starting out with MonoGame, MonoGame Extended and Tiled, so...

Trying to setup a parallax tile map in Tiled. Building the content pipeline gives an error. I probably do something weird you're not supposed to do. Is it the space in the file name....?

C:/Users/marc/source/repos/MonoTiled/MonoTiled/Content/Parallax/Back Mountain.tsx
        Importing 'C:/Users/marc/source/repos/MonoTiled/MonoTiled/Content/Parallax/Back Mountain.tsx'
           at MonoGame.Extended.Content.Pipeline.Tiled.TiledMapTilesetImporter.DeserializeTiledMapTilesetContent(String filePath, ContentImporterContext context)
   at MonoGame.Extended.Content.Pipeline.Tiled.TiledMapTilesetImporter.Import(String filePath, ContentImporterContext context)
    Importer 'TiledMapTilesetImporter' had unexpected failure!
C:/Users/marc/source/repos/MonoTiled/MonoTiled/Content/Parallax/Parallax.tmx: error: Importer 'TiledMapTilesetImporter' had unexpected failure!
System.NullReferenceException: Object reference not set to an instance of an object.
   at MonoGame.Extended.Content.Pipeline.Tiled.TiledMapTilesetImporter.DeserializeTiledMapTilesetContent(String filePath, ContentImporterContext context)
   at MonoGame.Extended.Content.Pipeline.Tiled.TiledMapTilesetImporter.Import(String filePath, ContentImporterContext context)
   at Microsoft.Xna.Framework.Content.Pipeline.ContentImporter`1.Microsoft.Xna.Framework.Content.Pipeline.IContentImporter.Import(String filename, ContentImporterContext context) in C:\BuildAgents\MonoGameWin1\work\f7381a85a626990\MonoGame.Framework.Content.Pipeline\ContentImporter.cs:line 45
   at MonoGame.Framework.Content.Pipeline.Builder.PipelineManager.ProcessContent(PipelineBuildEvent pipelineEvent) in C:\BuildAgents\MonoGameWin1\work\f7381a85a626990\MonoGame.Framework.Content.Pipeline\Builder\PipelineManager.cs:line 669

The tile map file:

<?xml version="1.0" encoding="UTF-8"?>
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" width="1" height="1" tilewidth="272" tileheight="160" infinite="0" nextlayerid="7" nextobjectid="1">
 <tileset firstgid="1" source="Back Mountain.tsx"/>
 <tileset firstgid="2" source="Mountain.tsx"/>
 <tileset firstgid="3" source="Trees.tsx"/>
 <tileset firstgid="4" source="Trees Forground.tsx"/>
 <tileset firstgid="5" source="Backdrop.tsx"/>
 <layer id="6" name="bgp0" width="1" height="1" locked="1">
  <data encoding="csv">
5
</data>
 </layer>
 <layer id="1" name="bgp1" width="1" height="1" parallaxx="1.1">
  <data encoding="csv">
1
</data>
 </layer>
 <layer id="3" name="bgp2" width="1" height="1" parallaxx="1.2">
  <data encoding="csv">
2
</data>
 </layer>
 <layer id="4" name="bgp3" width="1" height="1" parallaxx="1.3">
  <data encoding="csv">
3
</data>
 </layer>
 <layer id="5" name="bgp4" width="1" height="1" parallaxx="1.4">
  <data encoding="csv">
4
</data>
 </layer>
</map>

The tile set file:

<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.5" tiledversion="1.7.0" name="Back Mountain" tilewidth="272" tileheight="160" tilecount="1" columns="0">
 <grid orientation="orthogonal" width="1" height="1"/>
 <tile id="0">
  <image width="272" height="160" source="parallax-mountain-montain-far.png"/>
 </tile>
</tileset>
obiwanjacobi commented 3 years ago

Ok, I did some digging and debugging and the problem is that I made a tilemap that references multiple tilesets with multiple images. Tiled outputs a little different structure for these cases. The NullReferenceException was caused by the assumption that tileset.Image would always exist, which in my case is not true. The Image is now on the tile.

I have tried to fix the code in the content pipeline and it compiles and builds content, but there seems to be a problem when calling LoadContent on the root .tmx. In my test app I get an error like:

FileNotFoundException: Could not find file 'C:\Users\marc\source\repos\MonoTiled\MonoTiled\bin\Debug\netcoreapp3.1\Content\Parallax\%1%0%0�%0%0%0%1%0%0%0%0%0%0%0%0.xnb'.

That file name concerns me deeply...

Microsoft.Xna.Framework.Content.ContentLoadException
  HResult=0x80131500
  Message=The content file was not found.
  Source=MonoGame.Framework
  StackTrace:
   at Microsoft.Xna.Framework.Content.ContentManager.OpenStream(String assetName)
   at Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[T](String assetName, Action`1 recordDisposableObject)
   at Microsoft.Xna.Framework.Content.ContentManager.Load[T](String assetName)
   at Microsoft.Xna.Framework.Content.ContentReader.ReadExternalReference[T]()
   at MonoGame.Extended.Tiled.TiledMapTilesetReader.ReadTileset(ContentReader reader)
   at Microsoft.Xna.Framework.Content.ContentTypeReader`1.Read(ContentReader input, Object existingInstance)
   at Microsoft.Xna.Framework.Content.ContentReader.InnerReadObject[T](T existingInstance)
   at Microsoft.Xna.Framework.Content.ContentReader.ReadObject[T]()
   at Microsoft.Xna.Framework.Content.ContentReader.ReadAsset[T]()
   at Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[T](String assetName, Action`1 recordDisposableObject)
   at Microsoft.Xna.Framework.Content.ContentManager.Load[T](String assetName)
   at Microsoft.Xna.Framework.Content.ContentReader.ReadExternalReference[T]()
   at MonoGame.Extended.Tiled.TiledMapReader.ReadTilesets(ContentReader reader, TiledMap map)
   at MonoGame.Extended.Tiled.TiledMapReader.Read(ContentReader reader, TiledMap existingInstance)
   at Microsoft.Xna.Framework.Content.ContentTypeReader`1.Read(ContentReader input, Object existingInstance)
   at Microsoft.Xna.Framework.Content.ContentReader.InnerReadObject[T](T existingInstance)
   at Microsoft.Xna.Framework.Content.ContentReader.ReadObject[T]()
   at Microsoft.Xna.Framework.Content.ContentReader.ReadAsset[T]()
   at Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[T](String assetName, Action`1 recordDisposableObject)
   at Microsoft.Xna.Framework.Content.ContentManager.Load[T](String assetName)
   at MonoTiled.TiledParallaxScreen.LoadContent() in C:\Users\marc\source\repos\MonoTiled\MonoTiled\TiledParallaxScreen.cs:line 20
   at MonoTiled.GameLoop.LoadContent() in C:\Users\marc\source\repos\MonoTiled\MonoTiled\GameLoop.cs:line 55
   at MonoTiled.GameLoop.Initialize() in C:\Users\marc\source\repos\MonoTiled\MonoTiled\GameLoop.cs:line 40
   at Microsoft.Xna.Framework.Game.DoInitialize()
   at Microsoft.Xna.Framework.Game.Run(GameRunBehavior runBehavior)
   at MonoTiled.Program.Main() in C:\Users\marc\source\repos\MonoTiled\MonoTiled\Program.cs:line 11

  This exception was originally thrown at this call stack:
    System.IO.FileStream.ValidateFileHandle(Microsoft.Win32.SafeHandles.SafeFileHandle)
    System.IO.FileStream.CreateFileOpenHandle(System.IO.FileMode, System.IO.FileShare, System.IO.FileOptions)
    System.IO.FileStream.FileStream(string, System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare, int, System.IO.FileOptions)
    Microsoft.Xna.Framework.TitleContainer.PlatformOpenStream(string)
    Microsoft.Xna.Framework.TitleContainer.OpenStream(string)
    Microsoft.Xna.Framework.Content.ContentManager.OpenStream(string)

My fixes can be seen here: https://github.com/obiwanjacobi/MonoGame.Extended/commit/50badecb71a04da00dc06adb6cf41dce565f1fdb

-Sorry, for the mess of changes most of which are whitespace related. I have several housekeeping tools in my VS that sort usings and replace tabs with spaces etc...

Any insights or suggestions would be greatly appreciated.

lithiumtoast commented 3 years ago

Tiled software has changed over the years and thus the code has stagnated. I'm having the code move away from the content pipeline all together this summer. In the mean time if come up with a fix a PR would be welcome.

obiwanjacobi commented 3 years ago

Any idea why it is trying to load that weird file name (%1%0%0�%0%0%0%1%0%0%0%0%0%0%0%0.xnb)? I can see the correct .xnb files in the Content/bin, so it seems the pipeline processing is fine...?

obiwanjacobi commented 3 years ago

I think it might be a good idea to build an abstraction over the specific Tile Map Editor used. There are a couple and most of them work the same. All you need are deserializers for each specific editor that all generate a common object model...