Jump to content
  • Advertisement
  • entries
    109
  • comments
    175
  • views
    117904

About this blog

This journal is approved by the Get A Life Commitee. I swear.

Entries in this blog

 

Video game source code

This is more or less a reminder, as most of you already know this stuff. Here is a (incomplete) list of games whose source code is available, together with links (or, when not available, information on how to retrieve the source code). The origine of this entry can be found on my own personnal French blog.

Gish, Cryptic Sea.
Aquaria, Ryan Gordon (via mercurial)
Quake III, id Software (direct zip d/l)
Quake II, id Software (direct zip d/l).
Quake, id Software (direct zip d/l)
0 A.D., Wildfire Games.
Rise of the Triad, 3DRealms
Ryzom, Nevrax (stopped business a few years ago ; the game is now supported by another company)
Seven Kingdoms, Enlight.
Arx Fatalis, Arkane Studio
Return to Castle Wolfentstein single player and multiplayer, id Software (sirect zip d/l).
MechCommander 2, Microsoft
No One Lives Forever 2, Sierra : toolkit contains the source code of the game
Wolfenstein: Ennemy Territory
Duke Nukem 3D, 3DRealms
Lugaru, Wolfire Games
Penumbra - Overture, Frictional Games, via git
Alien Swarm, on steam (the source code is available in the SDK (URL = steam://install/513 ; you might need to install Steam to get it)
Allegiance, Microsoft

This is not (by far) a complete list. Some old games have been removed (mostly Doom-generation and before), some other are missing (Allegiance ; anybody have a link on this one ? It seems it disapeared from MS Reasearch downloads ; Hexen, Heven II and Heretic: cannot find a reliable download site; The Operative - NOLF : went AWOL a few years back, the Wayback machine doesn't help much ; and so on).

Do not assume that this post is not connected to my previous entries. There are evil forces in this world you can't know anything about, and I'm in a deal with some of them.

Emmanuel Deloget

Emmanuel Deloget

 

Text RPG update (post n100 in this journal!!11one)

Nothing much interesting. I was testing StarUML - the open source UML modeler - at work, so I decided to create a class diagram of the RPG rule system (read: what I've implemented so far).

Click on the image for full resolution.



The StarUML C++ reverse engineering tool is one of the best I ever used. The class diagram is rather easy to construct - but the window organisation of StarUML needs a big screen (this is not really a problem since you have a big screen, isn't it?).

I have to test the C++ code generation to see whether it can be used in a production environment.

Anyway, it's an impressive product!

Emmanuel Deloget

Emmanuel Deloget

 

Beginning game programming

Disclaimer: this is full of typos and grammar errors. I don't have much time to correct these right now, so I guess I'm going to correct them later...

OK, you decided to pick up game programming (and maybe MMO programming), and you want an advice about what language / API / tools you should use. As always, the thread starts with a "is Blah Blah Diese Plus Plus language good enough for game programming ?" or "Can I redo Oblivion with Bum Bum API ?" and usually ends in a language or API war, with zaelots of both camps coming with nebulous and misleading arguments (such as "langage Yuk is widely used in the industry, so it's the best one one one lol", with an obligatory "you must give a try to Lisp/Haskell/brainf**k, it rocks!").

Don't let the others tell you what you want to learn.

There are a lot of programming languages, some of them are widely known and rather hard to understand, while some others are easier to pich up but less known. My goal is not to list them - just have a look to the gdnet forums, you'll hear about most of them in less than a day (C++, C#, python, C, (Lisp, Haskell, Brainf**k), some Basic flavor, and so on; oh, I forgot Java, and the classic HAV1).

In this post my goal is to help you to choose a language - and to put some rationale behind the choice. Then, i'll try to give some advices about API that you might use.

Choosing a language
This is tricky. If you never programmed anything, then it will be The Hard Part. Why? Because hey, you probably don't have a clue about how a program works! If you ask the question here, you'll get answere ranging from "learn C++" to "learn C++" (with "teh Fruny exception" - he'll say "learn Python", or something like that). These are, IMHO, bad advices, because these languages are way too complex for a beginner. C++ requires years to be mastered, uses concepts that are hard to grab (for example, pointers), and both C++ and python are using a programming paradigm2 which looks simple (object-oriented often look simple) but which is, in fact very difficult to understand.

I believe you should learn a interpreted procedural language.

Why ?

First, by using an procedural language you'll be able to spot the flow of your program - meaning that the language will allow you to think your program as a list of actions that will be executed one after one. It is easier to get a mental representation of such kind of behavior.

Second, using an interpreted language will allow you to debug your code easily - because you'll be able to modify/run your program more easily than a compiled program. You'll be able to progress by trial and error - trial and error is a good progression method, especially if it is backed by a good book.

Of course, you need a decent IDE3 - something that will allow you to think to your program and not to the magic that happens behind the scene. While none is simple enough to be understood in a couple if minutes by a newcomer, you'll be able to find some of them that are easier to manage.

The first tool I'd advocate is MS QBasic. Yes. It is old (it came with MS DOS 5.0), works in console mode, and looks odd. But BASIC flavors are easy to understand, and this one is a good one. The problem is that it is not distributed by MS anymore. Powerload may still have it, but the legality of this page is questionable.

Another (far less questionable) solution is to use lua. Lua is a cross plateform interpreter, so you'll be able to use it on yout *nix box (young padawan). You'll be able to find an IDE for Windows (and even a compiler - thus, when you'll be more confident with your skills, you'll be able to release softwares! [smile])

Finally, python can be used as a procedural language, despite the fact that it is an object oriented programming language. You can use the interactive interpreter rather easily, but it will still be less convenient than a full blown IDE.

If you don't find the IDE that suits your need, you are still able to use a text editor (for example, PSPad) and integrate the interpreter you want with this editor (will not be easy, but it can be done).

Now, you are ready to learn how to program.

I want to maek a leet game!
Now, you need to learn an API. But which one will you choose? There are just too many of them! Oh, wait, there is only 2 main API (OpenGL and DirectX), maybe 3 if you count the Win32 GDI, so the choice is not that difficult, right?

The short answer is no. In fact, you have more than 2 choices: you have plenty of them, because what you want to do is to use a graphic engine, not to use a graphic API.

If you are still in the learning process, trying to build your own engine is rather pointless. Of course, you may be able to do it, but it is a huge task and while you do it, you don't do your game! Moreover, I bet that you won't be able to finish your engine - because your design is bad, because coding it is boring, and so on. There are tons of reasons to not finish a graphic engine.

BTW, I often see the utterly pointless discussion entitled "should I use DX or OpenGL?". The answer is: use whatever you want - it is up to you to pick up what you think is better. The worst answer I ever heard (and I continue to ear it very often) is "use both; put the renderer in a DLL and load it at runtime". This is pure nonsense. Unless you are able to prove me that there is an absolute need for this, don't do this (and yes: only one renderer will work at a given time).

On the contrary, if you use a pre-made engine, you'll be able to do your game directly. It means that you'll be able to concentrate on the game design and on the gameplay, which is a rather good thing [smile].

Now, what engine should you use? Well, it depends on your knowledge and on your experience. A 3D engine requires some linear algebra knowledge. A 2D engine is far simpler, but is more limited (of course). Anyway, since you are supposed to be a beginner, I suggest that you stick to a 2D graphic engine for an important reason: 3D games tends to be bigger than 2D games in terms of content - which means that they are also bigger in terms of code to manage this content.

Conclusion
The most important thing to remember is to keep it simple (but not stupid). A simple language in a simple IDE, simple tools and simple libraries. Once you'll master these, you'll be able to puch forward and to learn new things - learning a new language is far easier if you already know one, despite what everybody will tell you, because you acquired the basic knowledge of what a program is and how it works.

The other important thing to remember is to be patient - John Carmack himself had to learn before he was able to do these highly technical games.

The last thing to remember is that the others advices won't replace experience. They can help, of course, and it is still a good idea to ask for advices.

I hope I was able to help you :)



(1) HAV is the acronym of "HTML And Variables". This is a Hockey MMORPG programming language, and most of us (including programming beasts like Fruny or Washu) are still unable to use it correctly, so I suggest you to forget this at this stage.

(2) Most languages comes with a programming paradigm - for example, C#, C++ or Java are supposed to be used as object-oriented programming languages, Lisp is a functional language and C is a procedural language. Anyway, the programming paradigm is rarely enforced by the language itself (you can do procedural programming with C# or object-oriented programming with C).

(3) Integrated Development Environment. An IDE typically let you type code in an editor and integrates the tools that will allow you to run and debug this code.

Emmanuel Deloget

Emmanuel Deloget

 

Managing pipelines in C++

A few days ago, I released version 0.4 of my little security library. This version allows the creation of function pipeline using a simple syntax. If R is the return type of the pipeline and A is its argument type, then


pipeline p = stage(f1) | f2 | f3 | ... | fn;


Will construct a pipeline containing n stages.

The implementation is based on a limited version of boost.function as well as on simple expression templates (because we basically construct a representation of the pipeline function without evaluating it). The first function in the pipeline must be stageified - all the subsequent ones are automatically stageified by using operator|(pipeline_stage, F).

Pipeline stages are a strange beast. The pipeline_stage class has two specialization: the base one, which accept two callable F1 and F2 (functors that inherit std::unary_function or function pointers) and the limited one where F2 is nulltype. Using these two specialization I'm able to build an arbitrarly complex pipeline_stage:


// ps == pipeline_stage
typeid(stage(f1)|f2|f3|f4)).name() == typeif(ps,F3>,F4>,F5>).name()


Some code has been added recently to deal with the reconstruction of pipeline stages (stage(f1) constructs ps, so stage(f1) | stage(f2) would naturally construct ps,ps > ; this has been modified so that this expression constructs a ps object).

Version 0.4 has some small issues (does not compiles on Windows ; next version will ; pipeline p = stage(f1) cannot be written, due to the lack of a constructor in pipeline), but it works quite well in practice. I added adapters for the cryptographers and the encoders to enable the construction of encryption/decryption pipelines).

You can download the zlib-licenced code -ekogen-v0.4"]from my French speaking blog (scroll to the bottom up to the "annexes" part).

And if you like reading C++, this library will please you - nothing too advanced, everything can be read easily, although I make heavy use of templates.

Enjoy !

Emmanuel Deloget

Emmanuel Deloget

 

It's about frustration...

Granted, I rarely play games. As a direct consequence to this, there are lots of things I'm not good at when playing: I am slow at typing specific keys, I don't see patterns that have been added to please hardcode gamers (you know, things like "if you find a hidden way to a hidden place, then you'll find good stuff at this place").

And since I'm the proud father of a 15 month baby, I have limited time to play anyway, so there is little chance for me to enhance my playing skills (not to mention that I'm not sure that I really want to enhance them).

But mind you, I play. Mostly single-player RPGs or RTS - I tend to prefer the former. I've been long time adept of pen & paper role playing games, and when I believe a CRPG is to be tested, I buy it. Over the time, my collection has become quite interesting :


Baldur's gate I & II
TES 1: Arena
TES 2: Daggerfall
TES 3: Morrowind
TES 4: Oblivion
Neverwinter Nights 1
Return to the Temple of Element Evil
Ultima Undeworld I & II
Dragon Age: Origins
The Witcher 1 & 2


I believe I have some other CRPG lying around - and this is not counting adventure games (Raymon, I still have my copy of Morning Wrath) or hack-n-slash with a RPG twist. I just love them.

When I play, I tend to follow a simple pattern: when possible, I set the difficulty to the lowest level (I cannot afford to play the same section of the game again and again), I try to be always higher level than needed (to minimize the risks of restarting the same sequence again and again because I'm too weak to overcome the challenge) and I try to follow the base story as much as I can (in sandbox-like games such as the TES series, I try to avoid side quests unless they offer a very good reward or unless I need to level more in order to fight a big boss). The goal is simple: since they are games whose duration lie between 30 and 150 hours, and since I can play at most 4-6 hours per week, I must make sure that these hours are not lost doing things that are unnecessary. I'm looking for fun, not completeness.

Most of the time this simple strategy works fine. In fact, until recently, it worked each time I played - and it was fun.

I think what I find of interest in a CRPG is not the challenge. I don't mind challenge - especially when it fits the story - but I prefer the discovery of the story (this is why I'm not going to spend much time in a MMORPG: the story is secondary in these games). What I really dislike is everything that can frustrate me of this pleasure.

And this is here I wanted to come. I cannot understand why recent CRPGs are more akin to create moments of frustration than older ones. I remember I spent 30 minutes to one hour to understand how Morrowind's Shrine of Azura worked (my fault ; I don't remember why it took so much time, but I beleive I misread something at some point) - but I can afford to lost 30 minutes here and there. I also remember a few particular frustration moments in this game or that game but I was able to overcome the difficulty (sometimes by changing my way of doing something). After all, I have saves to allow me to start again later if I'm a bit stuck.

Take Obivion for instance: the choice made by designers to level the monsters as you level is just plain silly. Let me play as I want to play, damn it! But then, most of the time the difficulty during fights only happen on random encounters, so you just have to reload the latest save and you're done.

Then comes DA:O. If you play the whole game in "Easy" difficulty (or whatever it's named), then the whole game will be quite kind with you (you will die a few time - the challenge does not disapear) - that is, until you get to the final part of the game. Then you'll have to start and start again (and again, and again, and again) the same stupid fights because suddenly the game difficulty changed drastically. After 6 hours full of restarting again and again the same fight, I uninstalled the game with the following verdict: "that was a good game, too bad the game designers went kind of stupid at the end".

And more rencently, I decided to go for a ride with The Witcher 2. And frankly, this is a good game. Except that I'm stuck in a stupid village, trying to rescue this stupid bard, with this stupid "please, no blood" attitude. Which mean "you have to fight with your bare hands and if you lose, you die" (how stupid: they are allowed to kill me, but I can't?).

Dear Red Projeckt game designer, if you ever read this: this is just plain silly. I set the difficulty level to the lowest possible value because I'm not good at gaming. I only have my keyboard. And you ask me to punch a guy in the face with the following conditions:

if he touches me 3 to 4 times, I'm dead
I need to punch them 6 to 8 times to k.o. one guy
there are two of them (or more, depending on the dialog) in a row
I cannot save between the fights (meaning that if I die before I k.o. the last one, I must redo everythign, including the lengthy dialog at the beginning)


Not only the conditions are unfair but they are also directly responsible for the frustration moment. That and the (damn "I don't play on a console, you freak" stupid) fight system


wait for one to two second. The tempo is just here so that you cannot rely on the system to know when you're going to be active. In other words, it's here to artificially increase the difficulty.
press the key (one of QSDZ) that appear on the screen. you have less than (I'd say) 300ms to do that, and you must press the key only once (which means: go away, you little guy with disabilities, we don't want you to play) . At this point, if you fail, you might take a severe hit and loose 1/3 of your life. If you success then...
one time out of three? (random: it can be 3 times in a row) you have to press another key in - guestimate - (300 - the time you used to press the first key) millisecond. If you fail, you might take a severe hit again. Remeber that you have to press the first key once or you second keypress will directly be interpreted as a wrong key - and you'll take a hit.
If you succeed, then once in a while you will punch the stupid guy in front of you. A bit. Enough for him to scream "my nails!" or something like that.


Having two fights in a row using this system is just plain impossible for many people, including me. Now, if I manage to do a perfect on the first fight (I did this one - out of 20 tries), you still have to defeat the second guy. If you fail, you start over - and over - and over - and over... Repeat ad nauseam.

So here is my verdict, after 3 hours of play: "it's a good game, too bad it's not meant to be played". Uninstall (bonus: frees a few GB on the drive), sell DVD, and insult designers (during the frustration rampage).

The frustration is doubled, because it hurts to buy a 50EUR game just to see that a stupid game design decision prevent you to have any kind of pleasure once you hit that stupid village. That's 2 or 3 hours of game if you decided to let the cinematic play as I did.

Red Projekt guys, this is a failure - and an upseting one.

--
Bonus: count the number of time I wrote the word "stupid" and win a bonus cookie!

Emmanuel Deloget

Emmanuel Deloget

 

Asio C++ library: it gets nearly everything right

Bur nearly everything doesn't mean everything. For those who believe I'm talking about boost.asio, you're pretty much right as Asio has been incorporated in Boost. However, it exists as a non-Boost library, so I will continue to speak about Asio as Asio, and not boost.asio (there are a few differences, for example the namespace where the declarations are stored, but it's mostly the same thing).

