MultiplayerFighter
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.0 KiB

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

# 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

func _physics_process(delta):
    if not is_multiplayer_authority():
        return
    # Only the authority processes input/logic

RPC for State Changes

@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