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

About this blog

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

Entries in this blog

 

The new GDnet design

The new design is cool but it broke everything in my journal. I removed the stylesheet (and I guess I have to heavily modify it to get something good enough for your eyes). Too bad. I liked my green theme a lot :/

Oh, I finally saw Casshern. Great movie. I suggest you to give it a look.

Emmanuel Deloget

Emmanuel Deloget

 

No technical informations :'(

Investigation
No French game developper contacted me. I guess that I have to explain why I asked this in order to get some contacts.
First, I don't want your money. I don't want to contact you in order to get a job in your team. I am quite pleased with my current job (software architect is somewhat cool, you know) and I'm not going to change now. Next, I'm not going to sell you something. I don't have any ubertool for you, and you won't buy my game ideas - because you already have tons of them.
The reason why I want to get in touch with you is that I'm trying to promote game development in the south of France. I'm just beginning this neverending quest, and I still don't know what to do. That's why I want to speak to professional game developpers. I just want to find some informations about their needs, their dreams, and maybe their gf but this has nothing to do with the subject.

Project management
Disclaimer I'm dreaming. I don't have enough time to implement such kind of software, but I find the concept rather cool. Last year, I thought to how I would do a design document. My idea was to use a wiki-based system with an automatic ordering mecanism associated to some PDF printing capabilities. Unfortunately, like every idea I have, I don't have time to write this (I did some experimentation with phpwiki, but nothing very advanced). Here is a requirement list for such kind of tool:
User is allowed to write formatted text
User is allowed to link documents to the text (images, MS Word documents, PDF, web links, ...)
Since it is a wiki, the user must be able to create links to the other pages he created

I thought to this during the past month, and I believe that such system can be easily extended to something more usable.
First, the design document is not restricted to a gameplay document. It should also defines what are the needed resources - and if it defines the resources, why don't it store them? As a consequence, I need to integrate a resource management system that would allow me to store different versions of the same resource. The versionning system could also be used to track down the changes in the design document itself.
Next, I can't see why I should be able to manage the resources and not the code itself. Thus it would be nice to integrate a sophisticated software configuration management tool (SCM) - a la Perforce - along with a bug list management tool.
I can now manage the design document and the source code. There is a missing step between these two items: the software specification document and the detailed software design. This is more tricky part, because it would be nice to be able to create and store UML diagrams. Of course, this could be done in an external software (er... writing an UML modeler using AJAX is probably not teh funnay).
Having both the source code and the software design document in a centralied place, I can link them to have a clear idea of what's have to be done. The only mising point here is how long it will take to implement a feature - thus we need a project management software. We'll be able to define teams, individuals, assign their roles, watch out their progress, and so on.
Everything centralized in a web browser. I guess the idea is cool, but I believe the development time of such a beast is something along the line of 1 to 2 years. Anyway, I also believe their is a market for this tool - as it specifically target software development teams (and not only game dev teams). Anybody wants to fund a company?

Time, lack of
This is not god. I tried to do an entry for Ravuya's contest, and I came up with a good idea, a good enough code base. But in the end, my worst enemy (time, the one which tries to kill you during your whole life, and which will win at the end) stopped me. I needed two more days, but I wasn't able to find them.

Work, loads of
The company I work for is actively hiring software engineers in the south of France - and in other areas of France as well. There are no game development relate jobs (but who knows...), but the company is a good one - it is fair and loyal to its employees (not like most of the service industry) and the projects are challenging. Contact me for further informations ;) (and yes, the south of France is really great).

Emmanuel Deloget

Emmanuel Deloget

 

French game developpers here?

I am trying to find some French members of our community who works in the game industry (even you, dear AP). If you are reading this, can you PM me please (I know, this would be difficult for an AP) or send me a mail to my private email address (the nospam address is logout atnospam free dotnospam fr. I know, it is weird. But I receive more than 100 spam message a day...)

C ya later, d00dz!

Emmanuel Deloget

Emmanuel Deloget

 

I think therefore I think

4e4
The 4e4 contest is now over, and the results has been posted. Every entry is just plain cool, in fact, because every single entry is a finished game, and that's damn important.
This contest was huge. More than
Now, Ravuya created another contest which, I believe, can be very fun. I'm going to participate, because 1) I like the idea, 2) the time frame is ok, 3) I have some code, developped for my tic-tac-toe last year that I need to reuse.

