Version 55 (modified by Freagarach, 2 years ago) ( diff )



The goal of these coding conventions is to encourage consistency within the code, rather than claiming some particular style is better than any other. As such, the main guideline is:

  • When modifying a piece of code, try to follow its existing style. In particular:
    • Primarily, try to match the style of the functions that you're editing (assuming it's at least self-consistent and not too bizarre), in order to avoid making it less self-consistent.
    • Secondly, try to match the style of the files that you're editing.
    • Then, try to match the style of the other code in the subdirectory you're editing.
    • Finally, try to match the global guidelines discussed on this page.

Our code is currently not entirely consistent in places, but the following guidelines attempt to describe the most common style and are what we should converge towards. (Obviously we always want clean, readable, adequately-documented code - lots of articles and books already talk about how to do that - so here we're mostly describing minor details.)


  • Use the US variant of English when choosing variable names. For example, use 'color' instead of 'colour'. See also the English style guide.


Creating new files

  • All source files (.cpp, .h) must start with the following GPL license header, before any other content:
    /* Copyright (C) 2018 Wildfire Games.
     * This file is part of 0 A.D.
     * 0 A.D. is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 2 of the License, or
     * (at your option) any later version.
     * 0 A.D. is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * GNU General Public License for more details.
     * You should have received a copy of the GNU General Public License
     * along with 0 A.D.  If not, see <>.
    replacing 2018 with the year that the file was last updated.

Exception: Code in source/lib/ (and a few other files) should use the MIT license instead:

/* Copyright (C) 2018 Wildfire Games.
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
  • Wrap header files in include guards, using the name INCLUDED_filename, e.g. the file Foo.h should say:
    #ifndef INCLUDED_FOO
    #define INCLUDED_FOO
    #endif // INCLUDED_FOO
  • All source files must have the svn:eol-style property set to native


  • Use tabs for indentation, not spaces.
  • For any alignment within a line of code (as opposed to indentation at the start), use spaces, not tabs.
  • Indent braces and use whitespace like
    int CExampleObject::DoSomething(int value)
        if (value != 0)
            m_Value = value;
        for (int i = 0; i < value; ++i)
            DoSomethingElse(i, (unsigned int)value);
        return value;
    Exception: Code in source/lib/ omits the space before the '(' in statements like "if(...)", "while(...)", etc.
  • Try to avoid very wide lines. Typical wrapping points are 80 characters, or 120, or 132, etc. (There's no strict limit - aim for whatever seems most readable.)
  • Write switch statements like
    switch (n)
    case 10:
        return 1;
    case 20:
        // fall through to next case    [this should be explicit if you don't end with break or return]
    case 30:
    case 40:
        int i = n*2;       // [need the extra {...} scope when declaring variables inside the case]
        return i;
    case 50:
        int z = n*2;
        n = foobar(n, z);
        break;             // [place breaks inside the brackets]
        debug_warn(L"invalid value for n");   // [only do this kind of warning if this case is an engine bug]

Error reporting

See Logging for more info.

  • For engine bugs (that is, error cases which need to be fixed by C++ developers, and which should never be triggered by users or modders (even if they write invalid data files)), use "debug_warn(L"message")" to report the error. (This pops up an ugly dialog box with stack trace and continue/debug/exit buttons.)
  • For error cases that could be triggered by modders (e.g. invalid data files), use
    LOGERROR("Failed to load item %d from file %s", i, path);
    This gets display on screen in red, and saved in the log files. LOGERROR takes printf-style format strings (specially cppformat). Exception: Code in source/lib/ can't use LOGERROR (it can only use things defined in source/lib/).
  • The engine should try to cope gracefully with LOGERROR cases, e.g. abort loading the current file; it should never crash in those cases.


  • Use Doxygen comments (explained here as JavaDoc style), e.g.
     * A dull object for demonstrating comment syntax.
    class CExampleObject
         * Sets the object's current value to the passed value, if it's non-zero.
         * @param v the new value to set it to, or 0 to do nothing.
         * @return the value that was passed in.
        int DoSomething(int v);
        /// Current value (always non-zero)
        int m_Value;
  • Try not to repeat class names or function names in the descriptions, since that's redundant information.
  • Try to avoid very wide lines. As wrapped block comments are extremely unreadable. Always try to keep each line of those below 80 chars (counting tabs as 4 chars).
  • Don't need to bother documenting every line of code or every member function or every member variable; only when it'll add to a competent reader's understanding of the program.


  • Use CStr instead of std::string. Use CStrW instead of std::wstring. (These are subclasses of std::[w]string with various extra methods added for convenience.)
    • Exception: source/lib/ and source/simulation2/ and source/scriptinterface/ tend to prefer std::[w]string instead.
    • CStr8 is an alias for CStr. Prefer to use CStr.
  • Compare strings using == (not e.g. " == 0"). Compare to literals directly (e.g. "someCStrVariable == "foo"", not "someCStrVariable == CStr("foo")").
  • For portability, use the following formats for printf-style functions:
    printf("%s", "char string");
    printf("%ls", L"wchar_t string");
    wprintf(L"%hs", "char string");
    wprintf(L"%ls", L"wchar_t string");
  • In AtlasUI, you should prefer the wxWidgets API and construct strings like this (see the wxWiki for more examples):
    wxString str = _T("SomeUnicodeSTRING"); // Don't use wxT() directly because it breaks OS X build
    wxString translatedStr = _("A string that may be translated"); // Translated string for UI purposes
    // Sometimes you have to pass messages to the engine and need C++ strings:
    std::string cppStr = str.c_str();
    std::wstring cppWStr = str.wc_str(); // Don't use c_str() because it breaks OS X build

Preprocessor instructions

The #include instructions, placed after the license notice, should follow a consistent order (which is currently not the case). Between each section in the following list, we usually add an empty line.

  1. The first non-comment line of any source file must be #include "precompiled.h"
  2. For .cpp files that contain function implementations, headers that declare those functions should be included here. For instance, #include "Simulation2.h" for Simulation2.cpp, or #include "ICmpPosition.h" for CCmpPosition.cpp.
  3. Include the rest of the files that come from 0 A.D. Write the complete path relative to source/, for instance #include "simulation2/Simulation2.h". Use case-insensitive alphabetical order.
  4. Include system libraries, in case-insensitive alphabetical order.

Example, for the file source/simulation2/Simulation2.cpp (this is not the actual list, but an example that should cover all cases):

#include "precompiled.h"

#include "Simulation2.h"

#include "graphics/Terrain.h"
#include "lib/file/vfs/vfs_util.h"
#include "lib/timer.h"
#include "maths/MathUtil.h"
#include "ps/Loader.h"
#include "ps/lowercase.h"
#include "ps/Profile.h"
#include "ps/XML/Xeromyces.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptRuntime.h"
#include "simulation2/components/ICmpAIManager.h"
#include "simulation2/components/ICmpCommandQueue.h"
#include "simulation2/components/ICmpTemplateManager.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/system/ComponentManager.h"
#include "simulation2/system/ParamNode.h"

#include <iomanip>
#include <string>


  • In header files, avoid #include and use forward declarations wherever possible.
  • Class names are UpperCamelCase and prefixed with C, e.g. CGameObject. Member functions are UpperCamelCase, e.g. CGameObject::SetModifiedFlag(...). Member variables are UpperCamelCase prefixed with m_, e.g. CGameObject::m_ModifiedFlag. Files are named e.g. GameObject.cpp, GameObject.h, usually with one major class per file (possibly with some other support classes in the same files). Local variables and function parameters are lowerCamelCase. Structs are treated similarly to classes but prefixed with S, e.g. SOverlayLine.
  • Write pointer/reference types with the symbol next to the type name, as in
    void example(
      int* good,
      int& good,
      int *bad,
      int &bad
  • Use STL when appropriate.
  • Don't use RTTI (dynamic_cast etc). Exception: source/tools/atlas/AtlasUI/ can use RTTI.
  • Avoid global state: global variables, static variables inside functions or inside classes, and singletons.
    • When a module needs access to objects from outside its own environment, prefer to pass them in explicitly as arguments when instantiating that module, rather than making the objects global and having the module reach out to grab them.
    • When unavoidable, global variables should be named with a g_ prefix.
    • Prefer global variables over singletons, because then they're not trying to hide their ugliness.
  • Use nullptr for pointers (instead of NULL).
  • Don't do "if (p) delete p;". (That's redundant since "delete nullptr;" is safe and does nothing.)
  • If deleting a pointer, and it's not in a destructor, and it's not being immediately assigned a new value, use "SAFE_DELETE(p)" (which is equivalent to "delete p; p = nullptr;") to avoid dangling pointers to deleted memory.
  • Use "for range" loop instead of "std::for_each".
    // Avoid
    std::vector<T> anyVector;
    std::for_each(anyVector.begin(), anyVector.end(), [] (const T& element){
       // code
    // Better
    for (const T& element : anyVector)
       // code
  • Reminding the default value of parameters, if any, in a function definition can be useful.
    int Increase(int param, int optionalParam = 1);
    int Increase(int param, int optionalParam /* = 1 */)
       return param + optionalParam;
  • In c++11, it is possible to use the auto type-specifier, wherein the appropriate data-type is determined at compile time. Although potentially useful, overuse can cause serious problems in the long-run. Therefore, this code feature should be used in moderation. Specifically:
    • It should be clear from reading the code what type the auto is replacing. (Add a comment if necessary.)
    • It should only ever be used as a replacement for an iterator type.
    • It should only be used if the data-type-specifier it is standing in for is long. As a rule of thumb, if the line would be shorter than your chosen suitable line width (see convention on avoiding wide lines under formatting above) without it, don't use it.
  • Favour early returns where possible. The following:
    void foo(bool x)
        if (x)
            /* lines */
    is better when written like:
    void foo(bool x)
        if (!x)
        /* lines */


  • Use the same basic formatting as described above for C++.
  • Use roughly the same Doxygen-style comments for documentation as described above for C++. (But we don't actually run Doxygen on the JS code, so there's no need to make the comments use technically correct Doxygen syntax.)
  • When using Class syntax for JS:
    • Literals are stored in the prototype (e.g. BuildingAI.prototype.MaxPreferenceBonus = 2;).
    • The Schema is also a literal and hence is stored in the prototype.
      class Example
            this.value = 0;
      Example.prototype.Schema = 
      Example.prototype.MaxValue = 2;
      Engine.RegisterComponentType(IID_Example, "Example", Example);
  • Don't omit the optional semicolons after statements.
  • Use quotes around the key names in object literals:
    let x = 100, y = 200;
    let pos = { "x": x, "y": y };
  • Create empty arrays and objects with "[]" and "{}" respectively, not with "new Array()" and "new Object()".
  • Global variables and constants are named with a g_ prefix and the rest of the name in CamelCase, ie. var g_ParsedData or const g_BarterActions.
  • Omit zeroes after decimal point if possible. Use 1 instead 1.0 and 1.5 instead 1.50
  • To convert a string to a number, use the "+" prefix operator (not e.g. parseInt/parseFloat):
    let a = "1";
    let b = a + 1;     // string concatenation; b == "11"
    let c = (+a) + 1;  // numeric addition; c == 2
  • Always check for undefined properties and/or invalid object references, if it's possible they could occur.
  • To test if a property or variable is undefined, use explicit type+value equality (===), instead of value equality (==) or typeof():
    if ( === undefined)
      // foo is not defined
      // foo is defined
  • In general you don't want to explicitly check for null which has a distinct, often misunderstood, meaning from undefined. A few parts of the engine return a null object reference (for example, the component system when a component is not available for the specified entity, or the GUI when a requested object was not found), you can check for valid object references easily:
    if (!cmpFoo)
      // Oh it's not a valid component, don't use it
      // It is a valid component, we can use it
  • Use var only when declaring variables in the global scope. When inside functions, ifs, loops, etc., use let or const. We prefer let because:
    • It is restricted to the scope it is declared in, preventing unintentional clobbering of values
    • It is stricter than var, and throws an error if it a variable is declared twice within the same scope
        var x = 1;
        var x = "foo" // No Error
        let x = 1;
        let x = "foo"; // TypeError: redeclaration of let x
    • We prefer const when the value is not intended to be changed. This allows easier readability and is considered good practice to prevent accidental changing of values.
  • "Do not use let or const in global scope unless strictly necessary." to allow easier moddability. refs. #6094 (mostly a GUI concern)


  • Basically follow the JS formatting conventions.
  • When on the same line, insert spaces after {, and : and before }, but not after [ or before ]; e.g. { "foo": "bar" } and ["foo", "bar"].
  • Use tabs for indentation, not spaces.
  • The files should have the MIME-TYPE text/json (not application/json since that may break diffing).


  • All XML files should start with
    <?xml version="1.0" encoding="utf-8"?>
    and be UTF-8 encoded (preferably without a BOM but that doesn't really matter).
  • Empty-element tags should be written without a trailing space: use <foo/> or <foo bar="baz"/>, not <foo /> nor <foo bar="baz" />.
  • Indent using two spaces.
  • The files should have the mime-type text/xml.


  • Use the same basic formatting as described above for C++.
  • Use lowerCamelCase for names:
    // Bad
    vec2 local_position;
    mat4 CalculateInvertMatrix(...);
    int _pixelcounter;
    // Good
    vec2 localPosition;
    mat4 calculateInvertMatrix(...);
    int pixelCounter;
  • File extensions for shaders:
    • .fs - fragment shader
    • .vs - vertex shader
    • .gs - geometry shader
    • .cs - compute shader
  • All GLSL files should start with right version.
    #version 120
  • Prefer to use own uniforms instead of built-in (gl_ProjectionMatrix, gl_ModelViewProjectionMatrix, etc).
  • Strictly follow specifications for the selected version. Some drivers ignore errors, that can fail others.
  • Prefer to use swizzle masks:
    vec4 a;
    vec2 b;
    // Simple assign.
    a.x = b.x;
    a.y = b.z;
    // Assign with swizzle mask.
    a = b.xz;
  • Prefer to use prefixes for global variables to distinguish them: a_ - for attributes, v_ for varyings, u_ - for uniforms.
    uniform mat4 u_invertTransform;
    varying vec2 v_uv;
    attribute vec3 a_normal;
Note: See TracWiki for help on using the wiki.