Files
documentation/docs/LIBRARY_MANAGEMENT_SYSTEM.md
2025-10-23 09:41:21 -05:00

806 lines
21 KiB
Markdown

# Godot C# Library Management System
## Overview
This document describes a comprehensive system for managing reusable C# libraries across multiple Godot game projects using Git submodules. This approach enables clean separation of concerns, version control for individual libraries, and easy code reuse across multiple game projects.
## System Architecture
### Core Principles
1. **Separation of Concerns**: Libraries and game projects exist in separate Git repositories
2. **Modular Design**: Each library serves a specific purpose and can be developed independently
3. **Version Control**: Libraries can be versioned and updated independently of game projects
4. **Code Reuse**: Multiple projects can share the same libraries without code duplication
5. **Clean Dependencies**: C# project references ensure proper compilation and IntelliSense support
### Directory Structure
```
~/GameDev/
├── Godot/ # Godot engine installation
├── libraries/ # Local development copies of libraries
│ ├── utilities/ # Example: utilities library repository
│ ├── audio-manager/ # Example: audio management library
│ └── ui-framework/ # Example: UI framework library
└── projects/ # Game project repositories
├── test-game/ # Example: test game project
│ └── Libraries/ # Git submodules directory
│ ├── utilities/ # Submodule → library/utilities
│ └── audio-manager/# Submodule → library/audio-manager
└── puzzle-game/ # Example: another game project
└── Libraries/ # Git submodules directory
└── utilities/ # Submodule → library/utilities
```
### Git Repository Organization
**Gitea Organizations:**
- `library/` - Contains all reusable library repositories
- `project/` - Contains all game project repositories
**Repository Examples:**
- `https://git.ein-softworks.com/library/utilities.git`
- `https://git.ein-softworks.com/library/audio-manager.git`
- `https://git.ein-softworks.com/project/test-game.git`
- `https://git.ein-softworks.com/project/puzzle-game.git`
## How Git Submodules Work
Git submodules allow you to include one Git repository inside another as a subdirectory. In our case:
1. **Library repositories** contain reusable C# code and Godot scenes
2. **Game project repositories** include libraries as submodules in their `Libraries/` directory
3. **Submodules point to specific commits** in the library repositories, ensuring reproducible builds
4. **Updates are explicit** - you control when to pull in library changes
### Benefits of This Approach
- **Independent Development**: Work on libraries without affecting game projects
- **Version Stability**: Games use specific library versions until explicitly updated
- **Selective Updates**: Choose which libraries to update in each project
- **Clean History**: Each repository maintains its own commit history
- **Collaborative Friendly**: Multiple developers can work on different libraries simultaneously
## Initial Setup Process
### Prerequisites
- macOS with Godot v4.5.1 stable installed at `~/GameDev/Godot/`
- .NET 8.0.121 installed
- Git configured with access to your Gitea instance
- Gitea instance with `library` and `project` organizations created
### Step 1: Create Directory Structure
```bash
cd ~/GameDev
mkdir -p libraries projects
```
### Step 2: Create Your First Library
#### Initialize the Library Repository
```bash
cd ~/GameDev/libraries
git init utilities
cd utilities
mkdir -p Scripts
```
#### Create Library Project Configuration
Create `project.godot`:
```ini
; Engine configuration file.
[application]
config/name="Utilities Library"
config/features=PackedStringArray("4.3", "C#", "Forward Plus")
[dotnet]
project/assembly_name="EinSoftworks.Utilities"
```
Create `Utilities.csproj`:
```xml
<Project Sdk="Godot.NET.Sdk/4.3.0">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<RootNamespace>EinSoftworks.Utilities</RootNamespace>
<AssemblyName>EinSoftworks.Utilities</AssemblyName>
</PropertyGroup>
</Project>
```
#### Create Example Library Code
Create `Scripts/MathUtils.cs`:
```csharp
using Godot;
namespace EinSoftworks.Utilities
{
public static class MathUtils
{
/// <summary>
/// Clamps a value between 0 and 1.
/// </summary>
public static float Clamp01(float value)
{
return Mathf.Clamp(value, 0f, 1f);
}
/// <summary>
/// Checks if a number is approximately equal to another number.
/// </summary>
public static bool Approximately(float a, float b, float threshold = 0.001f)
{
return Mathf.Abs(a - b) < threshold;
}
/// <summary>
/// Converts degrees to radians.
/// </summary>
public static float DegreesToRadians(float degrees)
{
return degrees * Mathf.Pi / 180f;
}
/// <summary>
/// Converts radians to degrees.
/// </summary>
public static float RadiansToDegrees(float radians)
{
return radians * 180f / Mathf.Pi;
}
}
}
```
#### Create .gitignore
```gitignore
# Godot 4+ specific ignores
.godot/
# Godot-specific ignores
.import/
export.cfg
export_presets.cfg
# Imported translations (automatically generated from CSV files)
*.translation
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json
# .NET specific ignores
bin/
obj/
*.tmp
```
#### Commit and Push Library
```bash
git add .
git commit -m "Initial utilities library setup with MathUtils"
git remote add origin https://git.ein-softworks.com/library/utilities.git
git branch -M main
git push -u origin main
```
### Step 3: Create Your First Game Project
#### Initialize the Game Repository
```bash
cd ~/GameDev/projects
git init test-game
cd test-game
mkdir -p Scripts Scenes Libraries
```
#### Create Game Project Configuration
Create `project.godot`:
```ini
; Engine configuration file.
[application]
config/name="Test Game"
config/features=PackedStringArray("4.3", "C#", "Forward Plus")
run/main_scene="res://Scenes/Main.tscn"
[dotnet]
project/assembly_name="TestGame"
```
Create `TestGame.csproj`:
```xml
<Project Sdk="Godot.NET.Sdk/4.3.0">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<RootNamespace>TestGame</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="Libraries/utilities/Utilities.csproj" />
</ItemGroup>
</Project>
```
#### Add Library as Submodule
```bash
git submodule add https://git.ein-softworks.com/library/utilities.git Libraries/utilities
git submodule update --init --recursive
```
#### Create Game Code
Create `Scripts/Main.cs`:
```csharp
using Godot;
using EinSoftworks.Utilities;
namespace TestGame
{
public partial class Main : Node2D
{
public override void _Ready()
{
GD.Print("Test Game Started!");
// Test the utilities library
TestMathUtils();
}
private void TestMathUtils()
{
// Test degree/radian conversion
float testAngle = 90f;
float radians = MathUtils.DegreesToRadians(testAngle);
float backToDegrees = MathUtils.RadiansToDegrees(radians);
GD.Print($"90° → {radians} rad → {backToDegrees}°");
// Test clamping
float clampedValue = MathUtils.Clamp01(1.5f);
GD.Print($"Clamped 1.5 to 0-1 range: {clampedValue}");
// Test approximation
bool isApprox = MathUtils.Approximately(0.1f, 0.100001f);
GD.Print($"0.1 ≈ 0.100001: {isApprox}");
}
}
}
```
Create `Scenes/Main.tscn`:
```
[gd_scene load_steps=2 format=3 uid="uid://b8y1qxqxqxqxq"]
[ext_resource type="Script" path="res://Scripts/Main.cs" id="1"]
[node name="Main" type="Node2D"]
script = ExtResource("1")
[node name="Label" type="Label" parent="."]
offset_right = 400.0
offset_bottom = 100.0
text = "Test Game - Check console for utilities library output"
horizontal_alignment = 1
vertical_alignment = 1
```
#### Create .gitignore
```gitignore
# Godot 4+ specific ignores
.godot/
# Godot-specific ignores
.import/
export.cfg
export_presets.cfg
# Imported translations (automatically generated from CSV files)
*.translation
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json
# .NET specific ignores
bin/
obj/
*.tmp
```
#### Commit and Push Game Project
```bash
git add .
git commit -m "Initial test game setup with utilities library submodule"
git remote add origin https://git.ein-softworks.com/project/test-game.git
git branch -M main
git push -u origin main
```
### Step 4: Open and Build in Godot
1. Launch Godot: `~/GameDev/Godot/Godot.app/Contents/MacOS/Godot`
2. In Project Manager, click **"Import"**
3. Navigate to `~/GameDev/projects/test-game/` and select `project.godot`
4. Click **"Import & Edit"**
5. In the Godot editor, go to **Project → Tools → C# → Create C# solution**
6. Click the **"Build"** button in the toolbar (or **Project → Tools → C# → Build Solution**)
7. Run the game with the **Play** button
Expected console output:
```
Test Game Started!
90° → 1.5708 rad → 90°
Clamped 1.5 to 0-1 range: 1
0.1 ≈ 0.100001: True
```
## Usage Workflows
### Working on Libraries
#### Developing New Features
When you want to add functionality to an existing library:
```bash
# Navigate to library
cd ~/GameDev/libraries/utilities
# Create a new branch for your feature
git checkout -b feature/string-utilities
# Add your new code (example)
cat >> Scripts/StringUtils.cs << 'EOF'
using Godot;
namespace EinSoftworks.Utilities
{
public static class StringUtils
{
public static string ToPascalCase(string input)
{
if (string.IsNullOrEmpty(input)) return input;
return char.ToUpper(input[0]) + input.Substring(1).ToLower();
}
public static bool IsValidEmail(string email)
{
return email.Contains("@") && email.Contains(".");
}
}
}
EOF
# Test your changes in Godot
# Open ~/GameDev/libraries/utilities in Godot
# Build and test the library
# Commit your changes
git add .
git commit -m "Add StringUtils with PascalCase and email validation"
# Push the feature branch
git push origin feature/string-utilities
# Create a pull request in Gitea
# After review and merge, switch back to main
git checkout main
git pull origin main
# Clean up feature branch
git branch -d feature/string-utilities
```
#### Creating a New Library
```bash
# Create new library repository
cd ~/GameDev/libraries
git init audio-manager
cd audio-manager
# Set up library structure
mkdir -p Scripts Resources
cat > project.godot << 'EOF'
; Engine configuration file.
[application]
config/name="Audio Manager Library"
config/features=PackedStringArray("4.3", "C#", "Forward Plus")
[dotnet]
project/assembly_name="EinSoftworks.AudioManager"
EOF
cat > AudioManager.csproj << 'EOF'
<Project Sdk="Godot.NET.Sdk/4.3.0">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<RootNamespace>EinSoftworks.AudioManager</RootNamespace>
<AssemblyName>EinSoftworks.AudioManager</AssemblyName>
</PropertyGroup>
</Project>
EOF
# Create basic audio manager
cat > Scripts/AudioManager.cs << 'EOF'
using Godot;
namespace EinSoftworks.AudioManager
{
public partial class AudioManager : Node
{
private AudioStreamPlayer _musicPlayer;
private AudioStreamPlayer _sfxPlayer;
public override void _Ready()
{
_musicPlayer = new AudioStreamPlayer();
_sfxPlayer = new AudioStreamPlayer();
AddChild(_musicPlayer);
AddChild(_sfxPlayer);
}
public void PlayMusic(AudioStream music, float volume = 1.0f)
{
_musicPlayer.Stream = music;
_musicPlayer.VolumeDb = Mathf.LinearToDb(volume);
_musicPlayer.Play();
}
public void PlaySFX(AudioStream sfx, float volume = 1.0f)
{
_sfxPlayer.Stream = sfx;
_sfxPlayer.VolumeDb = Mathf.LinearToDb(volume);
_sfxPlayer.Play();
}
}
}
EOF
# Copy standard .gitignore
cp ../utilities/.gitignore .
# Commit and push
git add .
git commit -m "Initial audio manager library"
git remote add origin https://git.ein-softworks.com/library/audio-manager.git
git branch -M main
git push -u origin main
```
### Working with Game Projects
#### Adding Libraries to Existing Projects
To add a new library to an existing game project:
```bash
# Navigate to your game project
cd ~/GameDev/projects/test-game
# Add the new library as a submodule
git submodule add https://git.ein-softworks.com/library/audio-manager.git Libraries/audio-manager
# Update the .csproj file to reference the new library
# Edit TestGame.csproj and add to ItemGroup:
# <ProjectReference Include="Libraries/audio-manager/AudioManager.csproj" />
# Example of the updated .csproj:
cat > TestGame.csproj << 'EOF'
<Project Sdk="Godot.NET.Sdk/4.3.0">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<RootNamespace>TestGame</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="Libraries/utilities/Utilities.csproj" />
<ProjectReference Include="Libraries/audio-manager/AudioManager.csproj" />
</ItemGroup>
</Project>
EOF
# Commit the changes
git add .
git commit -m "Add audio-manager library dependency"
git push origin main
# In Godot: Project → Reload Current Project
# Then build the solution
```
#### Updating Libraries in Projects
To update a library to the latest version:
```bash
# Navigate to your game project
cd ~/GameDev/projects/test-game
# Update specific library to latest
git submodule update --remote Libraries/utilities
# Or update all submodules
git submodule update --remote
# Commit the library updates
git add .
git commit -m "Update utilities library to latest version"
git push origin main
# In Godot: Project → Reload Current Project
# Then rebuild the solution
```
To update to a specific version/commit:
```bash
# Navigate to the submodule
cd ~/GameDev/projects/test-game/Libraries/utilities
# Check available tags/versions
git tag -l
# Checkout specific version
git checkout v1.2.0
# Go back to project root
cd ~/GameDev/projects/test-game
# Commit the specific version
git add Libraries/utilities
git commit -m "Update utilities library to v1.2.0"
git push origin main
```
#### Creating New Game Projects
To create a new game project with existing libraries:
```bash
# Create new project
cd ~/GameDev/projects
git init puzzle-game
cd puzzle-game
# Set up basic structure
mkdir -p Scripts Scenes Libraries
# Create project.godot
cat > project.godot << 'EOF'
; Engine configuration file.
[application]
config/name="Puzzle Game"
config/features=PackedStringArray("4.3", "C#", "Forward Plus")
run/main_scene="res://Scenes/Main.tscn"
[dotnet]
project/assembly_name="PuzzleGame"
EOF
# Add required libraries
git submodule add https://git.ein-softworks.com/library/utilities.git Libraries/utilities
git submodule add https://git.ein-softworks.com/library/audio-manager.git Libraries/audio-manager
# Create .csproj with all dependencies
cat > PuzzleGame.csproj << 'EOF'
<Project Sdk="Godot.NET.Sdk/4.3.0">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<RootNamespace>PuzzleGame</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="Libraries/utilities/Utilities.csproj" />
<ProjectReference Include="Libraries/audio-manager/AudioManager.csproj" />
</ItemGroup>
</Project>
EOF
# Copy .gitignore from another project
cp ../test-game/.gitignore .
# Initialize submodules
git submodule update --init --recursive
# Create basic game code
cat > Scripts/Main.cs << 'EOF'
using Godot;
using EinSoftworks.Utilities;
using EinSoftworks.AudioManager;
namespace PuzzleGame
{
public partial class Main : Node2D
{
private AudioManager _audioManager;
public override void _Ready()
{
GD.Print("Puzzle Game Started!");
// Set up audio manager
_audioManager = new AudioManager();
AddChild(_audioManager);
// Test utilities
float angle = MathUtils.DegreesToRadians(45f);
GD.Print($"45 degrees = {angle} radians");
}
}
}
EOF
# Commit and push
git add .
git commit -m "Initial puzzle game setup with utilities and audio-manager libraries"
git remote add origin https://git.ein-softworks.com/project/puzzle-game.git
git branch -M main
git push -u origin main
```
### Advanced Workflows
#### Working with Library Dependencies
If one library depends on another:
```csharp
// In Libraries/ui-framework/UIFramework.csproj
<Project Sdk="Godot.NET.Sdk/4.3.0">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<RootNamespace>EinSoftworks.UIFramework</RootNamespace>
<AssemblyName>EinSoftworks.UIFramework</AssemblyName>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../utilities/Utilities.csproj" />
</ItemGroup>
</Project>
```
#### Creating Library Releases
For important library milestones:
```bash
# In your library repository
cd ~/GameDev/libraries/utilities
# Create and push a tag
git tag -a v1.0.0 -m "Release version 1.0.0 - Initial stable release"
git push origin v1.0.0
# Create release notes in Gitea
# Navigate to your Gitea repository → Releases → New Release
```
#### Cloning Projects on New Machines
When setting up on a new development machine:
```bash
# Clone project with submodules
git clone --recursive https://git.ein-softworks.com/project/test-game.git
# Or if already cloned without --recursive
cd test-game
git submodule update --init --recursive
```
## Best Practices
### Library Design
1. **Keep libraries focused**: Each library should have a single, well-defined purpose
2. **Use proper namespacing**: Follow the pattern `CompanyName.LibraryName`
3. **Document your code**: Use XML documentation comments for public APIs
4. **Version your releases**: Use semantic versioning (major.minor.patch)
5. **Test thoroughly**: Create test projects for each library
### Project Organization
1. **Consistent naming**: Use kebab-case for repository names, PascalCase for namespaces
2. **Clear dependencies**: Only include libraries you actually use
3. **Regular updates**: Keep libraries updated, but test thoroughly after updates
4. **Backup strategy**: Ensure all repositories are backed up on your Gitea instance
### Git Workflow
1. **Feature branches**: Use feature branches for library development
2. **Descriptive commits**: Write clear commit messages
3. **Regular pushes**: Push changes regularly to avoid data loss
4. **Clean history**: Squash commits when merging features
### Development Environment
1. **Consistent paths**: Always use the `~/GameDev/` structure
2. **IDE setup**: Configure your IDE to recognize the library references
3. **Build automation**: Consider setting up CI/CD for library testing
4. **Documentation**: Keep this document updated as your system evolves
## Troubleshooting
### Common Issues
#### Submodule Not Found
```bash
# If Libraries/utilities is empty
cd ~/GameDev/projects/test-game
git submodule update --init --recursive
```
#### Build Errors
```bash
# In Godot editor
# Project → Reload Current Project
# Project → Tools → C# → Create C# solution
# Build
```
#### Missing References
Check that your `.csproj` file includes all required `<ProjectReference>` entries for your submodules.
#### Submodule Update Conflicts
```bash
# If you have uncommitted changes in a submodule
cd Libraries/utilities
git stash
git pull origin main
git stash pop
```
### Getting Help
1. Check Godot's C# documentation
2. Review Git submodule documentation
3. Examine this document for reference patterns
4. Test changes in isolation before applying to important projects
---
*This documentation should be updated as your library system evolves and new patterns emerge.*