Teh funnay
I guess that I am the last guy on earth that discovered Pure Pwnage. This "Wow is a feeling" song is probably one of the biggest moment in my life. These guys are scary. Really.

I also had a look to the 4 first seasons of Buffy TVS, with the following idea in my mind: Joss Hates Buffy. Really. It is much more funny. I am going to buy the 3 last seasons (I already saw them numerous times, but, hey, there is so much enjoyment in season 6...)

I'm tired (I know you don't really care, but it is still true). And I am looking for a good looking girlfriend. If anyone has a sister he can rent, or something like that... Oh, sorry.

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

 

Scary story

As a pro software developper, I find copyrights very useful, as they don't legally allow you to copy my work. But what should you do if I go too far?

Read this horrible story about Mark Russinovitch(1) vs. Sony


(1) Mark Russinovitch, for those who don't know him, is a leading Windows Developper. He wrote Windows Internal, a comprehensive book about how Windows really works, and a fair numbers of advanced small programs (some of them are even used at Microsoft to diagnose some programs). He is teh c00l. Too bad he had been pwn3d by Sony :/

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

 

A follow up, databases, and more

The followup(1)
Nice to see that Radupvr is not going to leave right now, and will continue to update his journal. But it doesn't change anything to the behavior of those who bashed him. Believe it or not but I find user-bashing rather imature and childish. User bashers tend to think that they are the only ones who are right in the entire universe, and that their way of thinking is the best one can possibly have. They deny you the right to be wrong - in their opinion, but since their opinion should be Teh Opini0n... - or to think differently than they are - omg! j00 r not fol0win Teh Opini0n!!!!11.

They are responsible for a number of departure - or visible dismiss - here. Remember the dismiss of S1CA? Even if he came back, who can say that bashing S1CA was a good thing? Wasn't he helpfull?

I happen to believe that Radupvr, S1CA and a lot of others are far more important for this board than any lounge lizard user basher.

