LO's Sharp Devs

C# , XNA Game Development Blog. Intentionally more than only for game authors but for C# programmers in general.

Moje zdjęcie
Nazwa:
Lokalizacja: Poland

środa, 8 października 2008

[Coding] Dealing with multithread

Today's thing that may be little tricky for a newbie developer, causing sometimes a bunch of frustration - I mean dealing with access to main-thread-only objects [MTOOs] (like windows and (suggested) files) form other threads.

Using BackgroundWorker.OnProgress seems to work fine, and does not require writing synchronization code. But what with other threads or accessing MTOOs directly from OnWork method?
Solution is fairy well designed by .Net designers and simple but little troblesome to implement. When in need to display some text for example in "textarea" one could just write:
form1.textLog.AppendText("some text");

But this would just throw InvalidOperationException. What do then?
One must use delegates. So first of all - code that will be synchronized shall be placed into separate method. There are two possible ways to do, because of delegate required:
1. Use one of the existing delegates (EventHandlers) and create method's arguments to fit it.
2. Write your own simple delegate to pass required set of arguments with it.
Sample declaration may look like:
public delegate void DebugMessageInvocation(string message, ErrorSeverity severity);

And then just delegated method:
private void WriteLog(string message, ErrorSeverity severity)
{
  form1.textLog.AppendText(message);
}

And we just call the function instead:
WriteLog("some text", ErrorSeverity.Info);

Of course this will not fix the problem yet, nor it explain why did we used delegate parametrization. The answer is System.Control.Invoke() method. It's supposed to execute given (by delegate) method in context of the main thread! Yes - it is what we were looking for. And why did we required delegate. Now it can be implemented in two ways: making delegate call directly from "main" code, or encapsulating all the logic inside delegate function using some simple "tricks":
private void WriteLog(string message, ErrorSeverity severity)
{
  if (form1.textLog.InvokeRequired) // yes, we are checking if we are already in the main thread, very nice thing :)
  {
    form1.textLog.AppendText(message); // safe, we are already in main thread
  }
  else
  {
    form1.textLog.Invoke(new DebugMessageInvocation(WriteLog),
      new object[] { message, severity }); // [1]
  }
}

Eventually one may negate condition and put all the logic further in the method body - preferred way of doing things.
What is going on up there? I think that only required explanation is for line [1]. Is what happen in the way:
1. We are building array of all arguments passed to the method - we are responsible for right order etc.
2. We create back delegate for the very same method we are in
3. We order System.Control to invoke this delegate in the main thread context and pass to this given array of parameters (will be unwrapped on the way).
And that's all - when function is invoked, it just passes to the main branch and does what it was supposed to do.

One important thing to add: I was talking about Invoke being executed into the main thread, what is not essentially the truth. In fact both using controls require and invoking code results with is to be in the same thread that when control was created. While majority of controls is being created in the main thread this works fine. So I strictly recommend to create controls from inside the main thread - and the best solution for that is to use... Invocation again (using a main form control for that)!

Have nice time. Hope this will be something new and interesting for some devs :D

wtorek, 7 października 2008

[Coding] Debug Design

Designing basic debug channel.

Downloads:

Binaries: SharpDevs.Debug.zip

What I would like to cover first is having a standard debug routines. My intention was to have easy access to debug stream from anywhere in the code and centralized control of it's flow. Therefore I'd created that library I'm using in all other projects I'm creating. A singleton solution is obvious enough to use it for this purpose. So, therefore basic usage of the lib is just logging debug almost like using standard .Net routines:

SharpDevs.Debugging.DebugLogsSink.Instance.Log("Some debug message", SharpDevs.Debugging.ErrorSeverity.Debug);

SharpDevs.Debugging.DebugLogsSink.Instance.Log("Some debug message");


After importing SharpDevs.Debugging namespace it can be even more consistent. ErrorSeverity enumeration has several levels of seriousness (numbering from less to more serious):


  • Debug
  • Info, Info1, Info2, Info3
  • Warning
  • Error
  • Critical