So, Asio is supposed to be a well designed library. When it comes to generic AIO (asynchronous I/O), I tend to agree to that statement. When it comes to the network part, I protest : there are obvious design errors, and they lead to real design issue in th client code.

Let'sbegin by the first, obvious design limitation: the definition of socket types. There are two problems with this one:

* sockets are non-copiable. This is lame: I want to copy my sockets! I can transfer a file descriptor to another thread, but I can't transfer a socket to another thread - unless I store a pointer to it in a shared_ptr. Wow. I need to do a dynamic allocation to be able to use a socket in multiple threads?

No, guys. Sockets needs to be copiable. First, they are supposed to be lightweight objects (we're speaking of an object with only a few properties). Second, some well-known server architecture patterns mandates this (you accept the socket in the main thread, and yo pass it to another thread).

If you can't make it copiable, make it movable (that's going to be super-easy with the move constructors in C++1x, but it can be implemented today on th model of auto_ptr), or allow for the creation of weak socket (something along the line of weak_ptr ; this is bad: no one owns the socket and this is as well a design issue. You shall always know who is owning what).

* bad interface design. Both synchronous ans asynchronous behaviors in the same interface? that's an Error. Either you do synchronous operations (read, write, readfrom, writeto) or you do async operations. There is a good reason for that: it's not the same thing, it's not the same use. Of course, one can mix both types of operations in the same application, using the same socket - in which case he'd better instantiate an async_socket from his socket or the other way round. Two different behaviors in the same interface, this is a clear violation of the single responsibility principle. Bad, bad, bad.