The more technical stuff
I nearly finished the SQLite driver code - it is not so bad, but I had a number of problems and it not completely tested (for example, I didn't test the transaction mode). Anyway, it is cool because I can now continue to program on my laptop or on my main PC. I must now do the PostgreSQL code - it will probably be easy too, since libpq is rather easy to use. I may give you the code once it will be ok (and if you want it, of course).

Now, I have to create my tables - they have to be simple enough to be understood by both SQLite and PostgreSQL.

I am not happy at all with my network code - maybe you can help me, guys - actually, I have two problems:
when I run my code in the vc++ debugger, the socket I creates have socket descriptor > 4096, while FDSET_SIZE is defined to 64. You have to change the definition of FDSET_SIZE (this is legal, so there is no real problem here... except that your fdsets are now very big and that select() performs poorly). I believe that this is a Microsoft trick to enforce the use of these asynchronous sockets in their Winsock API but this is something I can't do, because my network code has to be (nearly) portable - my server will run on a linux box.
I may have to manage a large number of connections anyway (I still don't know exactly, but it would not be a bad idea to target more than 1000 tcp/ip connections). I still don't know whether it is a good idea to use tcp/ip or not, because I don't know how an ip stack will peform with such a high number of incomming, permanent connections. I may be able to redesign the communication process to use udp/ip - maybe... :S
If you, guys, have some ideas about this subjects, let me know! I guess I have to visit the Multiplayer and Network Programming forum ^_^.

Other project, other thoughts: I reread [SB-1](2) and I though to their terrain engine, and more precisely to their LOD system. In Scott Bilas's paper, no indication is given concerning the terrain LOD system they use, and maybe the reason is that they don't use a terrain LOD system. Tis would be crazy, but in fact most of time you don't see much of the terrain - because the view is centered on the player - so a brute force algorithm should be possible. If a LOD system is implemented, it has probably to do with the terrain node system - since the terrain is splitted in nodes, once can use a method similar to GeoMipMap to LOD far nodes.

So: brute force or LOD? I guess both have to be tried... :)


Update! H_o_p_s right side notes are finnaly nearly working! (1) edit: I tried to use H_o_p_s side notes, but was unable to use them. My HTML r the sukz0r.
(2) references are now put in the small references collection on the left of the journal entries.

Emmanuel Deloget

Emmanuel Deloget

 

And teh winnar is...

Noone.

No, it is even worse: if this situation continues, we are all the loosers.

Radupvr started this thread two days ago. His idea will probably never work, everything agrees on this point. What I find bad is not his post, but the answers he got.

All went right during the first posts. The idea he describe cannot work, ok. Even APC's first post was OK.

Then, the whole thing began to smell like a putrefied egg. I know that you'll say that these are out-of-context quotes, but I believe their context don't make them less harmful. We need to eliminate this sort of ignorance once and for all(1), speaking of Radupvr's original post. Raduprv, you have to be the most stubborn GDNet poster I know of, speaking of Radupvr's will to defend his idea - even proven inefficient and flawed; but are you sure that you never ever did the same thing? For someone who wrote an mmorpg, Rad is surprisingly dumb about some things. This is one of them. - What an interesting insight, but I beleive you can speak of someone without insulting him (the same guy continues with a superb "you guys should be ashamed of your ignorance", probably meaning that he knows everything on earth. "greet, now the idiots are rating me down" is also a good quote. I won't ask who are "the idiots", because I don't want to know the answer). Great idea! Raduprv, NASA is hiring. Just so you know... is also a clever insight - I just find it nastily sarcastic in this context, but who knows, maybe it was a proof of love?

During this conversation, I also saw some As for the rest of the thread - try to exercise a greater degree of diplomacy please, or Guys, it was just an idea; really now, no need to chew this guy to pieces. So clearly, there was something wrong, and some people saw it.

It is not difficult to understand why Radupvr was feeling attacked. Mockery, insults, condescension - I would probably had the same feeling.

Everything ended up with (2)

The rating problem is not very important - he still have more than a lot of people here. But the fact that he may be gone is more problematic.

Radupvr is a great guy. He his full of knowledge - even if his knowledge do not encompass astrophysics and planet studies: who cares? He exposed an idea, was bashed, and quit. I really hope he didn't leave GDnet permanently - but even if he want to come back later and just take a break, I really think that you, guys, should be ashamed by your behavior and your resulting "success". That's really a pity.

Really, there is no winner in this story - only loosers.



(1) APC managed to bash two user in one sentence. That's great. Clever, probably. If it wasn't a clever idea, he would not have posted that, right?
(2) I can't understand why I can't change the style of these quotes. If someone has an idea...

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

 

SQL API reloaded

... I just missed my 1337th post, and this one is the 1338th.

This clicky linky contains additional informations about linux SQL servers.

I seems I said wrong things about PostgreSQL - about its speed to be more precise. The version I tested when I was younger was a v6.something (back in 1999). It was painfull to setup and was slow. But in y2k they released their v7 and they vastly improved the speed of their engine.

I guess I should check the facts instead of saying dumb things :)

So far, PostgreSQL is the DBMS I choose. I am currently creating a simple C++ wrapper over the C API - using the functions I need and only them - in order to encapsulate the libpq calls.

The encapsulation of the calls will allow me to use SQLite as a secondary API. I am not always at home (and I often program on my laptop where I don't want to install too much softwares - mostly because I often use it as a professional tool). SQLite is an easy-to-go solution to avoid the installation of another SQL server.

I am pretty sure that one of the Firefox extension/theme I installed is the cause of my copy/paste problem. If you know anything about the subject, I'll give you a cookie (or a piece of caek if you don't like cookies). Currently, I have thes installed extensions:
Customize Google
PDF Download
Linkification
downTHEMall!
GooglePreview
googlebar
LinkPreview
LinkVisitor
Print Preview
I find them very handy, and I'm not sure I want to remove them - so I hope that my problem don't come from one of them.

I run the Brushed theme (version 0.9.9.5 (I guess that someone will begin to create version numbers with an infinite number of dots in the next few years)), and I find it clean. But I can revert back to the normal theme if it is the source of my problems.

As I think to it, I beleive it is the only difference between the firefox install on my own box and the install on this box. Oh, well, I have a couple of screenshots to do and I'll uninstall ot to see whether the problem is corrected or not.

Emmanuel Deloget

Emmanuel Deloget

 

C/C++ DBMS API

Today, I gave a fast look to some popular SQL database API. If you want to dig them, I suggest you to have a look to the Qt library - the database driver part.

