mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-19 18:14:44 +00:00
Initial implementation of server-side bots that: - Spawn as real Player entities with full equipment - Move and broadcast position updates (10 tick/sec) - Take damage and die with backpack drops - Respawn after death - Combat system with accuracy model (adjustment vs recoil) Includes project documentation in bot-docs/ and Claude agent helpers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
9.3 KiB
9.3 KiB
PlanetSide Bots - Technical Architecture
PSF-LoginServer Codebase Analysis
Technology Stack
- Language: Scala (99.5%)
- Actor Framework: Akka (classic actors)
- Database: PostgreSQL
- Build: sbt
Key Components Discovered
Entity Hierarchy
PlanetSideServerObject (base)
├── Player
│ ├── Vitality (health, armor)
│ ├── FactionAffinity (TR/NC/VS)
│ ├── Container (inventory)
│ ├── ZoneAware (continent awareness)
│ └── MountableEntity (vehicle seats)
├── Vehicle
├── Deployable
└── FacilityTurret
Actor System
SessionActor (per-connection)
├── Handles network packets from client
├── Manages player state
├── Routes to subsystem handlers
└── Mode-based behavior (Normal, Spectator, CSR)
PlayerControl (per-player entity)
├── Akka Actor controlling Player object
├── Handles damage, healing, death
├── Equipment management
├── Containable behavior
└── Environment interaction
Relevant Files
| File | Purpose |
|---|---|
objects/Player.scala |
Player entity class |
objects/avatar/Avatar.scala |
Persistent player data (certs, loadouts) |
objects/avatar/PlayerControl.scala |
Player behavior Actor |
actors/session/SessionActor.scala |
Network session handler |
objects/SpawnPoint.scala |
Spawn location trait |
objects/serverobject/turret/auto/AutomatedTurretBehavior.scala |
AI reference implementation |
Existing AI Pattern: AutomatedTurretBehavior
The codebase already has AI! AutomatedTurretBehavior is a trait that provides:
Target Management
Targets- list of known potential targetsTarget- current active targetAddTarget()/RemoveTarget()- target list managementDetected()- check if target is already known
Detection & Engagement
Alert(target)- new target spottedUnalert(target)- target lostConfirmShot(target)- hit confirmation- Range-based detection (
ranges.trigger,ranges.escape,ranges.detection) - Periodic validation sweeps (
detectionSweepTime)
Combat Logic
engageNewDetectedTarget()- begin shootingnoLongerEngageDetectedTarget()- stop shootingtrySelectNewTarget()- target selection algorithm- Target decay checks (destroyed, out of range, MIA)
- Retaliation behavior (respond to being attacked)
Timing/Cooldowns
cooldowns.missedShot- timeout for unconfirmed hitscooldowns.targetSelect- delay before selecting new targetcooldowns.targetElimination- delay after killing target- Self-reported refire timer for continuous fire
Key Insight
The turret AI works by being an Akka Actor that receives messages (
Alert,ConfirmShot,PeriodicCheck) and maintains internal state. Bots can follow the same pattern.
Proposed Bot Architecture
Option A: Server-Side Native Bots (Preferred)
BotActor (extends Actor)
├── BotBehavior trait (similar to AutomatedTurretBehavior)
│ ├── Target detection (vision cone, partial/full spot)
│ ├── Combat engagement
│ ├── V-menu communication
│ └── Attitude/Vengeance system
├── BotMovement trait
│ ├── Pathfinding
│ ├── ADAD strafing
│ └── Retreat behavior
├── BotObjective trait
│ ├── Follow orders (attack/defend)
│ ├── Base capture
│ └── Help responses (VNG, VNH)
└── Controls a Player object (no SessionActor needed)
How It Would Work
-
BotManager actor manages bot lifecycle
- Spawns bots when population is low
- Removes bots when real players join
- Assigns bots to factions
-
Bot entity is a
Playerobject- Has
Avatarwith certs, loadouts (predefined by class) - Has
PlayerControlactor for damage/death handling - Has new
BotActorfor AI decision-making
- Has
-
Bot appears to clients as normal player
- Spawns at SpawnPoints
- Sends PlayerStateMessage updates
- Fires weapons, takes damage, dies normally
Integration Points
Zone.LivePlayers- bots appear hereZone.AvatarEvents- bots send/receive eventsPlayerStateMessage- bots broadcast position/orientationChatMsg- bots send V-menu voice commands
Option B: Bot-as-Client (Fallback)
External process connects to server as fake client, mimics player packets.
Pros: No server code changes needed initially Cons: Network overhead, harder to scale, more fragile
Bot Class Implementation
Each bot class needs:
Data Definition
case class BotClass(
name: String,
certifications: Set[Certification],
loadout: Loadout,
experienceLevel: BotExperience, // Newbie, Vet, Ace
primaryRole: BotRole // Driver, Support, Hacker, AV, MAX, Vet, Ace
)
Behavior Weights
trait BotPersonality {
def aggressionLevel: Float // 0.0 = passive, 1.0 = aggressive
def accuracyBase: Float // Base accuracy modifier
def movementStyle: MovementStyle // Newbie (straight), Vet (ADAD), etc.
def retreatThreshold: Float // HP percentage to retreat
}
Communication System
V-Menu Integration
// Bot sends voice command
def sendVoiceCommand(cmd: VoiceCommand): Unit = {
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
AvatarAction.SendResponse(botGUID, ChatMsg(ChatMessageType.CMT_VOICE, cmd.text))
)
}
// Bot responds to nearby voice commands
def handleVoiceCommand(sender: Player, cmd: VoiceCommand): Unit = cmd match {
case VNG if canBeGunner => respondAndAssist(sender)
case VNH if canHack => respondAndAssist(sender)
case VVV => evaluateHelpRequest(sender)
// etc.
}
Celebration Coordination
object CelebrationCoordinator {
def onBaseCapture(zone: Zone, faction: PlanetSideEmpire): Unit = {
val eligibleBots = zone.LivePlayers
.filter(_.isBot)
.filter(_.Faction == faction)
.filter(_.isAlive)
val responderCount = Random.nextInt(5) + 1 // 1-6
val responders = Random.shuffle(eligibleBots).take(responderCount)
responders.zipWithIndex.foreach { case (bot, i) =>
val delay = Random.nextFloat() * 1.5f // 0-1.5 seconds
scheduler.scheduleOnce(delay.seconds) {
bot.sendVoiceCommand(randomCelebration())
}
}
}
}
Spawn/Despawn Logic
Dynamic Population Management
class BotPopulationManager(zone: Zone) {
val targetBotsPerFaction = 100
val minRealPlayersBeforeScaling = 10
def tick(): Unit = {
PlanetSideEmpire.values.foreach { faction =>
val realPlayers = zone.LivePlayers.count(p => !p.isBot && p.Faction == faction)
val currentBots = zone.LivePlayers.count(p => p.isBot && p.Faction == faction)
val targetBots = math.max(0, targetBotsPerFaction - realPlayers)
if (currentBots < targetBots) spawnBots(faction, targetBots - currentBots)
if (currentBots > targetBots) despawnBots(faction, currentBots - targetBots)
}
}
def despawnBots(faction: PlanetSideEmpire, count: Int): Unit = {
// Remove non-Ace bots first, Ace is last to go
val bots = zone.LivePlayers
.filter(p => p.isBot && p.Faction == faction)
.sortBy(b => if (b.botClass == Ace) Int.MaxValue else 0)
.take(count)
bots.foreach(gracefulLogout)
}
}
Proof of Concept Milestones
Milestone 1: Static Bot
- Create
BotActorskeleton - Spawn a
Playerentity withoutSessionActor - Bot appears in zone, visible to clients
- Bot stands still (no AI)
Milestone 2: Moving Bot
- Implement basic movement
- Bot walks in a pattern
PlayerStateMessagebroadcasts correctly
Milestone 3: Reactive Bot
- Detect nearby enemies (vision cone)
- Turn to face target
- Basic shooting (ChangeFireStateMessage)
Milestone 4: Smart Bot
- Target selection logic
- Retreat on low HP
- V-menu help requests
Milestone 5: Team Bot
- Follow orders from Ace
- Respond to V-menu requests
- Coordinated behavior
Questions for PSForever Devs
- Player without SessionActor: Is this currently possible? What breaks?
- Bot flag: Should we add
isBot: BooleantoPlayerclass? - GUID allocation: How do we get GUIDs for bot entities?
- Zone registration: What's the proper way to add a player to a zone without client connection?
- Existing NPC code: Is there any other AI code beyond
AutomatedTurretBehavior?
File Structure (Proposed)
src/main/scala/net/psforever/
├── objects/
│ └── bot/
│ ├── Bot.scala # Bot entity (extends Player?)
│ ├── BotClass.scala # Class definitions (Driver, Support, etc.)
│ ├── BotLoadouts.scala # Predefined loadouts per class
│ └── BotPersonality.scala # Behavior weights
├── actors/
│ └── bot/
│ ├── BotActor.scala # Main bot AI actor
│ ├── BotBehavior.scala # Combat/detection trait
│ ├── BotMovement.scala # Movement trait
│ ├── BotObjective.scala # Objective handling trait
│ └── BotManager.scala # Population management
└── services/
└── bot/
├── BotService.scala # Global bot coordination
└── BotVoiceCoordinator.scala # V-menu coordination