since installing CrashReporter.net, I noticed that I haven’t really been storing Mysterious Space’s version information correctly. well, or not in a way that Windows really understands. as a result, my reports from CrashReporter.NET keep reporting version “0.0.0.0”.
I wanted to fix this.
and that lead down an exciting rabbit hole!
…
SO: in my code, I used to have a few variables, like this:
public const int VERSION = "0.7.5"; public const int RELEASE_YEAR = 2015; public const int RELEASE_MONTH = 5; public const int RELEASE_DAY = 24;
a single place for me to keep all the version-related info, that I always made sure to update when compiling for release.
at first I thought I wanted a way to get that VERSION into whatever field Windows and CrashReporter.NET were looking at, but I quickly realized this was backwards; what I REALLY wanted was for VERSION to be populated from the field that Windows and CrashReporter.NET look at: Windows keeps a bunch of application metadata in the “assembly”, including version number, copyright date… all that stuff you see when you right-click an executable and get its properties, and you can’t set that from a variable!
you can find all that stuff in your project’s AssemblyInfo.cs file, which has lines like this:
[assembly: AssemblyTitle("Mysterious Space")]
and this:
[assembly: AssemblyVersion("0.0.0.0")] [assembly: AssemblyFileVersion("0.0.0.0")]
hey, those look like version numbers!
but what’s the difference between the two?
honestly, it’s all still a little confusing to me, but the details mostly seems to matter only when you’re building a library, or something; less so when building a stand-alone application. you can read all about it on StackOverflow.
but I also learned that there’s an AssmeblyInformationVersion, and THIS one is intended for display to users, and is listed in your file properties as “Product version”, which sounded EXACTLY like what I wanted, so: in goes:
[assembly:AssemblyInformationVersion("0.7.5")]
(it’s perhaps worth mentioning that CrashReporter.NET looks to the AssemblyVersion, so I made sure to set that, as well. though P.S. it irks me that I can’t #define VERSION "0.7.5"
and then use that in both places, like I would be able to in C++. I want all the things in place! oh, god, did I just express a longing for C++? sorry, sorry. that was a mistake! :P)
at this point, I still wasn’t sure how I was going to get at this version string in the code, but something else was on my mind: “can I add my release date here, as well?”
I COULD have left it in as those const
variables, but that bugged me, somehow. I want all this stuff in ONE PLACE, so that when I go to edit, say, the version string, I KNOW I’ll see the date, too, and so be sure to update it accordingly.
unfortunately, there isn’t a built-in field for storing a release date. fortunately, you can add ANYTHING YOUR HEART DESIRES through the use of custom assembly attributes.
you can google around for how to implement those, but here’s what I came up with for Mysterious Space:
using System; namespace MysteriousSpace { [AttributeUsage(AttributeTargets.Assembly)] class BuildDateAttribute: Attribute { public int Year { get; private set; } public int Month { get; private set; } public int Day { get; private set; } public BuildDateAttribute(int year, int month, int day) { Year = year; Month = month; Day = day; } } }
which allows me to add, in my AssemblyInfo.cs file:
[assembly:BuildDate(2015, 5, 24)]
awesome!
but NOW: how do we get at that information from within the game code? for example, I display the game’s version and release date in the lower-left of the title menu.
well, the AssemblyInformationalVersion is easy to get at:
string version = Application.ProductVersion;
(you’ll need to be using System.Windows.Forms;
to get the Application object.)
getting at custom attributes is a bit trickier, though, and again, in the interest of keeping all this stuff in one place, I created yet another class:
using System.Reflection; using System.Windows.Forms; namespace MysteriousSpace { public class BuildInfo { public string Version { get; private set; } public int Year { get; private set; } public int Month { get; private set; } public int Day { get; private set; } public BuildInfo() { Version = Application.ProductVersion; BuildDateAttribute[] attributes = (BuildDateAttribute[])Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(BuildDateAttribute), false); Year = attributes[0].Year; Month = attributes[0].Month; Day = attributes[0].Day; } } }
excellent.
now I can just make a new instance of BuildInfo, and I’m good to go. since I’m using MonoGame, I added a public static
variable on my main Game
object (which I creatively called TheGame
), and initialize it before calling Run()
. this way, it’s available anywhere:
string releaseDate = TheGame.Build.Year + "-" + TheGame.Build.Month + "-" + TheGame.Build.Day;
ta-da! at last!
as it always is with code, there are many possible solutions to the same problem. I happened to feel strongly that all this information should be in one place, and that lead to the addition of two new classes to my code! overkill? *shrugs* I think it’s cool, and I learned how to do some new stuff. I’m’a call that a win 😛