diff options
| author | Ethan Morgan <ethan@gweithio.com> | 2026-02-14 16:44:06 +0000 |
|---|---|---|
| committer | Ethan Morgan <ethan@gweithio.com> | 2026-02-14 16:44:06 +0000 |
| commit | 54409423f767d8b1cf30cb7d0efca6b4ca138823 (patch) | |
| tree | d915ac7828703ce4b963efdd9728a1777ba18c1e /CONTRIBUTING.md | |
Diffstat (limited to 'CONTRIBUTING.md')
| -rw-r--r-- | CONTRIBUTING.md | 621 |
1 files changed, 621 insertions, 0 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8ba7cf8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,621 @@ +# C++ Coding Style Guide for OpenMB + +This document outlines the C++ coding standards for the OpenMB project. All contributors are expected to follow these guidelines to maintain consistency and code quality. + +## Table of Contents + +- [Code Formatting](#code-formatting) +- [Naming Conventions](#naming-conventions) +- [Code Organization](#code-organization) +- [Best Practices](#best-practices) +- [Tooling](#tooling) + +## Code Formatting + +### Automated Formatting + +OpenMB uses **clang-format** to enforce consistent code formatting. The configuration is defined in `.clang-format` at the repository root. + +**Before submitting a merge request:** +```bash +# Format your changed files +clang-format -i path/to/your/file.cpp +clang-format -i path/to/your/file.hpp +``` + +### Key Formatting Rules + +#### Brace Style +We use **Allman/BSD style** - braces on their own line for all constructs: + +```cpp +// Classes +class MyClass +{ +public: + void myMethod(); +}; + +// Functions +void MyClass::myMethod() +{ + // implementation +} + +// Control statements +if (condition) +{ + doSomething(); +} +else +{ + doSomethingElse(); +} + +// Namespaces +namespace MyNamespace +{ + // content +} +``` + +#### Indentation +- **4 spaces** (no tabs) +- All namespaces are indented +- Access modifiers are outdented by 4 spaces from the class body + +```cpp +namespace MBWorld +{ + class MyClass + { + public: // Outdented by 4 spaces + void publicMethod(); + + private: + int mMemberVariable; + }; +} +``` + +#### Line Length +- Maximum **120 columns** +- Break long lines logically, preferring to break before operators + +```cpp +// Good +bool result = someVeryLongCondition + && anotherCondition + && yetAnotherCondition; + +// Bad - exceeds 120 columns +bool result = someVeryLongCondition && anotherCondition && yetAnotherCondition && evenMoreConditions; +``` + +#### Pointer and Reference Alignment +- **Left-aligned** with the type + +```cpp +// Good +Ptr* ptr; +const std::string& name; + +// Bad +Ptr *ptr; +const std::string &name; +``` + +#### Spacing +- Space after control statement keywords: `if (`, `for (`, `while (` +- No space after function names: `myFunction(args)` +- Single space before trailing comments +- No spaces inside parentheses, brackets, or angle brackets + +```cpp +// Good +if (condition) +{ + myFunction(arg1, arg2); + std::vector<int> numbers; // A comment +} + +// Bad +if( condition ){ + myFunction (arg1,arg2); + std::vector < int > numbers;//A comment +} +``` + +## Naming Conventions + +These conventions are enforced by **clang-tidy**. + +### Classes and Structs +**PascalCase** - capitalize the first letter of each word + +```cpp +class CreatureStats { }; +class NpcStats { }; +struct CellState { }; +``` + +### Functions and Methods +**camelCase** - lowercase first letter, capitalize subsequent words + +```cpp +void processData(); +bool isValid() const; +float getMaxSpeed(const Ptr& ptr) const; +``` + +### Member Variables +Prefix with `m` followed by **PascalCase** + +```cpp +class MyClass +{ +private: + int mHealthPoints; + std::string mPlayerName; + bool mIsActive; + ESM::RefId mRaceId; +}; +``` + +### Local Variables and Parameters +**camelBack** (same as functions) + +```cpp +void processPlayer(const Ptr& playerPtr, bool forceUpdate) +{ + int currentHealth = getHealth(playerPtr); + std::string characterName = getName(playerPtr); +} +``` + +### Namespaces +**PascalCase** + +```cpp +namespace MBWorld { } +namespace MBMechanics { } +namespace ESM { } +``` + +**Exception:** External library namespaces may use their own conventions (e.g., `osg`, `osgDB`) + +### Constants and Enums +**PascalCase** for enum types, members follow context + +```cpp +enum class Specialization +{ + Combat, + Magic, + Stealth +}; + +// Enum class members use PascalCase +enum RecordFlag +{ + Persistent = 0x0400, + Deleted = 0x0020 +}; +``` + +### Template Parameters +**PascalCase** + +```cpp +template <class T> +class Record : public RecordBase +{ + T mData; +}; +``` + +## Code Organization + +### Header Files + +#### Include Guards +Use traditional `#ifndef` include guards: + +```cpp +#ifndef OPENMB_COMPONENTS_ESM_UTIL_H +#define OPENMB_COMPONENTS_ESM_UTIL_H + +// Header content + +#endif +``` + +**Format:** `OPENMB_<PATH_TO_FILE>_H` +- Replace directory separators with underscores +- Use uppercase +- Path should be relative to project root (apps/ or components/) + +#### Include Order +1. Related header (for .cpp files) +2. C system headers +3. C++ standard library headers +4. External library headers +5. Project headers + +```cpp +#include "myclass.hpp" // Related header first + +#include <cmath> +#include <cstdint> + +#include <string> +#include <vector> + +#include <osg/Vec3f> +#include <MyGUI_Gui.h> + +#include <components/esm/records.hpp> +#include "../mbbase/environment.hpp" +``` + +#### Header Structure +```cpp +#ifndef OPENMB_APPS_OPENMB_MBWORLD_CLASS_H +#define OPENMB_APPS_OPENMB_MBWORLD_CLASS_H + +// Includes + +// Forward declarations +namespace ESM +{ + class ESMReader; +} + +namespace MBWorld +{ + class Ptr; + + /// Brief class description + class MyClass + { + public: + MyClass(); + ~MyClass(); + + /// Brief method description + /// \param ptr Description of parameter + /// \return Description of return value + bool myMethod(const Ptr& ptr) const; + + private: + int mData; + }; +} + +#endif +``` + +### Source Files + +```cpp +#include "myclass.hpp" + +#include <other/includes> + +namespace MBWorld +{ + MyClass::MyClass() + : mData(0) + { + } + + bool MyClass::myMethod(const Ptr& ptr) const + { + // Implementation + return true; + } +} +``` + +## Best Practices + +### Modern C++ (C++20) + +OpenMB targets **C++20**. Use modern C++ features appropriately: + +```cpp +// Use auto for complex types +auto iterator = myMap.find(key); + +// Use smart pointers +std::unique_ptr<Dialog> mDialog; + +// Use range-based for loops +for (const auto& item : container) +{ + process(item); +} + +// Use nullptr instead of NULL or 0 +Ptr* ptr = nullptr; + +// Use override keyword +void myMethod() override; + +// Use constexpr where appropriate +constexpr int MaxValue = 100; +``` + +### Const Correctness + +Be diligent about const correctness: + +```cpp +class MyClass +{ +public: + // Const methods don't modify the object + int getValue() const { return mValue; } + + // Use const references for parameters + void setName(const std::string& name); + + // Use const pointers when appropriate + void process(const MBWorld::ConstPtr& ptr) const; + +private: + int mValue; +}; +``` + +### Virtual Functions + +- Always use `override` keyword for overridden virtual functions +- Use `const` on virtual functions when they don't modify state +- Add `= 0` for pure virtual functions + +```cpp +class Base +{ +public: + virtual ~Base() = default; + virtual void myMethod() = 0; +}; + +class Derived : public Base +{ +public: + void myMethod() override; // Use override, not virtual +}; +``` + +### Exception Handling + +Use exceptions for error conditions: + +```cpp +void MyClass::someMethod() +{ + if (!isValid()) + { + throw std::runtime_error("Invalid state"); + } +} +``` + +### Comments and Documentation + +Use Doxygen-style comments: + +```cpp +/// Brief description of the class +class MyClass +{ +public: + /// Brief description of the method + /// \param input Description of parameter + /// \return Description of return value + int processData(int input); + + int mPublicMember; ///< Brief description after member +}; + +// Regular comments for implementation details +// This algorithm uses a binary search because... +``` + +### Memory Management + +- Prefer stack allocation over heap when possible +- Use smart pointers (`std::unique_ptr`, `std::shared_ptr`) instead of raw `new`/`delete` +- Follow RAII principles + +```cpp +// Good - automatic cleanup +{ + std::unique_ptr<Dialog> dialog = std::make_unique<Dialog>(); + dialog->show(); +} // Automatically deleted + +// Bad - manual memory management +Dialog* dialog = new Dialog(); +dialog->show(); +delete dialog; // Easy to forget or miss in error paths +``` + +### Error Handling in Base Classes + +Use exceptions to indicate unsupported operations: + +```cpp +class Base +{ +public: + virtual ContainerStore& getContainerStore(const Ptr& ptr) const + { + throw std::runtime_error("class does not have a container store"); + } +}; +``` + +### Type Aliases + +Use clear type aliases for complex types: + +```cpp +using ExtraList = std::vector<ExtraPtr>; +using OwnerMap = std::map<Owner, int>; +``` + +## Tooling + +### clang-tidy + +OpenMB uses **clang-tidy** for static analysis. The configuration is in `.clang-tidy`. + +```bash +# Run clang-tidy on your files +clang-tidy path/to/your/file.cpp -- -I/path/to/includes +``` + +**Key checks enabled:** +- `portability-*` - Cross-platform compatibility +- `clang-analyzer-*` - Static analysis +- `modernize-avoid-bind` - Modern C++ practices +- `readability-identifier-naming` - Naming conventions + +### Running Checks Locally + +```bash +# Format code +clang-format -i apps/openmb/myfile.cpp + +# Check formatting without modifying +clang-format --dry-run --Werror apps/openmb/myfile.cpp + +# Run clang-tidy +clang-tidy apps/openmb/myfile.cpp +``` + +## Common Pitfalls + +### Don't Mix Concerns +- One class = one responsibility +- One commit = one logical change +- Don't combine formatting changes with functional changes + +### Avoid Unnecessary Changes +- Don't reformat code you didn't modify +- Don't change whitespace in unrelated files +- Focus changes on what's necessary for your feature/fix + +### Platform Compatibility +- OpenMB runs on Windows, Linux, and macOS +- Avoid platform-specific code unless absolutely necessary +- Use CMake for build configuration +- Test on multiple platforms if possible + +## Examples + +### Good Class Example + +```cpp +#ifndef OPENMB_APPS_OPENMB_MBWORLD_MYCLASS_H +#define OPENMB_APPS_OPENMB_MBWORLD_MYCLASS_H + +#include <string> +#include <vector> + +#include <components/esm/refid.hpp> + +namespace MBWorld +{ + class Ptr; + + /// Manages player inventory and equipment + class InventoryManager + { + public: + InventoryManager(); + ~InventoryManager() = default; + + /// Add an item to the inventory + /// \param itemId The ID of the item to add + /// \param count Number of items to add + /// \return True if successful + bool addItem(const ESM::RefId& itemId, int count); + + /// Check if an item is in inventory + /// \param itemId The ID to check + /// \return True if item exists in inventory + bool hasItem(const ESM::RefId& itemId) const; + + /// Get the total weight of all items + float getTotalWeight() const; + + private: + std::vector<ESM::RefId> mItems; + float mCurrentWeight; + int mMaxCapacity; + }; +} + +#endif +``` + +### Good Implementation Example + +```cpp +#include "inventorymanager.hpp" + +#include <algorithm> + +#include "../mbbase/environment.hpp" +#include "../mbworld/esmstore.hpp" + +namespace MBWorld +{ + InventoryManager::InventoryManager() + : mCurrentWeight(0.0f) + , mMaxCapacity(100) + { + } + + bool InventoryManager::addItem(const ESM::RefId& itemId, int count) + { + if (count <= 0) + { + return false; + } + + const MBWorld::ESMStore& store = *MBBase::Environment::get().getESMStore(); + + // Add item logic here + mItems.push_back(itemId); + + return true; + } + + bool InventoryManager::hasItem(const ESM::RefId& itemId) const + { + return std::find(mItems.begin(), mItems.end(), itemId) != mItems.end(); + } + + float InventoryManager::getTotalWeight() const + { + return mCurrentWeight; + } +} +``` + +## Additional Resources + +- [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) +- [Clang Format Documentation](https://clang.llvm.org/docs/ClangFormat.html) +- [Clang Tidy Documentation](https://clang.llvm.org/extra/clang-tidy/) + +## Questions? + +If you're unsure about any aspect of the coding style, look at existing code in the codebase for examples. + +Remember: Consistency is more important than personal preference. When in doubt, follow the existing patterns in the codebase. |