PostgreSQL pgsql API
Free. Actually, the main problem is more PostgreSQL than the API. PostgreSQL is supposedly better than MySQL, but setting up the database server is somewhat complex (on a unix system; it seems to be easier on a Windows system) and it seems that MySQL is faster. It supports atomic transactions.

The API is C oriented, and is farily simple. The function used to send a SQL query is:

PGresult *PQexec(PGconn *conn, const char *command);

To retrieve the results, I have to use

// to test the PGresult status:
ExecStatusType PQresultStatus(const PGresult *res);
// to count the number of tuples
int PQntuples(const PGresult *res);
// to get the number of column in each tuple
int PQnfields(const PGresult *res);
// to get the type of a column
Oid PQftype(const PGresult *res, int column_number);
// to get the value
char *PQgetvalue(const PGresult *res, int row_number, int column_number);


SQLite API
Free too. No dependencies (SQLite doesn't connect to a database, it stores your datas in plain files) but is limited to a subset of SQL.

The API is very simple too: it is described here.

int sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void*, char**);

Is used to send a SQL query to the engine. Newt, we use

int sqlite3_column_count(sqlite3_stmt *pStmt);
int sqlite3_column_type(sqlite3_stmt*, int iCol);

sqlite3_column_XXX() functions are used to read the data.

There is an atomic transaction management.

MySQL API
Free for open-source use (which is not my plan at all); otherwise it is not very expensive - well... It can be...

The InnoDB table engine provide the needed atomic transactions.

The API is very simple too: there is a bunch of mysql_* C function that are rather easy to use. There is also a C++ API (which can be found here) - and that's a very good point (because, take it or not, I program in C++).

Here is a simple example of the MySQL++ API.

Of course, if you know some other SQL API (either C or C++), I encourage you to post a comment here ;)

Emmanuel Deloget

Emmanuel Deloget

 

more on creating a Win32 Window wrapper class...

WTF again. This version of Firefox decided that the gamedev.net edit fields were not good enough targets to copy the content of the clipboard. This is very annoying, as it happens only on this machine. It is painfull, as I have to type URL instead of just copy them.

This is a simple followup to Oluseyi's article. Some of you might ask how to add non-static message handlers to his nifty Window class - here is the solution!

First, let's define a new tyMessageHandler type as a pointer-to-member-function type. If you need to learn more about these, please have a look to the corresponding section of the C++ FAQ Lite.

typedef LRESULT (Window::*tyMessageHandler)(WPARAM, LPARAM);

Oluseyi's version of tyMessageHandler is a pointer to function, meaning that it can handle only static member functions and non-member functions. This is the key difference between my code and Oluseyi's. The other big difference lies in the MsgRouter() method:

// Final message handler version
LRESULT CALLBACK Window::MsgRouter(HWND hwnd, UINT message,
WPARAM wparam, LPARAM lparam)
{
Window *wnd = 0;

if(message == WM_NCCREATE)
{
// retrieve Window instance from window creation data and associate
wnd = reinterpret_cast((LPCREATESTRUCT)lparam)->lpCreateParams;
::SetWindowLong(hwnd, GWL_USERDATA, reinterpret_cast(wnd));

// save window handle
wnd->SetHWND(hwnd);
}
else
// retrieve associated Window instance
wnd = reinterpret_cast(::GetWindowLong(hwnd, GWL_USERDATA));

if(wnd)
{
tyMessageIterator it;
it = wnd->GetMessageHandler(message);
if(it != NULL)
{
// this is the main difference:
// it->second is a pointer to a member function, not a
// pointer to a function. We also get rid of *wnd (pretty useless)
// and hwnd (pretty useless too, since we'll call this->GetHWND())
return ((*wnd).*(it->second))(wparam, lparam);
}
}
return DefWindowProc(hwnd, message, wparam, lparam);
}

There is only one remaining difference between Oluseyi's version and mine, and it copes with the way we register the handlers. While the RegsiterMessagehandler() method don't change at all, the way we call it change.

// how to register a handler:
RegisterMessageHandler(WM_CLOSE, &Window::OnClose);

Don't forget the '&', the class name and the scope resolution operator - they are mandatory in this case.

I left the easy part for the end - the message handler method signatures are now far simpler:

class MyWindow
{
LRESULT MyMessageHandler(WPARAM wparam, LPARAM lparam);
};

Voila. Once it has been registered, your member function will be automagically called if the message is triggered. It was rather easy, isn't it?

One can add more complexity by using marshallers objects that will first decode both wparam and lparam then call your message handler using the right arguments (for example, a POINT object and a flag value for a mouse message). This is beyond the scope of this journal entry - but be quiet, and I'll give some hints to you ;)

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

 

Closer, closer, cloooseeeer...

I'd like to say that the GDC coverage is AMAZING. Thanks to the whole GDnet team (they are working hard to provide us these informations).

Linux programming
It has been a long time since I stopped linux programming. Something like 4 to 5 years in fact (I attended the guadec 2000 in Paris, and the guys I saw there were stupid and disgusting - I really felt bad because of them and I stopped my collaboration with Tony Gale and Erik Mouw immediately (despite their human qualities), mainly because I decided to beleive that they were all the same). My company sold some Linux/Qt/MySQL work to another company.

Some points:
1) Mandrake 10.1 is really a PITA. On the machine I use (a DELL PowerEdge 750) the kernel 2.6 do not see the TEAC CD-ROM, while the 2?4 sees it. Stupid.
2) there are numerous problems with the "control panel" (example: renaming a user do not rename the user home directory, but change the home directory in the password file - maybe it works in the general case...)
3) harddrake is stupid. It decided that my mouse is a USB mouse. It is a PS2 mouse. I initially added harddrake in the init.d services - but it systematically updated my X configuration to tell the server that I use a USB mouse - X without a mouse is not teh funnay.
4) I really though that KDevelop was intelligent. It seems he can import Makefile based-projects - it cannot create them (in the default Mandrake configuration).

...and so on...

Anyway, I'm pleased with this box.

resume()
Next entry - all about the graphic engine! - : what we already done, and what we still have to do.

See you soon.

Emmanuel Deloget

Emmanuel Deloget

 

Playing with integers....

I know I haven't updated my journal for a long time. I don't have much time by now, so I will only describe a small bug I just saw.

Consider this piece of code (32 bit integer is assumed):

void function()
{
try {
throw 0x80000000;
} catch (int a) {
std::cout } catch (...) {
}

try {
throw 0x40000000;
} catch (int b) {
std::cout } catch (...) {
}
}


What will be written on your console? Those who answered

catched a: 80000000
catched b: 40000000

Are WRONG.

Ah ah ah ah.
The correct answer is:


catched b: 40000000


Yes, the first one will not be catched. The reason lies in section 2.13.1.2 of the C++ ISO. The text reads:



Here, the norm is not defined very well - obvioulsy it is possible to represent the 0x80000000 value by using an int. But some might choose to consider that -2147483648 is not a correct representation of 0x80000000, hence the promotion to unsigned int, which in turn makes our catch useless.

It was teh funnay to find, and teh funnay to fix.

Comming...
I'll have more time to spend during the next weeks, with some extra bonuses: updates of my ongoing "how to design this 2D graphic engine" series + the code which is related to this serie + some technical stuff I'm writing about image manipulation algorithm + a lot of other goodies.

Have a nice week-end!

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

 

Yeah! The sun is shining

Good to see that the sun is shinning (not now, during the day, you know). And the real good news of the day: I received my copy of A Theory of Fun for Game Design. A quote, p189: 2B||!(2B). This is not a valid C expression. This guy doesn't know how to program. I want my money back (kidding. It seems to be an exceptionnal book so far).

No, I go to bed again - no MMOMSF1 tonight.


1: massively multiplayer online "mine sweeper flag".

Emmanuel Deloget

Emmanuel Deloget

 

Low level resources

Thoughts
We really need to stop and consider what we've already written yet. And provides an abstract. Because it becomes to be big.

Unusual entry
I'd like to discuss about the design of the low level resource classes. This has really nothing to do with our main subject but it is still interesting since it shows how to choose a solution which maximize the benefits of OO, minimize the drawbacks, and produce very clean code.

And I'm happy to know that somebody loves me :) Thanks for your support [grin].

Some bits of informations about low level resources
Only the DisplayDriver creates or destroy low level resources. Therefore, there is no need for the resource to provide a public constructor or a public destructor. Having a protected one allow a child class to access it and disallow you to create a resource outside of the DisplayDriver. Of course, the DisplayDriver needs to be a friend class of all the DisplayResource child classes.

