| 1 |
|
| 2 |
PVPGN (wannabe) DEVELOPERS GUIDE
|
| 3 |
|
| 4 |
Please read this file before asking any questions !
|
| 5 |
|
| 6 |
0. About this document
|
| 7 |
=======================
|
| 8 |
|
| 9 |
This document is intended to be read by all of you out there wanting to do
|
| 10 |
development or testing for/on PvPGN.
|
| 11 |
|
| 12 |
ChangeLog:
|
| 13 |
29-07-2003: dizzy : Initial version
|
| 14 |
28-08-2004: dizzy : Run-time debugging : Appendix A
|
| 15 |
30-08-2004: dizzy : Updated (fdwatch, xalloc, goto, gdb)
|
| 16 |
|
| 17 |
1. Why ?
|
| 18 |
=========
|
| 19 |
|
| 20 |
You want to start coding for PvPGN ? Why ? What do you expect to get from it ?
|
| 21 |
Answer yourself this questions and then go to the next paragraph.
|
| 22 |
|
| 23 |
2. History
|
| 24 |
===========
|
| 25 |
|
| 26 |
PvPGN has started as a game server emulation project, taking the excelent's
|
| 27 |
bnetd project source and working on it. While initially it has started as
|
| 28 |
a War3 emulation patch over bnetd it become a lot more than that (lots of
|
| 29 |
new features, lots of code changes organizing). Because of the code roots
|
| 30 |
you will notice a lot of things still carrying the "bnetd" word (most notable
|
| 31 |
examples are the main servers program file called "bnetd" or the main server
|
| 32 |
configuration file called bnetd.conf). We considered that as a sign of respect
|
| 33 |
from us to the bnetd coders we keep their names on the code they written (but
|
| 34 |
on the new code of course we may name them different).
|
| 35 |
|
| 36 |
3. Objective
|
| 37 |
=============
|
| 38 |
|
| 39 |
PvPGN's main objective is to support all Battle.Net clients (games) but are
|
| 40 |
considering in the near future to extend it to support other game protocols
|
| 41 |
as well.
|
| 42 |
|
| 43 |
4. Layout of files
|
| 44 |
===================
|
| 45 |
|
| 46 |
Note: Starting here on you may find lots of terms and wors which may sound
|
| 47 |
"strange" to you and for this reason we have included a glossary of terms
|
| 48 |
in the end of this file.
|
| 49 |
|
| 50 |
The PvPGN project consists of a main server (called "bnetd") and various
|
| 51 |
other programs and (little) servers (ex. bnchat, d2cs, d2dbs etc...).
|
| 52 |
|
| 53 |
PvPGN follows the bnetd's layout of files:
|
| 54 |
./bin -> used to store binaries (after compilation)
|
| 55 |
./conf -> configuration files (many of them templates)
|
| 56 |
./files -> various files needed for clients connecting
|
| 57 |
./man -> outdated man pages :(
|
| 58 |
./sbin -> same as ./bin
|
| 59 |
./scripts -> various scripts for testing/managing pvpgn
|
| 60 |
./src -> the source main directory
|
| 61 |
./src/bnetd -> source files used only by the main server
|
| 62 |
./src/common -> source files used in common by different programs
|
| 63 |
./src/compat -> source files concerning portability of code
|
| 64 |
...
|
| 65 |
|
| 66 |
The build process takes place in the "src" dir.
|
| 67 |
|
| 68 |
5. Coding Style
|
| 69 |
================
|
| 70 |
|
| 71 |
a. General
|
| 72 |
|
| 73 |
PvPGN is mainly developed on and for UNIX/Linux. The reasons are because we
|
| 74 |
as coders work on this systems and because our users (at least the big ones)
|
| 75 |
use UNIX/Linux more than ex. Windows. But, even so we try to code in a portable
|
| 76 |
fashion and any release PvPGN had we made sure it compiles on win32 too (we
|
| 77 |
even release win32 binaries). We will never refuse a good win32 coder but we
|
| 78 |
prefer UNIX ones ;)
|
| 79 |
|
| 80 |
One thing which is overlooked by newbie coders is the "esthetical" side of the
|
| 81 |
code. It may not be so important to many people (which code on the idea "if it
|
| 82 |
works then its good") but for us, coding on PvPGN is VERY important. When you
|
| 83 |
are coding for PvPGN PLEASE try to make your code look similar to already
|
| 84 |
written code (this includes identing, identificator names, etc...). Keeping
|
| 85 |
the code look "the same" makes its reading a lot more easier so, finding
|
| 86 |
bugs easier so coding better.
|
| 87 |
|
| 88 |
One way to make sure your code sticks with the PvPGN coding style is to use
|
| 89 |
"indent" code indenting formating tool (http://www.gnu.org/software/indent/).
|
| 90 |
To use indent to get your code acording to PvPGN coding style use something
|
| 91 |
like this (it will format properly the file clan.c and save the new file to
|
| 92 |
clan.c.new):
|
| 93 |
$ indent -kr -bl -bli0 -l0 -br -ce -cli4 clan.c -o clan.c.new
|
| 94 |
|
| 95 |
Other overlooked aspect for newbie coders is code replication. Please DONT
|
| 96 |
copy and paste code arround !!! If you need to copy common functionality from
|
| 97 |
some place, think about making some API of that functionalilty, put it in
|
| 98 |
some functions and use it from both places. I repeat, DONT replicate code.
|
| 99 |
|
| 100 |
When allocating memory inside a function always free it in the same function
|
| 101 |
before its exit (exceptions: the function returns the allocated memory in
|
| 102 |
which case the calling function should take care of the allocated memory;
|
| 103 |
or the allocated memory is cached/stored somewhere to be used later, in which
|
| 104 |
case you must make sure it will be free()d when not needed anymore).
|
| 105 |
|
| 106 |
In the startup code of any "external" function (function which may be called
|
| 107 |
from other modules then the one containing it) please check ALL the input
|
| 108 |
parameters (you will notice how PvPGN already does that in such functions).
|
| 109 |
Traditionally this has been done with contructs like:
|
| 110 |
if (var==NULL) { eventlog(error); return <error-code>; }
|
| 111 |
We have recently changed our policy because of increasing code size and lots
|
| 112 |
of redundant checks, we now usually prefer using assert() for this type of
|
| 113 |
checks (check the manual page for assert).
|
| 114 |
|
| 115 |
A new addition is the xalloc memory allocation wrappers (for malloc,
|
| 116 |
calloc, realloc, strdup and free). We now do NOT allow memory allocation to
|
| 117 |
take place without this wrappers. They make the code smaller and easier to
|
| 118 |
read because they never fail so they eliminate the need for failure checks
|
| 119 |
and eventlogs in such cases.
|
| 120 |
|
| 121 |
Another thing recently adopted in the team is the usage of "goto" keyword.
|
| 122 |
Many people have argued (and continue to) that goto are inherently _evil_ and
|
| 123 |
they produce "spaghetti code". But that is true for pointers and lots of other
|
| 124 |
powerfull C features. We consider the programmer to be the cause of bad
|
| 125 |
written codes and not some keywords. So we are moving into using "goto"
|
| 126 |
mainly in the "error exit paths" cases. Let's say you have a function which
|
| 127 |
needs to allocate some stuff (in 3 variables), checks for each allocation,
|
| 128 |
exits with error for each specific failure (also free()ing the already
|
| 129 |
allocated space). You can do it the PvPGN traditional way like:
|
| 130 |
|
| 131 |
------- CODE ----------
|
| 132 |
a = malloc(X);
|
| 133 |
if (!a) {
|
| 134 |
eventlog(bla);
|
| 135 |
return -1;
|
| 136 |
}
|
| 137 |
|
| 138 |
b = malloc(Y);
|
| 139 |
if (!b) {
|
| 140 |
eventlog(bla);
|
| 141 |
free(a);
|
| 142 |
return -1;
|
| 143 |
}
|
| 144 |
|
| 145 |
c = malloc(Z);
|
| 146 |
if (!c) {
|
| 147 |
eventlog(bla);
|
| 148 |
free(b);
|
| 149 |
free(a);
|
| 150 |
return -1;
|
| 151 |
}
|
| 152 |
<do stuff>
|
| 153 |
|
| 154 |
return 0;
|
| 155 |
------ CODE -----------
|
| 156 |
|
| 157 |
Or you can do it the new, "goto" way:
|
| 158 |
--------- CODE --------------
|
| 159 |
a = malloc(X);
|
| 160 |
if (!a) {
|
| 161 |
eventlog(bla);
|
| 162 |
goto err_a;
|
| 163 |
}
|
| 164 |
|
| 165 |
b = malloc(Y);
|
| 166 |
if (!b) {
|
| 167 |
eventlog(bla);
|
| 168 |
goto err_b;
|
| 169 |
}
|
| 170 |
|
| 171 |
c = malloc(Z);
|
| 172 |
if (!c) {
|
| 173 |
eventlog(bla);
|
| 174 |
goto err_c;
|
| 175 |
}
|
| 176 |
<do stuff>
|
| 177 |
return 0;
|
| 178 |
|
| 179 |
err_c:
|
| 180 |
free(c);
|
| 181 |
err_b:
|
| 182 |
free(b);
|
| 183 |
err_a:
|
| 184 |
return -1;
|
| 185 |
-------- CODE ------------
|
| 186 |
|
| 187 |
The last version is a lot easier to read/debug. Because the reader is usually
|
| 188 |
interested by the main (non-error) code path not about the error code path.
|
| 189 |
Also the traditional way replicates codes (those replicated free()) which is
|
| 190 |
error prone (more badly, its prone to memory leaks, and believe me, I did
|
| 191 |
fixed a lot of this forgotten free() in error exit paths).
|
| 192 |
NOTE: however, with the xalloc wrappers that never fail the above examples
|
| 193 |
are obsolete, still there are many other similar situations where you need
|
| 194 |
a complex error exit path and goto comes in handy.
|
| 195 |
|
| 196 |
When developing code you should compile always with "all warnings" enabled
|
| 197 |
and try to fix them (some warnings uncover real bugs), like for gcc use
|
| 198 |
"-Wall". When running/testing the codes you should use a memory debugger like
|
| 199 |
valgrind and/or bounds checking compiler patches like bounds-checking-gcc.
|
| 200 |
For more details about how to run pvpgn check Appendix B.
|
| 201 |
|
| 202 |
b. Code Organizing
|
| 203 |
|
| 204 |
PvPGN (inhireted from bnetd) trys to group functions based on their data on
|
| 205 |
which they work on. Thus it somewhat resembles the principle of data
|
| 206 |
encapsulation used in OOP. For example connection.h defines structs necesarry
|
| 207 |
to work with connections (the main one beeing t_connection) and functions
|
| 208 |
which work with this structs and which do actions related to this connections.
|
| 209 |
Also notice that all this functions are named like conn_<name>(). In general
|
| 210 |
if you have a set of functions working on a set of structs, you define the
|
| 211 |
structs and the functions in a <module>.h file (thus defining the API) then
|
| 212 |
write the function implementations in <module>.c file. And the function names
|
| 213 |
should be something like <module>_<name>(). Separating and grouping functions
|
| 214 |
in their own modules, working on their own data structures helps isolating
|
| 215 |
functionality in the server and thus hunting bugs a lot easier.
|
| 216 |
|
| 217 |
c. Code Flow
|
| 218 |
|
| 219 |
Note: this chapter is more or less correct. Is intended to give a general
|
| 220 |
ideea about the code flow, it may contain flows and things which are not
|
| 221 |
in sync with the actual code, for a better understanding I recommend reading
|
| 222 |
the actual code
|
| 223 |
|
| 224 |
The server starts in main() found in bnetd/main.c file. There it parses
|
| 225 |
command line arguments then tries to initilize the modules by calling their
|
| 226 |
initilizing function (many of them called like <module>_init()). Some of this
|
| 227 |
modules depend on the initilizing of others so the order there may be important.
|
| 228 |
Also some of the modules if fail on initilizing may represent a general fail
|
| 229 |
thus PvPGN bails out. But should print out a explaining message (or write it
|
| 230 |
to the log configured file).
|
| 231 |
|
| 232 |
From there the server calls the main server loop function found in server.c.
|
| 233 |
Here the loop may be simplified to something like:
|
| 234 |
1. fdwatch() on the set of opened sockets
|
| 235 |
2. call fdwatch_handle() which reads (if any) network data and "handle" them
|
| 236 |
(by calling the handle function coresponding to the class of the connection
|
| 237 |
receiving the data)
|
| 238 |
3. write (if any) to the network from the output queue
|
| 239 |
4. do any periodic or time based events
|
| 240 |
5. go to 1
|
| 241 |
|
| 242 |
As seen on step 2. above we call the handling functions coresponding to the
|
| 243 |
class of the connection. The handle functions usualy are a big switch/case or
|
| 244 |
a for/if (which is equivalent) and they check for the packet type received, then
|
| 245 |
they act acordingly to the data received (many of them responing with packets
|
| 246 |
by adding them to the output packet queue of the connection).
|
| 247 |
|
| 248 |
Appendix A. Glossary of terms
|
| 249 |
===============================
|
| 250 |
|
| 251 |
* autoupdate: the feature of Battle.Net servers to send a (MPQ, see MPQ) file
|
| 252 |
to the client which after downloading it, it is used to update the client
|
| 253 |
|
| 254 |
* connection class: when a connection is established to a bnet listening
|
| 255 |
address the client sends an initial byte which tells the server of what class
|
| 256 |
of connection the following packets will be; classes of connections determine
|
| 257 |
what packets can go through that connection.
|
| 258 |
|
| 259 |
* MPQ: a format archive which is used by Blizzard clients and Battle.Net
|
| 260 |
servers. This files are used for containing verious files (sound/graphics in
|
| 261 |
clients, AI scripts, update patches etc...)
|
| 262 |
|
| 263 |
* versioncheck: also know as vcheck and sometimes just vc ; a client
|
| 264 |
verification system used by Battle.Net servers to identify client version and
|
| 265 |
"purity". Based on this the server may accept/refuse connection or ask for
|
| 266 |
upgrade (see autoupdate).
|
| 267 |
|
| 268 |
Appendix B. How to run PvPGN for debugging
|
| 269 |
===========================================
|
| 270 |
|
| 271 |
It is very helpfull in finding out memory coruption bugs as soon as possible
|
| 272 |
so while developing codes or just when running a server it is good that you
|
| 273 |
use some memory coruption run-time debuggers. I dont know about Win32 users
|
| 274 |
but on Unix/Linux there are some good options.
|
| 275 |
|
| 276 |
1. valgrind (http://valgrind.kde.org)
|
| 277 |
|
| 278 |
Valgrind is not very portable (only x86, Linux and very recently FreeBSD),
|
| 279 |
also it slows down the debugged codes (it acts like a CPU emulator so it
|
| 280 |
has to do that) but I have yet to find out a better debugging tool for what
|
| 281 |
he does. Valgrind is so cool that recently many OSS projects use it for
|
| 282 |
finding out bugs in their codes. For more information you can check out their
|
| 283 |
web page. I will focus on valgrind with PvPGN.
|
| 284 |
|
| 285 |
After you have compiled and installed valgrind (it's easy, ./configure, make,
|
| 286 |
make install) you will use it by running PvPGN like this:
|
| 287 |
|
| 288 |
$ valgrind --tool=memcheck --num-callers=10 /path/to/bnetd -f 2> valg.out
|
| 289 |
|
| 290 |
"num-callers" makes valgrind record backtraces with 10 entries and is usually
|
| 291 |
needed with PvPGN which has not very small backtrace path :)
|
| 292 |
|
| 293 |
Another option you might want to use is "--leak-check=yes" and probably
|
| 294 |
"--leak-resolution=high". This options make valgrind even slower but they
|
| 295 |
will give memory leak information when PvPGN exits.
|
| 296 |
|
| 297 |
I encourage EVERYONE to use it if available (that is if you run PvPGN on
|
| 298 |
a supported platform). Only very big servers won't be able to do it because
|
| 299 |
there is no hardware powerfull enough to run a big server with valgrind (but
|
| 300 |
big means over 500 users online). You should test it with your server and if
|
| 301 |
it does not make your bnetd go over 90% CPU then you should be fine. If you
|
| 302 |
cannot run valgrind for any reason or if you are hunting for some bugs
|
| 303 |
valgrind cannot find (yes, valgrind is superb but there is a class of bugs,
|
| 304 |
especially overflows which valgrind can't help you with) you should then try
|
| 305 |
the next debugging tool.
|
| 306 |
|
| 307 |
2. bounds-checking GCC (http://sourceforge.net/projects/boundschecking/)
|
| 308 |
|
| 309 |
This has the drawback of beeing just for GCC (as far as I know of) but has
|
| 310 |
the advantage over valgrind that is : more portable (virtually it should be
|
| 311 |
possible to use it anywhere you can use a recent GCC), a lot faster and can
|
| 312 |
detect bugs that valgrind cannot find. However it has the disadvantage that
|
| 313 |
it's error reports do not contain backtraces so they might be lesser usefull.
|
| 314 |
|
| 315 |
To use it basically it cuts down to:
|
| 316 |
- download a recent compiler (ex GCC 3.4.1)
|
| 317 |
- download the coresponding patch from the project's sf.net page
|
| 318 |
- apply the patch while beeing in the gcc source dir you unpacked with a
|
| 319 |
command like bunzip2 -c /path/to/patch.bz2 | patch -p1
|
| 320 |
- compile gcc and install gcc
|
| 321 |
- to make pvpgn compile with your new gcc do something like:
|
| 322 |
$ CC=/path/to/new/gcc CFLAGS="-O0 -Wall -fbounds-checking -g" ./configure
|
| 323 |
- then the usual make, make install
|
| 324 |
- to run PvPGN you will probably need to redirect stderr like:
|
| 325 |
$ /path/to/bnetd -f 2> debug.out
|
| 326 |
|
| 327 |
You will probably encounter some problems when configuring, building gcc, so
|
| 328 |
you should read the text in the beginning of the bounds-checking patch file.
|
| 329 |
Also the gcc install docs are usefull.
|
| 330 |
|
| 331 |
When starting up your new bounds checking enabled binary you will see some
|
| 332 |
usefull messages. By default the patch makes the debugged program exit on the
|
| 333 |
first error it encounters. For a production server this is not very nice so
|
| 334 |
you will want to set GCC_BOUNDS_OPTS shell variable to "-never-fatal" like:
|
| 335 |
$ GCC_BOUNDS_OPTS="-never-fatal" /path/to/bnetd -f 2> debug.out
|
| 336 |
|
| 337 |
Appendix C. How to generate and use "core" files
|
| 338 |
=================================================
|
| 339 |
|
| 340 |
This appendix is for Unix users. I dont know if other platforms have similar
|
| 341 |
features, that when the program crashes unexpectedly the OS would dump the
|
| 342 |
contents of the memory of the crashing process into a disk file for later
|
| 343 |
inspection.
|
| 344 |
|
| 345 |
First make sure that PvPGN has been compiled with debugging ("-g" parameter
|
| 346 |
to gcc) and with no optimizations ("-O0" parameter to gcc). PvPGN default
|
| 347 |
build process puts "-g -O2" so you need to edit Makefile file before compile
|
| 348 |
and change it to "-g -O0". Then something like "make clean; make".
|
| 349 |
|
| 350 |
On Unix/Linux to be able to get core dumps you first need to make sure your
|
| 351 |
core file size limit is set acordingly. Use "ulimit -c" for querying and
|
| 352 |
setting this limit (I recommend setting it to "unlimited"). After that when
|
| 353 |
you start PvPGN make sure you are in a directory where you have write access
|
| 354 |
(so the OS when it wants to do the core dump it will be allowed to do so).
|
| 355 |
The last thing to do is when starting PvPGN make sure it starts in FOREGROUND,
|
| 356 |
example : /path/to/bnetd -f . If you did all this then when PvPGN will crash
|
| 357 |
you will get a core dump. On linux this is a file called "core", on *BSD it's
|
| 358 |
called <processname>.core (for bnetd that means it's called bnetd.core).
|
| 359 |
|
| 360 |
Now that you got a core file it is time to use it to identify what happened
|
| 361 |
wrong in the crashing process. We use gdb (the GNU debugger, should be
|
| 362 |
available on all Unices) to do this. Run gdb like this:
|
| 363 |
$ gdb /path/to/bnetd /path/to/corefile
|
| 364 |
|
| 365 |
Then gdb should startup, print out a lot of messages and stop after printing
|
| 366 |
a file and line number and quoting some C code where the crash has happened.
|
| 367 |
You can find out a lot more information than this. Run gdb's command "bt full"
|
| 368 |
and it will display a full backtrace of the moment of the crash. The backtrace
|
| 369 |
will contain how the functions were called along the way (their parameters),
|
| 370 |
and also any local variables. If you do not know what to do next from here
|
| 371 |
contact a PvPGN developer and give him exactly that backtrace dump, he should
|
| 372 |
know more.
|