# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview This is a 3D multiplayer fighting game built in Godot 4.5 using an inheritance-based architecture. The game uses ENet for client-server networking with the Network singleton (autoload) managing all multiplayer connections. ## Key Commands ### Running the Game - Open the project in Godot Editor and press F5 to run - Or use: `godot --path . res://level/scenes/level.tscn` ### Testing Multiplayer Locally 1. Run the main scene (level.tscn) 2. Click "Host" on one instance to create a server (default port 8080) 3. Run another instance and click "Join" to connect as a client ## Architecture ### Inheritance Structure The codebase follows an inheritance-based design pattern: - **BaseUnit** (base class) - Common functionality for all game entities that can take damage/have health - **Character** extends **CharacterBody3D** - Player character class (class_name: Character) - Currently implements movement, jumping, sprinting, respawning, and skin customization - Uses multiplayer authority for input handling (only the owner processes input) ### Networking (Network.gd) - **Type**: Autoload singleton (accessible via `Network` globally) - **Protocol**: ENet (UDP-based) - **Architecture**: Client-Server (one host, multiple clients up to MAX_PLAYERS=10) - **Player Data**: Stored in `Network.players` dictionary keyed by peer_id - Contains: `{"nick": String, "skin": Character.SkinColor}` ### RPC Patterns The project uses Godot 4's RPC system for multiplayer synchronization: - Use `@rpc("any_peer", "reliable")` for critical data (health, damage) - Use `@rpc("any_peer", "unreliable")` for frequent position updates - The server (multiplayer.is_server()) is authoritative for game state - Use `multiplayer.get_remote_sender_id()` to identify RPC sender - Use `rpc_id(peer_id, "method_name")` for targeted RPCs ### Scene Structure - **level/scenes/level.tscn** - Main scene with menu UI, chat, and PlayersContainer - level.gd spawns players dynamically when they connect - **level/scenes/player.tscn** - Player character scene - Instantiated by server when players connect - Named with peer_id for easy lookup ### Physics Layers - Layer 1: "player" - Player collision - Layer 2: "world" - Environment collision ### Input Actions Defined in project.godot: - `move_left`, `move_right`, `move_forward`, `move_backward` (WASD) - `jump` (Space) - `shift` (Left Shift for sprinting) - `quit` (Escape) - `toggle_chat` (Enter) ## Development Guidelines ### Adding New Components 1. Create scripts in `level/scripts/` 2. For multiplayer-synchronized components: - Always check `is_multiplayer_authority()` before processing input - Use RPC for state changes that need to replicate - Server should validate all important state changes ### Adding New Unit Types (Enemies, NPCs) 1. Create a new class that extends BaseUnit 2. Implement required virtual methods 3. Add any unique behavior in _physics_process/_process 4. Ensure multiplayer authority is set correctly ### Testing Multiplayer - Always test with at least 2 instances (1 host, 1 client) - Test edge cases: late joins, disconnections, packet loss - Verify state is synchronized correctly across all clients ## Common Patterns ### Spawning Networked Entities ```gdscript # Server-side only if multiplayer.is_server(): var entity = entity_scene.instantiate() entity.name = str(unique_id) # Name with ID for easy lookup entity.set_multiplayer_authority(owner_peer_id) container.add_child(entity, true) # true = force readable name ``` ### Multiplayer Authority Check ```gdscript func _physics_process(delta): if not is_multiplayer_authority(): return # Only the authority processes input/logic ``` ### RPC for State Changes ```gdscript @rpc("any_peer", "reliable") func take_damage(amount: int, attacker_id: int): if not multiplayer.is_server(): return # Server validates # Apply damage logic rpc("sync_health", current_health) # Broadcast to all ```