You'd ask me: why is this good? This is related to the DirectX philosophy. When you are using DirectX in fullscreen and alt-tabing to another application you have to reset the DirectX device. This cannot be done if the device contains resources which has been allocated outside of the managed pool. Therefore you have to release the resource, reset the device and then reset the resources. Now, there is two solutions to do this:

the DIY way: loop through the resources and do the stuff by yourself. You'll have to note that this is a common source of problem when programming DirectX. One may forget to release a single resource and the device will not be able to reset.
the "I don't want to tackkle with it" way: let the DisplayDriver do all the work for you. It is easy for the driver to know whether the resources have to released or not before the driver reset. It is therefore less error prone to go this way.

To design our solution, we chosed the second option. But since the display driver now handle the release and reset of the resources, it needs to reference all the resources which are used at the device reset time. We hit another alternative here (and therefore, two possibilities):

the resource knows how to add itself to the driver resource list and it knows how to remove itself from this list. The DisplayDriver needs to expose an interface to these function. If we don't want to expose this interface to the public we'll have to set all the possible resource classes as friend classes of DisplayDriver. This makes little sense to me.
the driver adds the resource it creates to a resource list. it requires that the whole resource creation is done by the driver - and only by it. Therefore, to avoid the creation of resources outside of the driver, we have to forbid this at the code level - by protecting the constructor and the destructor. This solution also requires to have at least one creation function by resource type in the DisplayDriver.

We again chosed the second option. The "one function by resource type" is not really a problem. More, it is actually a benefits because of the code it produces:

system::SurfaceResource *surface;
surface = mDisplayDriver.createSurface(...);

It makes it clear that the resource is owned by the display driver.

The destruction of the resource also needs a specific interface in DisplayDriver - because the resource can't be destroyed by a delete.

These are the reason behind the following base resource class (simplified) declaration:

namespace sys
{
class DisplayDriver;

class DisplayResource
{
virtual void releaseV() = 0;
virtual void resetV() = 0;

friend class DisplayDriver;

protected:
DisplayResource();
virtual ~DisplayResource();

void release();
void reset();
};

};


Hope you liked it :)

Emmanuel Deloget

Emmanuel Deloget

 

Doh! Another entry!

First, some news
I've been busy these days because I had to... OK. Let's forget personnal things, you obvioulsy don't want to waste some time. Just to say it: I am the happy owner of a 128MB USB2.0 Verbatim USB key. It was bundled with a pack of 50 CD-R. I bought this 24 euros (something like $30US). Cool enough. That's the price of either the CD tower or the USB key.

