Version: 0.13.0 | 17 manager classes in
manager/package
The manager layer contains all business logic for HyperFactions, organized by domain.
Managers are initialized in HyperFactions.java with explicit dependency injection. HyperFactions.java acts as an orchestrator, delegating lifecycle responsibilities to the lifecycle/ package:
lifecycle/CallbackWiring- Wires all manager callbacks: GUI updates, events, announcements, explosion/harvest/hammer protection hooks, economy, world map, map player filter, overclaim notificationlifecycle/PeriodicTaskManager- Periodic task management (power regen, combat tag decay, invite cleanup)lifecycle/MembershipHistoryHandler- Member join/leave history tracking
Each manager:
- Owns a specific domain of functionality
- Performs permission checks before operations
- Returns typed result enums (not boolean/exception)
- Uses async storage via
CompletableFuture
graph TD
FM[FactionManager] --> FS[(FactionStorage)]
PM[PowerManager] --> PS[(PlayerStorage)]
PM --> FM
CM[ClaimManager] --> FM
CM --> PM
RM[RelationManager] --> FM
ZM[ZoneManager] --> ZS[(ZoneStorage)]
ZM --> CM
TM[TeleportManager] --> FM
ChM[ChatManager] --> FM
ChM --> RM
EM[EconomyManager] --> FM
AM[AnnouncementManager]
SSM[SpawnSuppressionManager] --> ZM
SSM --> CM
SSM --> FM
ZMCM[ZoneMobClearManager] --> ZM
KDC[FactionKDCache] --> FM
CTM[CombatTagManager]
IM[InviteManager]
JRM[JoinRequestManager]
CoM[ConfirmationManager]
CHM[ChatHistoryManager]
style FM fill:#2563eb,color:#fff
style CM fill:#2563eb,color:#fff
style PM fill:#7c3aed,color:#fff
style RM fill:#059669,color:#fff
style ZM fill:#d97706,color:#fff
style AM fill:#0891b2,color:#fff
style SSM fill:#0891b2,color:#fff
| Manager | Responsibility | Dependencies |
|---|---|---|
| FactionManager | Faction CRUD, membership, roles | FactionStorage |
| ClaimManager | Territory claiming/unclaiming | FactionManager, PowerManager (+ ZoneManager injected post-construction) |
| PowerManager | Player power, regen, penalties | PlayerStorage, FactionManager |
| RelationManager | Diplomatic relations | FactionManager |
| ZoneManager | SafeZone/WarZone management | ZoneStorage, ClaimManager |
| CombatTagManager | Combat tagging, spawn protection | None |
| TeleportManager | Faction home teleportation | FactionManager |
| InviteManager | Faction invites with expiration | dataDir (Path) |
| JoinRequestManager | Join requests for closed factions | dataDir (Path) |
| ChatManager | Faction/ally chat channels | FactionManager, RelationManager, playerLookup |
| ConfirmationManager | Text-mode confirmations | None |
| EconomyManager | Faction economy (treasury, transactions) | FactionManager, VaultEconomyProvider, JsonEconomyStorage |
| AnnouncementManager | Server-wide event broadcasts | onlinePlayersSupplier |
| SpawnSuppressionManager | Mob spawn control in claims/zones | ZoneManager, ClaimManager, FactionManager |
| ChatHistoryManager | Faction chat history persistence | ChatHistoryStorage |
| ZoneMobClearManager | Periodic mob clearing in zones | ZoneManager, HyperFactions |
| FactionKDCache | Leaderboard K/D statistics cache | FactionManager, PlayerStorage |
Order matters due to dependencies:
// 1. Core managers (order matters for dependency injection)
factionManager = new FactionManager(factionStorage);
powerManager = new PowerManager(playerStorage, factionManager);
claimManager = new ClaimManager(factionManager, powerManager);
relationManager = new RelationManager(factionManager);
combatTagManager = new CombatTagManager();
zoneManager = new ZoneManager(zoneStorage, claimManager);
claimManager.setZoneManager(zoneManager); // Post-construction injection
// 2. Spawn/mob managers (depend on multiple managers)
spawnSuppressionManager = new SpawnSuppressionManager(zoneManager, claimManager, factionManager);
zoneMobClearManager = new ZoneMobClearManager(zoneManager, hyperFactions);
teleportManager = new TeleportManager(factionManager);
// 3. Standalone persistence managers
inviteManager = new InviteManager(dataPath);
joinRequestManager = new JoinRequestManager(dataPath);
confirmationManager = new ConfirmationManager();
// 4. Data loading phase
factionManager.loadAll().join();
powerManager.loadAll().join();
zoneManager.loadAll().join();
claimManager.buildIndex();
// 5. Economy (conditional — requires config enabled + VaultUnlocked)
economyManager = new EconomyManager(factionManager, vaultEconomyProvider, economyStorage);
// 6. Leaderboard K/D cache
factionKDCache = new FactionKDCache(factionManager, playerStorage);
// 7. Announcement manager (deferred online players supplier)
announcementManager = new AnnouncementManager(onlinePlayersSupplier);
// 8. Chat managers (deferred player lookup)
chatManager = new ChatManager(factionManager, relationManager, playerLookup);
chatHistoryManager = new ChatHistoryManager(chatHistoryStorage);
chatManager.setChatHistoryManager(chatHistoryManager);Core faction lifecycle and membership management.
- Create/disband factions
- Add/remove/kick members
- Promote/demote members
- Transfer leadership
- Update faction properties (name, description, tag, color)
- Track member last online timestamps
- Manage faction permissions (territory flags)
| Method | Permission | Returns |
|---|---|---|
createFaction(name, leaderUuid, leaderName) |
faction.create |
FactionResult |
disbandFaction(factionId, actorUuid) |
faction.disband |
FactionResult |
addMember(factionId, playerUuid, playerName) |
- | FactionResult |
removeMember(factionId, playerUuid, actorUuid, isKick) |
- | FactionResult |
promoteMember(factionId, playerUuid, actorUuid) |
member.promote |
FactionResult |
demoteMember(factionId, playerUuid, actorUuid) |
member.demote |
FactionResult |
transferLeadership(factionId, newLeader, actorUuid) |
member.transfer |
FactionResult |
setHome(factionId, home, actorUuid) |
teleport.sethome |
FactionResult |
public enum FactionResult {
SUCCESS,
ALREADY_IN_FACTION,
NOT_IN_FACTION,
FACTION_NOT_FOUND,
NAME_TAKEN,
NAME_TOO_SHORT,
NAME_TOO_LONG,
FACTION_FULL,
NOT_LEADER,
NOT_OFFICER,
CANNOT_KICK_LEADER,
CANNOT_DEMOTE_MEMBER,
CANNOT_PROMOTE_LEADER,
TARGET_NOT_IN_FACTION,
NO_PERMISSION
}// Get faction by ID (returns @Nullable Faction)
Faction faction = factionManager.getFaction(factionId);
// Get player's faction
UUID factionId = factionManager.getPlayerFactionId(playerUuid); // @Nullable UUID
Faction faction = factionManager.getPlayerFaction(playerUuid); // @Nullable Faction
// Check same faction
boolean same = factionManager.areInSameFaction(player1, player2);
// Get all factions
Collection<Faction> all = factionManager.getAllFactions();Territory claiming and chunk ownership tracking.
- Claim/unclaim chunks for factions
- Overclaim (take territory from weaker factions)
- Track chunk ownership via spatial index
- Validate claim constraints (adjacency, world whitelist/blacklist)
- Calculate claim limits based on faction power
| Method | Permission | Returns |
|---|---|---|
claim(playerUuid, world, chunkX, chunkZ) |
territory.claim |
ClaimResult |
unclaim(playerUuid, world, chunkX, chunkZ) |
territory.unclaim |
ClaimResult |
overclaim(playerUuid, world, chunkX, chunkZ) |
territory.overclaim |
ClaimResult |
getClaimOwner(world, chunkX, chunkZ) |
- | UUID (factionId, nullable) |
getTotalClaimCount() |
- | int |
countFactionClaimsInWorld(factionId, world) |
- | int |
getFactionClaims(factionId) |
- | Set<ChunkKey> |
public enum ClaimResult {
SUCCESS,
NO_PERMISSION,
NOT_IN_FACTION,
NOT_OFFICER,
ALREADY_CLAIMED_SELF,
ALREADY_CLAIMED_OTHER,
ALREADY_CLAIMED_ALLY,
ALREADY_CLAIMED_ENEMY,
NOT_ADJACENT,
MAX_CLAIMS_REACHED,
WORLD_MAX_CLAIMS_REACHED,
INSUFFICIENT_POWER,
WORLD_NOT_ALLOWED,
CHUNK_NOT_CLAIMED,
CANNOT_UNCLAIM_HOME,
NOT_YOUR_CLAIM,
OVERCLAIM_NOT_ALLOWED,
TARGET_HAS_POWER,
ORBISGUARD_PROTECTED,
ZONE_PROTECTED,
WOULD_DISCONNECT
}WORLD_MAX_CLAIMS_REACHED is returned when the faction has hit the per-world claim cap configured in worlds.json (the maxClaims setting). This is checked in both the claim() and overclaim() flows using the countFactionClaimsInWorld() helper, which counts existing claims for a faction in a specific world.
Claim and unclaim operations have a 500ms per-player debounce to prevent double-execution from rapid command dispatch or key-down/key-up events.
Claims are indexed by ChunkKey (world + chunk coordinates) for O(1) lookups:
private final Map<ChunkKey, UUID> claimIndex = new ConcurrentHashMap<>();
public UUID getClaimOwner(String world, int chunkX, int chunkZ) {
return claimIndex.get(new ChunkKey(world, chunkX, chunkZ));
}Player power mechanics that limit territory claiming.
- Track individual player power levels
- Apply death penalties
- Apply combat logout penalties
- Regenerate power over time
- Calculate faction total power
- Load/save player power data
| Method | Purpose | Returns |
|---|---|---|
getPlayerPower(playerUuid) |
Get full power record | PlayerPower (record with power, maxPower, etc.) |
applyDeathPenalty(playerUuid) |
Reduce power on death | double (amount lost) |
applyCombatLogoutPenalty(playerUuid, penalty) |
Reduce power on combat log | double (amount lost) |
tickPowerRegen() |
Called periodically to regenerate power | void |
getFactionPower(factionId) |
Sum of all member power | double |
getFactionPowerStats(factionId) |
Detailed power stats | FactionPowerStats |
playerOnline(playerUuid) |
Mark player as online | void |
playerOffline(playerUuid) |
Mark player as offline | void |
Faction Max Claims = min(
floor(totalFactionPower / powerPerClaim),
configMaxClaims
)
Where:
totalFactionPower= sum of all online member power (+ offline if configured)powerPerClaim= config value (default: 2.0)configMaxClaims= hard cap from config (default: 100)
Diplomatic relations between factions.
- Track ally/enemy/neutral relations
- Handle ally requests (two-way handshake)
- Enforce relation limits (max allies, max enemies)
- Provide relation lookups for protection/chat
| Method | Permission | Returns |
|---|---|---|
requestAlly(playerUuid, targetFactionId) |
relation.ally |
RelationResult |
acceptAlly(playerUuid, requesterFactionId) |
relation.ally |
RelationResult |
setEnemy(playerUuid, targetFactionId) |
relation.enemy |
RelationResult |
setNeutral(playerUuid, targetFactionId) |
relation.neutral |
RelationResult |
getRelation(factionId1, factionId2) |
- | RelationType |
getEffectiveRelation(factionId1, factionId2) |
- | RelationType |
getPlayerRelation(player1, player2) |
- | RelationType |
public enum RelationType {
OWN, // Same faction
ALLY, // Allied factions
NEUTRAL, // Default state
ENEMY // Declared enemies
}getRelation() returns the one-way declared relation (A→B). getEffectiveRelation() checks both directions and returns the effective relation:
- ENEMY wins if either side declares enemy
- ALLY only if both sides are allies
- Otherwise NEUTRAL
Used by map player filtering and /f info to show the true relationship state.
Ally relationships require mutual agreement:
- Faction A requests ally with Faction B
- Request is stored as pending
- Faction B accepts the request
- Both factions marked as allies
Admin-controlled SafeZones and WarZones.
- Create/delete zones
- Claim/unclaim chunks for zones
- Manage zone flags (PvP, build, interact, etc.)
- Priority over faction claims (zones override)
- Persist zone data
| Method | Purpose |
|---|---|
createZone(name, type, creatorUuid) |
Create new zone |
removeZone(zoneId) |
Remove zone and release chunks |
claimChunk(zoneId, world, chunkX, chunkZ) |
Add chunk to zone |
unclaimChunk(zoneId, world, chunkX, chunkZ) |
Remove chunk from zone |
getZone(world, chunkX, chunkZ) |
Get zone at location |
isInSafeZone(world, chunkX, chunkZ) |
Check if SafeZone |
isInWarZone(world, chunkX, chunkZ) |
Check if WarZone |
setZoneFlag(zoneId, flagName, value) |
Set zone flag |
changeZoneType(zoneId, resetFlags) |
Toggle zone type |
Defined in data/ZoneFlags.java — 52 boolean flags across 10 categories, plus 1 string setting (map_visibility).
Many flags require OrbisGuard-Mixins to function (marked with mixin).
| Category | Flags | Count |
|---|---|---|
| Combat | pvp_enabled, friendly_fire, friendly_fire_faction, friendly_fire_ally, projectile_damage, mob_damage, pve_damage |
7 |
| Damage | fall_damage, environmental_damage, explosion_damage mixin, fire_spread mixin |
4 |
| Death | keep_inventory mixin, power_loss |
2 |
| Building | build_allowed, block_place mixin, hammer_use mixin, builder_tools_use mixin |
4 |
| Interaction | block_interact, door_use, container_use, bench_use, processing_use, seat_use, mount_use mixin, light_use, npc_use, npc_tame mixin, npc_interact, crate_pickup mixin, crate_place mixin |
13 |
| Transport | teleporter_use mixin, portal_use mixin, mount_entry |
3 |
| Items | item_drop, item_pickup, item_pickup_manual mixin, invincible_items mixin |
4 |
| Spawning | mob_spawning, hostile_mob_spawning, passive_mob_spawning, neutral_mob_spawning, npc_spawning mixin |
5 |
| Mob Clearing | mob_clear, hostile_mob_clear, passive_mob_clear, neutral_mob_clear |
4 |
| Integration | gravestone_access, show_on_map, essentials_homes, essentials_warps, essentials_kits, essentials_back |
6 |
Flags support parent-child hierarchies (e.g., pvp_enabled > friendly_fire > friendly_fire_faction/friendly_fire_ally). Child flags only take effect when their parent is enabled.
Key SafeZone defaults: PvP off, building off, all damage off, keep inventory on, mob spawning off, hostile mob clearing on, doors/seats/block interaction/NPC shops allowed.
Key WarZone defaults: Full PvP on, building off (anti-grief), all damage on, no keep inventory, full mob spawning on, no mob clearing.
Combat tagging, spawn protection, and damage type tracking.
- Tag players when they enter combat
- Track tag expiration
- Detect combat logout
- Manage spawn protection (respawn invincibility)
- Call logout penalty callback
- Track last damage type per player (for configurable power loss by death cause)
Categorizes damage source for power loss configuration:
| Value | Description |
|---|---|
PVP |
Killed by another player (direct or projectile) |
MOB |
Killed by a mob (direct or mob projectile) |
ENVIRONMENTAL |
Fall damage, drowning, suffocation, or other non-entity damage |
UNKNOWN |
Death cause could not be determined |
| Method | Purpose |
|---|---|
tagCombat(attacker, defender) |
Tag both players in combat |
tagPlayer(playerUuid) |
Tag a single player |
isTagged(playerUuid) |
Check if combat tagged |
getRemainingSeconds(playerUuid) |
Get tag time left |
handleDisconnect(playerUuid) |
Process disconnect (returns wasTagged) |
applySpawnProtection(playerUuid, durationSeconds, world, chunkX, chunkZ) |
Grant respawn protection |
hasSpawnProtection(playerUuid) |
Check if spawn protected |
clearSpawnProtection(playerUuid) |
Remove spawn protection |
tickDecay() |
Called every second to expire tags |
recordDamageType(playerUuid, type) |
Record last damage cause type |
getLastDamageType(playerUuid) |
Get last damage type (or UNKNOWN) |
clearDamageType(playerUuid) |
Clear recorded damage type |
getLastAttacker(defenderUuid) |
Get and consume last attacker UUID |
Player A attacks Player B
│
▼
tagPlayers(A, B)
│
├─► Both tagged for tagDurationSeconds
│
▼
If player disconnects while tagged:
│
▼
handleDisconnect() returns true
│
▼
onCombatLogout callback invoked
│
▼
Power penalty applied via PowerManager
Faction home teleportation with warmup/cooldown.
- Track pending teleports
- Manage warmup timers
- Enforce cooldowns
- Cancel on movement or damage (if configured)
- Execute teleport via platform callback
| Method | Permission | Returns |
|---|---|---|
teleportToHome(playerUuid, startLocation, doTeleport, sendMessage, isTagged) |
teleport.home |
TeleportResult |
scheduleTeleport(playerUuid, startLocation, destination, warmupSeconds, isTagged) |
- | void |
checkMovement(playerUuid, currentX, currentY, currentZ, sendMessage) |
- | boolean |
cancelOnDamage(playerUuid, sendMessage) |
- | boolean |
removePending(playerUuid) |
- | void |
isOnCooldown(playerUuid) |
- | boolean |
getCooldownRemaining(playerUuid) |
- | int (seconds) |
public enum TeleportResult {
SUCCESS_INSTANT, // Teleport completed immediately (no warmup)
SUCCESS_WARMUP, // Warmup scheduled, teleport pending
NO_PERMISSION,
NO_HOME,
NOT_IN_FACTION,
ON_COOLDOWN,
COMBAT_TAGGED,
CANCELLED_MOVED,
CANCELLED_DAMAGE,
CANCELLED_MANUAL,
WORLD_NOT_FOUND
}Faction invites with expiration.
- Create invites with expiration timestamps
- Track pending invites per player
- Validate and accept invites
- Clean up expired invites
- Persist invites to survive restarts
| Method | Permission | Returns |
|---|---|---|
createInviteChecked(factionId, playerUuid, invitedBy) |
member.invite |
CreateInviteResult |
createInvite(factionId, playerUuid, invitedBy) |
- | PendingInvite |
getPlayerInvites(playerUuid) |
- | Set<PendingInvite> |
cleanupExpired() |
- | Called periodically |
manager/JoinRequestManager.java
Join requests for closed factions.
- Create join requests with optional messages
- Track requests per faction
- Allow officers to accept/deny
- Clean up expired requests
- Persist requests to survive restarts
| Method | Permission | Returns |
|---|---|---|
createRequestChecked(factionId, playerUuid, playerName, message) |
member.join |
CreateRequestResult |
acceptRequest(factionId, playerUuid) |
- | JoinRequest (nullable) |
getFactionRequests(factionId) |
- | List<JoinRequest> |
Faction and ally chat channels.
- Track player chat channel state
- Route messages to faction/ally channels
- Format chat messages with faction info
- Provide relation colors for public chat
| Method | Permission | Returns |
|---|---|---|
toggleFactionChatChecked(playerUuid) |
chat.faction |
ToggleResult |
toggleAllyChatChecked(playerUuid) |
chat.ally |
ToggleResult |
cycleChannelChecked(playerUuid) |
chat.faction/chat.ally |
ToggleResult |
processChatMessage(sender, message) |
- | boolean (was handled) |
resetChannel(playerUuid) |
- | void |
public enum ChatChannel {
NORMAL, // Normal server chat
FACTION, // Only faction members see
ALLY // Faction + allied factions see
}manager/ConfirmationManager.java
Text-mode command confirmations for destructive actions.
- Store pending confirmations with expiration
- Validate confirmation codes
- Execute confirmed actions
// In command: check or create confirmation
ConfirmationResult result = confirmationManager.checkOrCreate(
playerUuid, ConfirmationType.DISBAND, null
);
switch (result) {
case NEEDS_CONFIRMATION -> sendMessage("Run command again to confirm");
case CONFIRMED -> doDisband(); // Second invocation confirms
case EXPIRED_RECREATED -> sendMessage("Previous expired, run again");
case DIFFERENT_ACTION -> sendMessage("Replaced previous action, run again");
}public enum ConfirmationType {
DISBAND, LEAVE, TRANSFER, RESTORE_BACKUP
}
public enum ConfirmationResult {
NEEDS_CONFIRMATION, CONFIRMED, DIFFERENT_ACTION, EXPIRED_RECREATED
}Faction treasury management implementing the EconomyAPI interface.
- Manage faction balance (deposit, withdraw, transfer)
- Record transaction history with limit enforcement
- Currency formatting and naming (standard and compact)
- Treasury limits (per-transaction, per-period caps for withdrawals/transfers)
- Fee calculation for deposits, withdrawals, and transfers
- Admin balance adjustment and reset
- VaultUnlocked integration for player wallet transactions
| Method | Returns | Description |
|---|---|---|
getFactionBalance(factionId) |
BigDecimal |
Get treasury balance |
hasFunds(factionId, amount) |
boolean |
Check sufficient funds |
deposit(factionId, amount, actorId, desc) |
CompletableFuture<TransactionResult> |
Deposit into treasury |
withdraw(factionId, amount, actorId, desc) |
CompletableFuture<TransactionResult> |
Withdraw from treasury |
transfer(fromId, toId, amount, actorId, desc) |
CompletableFuture<TransactionResult> |
Inter-faction transfer |
adminAdjust(factionId, amount, adminId, desc) |
CompletableFuture<TransactionResult> |
Admin balance adjustment |
setBalance(factionId, newBalance, adminId) |
CompletableFuture<TransactionResult> |
Admin set balance |
getTransactionHistory(factionId, limit) |
List<Transaction> |
Recent transactions |
formatCurrency(amount) |
String |
Formatted display string |
See API Reference for the full EconomyAPI interface.
manager/AnnouncementManager.java
Server-wide broadcasts for significant faction events.
Constructor: AnnouncementManager(Supplier<Collection<PlayerRef>> onlinePlayersSupplier)
- Broadcast per-player i18n-resolved messages to all online players
- Check per-event toggle configuration via
AnnouncementConfig - Use configured colors from announcement config (not hardcoded prefix)
| Method | Event | Default Color |
|---|---|---|
announceFactionCreated(factionName, leaderName) |
Faction founded | config factionCreatedColor |
announceFactionDisbanded(factionName) |
Faction disbanded | config factionDisbandedColor |
announceLeadershipTransfer(factionName, oldLeader, newLeader) |
Leadership change | config leadershipTransferColor |
announceOverclaim(attackerFaction, defenderFaction) |
Territory overclaimed | config overclaimColor |
announceWarDeclared(declaringFaction, targetFaction) |
War declared | config warDeclaredColor |
announceAllianceFormed(faction1, faction2) |
Alliance formed | config allianceFormedColor |
announceAllianceBroken(faction1, faction2) |
Alliance broken | config allianceBrokenColor |
See Announcements for configuration and admin exclusion details.
manager/SpawnSuppressionManager.java
Controls mob spawning in faction territory and zones using Hytale's native spawn suppression API.
Constructor: SpawnSuppressionManager(ZoneManager, ClaimManager, FactionManager)
- Resolve NPC group indices (hostile, passive, neutral)
- Apply spawn suppression per world based on zone flags and faction territory permissions
- Update suppression when zones or claims change
- Generate unique suppressor IDs via XOR of prefix + zone/faction ID
- Handle both zone-based and claim-based suppression independently
| Method | Purpose |
|---|---|
initialize() |
Resolve NPC group indices |
applyToWorld(world) |
Apply suppression for a specific world (zones + claims) |
applyToAllWorlds(universe) |
Apply suppression across all worlds |
updateZoneSuppression(zone) |
Update for a specific zone change |
Controlled by faction territory permissions and zone flags:
| Flag | Controls |
|---|---|
MOB_SPAWNING |
All mob spawning (parent) |
HOSTILE_MOB_SPAWNING |
Hostile mob spawning |
PASSIVE_MOB_SPAWNING |
Passive mob spawning |
NEUTRAL_MOB_SPAWNING |
Neutral mob spawning |
Uses prefixed UUIDs: HFAC for zones, HFCL for claims. Y-range: -64 to 320.
Caches aggregated faction K/D statistics for the leaderboard.
Constructor: FactionKDCache(FactionManager, PlayerStorage)
- Periodically refresh faction K/D stats in the background (configurable interval)
- Sum member kills/deaths per faction for leaderboard display
- Provide cached stats via
getFactionKD(factionId)to avoid per-request computation - Daemon thread scheduler for non-blocking background refresh
| Method | Purpose |
|---|---|
start(intervalSeconds) |
Start periodic cache refresh |
shutdown() |
Shut down background scheduler |
getFactionKD(factionId) |
Get cached K/D stats (returns zeros if not yet cached) |
public record FactionKDStats(int totalKills, int totalDeaths, double kdr) {}