Let's continue with another annoying design issue: symbol names. On most occasions, Asio decided to keep the names that have been used for years in the network programming world in order to mimic a known interface and soften the learning curve. So we have an acceptor class, which accept() stream sockets. But on the other hand, Asio decided to renew the vocabulary of the network programmers by introducing new terms. We no longer gethostbyname(), we resolve a host name.

The latter is correct by all means: you generalize, so you provide a distinct name that explains the generalized concept and is semantically correct. The former is problematic: you use a name with a strong meaning, and you reuse it to do something different - although it's not very different. There is another problem that comes with name reuse : you can't provide an abstract meaning to your class, and that forces you to be very concrete. At the same time, your class is central to the design, and many people uses it. So you have something which is both concrete and stable (per the meaning given by R.C Martin: a stable package/class is a package/class on which many other package/class depends). And this is not a good thing.

I'm sure you all have issues with th design of a library you use. Feel free to share them !

Emmanuel Deloget

Emmanuel Deloget

 

Encapsulation For Openssl Rsa/Blowfish In C++

Now, that's not a wonderful piece of code, filled with incredible meta-programming snippets. But it's a piece of code that's usable and - more importantly - that can be modified to suit your needs.

So let me introduce you a small part of a large library en devenir. This code encapsulates the blowfish symetric cipher and the RSA asymetric cipher from OpenSSL. As of today (version 0.2), only Linux is supported, but a port to Windows is planned as soon as I get my own PC back. As a bonus, a modern C++ implementation (little endian) of the SHA256 hashing algorithm is provided as well (used together with RSA private encryption, this allows you to sign messages ; which is kind of cool).

If you are interested by the code, just remember the following:
you're not going to see a lot of comments ; this shall not be needed (my code is not that horrible). a large part of the code uses templated policies. whenever I can, I play with the standard library. While waiting C++1x, this code uses a small portion of the TR1 (tr1::uint32_t and tr1::uint64_t). You finally have the possibility to watch some code of mine in action! (ok, that's not really of interest).
Licensing information is not included yet. You can consider that this code is licensed under some kind of BSD-style licence (that is: do whatever with this code ; just remember I wrote it, but if you want to act like you are the original writer, you are allowed to do that).

Class list:
ekogen::security::rsa_cryptograph ekogen::security::rsa_key ekogen::security::rsa_public_key ekogen::security::blowfish_cryptograph ekogen::security::blowfish_key ekogen::security::xor_cryptograph ekogen::security::xor_key ekogen::security::default_rng ekogen::security::xor_rng ekogen::security::symetric_cryptographekogen::security::symetric_keyekogen::security::asymetric_cryptographekogen::security::asymetric_keyekogen::security::public_asymetric_keyekogen::encoder::base64 ekogen::encoder::hex ekogen::encoder::basic_encoder
Function list
ekogen::security::sha256(const unsigned char*, std::size_t, std::vector&);ekogen::security::sha256(const unsigned char*, std::size_t, E::output_type&, E)ekogen::security::sha256(Iterator, Iterator, std::vector&);ekogen::security::sha256(Iterator, Iterator, E::output_type&, E)
PDF Documents that explains part of the interface are in directory /doc. The build system is very rudimentary and shall be replaced by a more advanced one in a few days (probably cmake, since it works on all the platforme I target).

Details (in French, sorry) about the code can be found on my blog. For those who don't speak French, google makes a decent work at translating the text.

Happy... something !

Emmanuel Deloget

Emmanuel Deloget

 

I feel green !

That's pretty much how I feel today. Even if it was somewhat expected, this was still a surprise when I went to my cybercafe of choice in Stuttgart yesterday evening. So now, I'm a full staff member, complete with the staff color and the terrible power that comes with it (something that, I must admit, scares me).

I now need to change my behavior. When I was a regular user, I saw no problem in downrating. That's now something I see as odd - I would definitely expect a staff member to support a user that deserves it, but not to negatively judge a user that, from his own opinion, deserve it as well. So while I may continue to support you by giving you positive ratings, I will stop giving negative ratings.

I also need to stay in my own role, as a news editor. I'm not a moderator, and I have little knowledge about the system and so on. So I won't ban/suspend/warn/close thread/and so on. I'll post news. That's already a cool job, believe me [smile]. In fact, I don't want to act as a moderator, so I won't do it (unless it becomes one of my task; but as of today this is not what I want). The current mods do their job very very well, keeping the noise ratio very low.

I also wonder how I can make up some kind of Rejected News (not news that haven't been approved for whatever reason, but news that wouldn't be featured as gamedev.net news, mostly because they are not targeted to hobbyist game programmers).

Last thing I want, 10 million EUR. If you can help, please tell me [smile]

Emmanuel Deloget

Emmanuel Deloget

 

It's been a tough week

Yep, companones, it's been a tough week. I'm not speaking of work (that, too was a tough week, mostly because bash 2.3 and bash 4.1 do not work the same way, for unknown reasons (apparently, introducing breaking changes that deny the right to exist to myriads of old Makefiles is now called "bug fixing")).

I'm speaking of the next iteration of [ekogen] (the library name still evolves; see, I added brackets!) and it's upcoming addition : the network library. In my previous entry, I showed how Asio (and consequently boost.asio) is quite short when it comes to addressing some particular issue. The library has some design limitations that I find weird and unconvenient for many uses. So my goal is to address these limitations - and this cannot be done by pushing the proposed modifications to boost.asio, as such modification would break existing code (hey, bash team: existing code is precious). There are other C++ network libraries out there but none of them provide the level of abstraction provided by Asio so there is the place for yet another network library that do things correctly. This is the intent behind [ekogen]'s network component.

The technicalities of providing both synchronous and asynchronous operations is of limited interest to me (because it's well known since Steven's book on communication programming in the Unix environment). Sure, there are some platform specific issues to address (Windows Socket API version 2 does asynchronous operation differently from POSIX systems) but in the end ther's nothing complicated here.

On the architecture side, things are more complicated. Choices that seems unnatural are to be made (do I provide a tcp_server class? should sockets be copiable or movable ? and so on). Here are my observations :

* copying sockets is of limited use; most of the time, a socket copy is done to transfer its ownership to another thread - or at least another context of execution. It's far better to provide a socket with move semantics, as it's the best way to transfer an object to another context. C++0x makes move constructors and move operator= explicit (by using a rvalue reference). C++98 is akward wrt to the move semantic : the only movable object in the current standard is to be deprecated in the next standard (std::auto_ptr). But anyway, this can be done easily. It also gives a release() function for free (although I'm not sure this should be in the public interface). When real copying is to be used, a weak_socket can be constructed from the socket.

* both TCP servers and UDP servers are just wrappers around other, simple to use classes. A TCP server is made of a polling mechanism and a binded listening socket (the polling can be asynchronous, allowing the implementation of high performance servers in a single thread). This is simple to implement if you have access to both mechanism. It can be a little more cumbersome if you hide all that stuff in a tcp_server class (as Asio and most other network library does).

* naming is primordial for the success of a library: I don't want to hide the fact that I'm handling sockets, but I don't want to accept() incoming connections, as such accept() would necessarily imply an operation which is different from the original accept() in the BSD socket API. Same goes for listen(), select() (which might not even be implemented using select()) and so on. There is some reasearch to do to find clever equivalents that are both concise, clear and different from the original verbs.

* synchronous sockets are not asynchronous sockets: depending on the OS, they are not created using the same system calls and their use is vastly different. A distinction need to be made between UDP and TCP sockets as well, as UDP sockets gives me more informations than TCP sockets. It is possible to create a synchronous socket from an asynchronous one - the inverse is not always true. I should end up with something along the line of :


class stream_socket
{
stream_socket(const stream_socket&); // define only
stream_socket& operator)(const stream_socket&); // define only
public:
typedef implementation-defined native_socket_type;
typedef implementation-defined boolean_type;

stream_socket();
stream_socket(stream_socket&) throw(); // move ctor
stream_socket& operator=(stream_socket&) throw(); // move operator=
~stream_socket(); // does nothing is released

// properties and query
operator boolean_type (); // boolean_type converts to bool
weak_socket weak() const; // throws if (!s)

// operations
void read(...) const; // std::vector ?
void write(...) const; // std::vector ?
void shutdown_write() const; // = shutdown(SHUT_WR) (man 2 shutdown for further info)
void shutdown_read() const; // = shutdown(SHUT_RD)
void shutdown() const; // = shutdown(SHUT_RDWR)

private:
native_socket_type release() throw();
};

