Custom Content Pipeline with Automatic Serialization Load Error
- by Direweasel
I'm running into this error:
Error loading "desert". Cannot find type TiledLib.MapContent,
TiledLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
at
Microsoft.Xna.Framework.Content.ContentTypeReaderManager.InstantiateTypeReader(String
readerTypeName, ContentReader contentReader, ContentTypeReader&
reader) at
Microsoft.Xna.Framework.Content.ContentTypeReaderManager.GetTypeReader(String
readerTypeName, ContentReader contentReader, List1& newTypeReaders)
at
Microsoft.Xna.Framework.Content.ContentTypeReaderManager.ReadTypeManifest(Int32
typeCount, ContentReader contentReader) at
Microsoft.Xna.Framework.Content.ContentReader.ReadHeader() at
Microsoft.Xna.Framework.Content.ContentReader.ReadAsset[T]() at
Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[T](String
assetName, Action1 recordDisposableObject) at
Microsoft.Xna.Framework.Content.ContentManager.Load[T](String
assetName) at TiledTest.Game1.LoadContent() in C:\My
Documents\Dropbox\Visual Studio
Projects\TiledTest\TiledTest\TiledTest\Game1.cs:line 51 at
Microsoft.Xna.Framework.Game.Initialize() at
TiledTest.Game1.Initialize() in C:\My Documents\Dropbox\Visual Studio
Projects\TiledTest\TiledTest\TiledTest\Game1.cs:line 39 at
Microsoft.Xna.Framework.Game.RunGame(Boolean useBlockingRun) at
Microsoft.Xna.Framework.Game.Run() at
TiledTest.Program.Main(String[] args) in C:\My
Documents\Dropbox\Visual Studio
Projects\TiledTest\TiledTest\TiledTest\Program.cs:line 15
When trying to run the game. This is a basic demo to try and utilize a separate project library called TiledLib. I have four projects overall:
TiledLib (C# Class Library)
TiledTest (Windows Game)
TiledTestContent (Content)
TMX CP Ext (Content Pipeline Extension Library)
TiledLib contains MapContent which is throwing the error, however I believe this may just be a generic error with a deeper root problem.
EMX CP Ext contains one file: MapProcessor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using Microsoft.Xna.Framework.Content;
using TiledLib;
namespace TMX_CP_Ext
{
// Each tile has a texture, source rect, and sprite effects.
[ContentSerializerRuntimeType("TiledTest.Tile, TiledTest")]
public class DemoMapTileContent
{
public ExternalReference<Texture2DContent> Texture;
public Rectangle SourceRectangle;
public SpriteEffects SpriteEffects;
}
// For each layer, we store the size of the layer and the tiles.
[ContentSerializerRuntimeType("TiledTest.Layer, TiledTest")]
public class DemoMapLayerContent
{
public int Width;
public int Height;
public DemoMapTileContent[] Tiles;
}
// For the map itself, we just store the size, tile size, and a list of layers.
[ContentSerializerRuntimeType("TiledTest.Map, TiledTest")]
public class DemoMapContent
{
public int TileWidth;
public int TileHeight;
public List<DemoMapLayerContent> Layers = new List<DemoMapLayerContent>();
}
[ContentProcessor(DisplayName = "TMX Processor - TiledLib")]
public class MapProcessor : ContentProcessor<MapContent, DemoMapContent>
{
public override DemoMapContent Process(MapContent input, ContentProcessorContext context)
{
// build the textures
TiledHelpers.BuildTileSetTextures(input, context);
// generate source rectangles
TiledHelpers.GenerateTileSourceRectangles(input);
// now build our output, first by just copying over some data
DemoMapContent output = new DemoMapContent
{
TileWidth = input.TileWidth,
TileHeight = input.TileHeight
};
// iterate all the layers of the input
foreach (LayerContent layer in input.Layers)
{
// we only care about tile layers in our demo
TileLayerContent tlc = layer as TileLayerContent;
if (tlc != null)
{
// create the new layer
DemoMapLayerContent outLayer = new DemoMapLayerContent
{
Width = tlc.Width,
Height = tlc.Height,
};
// we need to build up our tile list now
outLayer.Tiles = new DemoMapTileContent[tlc.Data.Length];
for (int i = 0; i < tlc.Data.Length; i++)
{
// get the ID of the tile
uint tileID = tlc.Data[i];
// use that to get the actual index as well as the SpriteEffects
int tileIndex;
SpriteEffects spriteEffects;
TiledHelpers.DecodeTileID(tileID, out tileIndex, out spriteEffects);
// figure out which tile set has this tile index in it and grab
// the texture reference and source rectangle.
ExternalReference<Texture2DContent> textureContent = null;
Rectangle sourceRect = new Rectangle();
// iterate all the tile sets
foreach (var tileSet in input.TileSets)
{
// if our tile index is in this set
if (tileIndex - tileSet.FirstId < tileSet.Tiles.Count)
{
// store the texture content and source rectangle
textureContent = tileSet.Texture;
sourceRect = tileSet.Tiles[(int)(tileIndex - tileSet.FirstId)].Source;
// and break out of the foreach loop
break;
}
}
// now insert the tile into our output
outLayer.Tiles[i] = new DemoMapTileContent
{
Texture = textureContent,
SourceRectangle = sourceRect,
SpriteEffects = spriteEffects
};
}
// add the layer to our output
output.Layers.Add(outLayer);
}
}
// return the output object. because we have ContentSerializerRuntimeType attributes on our
// objects, we don't need a ContentTypeWriter and can just use the automatic serialization.
return output;
}
}
}
TiledLib contains a large amount of files including MapContent.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using Microsoft.Xna.Framework.Content.Pipeline;
namespace TiledLib
{
public enum Orientation : byte
{
Orthogonal,
Isometric,
}
public class MapContent
{
public string Filename;
public string Directory;
public string Version = string.Empty;
public Orientation Orientation;
public int Width;
public int Height;
public int TileWidth;
public int TileHeight;
public PropertyCollection Properties = new PropertyCollection();
public List<TileSetContent> TileSets = new List<TileSetContent>();
public List<LayerContent> Layers = new List<LayerContent>();
public MapContent(XmlDocument document, ContentImporterContext context)
{
XmlNode mapNode = document["map"];
Version = mapNode.Attributes["version"].Value;
Orientation = (Orientation)Enum.Parse(typeof(Orientation), mapNode.Attributes["orientation"].Value, true);
Width = int.Parse(mapNode.Attributes["width"].Value, CultureInfo.InvariantCulture);
Height = int.Parse(mapNode.Attributes["height"].Value, CultureInfo.InvariantCulture);
TileWidth = int.Parse(mapNode.Attributes["tilewidth"].Value, CultureInfo.InvariantCulture);
TileHeight = int.Parse(mapNode.Attributes["tileheight"].Value, CultureInfo.InvariantCulture);
XmlNode propertiesNode = document.SelectSingleNode("map/properties");
if (propertiesNode != null)
{
Properties = new PropertyCollection(propertiesNode, context);
}
foreach (XmlNode tileSet in document.SelectNodes("map/tileset"))
{
if (tileSet.Attributes["source"] != null)
{
TileSets.Add(new ExternalTileSetContent(tileSet, context));
}
else
{
TileSets.Add(new TileSetContent(tileSet, context));
}
}
foreach (XmlNode layerNode in document.SelectNodes("map/layer|map/objectgroup"))
{
LayerContent layerContent;
if (layerNode.Name == "layer")
{
layerContent = new TileLayerContent(layerNode, context);
}
else if (layerNode.Name == "objectgroup")
{
layerContent = new MapObjectLayerContent(layerNode, context);
}
else
{
throw new Exception("Unknown layer name: " + layerNode.Name);
}
// Layer names need to be unique for our lookup system, but Tiled
// doesn't require unique names.
string layerName = layerContent.Name;
int duplicateCount = 2;
// if a layer already has the same name...
if (Layers.Find(l => l.Name == layerName) != null)
{
// figure out a layer name that does work
do
{
layerName = string.Format("{0}{1}", layerContent.Name, duplicateCount);
duplicateCount++;
} while (Layers.Find(l => l.Name == layerName) != null);
// log a warning for the user to see
context.Logger.LogWarning(string.Empty, new ContentIdentity(), "Renaming layer \"{1}\" to \"{2}\" to make a unique name.", layerContent.Type, layerContent.Name, layerName);
// save that name
layerContent.Name = layerName;
}
Layers.Add(layerContent);
}
}
}
}
I'm lost as to why this is failing. Thoughts?
-- EDIT --
After playing with it a bit, I would think it has something to do with referencing the projects. I'm already referencing the TiledLib within my main windows project (TiledTest). However, this doesn't seem to make a difference. I can place the dll generated from the TiledLib project into the debug folder of TiledTest, and this causes it to generate a different error:
Error loading "desert". Cannot find ContentTypeReader for
Microsoft.Xna.Framework.Content.Pipeline.ExternalReference`1[Microsoft.Xna.Framework.Content.Pipeline.Graphics.Texture2DContent].
at
Microsoft.Xna.Framework.Content.ContentTypeReaderManager.GetTypeReader(Type
targetType, ContentReader contentReader) at
Microsoft.Xna.Framework.Content.ContentTypeReaderManager.GetTypeReader(Type
targetType) at
Microsoft.Xna.Framework.Content.ReflectiveReaderMemberHelper..ctor(ContentTypeReaderManager
manager, FieldInfo fieldInfo, PropertyInfo propertyInfo, Type
memberType, Boolean canWrite) at
Microsoft.Xna.Framework.Content.ReflectiveReaderMemberHelper.TryCreate(ContentTypeReaderManager
manager, Type declaringType, FieldInfo fieldInfo) at
Microsoft.Xna.Framework.Content.ReflectiveReader1.Initialize(ContentTypeReaderManager
manager) at
Microsoft.Xna.Framework.Content.ContentTypeReaderManager.ReadTypeManifest(Int32
typeCount, ContentReader contentReader) at
Microsoft.Xna.Framework.Content.ContentReader.ReadHeader() at
Microsoft.Xna.Framework.Content.ContentReader.ReadAsset[T]() at
Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[T](String
assetName, Action1 recordDisposableObject) at
Microsoft.Xna.Framework.Content.ContentManager.Load[T](String
assetName) at TiledTest.Game1.LoadContent() in C:\My
Documents\Dropbox\Visual Studio
Projects\TiledTest\TiledTest\TiledTest\Game1.cs:line 51 at
Microsoft.Xna.Framework.Game.Initialize() at
TiledTest.Game1.Initialize() in C:\My Documents\Dropbox\Visual Studio
Projects\TiledTest\TiledTest\TiledTest\Game1.cs:line 39 at
Microsoft.Xna.Framework.Game.RunGame(Boolean useBlockingRun) at
Microsoft.Xna.Framework.Game.Run() at
TiledTest.Program.Main(String[] args) in C:\My
Documents\Dropbox\Visual Studio
Projects\TiledTest\TiledTest\TiledTest\Program.cs:line 15
This is all incredibly frustrating as the demo doesn't appear to have any special linking properties. The TiledLib I am utilizing is from Nick Gravelyn, and can be found here: https://bitbucket.org/nickgravelyn/tiledlib.
The demo it comes with works fine, and yet in recreating I always run into this error.