| 1 |
sysadm |
1.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.
|