class async_stream_socket
{
async_stream_socket(const async_stream_socket&); // define only
async_stream_socket& operator)(const async_stream_socket&); // define only

public:
typedef implementation-defined native_socket_type;
typedef implementation-defined boolean_type;

async_stream_socket();
async_stream_socket(async_stream_socket&) throw(); // move ctor
async_stream_socket& operator=(async_stream_socket&) throw(); // move operator=
~async_stream_socket(); // throws if pending() ?

// properties and query
operator boolean_type (); // boolean_type converts to bool
weak_async_socket weak() const; // throws if (!s)
bool pending(); // socket is used in an async operation. Useful?

// operations
void read(...) const; // std::vector ?
void write(...) const; // std::vector ?
void shutdown_write() const; // = shutdown(SHUT_WR) (man 2 shutdown for further info)
void shutdown_read() const; // = shutdown(SHUT_RD)
void shutdown() const; // = shutdown(SHUT_RDWR)

weak_socket weak_sync(); // throws if (!s); returns a normal weak_socket

private:
native_socket_type release() throw();
};


This is a lot of code to provide only two operations (read and write, which are only partly defined here. Some buffer has to be provided, and I still have to decide whether a std::vector would do the trick - in which case this function shall be a template function).

Same goes for UDP sockets (datagram_socket and async_datagram_socket). Now, how do I want to create them? Using endpoints (endpoints and resolvers are two Asio features that I believe are very well designed - at least from a conceptual point of view; I haven't checked the full implementation yet), I can tie an IP address and a port number. Using a listener (name to be changed), I can bind this endpoint to a logical port and create an internal socket. Using a poller, I can use this listener to get new socket instances. So in the end, I never really create them - I get them from the poller (for an UDP server, there is no need to listen from the socket, but a poller can be of interest)

Polling also pose a (slightly limited) software design problem. I have no guarantee that polling will return me only one event, so I must find a way to handle possibly multiple incomming events. Moreover, polling gives me two information: the socket which is to be processed and the event to process (is the socket to be read ? write ?). This information is available from all pollers that are available on any platforms (*nix have epoll (linux), kqueue (BSD, OS X), select (all), /dev/poll (Solaris ; I can ignore that - not my target) ; Windows has select, WSAAsyncSelect). How do I handle these events ? I have two ways :

1/ feed a vector of (socket, event) to the poller. This is quite ugly, might need loads of reallocation if the vector grows, and so on. If I can avoid that, I won't be very sad.

2/ give the poller a tack to execute on the reception of an event. Asio provided the idea of strands, which are (as their name says) "mini-threads". This is not really true. In fact, strands are deferred tasks that are ran when a promise(*) is set (the promise itself is the result of the operation, be it a synchronous or an asynchronous one). If you already have a good knowledge of the upcoming C++0x standard, you can view them as generalized futures : futures only gives you back the promise when strands allows you to do some processing on the promise. Any word play aside, this is a promising solution.

Since (1) looks bad and (2) looks far better, the obvious choice it to implemement (2). For a synchronous poll, this is easy : I just wait for the poll to complete and then I execute my function (which is likely to be a std::function (or std::tr1::function if no C++0x features are available). For asynchonous operations, this is trickier. Windows has completion ports but that only allows me to execute a free function or a static member function, and the select operation is not implemented this way (keep away from WSAAsyncSelect - this is a WTF function that sends a message to a HWND when the operation completes). *nix has no system wide primitive that looks like completion ports so we have to fake them by creating a thread, waiting for the asynchronous function to end, then execute the operation.

In short, asynchronous poll mandates the creation of a short-lived thread in this design. The upcoming standard thread library offers std::thread which provides exactly what we need (I have re-implemented the basic mechanism of std::thread in [ekogen] to compile the library on platforms where it is not yet available, such as g++ prior to version 4.4 and all versions of Visual Studio (std::thread is not part of the tiny part of C++0x that has been implemented in Visual C++ 2010 ; we're looking forward for the next version...).

With such a design, the code for a TCP server shall look like (remember some names are to be changed):


using namespace ekogen::network;

struct event_receiver
{
listener& m_l;

event_receiver(listener& l) : m_l(l) { }

// not sure of the prototype yet
void operator()(poller& p, const socket& s, const event_type& event)
{
if (m_l.get_socket() == s)
{
// add the socket to the poller list
poller.add(m_l.incomming().get_socket(), event_type::read | event_type::write);
}
else
{
if (event & event_type::read)
{ s.read(... }
else if (event & event_type::write)
{ s.write(... }
}
}
};

int main()
{
listener my_listener(endpoint(ipv6_address::loopback, port::any));
poller my_poller;
my_poller.add(my_listener.get_socket(), event_type::read);
my_poller.wait_for_events(event_receiver(my_listener));
}


A TCP server class would just encapsulate a small part of this code. This can still be of interest for simple applications - but then, a tcp_async_server shall be provided, as well as a mechanism to filter the incoming connections and so on.

The code for an async TCP server is quite similar - and the code for the UDP server is not very far way from that (UDP servers does not need to listen() and accept() incoming connections ; you just need to poll the binded socket (and even polling is not really needed, as recvfrom() is a blocking call).

--
(*) don't worry if you're lost with these promise, future and strands ; I will publish something about this very subject in the coming weeks.

Emmanuel Deloget

Emmanuel Deloget

 

TCP, UDP, RakNet and reinventing the wheel

In my previous entries, I I spoke to you about a networked application I was creating. I used the TCP/IP protocol, and of course, the associated stream sockets, to build client/server applications. For some reasons, I had the feeling that the solution was limited in a number of ways: first, because of the way winsock works, I had to cheat about the fdset size. Moreover, since I can't know how much client will connect to my application, I was putting my server application in a continuous stress - becasue of the great number of opened sockets (the number of opened sockets in a TCP/IP cserver application is 1 + the number of connected clients). I finnaly decided that TCP was a pain - and I eventually switched to use UDP.

UDP/IP has its own problems though. The big one is that UDP/IP is an unreliable protocol - the packets may not be delivered to the destination application. As a consequence, I have to implement a ACK system - if I do not receive my ACK then I must keep sending the same data again.

UDP/IP is fairly simple to implement, since there is no select() and no accept(). You only need to call recvfrom() in order to receive your messages. sendto() is called to send a message? Instead of using these functions you can connect() your client to the server and then use send() and recv(). Moreover, a lot of network API are using UDP/IP as their base protocol. This is for example the case of RakNet and Hawk NL

HawkNL is a LGPL'ed piece of software - I can't use it for my purpose (GPL and LGPL products are free as in "I'm a virus license"). RakNet is a free product that can be used in a commercial project - although it is very game oriented. This is not very important, the main goal is to have a library that is already written and that has numerous interresting features such as encryption, cross platform support, and so on.

Another solution is to reinvent the wheel again - while this can be entertaining, it is really a bad idea. Consider this feature list:

easy to use C++ interface
cross platform
reliable communication
multithreading support
encryption
compression

Point 1 is OK by definition - the design give it to me. Point 2 is easy to do, but you must write two versions of your core - that encapsulate pure socket management. That's one day of work. Point 3 is also rather easy to obtain - ACK management is not very complex, but since you want it to work transparently (no, you really don't want to have this in the class interface), it has to be very good. One day of work for the implementation and test of this feature. Next point is more complex: multithreading support (for all your target platforms, remember point 2). This point has to be done very carefully and must be heavily tested before you can move on. That's at least one day of work again. Finally, point 5 is done by using an existing library like OpenSSL. Understand, integrate, test. If you are good at this game, you'll need more or less one day of work. Then another day is needed to implement and test compression.

That's at least 5 days of work. One week.

Now, downloading a known, reliable library will probably eat 15 minutes of your time. Designing then writing the C++ interface will need approximately one day, and you'll need another one to integrate the library in your application and to test everything. That's two days. That's 3 days I can spend to something else. For my boss, that's an price cut of nearly 1500 EUR, so he can know buy me a very good PC for the same price, and I'm happy (well... actually he won't do that).

RakNet also implement a lot of other very interresting features (NAT punch-through, multiple ways of handling the data that is sent or received, and so on). In a bunch of words: RakNet provides much more that I would be able to code in the time I'll need to integrate it in my project.

To conclude on this subject, I'm going to use RakNet as my networking API. Building a C++ encapsulation that will suit my needs is trivial (RakNet is already a C++ library, thus I only need to write an adaptor).

Emmanuel Deloget

Emmanuel Deloget

 

On a side note...

I modified my C# workshop project to

1) add big rooms from time to time
2) make bigger mazes (this one is 100x100).

It is not guaranteed to have only one path from two points in the maze, but it should be quite teh fun to solve:


click here for normal size

Good luck!

Code is here

Emmanuel Deloget

Emmanuel Deloget

 

Message handler management

In a previous entry, I explained how to use pointers to member functions in the design of a windowing system.Today, I applied this to another problem: network messaging.
In client-server mode, both the client and the server are sending messages to the other. It appeared to me that it was a good candidate for using a message map, and, of course, the corresponding message handlers. I'll explain everything from the server perspective - but the client is working the same way, and reuse the same code.
the client sends a message.
the server read the message from the client.Once he knows that the message is complete, he decodes it using a MessageCodec.
since he now know what is the exact message, he uses a handler manager that is similar to the one I already presented in order to hook to the right message handler.
The system is fairly simple, and it took only one hour to add it to my base code. Here is a small UML class diagram to show the different base classes:



Let's go and use an example: the client wants to log in the server. We defines the LoginMessageCodec:

class LoginMessageCodec : public MessageCodec
{
std::string mLogin;
std::string mPassword;

public:
LoginMessageCodec();
virtual ~LoginMessageCodec();

bool encode(std::vectorchar>& buffer);
bool decode(std::vectorchar>& buffer);

MessageCodec *clone();

void setLogin(const std::string& login) { mLogin = login; }
void setPassword(const std::string& passwd) { mPassword = passwd; }
const std::string& login() const { return mLogin; }
const std::string& password() const { return mPassword; }
};


The pseudo code from the client point of view is:

LoginMessageCodec login;
std::vector buffer;

login.setLogin("aaaaaa");
login.setPassword("bbbbb");
login.encode(buffer);
clientsocket.send(buffer);

From the server point of view, the pseudo-code is

LoginMessageCodec login;
std::vector buffer;

serversocket.recv(buffer);
login.decode(buffer);
mMessageDispatcher.dispatch(login, sender);

The message dispatcher pseudo-code is:

for each handler {
if (handler can handle the message) {
handler->call(message, sender);
}
}

The handler inherits from the MessageHandler class. This handler has an associated pointer to member and simply calls the member function. In the case of the login mesasge, I defined the LoginMessageHandler:

template class ReceiverType, class SenderType> class LoginMessageHandler
: public MessageHandler
{
public:
typedef void (ReceiverType::*LoginMethod)(SenderType sender,
const std::string& login,
const std::string& password);

private:
ReceiverType *mReceiver;
LoginMethod mMethod;

public:
LoginMessageHandler(ReceiverType *receiver, LoginMethod method)
: MessageDispatcherHandler(MessageCodecCodes::login),
mReceiver(receiver), mMethod(method)
{
}
virtual ~LoginMessageHandler() { }

virtual void call(MessageCodec *codec, SenderType sender)
{
LoginMessageCodec *logincodec;
logincodec = dynamic_cast(codec);
if (logincodec) {
((*mReceiver).*(mMethod))(sender, logincodec->login(), logincodec->password());
}
}
};

See the side note below for more informations about this dynamic_cast call. I created the login message handler instance using this call:

void Application::onLogin(ServerSocket& socket, const std::string& login, const std::string& password)
{
// verify user/password
}

void Application::initHandlers()
{
mMessageDispatcher.registerHandler(
new LoginMessageHandler(
this,
&Application::&onLogin));
}

dynamic_cast In our context, dynamic_cast can be used safely. It doesn't break the OCP [RM-1], nor it breaks the LSP [RM-2]. From a design point of view the login mesasge handler is meant to use a login message codec. The real magic of everything is not about the dispatching of the messages to member functions. It is about doing it safely, and having everything which is verified by the compiler. At the expense of using MessageHandler derived classes - which are rather simple tools - I implemented a reasonably secure, type-safe message handler system that is similar to the highly dangerous MFC message marshaler (seriously, have a look to the MFC message dispatcher (it is not difficult: create a message handler, adds a breakpoint, then step out the function and have a look to the infamous beast)). By defining the MessageDispatcher as a template, I also managed to create a highly reusable system.

Voila!

Any question or comment?

Emmanuel Deloget

Emmanuel Deloget

 

Some (somewhat crappy) code

Yes. Nothing related to our main activity, but here is some code to answer a question: how to know wether your window is partially/totally overlapped by another window. The code is actually quite - well, how to say that without being blunt? - stupid.


/* getwindowcoverage.c -- version 1, Feb. 15th, 2005

Copyright (C) 2005 Emmanuel Deloget

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

This license text is based upon the zlib license text.
*/

#include

typedef enum
{
NOCOVERAGE,
PARTIALCOVERAGE,
TOTALCOVERAGE
} WINDOWCOVERAGE;

WINDOWCOVERAGE GetWindowCoverage(HWND toplevel)
{
RECT wrect;
HRGN hrgnwin;
HRGN hrgnwincpy;
HWND hwnd;
HWND prevhwnd;
HRGN otherrgn;
RECT otherrect;
int rgntype;
int rescode;

::GetWindowRect(toplevel, &wrect);
hrgnwin = ::CreateRectRgn(wrect.left, wrect.top, wrect.right, wrect.bottom);
hrgnwincpy = ::CreateRectRgn(wrect.left, wrect.top, wrect.right, wrect.bottom);

prevhwnd = toplevel;
hwnd = ::GetNextWindow(prevhwnd, GW_HWNDPREV);
while (hwnd) {

if (::IsWindowVisible(hwnd)) {
::GetWindowRect(hwnd, &otherrect);
otherrgn = ::CreateRectRgn(otherrect.left, otherrect.top, otherrect.right, otherrect.bottom);
rgntype = ::CombineRgn(hrgnwin, hrgnwin, otherrgn, RGN_DIFF);
::DeleteObject(otherrgn);
if (rgntype == NULLREGION) {
::DeleteObject(hrgnwin);
::DeleteObject(hrgnwincpy);
return TOTALCOVERAGE;
}
}
prevhwnd = hwnd;
hwnd = ::GetNextWindow(prevhwnd, GW_HWNDPREV);
}

if (::EqualRgn(hrgnwin, hrgnwincpy)) {
rescode = NOCOVERAGE;
} else {
rescode = PARTIALCOVERAGE;
}

::DeleteObject(hrgnwin);
::DeleteObject(hrgnwincpy);

return rescode;
}




The pure Win32 code works correctly, but it only tests other top level windows. It means that it is more "is my application hidden by another application" function.

I already gave this code in a past gdnet thread (I believe it was during January).

Comments are welcome :)

Hope you like it ;)

Emmanuel Deloget

Emmanuel Deloget

 

"Somewhat type-safe " enums in C++

I don't know why I want to write this down, since it is a not-that-good solution to a known problem. Anyway, let's begin.

As you all know, C++ enumerations are not type safe. Let's have a look to a simple example:

enum Values
{
value0, value1, value2, value3, value4
};

Values myValue;

// .. later in the code ...
myValue = 100;

your compiler may issue a warning, but this code is still legal from a C++ standard point of view. The problem is that 100 is not a value of the enumeration.

As a consequence, I can't be sure that when I'll use myEnum its value will be one of the values I added to the enumeration - and guess what - this is a Bad Thing.

The solution I use is pretty simple: I encapsulate the enumeration in a class:

class Values
{
public:
enum Enum { value0, value1, value2, value3, value4 };
private:
Enum mInternal;
public:
Values() : mInternal(value0) { }
Values(const Enum& v) : mInternal(v) { }
Values(const Values& v) : mInternal(v.mInternal) { }
Values& operator=(const Values& v) { mInternal = v.mInternal; return *this; }
bool operator==(const Values& v) { return mInternal == v.mInternal; }
bool operator!=(const Values& v) { return mInternal != v.mInternal; }
Enum get() const { return mInternal; }
};

Now, I can write this code

Values v1(1); // -- ERROR !
Values v2(Values::value0); // valid
Values v3;
Values v4;
v3 = 1; // -- ERROR
v4 = Values::value0; // valid
if (v1 == 1) { } // -- ERROR
if (v2 == Values::value1) { } // valid

Of course, I am still able to setup a Values with an int - since there is no automatic cast, I will need an explicit cast

v1 = (Values::Enum)(1); // valid

The get() method is provided because there is no automatic conversion from the class to int - such conversion would break the == and != operators (because the compiler would try to convert my Values to an int when I write "v1 == 1"). Nevertheless, it can be useful to get the real enum value - for example:

switch (v1) // -- ERROR
{
// ...
}

switch (v1.get()) // valid
{
// ...
}


Cons
... Not much? Oh: you have to write the class. It takes more time than just writing the enum :)

