[Mono-dev] Coroutines, Monoco, and why we love them.
Lucas Meijer
lucas at lucasmeijer.com
Fri Nov 21 12:55:36 EST 2008
Hi,
At the Unite conference, I was delighted to meet up with Miguel, as that
would let me underline the parts of mono (existing or nonexisting) that
are very important to our mono using projects. He asked me if I could
write a message to mono-dev explaining not only privately why these
things are important to us.
We're working on a multiplayer MMO game. The backend is written in c#
running on mono, the client uses Unity3D, whose scripting is powered by
mono.
Eariler in the project the plan was to write the backend in Stackless
Python, because of its coroutines support. I was thrilled to find Tomi
Valkeinen's MonoCo. (http://www.bat.org/~tomba/monoco.html) (Thanks
Tomi!). MonoCo brings stackless style coroutines to mono, and this made
us switch to mono.
Monoco makes my life a lot easier. Often monoco or stackless python are
mistaken for a solution to multithreaded problems. It is not. I consider
it an elegant implementation of asynchronous programming. Imagine this
piece of backend code asking a player what move to make:
//excuse the c# <> pseudocode mix.
public class ImaginaryBackend
{
public void NeverEndingGameLoop()
{
while (players.Count > 0)
{
PlayerAction pa =
players[playerIndex].AskWhatToDo(timeout=20sec);
//this call has blocked our "microthread'
//the returned message is immidiately available on the
next line of code.
if (!pa.Validate()) pa = new EndTurnPlayerAction();
pa.Execute();
}
}
}
where the meat is in the "AskWhatToDo" method call. Somewhere in this
methodcall, a request packet gets sent to our player, with some
questionID. this microthread then gets put "in the fridge". while the
packet is travelling trough a series of tubes, my other microthreads are
happily running. When the player responds (tagged with the same
questionID), the network reading code sees that there is a microthread
in the fridge, that is waiting for an answer to the question that this
incoming message is answering. So it "requeues" the microthread, and
supplies it the incoming message.
So I can program as if the call were a blocking call, but the behaviour
does not block my (most likely only) thread. my other code can keep on
running. Basically it's an alternative to a state machine.
Another example is in scripting behaviour for computer controlled agents
in a game:
public class AnnoyingDragon
{
public void MicroThreadEntryPoint()
{
WalkTo(bridge);
Open(bridge);
WalkTo(castle);
Open(castledoor);
PutOnFire(wholefreakincastle);
}
}
With each of these calls "blocking" the microthread untill the action
has actually completed.. The non-coroutines solution to this problem
would look like:
public class AnnoyingDragon
{
public void Update()
{
DragonState mystate = DragonState.Idle;
switch (mystate)
{
case DragonState.Nothing:
mystate = DragonState.WalkingToBridge;
break;
case DragonState.WalkingToBridge:
if (!closeEnoughTo(bridge)) return MoveMoreTowards(bridge);
bridge.Open();
mystate = DragonState.OpeningBridge:
break;
case DragonState.OpeningBridge:
if (!Bridge.FullyOpen()) return Wait();
mystate = DragonState.WalkingToCastle;
case DragonState.WalkingToCastle:
if (!closeEnoughTo(castle)) return MoveMoreTowards(castle);
castledoor.Open();
mystate = DragonState.OpeningCastleDoor;
break;
case DragonState.OpeningCastleDoor:
if (!castledoor.FullyOpen()) return Wait();
mystate = DragonState.PuttingWholeFreakinCastleOnFire;
break;
case DragonState.PuttingWholeFreakinCastleOnFire:
if (!EverythingBurning(castle)) return ThrowMoreFire();
exit;
}
}
so the coroutine approach basically allows us to store the "state" of an
object on the callstack, instead of having to remember it "myself" from
one frame to the next. Granted the state machine example here could
probably be written a bit more elegantly, and I am not showing the code
inside the methods that the coroutine variant is calling, but I hope
this example illustrate the convenience coroutines bring to people who
would otherwise have to resort to a state machine to deal with their
asynchronous programming problems.
Please realize I'm not saying that the current implementation of MonoCo
is good (or bad). I'm just looking at it from a user's point of view. I
know next to nothing about the internals of the VM.
Other elements that are very important to our game development
activities, are being able to use the visual studio debugger on a mono
process. I just cannot wait until that beta starts. We type all our
code in Visual Studio. I hope I'm not stepping on any toes here, but I
feel it's just such an enormous productivity enhancement, it feels quite
silly to write anything more than a hello world program in a text editor
that has no understanding of the programming language you're typing into
it. We love visual studio, and I cannot wait until I can use its
debugging facilities to replace my printf based debugging.
We're extremely happy that the Unity3D folks (an engine I can highly
recommend), chose Mono as their scripting provider, as it allows us to
run the same code on the clientside and the serverside of our game.
(pathfinding, collision detection, etc etc). having it in the same
languages saves a lot of dev time, and bug tracking time.
Phew, this turned into a long post. Anyway, this was a message from a
very big Mono fan. Thanks to all the people that have made it to what it
is today, Mono is one of the best things that have happened to our game
development process in quite some time.
Bye, Lucas
PS Excuse a possible doublepost. Email weirdness in full swing.
-- Game Development Blog: http://lucasmeijer.com/blog
More information about the Mono-devel-list
mailing list