I think that most of them are self explanatory, but only having several info* may raise some doubts. I intended them to setting different, not necessarily debug informations but also other messages that might be usable by central message flow to control whole application or to cover multi thread messages. Here is how debug sink is being used:

DebugLogsSink.Instance.MainLogOutput += new InvocationOfSeverityString(Instance_MainLogOutput);


And then inside Instance_MainLogOutput() method serve all coming messages. It's important that event handling is used here, so there may be set several different "readers" - each one dealing with different type of messages, for example:

private void ServeCriticalMessages(string text, ErrorSeverity severity)
{
if (severity == ErrorSeverity.Critical)
{
ShowErrorMessage(text, FormatCritical);
WriteMessageToFile(severity);
Application.Exit(); // in XNA eventually: myGame.Exit();
}
}


Such logging to file is common enough behavior that I decided this to be implemented in Sink. To start logging simply call:

DebugLogsSink.Instance.WriteDebugToFile(".\\debug.txt");


And to finish:

DebugLogsSink.Instance.FinishDebugFile();


There are several options that control how log is written:

MinimumFileWriteSeverity
- decides minimum severity of messages that will be written to the log.
InsertDebugHeader - if TRUE - header with date & time will be added on the beggining.

Such usage is quite obvious in general. But doing system that was build with several singletoned subsystems, and have expecting more that could be attached to the system as plugins, I'd decided to add some additional functionality:

All debugged objects (intentionaly singletons, but it shall also work for base classes of big trees of inheritance) shall implement IDebugLog interface:

public interface IDebugLog
{
/// <summary>
/// Returned pure debug messages
/// </summary>
event InvocationOfString LogDebugMessage;

/// <summary>
/// Returned Debug/error messages
/// </summary>
event InvocationOfSeverityString LogErrorMessage;
}


Therefeore only thing to start debugging it is to add simple line:
DebugLogsSink.Instance.AppendLogOutput(someSubsystem);

Of course that class shall implement working with these vents by implementing some OnDebugMessage() methods, but it's left for class creator on how he would deal with it.

To stop tracking subsystems simply call:

DebugLogsSink.Instance.Clear();

... that will detach all of them.

Library is free of use for noncommercial users. For commercial ones - I't easy enough to write some similar by themselves so I do not think they will come to buy it anyway :)

czwartek, 2 października 2008

Game Creation Environment - Introduction

What I'm going to design first? I decided to describe rather advanced project - a complete development solution for creating, testing and localizing computer games. I would like to cover all areas that had came or will came to my mind, that could help with that.

I'm sure that describing all at once or at first is not possible. So I will be publishing rather scratches of ideas, differencing them with [tag] in the title. I'm going to describe stuff rather than coding it, but in the future, when vision is full and clear - who knows? But I'm sure that some [Coding] stuff will appear presenting what I think might be interesting for a C# developer.

What are main parts that I have already in mind?

Testing Server  - a mostly database server, running some bugtracking dedicated software, dividing access for various types of internal and external users (for beta tests), accessible by Web Services. It shall also be integrated by development software that can show bugs found in PARTICULAR areas of what they do.
Testing Module - used by testers or open-beta testers. Used to log bugs to database. It shall integrate with game somehow, so it can provide the base navigation data (on what is actual state of the game) to tester to precisely describe area of error.
Development Environment - where devs are doing their job. Divided into several modules, possibly gathered in one development studio. I'm not covering here details development of particular base content things like models, graphics, sounds etc. - but all possible and reasonable logic information than can be kept in the game and tested.

I'm going to describe these modules in following posts, as soon as I finish writing them. Updates to exiting modules will be posted as new posts, as soon as changes are required or new ideas developed.

Blog Introduction

At first - a warm welcome to everyone lost enough to visit my just starting blog :)

I would like to apologize for possible language mistakes - English is not my mother tongue, so if you could correct me - just do it - preferably in a comment that I could erase after fixing issue to keep comments clean.

I'm going to present here my developing ideas as I do not have time to write or configure any Nuke soft of my web. And I'm very eager to discuss these things with others. Just write comments, sent me infos or other.

Main projects page is located here: http://www.sharpdevs.com, but at the moment is not active. Probably I' will start publishing things from the begging of 2009.