In order for this to proceed, though, I naturally had to test to make sure the client would work on other machines. Also I wanted to run the servers on another machine as well. Everything worked fine together on my Dev box... but you know how that is.
So I copied a build of the game client to my daughter's computer (i.e. the Stainless Steel Brat). I double clicked it and... nothing. No log file generated either. I had actually figured it wouldn't work, as I didn't think that the XNA redist had been installed on her machine, but it was a good test.
I don't actually want the XNA redist to be a needed step as I plan to convert to MonoGame when practicable. But I did need the DLLs. So I used another machine that I knew didn't have the XNA redist, and added DLLs one by one until it worked.
In order to distribute a minimum XNA game (no avatars, built-in networking or other superfluous stuff) you need:
- Microsoft.Xna.Framework.dll
- Microsoft.Xna.Framework.Game.dll
- Microsoft.Xna.Framework.GamerServices.dll
- Microsoft.Xna.Framework.Graphics.dll
- Microsoft.Xna.Framework.Input.Touch.dll
- Microsoft.Xna.Framework.Net.dll
- Microsoft.Xna.Framework.Video.dll
I tried not including the GamerServices and Touch, but clearly there is a dependency somewhere; it simply won't work without them.
So, that made the client work. I had much the same problem with the server (i.e. it wouldn't start at all). This was a little more troublesome as the server includes MonoGame in order to use all the handy 3D utility classes (Vector3, etc) and to load the verticies of models in order to set up the physics.
After some testing it turned out that Lua.dll (I am currently using Lua as the scripting language) has a dependency on the VC 2010 runtime. After installing that, zing, the server worked.
There were a few other minor problems with directory paths and other sundry items, though none as irritating as simply failing to start.
The whole process did, however, reveal that my logging was hopelessly inadequate and a bit of a mess.
The server processes are meant to be run from a command line, or as a service. But normally they run in a sort of default UI shell that displays console-like output and some other handy info (lists of connections to the server, etc).
The logging to disk and display to the UI was completely separate. For the logging I used the very nice Object Guy Logging Framework. Thorough but simple, I have really liked using it. On the other hand, for the screen display I was just buffering up some text that the UI process polled, added to a text area and then cleared. Very sloppy, just something I threw together when assembling things.
I decided that this wouldn't cut it any more.
In the logging framework, I was using what is called a CompositeLogger. Basically this is just a Logger that can include other Logger classes. So if you called 'SomeCompositeLogger.LogDebug("Hello");', every child logger would execute .LogDebug("Hello");' The Framework includes all sorts of loggers, file, event log, TCP, SMTP, standard output, etc.
I had already included logging to the console along with file logging, left back from when I was running the servers strictly as command-line processes. At first I reasoned that I'd just capture the standard out stream and redirect it to my window somehow.
That proved a bit more involved than I wanted to get, however. Fortunately looking at the framework's source, another way presented itself.
The logger for logging to the console was a TextWriterLogger. Basically this is a logger that accepts a System.IO.TextWriter object (an abstract class inherited by StreamWriter, HttpWriter, StringWriter, etc).
private TextWriterLogger() : base() { } ////// Create a new instance of TextWriterLogger. /// /// A TextWriter to write LogEntries. public TextWriterLogger(TextWriter aTextWriter) : this() { TextWriter = aTextWriter; } ////// Utility for creating a logger that writes to the System.Console. /// ///A new TextWriterLogger. public static TextWriterLogger NewConsoleLogger() { return new TextWriterLogger(Console.Out); }
'Ho ho', I said to myself, I'll just add a TextWriterLogger using a StringWriter and everything logged will also be written to a string (ok, a StringBuilder as it turns out). Then the UI or whatever can write that to the screen, much like it does not.
So it was that easy in the end. Now what is written to the log is also exactly what is written to the screen; no messy different output handing. During the process I rationalized a great many things. In many places it was outputting text to the display but not logging, or logging information and not showing the same data to the display. Now it is much more thorough, and just as much to the point the code is aesthetically much better for it.
No comments:
Post a Comment