chore(release): final release commit for 0.1.0
this commit includes a whole lotta fuck yeah, a whole lotta we fuckin got this, and a lot of "please change the future." i hope it works. Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
This commit is contained in:
2436
.claude/rust-guidelines.txt
Normal file
2436
.claude/rust-guidelines.txt
Normal file
File diff suppressed because it is too large
Load Diff
68
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
68
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Report a bug to help us improve Marathon
|
||||
title: '[BUG] '
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Bug Description
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
## Minimal, Complete, Verifiable Example (MCVE)
|
||||
|
||||
Please provide the **smallest possible code example** that demonstrates the bug. This helps us reproduce and fix the issue faster.
|
||||
|
||||
### Minimal Code Example
|
||||
|
||||
```rust
|
||||
// Paste your minimal reproducible code here
|
||||
// Remove anything not necessary to demonstrate the bug
|
||||
```
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
What you expected to happen:
|
||||
|
||||
### Actual Behavior
|
||||
|
||||
What actually happened:
|
||||
|
||||
## Environment
|
||||
|
||||
- **OS**: [e.g., macOS 15.0, iOS 18.2]
|
||||
- **Rust Version**: [e.g., 1.85.0 - run `rustc --version`]
|
||||
- **Marathon Version/Commit**: [e.g., v0.1.0 or commit hash]
|
||||
- **Platform**: [Desktop / iOS Simulator / iOS Device]
|
||||
|
||||
## Logs/Stack Traces
|
||||
|
||||
If applicable, paste any error messages or stack traces here:
|
||||
|
||||
```
|
||||
paste logs here
|
||||
```
|
||||
|
||||
## Screenshots/Videos
|
||||
|
||||
If applicable, add screenshots or videos to help explain the problem.
|
||||
|
||||
## Additional Context
|
||||
|
||||
Add any other context about the problem here. For example:
|
||||
- Does it happen every time or intermittently?
|
||||
- Did this work in a previous version?
|
||||
- Are you running multiple instances?
|
||||
- Any relevant configuration or network setup?
|
||||
|
||||
## Possible Solution
|
||||
|
||||
If you have ideas about what might be causing the issue or how to fix it, please share them here.
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Question or Discussion
|
||||
url: https://github.com/r3t-studios/marathon/discussions
|
||||
about: Ask questions or discuss ideas with the community
|
||||
- name: Security Vulnerability
|
||||
url: https://github.com/r3t-studios/marathon/security/policy
|
||||
about: Please report security issues privately (see SECURITY.md)
|
||||
72
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
72
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest a new feature or enhancement for Marathon
|
||||
title: '[FEATURE] '
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Problem Statement
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
|
||||
A clear and concise description of what the problem is. For example:
|
||||
- "I'm always frustrated when..."
|
||||
- "It's difficult to..."
|
||||
- "Users need to be able to..."
|
||||
|
||||
## Feature Request (Given-When-Then Format)
|
||||
|
||||
Please describe your feature request using the Given-When-Then format to make the behavior clear:
|
||||
|
||||
### Scenario 1: [Brief scenario name]
|
||||
|
||||
**Given** [initial context or preconditions]
|
||||
**When** [specific action or event]
|
||||
**Then** [expected outcome]
|
||||
|
||||
**Example:**
|
||||
- **Given** I am editing a collaborative document with 3 other peers
|
||||
- **When** I lose network connectivity for 5 minutes
|
||||
- **Then** my local changes should be preserved and sync automatically when I reconnect
|
||||
|
||||
### Scenario 2: [Additional scenario if needed]
|
||||
|
||||
**Given** [initial context]
|
||||
**When** [action]
|
||||
**Then** [outcome]
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
**Describe alternatives you've considered.**
|
||||
|
||||
Have you thought of other ways to solve this problem? What are the pros and cons of different approaches?
|
||||
|
||||
## Technical Considerations
|
||||
|
||||
**Do you have thoughts on implementation?**
|
||||
|
||||
If you have ideas about how this could be implemented technically, share them here. For example:
|
||||
- Which modules might be affected
|
||||
- Potential challenges or dependencies
|
||||
- Performance implications
|
||||
- Breaking changes required
|
||||
|
||||
## Additional Context
|
||||
|
||||
Add any other context, mockups, screenshots, or examples from other projects that illustrate the feature.
|
||||
|
||||
## Priority/Impact
|
||||
|
||||
How important is this feature to you or your use case?
|
||||
- [ ] Critical - blocking current work
|
||||
- [ ] High - would significantly improve workflow
|
||||
- [ ] Medium - nice to have
|
||||
- [ ] Low - minor improvement
|
||||
|
||||
## Willingness to Contribute
|
||||
|
||||
- [ ] I'm willing to implement this feature
|
||||
- [ ] I can help test this feature
|
||||
- [ ] I can help with documentation
|
||||
- [ ] I'm just suggesting the idea
|
||||
117
.github/pull_request_template.md
vendored
Normal file
117
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
## Description
|
||||
|
||||
<!-- Provide a clear and concise description of what this PR does -->
|
||||
|
||||
## Related Issues
|
||||
|
||||
<!-- Link to related issues using #issue_number -->
|
||||
Fixes #
|
||||
Relates to #
|
||||
|
||||
## Type of Change
|
||||
|
||||
<!-- Mark relevant items with an [x] -->
|
||||
|
||||
- [ ] Bug fix (non-breaking change that fixes an issue)
|
||||
- [ ] New feature (non-breaking change that adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Documentation update
|
||||
- [ ] Refactoring (no functional changes)
|
||||
- [ ] Performance improvement
|
||||
- [ ] Test coverage improvement
|
||||
|
||||
## Changes Made
|
||||
|
||||
<!-- List the specific changes in this PR -->
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
## Testing Performed
|
||||
|
||||
<!-- Describe the testing you've done -->
|
||||
|
||||
- [ ] All existing tests pass (`cargo nextest run`)
|
||||
- [ ] Added new tests for new functionality
|
||||
- [ ] Tested manually on desktop
|
||||
- [ ] Tested manually on iOS (if applicable)
|
||||
- [ ] Tested with multiple instances
|
||||
- [ ] Tested edge cases and error conditions
|
||||
|
||||
### Test Details
|
||||
|
||||
<!-- Provide specific details about your testing -->
|
||||
|
||||
**Desktop:**
|
||||
-
|
||||
|
||||
**iOS:** (if applicable)
|
||||
-
|
||||
|
||||
**Multi-instance:** (if applicable)
|
||||
-
|
||||
|
||||
## Documentation
|
||||
|
||||
<!-- Mark relevant items with an [x] -->
|
||||
|
||||
- [ ] Updated relevant documentation in `/docs`
|
||||
- [ ] Updated README.md (if public API changed)
|
||||
- [ ] Added doc comments to new public APIs
|
||||
- [ ] Updated CHANGELOG.md
|
||||
|
||||
## Code Quality
|
||||
|
||||
<!-- Confirm these items -->
|
||||
|
||||
- [ ] Code follows project style guidelines
|
||||
- [ ] Ran `cargo +nightly fmt`
|
||||
- [ ] Ran `cargo clippy` and addressed warnings
|
||||
- [ ] No new compiler warnings
|
||||
- [ ] Added meaningful variable/function names
|
||||
|
||||
## AI Usage
|
||||
|
||||
<!-- If you used AI tools, briefly note how (see AI_POLICY.md) -->
|
||||
<!-- You don't need to disclose simple autocomplete, only substantial AI assistance -->
|
||||
|
||||
- [ ] No AI assistance used
|
||||
- [ ] Used AI tools (brief description below)
|
||||
|
||||
<!-- If used: -->
|
||||
<!-- AI tool: [e.g., Claude, Copilot] -->
|
||||
<!-- How: [e.g., "Used to generate boilerplate, then reviewed and modified"] -->
|
||||
<!-- I reviewed, understand, and am accountable for all code in this PR -->
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
<!-- If this is a breaking change, describe what breaks and how to migrate -->
|
||||
|
||||
**Does this PR introduce breaking changes?**
|
||||
- [ ] No
|
||||
- [ ] Yes (describe below)
|
||||
|
||||
<!-- If yes: -->
|
||||
<!-- - What breaks: -->
|
||||
<!-- - Migration path: -->
|
||||
|
||||
## Screenshots/Videos
|
||||
|
||||
<!-- If applicable, add screenshots or videos showing the changes -->
|
||||
|
||||
## Checklist
|
||||
|
||||
<!-- Final checks before requesting review -->
|
||||
|
||||
- [ ] My code follows the project's coding standards
|
||||
- [ ] I have tested my changes thoroughly
|
||||
- [ ] I have updated relevant documentation
|
||||
- [ ] I have added tests that prove my fix/feature works
|
||||
- [ ] All tests pass locally
|
||||
- [ ] I have read and followed the [CONTRIBUTING.md](../CONTRIBUTING.md) guidelines
|
||||
- [ ] I understand and accept the [AI_POLICY.md](../AI_POLICY.md)
|
||||
|
||||
## Additional Notes
|
||||
|
||||
<!-- Any additional information reviewers should know -->
|
||||
136
AI_POLICY.md
Normal file
136
AI_POLICY.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# AI and Machine Learning Usage Policy
|
||||
|
||||
## Core Principle: Human Accountability
|
||||
|
||||
Every contribution to Marathon must have a human who:
|
||||
- **Made the decisions** about what to build and how to build it
|
||||
- **Understands the code, design, or content** they're submitting
|
||||
- **Takes responsibility** for the outcome and any issues that arise
|
||||
- **Can be held accountable** for the contribution
|
||||
|
||||
AI and ML tools are welcome as assistants, but they cannot:
|
||||
- Make architectural or design decisions
|
||||
- Choose between technical trade-offs
|
||||
- Take responsibility for bugs or issues
|
||||
- Be credited as contributors
|
||||
|
||||
## Context: Pragmatism at a Small Scale
|
||||
|
||||
We're a tiny studio with limited resources. We can't afford large teams, professional translators, or extensive QA departments. **Machine learning tools help us punch above our weight class** - they let us move faster, support more languages, and catch bugs we'd otherwise miss.
|
||||
|
||||
We use these tools not to replace human judgment, but to stretch our small team's capacity. This is about working **smart with what we have**, not taking shortcuts that compromise quality or accountability.
|
||||
|
||||
We're using ethical and responsible machine learning as much as possible while ensuring that we are not erasing human contributions while we are resource-constrained.
|
||||
|
||||
## The Blurry Line
|
||||
|
||||
**Here's the honest truth:** The line between "generative AI" and "assistive AI" is fuzzy and constantly shifting. Is IDE autocomplete assistive? What about when it suggests entire functions? What about pair-programming with an LLM?
|
||||
|
||||
**We don't have perfect answers.** What we do have is a principle: **a human must make the decisions and be accountable.**
|
||||
|
||||
If you're unsure whether your use of AI crosses a line, ask yourself:
|
||||
- **"Do I understand what this code does and why?"**
|
||||
- **"Did I decide this was the right approach, or did the AI?"**
|
||||
- **"Can I maintain and debug this?"**
|
||||
- **"Am I comfortable being accountable for this?"**
|
||||
|
||||
If you answer "yes" to those questions, you're probably fine. If you're still uncertain, open a discussion - we'd rather have the conversation than enforce rigid rules that don't match reality.
|
||||
|
||||
## What This Looks Like in Practice
|
||||
|
||||
### Acceptable Use
|
||||
|
||||
**"I used Claude/Copilot to help write this function, I reviewed it, I understand it, and I'm responsible for it."**
|
||||
- You directed the tool
|
||||
- You reviewed and understood the output
|
||||
- You made the decision to use this approach
|
||||
- You take responsibility for the result
|
||||
|
||||
**"I directed an LLM to implement my design, then verified it meets requirements."**
|
||||
- You designed the solution
|
||||
- You used AI to speed up implementation
|
||||
- You verified correctness
|
||||
- You own the outcome
|
||||
|
||||
**"I used machine translation as a starting point, then reviewed and corrected the output."**
|
||||
- You acknowledge the limitations of automated translation
|
||||
- You applied human judgment to the result
|
||||
- You ensure accuracy and appropriateness
|
||||
|
||||
### Not Acceptable
|
||||
|
||||
**"Claude wrote this, I pasted it in, seems fine."**
|
||||
- No understanding of the code
|
||||
- No verification of correctness
|
||||
- Cannot maintain or debug
|
||||
- Cannot explain design decisions
|
||||
|
||||
**"I asked an LLM what architecture to use and implemented its suggestion."**
|
||||
- The AI made the architectural decision
|
||||
- No human judgment about trade-offs
|
||||
- No accountability for the choice
|
||||
|
||||
**"I'm submitting this AI-generated documentation without reviewing it."**
|
||||
- No verification of accuracy
|
||||
- No human oversight
|
||||
- Cannot vouch for quality
|
||||
|
||||
## Why This Matters
|
||||
|
||||
Marathon itself was largely written with AI assistance under human direction. **That's fine!** What matters is:
|
||||
|
||||
1. **A human made every architectural decision**
|
||||
2. **A human is accountable for every line of code**
|
||||
3. **A human can explain why things work the way they do**
|
||||
4. **Humans take credit AND responsibility**
|
||||
|
||||
Think of AI like a compiler, a library, or a really capable intern - it's a tool that amplifies human capability, but **the human is always the one making decisions and being accountable**.
|
||||
|
||||
## For Contributors
|
||||
|
||||
We don't care what tools you use to be productive. We care that:
|
||||
- **You made the decisions** (not the AI)
|
||||
- **You understand what you're submitting**
|
||||
- **You're accountable** for the contribution
|
||||
- **You can maintain it** if issues arise
|
||||
|
||||
Use whatever tools help you work effectively, but you must be able to answer "why did you make this choice?" with human reasoning, not "the AI suggested it."
|
||||
|
||||
### When Contributing
|
||||
|
||||
You don't need to disclose every time you use autocomplete or ask an LLM a question. We trust you to:
|
||||
- Use tools responsibly
|
||||
- Understand your contributions
|
||||
- Take ownership of your work
|
||||
|
||||
If you're doing something novel or pushing boundaries with AI assistance, mentioning it in your PR is welcome - it helps us all learn and navigate this space together.
|
||||
|
||||
## What We Use
|
||||
|
||||
For transparency, here's where Marathon currently uses machine learning:
|
||||
|
||||
- **Development assistance** - IDE tools, code completion, pair programming with LLMs
|
||||
- **Translation tooling** - Machine translation for internationalization (human-reviewed)
|
||||
- **Performance analysis** - Automated profiling and optimization suggestions
|
||||
- **Code review assistance** - Static analysis and potential bug detection
|
||||
- **Documentation help** - Grammar checking, clarity improvements, translation
|
||||
|
||||
In all cases, humans review, approve, and take responsibility for the output.
|
||||
|
||||
## The Bottom Line
|
||||
|
||||
**Machines can't be held accountable, so humans must make all decisions.**
|
||||
|
||||
Use AI tools to help you work faster and smarter, but you must understand and be accountable for what you contribute. When in doubt, ask yourself:
|
||||
|
||||
**"Can a machine be blamed if this breaks?"**
|
||||
|
||||
If yes, you've crossed the line.
|
||||
|
||||
## Questions or Concerns?
|
||||
|
||||
This policy will evolve as we learn more about working effectively with AI tools. If you have questions, concerns, or suggestions, please open a discussion. We're figuring this out together.
|
||||
|
||||
---
|
||||
|
||||
*This policy reflects our values as of February 2026. As technology and our understanding evolve, so will this document.*
|
||||
359
ARCHITECTURE.md
Normal file
359
ARCHITECTURE.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# Marathon Architecture
|
||||
|
||||
This document provides a high-level overview of Marathon's architecture to help contributors understand the system's design and organization.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Core Principles](#core-principles)
|
||||
- [System Architecture](#system-architecture)
|
||||
- [Crate Organization](#crate-organization)
|
||||
- [Key Components](#key-components)
|
||||
- [Data Flow](#data-flow)
|
||||
- [Technology Decisions](#technology-decisions)
|
||||
- [Design Constraints](#design-constraints)
|
||||
|
||||
## Overview
|
||||
|
||||
Marathon is a **peer-to-peer game engine development kit** built on conflict-free replicated data types (CRDTs). It enables developers to build multiplayer games where players can interact with shared game state in real-time, even across network partitions, with automatic reconciliation.
|
||||
|
||||
**Key Characteristics:**
|
||||
- **Decentralized** - No central game server required, all players are equal peers
|
||||
- **Offline-first** - Gameplay continues during network partitions
|
||||
- **Eventually consistent** - All players converge to the same game state
|
||||
- **Real-time** - Player actions propagate with minimal latency
|
||||
- **Persistent** - Game state survives application restarts
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **CRDTs for Consistency** - Use mathematically proven data structures that guarantee eventual consistency for multiplayer game state
|
||||
2. **Bevy ECS First** - Build on Bevy's Entity Component System for game development flexibility
|
||||
3. **Zero Trust Networking** - Assume peers may be malicious (future work for competitive games)
|
||||
4. **Separation of Concerns** - Clear boundaries between networking, persistence, and game logic
|
||||
5. **Performance Matters** - Optimize for low latency and high throughput suitable for real-time games
|
||||
|
||||
## System Architecture
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph App["Game Layer"]
|
||||
Demo[Demo Game / Your Game]
|
||||
Actions[Game Actions]
|
||||
Selection[Entity Selection]
|
||||
Input[Input Handling]
|
||||
Render[Rendering]
|
||||
end
|
||||
|
||||
subgraph Core["libmarathon Core"]
|
||||
Net[Networking<br/>• CRDT Sync<br/>• Gossip<br/>• Sessions<br/>• Op Apply]
|
||||
Engine[Engine Core<br/>• Event Loop<br/>• Commands<br/>• Discovery<br/>• Bridge]
|
||||
Persist[Persistence<br/>• SQLite<br/>• Type Registry<br/>• Migrations<br/>• Metrics]
|
||||
end
|
||||
|
||||
subgraph Foundation["Foundation Layer"]
|
||||
Bevy[Bevy ECS<br/>• Entities<br/>• Components<br/>• Systems]
|
||||
Iroh[iroh P2P<br/>• QUIC<br/>• Gossip<br/>• Discovery]
|
||||
end
|
||||
|
||||
Demo --> Actions
|
||||
Demo --> Selection
|
||||
Demo --> Input
|
||||
Demo --> Render
|
||||
|
||||
Actions --> Engine
|
||||
Selection --> Engine
|
||||
Input --> Engine
|
||||
Render --> Engine
|
||||
|
||||
Engine --> Net
|
||||
Engine --> Persist
|
||||
Net --> Persist
|
||||
|
||||
Net --> Iroh
|
||||
Engine --> Bevy
|
||||
Persist --> Bevy
|
||||
```
|
||||
|
||||
## Crate Organization
|
||||
|
||||
Marathon is organized as a Rust workspace with four crates:
|
||||
|
||||
### `libmarathon` (Core Library)
|
||||
|
||||
**Purpose**: The heart of Marathon, providing networking, persistence, and CRDT synchronization.
|
||||
|
||||
**Key Modules:**
|
||||
```
|
||||
libmarathon/
|
||||
├── networking/ # P2P networking and CRDT sync
|
||||
│ ├── crdt/ # CRDT implementations (OR-Set, RGA, LWW)
|
||||
│ ├── operations/ # Network operations and vector clocks
|
||||
│ ├── gossip/ # Gossip protocol bridge to iroh
|
||||
│ ├── session/ # Session management
|
||||
│ └── entity_map/ # UUID ↔ Entity mapping
|
||||
│
|
||||
├── persistence/ # SQLite-backed state persistence
|
||||
│ ├── database/ # SQLite connection and WAL
|
||||
│ ├── registry/ # Type registry for reflection
|
||||
│ └── health/ # Health checks and metrics
|
||||
│
|
||||
├── engine/ # Core engine logic
|
||||
│ ├── networking_manager/ # Network event loop
|
||||
│ ├── commands/ # Bevy commands
|
||||
│ └── game_actions/ # User action handling
|
||||
│
|
||||
├── debug_ui/ # egui debug interface
|
||||
├── render/ # Vendored Bevy render pipeline
|
||||
├── transform/ # Vendored transform with rkyv
|
||||
└── platform/ # Platform-specific code (iOS/desktop)
|
||||
```
|
||||
|
||||
### `app` (Demo Game)
|
||||
|
||||
**Purpose**: Demonstrates Marathon capabilities with a simple multiplayer cube game.
|
||||
|
||||
**Key Files:**
|
||||
- `main.rs` - Entry point with CLI argument handling
|
||||
- `engine_bridge.rs` - Connects Bevy game to Marathon engine
|
||||
- `cube.rs` - Demo game entity implementation
|
||||
- `session.rs` - Multiplayer session lifecycle management
|
||||
- `input/` - Input handling (keyboard, touch, Apple Pencil)
|
||||
- `rendering/` - Rendering setup and camera
|
||||
|
||||
### `macros` (Procedural Macros)
|
||||
|
||||
**Purpose**: Code generation for serialization and deserialization.
|
||||
|
||||
Built on Bevy's macro infrastructure for consistency with the ecosystem.
|
||||
|
||||
### `xtask` (Build Automation)
|
||||
|
||||
**Purpose**: Automate iOS build and deployment using the cargo-xtask pattern.
|
||||
|
||||
**Commands:**
|
||||
- `ios-build` - Build for iOS simulator/device
|
||||
- `ios-deploy` - Deploy to connected device
|
||||
- `ios-run` - Build and run on simulator
|
||||
|
||||
## Key Components
|
||||
|
||||
### 1. CRDT Synchronization Layer
|
||||
|
||||
**Location**: `libmarathon/src/networking/`
|
||||
|
||||
**Purpose**: Implements the CRDT-based synchronization protocol.
|
||||
|
||||
**Key Concepts:**
|
||||
- **Operations** - Immutable change events (Create, Update, Delete)
|
||||
- **Vector Clocks** - Track causality across peers
|
||||
- **OR-Sets** - Observed-Remove Sets for entity membership
|
||||
- **RGA** - Replicated Growable Array for ordered sequences
|
||||
- **LWW** - Last-Write-Wins for simple values
|
||||
|
||||
**Protocol Flow:**
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant A as Peer A
|
||||
participant G as Gossip Network
|
||||
participant B as Peer B
|
||||
|
||||
A->>A: Generate Op<br/>(with vector clock)
|
||||
A->>G: Broadcast Op
|
||||
G->>B: Deliver Op
|
||||
B->>B: Apply Op<br/>(update vector clock)
|
||||
B->>G: ACK
|
||||
G->>A: ACK
|
||||
```
|
||||
|
||||
See [RFC 0001](docs/rfcs/0001-crdt-gossip-sync.md) for detailed protocol specification.
|
||||
|
||||
### 2. Persistence Layer
|
||||
|
||||
**Location**: `libmarathon/src/persistence/`
|
||||
|
||||
**Purpose**: Persist game state to SQLite with minimal overhead.
|
||||
|
||||
**Architecture**: Three-tier system
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[In-Memory State<br/>Bevy ECS - Dirty Tracking] -->|Batch writes<br/>every N frames| B[Write Buffer<br/>Async Batching]
|
||||
B -->|Flush to disk| C[SQLite Database<br/>WAL Mode]
|
||||
|
||||
style A fill:#e1f5ff
|
||||
style B fill:#fff4e1
|
||||
style C fill:#e8f5e9
|
||||
```
|
||||
|
||||
**Key Features:**
|
||||
- **Automatic persistence** - Components marked with `Persisted` save automatically
|
||||
- **Type registry** - Reflection-based serialization
|
||||
- **WAL mode** - Write-Ahead Logging for crash safety
|
||||
- **Migrations** - Schema versioning support
|
||||
|
||||
See [RFC 0002](docs/rfcs/0002-persistence-strategy.md) for detailed design.
|
||||
|
||||
### 3. Networking Manager
|
||||
|
||||
**Location**: `libmarathon/src/engine/networking_manager.rs`
|
||||
|
||||
**Purpose**: Bridge between Bevy and the iroh networking stack.
|
||||
|
||||
**Responsibilities:**
|
||||
- Manage peer connections and discovery
|
||||
- Route operations to/from gossip network
|
||||
- Maintain session state
|
||||
- Handle join protocol for new peers
|
||||
|
||||
### 4. Entity Mapping System
|
||||
|
||||
**Location**: `libmarathon/src/networking/entity_map.rs`
|
||||
|
||||
**Purpose**: Map between Bevy's local `Entity` IDs and global `UUID`s.
|
||||
|
||||
**Why This Exists**: Bevy assigns local sequential entity IDs that differ across instances. We need stable UUIDs for networked entities that all peers agree on.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Bevy Entity<br/>Local ID: 123] <-->|Bidirectional<br/>Mapping| B[UUID<br/>550e8400-....-446655440000]
|
||||
|
||||
style A fill:#ffebee
|
||||
style B fill:#e8f5e9
|
||||
```
|
||||
|
||||
### 5. Debug UI System
|
||||
|
||||
**Location**: `libmarathon/src/debug_ui/`
|
||||
|
||||
**Purpose**: Provide runtime inspection of internal state.
|
||||
|
||||
Built with egui for immediate-mode GUI, integrated into Bevy's render pipeline.
|
||||
|
||||
**Features:**
|
||||
- View connected peers
|
||||
- Inspect vector clocks
|
||||
- Monitor operation log
|
||||
- Check persistence metrics
|
||||
- View entity mappings
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Local Change Flow
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[User Input] --> B[Bevy System<br/>e.g., move entity]
|
||||
B --> C[Generate CRDT<br/>Operation]
|
||||
C --> D[Apply Operation<br/>Locally]
|
||||
D --> E[Broadcast via<br/>Gossip]
|
||||
D --> F[Mark Dirty for<br/>Persistence]
|
||||
|
||||
style A fill:#e3f2fd
|
||||
style E fill:#fff3e0
|
||||
style F fill:#f3e5f5
|
||||
```
|
||||
|
||||
### Remote Change Flow
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Receive Operation<br/>from Gossip] --> B[Check Vector Clock<br/>causality]
|
||||
B --> C[Apply Operation<br/>to ECS]
|
||||
C --> D[Update Local<br/>Vector Clock]
|
||||
C --> E[Mark Dirty for<br/>Persistence]
|
||||
|
||||
style A fill:#fff3e0
|
||||
style C fill:#e8f5e9
|
||||
style E fill:#f3e5f5
|
||||
```
|
||||
|
||||
### Persistence Flow
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Every N Frames] --> B[Identify Dirty<br/>Entities]
|
||||
B --> C[Serialize to<br/>Write Buffer]
|
||||
C --> D[Batch Write<br/>to SQLite]
|
||||
D --> E[Clear Dirty<br/>Flags]
|
||||
E --> A
|
||||
|
||||
style A fill:#e8f5e9
|
||||
style D fill:#f3e5f5
|
||||
```
|
||||
|
||||
## Technology Decisions
|
||||
|
||||
### Why Bevy?
|
||||
|
||||
- **ECS architecture** maps perfectly to game development
|
||||
- **Cross-platform** (desktop, mobile, web)
|
||||
- **Active community** and ecosystem
|
||||
- **Performance** through data-oriented design
|
||||
|
||||
### Why iroh?
|
||||
|
||||
- **QUIC-based** - Modern, efficient transport
|
||||
- **NAT traversal** - Works behind firewalls
|
||||
- **Gossip protocol** - Epidemic broadcast for multi-peer
|
||||
- **Rust-native** - Zero-cost integration
|
||||
|
||||
### Why SQLite?
|
||||
|
||||
- **Embedded** - No server required
|
||||
- **Battle-tested** - Reliable persistence
|
||||
- **WAL mode** - Good write performance
|
||||
- **Cross-platform** - Works everywhere
|
||||
|
||||
### Why CRDTs?
|
||||
|
||||
- **No central authority** - True P2P
|
||||
- **Offline-first** - Work without connectivity
|
||||
- **Provable consistency** - Mathematical guarantees
|
||||
- **No conflict resolution UI** - Users don't see conflicts
|
||||
|
||||
## Design Constraints
|
||||
|
||||
### Current Limitations
|
||||
|
||||
1. **No Authentication** - All peers are trusted (0.1.x)
|
||||
2. **No Authorization** - All peers have full permissions
|
||||
3. **No Encryption** - Beyond QUIC's transport security
|
||||
4. **Limited Scalability** - Not tested beyond ~10 peers
|
||||
5. **Desktop + iOS Only** - Web and other platforms planned
|
||||
|
||||
### Performance Targets
|
||||
|
||||
- **Operation latency**: < 50ms peer-to-peer
|
||||
- **Persistence overhead**: < 5% frame time
|
||||
- **Memory overhead**: < 10MB for typical session
|
||||
- **Startup time**: < 2 seconds
|
||||
|
||||
### Intentional Non-Goals
|
||||
|
||||
- **Central server architecture** - Stay decentralized
|
||||
- **Strong consistency** - Use eventual consistency
|
||||
- **Traditional database** - Use CRDTs, not SQL queries
|
||||
- **General-purpose engine** - Focus on collaboration
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [RFC 0001: CRDT Synchronization Protocol](docs/rfcs/0001-crdt-gossip-sync.md)
|
||||
- [RFC 0002: Persistence Strategy](docs/rfcs/0002-persistence-strategy.md)
|
||||
- [RFC 0003: Sync Abstraction](docs/rfcs/0003-sync-abstraction.md)
|
||||
- [RFC 0004: Session Lifecycle](docs/rfcs/0004-session-lifecycle.md)
|
||||
- [RFC 0005: Spatial Audio System](docs/rfcs/0005-spatial-audio-vendoring.md)
|
||||
- [RFC 0006: Agent Simulation Architecture](docs/rfcs/0006-agent-simulation-architecture.md)
|
||||
|
||||
## Questions?
|
||||
|
||||
If you're working on Marathon and something isn't clear:
|
||||
|
||||
1. Check the RFCs in `docs/rfcs/`
|
||||
2. Search existing issues/discussions
|
||||
3. Ask in GitHub Discussions
|
||||
4. Reach out to maintainers
|
||||
|
||||
---
|
||||
|
||||
*This architecture will evolve. When making significant architectural changes, consider updating this document or creating a new RFC.*
|
||||
65
CHANGELOG.md
Normal file
65
CHANGELOG.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.1.0] - 2026-02-06
|
||||
|
||||
### Added
|
||||
|
||||
#### Core Features
|
||||
- CRDT-based synchronization using OR-Sets, RGA, and Last-Write-Wins semantics
|
||||
- Peer-to-peer networking built on iroh with QUIC transport
|
||||
- Gossip-based message broadcasting for multi-peer coordination
|
||||
- Offline-first architecture with automatic reconciliation
|
||||
- SQLite-backed persistence with WAL mode
|
||||
- Cross-platform support for macOS desktop and iOS
|
||||
|
||||
#### Demo Application
|
||||
- Replicated cube demo showcasing real-time collaboration
|
||||
- Multiple instance support for local testing
|
||||
- Apple Pencil input support on iPad
|
||||
- Real-time cursor and selection synchronization
|
||||
- Debug UI for inspecting internal state
|
||||
|
||||
#### Infrastructure
|
||||
- Bevy 0.17 ECS integration
|
||||
- Zero-copy serialization with rkyv
|
||||
- Automated iOS build tooling via xtask
|
||||
- Comprehensive RFC documentation covering architecture decisions
|
||||
|
||||
### Architecture
|
||||
|
||||
- **Networking Layer**: CRDT sync protocol, entity mapping, vector clocks, session management
|
||||
- **Persistence Layer**: Three-tier system (in-memory → write buffer → SQLite)
|
||||
- **Engine Core**: Event loop, networking manager, peer discovery, game actions
|
||||
- **Platform Support**: iOS and desktop with platform-specific input handling
|
||||
|
||||
### Documentation
|
||||
|
||||
- RFC 0001: CRDT Synchronization Protocol
|
||||
- RFC 0002: Persistence Strategy
|
||||
- RFC 0003: Sync Abstraction
|
||||
- RFC 0004: Session Lifecycle
|
||||
- RFC 0005: Spatial Audio System
|
||||
- RFC 0006: Agent Simulation Architecture
|
||||
- iOS deployment guide
|
||||
- Estimation methodology documentation
|
||||
|
||||
### Known Issues
|
||||
|
||||
- API is unstable and subject to change
|
||||
- Limited documentation for public APIs
|
||||
- Performance optimizations still needed for large-scale collaboration
|
||||
- iOS builds require manual Xcode configuration
|
||||
|
||||
### Notes
|
||||
|
||||
This is an early development release (version 0.x.y). The API is unstable and breaking changes are expected. Not recommended for production use.
|
||||
|
||||
[unreleased]: https://github.com/r3t-studios/marathon/compare/v0.1.0...HEAD
|
||||
[0.1.0]: https://github.com/r3t-studios/marathon/releases/tag/v0.1.0
|
||||
148
CODE_OF_CONDUCT.md
Normal file
148
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Addressing and Repairing Harm
|
||||
|
||||
If you are being harmed or notice that someone else is being harmed, or have any
|
||||
other concerns, please contact the community leaders responsible for enforcement
|
||||
at sienna@linux.com. All reports will be handled with discretion.
|
||||
|
||||
We are committed to addressing harm in a manner that is respectful to victims
|
||||
and survivors of violations of this Code of Conduct. When community leaders
|
||||
receive a report of a possible violation, they will:
|
||||
|
||||
1. **Acknowledge receipt** of the report
|
||||
2. **Assess the situation** and gather necessary information
|
||||
3. **Determine appropriate action** using the guidelines below
|
||||
4. **Communicate with all parties** involved
|
||||
5. **Take action** to address and repair harm
|
||||
6. **Follow up** to ensure the situation is resolved
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Restorative Justice
|
||||
|
||||
We believe in restorative justice and creating opportunities for those who have
|
||||
violated the Code of Conduct to repair harm and reintegrate into the community
|
||||
when appropriate. This may include:
|
||||
|
||||
* Facilitated conversations between affected parties
|
||||
* Public acknowledgment of harm and apology
|
||||
* Education and learning opportunities
|
||||
* Community service or contributions
|
||||
* Gradual reintegration with monitoring
|
||||
|
||||
The possibility of restoration depends on:
|
||||
* The severity of the violation
|
||||
* The willingness of the violator to acknowledge harm
|
||||
* The consent and comfort of those harmed
|
||||
* The assessment of community leaders
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official email address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 3.0, available at
|
||||
[https://www.contributor-covenant.org/version/3/0/code_of_conduct.html][v3.0].
|
||||
|
||||
The "Addressing and Repairing Harm" section is inspired by the restorative
|
||||
justice approach outlined in Contributor Covenant 3.0.
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v3.0]: https://www.contributor-covenant.org/version/3/0/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
343
CONTRIBUTING.md
Normal file
343
CONTRIBUTING.md
Normal file
@@ -0,0 +1,343 @@
|
||||
# Contributing to Marathon
|
||||
|
||||
Thank you for your interest in contributing to Marathon! We're excited to work with you.
|
||||
|
||||
This document provides guidelines for contributing to the project. Following these guidelines helps maintain code quality and makes the review process smoother for everyone.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Development Environment Setup](#development-environment-setup)
|
||||
- [How to Contribute](#how-to-contribute)
|
||||
- [Coding Standards](#coding-standards)
|
||||
- [Testing](#testing)
|
||||
- [Pull Request Process](#pull-request-process)
|
||||
- [Reporting Bugs](#reporting-bugs)
|
||||
- [Suggesting Features](#suggesting-features)
|
||||
- [AI Usage Policy](#ai-usage-policy)
|
||||
- [Questions?](#questions)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project adheres to the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to the project maintainers.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. **Fork the repository** on GitHub
|
||||
2. **Clone your fork** locally
|
||||
3. **Set up your development environment** (see below)
|
||||
4. **Create a branch** for your changes
|
||||
5. **Make your changes** with clear commit messages
|
||||
6. **Test your changes** thoroughly
|
||||
7. **Submit a pull request**
|
||||
|
||||
## Development Environment Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Rust** 2024 edition or later (install via [rustup](https://rustup.rs/))
|
||||
- **macOS** (for macOS desktop and iOS development)
|
||||
- **Xcode** and iOS simulator (for iOS development)
|
||||
- **Linux** (for Linux desktop development)
|
||||
- **Windows** (for Windows desktop development)
|
||||
- **Git** for version control
|
||||
|
||||
### Initial Setup
|
||||
|
||||
```bash
|
||||
# Clone your fork
|
||||
git clone https://github.com/user/marathon.git
|
||||
cd marathon
|
||||
|
||||
# Add upstream remote
|
||||
git remote add upstream https://github.com/r3t-studios/marathon.git
|
||||
|
||||
# Build the project
|
||||
cargo build
|
||||
|
||||
# Run tests
|
||||
cargo test
|
||||
|
||||
# Run the desktop demo
|
||||
cargo run --package app
|
||||
```
|
||||
|
||||
### iOS Development Setup
|
||||
|
||||
For iOS development, see our detailed [iOS Deployment Guide](docs/ios-deployment.md).
|
||||
|
||||
```bash
|
||||
# Build for iOS simulator
|
||||
cargo xtask ios-build
|
||||
|
||||
# Run on simulator
|
||||
cargo xtask ios-run
|
||||
```
|
||||
|
||||
### Useful Commands
|
||||
|
||||
```bash
|
||||
# Check code without building
|
||||
cargo check
|
||||
|
||||
# Run clippy for linting
|
||||
cargo clippy
|
||||
|
||||
# Format code
|
||||
cargo fmt
|
||||
|
||||
# Run tests with output
|
||||
cargo nextest run -- --nocapture
|
||||
|
||||
# Build documentation
|
||||
cargo doc --open
|
||||
```
|
||||
|
||||
## How to Contribute
|
||||
|
||||
### Types of Contributions
|
||||
|
||||
We welcome many types of contributions:
|
||||
|
||||
- **Bug fixes** - Fix issues and improve stability
|
||||
- **Features** - Implement new functionality (discuss first in an issue)
|
||||
- **Documentation** - Improve or add documentation
|
||||
- **Examples** - Create new examples or demos
|
||||
- **Tests** - Add test coverage
|
||||
- **Performance** - Optimize existing code
|
||||
- **Refactoring** - Improve code quality
|
||||
|
||||
### Before You Start
|
||||
|
||||
For **bug fixes and small improvements**, feel free to open a PR directly.
|
||||
|
||||
For **new features or significant changes**:
|
||||
1. **Open an issue first** to discuss the proposal
|
||||
2. Wait for maintainer feedback before investing significant time
|
||||
3. Reference the issue in your PR
|
||||
|
||||
This helps ensure your work aligns with project direction and avoids duplicate effort.
|
||||
|
||||
## Coding Standards
|
||||
|
||||
### Rust Style
|
||||
|
||||
- Follow the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/)
|
||||
- Follow the [Rust Style Guide](https://microsoft.github.io/rust-guidelines/guidelines/index.html)
|
||||
- Use `cargo +nightly fmt` to format code (run before committing)
|
||||
- Address all `cargo clippy` warnings
|
||||
- Use meaningful variable and function names
|
||||
- Add doc comments (`///`) for public APIs
|
||||
|
||||
### Code Organization
|
||||
|
||||
- Keep modules focused and cohesive
|
||||
- Prefer composition over inheritance
|
||||
- Use Rust's type system to enforce invariants
|
||||
- Avoid unnecessary `unsafe` code
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add doc comments for all public types, traits, and functions
|
||||
- Include examples in doc comments when helpful
|
||||
- Update relevant documentation in `/docs` when making architectural changes
|
||||
- Keep README.md in sync with current capabilities
|
||||
|
||||
### Commit Messages
|
||||
|
||||
Write clear, descriptive conventional commit messages:
|
||||
|
||||
```
|
||||
Short summary (50 chars or less)
|
||||
|
||||
More detailed explanation if needed. Wrap at 72 characters.
|
||||
|
||||
- Bullet points are fine
|
||||
- Use present tense ("Add feature" not "Added feature")
|
||||
- Reference issues and PRs with #123
|
||||
```
|
||||
|
||||
Good examples:
|
||||
```
|
||||
Add cursor synchronization to networking layer
|
||||
|
||||
Implement entity selection system for iOS
|
||||
|
||||
Fix panic in SQLite persistence during shutdown (#42)
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
cargo nextest run
|
||||
|
||||
# Run tests for specific crate
|
||||
cargo nextest run --package libmarathon
|
||||
|
||||
# Run specific test
|
||||
cargo nextest run test_vector_clock_merge
|
||||
|
||||
# Run tests with output
|
||||
cargo nextest run -- --nocapture
|
||||
```
|
||||
|
||||
### Writing Tests
|
||||
|
||||
- Add unit tests in the same file as the code (in a `mod tests` block)
|
||||
- Add integration tests in `tests/` directory
|
||||
- Test edge cases and error conditions
|
||||
- Keep tests focused and readable
|
||||
- Use descriptive test names: `test_vector_clock_handles_concurrent_updates`
|
||||
|
||||
### Test Coverage
|
||||
|
||||
We aim for good test coverage, especially for:
|
||||
- CRDT operations and synchronization logic
|
||||
- Persistence layer operations
|
||||
- Network protocol handling
|
||||
- Error conditions and edge cases
|
||||
|
||||
You don't need 100% coverage, but core logic should be well-tested.
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
### Before Submitting
|
||||
|
||||
1. **Update your branch** with latest upstream changes
|
||||
```bash
|
||||
git fetch upstream
|
||||
git rebase upstream/mainline
|
||||
```
|
||||
|
||||
2. **Run the test suite** and ensure all tests pass
|
||||
```bash
|
||||
cargo test
|
||||
```
|
||||
|
||||
3. **Run clippy** and fix any warnings
|
||||
```bash
|
||||
cargo clippy
|
||||
```
|
||||
|
||||
4. **Format your code**
|
||||
```bash
|
||||
cargo fmt
|
||||
```
|
||||
|
||||
5. **Update documentation** if you changed APIs or behavior
|
||||
|
||||
### Submitting Your PR
|
||||
|
||||
1. **Push to your fork**
|
||||
```bash
|
||||
git push origin your-branch-name
|
||||
```
|
||||
|
||||
2. **Open a pull request** on GitHub
|
||||
|
||||
3. **Fill out the PR template** with:
|
||||
- Clear description of what changed and why
|
||||
- Link to related issues
|
||||
- Testing performed
|
||||
- Screenshots/videos for UI changes
|
||||
|
||||
4. **Request review** from maintainers
|
||||
|
||||
### During Review
|
||||
|
||||
- Be responsive to feedback
|
||||
- Make requested changes promptly
|
||||
- Push updates to the same branch (they'll appear in the PR)
|
||||
- Use "fixup" commits or force-push after addressing review comments
|
||||
- Be patient - maintainers are volunteers with limited time
|
||||
|
||||
### After Approval
|
||||
|
||||
- Maintainers will merge your PR
|
||||
- You can delete your branch after merging
|
||||
- Celebrate! 🎉 You're now a Marathon contributor!
|
||||
|
||||
## Reporting Bugs
|
||||
|
||||
### Before Reporting
|
||||
|
||||
1. **Check existing issues** to avoid duplicates
|
||||
2. **Verify it's a bug** and not expected behavior
|
||||
3. **Test on the latest version** from mainline branch
|
||||
|
||||
### Bug Report Template
|
||||
|
||||
When opening a bug report, please include:
|
||||
|
||||
- **Description** - What went wrong?
|
||||
- **Expected behavior** - What should have happened?
|
||||
- **Actual behavior** - What actually happened?
|
||||
- **Steps to reproduce** - Minimal steps to reproduce the issue
|
||||
- **Environment**:
|
||||
- OS version (macOS version, iOS version)
|
||||
- Rust version (`rustc --version`)
|
||||
- Marathon version or commit hash
|
||||
- **Logs/Stack traces** - Error messages or relevant log output
|
||||
- **Screenshots/Videos** - If applicable
|
||||
|
||||
### Security Issues
|
||||
|
||||
**Do not report security vulnerabilities in public issues.**
|
||||
|
||||
Please see our [Security Policy](SECURITY.md) for how to report security issues privately.
|
||||
|
||||
## Suggesting Features
|
||||
|
||||
We welcome feature suggestions! Here's how to propose them effectively:
|
||||
|
||||
### Before Suggesting
|
||||
|
||||
1. **Check existing issues and discussions** for similar ideas
|
||||
2. **Consider if it aligns** with Marathon's goals (multiplayer game engine framework)
|
||||
3. **Think about the scope** - is this a core feature or better as a plugin/extension?
|
||||
|
||||
### Feature Request Template
|
||||
|
||||
When suggesting a feature, please include:
|
||||
|
||||
- **Problem statement** - What problem does this solve?
|
||||
- **Proposed solution** - How would this feature work?
|
||||
- **Alternatives considered** - What other approaches did you think about?
|
||||
- **Use cases** - Real-world scenarios where this helps
|
||||
- **Implementation ideas** - Technical approach (if you have thoughts)
|
||||
|
||||
### Feature Discussion
|
||||
|
||||
- Maintainers will label feature requests as `enhancement`
|
||||
- We'll discuss feasibility, scope, and priority
|
||||
- Features that align with the roadmap are more likely to be accepted
|
||||
- You're welcome to implement features you propose (with approval)
|
||||
|
||||
## AI Usage Policy
|
||||
|
||||
Marathon has specific guidelines around AI and ML tool usage. Please read our [AI Usage Policy](AI_POLICY.md) before contributing.
|
||||
|
||||
**Key points:**
|
||||
- AI tools (Copilot, ChatGPT, etc.) are allowed for productivity
|
||||
- You must understand and be accountable for all code you submit
|
||||
- Humans make all architectural decisions, not AI
|
||||
- When in doubt, ask yourself: "Can I maintain and debug this?"
|
||||
|
||||
## Questions?
|
||||
|
||||
- **General questions** - Open a [Discussion](https://github.com/yourusername/marathon/discussions)
|
||||
- **Bug reports** - Open an [Issue](https://github.com/yourusername/marathon/issues)
|
||||
- **Real-time chat** - [Discord/Slack link if you have one]
|
||||
- **Email** - [maintainer email if appropriate]
|
||||
|
||||
## Recognition
|
||||
|
||||
All contributors will be recognized in our release notes and can be listed in AUTHORS file (coming soon).
|
||||
|
||||
---
|
||||
|
||||
Thank you for contributing to Marathon! Your effort helps make collaborative software better for everyone.
|
||||
186
Cargo.lock
generated
186
Cargo.lock
generated
@@ -44,8 +44,8 @@ dependencies = [
|
||||
"accesskit_consumer",
|
||||
"hashbrown 0.15.5",
|
||||
"objc2 0.5.2",
|
||||
"objc2-app-kit 0.2.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -301,26 +301,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arboard"
|
||||
version = "3.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"image",
|
||||
"log",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation 0.3.2",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"windows-sys 0.60.2",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.9"
|
||||
@@ -2116,15 +2096,6 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
@@ -2990,12 +2961,6 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.22.11"
|
||||
@@ -3044,26 +3009,6 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fax"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
|
||||
dependencies = [
|
||||
"fax_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fax_derive"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.7"
|
||||
@@ -4064,7 +4009,6 @@ dependencies = [
|
||||
"moxcms",
|
||||
"num-traits",
|
||||
"png",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4594,7 +4538,6 @@ name = "libmarathon"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arboard",
|
||||
"async-channel",
|
||||
"bevy",
|
||||
"bevy_app",
|
||||
@@ -4649,7 +4592,6 @@ dependencies = [
|
||||
"proptest",
|
||||
"radsort",
|
||||
"rand 0.8.5",
|
||||
"raw-window-handle",
|
||||
"rkyv",
|
||||
"rusqlite",
|
||||
"rustc-hash 2.1.1",
|
||||
@@ -5428,22 +5370,10 @@ dependencies = [
|
||||
"objc2 0.5.2",
|
||||
"objc2-core-data",
|
||||
"objc2-core-image",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
"objc2-quartz-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-app-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-cloud-kit"
|
||||
version = "0.2.2"
|
||||
@@ -5454,7 +5384,7 @@ dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-core-location",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5465,7 +5395,7 @@ checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
|
||||
dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5477,7 +5407,7 @@ dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5487,21 +5417,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"dispatch2",
|
||||
"objc2 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-graphics"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"dispatch2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-surface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5512,7 +5427,7 @@ checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
|
||||
dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
"objc2-metal",
|
||||
]
|
||||
|
||||
@@ -5525,7 +5440,7 @@ dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-contacts",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5547,17 +5462,6 @@ dependencies = [
|
||||
"objc2 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-kit"
|
||||
version = "0.3.2"
|
||||
@@ -5568,17 +5472,6 @@ dependencies = [
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-surface"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-link-presentation"
|
||||
version = "0.2.2"
|
||||
@@ -5587,8 +5480,8 @@ checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
|
||||
dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-app-kit 0.2.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5600,7 +5493,7 @@ dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5612,7 +5505,7 @@ dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
"objc2-metal",
|
||||
]
|
||||
|
||||
@@ -5623,7 +5516,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
|
||||
dependencies = [
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5639,7 +5532,7 @@ dependencies = [
|
||||
"objc2-core-data",
|
||||
"objc2-core-image",
|
||||
"objc2-core-location",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
"objc2-link-presentation",
|
||||
"objc2-quartz-core",
|
||||
"objc2-symbols",
|
||||
@@ -5655,7 +5548,7 @@ checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
|
||||
dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5668,7 +5561,7 @@ dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-core-location",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6190,12 +6083,6 @@ version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.37.5"
|
||||
@@ -6775,7 +6662,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"quick-error 1.2.3",
|
||||
"quick-error",
|
||||
"tempfile",
|
||||
"wait-timeout",
|
||||
]
|
||||
@@ -7482,20 +7369,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
|
||||
dependencies = [
|
||||
"fax",
|
||||
"flate2",
|
||||
"half",
|
||||
"quick-error 2.0.1",
|
||||
"weezl",
|
||||
"zune-jpeg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.44"
|
||||
@@ -8347,12 +8220,6 @@ dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
|
||||
|
||||
[[package]]
|
||||
name = "wgpu"
|
||||
version = "26.0.1"
|
||||
@@ -9151,8 +9018,8 @@ dependencies = [
|
||||
"memmap2",
|
||||
"ndk 0.9.0",
|
||||
"objc2 0.5.2",
|
||||
"objc2-app-kit 0.2.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation",
|
||||
"objc2-ui-kit",
|
||||
"orbclient",
|
||||
"percent-encoding",
|
||||
@@ -9460,18 +9327,3 @@ dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
|
||||
28
Cargo.toml
28
Cargo.toml
@@ -10,13 +10,14 @@ edition = "2024"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-stream = "0.1"
|
||||
tokio-util = "0.7"
|
||||
futures-lite = "2.0"
|
||||
|
||||
# Iroh - P2P networking and gossip
|
||||
iroh = { version = "0.95.0", features = ["discovery-pkarr-dht"] }
|
||||
iroh-gossip = "0.95.0"
|
||||
|
||||
# Database
|
||||
rusqlite = "0.37.0"
|
||||
rusqlite = { version = "0.37.0", features = ["bundled"] }
|
||||
|
||||
# Serialization
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@@ -34,6 +35,8 @@ chrono = { version = "0.4", features = ["serde"] }
|
||||
# Logging
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
tracing-appender = "0.2"
|
||||
tracing-oslog = "0.3"
|
||||
|
||||
# Random
|
||||
rand = "0.8"
|
||||
@@ -41,17 +44,24 @@ rand = "0.8"
|
||||
# Encoding
|
||||
hex = "0.4"
|
||||
|
||||
# ML/AI
|
||||
candle-core = "0.8"
|
||||
candle-nn = "0.8"
|
||||
candle-transformers = "0.8"
|
||||
tokenizers = "0.20"
|
||||
hf-hub = "0.3"
|
||||
# Data structures
|
||||
bytes = "1.0"
|
||||
crossbeam-channel = "0.5"
|
||||
uuid = { version = "1.0", features = ["v4", "serde"] }
|
||||
|
||||
# Bevy
|
||||
bevy = "0.17"
|
||||
# Bevy and graphics
|
||||
bevy = "0.17.2"
|
||||
egui = { version = "0.33", default-features = false, features = ["bytemuck", "default_fonts"] }
|
||||
glam = "0.29"
|
||||
winit = "0.30"
|
||||
|
||||
# Synchronization
|
||||
parking_lot = "0.12"
|
||||
crdts = "7.3"
|
||||
inventory = "0.3"
|
||||
|
||||
# CLI
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
|
||||
# Testing
|
||||
tempfile = "3"
|
||||
|
||||
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Marathon Contributors
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
164
README.md
Normal file
164
README.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Marathon
|
||||
|
||||
**A peer-to-peer game engine development kit built with Rust and CRDTs**
|
||||
|
||||
Marathon is a multiplayer game engine framework designed for building real-time collaborative games with offline-first capabilities. Built on [Bevy](https://bevyengine.org/) and [iroh](https://iroh.computer/), it provides CRDT-based state synchronization, peer-to-peer networking, and persistent state management out of the box - so you can focus on making great games instead of wrestling with networking code.
|
||||
|
||||
## ⚠️ Early Development Notice
|
||||
|
||||
**This project is in early development (<v1.0.0).**
|
||||
|
||||
- The API is unstable and may change without notice
|
||||
- Breaking changes are expected between minor versions
|
||||
- Not recommended for production use
|
||||
- Documentation is still being written
|
||||
- We welcome feedback and contributions!
|
||||
|
||||
Version 1.0.0 will indicate the first stable release.
|
||||
|
||||
## Features
|
||||
|
||||
- **CRDT-based synchronization** - Conflict-free multiplayer state management using OR-Sets, RGA, and LWW semantics
|
||||
- **Peer-to-peer networking** - Built on iroh with QUIC transport and gossip-based message broadcasting
|
||||
- **Offline-first architecture** - Players can continue playing during network issues and sync when reconnected
|
||||
- **Persistent game state** - SQLite-backed storage with automatic entity persistence
|
||||
- **Cross-platform** - Supports macOS desktop and iOS (simulator and device), with more platforms planned
|
||||
- **Built with Bevy** - Leverages the Bevy game engine's ECS architecture and parts of its ecosystem
|
||||
|
||||
## Demo
|
||||
|
||||
The current demo is a **replicated cube game** that synchronizes in real-time across multiple instances:
|
||||
- Apple Pencil input support on iPad
|
||||
- Real-time player cursor and selection sharing
|
||||
- Automatic game state synchronization across network partitions
|
||||
- Multiple players can interact with the same game world simultaneously
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Rust** 2024 edition or later
|
||||
- **macOS** (for desktop demo)
|
||||
- **Xcode** (for iOS development)
|
||||
|
||||
### Building the Desktop Demo
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/yourusername/marathon.git
|
||||
cd marathon
|
||||
|
||||
# Build and run
|
||||
cargo run --package app
|
||||
```
|
||||
|
||||
To run multiple instances for testing multiplayer:
|
||||
|
||||
```bash
|
||||
# Terminal 1
|
||||
cargo run --package app -- --instance 0
|
||||
|
||||
# Terminal 2
|
||||
cargo run --package app -- --instance 1
|
||||
```
|
||||
|
||||
### Building for iOS
|
||||
|
||||
Marathon includes automated iOS build tooling via `xtask`:
|
||||
|
||||
```bash
|
||||
# Build for iOS simulator
|
||||
cargo xtask ios-build
|
||||
|
||||
# Deploy to connected device
|
||||
cargo xtask ios-deploy
|
||||
|
||||
# Build and run on simulator
|
||||
cargo xtask ios-run
|
||||
```
|
||||
|
||||
See [docs/ios-deployment.md](docs/ios-deployment.md) for detailed iOS setup instructions.
|
||||
|
||||
## Architecture
|
||||
|
||||
Marathon is organized as a Rust workspace with four crates:
|
||||
|
||||
- **`libmarathon`** - Core engine library with networking, persistence, and CRDT sync
|
||||
- **`app`** - Demo game showcasing multiplayer cube gameplay
|
||||
- **`macros`** - Procedural macros for serialization
|
||||
- **`xtask`** - Build automation for iOS deployment
|
||||
|
||||
### Key Components
|
||||
|
||||
- **Networking** - CRDT synchronization protocol, gossip-based broadcast, entity mapping
|
||||
- **Persistence** - Three-tier system (in-memory → write buffer → SQLite WAL)
|
||||
- **Engine** - Core event loop, peer discovery, session management
|
||||
- **Debug UI** - egui-based debug interface for inspecting state
|
||||
|
||||
For detailed architecture information, see [ARCHITECTURE.md](ARCHITECTURE.md).
|
||||
|
||||
## Documentation
|
||||
|
||||
- **[RFCs](docs/rfcs/)** - Design documents covering core architecture decisions
|
||||
- [0001: CRDT Synchronization Protocol](docs/rfcs/0001-crdt-gossip-sync.md)
|
||||
- [0002: Persistence Strategy](docs/rfcs/0002-persistence-strategy.md)
|
||||
- [0003: Sync Abstraction](docs/rfcs/0003-sync-abstraction.md)
|
||||
- [0004: Session Lifecycle](docs/rfcs/0004-session-lifecycle.md)
|
||||
- [0005: Spatial Audio System](docs/rfcs/0005-spatial-audio-vendoring.md)
|
||||
- [0006: Agent Simulation Architecture](docs/rfcs/0006-agent-simulation-architecture.md)
|
||||
- **[iOS Deployment Guide](docs/ios-deployment.md)** - Complete iOS build instructions
|
||||
- **[Estimation Methodology](docs/ESTIMATION.md)** - Project sizing and prioritization approach
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- **[Bevy 0.17](https://bevyengine.org/)** - Game engine and ECS framework
|
||||
- **[iroh 0.95](https://iroh.computer/)** - P2P networking with QUIC
|
||||
- **[iroh-gossip 0.95](https://github.com/n0-computer/iroh-gossip)** - Gossip protocol for multi-peer coordination
|
||||
- **[SQLite](https://www.sqlite.org/)** - Local persistence with WAL mode
|
||||
- **[rkyv 0.8](https://rkyv.org/)** - Zero-copy serialization
|
||||
- **[egui 0.33](https://www.egui.rs/)** - Immediate-mode GUI
|
||||
- **[wgpu 26](https://wgpu.rs/)** - Graphics API (via Bevy)
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for:
|
||||
- Development environment setup
|
||||
- Code style and conventions
|
||||
- How to submit pull requests
|
||||
- Testing guidelines
|
||||
|
||||
Please also read our [Code of Conduct](CODE_OF_CONDUCT.md) and [AI Usage Policy](AI_POLICY.md).
|
||||
|
||||
## Project Status
|
||||
|
||||
Marathon is actively developed and currently focused on:
|
||||
- Core CRDT synchronization protocol
|
||||
- Persistence layer stability
|
||||
- Multi-platform support (macOS, iOS)
|
||||
- Demo applications
|
||||
|
||||
See our [roadmap](https://github.com/yourusername/marathon/issues) for planned features and known issues.
|
||||
|
||||
## Community
|
||||
|
||||
- **Issues** - [GitHub Issues](https://github.com/yourusername/marathon/issues)
|
||||
- **Discussions** - [GitHub Discussions](https://github.com/yourusername/marathon/discussions)
|
||||
|
||||
## Security
|
||||
|
||||
Please see our [Security Policy](SECURITY.md) for information on reporting vulnerabilities.
|
||||
|
||||
## License
|
||||
|
||||
Marathon is licensed under the [MIT License](LICENSE).
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Marathon builds on the incredible work of:
|
||||
- The [Bevy community](https://bevyengine.org/) for the game engine
|
||||
- The [iroh team](https://iroh.computer/) for P2P networking infrastructure
|
||||
- The Rust CRDT ecosystem
|
||||
|
||||
---
|
||||
|
||||
**Built with Rust 🦀 and collaborative spirit**
|
||||
143
SECURITY.md
Normal file
143
SECURITY.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
As an early-stage project (version 0.x.y), security support is limited to the latest development version.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| mainline branch | :white_check_mark: |
|
||||
| 0.1.x | :white_check_mark: |
|
||||
| < 0.1.0 | :x: |
|
||||
|
||||
## Security Maturity
|
||||
|
||||
**Marathon is currently in early development (0.1.x) and is NOT recommended for production use or handling sensitive data.**
|
||||
|
||||
Security considerations for the current release:
|
||||
|
||||
- ⚠️ **Network protocol** is not hardened against malicious peers
|
||||
- ⚠️ **Authentication** is not yet implemented
|
||||
- ⚠️ **Encryption** is provided by QUIC but not verified against attacks
|
||||
- ⚠️ **Authorization** is not implemented
|
||||
- ⚠️ **Data validation** is basic and not audited
|
||||
- ⚠️ **Persistence layer** stores data unencrypted locally
|
||||
|
||||
**Use Marathon only in trusted development environments with non-sensitive data.**
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We take security issues seriously. If you discover a security vulnerability in Marathon, please help us address it responsibly.
|
||||
|
||||
### How to Report
|
||||
|
||||
**Please DO NOT report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, report vulnerabilities by:
|
||||
|
||||
1. **Email**: Send details to sienna@linux.com
|
||||
2. **Subject line**: Include "SECURITY" and a brief description
|
||||
3. **Include**:
|
||||
- Description of the vulnerability
|
||||
- Steps to reproduce
|
||||
- Potential impact
|
||||
- Suggested fix (if you have one)
|
||||
|
||||
### What to Expect
|
||||
|
||||
After you submit a report:
|
||||
|
||||
1. **Acknowledgment**: We'll confirm receipt within 48 hours
|
||||
2. **Assessment**: We'll evaluate the severity and impact within 5 business days
|
||||
3. **Updates**: We'll keep you informed of our progress
|
||||
4. **Resolution**: We'll work on a fix and coordinate disclosure timing with you
|
||||
5. **Credit**: We'll acknowledge your contribution (unless you prefer to remain anonymous)
|
||||
|
||||
### Disclosure Timeline
|
||||
|
||||
- **Critical vulnerabilities**: Aim to fix within 30 days
|
||||
- **High severity**: Aim to fix within 60 days
|
||||
- **Medium/Low severity**: Addressed in regular development cycle
|
||||
|
||||
We'll coordinate public disclosure timing with you after a fix is available.
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
If you're using Marathon (keeping in mind it's not production-ready):
|
||||
|
||||
### For Development
|
||||
|
||||
- **Use isolated networks** for testing
|
||||
- **Don't use real user data** or sensitive information
|
||||
- **Don't expose to the internet** without additional security layers
|
||||
- **Keep dependencies updated** with `cargo update`
|
||||
- **Review security advisories** for Rust crates you depend on
|
||||
|
||||
### For Deployment (Future)
|
||||
|
||||
Once Marathon reaches production readiness, we plan to implement:
|
||||
|
||||
- End-to-end encryption for all peer communications
|
||||
- Peer authentication and authorization
|
||||
- Encrypted local storage
|
||||
- Rate limiting and DoS protection
|
||||
- Security audit trail
|
||||
- Regular security audits
|
||||
|
||||
### Known Security Gaps
|
||||
|
||||
Current known limitations (to be addressed before 1.0):
|
||||
|
||||
- **No peer authentication** - Any peer can join a session
|
||||
- **No authorization system** - All peers have full permissions
|
||||
- **No encrypted storage** - Local SQLite database is unencrypted
|
||||
- **Limited input validation** - CRDT operations trust peer input
|
||||
- **No audit logging** - Actions are not logged for security review
|
||||
- **Network protocol not hardened** - Vulnerable to malicious peers
|
||||
|
||||
## Security Contact
|
||||
|
||||
For security-related questions or concerns:
|
||||
|
||||
- **Email**: sienna@linux.com
|
||||
- **Response time**: Within 48 hours for initial contact
|
||||
|
||||
## Security Advisories
|
||||
|
||||
Security advisories will be published:
|
||||
|
||||
- In GitHub Security Advisories
|
||||
- In release notes
|
||||
- In this SECURITY.md file
|
||||
|
||||
Currently, there are no published security advisories.
|
||||
|
||||
## Responsible Disclosure
|
||||
|
||||
We believe in responsible disclosure and request that you:
|
||||
|
||||
- Give us reasonable time to address issues before public disclosure
|
||||
- Make a good faith effort to avoid privacy violations and service disruption
|
||||
- Don't exploit vulnerabilities beyond demonstrating the issue
|
||||
- Don't access or modify data that doesn't belong to you
|
||||
|
||||
In return, we commit to:
|
||||
|
||||
- Respond promptly to your report
|
||||
- Keep you informed of our progress
|
||||
- Credit you for your discovery (if desired)
|
||||
- Not pursue legal action for good faith security research
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Rust Security Advisory Database](https://rustsec.org/)
|
||||
- [cargo-audit](https://github.com/RustSec/rustsec/tree/main/cargo-audit) - Audit Rust dependencies
|
||||
- [OWASP Top 10](https://owasp.org/www-project-top-ten/) - Common web application security risks
|
||||
|
||||
## Version History
|
||||
|
||||
- **2026-02-06**: Initial security policy for v0.1.0 release
|
||||
|
||||
---
|
||||
|
||||
**Thank you for helping keep Marathon and its users safe!**
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "app"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["desktop"]
|
||||
@@ -12,45 +12,45 @@ headless = []
|
||||
[dependencies]
|
||||
libmarathon = { path = "../libmarathon" }
|
||||
macros = { path = "../macros" }
|
||||
inventory = { workspace = true }
|
||||
rkyv = { workspace = true }
|
||||
bevy = { version = "0.17", default-features = false, features = [
|
||||
inventory.workspace = true
|
||||
rkyv.workspace = true
|
||||
bevy = { version = "0.17.2", default-features = false, features = [
|
||||
# bevy_render, bevy_core_pipeline, bevy_pbr are now vendored in libmarathon
|
||||
"bevy_ui",
|
||||
"bevy_text",
|
||||
"png",
|
||||
] }
|
||||
egui = { version = "0.33", default-features = false, features = ["bytemuck", "default_fonts"] }
|
||||
glam = "0.29"
|
||||
winit = "0.30"
|
||||
egui.workspace = true
|
||||
glam.workspace = true
|
||||
winit.workspace = true
|
||||
raw-window-handle = "0.6"
|
||||
uuid = { version = "1.0", features = ["v4", "serde"] }
|
||||
anyhow = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
tracing-appender = "0.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
rand = "0.8"
|
||||
iroh = { version = "0.95", features = ["discovery-local-network"] }
|
||||
iroh-gossip = "0.95"
|
||||
futures-lite = "2.0"
|
||||
bytes = "1.0"
|
||||
crossbeam-channel = "0.5.15"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
uuid.workspace = true
|
||||
anyhow.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing-appender.workspace = true
|
||||
serde.workspace = true
|
||||
rand.workspace = true
|
||||
iroh = { workspace = true, features = ["discovery-local-network"] }
|
||||
iroh-gossip.workspace = true
|
||||
futures-lite.workspace = true
|
||||
bytes.workspace = true
|
||||
crossbeam-channel.workspace = true
|
||||
clap.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies]
|
||||
objc = "0.2"
|
||||
raw-window-handle = "0.6"
|
||||
tracing-oslog = "0.3"
|
||||
tracing-oslog.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
iroh = { version = "0.95", features = ["discovery-local-network"] }
|
||||
iroh-gossip = "0.95"
|
||||
tempfile = "3"
|
||||
futures-lite = "2.0"
|
||||
rkyv = { workspace = true }
|
||||
bytes = "1.0"
|
||||
iroh = { workspace = true, features = ["discovery-local-network"] }
|
||||
iroh-gossip.workspace = true
|
||||
tempfile.workspace = true
|
||||
futures-lite.workspace = true
|
||||
rkyv.workspace = true
|
||||
bytes.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "app"
|
||||
|
||||
@@ -5,7 +5,6 @@ edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
arboard = "3.4"
|
||||
bevy.workspace = true
|
||||
rkyv.workspace = true
|
||||
|
||||
@@ -53,35 +52,34 @@ blake3 = "1.5"
|
||||
blocking = "1.6"
|
||||
hex.workspace = true
|
||||
bytemuck = { version = "1.14", features = ["derive"] }
|
||||
bytes = "1.0"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
bytes.workspace = true
|
||||
chrono.workspace = true
|
||||
crdts.workspace = true
|
||||
crossbeam-channel = "0.5"
|
||||
crossbeam-channel.workspace = true
|
||||
dirs = "5.0"
|
||||
egui = { version = "0.33", default-features = false, features = ["bytemuck", "default_fonts"] }
|
||||
egui.workspace = true
|
||||
encase = { version = "0.11", features = ["glam"] }
|
||||
futures-lite = "2.0"
|
||||
glam = "0.29"
|
||||
futures-lite.workspace = true
|
||||
glam.workspace = true
|
||||
inventory.workspace = true
|
||||
iroh = { workspace = true, features = ["discovery-local-network"] }
|
||||
iroh-gossip.workspace = true
|
||||
pkarr = "5.0"
|
||||
itertools = "0.14"
|
||||
rand = "0.8"
|
||||
raw-window-handle = "0.6"
|
||||
rusqlite = { version = "0.37.0", features = ["bundled"] }
|
||||
rand.workspace = true
|
||||
rusqlite.workspace = true
|
||||
rustc-hash = "2.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
sha2 = "0.10"
|
||||
thiserror = "2.0"
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-util.workspace = true
|
||||
toml.workspace = true
|
||||
tracing.workspace = true
|
||||
uuid = { version = "1.0", features = ["v4", "serde"] }
|
||||
uuid.workspace = true
|
||||
wgpu-types = "26.0"
|
||||
winit = "0.30"
|
||||
winit.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies]
|
||||
tracing-oslog = "0.3"
|
||||
@@ -90,8 +88,8 @@ tracing-oslog = "0.3"
|
||||
tokio.workspace = true
|
||||
iroh = { workspace = true, features = ["discovery-local-network"] }
|
||||
iroh-gossip.workspace = true
|
||||
futures-lite = "2.0"
|
||||
tempfile = "3"
|
||||
futures-lite.workspace = true
|
||||
tempfile.workspace = true
|
||||
proptest = "1.4"
|
||||
criterion = "0.5"
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ mod test_utils;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use bevy::prelude::Transform;
|
||||
use bevy::prelude::{App, FixedUpdate, Transform};
|
||||
use libmarathon::networking::{
|
||||
CurrentSession,
|
||||
GossipBridge,
|
||||
@@ -23,6 +23,14 @@ use libmarathon::networking::{
|
||||
use test_utils::{TestContext, create_test_app_maybe_offline};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Helper to ensure FixedUpdate runs (since it's on a fixed timestep)
|
||||
fn update_with_fixed(app: &mut App) {
|
||||
// Run Main schedule (which includes Update)
|
||||
app.update();
|
||||
// Explicitly run FixedUpdate to ensure systems there execute
|
||||
app.world_mut().run_schedule(FixedUpdate);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Session Lifecycle Tests
|
||||
// ============================================================================
|
||||
@@ -114,7 +122,7 @@ async fn test_join_request_sent() -> Result<()> {
|
||||
|
||||
// Create app offline
|
||||
let mut app = create_test_app_maybe_offline(node_id, ctx.db_path(), None);
|
||||
app.update();
|
||||
update_with_fixed(&mut app);
|
||||
|
||||
// Create and insert GossipBridge
|
||||
let bridge = GossipBridge::new(node_id);
|
||||
@@ -128,7 +136,7 @@ async fn test_join_request_sent() -> Result<()> {
|
||||
|
||||
// Update to trigger send_join_request_once_system
|
||||
// With the peer-wait logic, JoinRequest waits for peers or timeout
|
||||
app.update(); // Start wait timer
|
||||
update_with_fixed(&mut app); // Start wait timer
|
||||
|
||||
// Simulate 1-second timeout (first node case - no peers)
|
||||
{
|
||||
@@ -139,7 +147,7 @@ async fn test_join_request_sent() -> Result<()> {
|
||||
}
|
||||
|
||||
// Update again - should send JoinRequest due to timeout
|
||||
app.update();
|
||||
update_with_fixed(&mut app);
|
||||
|
||||
// Verify JoinRequest was sent by checking JoinRequestSent resource
|
||||
{
|
||||
@@ -393,7 +401,7 @@ async fn test_join_request_waits_for_peers() -> Result<()> {
|
||||
let bridge = GossipBridge::new(node_id);
|
||||
let mut app = create_test_app_maybe_offline(node_id, ctx.db_path(), Some(bridge.clone()));
|
||||
|
||||
app.update();
|
||||
update_with_fixed(&mut app);
|
||||
|
||||
// Transition to Joining
|
||||
{
|
||||
@@ -412,7 +420,7 @@ async fn test_join_request_waits_for_peers() -> Result<()> {
|
||||
|
||||
// Run for 10 frames (~166ms) - should NOT send JoinRequest yet (no peers)
|
||||
for i in 0..10 {
|
||||
app.update();
|
||||
update_with_fixed(&mut app);
|
||||
tokio::time::sleep(Duration::from_millis(16)).await;
|
||||
|
||||
let join_sent = app.world().resource::<libmarathon::networking::JoinRequestSent>();
|
||||
@@ -560,7 +568,7 @@ async fn test_join_request_sends_after_timeout() -> Result<()> {
|
||||
let bridge = GossipBridge::new(node_id);
|
||||
let mut app = create_test_app_maybe_offline(node_id, ctx.db_path(), Some(bridge.clone()));
|
||||
|
||||
app.update();
|
||||
update_with_fixed(&mut app);
|
||||
|
||||
// Transition to Joining
|
||||
{
|
||||
@@ -571,7 +579,7 @@ async fn test_join_request_sends_after_timeout() -> Result<()> {
|
||||
println!("Initial state: Session=Joining, Peers=0");
|
||||
|
||||
// Run one frame to start the wait timer
|
||||
app.update();
|
||||
update_with_fixed(&mut app);
|
||||
|
||||
// Manually set wait_started to 1.1 seconds ago to simulate timeout
|
||||
{
|
||||
@@ -583,7 +591,7 @@ async fn test_join_request_sends_after_timeout() -> Result<()> {
|
||||
}
|
||||
|
||||
// Run one frame - should send JoinRequest due to timeout
|
||||
app.update();
|
||||
update_with_fixed(&mut app);
|
||||
|
||||
{
|
||||
let join_sent = app.world().resource::<libmarathon::networking::JoinRequestSent>();
|
||||
@@ -627,7 +635,7 @@ async fn test_join_request_only_sent_once() -> Result<()> {
|
||||
let bridge = GossipBridge::new(node_id);
|
||||
let mut app = create_test_app_maybe_offline(node_id, ctx.db_path(), Some(bridge.clone()));
|
||||
|
||||
app.update();
|
||||
update_with_fixed(&mut app);
|
||||
|
||||
// Transition to Joining and add a peer
|
||||
{
|
||||
@@ -644,7 +652,7 @@ async fn test_join_request_only_sent_once() -> Result<()> {
|
||||
println!("Initial state: Session=Joining, Peers=1");
|
||||
|
||||
// Run frame - should send JoinRequest
|
||||
app.update();
|
||||
update_with_fixed(&mut app);
|
||||
|
||||
{
|
||||
let join_sent = app.world().resource::<libmarathon::networking::JoinRequestSent>();
|
||||
@@ -678,7 +686,7 @@ async fn test_join_request_only_sent_once() -> Result<()> {
|
||||
|
||||
// Run 20 more frames - should NOT send JoinRequest again
|
||||
for i in 0..20 {
|
||||
app.update();
|
||||
update_with_fixed(&mut app);
|
||||
tokio::time::sleep(Duration::from_millis(16)).await;
|
||||
|
||||
let app_bridge = app.world().resource::<libmarathon::networking::GossipBridge>();
|
||||
|
||||
@@ -95,6 +95,17 @@ struct TestHealth {
|
||||
|
||||
use rusqlite::Connection;
|
||||
|
||||
/// Helper to ensure FixedUpdate and FixedPostUpdate run (since they're on a fixed timestep)
|
||||
fn update_with_fixed(app: &mut App) {
|
||||
use bevy::prelude::{FixedUpdate, FixedPostUpdate};
|
||||
// Run Main schedule (which includes Update)
|
||||
app.update();
|
||||
// Explicitly run FixedUpdate to ensure systems there execute
|
||||
app.world_mut().run_schedule(FixedUpdate);
|
||||
// Explicitly run FixedPostUpdate to ensure delta generation executes
|
||||
app.world_mut().run_schedule(FixedPostUpdate);
|
||||
}
|
||||
|
||||
/// Check if an entity exists in the database
|
||||
fn entity_exists_in_db(db_path: &PathBuf, entity_id: Uuid) -> Result<bool> {
|
||||
let conn = Connection::open(db_path)?;
|
||||
@@ -868,8 +879,8 @@ async fn test_lock_heartbeat_expiration() -> Result<()> {
|
||||
|
||||
// Update to allow lock propagation
|
||||
for _ in 0..10 {
|
||||
app1.update();
|
||||
app2.update();
|
||||
update_with_fixed(&mut app1);
|
||||
update_with_fixed(&mut app2);
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
@@ -899,7 +910,7 @@ async fn test_lock_heartbeat_expiration() -> Result<()> {
|
||||
// Run cleanup system (which removes expired locks and broadcasts LockReleased)
|
||||
println!("Running cleanup to expire locks...");
|
||||
for _ in 0..10 {
|
||||
app2.update();
|
||||
update_with_fixed(&mut app2);
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
@@ -1119,7 +1130,7 @@ async fn test_offline_to_online_sync() -> Result<()> {
|
||||
}
|
||||
|
||||
// Update to trigger delta generation (offline)
|
||||
app1.update();
|
||||
update_with_fixed(&mut app1);
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
|
||||
// Verify clock incremented for spawn
|
||||
@@ -1156,7 +1167,7 @@ async fn test_offline_to_online_sync() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
app1.update();
|
||||
update_with_fixed(&mut app1);
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
|
||||
let clock_after_second_spawn = {
|
||||
@@ -1179,7 +1190,7 @@ async fn test_offline_to_online_sync() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
app1.update();
|
||||
update_with_fixed(&mut app1);
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
|
||||
let clock_after_modify = {
|
||||
@@ -1197,7 +1208,7 @@ async fn test_offline_to_online_sync() -> Result<()> {
|
||||
commands.entity(entity_b_bevy).insert(ToDelete);
|
||||
}
|
||||
|
||||
app1.update();
|
||||
update_with_fixed(&mut app1);
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
|
||||
let clock_after_delete = {
|
||||
@@ -1262,8 +1273,8 @@ async fn test_offline_to_online_sync() -> Result<()> {
|
||||
|
||||
// Wait a bit more for tombstone to sync
|
||||
for _ in 0..20 {
|
||||
app1.update();
|
||||
app2.update();
|
||||
update_with_fixed(&mut app1);
|
||||
update_with_fixed(&mut app2);
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,17 @@ pub fn create_test_app_maybe_offline(node_id: Uuid, db_path: PathBuf, bridge: Op
|
||||
app
|
||||
}
|
||||
|
||||
/// Helper to ensure FixedUpdate and FixedPostUpdate run (since they're on a fixed timestep)
|
||||
fn update_with_fixed(app: &mut App) {
|
||||
use bevy::prelude::{FixedUpdate, FixedPostUpdate};
|
||||
// Run Main schedule (which includes Update)
|
||||
app.update();
|
||||
// Explicitly run FixedUpdate to ensure systems there execute
|
||||
app.world_mut().run_schedule(FixedUpdate);
|
||||
// Explicitly run FixedPostUpdate to ensure delta generation executes
|
||||
app.world_mut().run_schedule(FixedPostUpdate);
|
||||
}
|
||||
|
||||
/// Wait for sync condition to be met, polling both apps
|
||||
pub async fn wait_for_sync<F>(
|
||||
app1: &mut App,
|
||||
@@ -102,8 +113,8 @@ where
|
||||
|
||||
while start.elapsed() < timeout {
|
||||
// Tick both apps
|
||||
app1.update();
|
||||
app2.update();
|
||||
update_with_fixed(app1);
|
||||
update_with_fixed(app2);
|
||||
tick_count += 1;
|
||||
|
||||
if tick_count % 50 == 0 {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition.workspace = true
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
anyhow.workspace = true
|
||||
clap.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
|
||||
Reference in New Issue
Block a user