Pros
A lot:
type-safetty (even if it is not total) is better than no type safety
this type-safety is hidden, since most of the usage of the variable won't change
since everything is inlined, the use of this class costs nothing
the explicit cast (to throw an int into the value) is more readable and less error prone than an implicit cast
writing Values::value0 is more readable than value0 alone
I can enrich the class to do a lot of things.


There are probably a lot of other advantages to this method - that's why I use it :)

Emmanuel Deloget

Emmanuel Deloget

 

Ooops. My title was too long.

The good thing about working for a car equipment maker that sells electronic products is that they want to show their talent every year at the CES. Since they do high-end car audio and navigation systems, the best way to present them is to put them in situation - I mean, in a car.

But if you show something at the CES, you'd better try to make something that everyone will remember. So there's not way you'll present your equipment in a normal car.

That why, at Alpine, they decided to tune cars a bit. The result if quite impressive - this is not the average rice boy tuning here.

For exemple, let's begin with a close-up:


clicky's biggar

We see the Alpine logo quite well, and it seems that this "thing" has huge wheels. That's quite true - although it's difficult to know the exact wheel size. Something around 24 or 26 inches.


clicky's biggar too

But changing the wheels doesn't make your car a nice one - you have to change something else also. Why not removing everything which was in the inside to make your own? After all, who needs to get more than one passenger in a car? Also, don't forget the sound system (remember, you're selling them).


clicky's biggar again


yar yar yar, clicky's biggar

Now, you may have forgot what was the car at the beginning - so it's still interesting to not remove some distinctive signs. After all, you have to tell your friends that you are richer than them, and that you can do whatever you want with your BMW SUV...


clicky's biggar. still smallar's than da car

Emmanuel Deloget

Emmanuel Deloget

 

News from the world outside

Yeah, there's a world outside gamedev.net, and most of the time we call it "the real world".

The work on the future "standard 3D game math library" (no name yet, but that's not very important for the moment; its current codename is math3d, thanks to Jon Watte) is going well. Lots of questions, lots of interresting proposals and points of view on the dedicated wiki. Very interresting.

About the PS3/X360 war, let's say something fun: the ability to plug an external player in the X360 is clean, because if Sony get the HD market, MS will be able to release a blu-ray player for the X360. I just wanted to make sure that everybody was aware of this. I continue with Toshiba, who just released the first HD-DVD player. Its price: something along the line of 500$ (according to the article), to be compared to the price of a blu-ray player (1000$). I guess that an external player for the X360 would cost less (MS plans for 250$).

Toshiba also released the first HD-DVD labtop. Sony just released the first blu-ray labtop. Price: 3000$ and more. Of course, at this price, you get the best of the best. Hell, no: Toshiba's HD is only a 100GB at 5400 rpm. Stupid.

Now that war in Iraq is officially "finished", the HD war begins. I believe this one will be rather funny to watch.

Emmanuel Deloget

Emmanuel Deloget

 

As promised....

Well, see below to read something which is barely related to the title of this post.

My coverage of the GCDC'07 is on the way. It should appear sometimes today, and contains a lot of things. I would like to thank both Drew (that forgave me my slowness) and Felix Kerger (Mirlix on GameDev.Net) who (unexpectedly) helped me during the conference. That was very nice from him.

I met another (French) GameDev.Net guy in Leipzig - but unfortunatly I have lost contact with him (mstly because my small brain have some problem to recall his name; I'm ashamed). That's really unfortunate because we had interesting talks about graphic engines, and I'd like to continue the discussions). So, please, guy, the next time you log on GDnet and read this journal entry, please, please, PM me! (or send me an email - I think I gave you have my business card; I hope).

Speaking of conferences, there is a good chance that I will attend both the Game Connection Lyon (a business related event, the meeting kind; I'll use this opportunity to speak to developers and publishers) and the Lyon GDC. I believe that both events are going to be teh cool.



In my latest post here (yeah, the one from saturday) I promised you a screenshot of the incredible XNA/C# Maze Generator. And this journal post is featuring said screenshot.

I believe it's the first screenshot I even feature in my journal...

But even cooler, I'm posting the code that allows you to play. There is no victory condition, and there is nothing such as a failure. It just display a maze, the minimap, and features you as an orange dot (that's a bit weird, I know... forgot to say that the shot features programmer art). Only on maze is generated for every run of the program - you'll have to quit and relauch to generate another maze. The software runs in windowed 800x600. You can change that in ex_MazeGame\MazeGame.cs - just modify these lines to whatever you want. For the screenshot, I used fullscreen 1680x1050.
private const int ScreenWidth = 800;
private const int ScreenHeight = 600;
private const bool fullScreen = false;There is not much polishing - it's really a bare example of what I could do with my Pawn library - whose latest code is also included in the archive. I'm planning to improve everything and to ultimately feature a fully functionnal game (and to include my in-progress D20 lib, which is also excerpted from the C# workshop).

Ok, time for the screeny. Click the image to get the real size one!


And time for the code: 20070910-GDnet_CS_WorkShop.rar!

Note: obviously, you'll need XNA Game Studio Express to build the code. Both the 1.0 and 1.0 refresh are able to build the project.



On a side note, I received a mail from Multi 3D Animation Limited, a 3D animation company located in Hong Kong. I don't know what to do with that, so I decided to give you th link. Feel free to visit them (although I'm sure sure about what to visit here...).

Multi 3D Animation Limited

Emmanuel Deloget

Emmanuel Deloget

 

Fr1st (GDC) pots!

Heading to the Lyon GDC
I finally managed to get in Lyon tonight, just in time for the beginning of the Lyon Game Developper Conference - an event I have waited for month and month. Organisation is not that simple: there are tons of people in Lyon these days, and I have only one hotel night booked (I might be able to book tomorrow night in the same hotel, but after that I'll have to move).

What will I do during this 2 day conference? Well, I will try to get contacts with industry leaders (hopefully, I'll also get some short interviews that will be posted here as podcasts). I will attend some highly technical sessions (the research sessions are looking good), I will spend some time with the game developpers around there.

My only probem is that I don't have a schedule yet - I don't know which session I can attend. Look: I'm supposed to go to the Serious Games Summit on monday. I will do that, of course, but what lectures will I miss? I don't know. That's a bit frustrating because I just can't organize myself. I guess I'll be able to get this information monday morning...

The program (for you):
As many podcast as I can record loads of lecture report pictures... and, of course, a bit of fun [smile]
So see you tomorrow for the ongoing Lyon GDC report!

Emmanuel Deloget

Emmanuel Deloget

 

ED phone home

Time to come back...
I don't have much time these days. After this Linux-based project, I have been hijacked by my boss in order to work on a software architecture project. It is rather refreshing to go back to UML and OO methodologies. Unfortunately, this is only a 3 month job and... it takes place in Paris - this is 800 km from my flat :( Anyway, the project is interesting, and while I'm in my hotel room, I can't do much but gaming/programming/reading (TV is so stupid...). I only have a crappy internet access (via my cell phone, a 9,6 kbit/s line. Ouch) so I can't really 'waste' my time on gamedev :)

Concerning UML, I also tried different softwares - in order to evaluate them. The best I found is Objecteering/UML - it is a bit expensive (3000EUR) but contains everything I need, from doc generation to requirement management via C++ code generation/reverse engineering. I Also tried Sparx System's Entreprise Architect, which is very good - and far less expensive ! - but it lacks some features I want (maybe I didn't see them, but spending 3 days to evaluate a $300 program is rather stupid when these 3 days costs nearly $1500...). Borland's Together and Embarcadero's Describe are also good modeling softwares (well, Together Architect is, but Describe's graphics are poor and its use is rather unintuitive).

There is a free Objecteering/UML edition (for personnal use). It works under Win32 and Linux. Good to know. You can download it here. It is the software I use to draw the nifty graphs below :)

But let's continue our main story.

... to our design
The last entry I wrote about the design of a 2D graphic engine is dated from 2005, february the 10th. It is rather old, and I believe I should refresh your mind about our goal and our current state.

First, our apparent goal is to design a 2D graphic engine. Such an engine is rather simple, with little functionalities, so our main goal is not to really design it but to see the design process in action. I try to give you hints and informations about how to correctly design a software.

What we've done so far
We defined two packages:


.

The sys packages contains our system classes - I will clear this in a subsequent post.

The engine package is the one we are studying. It contains the following classes:





We didn't describe the class with load of details for the moment. I just emphasized the creation/destruction of the engine (here is a simple sequence diagram which shows it in action)





... and the drawing operation (which is rather simple).





Conclusion
I promise you another release next week, with more informations about graphic elements and the sys package. Feel free to post your comments!

And now it's time to go to bed :)

Regards,

Emmanuel Deloget

Emmanuel Deloget

 

OK, let's begin serious things...

About my projects
I currently have tons of different projects in mind - at least 4 of them are related to game development. I already spoke about the Designer Wiki System (oh! DWS looks like a cool name...) in one or two threads so I won't speak a bout it here unless I make some major improvement to the PHP code. 2 out of the 3 other projects are still in the "early development stages" (usually you use this expression when you haven't done anything yet).

The last one is a 2D RPG (2D top view), and I'm currently designing the graphic engine. I won't speak about the game itself, and I won't speak about the gameplay - which is rather basic. I target both PC and mobile phone (I'd like to program a nokia - this may modify the gameplay because of the limitation of cell phones). You might think that I should use java to do it. I won't (I never used java. I bet a game should not be the first thing to do with a language that you don't master).