I was very happy with my rating. 1343. Was cool. Then I said something (stupid? Not sure. Believe it was that, but I can't verify) and I was rated--. And I ended up to be 1337 rated!!1one Soooo coooooool [grin].

And I really love this journal.

We have some work to do now
Resources are a key element in both 2D and 3D graphic engines. There are a lot of them and they should be managed in a way that won't affect performance.

Last week (yes, it was last week) we spoke about high level resources and low level resources. The distinction between the two is rather easy to do, because there is an obvious difference between a sprite and a bare bitmap, or a mesh and a vertex buffer. High level resources can be used to directly abstract game object representations. To do such, they have to use one or more low level resources because those are the underlying system objects which are needed to really draw something.

I should tell you that there is nothing terribly exciting in this journal entry, because this is more an enumeration of our resources type than something else. Anyway, it has to be done, so...

the return of system::DisplayDriver
Remember that the design of the system abstraction is not our business since we are creating a 2D graphic engine. Nevertheless, since we are designing both at the same time, I allow myself to think about the system::DisplayDriver. This is important because - as I said some days ago - the low level resources are system resources, ie they are created by the DisplayDriver.

As of today, classical DisplayDriver resources are:
surfaces - textures, render targets, and so on
vertex and index buffers
shaders car also be considered as resources - mainly because they are used in the same way, even if their purpose is different because they are not passive.
some may also consider the internal driver state to be a resource. This is arguable and I believe this is not a good design solution.

Driver state may be implemented as a resource because it really looks like a resource. You can set it, get it, create a new state and such. But creating such a resource kind do not allow us to make little modifications to the current driver state because the granularity of such modification would be below the granularity level of resources.

I won't discuss more about these, mainly because it is not our goal. The main point is that we now have a list of available low level resources to build our high level resource abstraction.

High level resources?
Yes.

We don't want to cope with these low level resource at the application level, mostly because, hell, we are doing a 2D graphic engine, not a full featured 3D engine with low level control on every bits of information.

Do you remember my very first entry about this design? I asked myself what should be needed in our graphic engine. Let's do it again: what are my answers?

We need to be able to draw our world - if there is one, of course
We need to be able to draw our entities - the player, the ennemies, and any other non static entity.
We also need to be able to use some special effects as well

All of these actions needs to use at least two resources: a bitmap surface (a texture) and an implicit rendering surface (different rendering surfaces may not break our design. We can therefore safely ignore them for the moment, but we'll have to think about them later). Some of these actions may even need more than one bitmap surface - the non static entity should be a sprite and may be animated, for example.

There exists numerous techniques to draw a world. All of these techniques are dealing either with a tiled world (zelda-like, for example) or with a non-tiled, bitmap based world (the great, incredible Worms, for example). Since we didn't say anything about the game type we want to implement, we have to provide the two possibilities.

Entities are objects which are not part of the background. I call them non-static they can change. Not all of them will actually change but the possibility make them - in my humble opinion - non static. As non-static objects, they can be represented by one image (if they don't change their appearance) or multiple images (if they change) or one image with multiple states defined inside this image. 2D special effects can be implemented as special entities since they share the same properties.

Before we continue we have to consider something which we didn't consider yet: we'll always draw quads. This will implicitely use a vertex buffer. There is no reason to actually expose this buffer to the rest of the world. Thus, he will probably be hidden in our graphic engine.

So, what did we get?
We finally got a list of possible 2D high level resources:

simple bitmap
sprite with multiple bitmaps or states
tile with a underlying tileset

These objects will be implemented as our high level resources.

The end
I know, you probably want more. These articles are so good (I have 2 regular readers! gosh!).

Have a good night now, cos I need to go to bed again (this probably means I'm still teh sux0rz).

Emmanuel Deloget

Emmanuel Deloget

 

The story of the story of the story of the story o

Day D-1
Tomorrow I'll have an English exam. Nothing very fancy. But I'll know whether I'm good enough to speak 1337 English or not. Oh, wait. I already know.

I'm teh sux0rz at English. Just forgotten that. Crap.

Graphic elements
Yesterday we decided to create an adapter to the graphic driver. Of course, since we are doing this, the first thing will have to do is to define the new interface of this graphic driver adapter (I called it GraphicDriver - the one in the system package is DisplayDriver). And to define it, we need to know what a GraphicElement is because once we are able to say what it is we are able to say how it should use the GraphicDriver.

So, what is a GraphicElement? We have at least three possible choices.

a GraphicElement is a bare resource, for example a texture. In this case the granularity of the DisplayDriver resources encapsulation is very low - and it doesn't seem to be the correct solution.
a GraphicElement is an intelligent resource - for example, a sprite or a tile. This could be a possible solution.
a GraphicElement is the representation of an in-game object - for example, the player representation or the world representation. A GraphicElement may then hold multiple intelligent resources. This solution seems to be good too.


Once we consider the last possible solutions, the first one is obviously a bad choice. The encapsulation of a system resource should be a graphic resource, and a graphic element should contain them. While being a bad choice, the solution reminds us that we will need to encapsulate the system resources in order to ease their use.

Now we have to choose among the two other solutions. If solution 2 seems to be pretty it is less high level than the other one. Moreover, to define the kind of objects which would be implemented in solution 2 we used a magic word: resource. This should give us hints that would help the design. I'll choose solution 3 because of this hint. Sprites, tiles or tile sets can be implemented as high level resources - which in turn can be implemented by using low level (read system level) resources.

Although the object names would probably need a rework, we now have something like this:


class LowLevelResource
{
system::DisplayResource rsc;
};

class HighLevelResource
{
set llrsc;
};

class ConcreteGraphicElement : public AbstractGraphicElement
{
set hlrsc;
public:
create()
{
create some HighLevelResource items
populates hlrsc
}
destroy()
{
destroy the items in hlrsc
}
virtual draw(GraphicDriver gdrv)
{
setup gdrv
draw the different hlrsc items
}
};


The main point now is that the graphic engine user is now responsible for creating the ConcreteGraphicElement objects.

Teh Xtrab0lz!
I manage to not write "Teh xtr3b0lz0rz", which may have been too much 1337 for you, young padawan.

The next time we'll speak about the low level and high level resources. And we'll explain things about these two entries. Yeah. Any comments?

Really?
Oh, come on! Stop h2'ing your questions. You look like a 10 y.o.

Emmanuel Deloget

Emmanuel Deloget

 

FOR I=0 TO 5 STEP 1

ph24r the 1337
I find the so-called elite speaking cool. The r0x0rz. Not because it is cool, but because it is weird. Probably as weird as the pope singing a rap song. Since he did, I guess there is nothing wrong in da leet language. Quite teh funnay.

Where are the comments?
Since you don't read this, you don't comment this. Quite teh logical. But if (for whatever reason) you want to add a comment - not very difficult you know - feel free to do it. Of course, if you feel you should paypal me, that's even better...

Iterative design
Designing a software is an iterative process. One have to think to a solution, design it, then he must improve its design. Once the software is released, he has to work on the next version, which of course needs more design.
The only one who can create a complete software with only one design iteration Gandalf, but AFAIK he does not exists in our world.

And thus...
yesterday we had a talk about the ownage of the graphic driver. We found that both the graphic engine and the system object can own the driver. Today I want to dig further.

There is nothing wrong in having the graphic driver inside the graphic engine. We saw that it only adds implementation-based drawbacks, and those can be defeated by implementation-based techniques. However the creation of the driver will probably need some access to the system.

On Jan-27, I talked about the system component. It seems that this component needs to provide an access to at least two major UI components: the diplay manager and the input manager. We define the system component with:

namespace System
{
class Display; // not defined yet
class Input; // not define yet
};


Because the graphic driver works tightly with the display subsystem, it will probably have to use it (we already saw that the display system can't be implemented in the graphic engine because of the dependance it implies: it is up to the game frawork to implement it, not to the graphic engine). But, from an abstract point of view, the display system - which is aimed at displaying things - and the graphic driver - which is aimed at (guess...) displaying things - are somewhat equivalent.

If you have two display systems in two different modules then there is something weird. We should be able to factorize things a bit more.

Remember then our first solution (the system own the graphic driver). Now, it seems we have two display systems in a single package. This is really bad, and it is probably not what we want.

Since they are basically the same thing, why don't we factorize at this level? The graphic engine is the display, and the display is the graphic driver. It means that we pull the graphic driver away from the graphic engine.


namespace System
{
class DisplayDriver; // this is the graphic driver!
class InputDriver; // renamed, for consistency
};


Yeah! Teh FacOrizOrz!!!11one
Therefore the driver is now in the system package, meaning that our graphic engine is using the DisplayDriver.
We haven't thought about that until now, but why should the system package (which belongs to the game framework) be aware of the graphic engine who is using it? You'd say that we never said something like that. But we just took a graphic driver from the engine and we put it in the system package. How do we know is the driver is aimed at 2D rendering or not? Even if its interface is an abstraction of the real underlying driver, it should not be designed with 2D game in mind, because we won't be able to reuse it. But we don't want our engine to cope with some advanced 3D rendering issue - hell, it is a 2D engine.

Again, as always, we are lucky. The GoF gave us the solution: we have to use the Adapter pattern (check here if you are unfamiliar with this pattern - although the site speak of C#, the pattern description is fairly generic).

The Adapter pattern will allow us to provide a 2D graphic driver - internal to the graphic engine, this time - which will encapsulate the system DisplayDriver.


class AbstractGraphicElement
{
public:
virtual draw(GraphicDriver gdrv);
};

class GraphicEngine
{
GraphicDriver gdrv;

createDriver(system::DisplayDriver drv)
{
gdrv = new GraphicDriver(drv);
}
destroyDriver() { delete gdrv; }

public:
init(system::DisplayDriver drv) { createDriver(drv); }
uninit() { destroyDriver(); }
draw(AbstractGraphicElement gel) { gel.draw(gdrv) }
};


Quite teh c00lz0r, isn"t it?

Hey, you won't stop now!
Hey, I have a life! See you 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!