Please open an issue first discussing the idea before starting to write code. It would be unfortunate if you spend time working on a contribution that does not align with the ideals of the DDNet project.
A non-exhaustive list of things that usually get rejected:
- Extending dummy with new gameplay-affecting features. ddnet#8275 ddnet#5443 (comment)
- Breaking backwards compatibility in the network protocol or file formats such as skins and demos.
- Breaking backwards compatibility in gameplay:
- Existing ranks should not be made impossible.
- Existing maps should not break.
- New gameplay should not make runs easier on already completed maps.
There are a few style rules. Some of them are enforced by CI and some of them are manually checked by reviewers. If your github pipeline shows some errors please have a look at the logs and try to fix them.
Such fix commits should ideally be squashed into one big commit using git commit --amend
or git rebase -i
.
A lot of the style offenses can be fixed automatically by running the fix script ./scripts/fix_style.py
With the exception of base/system.{h,cpp}
For single words
int length = 0;
❌int Length = 0;
✅
For multiple words:
int maxLength = 0;
❌int MaxLength = 0;
✅
❌ Avoid:
for(int i = 0; i < MAX_CLIENTS; i++)
{
for(int k = 0; k < NUM_DUMMIES; k++)
{
if(k == 0)
continue;
m_aClients[i].Foo();
}
}
✅ Instead do:
for(int ClientId = 0; ClientId < MAX_CLIENTS; ClientId++)
{
for(int Dummy = 0; Dummy < NUM_DUMMIES; Dummy++)
{
if(Dummy == 0)
continue;
m_aClients[ClientId].Foo();
}
}
More examples can be found here
DDNet inherited the hungarian notation like prefixes from Teeworlds
m_
Class member
g_
Global variable
s_
Static variable
p
Pointer
a
Fixed array
Combine them appropriately. Class Prefixes
C
Class, CMyClass, This goes for structures as well.
I
Interface, IMyClass
Only use those prefixes. The ddnet code base does NOT follow the whole hungarian notation strictly.
Do NOT use c
for constants or b
for booleans or i
for integers.
Examples:
class CFoo
{
int m_Foo = 0;
const char *m_pText = "";
void Func(int Argument, int *pPointer)
{
int LocalVariable = 0;
};
};
Do not use the goto
keyword in new code, there are better control flow constructs in C++.
Do not set variables in if statements.
❌
int Foo;
if((Foo = 2)) { .. }
✅
int Foo = 2;
if(Foo) { .. }
Unless the alternative code is more complex and harder to read.
Default arguments tend to break quickly, if you have multiple you have to specify each even if you only want to change the last one.
Try finding descriptive names instead.
While the code base already has a lot of methods that start with a Get
prefix. If new getters are added they should not contain a prefix.
❌
int GetMyVariable() { return m_MyVariable; }
✅
int MyVariable() { return m_MyVariable; }
Instead of doing this ❌:
class CFoo
{
int m_Foo;
};
Do this instead if possible ✅:
class CFoo
{
int m_Foo = 0;
};
DDNet balances in being portable (easy to compile on all common distributions) and using modern features. So you are encouraged to use all modern C++ features as long as they are supported by the C++ version we use. Still be aware that in game loop code you should avoid allocations, so static buffers on the stack can be preferable.
Examples:
- Use
nullptr
instead of0
orNULL
.
Do not use int
as return type for methods that can either succeed or fail.
Use bool
instead. And true
means success and false
means failure.
See ddnet#6436
Code file names should be all lowercase and words should be separated with underscores.
❌
src/game/FooBar.cpp
✅
src/game/foo_bar.cpp
Describe the change your contribution is making for the player/user instead of talking about what you did in a technical sense. Your PR messages will ideally be in a format that can directly be used in the change log.