Anyway, I was speaking about the graphic engine design. Why should I begin with it? There are tons of other components in a game - sounds, gamerules, input and son on - and the graphic part is just one of them.

The point is that once the graphics are set up, I can show the game to others, and maybe I'll be able to hire artists to deal with sound and sprites. I'm not able to do my own sprites and I really need (well: I'll really need) an artist to help me. This will be easier when I'll get something to give him ideas about the game itself. Until then, I'll use free tiles/sprites which can be found all over the net.

About design
If we do some short UML design sequence, we have to ask ourselves the question: who is going to use the software we are currently doing? In my own case, this is simple: the game will do. Not me, not the user, only the game. When it comes to library design, this is the only answer you'll be able to do. Since human beeing are not able to read binary code and execute it in their mind, only a program can use a library (a package if we use UML terminology). That's really cool.

The difficulties begins when we ask the second question: what will the actor do with your software? In UML terminology, you deal with use case scenarii. This is typically the negliged design part, but I believe it is the more important. How can you design a library interface if you don't know what your library will have to do? This is not possible. Moreover, I'll bet you'll experience some really difficult design issue even if you barely know what should be the goal of your program.

When I say "my graphic lib will be able to display a 2D world and the player and 2D object sprite and monsters and cool graphical effects" then I said nothing interesting. The real "use case scenario" question is a bit more complex.

So, what are the use case scenarii? Let's examine some (like in: not all) of them:


the game needs to init and uninit the graphic library. This one was an easy one. It simply says that I'll have at least two entry points in my package: init() and uninit().
the game needs to display the static world. Wow. The big one. What does mean "displaying static the world?". Static is easy: this part of the world only deals with things that don't move. We'll have to admit that we still don't have a clue of what a world is. What this issue tells us is that a world is not really a graphical object - it is more a game object - and therefore is not a part of our library. We don't care about its internal representation for now - this may evolve during the design process.We have now another entry point: draw(worldtype world);
the game will need to display non static objects. Since static means 'which do not change during the game', we can understand that non static means 'which can be modified during the game'. A player's avatar is a non static game object. A door is also a non static object. There are tons of non static object in the game world, and this use case deals with their display. Again, we add one entry point to our package: draw(nonstaticobjecttype object).


Of course, there are numerous other "use cases". I'll probably give you all of them in a subsequent journal entry. In this (rather short) design, I can only display my world and display a player.

Before we'll stop considering the different use cases, we'll have to ask ourselve another important question: 'did I forget anything important?'. Of course we did. We forgot to ask ourselves numerous questions which are related to the design of our graphic library: who is responsible for doing the collision detection? If we need a per-pixel collision detection then the only subsystem who is able to do it is the graphic engine. If we only need a simple collision detection then we'll may be able to perform it outside of our package - this should be better.

What next?
The next point will be simple: we'll begin to design the different class we need, and - of course - we'll have to update our preliminary design. We'll need to have a look to the inside of a graphic system.

And?
Nothing else.

Emmanuel Deloget

Emmanuel Deloget

 

OK, I give up...

I mean, this GeForce FX 5600TX is just too lame, and I just can't accept that anymore. So I went to Staurn in the Stuttgart town center and I bought an AGP GeForce 7600 GS (the one by MSI, with 256MB DDR2; see here for the benchmark results). It was cheap, and it's supposed to be good enough - at leat it will help me to wait for the end of the year.

I also decided to buy a Xbox360 pad for my PC (because I plan to write an article about XNA input systems for my blog in a few weeks). I first wanted to get the normal one but it was unavailable - so I bought the cordless controller. Hopefully, I will get some fun with that.

Speaking of XNA, I decided to test my implementation of the C# project 1 in a XNA program (2d, top view). This proved to be tricky - not on the maze part, on the rendering part. I'm writing a series of XNA tutorials for ny blog, so I decided that I should use my own sprite library (pawn) and give it a test. My design is quite effective... for a C++ program. I'm pretty sure that a C# program should NOT be designed this way, but my knowledge of C# is still too limited and disallow me to write something better. Wait, it's not uterly slow. It's just not as fast as it could be.

The maze is implemented as a specific kind of tiled background layer. My library allows me to create a background with an unlimited number of layer (hello mister parralax scrolling). Layers have to inherit the IBackgroundLayer interface and are then free to do whatever they want. I have three implemented types of background layer:
* a "simple image" layer: an image which I directly copy to the position the user specified
* a scrolling image: an image in which you can freely scroll. It's up to the client application to describe the scrolling by specifying the visible area in this image.
* a tiled background: a background which is made of tiles (really?). This is the most interesting one in our case. It only implements how to scroll in the background and the high level rendering operations (ie choice of tiles you want to render and so on). Tile storage is implemented in a IBackgroundLayerTiledStorage interface (which inherit IBackgroundLayerStorage). The library contains a specialized implementation of this interface to load the data from a XML file. I then decided to store the maze in another implementation of this interface to see how it would work.

Well, from a design point of view it works great. From a speed point of view, it works not so bad - but there is so much dumb code executed that I'm sure I can do far better.

To ease the rendering and to limit the number of needed tiles, each cell of the maze is divided in four quadrants - topleft, topright, bottomleft, bottomright. As a result, I only have to deal with 12 tiles (13 if I use my "big rooms" feature while building the maze). Getting the corret tile for a particular quadrant in a particular cell was quite annoying. I finally got everything right (after, say, 1h or 2h of debug... :/).

Then I decided to implement "fog of war". As another background layer. Oh. This is inneficient (as some invisible tiles are drawn twice: once when I render the maze and once when I draw a solid black tile onto them). Given the fact that I just did something very similar (the maze), it only took me one hour to get it running. I also had to mess with the tiles but that was a bit eaier (now thT I had a working scheme).

Implementing the player movement was quite easy, but I had a stupid problem when I tried to update the fog of war w.r.t visited tiles. Well, each cell in the maze have a "bool visited" member - but the cell is a struct, meaning that anytime I got the cell struct from outside the maze, I got a copy of the struct - and not a reference. Well, a struct is a value type in C#. Changing it to a class fixed the problem (but forced me to do a bunch of new allocations, ok, that's not really a problem). Once I got that, everything was working.

I have a perfect maze which is displayed in an XNA program. I find this damn cool, given the fact that I started C# 3 month ago.

Last but not least, I decided to display a minimap of the maze in the top right corner of the screen. It was the easiest part: I just had to specify that the whole bakground was visible and that it should be drawn in a small rectangle in the top right corner. 1 minute later, I had my minimap.

Screenies on monday if you're nice ;)

And now I go back to home ('m again in a cybercafe) - and I'll give a try to these two brand new pieces of hardware.


Cya! (not at home, I mean)

Emmanuel Deloget

Emmanuel Deloget

 

GCDC day 1 - talks, talks, talks

Hello Everyone,

So the GCDC in Leipzig has begun, and believe me, it's quite good. Speakers are incredible and I have the feeling that quality is going to improve even more on day 2 and 3.

What did I attended on day 1? Well, because of some issue with the registration process (including the fact that press registration is actually done on the 1st floor while normal attendees are using another entrance), the first keynotes were over when I finally got my badge. But I was able to attend Doug Binks and Leigh Davies's talk about "practical multithreading for game performance". Doug revealed some details about the architecture of Crytek's CryEngine2, while Leigh gave very interesting information about how to design a game with multithreading in mind. "Better games through Usability" by Mario Wynards was about including usability in your game design process. That means a lot of usability testing (is that menu clear enough, and so on).

The "Life after WoW: MMO Games of 2008 and beyond" panel was both entertaining and refreshing. Key players in the field discussed what one should expect in coming MMOGs. We had people from Turbine, ZeniMax Online (I tried to get information about what they are doing right now, but that was not possible; damn... :)).

"Procedural texture generation theory and practice" was a talk by Dierk Chaos Ohlerich - from .theprodukt. .theprodukt initially come from the demo-scene, and several years ago they developped a procedural texture creation system which is quite powerfull (some points of it are similar to Allegorithmics's ProFX, but there are still some differences).

Stromfront and game industry veteran Don Glagow gave a talk about "what makes a game next gen". His huge game industry experience (you were too young when he started to make games) proved to be a key aspect of his talk, as he explained us what next-gen is about - it's not about technology, it's about user experience. That was truely The Talk of the Day.

"Interactive ray tracing in games" was amazing - expecially the real-time we had (not to mention the description of the base algorithm). Seing a Q4 map raytraced at 130 fps in a 800x600 window (maybe more) full with reflection and characters) was quite impressive.

And that was the end of the day for me - more precisely, the end of the talks. At this time I began to wander around, asking questions to exhibitors, getting people interested in gamedev.net and so on, orging contacts and so on. I ended the day by a dinner with Fred Hasson (TIGA, UK), Erik Robertson (Nordic Game Program, Sweden), Malte Berhmann (GAME, Germany), Eamon O'Reilly (EACEA, European Commission) as well as many more key European industry players.

I recorded a lot of talks, and made a lot of photos. Some of them might end up here, so stay tuned!

Emmanuel Deloget

Emmanuel Deloget

 

Databases, continued.

The main problem about databases lies in how one can fetch the data from the db. Simply put, storing the result of a SELECT statement is a pain.

Consider this simple table:

CREATE TABLE user (id INTEGER, login TEXT, password TEXT);


Now, a very simple select statement:

SELECT * FROM user;


The C++ typical code to deal with the result of this query is something along the line of:


if (teh_sql_api_exec_function("SELECT * FROM user;")) {
int rowcount = teh_sql_api_getrowcount();
int colcount = teh_sql_api_getcolcount();
for (int r=0; r for (int c=0; c std::string s = teh_sql_api_getstringvalue(c, r);
my_clever_query_object.setdata(c, r, s);
}
}
}

We just have lost the type of our different fields - our id is not more an integer, it is a string. You can avoid this by doing:

teh_sql_type t = teh_sql_api_getdatatype(c);
if (t == teh_sql_integer) {
int i = teh_sql_api_getintvalue(c, r);
my_clever_query_object.setintdata(c, r, i);
} else if (t == teh_sql_string) {
std:string s = teh_sql_api_getstringvalue(c, r);
my_clever_query_object.setstringdata(c, r, s);
} else {
// ... other cases
}

We now know the type of our objects but we can't say anymore that the code is generic. We need to have one method per SQL data type in my_clever_query_object (MySQL has many many different types). We also have to store the values in different places, because a string is not a date and an integer is not a float - C++ is bad at mixing oranges and apples. Thus, you'll need one container per type. It means that you'll have to cleverly store your informations in my_clever_query_object to be able to fetch them back when needed (you'll be able to rename it to my_uber_clever_query_object if you succeed).

