mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 21:35:42 +00:00
The Hetzner on-demand compute implementation is complete: - ARM64 builds working in CI - Shardok running on Hetzner CAX41 in Helsinki - Production Eagle connected via TLS + token auth - IPv6/NAT64 networking configured Delete the completed planning doc and add a new doc covering future latency optimization strategies: 1. Client-side animation masking (recommended first step) 2. Split Shardok architecture (future if needed) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
217 lines
6.7 KiB
Markdown
217 lines
6.7 KiB
Markdown
# Shardok Latency Hiding Strategies
|
||
|
||
## Problem Statement
|
||
|
||
With Shardok running on Hetzner (Helsinki) and Eagle on DigitalOcean (US), the round-trip latency for human commands is ~200-400ms:
|
||
|
||
```
|
||
Human posts command:
|
||
Unity → Eagle (DO) → Shardok (Hetzner) → Eagle (DO) → Unity
|
||
~10ms ~100ms ~100ms ~10ms
|
||
Total: ~220ms round-trip
|
||
```
|
||
|
||
This latency is acceptable for AI turns (users watch animations anyway), but creates noticeable lag when humans post commands.
|
||
|
||
---
|
||
|
||
## Strategy 1: Client-Side Animation Masking
|
||
|
||
### Concept
|
||
|
||
Start animations immediately when the user clicks, before server confirmation arrives. The animation duration masks the network latency.
|
||
|
||
### Implementation by Command Type
|
||
|
||
**Movement Commands** (deterministic):
|
||
- Client knows the destination hex and movement path
|
||
- Start movement animation immediately on click
|
||
- Server confirms the move (should always match)
|
||
- If server rejects (invalid state), snap unit back to origin
|
||
|
||
**Attack Commands** (RNG-dependent):
|
||
- Show attack animation immediately (unit swings sword, fires arrow)
|
||
- Wait for server to return dice roll result
|
||
- Show damage numbers / hit effects when server responds
|
||
- Animation typically takes 300-500ms, masking most of the latency
|
||
|
||
**End Turn**:
|
||
- Latency not noticeable (user expects transition delay)
|
||
|
||
### Unity Implementation Sketch
|
||
|
||
```csharp
|
||
// In CommandHandler.cs
|
||
public void OnCommandSelected(Command command) {
|
||
// Start animation immediately
|
||
if (command.Type == CommandType.Move) {
|
||
unitController.StartMoveAnimation(command.TargetHex);
|
||
} else if (command.Type == CommandType.Attack) {
|
||
unitController.StartAttackAnimation(command.TargetUnit);
|
||
}
|
||
|
||
// Send to server in parallel
|
||
connection.SendCommand(command, (response) => {
|
||
if (response.Success) {
|
||
// Animation continues, apply result
|
||
ApplyCommandResult(response);
|
||
} else {
|
||
// Rollback animation
|
||
unitController.CancelAnimation();
|
||
ShowError(response.ErrorMessage);
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
### Pros
|
||
- Simple implementation
|
||
- No server-side changes
|
||
- Works with existing architecture
|
||
|
||
### Cons
|
||
- Doesn't eliminate latency for attacks with RNG (must wait for dice roll)
|
||
- Rollback needed if server rejects command (rare but possible)
|
||
|
||
### Estimated Improvement
|
||
- Movement: ~200ms latency hidden (feels instant)
|
||
- Attacks: ~100-200ms hidden by animation, ~100ms visible wait for dice result
|
||
|
||
---
|
||
|
||
## Strategy 2: Split Shardok Architecture
|
||
|
||
### Concept
|
||
|
||
Run two Shardok instances:
|
||
- **Shardok-Primary (DigitalOcean)**: Handles command processing, source of truth
|
||
- **Shardok-AI (Hetzner)**: AI computation only
|
||
|
||
Human commands go to the nearby Primary for low latency. AI computation uses the powerful Hetzner instance.
|
||
|
||
### Architecture
|
||
|
||
```
|
||
Human commands (low latency ~20ms)
|
||
Unity ←→ Eagle ←→ Shardok-Primary (DigitalOcean)
|
||
↓ state sync (when AI turn starts)
|
||
Shardok-AI (Hetzner)
|
||
↑ AI command response
|
||
```
|
||
|
||
### How It Works
|
||
|
||
**Human Turn:**
|
||
1. Human posts command → Eagle → Shardok-Primary (DO)
|
||
2. Primary processes command immediately (~10ms local)
|
||
3. Primary streams result to client via Eagle (~10ms)
|
||
4. **Total latency: ~20ms** (vs ~220ms current)
|
||
|
||
**AI Turn:**
|
||
1. When AI's turn starts, Primary sends game state snapshot to Hetzner
|
||
2. Shardok-AI computes best command using full CPU power
|
||
3. Shardok-AI returns command index to Primary
|
||
4. Primary executes command locally and streams to client
|
||
5. Repeat until AI turn ends
|
||
|
||
### Protocol Changes
|
||
|
||
```protobuf
|
||
// New service for AI-only computation
|
||
service ShardokAIService {
|
||
// Send game state, receive AI's chosen command
|
||
rpc GetAICommand(AICommandRequest) returns (AICommandResponse);
|
||
}
|
||
|
||
message AICommandRequest {
|
||
bytes game_state = 1; // Serialized game state
|
||
int32 player_id = 2; // Which AI player
|
||
repeated bytes available_commands = 3; // Available command descriptors
|
||
}
|
||
|
||
message AICommandResponse {
|
||
int32 command_index = 1; // Index into available_commands
|
||
int32 search_depth = 2; // For debugging
|
||
double best_score = 3; // For debugging
|
||
}
|
||
```
|
||
|
||
### Shardok-Primary Requirements
|
||
|
||
Shardok-Primary on DigitalOcean needs to:
|
||
- Process all commands (human and AI)
|
||
- Maintain authoritative game state
|
||
- Serialize/deserialize game state for AI requests
|
||
- Run on minimal CPU (command processing is fast)
|
||
|
||
This is essentially the current Shardok, but without running the AI search.
|
||
|
||
### Shardok-AI Requirements
|
||
|
||
Shardok-AI on Hetzner needs to:
|
||
- Receive game state snapshots
|
||
- Run AI evaluation (IterativeDeepeningAI or MCTS)
|
||
- Return best command index
|
||
- No persistent state (stateless worker)
|
||
|
||
### AI Turn Latency
|
||
|
||
Each AI command has ~200ms network latency. This is acceptable because:
|
||
1. User is watching animations anyway
|
||
2. Natural pacing lets user observe AI decisions
|
||
3. AI computation is fast on Hetzner's 16 cores
|
||
|
||
For a typical AI turn with 5 commands: 5 × 200ms = 1 second network overhead, plus AI thinking time. With animations, this feels natural.
|
||
|
||
### Implementation Phases
|
||
|
||
**Phase 1: Add Shardok-Primary (minimal)**
|
||
- Deploy existing Shardok container to DigitalOcean
|
||
- Configure Eagle to use local Shardok for all commands
|
||
- Human latency immediately improves
|
||
|
||
**Phase 2: Add AI offload**
|
||
- Implement `ShardokAIService` RPC
|
||
- Shardok-Primary calls Hetzner for AI commands
|
||
- Shardok-AI processes requests statelessly
|
||
|
||
**Phase 3: Optimize**
|
||
- Batch multiple AI actions if possible
|
||
- Pre-warm Shardok-AI connection
|
||
- Add fallback if Hetzner unavailable
|
||
|
||
### Pros
|
||
- Human command latency drops from ~220ms to ~20ms
|
||
- AI still gets Hetzner's CPU power
|
||
- Clear separation of concerns
|
||
- Shardok-Primary can fall back to local AI if Hetzner unavailable
|
||
|
||
### Cons
|
||
- Two Shardok instances to maintain
|
||
- State serialization overhead for AI requests
|
||
- Each AI action has network round-trip (acceptable with animations)
|
||
|
||
---
|
||
|
||
## Comparison
|
||
|
||
| Approach | Human Latency | AI Throughput | Complexity | Changes Required |
|
||
|----------|---------------|---------------|------------|------------------|
|
||
| Current | ~220ms | High | - | - |
|
||
| Animation masking | ~220ms (perceived ~50ms) | High | Low | Unity only |
|
||
| Split architecture | ~20ms | High | Medium | New RPC, two deployments |
|
||
|
||
---
|
||
|
||
## Recommendation
|
||
|
||
**Phase 1 (now)**: Implement animation masking in Unity client
|
||
- Quick win, no server changes
|
||
- Improves perceived latency significantly for movement
|
||
- Attacks still show dice animation while waiting
|
||
|
||
**Phase 2 (future)**: Split architecture if animation masking insufficient
|
||
- Only needed if users complain about attack latency
|
||
- More complex but provides true low latency
|
||
- Natural evolution of current architecture
|