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 /.github/copilot-instructions.md | |
Diffstat (limited to '.github/copilot-instructions.md')
| -rw-r--r-- | .github/copilot-instructions.md | 736 |
1 files changed, 736 insertions, 0 deletions
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..2249ade --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,736 @@ +# GitHub Copilot Instructions for OpenMB + +This document provides context and instructions for GitHub Copilot and other AI coding assistants when working on the OpenMB project. + +## Project Overview + +OpenMB is a C++20 game engine project that follows strict coding standards and conventions. When generating code or suggestions, always adhere to the guidelines outlined in this document. + +## Core Principles + +1. **Consistency over personal preference** - Follow existing patterns in the codebase +2. **Modern C++20** - Use modern C++ features appropriately +3. **Cross-platform compatibility** - Code must work on Windows, Linux, and macOS +4. **Clear documentation** - All public APIs should have Doxygen comments +5. **Memory safety** - Prefer RAII and smart pointers over manual memory management + +## Code Style Requirements + +### Formatting + +**CRITICAL: All code must follow these formatting rules:** + +```cpp +// Braces - ALWAYS on their own line (Allman/BSD style) +void myFunction() +{ + if (condition) + { + doSomething(); + } + else + { + doSomethingElse(); + } +} + +// NOT this: +void myFunction() { + if (condition) { + doSomething(); + } else { + doSomethingElse(); + } +} +``` + +- **Indentation**: 4 spaces (NEVER tabs) +- **Line length**: Maximum 120 columns +- **Pointer alignment**: Left-aligned with type (`Ptr* ptr`, not `Ptr *ptr`) +- **Spacing**: Space after control keywords (`if (`, `for (`, `while (`), no space after function names + +### Naming Conventions + +**CRITICAL: Follow these naming rules exactly:** + +```cpp +// Classes and Structs - PascalCase +class PlayerManager { }; +struct GameState { }; + +// Functions and Methods - camelCase +void processInput(); +bool isGameRunning() const; + +// Member Variables - 'm' prefix + PascalCase +class MyClass +{ +private: + int mHealthPoints; + std::string mPlayerName; + bool mIsActive; +}; + +// Local Variables and Parameters - camelBack +void updatePlayer(const Ptr& playerPtr, int deltaTime) +{ + int currentHealth = getHealth(playerPtr); + float movementSpeed = calculateSpeed(); +} + +// Namespaces - PascalCase with MB prefix for project namespaces +namespace MBWorld { } +namespace MBMechanics { } +namespace MBBase { } + +// Constants and Enums - PascalCase +enum class GameState +{ + Loading, + Running, + Paused +}; + +constexpr int MaxPlayers = 64; +``` + +### Include Guards + +**ALWAYS use this format for include guards:** + +```cpp +#ifndef OPENMB_<PATH_TO_FILE>_H +#define OPENMB_<PATH_TO_FILE>_H + +// Header content + +#endif +``` + +Example: For file `apps/openmb/mbworld/player.hpp` +```cpp +#ifndef OPENMB_APPS_OPENMB_MBWORLD_PLAYER_H +#define OPENMB_APPS_OPENMB_MBWORLD_PLAYER_H +``` + +### Include Order + +**ALWAYS organize includes in this order:** + +```cpp +#include "relatedheader.hpp" // Related header first (for .cpp files) + +#include <cstdint> // C system headers +#include <cmath> + +#include <string> // C++ standard library +#include <vector> +#include <memory> + +#include <osg/Vec3f> // External libraries +#include <MyGUI_Widget.h> + +#include <components/esm/refid.hpp> // Project headers +#include "../mbbase/environment.hpp" +``` + +### Namespace Structure + +```cpp +namespace MBWorld +{ + // All namespace content indented + class MyClass + { + public: // Access modifiers outdented by 4 spaces + MyClass(); + + private: + int mData; + }; + + void freeFunction(); +} +``` + +## Code Generation Guidelines + +### When Creating New Classes + +**ALWAYS generate headers like this:** + +```cpp +#ifndef OPENMB_APPS_OPENMB_MBWORLD_MYCLASS_H +#define OPENMB_APPS_OPENMB_MBWORLD_MYCLASS_H + +#include <string> +#include <vector> + +namespace MBWorld +{ + /// Brief description of what this class does + class MyClass + { + public: + MyClass(); + ~MyClass() = default; + + /// Brief description of method + /// \param input Description of parameter + /// \return Description of return value + bool processData(int input); + + /// Get the current value + int getValue() const; + + private: + int mValue; + std::string mName; + std::vector<int> mData; + }; +} + +#endif +``` + +**And implementations like this:** + +```cpp +#include "myclass.hpp" + +#include <algorithm> +#include <stdexcept> + +namespace MBWorld +{ + MyClass::MyClass() + : mValue(0) + , mName() + , mData() + { + } + + bool MyClass::processData(int input) + { + if (input < 0) + { + throw std::runtime_error("Invalid input"); + } + + mValue = input; + return true; + } + + int MyClass::getValue() const + { + return mValue; + } +} +``` + +### Modern C++ Patterns to Use + +```cpp +// Smart pointers instead of raw pointers +std::unique_ptr<Dialog> mDialog; +std::shared_ptr<Resource> mResource; + +// Range-based for loops +for (const auto& item : container) +{ + process(item); +} + +// Auto for complex types +auto iterator = myMap.find(key); +auto result = complexFunction(); + +// nullptr instead of NULL +Ptr* ptr = nullptr; + +// override keyword for virtual functions +void myMethod() override; + +// constexpr for compile-time constants +constexpr int BufferSize = 1024; + +// Const correctness +void processData(const std::string& input) const; + +// Structured bindings (C++17) +for (const auto& [key, value] : myMap) +{ + // ... +} +``` + +### Patterns to AVOID + +```cpp +// DON'T use manual memory management +MyClass* obj = new MyClass(); // BAD +delete obj; // BAD + +// DO use smart pointers or stack allocation +auto obj = std::make_unique<MyClass>(); // GOOD +MyClass obj; // GOOD (when appropriate) + +// DON'T omit braces for single-line blocks +if (condition) + doSomething(); // BAD + +// DO always use braces +if (condition) +{ + doSomething(); // GOOD +} + +// DON'T use raw pointers for ownership +void takeOwnership(MyClass* ptr); // BAD + +// DO use smart pointers +void takeOwnership(std::unique_ptr<MyClass> ptr); // GOOD + +// DON'T ignore const correctness +void getData(Ptr& ptr); // BAD if not modifying + +// DO use const when appropriate +void getData(const Ptr& ptr) const; // GOOD +``` + +### Virtual Functions and Inheritance + +```cpp +class Base +{ +public: + virtual ~Base() = default; + + /// Pure virtual function + virtual void mustImplement() = 0; + + /// Virtual function with default implementation + virtual void canOverride(); +}; + +class Derived : public Base +{ +public: + // Always use override keyword + void mustImplement() override; + void canOverride() override; + + // Use final to prevent further overriding + void finalMethod() final; +}; +``` + +### Exception Handling + +```cpp +// Throw exceptions for error conditions +void MyClass::validate() +{ + if (!isValid()) + { + throw std::runtime_error("Invalid state detected"); + } + + if (mData.empty()) + { + throw std::logic_error("Data cannot be empty"); + } +} + +// Use specific exception types +void processFile(const std::string& filename) +{ + if (!fileExists(filename)) + { + throw std::invalid_argument("File does not exist: " + filename); + } +} +``` + +### Documentation Comments + +**ALWAYS add Doxygen comments for:** +- All public classes +- All public methods +- All public member variables +- Complex private methods + +```cpp +/// Manages the game's resource loading and caching +/// +/// This class handles asynchronous loading of game resources +/// and maintains a cache to avoid redundant disk access. +class ResourceManager +{ +public: + /// Load a resource by its identifier + /// + /// \param resourceId The unique identifier of the resource + /// \param async If true, load asynchronously + /// \return Pointer to the loaded resource, or nullptr if failed + /// \throws std::runtime_error if resource format is invalid + Resource* loadResource(const ESM::RefId& resourceId, bool async = false); + + int mCacheSize; ///< Maximum number of cached resources +}; +``` + +## Common OpenMB Patterns + +### Using Project Types + +```cpp +// ESM::RefId for game object identifiers +ESM::RefId mItemId; +ESM::RefId mSpellId; + +// Ptr and ConstPtr for game object references +void processObject(const MBWorld::ConstPtr& ptr); +void modifyObject(MBWorld::Ptr& ptr); + +// Common project classes +MBWorld::CellStore* cell; +MBBase::Environment& env = MBBase::Environment::get(); +``` + +### Initialization Lists + +**ALWAYS use initialization lists for constructors:** + +```cpp +MyClass::MyClass(int value, const std::string& name) + : mValue(value) + , mName(name) + , mCounter(0) + , mIsActive(false) +{ + // Constructor body for additional logic only +} +``` + +### Const Correctness + +```cpp +class DataManager +{ +public: + // Non-const methods for modification + void addData(int value); + void clearData(); + + // Const methods for reading only + int getDataCount() const; + bool hasData() const; + const std::vector<int>& getData() const; + + // Return const reference when not modifying + const std::string& getName() const { return mName; } + +private: + std::vector<int> mData; + std::string mName; +}; +``` + +### Error Handling in Base Classes + +```cpp +// Use exceptions for unsupported operations in base classes +class GameObjectBase +{ +public: + virtual InventoryStore& getInventoryStore(const Ptr& ptr) const + { + throw std::runtime_error("This object type does not have an inventory"); + } + + virtual float getWeight(const Ptr& ptr) const + { + throw std::runtime_error("This object type does not have weight"); + } +}; +``` + +## File Organization + +### Header File Template + +```cpp +#ifndef OPENMB_<PATH>_H +#define OPENMB_<PATH>_H + +// Includes in proper order +#include <standard/library> + +#include <external/library> + +#include <components/project/header.hpp> + +// Forward declarations +namespace MBWorld +{ + class Ptr; +} + +namespace <YourNamespace> +{ + /// Class documentation + class ClassName + { + public: + // Public interface + + protected: + // Protected members + + private: + // Private members + // All member variables with 'm' prefix + }; +} + +#endif +``` + +### Implementation File Template + +```cpp +#include "classname.hpp" + +#include <algorithm> +#include <stdexcept> + +#include "../other/project/headers.hpp" + +namespace <YourNamespace> +{ + ClassName::ClassName() + : mMember1() + , mMember2(0) + { + } + + void ClassName::method() + { + // Implementation + } +} +``` + +## Platform Considerations + +```cpp +// Avoid platform-specific code when possible +// If necessary, use clear preprocessor guards + +#ifdef _WIN32 + // Windows-specific code +#elif defined(__linux__) + // Linux-specific code +#elif defined(__APPLE__) + // macOS-specific code +#else + #error "Unsupported platform" +#endif + +// Prefer cross-platform solutions +#include <filesystem> // C++17 cross-platform filesystem +namespace fs = std::filesystem; +``` + +## Testing and Validation + +When generating code, consider: + +1. **Is the code cross-platform compatible?** +2. **Are all member variables initialized?** +3. **Is const correctness maintained?** +4. **Are there proper Doxygen comments?** +5. **Does it follow the naming conventions?** +6. **Is memory managed safely (RAII/smart pointers)?** +7. **Are exceptions used appropriately?** +8. **Does it follow the brace style (Allman)?** + +## Quick Reference Checklist + +Before generating code, verify: + +- [ ] Braces on their own line (Allman style) +- [ ] 4 space indentation (no tabs) +- [ ] Member variables have 'm' prefix +- [ ] Functions use camelCase +- [ ] Classes use PascalCase +- [ ] Include guards follow OPENMB_<PATH>_H format +- [ ] Includes are in correct order +- [ ] Const correctness is maintained +- [ ] Smart pointers used instead of raw pointers for ownership +- [ ] Override keyword used for virtual functions +- [ ] Doxygen comments on public APIs +- [ ] Initialization lists used in constructors +- [ ] Namespaces properly indented +- [ ] Line length under 120 columns +- [ ] Exception handling for error conditions + +## Example: Complete Class Generation + +When asked to create a new class, generate both header and implementation following this pattern: + +**Header (playermanager.hpp):** +```cpp +#ifndef OPENMB_APPS_OPENMB_MBWORLD_PLAYERMANAGER_H +#define OPENMB_APPS_OPENMB_MBWORLD_PLAYERMANAGER_H + +#include <memory> +#include <string> +#include <vector> + +#include <components/esm/refid.hpp> + +namespace MBWorld +{ + class Ptr; + class CellStore; + + /// Manages player state and interactions + /// + /// This class handles player initialization, state updates, + /// and interactions with the game world. + class PlayerManager + { + public: + PlayerManager(); + ~PlayerManager() = default; + + /// Initialize the player in the game world + /// \param playerId The unique identifier for the player + /// \param startCell The starting cell for the player + /// \return True if initialization succeeded + bool initialize(const ESM::RefId& playerId, CellStore* startCell); + + /// Update player state + /// \param deltaTime Time elapsed since last update in seconds + void update(float deltaTime); + + /// Get the current player pointer + /// \return Const reference to the player pointer + const Ptr& getPlayer() const; + + /// Check if player is initialized + bool isInitialized() const; + + private: + /// Load player data from save + void loadPlayerData(const ESM::RefId& playerId); + + /// Validate player state + bool validateState() const; + + Ptr mPlayer; + ESM::RefId mPlayerId; + bool mIsInitialized; + float mTimeSinceLastUpdate; + }; +} + +#endif +``` + +**Implementation (playermanager.cpp):** +```cpp +#include "playermanager.hpp" + +#include <stdexcept> + +#include "../mbbase/environment.hpp" +#include "cellstore.hpp" +#include "ptr.hpp" + +namespace MBWorld +{ + PlayerManager::PlayerManager() + : mPlayer() + , mPlayerId() + , mIsInitialized(false) + , mTimeSinceLastUpdate(0.0f) + { + } + + bool PlayerManager::initialize(const ESM::RefId& playerId, CellStore* startCell) + { + if (!startCell) + { + throw std::invalid_argument("Start cell cannot be null"); + } + + if (playerId.empty()) + { + throw std::invalid_argument("Player ID cannot be empty"); + } + + mPlayerId = playerId; + loadPlayerData(playerId); + + // Additional initialization logic here + + mIsInitialized = validateState(); + return mIsInitialized; + } + + void PlayerManager::update(float deltaTime) + { + if (!mIsInitialized) + { + throw std::runtime_error("Cannot update uninitialized player"); + } + + mTimeSinceLastUpdate += deltaTime; + + // Update logic here + } + + const Ptr& PlayerManager::getPlayer() const + { + if (!mIsInitialized) + { + throw std::runtime_error("Player not initialized"); + } + + return mPlayer; + } + + bool PlayerManager::isInitialized() const + { + return mIsInitialized; + } + + void PlayerManager::loadPlayerData(const ESM::RefId& playerId) + { + // Load implementation + } + + bool PlayerManager::validateState() const + { + // Validation implementation + return true; + } +} +``` + +## Summary + +When generating code for OpenMB: +1. Follow Allman brace style (braces on own lines) +2. Use proper naming (mMemberVars, camelCaseFunctions, PascalCaseClasses) +3. Include Doxygen documentation +4. Use modern C++20 features +5. Maintain const correctness +6. Use smart pointers and RAII +7. Follow include order and guards +8. Keep code cross-platform + +**Remember: Consistency with existing code is paramount. When in doubt, follow the patterns already established in the codebase.** |