There is obviously a better solution to this problem - and you already know its name: variants.

You can implement variant in numerous ways. Qt variant is a basic one - everything is done without using templates (at least, it was true in Qt3. I haven't checked this in Qt4). The Qt variant is very easy to use - and believe it or not but they use it to store database results from their SQL drivers, as shown here.

Another solution is to use typelists[1]. Both the Tiny Template Library variant and the boost.variant use this clever technique.

With variants, we are able to store any kind of data in a single container. Our code becomes:

teh_sql_type t = teh_sql_api_getdatatype(c);
my_cOOl_variant v;
if (t == teh_sql_integer) {
v = teh_sql_api_getintvalue(c, r);
} else if (t == teh_sql_string) {
v = teh_sql_api_getstringvalue(c, r);
} else {
// ... other cases
}
my_clever_query_object.setdata(c, r, v);

That's still a pita, but it's probably the best we can do in C++ :)



[1] typelists are described by A. Alexandrescu in his Modern C++ Design book.

Emmanuel Deloget

Emmanuel Deloget

 

Wow. An alien.

News 'n stuff
Customizing this journal is not so complex after all. It may be dangerous but not complex (I hope noone will try to misuse this great opportunity).

I added some links in a column on the right. I'm happy.


O_o
._.
;_;


My html skills are teh sux0rz. Like my notebook kebord.

