So my Save / Load system uses XML.
It has been working without problems so far, but now one of my testers has crash when trying to load game or starting a new game. Error seems to be in XML Serialization for some reason…
Here’s my XML Handler
namespace MyGame
{
public class SaveHandler
{
public static void SaveData(object IClass, string filename)
{
StreamWriter writer = null;
try
{
XmlSerializer xmlSerializer = new XmlSerializer((IClass.GetType()));
writer = new StreamWriter(filename);
xmlSerializer.Serialize(writer, IClass);
}
finally
{
if (writer != null)
writer.Close();
writer = null;
}
}
}
public class LoadHandler<T>
{
public static Type type;
public LoadHandler()
{
type = typeof(T);
}
public T LoadData(string filename)
{
if (!System.IO.File.Exists(filename))
{ return default(T); }
T result;
XmlSerializer xmlserializer = new XmlSerializer(type);
FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
try
{ result = (T)xmlserializer.Deserialize(fs); }
catch
{ return default(T); }
fs.Close();
return result;
}
}
}
The error says
Unhandled Exception: System.InvalidOperationException:
There was an error reflecting type 'MyGame.SavedGame'. ---> System.InvalidOperationException:
There was an error reflection field 'PlayerPos'. ---> System.InvalidOperationException:
There was an error reflecting type 'Microsoft.Xna.Framework.Vector2'. ---> System.IO.FileLoadException:
Could not load file or assemply 'System.Runtime.Serialization, Version=4.0.0.0,
Culture=neutral, PiublicKeyToken=dsf2q34sdaf231' or one of its dependencies.
Not enough storage is available to process this command. (Exception from HRESULT: 0x800700008)
at System.ModuleHandle.ResolveType(RuntimeModule module, Int32 typeToken, IntPtr* typeInstArgs, Int32 typeInstCount, IntPtr* methodInstArgs, Int32 methodInstCount, 0projectHandleOnStackType)
I did read it, but I didn’t understand why that would be a problem.
Now I asked my tester to send picture of RAM usage, and the game takes whopping 1.7GB of RAM… On my computer it uses around 110-130MB.
The game doesn’t use all of his RAM, and there is still lots of free space on hard disk, but something is definitely wrong.
How can this be, what can cause that amount of RAM usage?
First I load my main Content: Textures, Font, Sound Effects and Music.
Then I create another thread to loop through levels, save data and settings, which are XML files. On my Main Thread I show loading screen.
I also create Textures with rendertargets out of my tile based levels, but do that only once in the beginning, and create recolors of some Textures during loading.
After I have loaded all of my content I run Garbage Collector manually.
I don’t think GC is relevant here, it won’t just randomly not collect 1.5gb of data, it’s most likely a problem wiht loading/saving data (I believe)
There seams to be a memory leak somewhere else. Stuff that will never be collected by the gc. Try to recreate the steps your friends took to get the crash and debug the program with the .net memory profiler which is provided by Visual studio
My friend just starts the game and selects New Game (or Continue) and it crashes. I’m not 100% sure but I think that he had crashes few times before even the Main Menu loaded up, that was before I moved XML Loading to another thread.
I don’t have access to his laptop and he doesn’t have Visual Studio, but here’s Memory Usage test on my computer: Memory Test result
Edit: The game currently uses MonoGame 3.5, and its DesktopGL Project. My friend has Windows 10, and should have OpenAL / .NET Framework 4.5.1 installed.
You’ll have to show us some sample XML file and some of the code you use to process that XML file. My first guess is something to do with culture differences if the other user has a different language.
Cultural difference sounds highly possible reason, my friend is big anime / Japan fan and (probably) has Japanese settings on the computer.
I use Path.Combine to build paths to access XML files, and when loading Content I use //FolderName//Filename.extension
Saving / Loading data is handled with XML Serialization seen in my first post.
The data is loaded with
LoadHandler<Settings> loadedSettings = new LoadHandler<Settings>();
settings = loadedSettings.LoadData(filePath);
XML file
<?xml version="1.0" encoding="utf-8"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ScreenWidth>1920</ScreenWidth>
<ScreenHeight>1080</ScreenHeight>
<WindowIsFullscreen>false</WindowIsFullscreen>
<WindowIsBorderless>false</WindowIsBorderless>
<Music>0.7</Music>
<SFX>1</SFX>
</Settings>
Creatures / Monsters / NPCs etc use Homemade animation tool that allows me to create animations without having to depend on pixel perfect sheets.
<?xml version="1.0" encoding="utf-8"?>
<animation name="npc_idle_animation" looping="True" length="3">
<frame ID="0" frameDuration="8" frameX="0" frameY="0" frameWidth="16" frameHeight="19" texture="npc_idle_sheet.png" />
<frame ID="1" frameDuration="8" frameX="16" frameY="0" frameWidth="16" frameHeight="19" texture="npc_idle_sheet.png" />
<frame ID="2" frameDuration="8" frameX="32" frameY="0" frameWidth="16" frameHeight="19" texture="npc_idle_sheet.png" />
</animation>
The data is loaded with
XDocument doc = XDocument.Load(filePath);
var header = doc.Element("animation");
foreach (var attrib in header.Attributes())
{
switch (attrib.Name.ToString())
{
case "name": name = attrib.Value; break;
case "looping": looping = attrib.Value == "true"; break;
case "length":
{
int dummy = 0;
if (int.TryParse(attrib.Value, out dummy))
{ frames = new List<int, Frame>(); }
break;
}
}
}
var xframes = doc.Descendants("frame");
foreach (var xframe in xframes)
{
AnimationFrame newFrame = new AnimationFrame();
foreach (var attrib in xframe.Attributes())
{
int parsed = 0;
int.TryParse(attrib.Value, out parsed);
switch (attrib.Name.ToString())
{
case "ID": newFrame.Index = parsed; break;
case "frameDuration": newFrame.Duration = parsed; break;
case "frameX": newFrame.X = parsed; break;
case "frameY": newFrame.Y = parsed; break;
case "frameWidth": newFrame.Width = parsed; break;
case "frameHeight": newFrame.Height = parsed; break;
case "texture": { newFrame.TextureFileName = attrib.Value; break; }
}
}
frames.Add(newFrame);
}
Level Collision data looks like this, and is loaded similarly to animations
Any time you are writing or reading numbers, use CultureInfo.InvariantCulture. This will use a consistent formatting regardless of the region setting on your PC. The original post showed the error to come from deserializing a Vector2 which contains two floating-point numbers. These are the number types that usually cause these errors across different regions. Many European regions use a comma as a decimal separator whereas most other regions use a period as the decimal separator.
Try setting your PC to a different region, such as en-US, and load your game. This should help show where issues with parsing of XML files are located.