pvpgn-server/README.DEV

377 lines
17 KiB
Text

PVPGN 2.x DEVELOPERS GUIDE
Please read this file before asking any questions !
0. About this document
=======================
This document is intended to be read by all of you out there wanting to do
development or testing for/on PvPGN.
1. Why ?
=========
You want to start coding for PvPGN ? Why ? What do you expect to get from it ?
Answer yourself this questions and then go to the next paragraph.
2. History
===========
PvPGN has started as a game server emulation project, taking the excelent's
bnetd project source and working on it. While initially it has started as
a War3 emulation patch over bnetd it become a lot more than that (lots of
new features, lots of code changes organizing). Because of the code roots
you will notice a lot of things still carrying the "bnetd" word (most notable
examples are the main servers program file called "bnetd" or the main server
configuration file called bnetd.conf). We considered that as a sign of respect
from us to the bnetd coders we keep their names on the code they written (but
on the new code of course we may name them different).
Arround the time of pvpgn 1.7.9 release we have branched the codes and started
to work on pvpgn 2.x, the next big version, a C++ based version.
3. Objective
=============
PvPGN's main objective is to support many gaming network protocols so that
users can setup LAN parties and Internet gaming communities. As such it
focuses mainly on those protocols that are both popular and where the
client vendor (game maker) has NOT released any kind of server software that
you can run to achieve the goal above (like Battle.Net, Westwood Online, etc).
The current status is almost complete support for Battle.Net (all games) and
limited Westwood Online support. We also offer the possibility to connect to
PvPGN using non-gaming related protocols like IRC and telnet for better
interoperability regarding the chat feature of the server (one of the main
features as a gaming server).
4. Layout of files
===================
Note: Starting here on you may find lots of terms and wors which may sound
"strange" to you and for this reason we have included a glossary of terms
in the end of this file.
The PvPGN project consists of a main server (called "bnetd") and various
other programs and (little) servers (ex. bnchat, d2cs, d2dbs etc...).
PvPGN follows the bnetd's layout of files:
./bin -> used to store binaries (after compilation)
./conf -> configuration files (many of them templates)
./files -> various files needed for clients connecting
./man -> outdated man pages :(
./sbin -> same as ./bin
./scripts -> various scripts for testing/managing pvpgn
./src -> the source main directory
./src/bnetd -> source files used only by the main server
./src/common -> source files used in common by different programs
./src/compat -> source files concerning portability of code
...
PvPGN uses automake/autoconf build and platform portability system. This is
the main build method for UNIX/POSIX builds. For Win32 we offer the option to
build using mingw tools.
5. Coding Style
================
a. General
Because PvPGN is developed mainly on POSIX/Linux systems there are some
things specific to this type of platform (like the line ending of all
text/source files to be "Unix-style").
One thing which is overlooked by newbie coders is the "esthetical" side of the
code. It may not be so important to many people (which code on the idea "if it
works then its good") but for us, coding on PvPGN is VERY important. When you
are coding for PvPGN PLEASE try to make your code look similar to already
written code (this includes identing, identificator names, etc...). Keeping
the code look "the same" makes its reading a lot more easier so, finding
bugs easier so coding better.
Other overlooked aspect for newbie coders is code replication. Please DONT
copy and paste code arround !!! If you need to copy common functionality from
some place, think about making some API of that functionalilty, put it in
some classes and use it from both places. I repeat, DONT replicate code.
When allocating memory (or some other kind of resource like a file, etc)
inside a function always free it (release the resource) in the same function
before its exit (exceptions: the function returns the allocated memory/resource in
which case the calling function should take care of the allocated memory;
or the allocated memory is cached/stored somewhere to be used later, in which
case you must make sure it will be free()d when not needed anymore).
In the startup code of any "external" function (function which may be called
from other modules then the one containing it or a public method ) please
check ALL the input parameters (you will notice how PvPGN already does that
in such functions). In case of errors detected in such cases (and not only)
code should "throw" an exception (see section 5.d).
When developing code you should compile always with "all warnings" enabled
and try to fix them (some warnings uncover real bugs), like for gcc use
"-Wall". When running/testing the codes you should use a memory debugger like
valgrind. For more details about how to run pvpgn check Appendix B.
To allocate/free memory use ONLY new/delete calls (this makes sure that out
of memory condition is detected and nicely handled because in that case
"new" automatically "throws" std::bad_alloc exception).
b. Indenting
PvPGN indentation should be done ONLY with "tabs" (exception see below), the
tab size is 8 "spaces".
We indent a level after if, while, do-while, for, case (but NOT from switch),
class/struct/union (but NOT the access modifier public/protected/private) and
a starting brace. The position of the starting brace relative to the keywords
from above is at the same level as the keyword.
We except to indent after switch, namespace braces and the access modifiers
because otherwise things will look "too indented" and make codes less readable.
c. Functions (methods/constructors) and their parameters
Function definitions should have the type returned by the function on it's own
line than the function name and it's parameter list. Like in:
const std::string&
MyClass::getString()
{
}
Function parameters should be passed by reference for "aggregate" types (like
class/struct/union). This reference usually is const, ie "const Type& param".
Accessors should return references to aggregate members (this is safe because
we presume the object's life time exceeds the reference cacher's use time, if
not the calling codes can just cache a copy of the object).
Functions should never have their bodies in the .h file (exception, class and
function templates that NEED to be in the .h file completely).
d. Exceptions and error handling
"Errors are not always exceptions". Keep this in mind!
But generally, errors are exceptions. In the common sense and in turn, in the
codes. In case of errors (that is errors that shouldnt happen too often, not
common case errors) the codes should throw an exception. Exceptions allows for
a very clean and versatile way to detach the place of error detection to the
place of error handling (in C you detect an error in some low-level codes and
you report it in the return value and this is checked and propagated by checks
to the higher level codes that will probably handle the error). Also with C++
exceptions the "normal" code flow it's NOT disrupted and polluted by trying to
handle errors everywhere you might hit one. Exceptions basically offer an
alternative SLOW code path. It is slow (much slower than normal code flow) and
this is why it should be used to signal relatively "rare" events (thus called
exceptions) and not something "common".
But using exceptions you might be carefull with them to not "leak" resources.
The problem is that if you allocate some resource in some codes and then you
might get an exception (not even from your codes but indirectly from codes you
call) you need to make sure the resources aquired are released BEFORE the
exception propagates out of your code scope. This is _easily_ done with C++
and the RAII principle
(http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization). The
rule is simple, ALL resource aquisitions should be done using some kind of
wrappers that release the resource in their destructor. This also makes codes
a lot more simple when exiting code scope on non-error cases because you do
NOT need to explicitely release the "wrapped" resource as it will release
automatically when leaving the code scope.
You are allowed ONLY to throw objects, NEVER throw basic types. All the
exceptions you throw HAVE to be derived from std::exception (thus providing
the .what() method to get a C-string of the description of the error). It is
best however, you derivate from std::runtime_error & co. You MAY create your
own exception class (with the limitations from above) IF AND ONLY IF your
exception represents an error that NEEDS to be handled differentely (ie
catched separately from other exceptions). All such user defined exception
classes should be defined NESTED in the class that throws them.
Catching exceptions should ONLY be done using the "const reference" catching
scheme like in:
} catch (const std::exception& ex) {
This allows for 2 important things to happen:
- it allows proper polymorphic mechanisms for exceptions (so with the example
above ANY exception derived from std::exception is caught and handled as it
should be)
- it behaves nicely on out of memory condition ie an ISO C++98 compliant
compiler even when out of memory condition is reached has to provide ENOUGH
memory to create a temporary object that (and this is what you do when
throwing an exception to be caught with reference, you throw like
throw MyException(); whcih means it creates a temporary object MyException()
and that object reference is passed to the handlers)
e. Namespaces
All pvpgn codes will belong to the "pvpgn" namespace. Specific application
codes will belong to a namespace inside "pvpgn" like "pvpgn::bnetd". Common
codes need to still be just in "pvpgn" so all other codes will "see" them.
Never ever use a "using" keyword inside a .h file. Inside .cpp files you
_may_ use them.
Some types, classes, etc are better to be declared inside some other class
(like the exceptions, see the above section or smart pointers for the class).
"static" keyword to declare "local" variables and functions it's not allowed.
Instead of it, use unnamed namespaces. A ISO C++98 compliant compiler has to
provide for each separate translation unit an unique name for the unnamed
namespace that could be declared in that translation unit. As such, instead of
------------------------
static int myvar;
static int myfunc(int);
extern int externfunc()
{
}
static int myfunc()
{
}
------------------------
Make it
------------------------
namespace
{
int myvar;
int myfunc(int);
}
extern int externfunc()
{
}
namespace
{
int myfunc(int)
{
}
}
------------------------
f. Identifiers
Class names should start with a capital and all words composing it should too
(Java-like style, like in "MyStuff"). All methods should start with a lowercase
letter and then all words composing it with uppercase. Accessor methods should
start with get/set and then the name of the member accessed with the case
rules already mentioned.
g. Include order
In .cpp files the first include should be "common/setup_before.h" followed by
the coresponding header of the cpp file (ie in clan.cpp after setup_before.h
you include clan.h).
Then you include (this applies to headers too not only .cpp now) the headers
of standard C++ lib, then the headers from C90 as offered by standard C++
(ex. <cstring> etc), then the POSIX/win32/system dependant headers.
h. Code Flow
<to be written, not sure how it will be after the complete C++ conversion>
Appendix A. Glossary of terms
===============================
* autoupdate: the feature of Battle.Net servers to send a (MPQ, see MPQ) file
to the client which after downloading it, it is used to update the client
* connection class: when a connection is established to a bnet listening
address the client sends an initial byte which tells the server of what class
of connection the following packets will be; classes of connections determine
what packets can go through that connection.
* MPQ: a format archive which is used by Blizzard clients and Battle.Net
servers. This files are used for containing verious files (sound/graphics in
clients, AI scripts, update patches etc...)
* versioncheck: also know as vcheck and sometimes just vc ; a client
verification system used by Battle.Net servers to identify client version and
"purity". Based on this the server may accept/refuse connection or ask for
upgrade (see autoupdate).
Appendix B. How to run PvPGN for debugging
===========================================
It is very helpfull in finding out memory coruption bugs as soon as possible
so while developing codes or just when running a server it is good that you
use some memory coruption run-time debuggers. I dont know about Win32 users
but on Unix/Linux there are some good options.
1. valgrind (http://valgrind.kde.org)
Valgrind is not very portable (only x86, Linux and very recently FreeBSD),
also it slows down the debugged codes (it acts like a CPU emulator so it
has to do that) but I have yet to find out a better debugging tool for what
he does. Valgrind is so cool that recently many OSS projects use it for
finding out bugs in their codes. For more information you can check out their
web page. I will focus on valgrind with PvPGN.
After you have compiled and installed valgrind (it's easy, ./configure, make,
make install) you will use it by running PvPGN like this:
$ valgrind --tool=memcheck --num-callers=10 /path/to/bnetd -f 2> valg.out
"num-callers" makes valgrind record backtraces with 10 entries and is usually
needed with PvPGN which has not very small backtrace path :)
Another option you might want to use is "--leak-check=yes" and probably
"--leak-resolution=high". This options make valgrind even slower but they
will give memory leak information when PvPGN exits.
I encourage EVERYONE to use it if available (that is if you run PvPGN on
a supported platform). Only very big servers won't be able to do it because
there is no hardware powerfull enough to run a big server with valgrind (but
big means over 500 users online). You should test it with your server and if
it does not make your bnetd go over 90% CPU then you should be fine. If you
cannot run valgrind for any reason or if you are hunting for some bugs
valgrind cannot find (yes, valgrind is superb but there is a class of bugs,
especially overflows which valgrind can't help you with) you should then try
the next debugging tool.
Appendix C. How to generate and use "core" files
=================================================
This appendix is for Unix users. I dont know if other platforms have similar
features, that when the program crashes unexpectedly the OS would dump the
contents of the memory of the crashing process into a disk file for later
inspection.
First make sure that PvPGN has been compiled with debugging ("-g" parameter
to gcc) and with no optimizations ("-O0" parameter to gcc). PvPGN default
build process puts "-g -O2" so you need to edit Makefile file before compile
and change it to "-g -O0". Then something like "make clean; make".
On Unix/Linux to be able to get core dumps you first need to make sure your
core file size limit is set acordingly. Use "ulimit -c" for querying and
setting this limit (I recommend setting it to "unlimited"). After that when
you start PvPGN make sure you are in a directory where you have write access
(so the OS when it wants to do the core dump it will be allowed to do so).
The last thing to do is when starting PvPGN make sure it starts in FOREGROUND,
example : /path/to/bnetd -f . If you did all this then when PvPGN will crash
you will get a core dump. On linux this is a file called "core", on *BSD it's
called <processname>.core (for bnetd that means it's called bnetd.core).
Now that you got a core file it is time to use it to identify what happened
wrong in the crashing process. We use gdb (the GNU debugger, should be
available on all Unices) to do this. Run gdb like this:
$ gdb /path/to/bnetd /path/to/corefile
Then gdb should startup, print out a lot of messages and stop after printing
a file and line number and quoting some C code where the crash has happened.
You can find out a lot more information than this. Run gdb's command "bt full"
and it will display a full backtrace of the moment of the crash. The backtrace
will contain how the functions were called along the way (their parameters),
and also any local variables. If you do not know what to do next from here
contact a PvPGN developer and give him exactly that backtrace dump, he should
know more.