Version: 0.13.0 | Config version: 8 | 11 config files
Architecture documentation for the HyperFactions configuration system.
HyperFactions uses a modular JSON-based configuration system with:
- ConfigManager - Central coordinator for all config files
- FactionsConfig - Faction gameplay settings in
config/factions.json - ServerConfig - Server behavior settings in
config/server.json(includesconfigVersion) - Module Configs - 8 feature-specific configs in
config/subdirectory - Validation - Automatic validation with warnings and auto-correction
- Migration - Automatic config migration (v1→v2→v3→v4→v5→v6→v7→v8) with backup/rollback
Note:
CoreConfigandconfig.jsonare deprecated. The V5→V6 migration splitsconfig.jsonintoconfig/factions.jsonandconfig/server.json, then deletesconfig.json. New installs create only the split files. If migration fails, the plugin falls back to loading from the legacyconfig.json.
ConfigManager (singleton)
│
├─► FactionsConfig (config/factions.json)
│ │
│ └─► Roles, Faction, Power, Claims, Combat, Relations, Invites, Stuck
│
├─► ServerConfig (config/server.json, configVersion: 8)
│ │
│ └─► Teleport, AutoSave, Messages, GUI, Permissions, Language, MobClearing, Updates
│
└─► Module Configs (config/*.json)
│
├─► BackupConfig (backup.json)
├─► ChatConfig (chat.json)
├─► DebugConfig (debug.json)
├─► EconomyConfig (economy.json)
├─► FactionPermissionsConfig (faction-permissions.json)
├─► AnnouncementConfig (announcements.json)
├─► GravestoneConfig (gravestones.json)
├─► WorldMapConfig (worldmap.json)
└─► WorldsConfig (worlds.json) — per-world behavior overrides
Deprecated:
CoreConfig(config.json) is no longer used. It has been replaced byFactionsConfigandServerConfig. Existing installs are automatically migrated (V5→V6).
<server>/mods/com.hyperfactions_HyperFactions/
├── config/ # All configuration files
│ ├── factions.json # Faction gameplay (roles, faction, power, claims, combat, relations, invites, stuck)
│ ├── server.json # Server behavior (teleport, autoSave, messages, gui, permissions, language, mobClearing, updates, configVersion)
│ ├── backup.json
│ ├── chat.json
│ ├── debug.json
│ ├── economy.json
│ ├── faction-permissions.json
│ ├── announcements.json # Event broadcast toggles + territory notifications
│ ├── gravestones.json # Gravestone integration settings
│ ├── worldmap.json # World map refresh modes
│ └── worlds.json # Per-world settings
├── data/ # All data files (see storage.md)
│ ├── factions/ # Per-faction data
│ ├── players/ # Per-player power data
│ ├── chat/ # Per-faction chat history
│ ├── economy/ # Per-faction treasury data
│ ├── zones.json # Zone definitions
│ ├── invites.json # Pending invites
│ ├── join_requests.json # Pending join requests
│ └── .version # Data layout version marker
└── backups/ # Backup archives (see storage.md)
Note:
config.jsonno longer exists. It is deleted after the V5→V6 migration. New installs never create it.
Configuration is automatically migrated on startup. See Data Import & Migration for the full migration chain (v1→v2→v3→v4→v5→v6→v7→v8).
The V5→V6 migration restructures the configuration layout:
- Backs up
config.jsonbefore making changes - Splits
config.jsoninto two new files:config/factions.json— faction gameplay settings (roles, faction, power, claims, combat, relations, invites, stuck)config/server.json— server behavior settings (teleport, autoSave, messages, gui, permissions, updates, configVersion)
- Moves
territoryNotificationsEnabledtoconfig/announcements.json - Replaces
allowExplosionsInClaimswith 3 granular explosion flags inconfig/factions.jsonunderclaims - Adds 9 new claim protection settings in
config/factions.jsonunderclaims - Sets
configVersionto 6 inconfig/server.json - Deletes
config.jsonafter successful migration
Legacy fallback: If migration fails, the plugin can still load from the old config.json.
The V6→V7 migration adds economy upkeep fields and removes deprecated world map settings:
- Adds upkeep configuration fields to
config/economy.json(upkeep interval, rates, and thresholds) - Removes deprecated
worldMapsection fromconfig/server.json(world map settings moved toconfig/worldmap.jsonin earlier versions) - Sets
configVersionto 7 inconfig/server.json
The V7→V8 migration adds fields required by the runtime config editor and localization system:
- Adds localization configuration fields to
config/server.json(default locale, player language detection settings) - Adds any missing config keys required by the admin GUI config editor
- Sets
configVersionto 8 inconfig/server.json
| Class | Path | Purpose |
|---|---|---|
| ConfigManager | config/ConfigManager.java |
Singleton coordinator |
| ConfigFile | config/ConfigFile.java |
Base class for config files |
| FactionsConfig | config/modules/FactionsConfig.java |
Faction gameplay (config/factions.json) |
| ServerConfig | config/modules/ServerConfig.java |
Server behavior (config/server.json) |
| CoreConfig | config/CoreConfig.java |
Deprecated — legacy config.json fallback |
| ModuleConfig | config/ModuleConfig.java |
Base for module configs |
| ValidationResult | config/ValidationResult.java |
Validation tracking |
Singleton that orchestrates all configuration:
public class ConfigManager {
private static ConfigManager instance;
private Path dataDir;
private FactionsConfig factionsConfig;
private ServerConfig serverConfig;
private BackupConfig backupConfig;
private ChatConfig chatConfig;
private DebugConfig debugConfig;
private EconomyConfig economyConfig;
private FactionPermissionsConfig factionPermissionsConfig;
public static ConfigManager get() {
if (instance == null) {
instance = new ConfigManager();
}
return instance;
}
public void loadAll(Path dataDir) {
this.dataDir = dataDir;
// 1. Run pending migrations (includes V5→V6 config split)
runMigrations();
// 2. Load faction gameplay config
Path configDir = dataDir.resolve("config");
factionsConfig = new FactionsConfig(configDir.resolve("factions.json"));
factionsConfig.load();
// 3. Load server behavior config
serverConfig = new ServerConfig(configDir.resolve("server.json"));
serverConfig.load();
// 4. Load module configs
backupConfig = new BackupConfig(configDir.resolve("backup.json"));
chatConfig = new ChatConfig(configDir.resolve("chat.json"));
// ... etc
// 5. Validate all configs
validateAll();
}
public void reloadAll() { ... }
public void saveAll() { ... }
}// Access config values
ConfigManager config = ConfigManager.get();
int maxMembers = config.getMaxMembers();
boolean pvpEnabled = config.isFactionDamage();
// Access specific config objects
FactionsConfig factions = config.factions();
ServerConfig server = config.server();
BackupConfig backup = config.backup();1. ConfigManager.loadAll(dataDir)
│
▼
2. Run pending migrations (MigrationRunner)
- Includes V5→V6: split config.json → config/factions.json + config/server.json
│
▼
3. Load FactionsConfig
- Read config/factions.json
- Apply defaults for missing keys
- Save with new keys added
│
▼
4. Load ServerConfig
- Read config/server.json
- Apply defaults for missing keys
- Save with new keys added
│
▼
5. Load Module Configs (same process)
│
▼
6. Validate all configs
- Check value ranges
- Log warnings for invalid values
- Auto-correct where possible
Base class providing common functionality:
public abstract class ConfigFile {
protected final Path path;
protected JsonObject data;
protected ValidationResult lastValidationResult;
public void load() {
if (Files.exists(path)) {
// Load existing config
data = parseJson(path);
} else {
// Create with defaults
data = new JsonObject();
}
// Apply defaults for any missing keys
applyDefaults();
// Save (adds missing keys to file)
save();
}
protected abstract void applyDefaults();
public void validateAndLog() {
lastValidationResult = validate();
for (String warning : lastValidationResult.getWarnings()) {
Logger.warn("[Config] %s: %s", path.getFileName(), warning);
}
}
protected abstract ValidationResult validate();
}config/modules/FactionsConfig.java — config/factions.json
Custom display names and abbreviations for faction roles. These are presentation-only — the internal role names (Leader, Officer, Member) stored in data files are unchanged.
| Key | Type | Default | Description |
|---|---|---|---|
roles.leader.displayName |
string | "Leader" |
Display name for the Leader role |
roles.leader.shortName |
string | "LD" |
Short abbreviation (max 4 chars) |
roles.officer.displayName |
string | "Officer" |
Display name for the Officer role |
roles.officer.shortName |
string | "OF" |
Short abbreviation (max 4 chars) |
roles.member.displayName |
string | "Member" |
Display name for the Member role |
roles.member.shortName |
string | "MB" |
Short abbreviation (max 4 chars) |
Example — Mafia theme:
{
"roles": {
"leader": { "displayName": "Boss", "shortName": "BO" },
"officer": { "displayName": "Underboss", "shortName": "UB" },
"member": { "displayName": "Soldier", "shortName": "SO" }
}
}All GUIs, commands, log messages, and the factions_role_display / factions_role_short placeholders use these configured names. Changes take effect immediately after /f admin reload.
Basic faction settings:
| Key | Type | Default | Description |
|---|---|---|---|
faction.maxMembers |
int | 50 | Maximum members per faction |
faction.maxMembershipHistory |
int | 10 | Maximum membership history entries tracked per faction |
faction.maxNameLength |
int | 24 | Maximum faction name length (validated: 1-64) |
faction.minNameLength |
int | 3 | Minimum faction name length (validated: 1-maxNameLength) |
faction.allowColors |
bool | true | Allow color codes in faction names |
Power mechanics:
| Key | Type | Default | Description |
|---|---|---|---|
power.maxPlayerPower |
double | 20.0 | Maximum power per player |
power.startingPower |
double | 10.0 | Initial power for new players (validated: 0-maxPlayerPower) |
power.powerPerClaim |
double | 2.0 | Power cost per claim |
power.deathPenalty |
double | 1.0 | Power lost on death |
power.killReward |
double | 0.0 | Power gained on killing another player |
power.killRewardRequiresFaction |
bool | true | Only gain power from killing factioned players |
power.powerLossOnMobDeath |
bool | true | Apply death penalty for mob kills |
power.powerLossOnEnvironmentalDeath |
bool | true | Apply death penalty for fall/drowning/suffocation |
power.regenPerMinute |
double | 0.1 | Power regeneration rate |
power.regenWhenOffline |
bool | false | Regen while offline |
power.hardcoreMode |
bool | false | Shared faction power pool — deaths/kills affect the faction total directly, no per-death cap or floor |
Territory settings:
| Key | Type | Default | Description |
|---|---|---|---|
claims.maxClaims |
int | 100 | Global hard limit per faction (can be overridden per-world via worlds.json) |
claims.onlyAdjacent |
bool | false | Require adjacent claims |
claims.preventDisconnect |
bool | false | Prevent unclaiming if it would disconnect remaining claims |
claims.decayEnabled |
bool | true | Enable claim decay |
claims.decayDaysInactive |
int | 30 | Days before decay starts |
claims.decayClaimsPerCycle |
int | 5 | Number of claims removed per decay cycle |
claims.worldWhitelist |
array | [] | Only these worlds allow claiming |
claims.worldBlacklist |
array | [] | These worlds block claiming |
claims.outsiderPickupAllowed |
bool | true | Allow outsiders to pick up items in claimed territory |
claims.outsiderDropAllowed |
bool | true | Allow outsiders to drop items in claimed territory |
claims.factionlessExplosionsAllowed |
bool | false | Allow explosions in claims when the source has no faction |
claims.enemyExplosionsAllowed |
bool | false | Allow explosions in claims caused by enemy faction members |
claims.neutralExplosionsAllowed |
bool | false | Allow explosions in claims caused by neutral faction members |
claims.fireSpreadAllowed |
bool | true | Allow fire to spread within claimed territory |
claims.factionlessDamageAllowed |
bool | true | Allow factionless players to deal damage to entities in claims |
claims.enemyDamageAllowed |
bool | true | Allow enemy faction members to deal damage to entities in claims |
claims.neutralDamageAllowed |
bool | true | Allow neutral faction members to deal damage to entities in claims |
Migration note: The old
allowExplosionsInClaimsboolean has been replaced by three granular explosion flags:factionlessExplosionsAllowed,enemyExplosionsAllowed, andneutralExplosionsAllowed. The oldallowCreatureExplosions,allowBlockExplosions, andallowOtherExplosionsflags are no longer present — explosions are now controlled by faction relationship.
Combat settings:
| Key | Type | Default | Description |
|---|---|---|---|
combat.tagDurationSeconds |
int | 15 | Combat tag duration |
combat.allyDamage |
bool | false | Allow ally damage |
combat.factionDamage |
bool | false | Allow faction damage |
combat.taggedLogoutPenalty |
bool | true | Punish combat logout |
combat.logoutPowerLoss |
double | 1.0 | Power lost when logging out while combat tagged |
combat.neutralAttackPenalty |
double | 0.0 | Power penalty for attacking a neutral player |
combat.spawnProtection.enabled |
bool | true | Enable spawn protection |
combat.spawnProtection.durationSeconds |
int | 5 | Protection duration |
combat.spawnProtection.breakOnAttack |
bool | true | Cancel spawn protection when the player attacks |
combat.spawnProtection.breakOnMove |
bool | true | Cancel spawn protection when the player moves |
Relation settings:
| Key | Type | Default | Description |
|---|---|---|---|
relations.maxAllies |
int | 10 | Maximum allied factions (-1 = unlimited) |
relations.maxEnemies |
int | -1 | Maximum enemy factions (-1 = unlimited) |
Invite and join request settings:
| Key | Type | Default | Description |
|---|---|---|---|
invites.inviteExpirationMinutes |
int | 5 | Minutes before a faction invite expires |
invites.joinRequestExpirationHours |
int | 24 | Hours before a join request expires |
The /f stuck command teleports players out of enemy territory when trapped. The command searches for a safe location in expanding rings.
| Key | Type | Default | Description |
|---|---|---|---|
stuck.minRadius |
int | 3 | Minimum search radius (chunks) |
stuck.radiusIncrease |
int | 3 | Radius increase per failed attempt (chunks) |
stuck.maxAttempts |
int | 10 | Maximum search attempts before giving up |
stuck.warmupSeconds |
int | 30 | Warmup delay before teleport (cancelled by movement/damage) |
stuck.cooldownSeconds |
int | 300 | Cooldown between /f stuck uses |
Default factions.json:
{
"roles": {
"leader": { "displayName": "Leader", "shortName": "LD" },
"officer": { "displayName": "Officer", "shortName": "OF" },
"member": { "displayName": "Member", "shortName": "MB" }
},
"faction": {
"maxMembers": 50,
"maxMembershipHistory": 10,
"maxNameLength": 24,
"minNameLength": 3,
"allowColors": true
},
"power": {
"maxPlayerPower": 20.0,
"startingPower": 10.0,
"powerPerClaim": 2.0,
"deathPenalty": 1.0,
"killReward": 0.0,
"killRewardRequiresFaction": true,
"powerLossOnMobDeath": true,
"powerLossOnEnvironmentalDeath": true,
"regenPerMinute": 0.1,
"regenWhenOffline": false,
"hardcoreMode": false
},
"claims": {
"maxClaims": 100,
"onlyAdjacent": false,
"preventDisconnect": false,
"decayEnabled": true,
"decayDaysInactive": 30,
"decayClaimsPerCycle": 5,
"worldWhitelist": [],
"worldBlacklist": [],
"outsiderPickupAllowed": true,
"outsiderDropAllowed": true,
"factionlessExplosionsAllowed": false,
"enemyExplosionsAllowed": false,
"neutralExplosionsAllowed": false,
"fireSpreadAllowed": true,
"factionlessDamageAllowed": true,
"enemyDamageAllowed": true,
"neutralDamageAllowed": true
},
"combat": {
"tagDurationSeconds": 15,
"allyDamage": false,
"factionDamage": false,
"taggedLogoutPenalty": true,
"logoutPowerLoss": 1.0,
"neutralAttackPenalty": 0.0,
"spawnProtection": {
"enabled": true,
"durationSeconds": 5,
"breakOnAttack": true,
"breakOnMove": true
}
},
"relations": {
"maxAllies": 10,
"maxEnemies": -1
},
"invites": {
"inviteExpirationMinutes": 5,
"joinRequestExpirationHours": 24
},
"stuck": {
"minRadius": 3,
"radiusIncrease": 3,
"maxAttempts": 10,
"warmupSeconds": 30,
"cooldownSeconds": 300
}
}config/modules/ServerConfig.java — config/server.json
Teleportation settings:
| Key | Type | Default | Description |
|---|---|---|---|
teleport.warmupSeconds |
int | 5 | Warmup before teleport |
teleport.cooldownSeconds |
int | 300 | Cooldown between teleports |
teleport.cancelOnMove |
bool | true | Cancel on movement |
teleport.cancelOnDamage |
bool | true | Cancel on damage |
Auto-save settings:
| Key | Type | Default | Description |
|---|---|---|---|
autoSave.enabled |
bool | true | Enable periodic auto-save |
autoSave.intervalMinutes |
int | 5 | Auto-save interval in minutes |
Message formatting settings:
| Key | Type | Default | Description |
|---|---|---|---|
messages.prefix.text |
string | "HyperFactions" |
The text displayed inside the chat prefix brackets |
messages.prefix.color |
string | "#55FFFF" |
Color of the prefix text |
messages.prefix.bracketColor |
string | "#AAAAAA" |
Color of the bracket characters [ and ] |
messages.primaryColor |
string | "#00FFFF" |
Primary accent color used throughout messages |
GUI behavior settings:
| Key | Type | Default | Description |
|---|---|---|---|
gui.title |
string | "HyperFactions" |
Title displayed in the main GUI window |
gui.terrainMapEnabled |
bool | true | Enable terrain map rendering in the territory GUI |
gui.leaderboardKdRefreshSeconds |
int | 300 | Background cache refresh interval for aggregated faction K/D ratios on the leaderboard page |
Permission behavior:
| Key | Type | Default | Description |
|---|---|---|---|
permissions.adminRequiresOp |
bool | true | Admin commands require OP |
permissions.allowWithoutPermissionMod |
bool | false | Allow all commands when no permission plugin is installed (if false, non-admin commands are denied without a permission plugin) |
Localization / i18n settings:
| Key | Type | Default | Description |
|---|---|---|---|
language.default |
string | "en-US" |
Default server language code |
language.usePlayerLanguage |
bool | true | Respect each player's client language for translations |
Mob clearing settings (removes hostile mobs near claims periodically):
| Key | Type | Default | Description |
|---|---|---|---|
mobClearing.enabled |
bool | true | Enable periodic mob clearing |
mobClearing.intervalSeconds |
int | 10 | Sweep interval in seconds |
Update checking and HyperProtect-Mixin management:
| Key | Type | Default | Description |
|---|---|---|---|
updates.enabled |
bool | true | Enable update checking on startup |
updates.url |
string | GitHub Releases API | API endpoint for HyperFactions version checking |
updates.releaseChannel |
string | "stable" |
Release channel: "stable" or "prerelease" |
updates.hyperProtect.autoDownload |
bool | false | Auto-download HP-Mixin to earlyplugins/ if not installed |
updates.hyperProtect.autoUpdate |
bool | true | Check for HP-Mixin updates on startup, notify admins |
updates.hyperProtect.url |
string | GitHub Releases API | API endpoint for HP-Mixin version checking |
When autoDownload is disabled and HP-Mixin is not installed, the server logs install instructions. Use /f admin update mixin for manual install/update regardless of config. Use /f admin update toggle-mixin-download to toggle auto-download at runtime (persisted).
Default server.json:
{
"configVersion": 8,
"teleport": {
"warmupSeconds": 5,
"cooldownSeconds": 300,
"cancelOnMove": true,
"cancelOnDamage": true
},
"autoSave": {
"enabled": true,
"intervalMinutes": 5
},
"messages": {
"prefix": {
"text": "HyperFactions",
"color": "#55FFFF",
"bracketColor": "#AAAAAA"
},
"primaryColor": "#00FFFF"
},
"gui": {
"title": "HyperFactions",
"terrainMapEnabled": true,
"leaderboardKdRefreshSeconds": 300
},
"permissions": {
"adminRequiresOp": true,
"allowWithoutPermissionMod": false
},
"language": {
"default": "en-US",
"usePlayerLanguage": true
},
"mobClearing": {
"enabled": true,
"intervalSeconds": 10
},
"updates": {
"enabled": true,
"url": "https://api.github.com/repos/HyperSystems-Development/HyperFactions/releases/latest",
"releaseChannel": "stable",
"hyperProtect": {
"autoDownload": false,
"autoUpdate": true,
"url": "https://api.github.com/repos/HyperSystems-Development/HyperProtect-Mixin/releases/latest"
}
}
}config/modules/BackupConfig.java
GFS (Grandfather-Father-Son) backup system:
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true | Enable automatic backups |
hourlyRetention |
int | 24 | Hourly backups to keep |
dailyRetention |
int | 7 | Daily backups to keep |
weeklyRetention |
int | 4 | Weekly backups to keep |
manualRetention |
int | 10 | Manual backups to keep (0 = keep all) |
onShutdown |
bool | true | Backup on server shutdown |
shutdownRetention |
int | 5 | Shutdown backups to keep (0 = keep all) |
config/modules/ChatConfig.java
Chat formatting:
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true | Enable chat formatting |
format |
string | "{faction_tag}{prefix}{player}{suffix}: {message}" |
Chat format template |
tagDisplay |
string | "tag" |
Tag display mode: "tag", "name", or "none" |
tagFormat |
string | "[{tag}] " |
Tag format template |
noFactionTag |
string | "" |
Tag shown for players without a faction (empty = no tag) |
noFactionTagColor |
string | "#555555" |
Color for the no-faction tag (dark gray) |
playerNameColor |
string | "#FFFFFF" |
Color for {player} in public chat |
priority |
string | "LATE" |
Event priority (EARLIEST, EARLY, NORMAL, LATE, LATEST) |
relationColors.own |
string | "#00FF00" |
Own faction color (green) |
relationColors.ally |
string | "#FF69B4" |
Ally color (pink) |
relationColors.neutral |
string | "#AAAAAA" |
Neutral color (gray) |
relationColors.enemy |
string | "#FF0000" |
Enemy color (red) |
factionChat.factionChatColor |
string | "#00FFFF" |
Faction channel message color (cyan) |
factionChat.factionChatPrefix |
string | "[Faction]" |
Prefix shown on faction chat messages |
factionChat.allyChatColor |
string | "#AA00AA" |
Ally channel message color (purple) |
factionChat.allyChatPrefix |
string | "[Ally]" |
Prefix shown on ally chat messages |
factionChat.senderNameColor |
string | "#FFFF55" |
Sender name color in faction/ally chat (yellow) |
factionChat.messageColor |
string | "#FFFFFF" |
Message text color in faction/ally chat (white) |
factionChat.historyEnabled |
bool | true | Enable faction chat history persistence |
factionChat.historyMaxMessages |
int | 200 | Maximum stored messages per faction (validated: 10-1000) |
factionChat.historyRetentionDays |
int | 7 | Days to retain chat history |
factionChat.historyCleanupIntervalMinutes |
int | 60 | Interval for automatic history cleanup (validated: min 5) |
config/modules/EconomyConfig.java
Faction treasury, currency display, fees, and upkeep:
| Key | Type | Default | Description |
|---|---|---|---|
currency.name |
string | "dollar" |
Singular currency name |
currency.namePlural |
string | "dollars" |
Plural currency name |
currency.symbol |
string | "$" |
Currency symbol |
currency.symbolPosition |
string | "left" |
Symbol placement: "left" ($100.00) or "right" (100.00$) |
treasury.startingBalance |
decimal | 0 | Starting balance for new factions |
treasury.disbandRefundToLeader |
bool | true | Refund balance to leader on disband |
treasury.limits.maxWithdrawAmount |
decimal | 0 | Per-transaction withdraw limit (0 = unlimited) |
treasury.limits.maxWithdrawPerPeriod |
decimal | 0 | Cumulative withdraw limit per period (0 = unlimited) |
treasury.limits.maxTransferAmount |
decimal | 0 | Per-transaction transfer limit (0 = unlimited) |
treasury.limits.maxTransferPerPeriod |
decimal | 0 | Cumulative transfer limit per period (0 = unlimited) |
treasury.limits.periodHours |
int | 24 | Rolling window for cumulative limits |
fees.depositPercent |
decimal | 0 | Deposit fee percentage (0-100) |
fees.withdrawPercent |
decimal | 0 | Withdrawal fee percentage (0-100) |
fees.transferPercent |
decimal | 0 | Transfer fee percentage (0-100) |
upkeep.enabled |
bool | true | Enable territory upkeep costs |
upkeep.costPerChunk |
decimal | 2.0 | Cost per chunk per cycle (flat mode) |
upkeep.intervalHours |
int | 24 | Collection interval |
upkeep.gracePeriodHours |
int | 48 | Grace period before claim forfeiture |
upkeep.autoPayDefault |
bool | true | Default auto-pay for new factions |
upkeep.freeChunks |
int | 3 | Chunks exempt from upkeep |
upkeep.claimLossPerCycle |
int | 1 | Claims lost per failed cycle after grace |
upkeep.warningHours |
int | 6 | Hours before collection to warn members |
upkeep.maxCostCap |
decimal | 0 | Max cost per cycle (0 = unlimited) |
upkeep.scalingMode |
string | "flat" |
"flat" or "progressive" tiered pricing |
upkeep.scalingTiers |
array | see below | Progressive tier definitions |
Scaling tiers (when scalingMode is "progressive"):
[
{ "chunkCount": 10, "costPerChunk": "2.00" },
{ "chunkCount": 15, "costPerChunk": "3.00" },
{ "chunkCount": 0, "costPerChunk": "5.00" }
]A chunkCount of 0 means "all remaining chunks".
config/modules/DebugConfig.java
Debug logging:
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false | Enable the debug module |
enabledByDefault |
bool | false | Enable all categories by default |
logToConsole |
bool | true | Output debug messages to console |
categories.power |
bool | false | Power system debug |
categories.claim |
bool | false | Claim system debug |
categories.combat |
bool | false | Combat system debug |
categories.protection |
bool | false | Protection debug |
categories.relation |
bool | false | Relation debug |
categories.territory |
bool | false | Territory debug |
categories.worldmap |
bool | false | World map debug |
categories.interaction |
bool | false | Block/entity interaction debug |
categories.mixin |
bool | false | HyperProtect-Mixin debug |
categories.spawning |
bool | false | Mob spawning debug |
categories.integration |
bool | false | Third-party integration debug |
categories.economy |
bool | false | Economy/treasury debug |
sentry.enabled |
bool | true | Enable Sentry error tracking |
sentry.dsn |
string | (built-in) | Sentry DSN (Data Source Name) URL |
sentry.environment |
string | "production" |
Environment name sent to Sentry (e.g. "production", "development") |
sentry.debug |
bool | false | Enable Sentry debug logging |
sentry.tracesSampleRate |
double | 0.0 | Performance trace sample rate (0.0 = none, 1.0 = all) |
Default debug.json:
{
"enabled": false,
"enabledByDefault": false,
"logToConsole": true,
"categories": {
"power": false,
"claim": false,
"combat": false,
"protection": false,
"relation": false,
"territory": false,
"worldmap": false,
"interaction": false,
"mixin": false,
"spawning": false,
"integration": false,
"economy": false
},
"sentry": {
"enabled": true,
"dsn": "https://...",
"environment": "production",
"debug": false,
"tracesSampleRate": 0.0
}
}Note: If a legacy
config/sentry.jsonfile exists, its settings are automatically migrated into thesentrysection ofdebug.jsonand the old file is deleted.
config/modules/AnnouncementConfig.java
Server-wide faction event broadcast toggles, per-event colors, and territory notification customization:
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true | Enable the announcement module |
events.factionCreated |
bool | true | Announce when a faction is created |
events.factionDisbanded |
bool | true | Announce when a faction is disbanded |
events.leadershipTransfer |
bool | true | Announce leadership transfers |
events.overclaim |
bool | true | Announce territory overclaims |
events.warDeclared |
bool | true | Announce war declarations |
events.allianceFormed |
bool | true | Announce new alliances |
events.allianceBroken |
bool | true | Announce broken alliances |
colors.factionCreated |
string | "#55FF55" |
Color for faction creation announcements |
colors.factionDisbanded |
string | "#FF5555" |
Color for faction disband announcements |
colors.leadershipTransfer |
string | "#FFAA00" |
Color for leadership transfer announcements |
colors.overclaim |
string | "#FF5555" |
Color for overclaim announcements |
colors.warDeclared |
string | "#FF5555" |
Color for war declaration announcements |
colors.allianceFormed |
string | "#55FF55" |
Color for alliance formed announcements |
colors.allianceBroken |
string | "#FFAA00" |
Color for alliance broken announcements |
territoryNotifications.enabled |
bool | true | Enable territory enter/leave title notifications |
territoryNotifications.wilderness.onLeaveZone.enabled |
bool | true | Show wilderness notification when leaving a zone (safe/war) |
territoryNotifications.wilderness.onLeaveZone.upper |
string | "" |
Upper title text when leaving a zone to wilderness |
territoryNotifications.wilderness.onLeaveZone.lower |
string | "Wilderness" |
Lower subtitle text when leaving a zone to wilderness |
territoryNotifications.wilderness.onLeaveClaim.enabled |
bool | true | Show wilderness notification when leaving a faction claim |
territoryNotifications.wilderness.onLeaveClaim.upper |
string | "" |
Upper title text when leaving a claim to wilderness |
territoryNotifications.wilderness.onLeaveClaim.lower |
string | "Wilderness" |
Lower subtitle text when leaving a claim to wilderness |
Default announcements.json:
{
"enabled": true,
"events": {
"factionCreated": true,
"factionDisbanded": true,
"leadershipTransfer": true,
"overclaim": true,
"warDeclared": true,
"allianceFormed": true,
"allianceBroken": true
},
"colors": {
"factionCreated": "#55FF55",
"factionDisbanded": "#FF5555",
"leadershipTransfer": "#FFAA00",
"overclaim": "#FF5555",
"warDeclared": "#FF5555",
"allianceFormed": "#55FF55",
"allianceBroken": "#FFAA00"
},
"territoryNotifications": {
"enabled": true,
"wilderness": {
"onLeaveZone": {
"enabled": true,
"upper": "",
"lower": "Wilderness"
},
"onLeaveClaim": {
"enabled": true,
"upper": "",
"lower": "Wilderness"
}
}
}
}config/modules/GravestoneConfig.java
Faction-aware gravestone access rules per zone type. Controls how the GravestonePlugin integration interacts with faction territory protection.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true | Enable gravestone integration |
protectInOwnTerritory |
bool | true | Protect gravestones in own faction territory |
factionMembersCanAccess |
bool | true | Allow faction members to access each other's gravestones |
alliesCanAccess |
bool | false | Allow allied faction members to access gravestones |
protectInSafeZone |
bool | true | Protect gravestones in safe zones |
protectInWarZone |
bool | false | Protect gravestones in war zones |
protectInWilderness |
bool | false | Protect gravestones in wilderness |
announceDeathLocation |
bool | true | Announce death location to faction members |
protectInEnemyTerritory |
bool | false | Protect gravestones in enemy territory |
protectInNeutralTerritory |
bool | true | Protect gravestones in neutral territory |
enemiesCanLootInOwnTerritory |
bool | false | Allow enemies to loot gravestones in your territory |
allowLootDuringRaid |
bool | true | Allow gravestone looting during raids (placeholder — not enforced until raid system is implemented) |
allowLootDuringWar |
bool | true | Allow gravestone looting during wars (placeholder — not enforced until war system is implemented) |
Default gravestones.json:
{
"enabled": true,
"protectInOwnTerritory": true,
"factionMembersCanAccess": true,
"alliesCanAccess": false,
"protectInSafeZone": true,
"protectInWarZone": false,
"protectInWilderness": false,
"announceDeathLocation": true,
"protectInEnemyTerritory": false,
"protectInNeutralTerritory": true,
"enemiesCanLootInOwnTerritory": false,
"allowLootDuringRaid": true,
"allowLootDuringWar": true
}config/modules/WorldMapConfig.java
World map integration controls how claim overlays are rendered on the in-game world map, with multiple refresh modes to balance performance vs. responsiveness.
Refresh modes:
| Mode | Description |
|---|---|
proximity |
Only refresh for players within range of claim changes. Most performant. |
incremental |
Refresh specific chunks for all players. Good balance of performance and consistency. (default) |
debounced |
Full map refresh after a quiet period with no changes. Use if incremental causes issues. |
immediate |
Full map refresh on every claim change. Original behavior, not recommended for busy servers. |
manual |
No automatic refresh. Use /f admin map refresh to update manually. |
Top-level settings:
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true | Enable world map integration |
refreshMode |
string | "incremental" |
Refresh mode (see table above) |
autoFallbackOnError |
bool | true | Auto-fall back to debounced mode if reflection errors occur |
showFactionTags |
bool | true | Show faction tag text on claimed chunks |
factionWideRefreshThreshold |
int | 200 | If a faction has more claims than this, use full refresh instead of queuing each chunk |
respectWorldConfig |
bool | true | Inherit map settings from world config; disabled worlds are skipped |
betterMapCompat |
string | "auto" |
BetterMap compatibility mode: "auto" (detect), "always" (force on), "never" (force off) |
Proximity mode settings (proximity.*):
| Key | Type | Default | Description |
|---|---|---|---|
proximity.chunkRadius |
int | 32 | Chunk radius for proximity refresh (validated: 1-128) |
proximity.batchIntervalTicks |
int | 30 | Ticks between batch processing (30 ticks = 1s at 30 TPS) |
proximity.maxChunksPerBatch |
int | 50 | Maximum chunks per batch (validated: 1-500) |
Incremental mode settings (incremental.*):
| Key | Type | Default | Description |
|---|---|---|---|
incremental.batchIntervalTicks |
int | 30 | Ticks between batch processing (30 ticks = 1s at 30 TPS) |
incremental.maxChunksPerBatch |
int | 50 | Maximum chunks per batch (validated: 1-500) |
Debounced mode settings (debounced.*):
| Key | Type | Default | Description |
|---|---|---|---|
debounced.delaySeconds |
int | 5 | Quiet period before triggering refresh (validated: 1-60) |
Player visibility filtering (playerVisibility.*):
Controls which players are visible on the world map and compass based on faction relations.
| Key | Type | Default | Description |
|---|---|---|---|
playerVisibility.enabled |
bool | true | Enable player visibility filtering (disabled = all players visible, vanilla behavior) |
playerVisibility.showOwnFaction |
bool | true | Show own faction members on the map |
playerVisibility.showAllies |
bool | true | Show allied faction members on the map |
playerVisibility.showNeutrals |
bool | false | Show neutral faction members on the map |
playerVisibility.showEnemies |
bool | false | Show enemy faction members on the map |
playerVisibility.showFactionlessPlayers |
bool | false | Show factionless players to faction members |
playerVisibility.showFactionlessToFactionless |
bool | true | Show factionless players to other factionless players |
Settings overrides (settingsOverrides.*):
Override map settings inherited from the world config. Remove a key or set to null to inherit from the world.
| Key | Type | Default | Description |
|---|---|---|---|
settingsOverrides.defaultScale |
float | null | Override default map zoom scale |
settingsOverrides.minScale |
float | null | Override minimum map zoom |
settingsOverrides.maxScale |
float | null | Override maximum map zoom |
settingsOverrides.imageScale |
float | null | Override map image scale |
settingsOverrides.allowTeleportToCoordinates |
bool | null | Override teleport-to-coordinates permission |
settingsOverrides.allowTeleportToMarkers |
bool | null | Override teleport-to-markers permission |
settingsOverrides.allowCreatingMapMarkers |
bool | null | Override map marker creation permission |
config/modules/FactionPermissionsConfig.java
Territory permission defaults and locks. Uses a nested JSON format grouped by role level for readability. Also supports the legacy flat format (e.g. outsiderBreak) for backward compatibility.
Two-section design:
- defaults - Default values for new factions AND the forced value when locked
- locks - Whether each flag is locked (factions cannot change it)
When a flag is locked, its effective value is always the defaults value.
Per-level flags (applied for each level: outsider, ally, member, officer):
| Suffix | Description | Outsider Default | Ally Default | Member Default | Officer Default |
|---|---|---|---|---|---|
break |
Block breaking | false | false | true | true |
place |
Block placement | false | false | true | true |
interact |
General interaction (parent of door/container/bench/processing/seat/transport) | false | true | true | true |
doorUse |
Door and gate use | false | true | true | true |
containerUse |
Container (chest, barrel) access | false | false | true | true |
benchUse |
Crafting bench use | false | false | true | true |
processingUse |
Processing station use (furnace, etc.) | false | false | true | true |
seatUse |
Seat use | false | true | true | true |
transportUse |
Transport use (vehicles, teleporters) | false | true | true | true |
crateUse |
Crate access | false | false | true | true |
npcTame |
NPC taming | false | false | true | true |
pveDamage |
PvE damage (attacking mobs) | false | true | true | true |
Mob spawning flags:
| Key | Default | Description |
|---|---|---|
mobSpawning.enabled |
true | Master toggle for mob spawning in territory |
mobSpawning.hostile |
true | Allow hostile mob spawning |
mobSpawning.passive |
true | Allow passive mob spawning |
mobSpawning.neutral |
true | Allow neutral mob spawning |
Treasury flags:
| Key | Default | Description |
|---|---|---|
treasury.deposit |
true | Whether members can deposit into the faction treasury |
treasury.withdraw |
false | Whether officers can withdraw from the faction treasury |
treasury.transfer |
false | Whether officers can transfer money to other factions |
Global flags:
| Key | Default | Description |
|---|---|---|
pvpEnabled |
true | Whether PvP is enabled in faction territory |
officersCanEdit |
false | Whether officers can edit faction permissions |
Parent-child relationships:
{level}Interactis the parent of{level}DoorUse,{level}ContainerUse,{level}BenchUse,{level}ProcessingUse,{level}SeatUse,{level}TransportUsemobSpawning.enabledis the parent ofhostile,passive,neutral
When a parent flag is false, all child flags are effectively false regardless of their stored value.
Default faction-permissions.json (nested format):
{
"defaults": {
"outsider": {
"break": false, "place": false, "interact": false,
"doorUse": false, "containerUse": false, "benchUse": false,
"processingUse": false, "seatUse": false, "transportUse": false,
"crateUse": false, "npcTame": false, "pveDamage": false
},
"ally": {
"break": false, "place": false, "interact": true,
"doorUse": true, "containerUse": false, "benchUse": false,
"processingUse": false, "seatUse": true, "transportUse": true,
"crateUse": false, "npcTame": false, "pveDamage": true
},
"member": {
"break": true, "place": true, "interact": true,
"doorUse": true, "containerUse": true, "benchUse": true,
"processingUse": true, "seatUse": true, "transportUse": true,
"crateUse": true, "npcTame": true, "pveDamage": true
},
"officer": {
"break": true, "place": true, "interact": true,
"doorUse": true, "containerUse": true, "benchUse": true,
"processingUse": true, "seatUse": true, "transportUse": true,
"crateUse": true, "npcTame": true, "pveDamage": true
},
"mobSpawning": {
"enabled": true, "hostile": true, "passive": true, "neutral": true
},
"treasury": {
"deposit": true, "withdraw": false, "transfer": false
},
"pvpEnabled": true,
"officersCanEdit": false
},
"locks": {
"outsider": {
"break": false, "place": false, "interact": false,
"doorUse": false, "containerUse": false, "benchUse": false,
"processingUse": false, "seatUse": false, "transportUse": false,
"crateUse": false, "npcTame": false, "pveDamage": false
},
"ally": { "...": "same structure, all false" },
"member": { "...": "same structure, all false" },
"officer": { "...": "same structure, all false" },
"mobSpawning": {
"enabled": false, "hostile": false, "passive": false, "neutral": false
},
"treasury": {
"deposit": false, "withdraw": false, "transfer": false
},
"pvpEnabled": false,
"officersCanEdit": false
}
}config/modules/WorldsConfig.java
Per-world behavior overrides in config/worlds.json:
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true | Enable per-world settings module |
defaultPolicy |
string | "allow" |
Default policy for unconfigured worlds: "allow" or "deny" |
worlds |
object | {} |
Per-world setting overrides (keyed by world name or wildcard pattern) |
Per-world settings (5 per entry, all nullable — null means defer to global config):
| Key | Type | Default | Description |
|---|---|---|---|
claiming |
bool | null | Whether claiming is allowed in this world |
powerLoss |
bool | null | Whether power loss applies in this world |
friendlyFireFaction |
bool | null | Same-faction PvP override |
friendlyFireAlly |
bool | null | Ally PvP override |
maxClaims |
int | null | Maximum claims a faction can hold in this world. null or 0 = use global limit, >0 = per-faction per-world hard cap |
Wildcard support: Use % as a wildcard in world names (e.g., arena_% matches arena_1, arena_pvp). Priority resolution: exact name match > wildcard patterns (fewer wildcards = higher priority) > default policy.
Default rules: A default instance-% wildcard rule blocks claiming in temporary instance worlds.
Default worlds.json:
{
"enabled": true,
"defaultPolicy": "allow",
"worlds": {
"instance-%": { "claiming": false },
"example-world-abc": { "claiming": true, "powerLoss": true, "friendlyFireFaction": false, "friendlyFireAlly": false }
}
}Admin commands: /f admin world list|info|set|reset
public class ValidationResult {
private final List<String> warnings = new ArrayList<>();
private final List<String> errors = new ArrayList<>();
public void addWarning(String message) {
warnings.add(message);
}
public void addError(String message) {
errors.add(message);
}
public boolean hasIssues() {
return !warnings.isEmpty() || !errors.isEmpty();
}
public void merge(ValidationResult other) {
warnings.addAll(other.warnings);
errors.addAll(other.errors);
}
}Example validation in FactionsConfig:
@Override
protected ValidationResult validate() {
ValidationResult result = new ValidationResult();
if (getMaxMembers() < 1) {
result.addWarning("maxMembers must be at least 1, using 1");
data.addProperty("faction.maxMembers", 1);
}
if (getDeathPenalty() < 0) {
result.addWarning("deathPenalty cannot be negative, using 0");
data.addProperty("power.deathPenalty", 0.0);
}
return result;
}migration/MigrationRunner.java
Config migrations run automatically on load:
public static List<MigrationResult> runPendingMigrations(Path dataDir, MigrationType type) {
List<Migration> migrations = getMigrationsForType(type);
List<MigrationResult> results = new ArrayList<>();
for (Migration migration : migrations) {
if (migration.isNeeded(dataDir)) {
MigrationResult result = migration.run(dataDir);
results.add(result);
}
}
return results;
}Migrations handle:
- Renaming config keys
- Moving settings between files
- Adding new required keys
- Converting data formats
When /f admin reload is called:
- All config files are re-read from disk
- Validation runs again
- Managers receive updated values
- Debug logging levels are reapplied
public void reloadAll() {
factionsConfig.reload();
serverConfig.reload();
backupConfig.reload();
chatConfig.reload();
debugConfig.reload();
economyConfig.reload();
factionPermissionsConfig.reload();
validateAll();
}On first run, all config files are created with defaults:
config/directory createdconfig/factions.jsoncreated with faction gameplay defaultsconfig/server.jsoncreated with server behavior defaults (includingconfigVersion: 8)- Module configs created with their defaults
- All files are pretty-printed JSON
ConfigManager config = ConfigManager.get();
// Via convenience methods (most common)
int maxMembers = config.getMaxMembers();
double powerPerClaim = config.getPowerPerClaim();
boolean pvpEnabled = config.isFactionDamage();
// Via config object (for grouped access)
FactionsConfig factions = config.factions();
ServerConfig server = config.server();
BackupConfig backup = config.backup();public class ClaimManager {
public ClaimResult claim(UUID playerUuid, String world, int chunkX, int chunkZ) {
ConfigManager config = ConfigManager.get();
// Check world whitelist/blacklist
if (!config.isWorldAllowed(world)) {
return ClaimResult.WORLD_BLACKLISTED;
}
// Check max claims
int maxClaims = config.calculateMaxClaims(factionPower);
if (currentClaims >= maxClaims) {
return ClaimResult.MAX_CLAIMS_REACHED;
}
// ...
}
}-
Add to appropriate config class (FactionsConfig, ServerConfig, or a module):
public int getNewSetting() { return data.get("newSection").getAsJsonObject() .get("newSetting").getAsInt(); }
-
Add default value:
@Override protected void applyDefaults() { // ... setDefault("newSection.newSetting", 42); }
-
Add validation (if needed):
if (getNewSetting() < 0) { result.addWarning("newSetting cannot be negative"); }
-
Add convenience method to ConfigManager (optional):
public int getNewSetting() { return factionsConfig.getNewSetting(); // or serverConfig for server settings }
| Class | Path |
|---|---|
| ConfigManager | config/ConfigManager.java |
| ConfigFile | config/ConfigFile.java |
| FactionsConfig | config/modules/FactionsConfig.java |
| ServerConfig | config/modules/ServerConfig.java |
| CoreConfig | config/CoreConfig.java (deprecated) |
| ModuleConfig | config/ModuleConfig.java |
| ValidationResult | config/ValidationResult.java |
| BackupConfig | config/modules/BackupConfig.java |
| ChatConfig | config/modules/ChatConfig.java |
| DebugConfig | config/modules/DebugConfig.java |
| EconomyConfig | config/modules/EconomyConfig.java |
| FactionPermissionsConfig | config/modules/FactionPermissionsConfig.java |
| AnnouncementConfig | config/modules/AnnouncementConfig.java |
| GravestoneConfig | config/modules/GravestoneConfig.java |
| WorldMapConfig | config/modules/WorldMapConfig.java |
| WorldsConfig | config/modules/WorldsConfig.java |