Anyway, let's continue my "design course" (can I call this a course? I think it is obvious that my goal is not to design my 2D engine but to show how I design it, and why I design it this way).

Three components and one "little" deflection
I'm not sure deflection is the right word to use. Damn, I'm still unable to write correct english sentences (yeah, I know, I'm teh sux0rz too).

Last night I started to speak about design patterns. I made it clear that a game is made with data, algorithm, and a representation of these. Datas and algorithms can be abstracted as the model, and their representation is of course the view. We need the last one to complete the holy trinity: the controler. This is not difficult to locate it: the game input system is a very good candidate - and for now we'll stick with that.

You got it: this is one of the most famous mecanism: the model-view-controler, or MVC for those who are intimate with him. The MVC is well-known for its ability to be mapped to useful design patterns. Maybe we'll be able to study this in details in a subsequent journal entry (read: certainly not today, and maybe never. I'm dumbass ;)).

For those who don't know how the MVC works, let's define it using simple words. To a view is attached a controler. The controler is responsible for handling (for example) user input. In response to these inputs it modifies the model. In response to this modification, the model updates the attached views. You can learn more about MVC in [1].

MVC implies that the view knows how to represent the data. Since we are developping our graphic engine as a "component-on-the-shelf" (COTS, see [2]) we don't want the engine to know the model. Remember, We have discussed this issue in a previous journal entry. When we consider both predicates (the view knows the model and the graphic engine does not knows the model) we clearly see that the view component of the MVC must be split into two different components: the first one is the graphic engine itself. The second one is some kind of glue which will be able to transform a model into its representation.

The glue object interface may use the observer design pattern. In this pattern, the subject (the model in the MVC) sends an update() message to all its observers when it is modified. Let's do this more clearly using a pseudo C++ language:


class Controler
{
World w;
modifyWorld() { w.update(); }
public:
onUserInput() { modifyWorld(); }
};

class World
{
View v;
updateView() { v.draw(this); }
public:
update() { updateView(); }
};

class View
{
set gels;
GraphicEngine ge;

drawElement(GraphicElement gel)
{
ge.draw(gel);
}

public:
draw(World w)
{
updateGraphicElements(w);
for_each_element(gels, drawElement);
}
};


This pseudo codes shows how we can create a game which is independant from the view. If we want to modify the view (maybe adding a third dimension) then we only have to modify the view. This is of course not simple - it may need a lot of work. But it is far better than modifying the whole game!

What else?
I believe I have to speak about the GraphicEngine itself again... Yeah. I may do that...

References!
[1] Glenn E. Krasner and Stephen T. Pope. A cookbook for using the model-view controller user interface paradigm in Smalltalk-80. Journal of Object-Oriented Programming, 1(3):26-49, August/September 1988.
Some info can be found on wikipedia
[2] COTS are discussed at cmu.edu - they appear to name them "commercial-on-the-shelf" because of the nature of teh components themselves. For our purpose, what we want to create is really a COTS-like, not a real commercial component, hence my initial pick: "component-on-the-shef".

PS: it seems I can't remember how to create an anchor in da html. Let's do that next time...

Emmanuel Deloget

Emmanuel Deloget

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!