diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7df47652..64216288 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Rollback support with two-click confirmation
- HyperProtect detection via ProtectionMixinBridge (works even without update checker)
+**SimpleClaims Data Importer ([#99](https://github.com/HyperSystems-Development/HyperFactions/issues/99))**
+- Import faction claims from SimpleClaims, converting claim data to HyperFactions territory
+- Command: `/f admin import simpleclaims [path]`
+
+**FactionsX Data Importer ([#98](https://github.com/HyperSystems-Development/HyperFactions/issues/98))**
+- Import faction data from FactionsX, converting factions, claims, and player data
+- Command: `/f admin import factionsx [path]`
+
+**World Map Config & BetterMap Compatibility ([#102](https://github.com/HyperSystems-Development/HyperFactions/issues/102))**
+- Respect per-world WorldMap enable/disable from world config
+- BetterMap integration: compatible with exploration-based map reveal
+- Claims and zones render correctly on BetterMap-managed worlds
+
+**Built-in Localization (i18n) ([#92](https://github.com/HyperSystems-Development/HyperFactions/issues/92))**
+- 10 languages: en-US, de-DE, es-ES, fr-FR, it-IT, nl-NL, pl-PL, pt-BR, ru-RU, tl-PH
+- ~467 translation entries per locale covering all commands, GUI labels, help content
+- Player language detection with configurable default and per-player override
+- Markdown-based help content system with translation guide
+
**Split MessageKeys into Domain-Specific Files**
- Split the monolithic `MessageKeys.java` (2,789 lines, 60 inner classes, ~1,298 constants) into 6 focused domain files:
- `CommonKeys.java` — shared messages, protection denial, territory notifications, announcements, teleport, chat display
@@ -65,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Command-specific permission messages with unique wording (e.g., "to create factions", "to claim territory") kept as-is
### Fixed
+- **Faction claims in water/ocean nearly invisible on world map** — moved claim overlay to render after water/fluid color ([#90](https://github.com/HyperSystems-Development/HyperFactions/issues/90))
- **Water/lava disappears in own faction claim** — fluid spread was incorrectly tied to `fireSpreadAllowed` config, causing all fluid to be removed in claims when fire spread was disabled. Fluid spread in faction claims is now always allowed ([#95](https://github.com/HyperSystems-Development/HyperFactions/issues/95))
## [0.11.1] - 2026-03-11
diff --git a/README.md b/README.md
index bcf4e4e1..e6dcb7a5 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ A comprehensive faction management mod for Hytale servers featuring territory cl
## Overview
-HyperFactions transforms your Hytale server into a dynamic faction-based environment where players create factions, claim territories, forge alliances, manage treasuries, and engage in strategic PvP combat. With 76 interactive GUI pages, 46 commands, and deep integration with the HyperSystems ecosystem, it provides a complete faction experience out of the box.
+HyperFactions transforms your Hytale server into a dynamic faction-based environment where players create factions, claim territories, forge alliances, manage treasuries, and engage in strategic PvP combat. With 70+ interactive GUI pages, 46 commands, and deep integration with the HyperSystems ecosystem, it provides a complete faction experience out of the box.
**Main Commands:** `/faction` | `/f` | `/hf`
@@ -93,21 +93,20 @@ HyperFactions transforms your Hytale server into a dynamic faction-based environ
| Feature | Status |
|---------|--------|
| Block, item, PvP protection | Implemented |
-| [HyperProtect-Mixin](https://www.curseforge.com/hytale/bootstrap/hyperprotect-mixin) (27 hooks, recommended) | Implemented |
+| [HyperProtect-Mixin](https://www.curseforge.com/hytale/bootstrap/hyperprotect-mixin) (28 hooks, recommended) | Implemented |
| OrbisGuard-Mixins (11 hooks, alternative) | Implemented |
| Dual-provider auto-detection | Implemented |
| Mob spawn suppression | Implemented |
| Mob clearing zone flags | Implemented |
| Gravestones integration | Implemented |
-| Zone flags (50) | Implemented |
-| Command blocking in zones | Implemented |
+| Zone flags (51) | Implemented |
| Sentry error tracking | Implemented |
### GUI
| Feature | Status |
|---------|--------|
-| 76 interactive pages across 3 registries | Implemented |
+| 70+ interactive pages across 3 registries | Implemented |
| Faction leaderboard | Implemented |
| Admin dashboard | Implemented |
| Faction browser with search | Implemented |
@@ -120,12 +119,12 @@ HyperFactions transforms your Hytale server into a dynamic faction-based environ
|---------|--------|
| Zone management (SafeZone, WarZone) | Implemented |
| Backup system (GFS rotation) | Implemented |
-| Data import (ElbaphFactions, HyFactions) | Implemented |
-| Config migration (v1-v7) | Implemented |
+| Data import (ElbaphFactions, HyFactions, SimpleClaims, FactionsX) | Implemented |
+| Config migration (v1-v8) | Implemented |
| Update checker | Implemented |
-| Admin GUI: Config editor | [Planned #40](https://github.com/HyperSystems-Development/HyperFactions/issues/40) |
-| Admin GUI: Backup manager | [Planned #41](https://github.com/HyperSystems-Development/HyperFactions/issues/41) |
-| Admin GUI: Updates page | [Planned #42](https://github.com/HyperSystems-Development/HyperFactions/issues/42) |
+| Admin GUI: Config editor | Implemented |
+| Admin GUI: Backup manager | Implemented |
+| Admin GUI: Updates page | Implemented |
### Integrations
@@ -156,7 +155,7 @@ HyperFactions transforms your Hytale server into a dynamic faction-based environ
| Server-managed factions | [Planned #33](https://github.com/HyperSystems-Development/HyperFactions/issues/33) |
| ~~Relational placeholders~~ | [Done in 0.10.0](https://github.com/HyperSystems-Development/HyperFactions/issues/72) |
| NPC integrations | [Considering #21](https://github.com/HyperSystems-Development/HyperFactions/issues/21) |
-| Localization | [Planned #19](https://github.com/HyperSystems-Development/HyperFactions/issues/19) |
+| Localization (10 languages) | Implemented |
| CurseForge updates | [Planned #17](https://github.com/HyperSystems-Development/HyperFactions/issues/17) |
---
diff --git a/build.gradle b/build.gradle
index 98289c94..d1fe7993 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ plugins {
}
group = 'com.hyperfactions'
-version = '0.11.1'
+version = '0.12.0'
// Shared version property avoids accessing project at execution time
def buildVersion = objects.property(String).convention(version)
diff --git a/curseforge-description.html b/curseforge-description.html
index b639e39b..93868f85 100644
--- a/curseforge-description.html
+++ b/curseforge-description.html
@@ -5,26 +5,23 @@
⚔️HyperFactions - The Complete Fact
✨ Why HyperFactions?
- - 🖥️ 76 Interactive GUI Pages - Every feature has a polished, interactive GUI. No command memorization needed.
+ - 🖥️ 70+ Interactive GUI Pages - Every feature has a polished, interactive GUI. No command memorization needed.
- ⚡ Real-Time GUI Updates - When a member joins, a chunk is claimed, or a relation changes, every open GUI refreshes automatically.
- - 🛡️ 50 Zone Flags - The most granular territory protection available, from PvP and granular friendly fire to mob spawning, transport control, and F-key pickup.
- - 📦 Data Import - Migrating from ElbaphFactions or HyFactions? One command imports your factions, claims, and relations.
+ - 🛡️ 51 Zone Flags - The most granular territory protection available, from PvP and granular friendly fire to mob spawning, transport control, and F-key pickup.
+ - 📦 Data Import - Migrating from ElbaphFactions, HyFactions, SimpleClaims, or FactionsX? One command imports your factions, claims, and relations.
- ⚙️ Deep Configurability - 11 modular config files covering every aspect of gameplay. Tune it to your server's style.
- 🚀 Active Development - Regular updates with community-driven features. Open source on GitHub.
-🆕 What's New in v0.11.0
+🆕 What's New in v0.12.0
- - 💰 Faction Upkeep System - Automated territory maintenance costs with flat or progressive tiered pricing, grace periods, and auto-pay
- - 🐾 Mob Clearing Zone Flags - 4 new flags to periodically remove hostile, passive, and neutral mobs from zones
- - 🔍 Sentry Error Tracking - Automatic error reporting with Sentry SDK, admin enable/disable, and source context
- - 🗺️ World Map Player & Marker Hiding - Hide enemy/neutral players and shared markers on the world map per faction relation
- - 💡 Light Use Zone Flag - Control toggling lanterns, campfires, torches, and lamps in zones
- - 🐴 Mount Entry Enforcement - Block mounted players from entering zones, with safe teleport push-back
- - 🔧 KyuubiSoft Core Integration - Citizen NPC zone protection with auto-detection
- - 🛡️ HyperProtect-Mixin v1.2.0 - 7 new hook wrappers for mount, barter, fluid, prefab, projectile, crafting, and map markers
- - 💬 Specific Denial Messages - Action-specific protection denial messages with territory context instead of generic text
- - 🐛 20+ Bug Fixes - Including SafeZone mount bypass, light use blocking, spawn suppression timing, gravestone loot, and backup race conditions
+ - 🌍 Built-in Localization (i18n) - 10 languages out of the box: English, German, Spanish, French, Italian, Dutch, Polish, Portuguese, Russian, Filipino. Player language auto-detection with configurable default.
+ - ⚙️ Admin GUI: Config Editor - Edit all HyperFactions settings in-game with 11 tabs, size-adaptive layouts, boolean toggles, steppers, color pickers, dropdowns, and input validation
+ - 💾 Admin GUI: Backup Manager - Paginated backup list with create, restore, and delete operations, plus type filtering
+ - 🔄 Admin GUI: Updates Page - Check for HyperFactions and HyperProtect-Mixin updates, download, and rollback — all from the GUI
+ - 📦 SimpleClaims & FactionsX Importers - Two new data importers: migrate from SimpleClaims (SQLite or JSON) or FactionsX with full claim, member, and relation import
+ - 🗺️ BetterMap Compatibility - Per-world WorldMap enable/disable config, claims and zones render correctly on BetterMap-managed worlds
+ - 🌊 Ocean Claim Visibility Fix - Faction claims in water/ocean are now clearly visible on the world map
🏰 Core Features
@@ -75,9 +72,21 @@ ⚔️ Combat System
Relationship-based PvP - Allies protected, enemies open, configurable per zone
Overclaim defender alerts - Faction members get real-time alerts when territory is being taken
+💰 Faction Economy
+
+ - Faction treasury with deposits, withdrawals, inter-faction transfers, and transaction history
+ - Upkeep system - Automated territory maintenance costs with flat or progressive tiered pricing, grace periods, and auto-pay
+ - VaultUnlocked integration - Works with any economy plugin via the standard economy API
+
+🌍 Localization
+
+ - 10 languages built in: English, German, Spanish, French, Italian, Dutch, Polish, Portuguese, Russian, Filipino
+ - Player language auto-detection with configurable default and per-player override
+ - ~467 translation entries per locale covering all commands, GUI labels, and help content
+
🛡️ Protection System
-HyperFactions provides comprehensive territory protection with 50 configurable zone flags organized into categories:
+HyperFactions provides comprehensive territory protection with 51 configurable zone flags organized into 10 categories:
- ⚔️ Combat (7) - PvP, friendly fire (per-faction/per-ally), projectile damage, PvE damage, mob damage
- 🧱 Building (4) - Build allowed, block place (mixin), hammer use (mixin), builder tools (mixin)
@@ -88,7 +97,7 @@ 🛡️ Protection System
- 💀 Death (2) - Keep inventory (mixin), power loss
- 💥 Damage (4) - Fall damage, environmental damage, explosion damage (mixin), fire spread (mixin)
- 🚀 Transport (3) - Teleporter use (mixin), portal use (mixin), mount entry
- - 🔗 Integration (5) - Show on map, command blocking, map visibility, fluid spread (mixin), mount entry
+ - 🔗 Integration (5) - Gravestone access, show on map, essentials homes, essentials warps, essentials kits
🏟️ SafeZones & WarZones
@@ -124,7 +133,7 @@ 📢 Server-Wide Announcements
Each event type can be individually enabled or disabled in announcements.json.
🖥️ Full GUI System
-HyperFactions features 76 interactive GUI pages covering every aspect of gameplay:
+HyperFactions features 70+ interactive GUI pages covering every aspect of gameplay:
🎮 Player GUI (/f)
- 📊 Dashboard - Power, claims, members, relations, status, and invites at a glance
@@ -177,7 +186,7 @@ 🔧 Admin Tools
- ⏪ Rollback support -
/f admin rollback reverts to the previous version before restart
- 🐛 Debug system - Toggle 7+ debug categories individually for targeted troubleshooting
- ⏳ Claim decay management - Monitor and manually trigger inactive faction cleanup
- - 📦 Data import - Migrate from ElbaphFactions or HyFactions with validation reports
+ - 📦 Data import - Migrate from ElbaphFactions, HyFactions, SimpleClaims, or FactionsX with validation reports
- ⚡ Power management - Per-player power set, adjust, reset, bypass toggles, and bulk faction operations
- 💰 Economy management - Server-wide treasury overview, per-faction balance adjustment
- 👥 Player browser - Search and manage all server players with sort and quick actions
@@ -199,48 +208,132 @@ 📊 PlaceholderAPI Support
Use with any scoreboard, hologram, or menu plugin that supports PAPI or WiFlow.
🔌 Integrations
-⭐ = Recommended Mod by HyperSystems Team
-🔑 Permission Systems
-HyperFactions supports multiple permission providers with automatic detection:
-
- - ⭐ HyperPerms - Full integration with faction chat prefixes, rank display, and contextual permissions (Recommended)
- - LuckPerms - Granular permission control
- - ⭐ VaultUnlocked - Chat, economy, and permission compatibility
- - No permission mod - Works without any permission plugin (configurable allow/deny default)
-
-🛡️ Protection Extensions
-
- - ⭐ Hyxin + HyperProtect-Mixin - Recommended — 27 protection hooks including F-key pickup, keep inventory, explosion/fire/fluid protection, block placement, transport control, entity damage, mount/barter/projectile control, map marker filtering, and more
- - Hyxin + OrbisGuard-Mixins - Alternative — 11 protection hooks. Both can run simultaneously (HyperFactions auto-detects and routes hooks accordingly)
-
-📊 Placeholder Systems
-
-📦 Data Migration
-
- - ElbaphFactions - Full import of factions, members, claims, relations, and zones
- - HyFactions - Full import with validation reporting
-
-🗺️ Region Protection
-
- - OrbisGuard - Auto-blocks claims in OG-protected regions, renders OG regions on world map and territory GUI with colored overlays
-
-🤝 Direct Mod Integrations
-
- - ⭐ HyBounty — Place bounties on other players with faction-aware protections against abuse
- - 🪦 Gravestones — Faction-aware gravestone protection with per-zone access control, configurable ally/member access, and death location announcements
- - 💰 Ecotale — Faction treasury system with deposits, withdrawals, inter-faction transfers, and admin economy tools via VaultUnlocked bridge
- - 🏘️ KyuubiSoft Core — Citizen NPC zone protection — auto-detects KyuubiSoft citizens and enforces faction territory rules for NPC dialog interactions
-
+All integrations use automatic detection and fail-open design. ⭐ = Recommended
+
+
+
+ | Mod |
+ Category |
+ Description |
+
+
+
+
+ | ⭐ HyperPerms |
+ Permissions |
+ Faction chat prefixes, rank display, and contextual permissions |
+
+
+ | LuckPerms |
+ Permissions |
+ Granular permission control |
+
+
+ | ⭐ VaultUnlocked |
+ Economy |
+ Chat, economy, and permission compatibility layer |
+
+
+ | ⭐ HyperProtect-Mixin |
+ Protection |
+ 28 mixin hooks — F-key pickup, keep inventory, explosions, fire, fluid, transport, mount, barter, projectile, map markers, and more |
+
+
+ | OrbisGuard-Mixins |
+ Protection |
+ 11 mixin hooks (alternative). Both can run simultaneously — auto-detected |
+
+
+ | OrbisGuard |
+ Regions |
+ Blocks claims in OG regions, renders OG regions on world map and territory GUI |
+
+
+ | ⭐ PlaceholderAPI |
+ Placeholders |
+ 49 placeholders for scoreboards, chat, and menus |
+
+
+ | WiFlow PlaceholderAPI |
+ Placeholders |
+ 47 placeholders in WiFlow format |
+
+
+ | ⭐ HyBounty |
+ Gameplay |
+ Player bounties with faction-aware protections against abuse |
+
+
+ | Gravestones |
+ Gameplay |
+ Faction-aware gravestone protection, per-zone access, death location announcements |
+
+
+ | Ecotale |
+ Economy |
+ Faction treasury via VaultUnlocked — deposits, withdrawals, transfers, admin tools |
+
+
+ | KyuubiSoft Core |
+ NPC |
+ Citizen NPC zone protection with auto-detection |
+
+
+
+📦 Data Import
+
+
+
+ | Source |
+ What’s Imported |
+
+
+
+
+ | ElbaphFactions |
+ Factions, members, claims, relations, and zones |
+
+
+ | HyFactions |
+ Factions, members, claims, relations, zones, and power |
+
+
+ | SimpleClaims |
+ Parties, claims (SQLite or JSON), and mutual alliances |
+
+
+ | FactionsX |
+ Factions, claims, zones, player power, and per-role permissions |
+
+
+
🔮 Upcoming Integrations
-
- - 📈 RPG Leveling - Faction bonuses and level-based perks for faction members (Planned)
- - 🗣️ NPC Dialog - Faction NPCs and dialog interactions in claimed territory (Planned)
- - 📝 NPC Quests Maker - Faction quests and mission systems (Planned)
- - 📋 BetterScoreBoard - Faction data on the scoreboard HUD (Waiting on BetterScoreBoard to support PlaceholderAPI or WiFlow)
-
+
+
+
+ | Mod |
+ Status |
+
+
+
+
+ | RPG Leveling |
+ Planned — Faction bonuses and level-based perks |
+
+
+ | NPC Dialog |
+ Planned — Faction NPCs and dialog in claimed territory |
+
+
+ | NPC Quests Maker |
+ Planned — Faction quests and mission systems |
+
+
+ | BetterScoreBoard |
+ Waiting — Needs PlaceholderAPI or WiFlow support |
+
+
+
💡 Want HyperFactions to integrate with your plugin? Reach out to DMehaffy on Discord to discuss integration opportunities.
📥 Installation
@@ -401,7 +494,7 @@ 🧑💻 For Developers
}
dependencies {
- compileOnly 'com.github.HyperSystems-Development:HyperFactions:v0.11.0'
+ compileOnly 'com.github.HyperSystems-Development:HyperFactions:v0.12.0'
}
See the full Developer API Reference on GitHub for API usage, event listeners, economy integration, and more.
@@ -420,7 +513,7 @@ 🧩 The HyperSystems Suite
| ⚔️ HyperFactions |
- Complete faction system with territory, diplomacy, economy, and 65+ GUI pages |
+ Complete faction system with territory, diplomacy, economy, and 70+ GUI pages |
| 🛡️ HyperProtect-Mixin |
diff --git a/docs/api.md b/docs/api.md
index 358bd9c8..4dd41ace 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -1,6 +1,6 @@
# HyperFactions Developer API Reference
-> **Version**: 0.11.0 | **Package**: `com.hyperfactions.api`
+> **Version**: 0.12.0 | **Package**: `com.hyperfactions.api`
This document is for third-party mod developers who want to hook into HyperFactions from their own plugins.
diff --git a/docs/architecture.md b/docs/architecture.md
index 739004fc..4d7bcf27 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -1,6 +1,6 @@
# HyperFactions Architecture
-> **Version**: 0.10.0 | **377 classes** across **69 packages**
+> **Version**: 0.12.0 | **451 classes** across **74 packages**
## Overview
diff --git a/docs/commands.md b/docs/commands.md
index 262a085e..48df267b 100644
--- a/docs/commands.md
+++ b/docs/commands.md
@@ -1,6 +1,6 @@
# HyperFactions Command System
-> **Version**: 0.11.0 | **~46 subcommands** across **10 categories**
+> **Version**: 0.12.0 | **~46 subcommands** across **10 categories**
Architecture documentation for the HyperFactions command system.
@@ -355,6 +355,11 @@ Admin commands use nested subcommand structure:
│ ├── list
│ ├── restore
│ └── delete
+├── import # Data import from other faction plugins
+│ ├── elbaphfactions [path] [flags] # Import from ElbaphFactions
+│ ├── hyfactions [path] [flags] # Import from HyFactions V1
+│ ├── simpleclaims [path] [flags] # Import from SimpleClaims
+│ └── factionsx [path] [flags] # Import from FactionsX
├── reload # Reload config
├── update # Check for updates
│ ├── mixin # Check/download HyperProtect-Mixin
diff --git a/docs/config.md b/docs/config.md
index 06dd2402..64a8f407 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -1,6 +1,6 @@
# HyperFactions Config System
-> **Version**: 0.11.0 | **Config version**: 7 | **11 config files**
+> **Version**: 0.12.0 | **Config version**: 8 | **11 config files**
Architecture documentation for the HyperFactions configuration system.
diff --git a/docs/data-import.md b/docs/data-import.md
index d09ba51f..9707dde4 100644
--- a/docs/data-import.md
+++ b/docs/data-import.md
@@ -1,6 +1,6 @@
# HyperFactions Data Import & Migration
-> **Version**: 0.10.0 | **Packages**: `com.hyperfactions.importer`, `com.hyperfactions.migration`
+> **Version**: 0.12.0 | **Packages**: `com.hyperfactions.importer`, `com.hyperfactions.migration`
HyperFactions supports importing data from other faction plugins and automatically migrating its own configuration between versions.
@@ -10,6 +10,8 @@ HyperFactions supports importing data from other faction plugins and automatical
- [ElbaphFactions Importer](#elbaphfactions-importer)
- [HyFactions V1 Importer](#hyfactions-v1-importer)
+- [SimpleClaims Importer](#simpleclaims-importer)
+- [FactionsX Importer](#factionsx-importer)
- [Config Migration System](#config-migration-system)
- [Pre-Import Backup](#pre-import-backup)
@@ -124,6 +126,104 @@ Same flags as ElbaphFactions: `--dry-run`, `--overwrite`, `--no-zones`, `--no-po
---
+## SimpleClaims Importer
+
+**Command**: `/f admin import simpleclaims [path] [flags]`
+**Permission**: `hyperfactions.admin.use`
+
+Imports faction data from the SimpleClaims mod, converting parties and claims to HyperFactions format.
+
+### Data Directory
+
+Default: `mods/SimpleClaims/` (or specify a custom path)
+
+Supports two storage formats (auto-detected):
+
+| Format | File | Contents |
+|--------|------|----------|
+| SQLite | `SimpleClaims.db` | Modern format — parties, claims, name cache in one database |
+| JSON | `Parties.json` | Legacy format — party definitions |
+| JSON | `Claims.json` | Legacy format — territory claims (ChunkY=Z quirk) |
+| JSON | `NameCache.json` | Legacy format — UUID to player name mapping |
+
+### Command Options
+
+| Flag | Description |
+|------|-------------|
+| `--dry-run` | Validate data without importing |
+| `--overwrite` | Overwrite existing factions with matching names |
+| `--no-power` | Skip power assignment |
+
+### Data Mapping
+
+| SimpleClaims | HyperFactions |
+|-------------|---------------|
+| Party name, description, color | Direct mapping (signed RGB integer converted to `#RRGGBB`) |
+| Owner → LEADER, Members → MEMBER | 2 roles only (no officer equivalent) |
+| Claims per dimension | FactionClaim records with world/chunkX/chunkZ |
+| Protection overrides (place, break, interact, pvp) | FactionPermissions outsider flags |
+| Mutual party alliances | ALLY relations (one-way alliances skipped) |
+| Player allies | No equivalent — logged as warnings |
+
+> **Notes:**
+> - SimpleClaims has no power system — all imported players receive the configured max power
+> - No faction home support
+> - No zone (safezone/warzone) support
+> - SQLite format requires the SimpleClaims JAR in the mods folder (for the JDBC driver)
+> - Black or missing colors are replaced with a random color
+
+---
+
+## FactionsX Importer
+
+**Command**: `/f admin import factionsx [path] [flags]`
+**Permission**: `hyperfactions.admin.use`
+
+Imports faction data from the FactionsX mod (by Humblegod666), converting factions, claims, zones, and player data to HyperFactions format.
+
+### Data Directory
+
+Default: `mods/FactionsX/config/` (or specify a custom path)
+
+Expected structure:
+
+| Path | Contents |
+|------|----------|
+| `factions/{UUID}.json` | Individual JSON files per faction |
+| `players/{UUID}.json` | Per-player files (name + power) |
+| `Claims.json` | Territory claims by dimension (ChunkY=Z quirk) |
+| `Zones.json` | SafeZone and WarZone chunks per dimension |
+
+### Command Options
+
+| Flag | Description |
+|------|-------------|
+| `--dry-run` | Validate data without importing |
+| `--overwrite` | Overwrite existing factions with matching names |
+| `--no-zones` | Skip zone import |
+| `--no-power` | Skip power data import |
+
+### Data Mapping
+
+| FactionsX | HyperFactions |
+|-----------|---------------|
+| Faction name, description, color | Direct mapping (color converted to `#RRGGBB`) |
+| Owner (implicit LEADER) + Members | FactionMember records; RECRUIT mapped to MEMBER |
+| Claims per dimension | FactionClaim records with world/chunkX/chunkZ |
+| SafeZones / WarZones | Zone records with type and claim set |
+| Per-player power/maxPower | PlayerPower records (power + max power preserved) |
+| Per-role permissions (Build, Claim, Interact, Invite, Kick) | FactionPermissions flags per role |
+| Home (x/y/z/dimension) | FactionHome with world mapping |
+| Relations (ally/enemy/neutral) | FactionRelation records |
+
+> **Notes:**
+> - Owner is NOT in the Members map — always treated as LEADER implicitly
+> - RECRUIT role is mapped to MEMBER (HyperFactions has 3 roles: LEADER, OFFICER, MEMBER)
+> - Thread-safe: `ReentrantLock` + `AtomicBoolean` prevents concurrent imports
+> - Empty factions (no members) are skipped with a warning
+
+---
+
## Config Migration System
HyperFactions automatically migrates configuration files between versions on startup.
@@ -152,6 +252,8 @@ Migrations are applied in sequence. The `MigrationRegistry` builds the chain aut
| `ConfigV3ToV4Migration` | v3 | v4 | Restructure permissions, add interaction sub-types |
| `ConfigV4ToV5Migration` | v4 | v5 | Remove `warzonePowerLoss`, add per-zone `power_loss` flag |
| `ConfigV5ToV6Migration` | v5 | v6 | Split `config.json` into `config/factions.json` + `config/server.json` |
+| `ConfigV6ToV7Migration` | v6 | v7 | Restructure economy config, add upkeep settings |
+| `ConfigV7ToV8Migration` | v7 | v8 | Add localization settings, language config |
**Data Migrations** (run before storage init in `HyperFactions.enable()`):
diff --git a/docs/gui.md b/docs/gui.md
index 38ffd748..7de12017 100644
--- a/docs/gui.md
+++ b/docs/gui.md
@@ -1,6 +1,6 @@
# HyperFactions GUI System
-> **Version**: 0.11.0 | **~76 pages** across **3 registries**
+> **Version**: 0.12.0 | **~70 pages** across **3 registries**
Architecture documentation for the HyperFactions GUI system using Hytale's CustomUI.
@@ -678,6 +678,22 @@ Inter-faction transfer search. Browse and search target factions for treasury tr
#### TreasuryTransferConfirmPage
Transfer confirmation modal. Shows source faction, target faction, amount, and fee (if configured). Requires officer+ permission.
+## New Pages in v0.12.0
+
+### Admin Pages
+
+#### AdminConfigPage
+Runtime config editor with 11 tabs: Server, Chat, Announcements, Economy, Factions, Faction Perms, Worldmap, Worlds, Backup, Debug, Gravestones. Size-adaptive layouts (narrow/standard/wide), inline editing with boolean toggles, integer/double steppers, text fields, color pickers, enum dropdowns, and locale selectors. Edit session caching survives page close/reopen. Per-field validation with error highlighting. Uses ConfigSnapshot for applying changes and ConfigValidator for input bounds.
+
+#### AdminBackupsPage
+Paginated backup list with expand/collapse detail view per entry. Create manual backups with optional custom name. Restore with two-click confirmation and automatic safety backup. Delete with two-click confirmation. Backup type filter dropdown (All / Hourly / Daily / Weekly / Manual / Migration).
+
+#### AdminUpdatesPage
+Two-column layout: HyperFactions (left) and HyperProtect Mixin (right). Shows current version, latest version, channel, build date, and update status. Single "Check for Updates" button checks both simultaneously. Download buttons appear when updates are available. Changelog display for HyperFactions updates. Rollback support with two-click confirmation.
+
+#### ScalingTiersModalPage
+Upkeep scaling tiers editor modal opened from AdminConfigPage Economy tab. Add/remove/reorder tiers with promote/demote buttons (disabled on first/last). Live cost example display.
+
## Adding New Pages
1. **Create data record** in appropriate `data/` package:
diff --git a/docs/integrations.md b/docs/integrations.md
index 45acea07..95605c27 100644
--- a/docs/integrations.md
+++ b/docs/integrations.md
@@ -1,6 +1,6 @@
# HyperFactions Integration Breakdown
-> **Version**: 0.11.0 | **Package**: `com.hyperfactions.integration`
+> **Version**: 0.12.0 | **Package**: `com.hyperfactions.integration`
HyperFactions integrates with external plugins through soft dependencies. All integrations use reflection-based detection and fail-open design — if a dependency is missing, the feature gracefully degrades.
@@ -16,7 +16,7 @@ HyperFactions integrates with external plugins through soft dependencies. All in
- [Protection Mixin Bridge](#protection-mixin-bridge)
- [HyperProtect-Mixin](#hyperprotect-mixin) (recommended)
- [OrbisGuard-Mixins](#orbisguard-mixins)
-- [World Map](#world-map)
+- [World Map](#world-map) (incl. BetterMap compatibility)
- [GravestonePlugin](#gravestoneplugin)
- [KyuubiSoft Core](#kyuubisoft-core)
- [Sentry](#sentry)
@@ -470,6 +470,15 @@ Key settings in `config/worldmap.json`:
- `maxChunksPerBatch` — Throttle for large updates
- `showFactionTags` — Display faction names on the map
+### BetterMap Compatibility
+
+HyperFactions is compatible with BetterMap's exploration-based map reveal system. When BetterMap is installed:
+
+- Per-world WorldMap enable/disable settings in `config/worlds.json` are respected
+- Claims and zones render correctly on BetterMap-managed worlds
+- The `WorldMapService` checks world config before registering map providers
+- No additional configuration needed — auto-detected at world load
+
---
## GravestonePlugin
diff --git a/docs/managers.md b/docs/managers.md
index 9e16a29d..120fe720 100644
--- a/docs/managers.md
+++ b/docs/managers.md
@@ -1,6 +1,6 @@
# HyperFactions Manager Layer
-> **Version**: 0.10.0 | **15 core managers** (20 total)
+> **Version**: 0.12.0 | **16 core managers** (22 total)
The manager layer contains all business logic for HyperFactions, organized by domain.
@@ -68,6 +68,7 @@ graph TD
| [EconomyManager](#economymanager) | Faction economy (treasury, transactions) | FactionManager |
| [AnnouncementManager](#announcementmanager) | Server-wide event broadcasts | None |
| [SpawnSuppressionManager](#spawnsuppressionmanager) | Mob spawn control in claims/zones | ZoneManager, ClaimManager |
+| [ZoneMobClearManager](#zonemobclearmanager) | Periodic mob clearing in zones | ZoneManager |
## Initialization Order
diff --git a/docs/permissions.md b/docs/permissions.md
index dd862344..08dfa1ec 100644
--- a/docs/permissions.md
+++ b/docs/permissions.md
@@ -1,6 +1,6 @@
# HyperFactions Permission Framework
-> **Version**: 0.11.0 | **76 permission nodes** across **12 categories**
+> **Version**: 0.12.0 | **76 permission nodes** across **12 categories**
Architecture documentation for the HyperFactions permission system.
diff --git a/docs/placeholders.md b/docs/placeholders.md
index e61ad0e7..9f1f32df 100644
--- a/docs/placeholders.md
+++ b/docs/placeholders.md
@@ -1,6 +1,6 @@
# HyperFactions Placeholders
-> **Version**: 0.10.0 | **Expansion Identifier**: `factions` | **51 placeholders**
+> **Version**: 0.12.0 | **Expansion Identifier**: `factions` | **51 placeholders**
HyperFactions exposes faction data as placeholders through two placeholder APIs: **PlaceholderAPI (PAPI)** and **WiFlow PlaceholderAPI**. Both APIs support the same set of placeholders with identical behavior.
diff --git a/docs/protection.md b/docs/protection.md
index 2f3e9568..dcfbc910 100644
--- a/docs/protection.md
+++ b/docs/protection.md
@@ -1,6 +1,6 @@
# HyperFactions Protection System
-> **Version**: 0.11.0
+> **Version**: 0.12.0
Multi-layered protection controlling block interactions, PvP combat, damage types, and mob spawning based on zones, faction claims, and player relations.
diff --git a/docs/readme.md b/docs/readme.md
index 423d6b5a..4814d8ec 100644
--- a/docs/readme.md
+++ b/docs/readme.md
@@ -1,6 +1,6 @@
# HyperFactions Developer Documentation
-> **Version**: 0.11.0 | **~409 classes** | **69 packages** | **20 managers** | **~46 commands** | **76 permissions**
+> **Version**: 0.12.0 | **~451 classes** | **74 packages** | **22 managers** | **~46 commands** | **76 permissions**
Developer documentation for HyperFactions - a comprehensive faction management plugin for Hytale servers.
diff --git a/src/main/java/com/hyperfactions/api/events/EventBus.java b/src/main/java/com/hyperfactions/api/events/EventBus.java
index 66fb293f..7e35c8f7 100644
--- a/src/main/java/com/hyperfactions/api/events/EventBus.java
+++ b/src/main/java/com/hyperfactions/api/events/EventBus.java
@@ -1,5 +1,6 @@
package com.hyperfactions.api.events;
+import com.hyperfactions.util.ErrorHandler;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -54,8 +55,7 @@ public static void publish(@NotNull T event) {
try {
((Consumer) listener).accept(event);
} catch (Exception e) {
- // Log but don't propagate
- System.err.println("[HyperFactions] Error in event listener: " + e.getMessage());
+ ErrorHandler.report("Event bus listener error", e);
}
}
}
diff --git a/src/main/java/com/hyperfactions/backup/BackupManager.java b/src/main/java/com/hyperfactions/backup/BackupManager.java
index f4cb3c3d..33dd24f0 100644
--- a/src/main/java/com/hyperfactions/backup/BackupManager.java
+++ b/src/main/java/com/hyperfactions/backup/BackupManager.java
@@ -443,8 +443,7 @@ public List listBackups() {
));
}
} catch (Exception e) {
- Logger.warn("[Backup] Could not read backup metadata for %s: %s",
- file.getFileName(), e.getMessage());
+ ErrorHandler.report(String.format("[Backup] Could not read backup metadata for %s", file.getFileName()), e);
}
}
}
@@ -556,15 +555,14 @@ private void rotateShutdownBackups() {
Files.delete(toDelete);
Logger.debug("[Backup] Rotated out old shutdown backup: %s", toDelete.getFileName());
} catch (IOException e) {
- Logger.warn("[Backup] Failed to delete old shutdown backup %s: %s",
- toDelete.getFileName(), e.getMessage());
+ ErrorHandler.report(String.format("[Backup] Failed to delete old shutdown backup %s", toDelete.getFileName()), e);
}
}
int deleted = shutdownBackups.size() - retention;
Logger.info("[Backup] Cleaned up %d old shutdown backup(s), keeping %d", deleted, retention);
} catch (IOException e) {
- Logger.warn("[Backup] Failed to rotate shutdown backups: %s", e.getMessage());
+ ErrorHandler.report("[Backup] Failed to rotate shutdown backups", e);
}
}
diff --git a/src/main/java/com/hyperfactions/command/admin/AdminSubCommand.java b/src/main/java/com/hyperfactions/command/admin/AdminSubCommand.java
index ea21a8ea..b0271942 100644
--- a/src/main/java/com/hyperfactions/command/admin/AdminSubCommand.java
+++ b/src/main/java/com/hyperfactions/command/admin/AdminSubCommand.java
@@ -25,7 +25,6 @@
import com.hyperfactions.util.HelpFormatter;
import com.hyperfactions.util.AdminKeys;
import com.hyperfactions.util.HelpKeys;
-import com.hyperfactions.util.MessageUtil;
import com.hyperfactions.util.PlayerResolver;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
diff --git a/src/main/java/com/hyperfactions/command/admin/handler/AdminEconomyHandler.java b/src/main/java/com/hyperfactions/command/admin/handler/AdminEconomyHandler.java
index f501abcc..e136177f 100644
--- a/src/main/java/com/hyperfactions/command/admin/handler/AdminEconomyHandler.java
+++ b/src/main/java/com/hyperfactions/command/admin/handler/AdminEconomyHandler.java
@@ -14,7 +14,6 @@
import com.hyperfactions.util.CommonKeys;
import com.hyperfactions.util.HelpKeys;
import com.hyperfactions.util.Logger;
-import com.hyperfactions.util.MessageUtil;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.universe.PlayerRef;
diff --git a/src/main/java/com/hyperfactions/command/economy/TreasuryCommandHandler.java b/src/main/java/com/hyperfactions/command/economy/TreasuryCommandHandler.java
index 330f7765..ac178d97 100644
--- a/src/main/java/com/hyperfactions/command/economy/TreasuryCommandHandler.java
+++ b/src/main/java/com/hyperfactions/command/economy/TreasuryCommandHandler.java
@@ -22,7 +22,6 @@
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
-import java.util.UUID;
import org.jetbrains.annotations.NotNull;
/**
diff --git a/src/main/java/com/hyperfactions/config/modules/DebugConfig.java b/src/main/java/com/hyperfactions/config/modules/DebugConfig.java
index 661342da..dc14a9c3 100644
--- a/src/main/java/com/hyperfactions/config/modules/DebugConfig.java
+++ b/src/main/java/com/hyperfactions/config/modules/DebugConfig.java
@@ -3,6 +3,7 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.hyperfactions.config.ModuleConfig;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -596,7 +597,7 @@ private void migrateLegacySentryConfig() {
Files.delete(sentryFile);
Logger.info("[Config] Deleted old config/sentry.json");
} catch (Exception e) {
- Logger.warn("[Config] Failed to migrate sentry.json: %s", e.getMessage());
+ ErrorHandler.report("[Config] Failed to migrate sentry.json", e);
}
}
}
diff --git a/src/main/java/com/hyperfactions/gui/GuiUpdateService.java b/src/main/java/com/hyperfactions/gui/GuiUpdateService.java
index 8f35d31a..ab7ccc0e 100644
--- a/src/main/java/com/hyperfactions/gui/GuiUpdateService.java
+++ b/src/main/java/com/hyperfactions/gui/GuiUpdateService.java
@@ -4,6 +4,7 @@
import com.hyperfactions.data.JoinRequest;
import com.hyperfactions.data.PendingInvite;
import com.hyperfactions.manager.FactionManager;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.Universe;
@@ -219,7 +220,7 @@ private void dispatchRefresh(@NotNull UUID playerUuid) {
try {
r.refreshContent();
} catch (Exception e) {
- Logger.warn("[GuiUpdate] Error refreshing page for %s: %s", playerUuid, e.getMessage());
+ ErrorHandler.report("[GuiUpdate] Error refreshing page for " + playerUuid, e);
}
}
});
diff --git a/src/main/java/com/hyperfactions/gui/admin/ConfigSnapshot.java b/src/main/java/com/hyperfactions/gui/admin/ConfigSnapshot.java
index da72ecce..1ef12dc8 100644
--- a/src/main/java/com/hyperfactions/gui/admin/ConfigSnapshot.java
+++ b/src/main/java/com/hyperfactions/gui/admin/ConfigSnapshot.java
@@ -2,6 +2,7 @@
import com.hyperfactions.config.ConfigManager;
import com.hyperfactions.config.modules.*;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import java.math.BigDecimal;
@@ -268,7 +269,7 @@ public static void applyChange(String key, Object value) {
}
}
} catch (Exception e) {
- Logger.warn("[ConfigEditor] Failed to apply change for key '%s': %s", key, e.getMessage());
+ ErrorHandler.report("[ConfigEditor] Failed to apply change for key '" + key + "'", e);
}
}
diff --git a/src/main/java/com/hyperfactions/gui/faction/page/FactionChatPage.java b/src/main/java/com/hyperfactions/gui/faction/page/FactionChatPage.java
index 7722a4b4..919931d7 100644
--- a/src/main/java/com/hyperfactions/gui/faction/page/FactionChatPage.java
+++ b/src/main/java/com/hyperfactions/gui/faction/page/FactionChatPage.java
@@ -15,7 +15,7 @@
import com.hyperfactions.manager.ChatHistoryManager;
import com.hyperfactions.manager.ChatManager;
import com.hyperfactions.manager.FactionManager;
-import com.hyperfactions.util.Logger;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.MessageUtil;
import com.hyperfactions.util.HFMessages;
import com.hyperfactions.util.GuiKeys;
@@ -211,7 +211,7 @@ private List loadMessages() {
.toList();
}
} catch (Exception e) {
- Logger.warn("[FactionChatPage] Failed to load messages: %s", e.getMessage());
+ ErrorHandler.report("Failed to load chat messages", e);
return List.of();
}
}
diff --git a/src/main/java/com/hyperfactions/gui/help/HelpRegistry.java b/src/main/java/com/hyperfactions/gui/help/HelpRegistry.java
index d8697468..aa1b9f26 100644
--- a/src/main/java/com/hyperfactions/gui/help/HelpRegistry.java
+++ b/src/main/java/com/hyperfactions/gui/help/HelpRegistry.java
@@ -4,6 +4,7 @@
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -107,7 +108,7 @@ private void loadFromManifest() {
Logger.info("Loaded %d help topics from manifest", topicsById.size());
} catch (Exception e) {
- Logger.warn("Failed to load help manifest: %s", e.getMessage());
+ ErrorHandler.report("Failed to load help manifest", e);
}
}
diff --git a/src/main/java/com/hyperfactions/integration/SentryIntegration.java b/src/main/java/com/hyperfactions/integration/SentryIntegration.java
index bb6188e3..97d098b1 100644
--- a/src/main/java/com/hyperfactions/integration/SentryIntegration.java
+++ b/src/main/java/com/hyperfactions/integration/SentryIntegration.java
@@ -25,7 +25,7 @@
*/
public final class SentryIntegration {
- private static boolean initialized = false;
+ private static volatile boolean initialized = false;
/** Buffered errors from before Sentry was initialized. */
private static final List preInitErrors = new ArrayList<>();
diff --git a/src/main/java/com/hyperfactions/integration/permissions/HyperPermsIntegration.java b/src/main/java/com/hyperfactions/integration/permissions/HyperPermsIntegration.java
index 6a8f307a..228963e9 100644
--- a/src/main/java/com/hyperfactions/integration/permissions/HyperPermsIntegration.java
+++ b/src/main/java/com/hyperfactions/integration/permissions/HyperPermsIntegration.java
@@ -1,5 +1,6 @@
package com.hyperfactions.integration.permissions;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import java.lang.reflect.Method;
import java.util.UUID;
@@ -85,11 +86,11 @@ public static void init() {
} catch (NoSuchMethodException e) {
available = false;
initError = "Method not found: " + e.getMessage();
- Logger.warn("HyperPerms API mismatch: %s - defaulting to allow all", e.getMessage());
+ ErrorHandler.report("HyperPerms API mismatch - defaulting to allow all", e);
} catch (Exception e) {
available = false;
initError = e.getClass().getSimpleName() + ": " + e.getMessage();
- Logger.warn("Failed to initialize HyperPerms integration: %s - defaulting to allow all", e.getMessage());
+ ErrorHandler.report("Failed to initialize HyperPerms integration - defaulting to allow all", e);
}
}
@@ -167,8 +168,7 @@ public static boolean hasPermission(@NotNull UUID playerUuid, @NotNull String pe
} catch (Exception e) {
// Any error in permission check = allow (fail-open)
- Logger.warn("[PERM] Exception checking %s for %s: %s, ALLOWING",
- permission, playerUuid, e.getMessage());
+ ErrorHandler.report("Exception checking permission " + permission + " for " + playerUuid + ", ALLOWING", e);
return true;
}
}
diff --git a/src/main/java/com/hyperfactions/integration/placeholder/PlaceholderAPIIntegration.java b/src/main/java/com/hyperfactions/integration/placeholder/PlaceholderAPIIntegration.java
index 25dcd172..8b183515 100644
--- a/src/main/java/com/hyperfactions/integration/placeholder/PlaceholderAPIIntegration.java
+++ b/src/main/java/com/hyperfactions/integration/placeholder/PlaceholderAPIIntegration.java
@@ -1,6 +1,7 @@
package com.hyperfactions.integration.placeholder;
import com.hyperfactions.HyperFactions;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import org.jetbrains.annotations.Nullable;
@@ -51,7 +52,7 @@ public static void init(HyperFactions plugin) {
expansion = null;
}
} catch (Exception e) {
- Logger.warn("Failed to register PlaceholderAPI expansion: %s", e.getMessage());
+ ErrorHandler.report("Failed to register PlaceholderAPI expansion", e);
expansion = null;
}
}
diff --git a/src/main/java/com/hyperfactions/integration/placeholder/WiFlowPlaceholderIntegration.java b/src/main/java/com/hyperfactions/integration/placeholder/WiFlowPlaceholderIntegration.java
index f03f89d4..76cab5a9 100644
--- a/src/main/java/com/hyperfactions/integration/placeholder/WiFlowPlaceholderIntegration.java
+++ b/src/main/java/com/hyperfactions/integration/placeholder/WiFlowPlaceholderIntegration.java
@@ -1,6 +1,7 @@
package com.hyperfactions.integration.placeholder;
import com.hyperfactions.HyperFactions;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import java.lang.reflect.Method;
import org.jetbrains.annotations.Nullable;
@@ -69,7 +70,7 @@ public static void init(HyperFactions plugin) {
expansion = null;
}
} catch (Exception e) {
- Logger.warn("Failed to register WiFlow PlaceholderAPI expansion: %s", e.getMessage());
+ ErrorHandler.report("Failed to register WiFlow PlaceholderAPI expansion", e);
expansion = null;
}
}
diff --git a/src/main/java/com/hyperfactions/integration/protection/OrbisGuardIntegration.java b/src/main/java/com/hyperfactions/integration/protection/OrbisGuardIntegration.java
index 331a6f7c..2fff89bd 100644
--- a/src/main/java/com/hyperfactions/integration/protection/OrbisGuardIntegration.java
+++ b/src/main/java/com/hyperfactions/integration/protection/OrbisGuardIntegration.java
@@ -1,5 +1,6 @@
package com.hyperfactions.integration.protection;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -119,11 +120,11 @@ public static void init() {
} catch (NoSuchMethodException e) {
available = false;
initError = "OrbisGuard API mismatch: " + e.getMessage();
- Logger.warn("OrbisGuard API version incompatible: %s", e.getMessage());
+ ErrorHandler.report("OrbisGuard API version incompatible", e);
} catch (Exception e) {
available = false;
initError = e.getClass().getSimpleName() + ": " + e.getMessage();
- Logger.warn("Error initializing OrbisGuard integration: %s", e.getMessage());
+ ErrorHandler.report("Error initializing OrbisGuard integration", e);
}
initialized = true;
@@ -182,8 +183,7 @@ public static boolean hasProtectiveRegions(@NotNull String worldName, int x, int
return false;
} catch (Throwable e) {
- Logger.warn("Error checking OrbisGuard regions at %s/%d/%d/%d: %s",
- worldName, x, y, z, e.getMessage());
+ ErrorHandler.report("Error checking OrbisGuard regions", e);
return false; // Fail-open
}
}
@@ -326,7 +326,7 @@ public static List getRegionsForWorld(@NotNull String worldName) {
return Collections.emptyList();
} catch (Throwable e) {
- Logger.warn("Error getting OrbisGuard regions for world %s: %s", worldName, e.getMessage());
+ ErrorHandler.report("Error getting OrbisGuard regions for world " + worldName, e);
return Collections.emptyList();
}
}
@@ -368,7 +368,7 @@ public static List getAllRegions() {
return result;
} catch (Throwable e) {
- Logger.warn("Error getting all OrbisGuard regions: %s", e.getMessage());
+ ErrorHandler.report("Error getting all OrbisGuard regions", e);
return Collections.emptyList();
}
}
diff --git a/src/main/java/com/hyperfactions/integration/protection/OrbisMixinsIntegration.java b/src/main/java/com/hyperfactions/integration/protection/OrbisMixinsIntegration.java
index 4dd456a1..476d5ddf 100644
--- a/src/main/java/com/hyperfactions/integration/protection/OrbisMixinsIntegration.java
+++ b/src/main/java/com/hyperfactions/integration/protection/OrbisMixinsIntegration.java
@@ -1,5 +1,6 @@
package com.hyperfactions.integration.protection;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.world.World;
@@ -164,7 +165,7 @@ public static void init() {
} catch (Exception e) {
mixinsAvailable = false;
initError = e.getClass().getSimpleName() + ": " + e.getMessage();
- Logger.warn("Error checking OrbisGuard-Mixins availability: %s", e.getMessage());
+ ErrorHandler.report("Error checking OrbisGuard-Mixins availability", e);
}
initialized = true;
@@ -436,7 +437,7 @@ public static boolean registerPickupHook(@NotNull PickupCheckCallback callback)
Logger.debug("Registered pickup protection hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register pickup hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register pickup hook", e);
return false;
}
}
@@ -512,7 +513,7 @@ public static boolean registerHammerHook(@NotNull HammerCheckCallback callback)
Logger.debug("Registered hammer protection hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register hammer hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register hammer hook", e);
return false;
}
}
@@ -631,7 +632,7 @@ public static boolean registerExplosionHook(@NotNull ExplosionCheckCallback call
Logger.debug("Registered explosion protection hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register explosion hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register explosion hook", e);
return false;
}
}
@@ -706,7 +707,7 @@ public static boolean registerCommandHook(@NotNull CommandCheckCallback callback
Logger.debug("Registered command protection hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register command hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register command hook", e);
return false;
}
}
@@ -829,7 +830,7 @@ public static boolean registerDeathHook(@NotNull DeathCheckCallback callback) {
Logger.debug("Registered death (keep inventory) hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register death hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register death hook", e);
return false;
}
}
@@ -902,7 +903,7 @@ public static boolean registerDurabilityHook(@NotNull DurabilityCheckCallback ca
Logger.debug("Registered durability protection hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register durability hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register durability hook", e);
return false;
}
}
@@ -975,7 +976,7 @@ public static boolean registerUseHook(@NotNull UseCheckCallback callback) {
Logger.debug("Registered use protection hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register use hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register use hook", e);
return false;
}
}
@@ -1048,7 +1049,7 @@ public static boolean registerSeatHook(@NotNull SeatCheckCallback callback) {
Logger.debug("Registered seat protection hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register seat hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register seat hook", e);
return false;
}
}
@@ -1121,7 +1122,7 @@ public static boolean registerHarvestHook(@NotNull HarvestCheckCallback callback
Logger.debug("Registered harvest protection hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register harvest hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register harvest hook", e);
return false;
}
}
@@ -1279,7 +1280,7 @@ public static boolean registerPlaceHook(@NotNull PlaceCheckCallback callback) {
Logger.debug("Registered place protection hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register place hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register place hook", e);
return false;
}
}
@@ -1352,7 +1353,7 @@ public static boolean registerSpawnHook(@NotNull SpawnCheckCallback callback) {
Logger.debug("Registered spawn control hook");
return true;
} catch (Exception e) {
- Logger.warn("Failed to register spawn hook: %s", e.getMessage());
+ ErrorHandler.report("Failed to register spawn hook", e);
return false;
}
}
diff --git a/src/main/java/com/hyperfactions/manager/AnnouncementManager.java b/src/main/java/com/hyperfactions/manager/AnnouncementManager.java
index 50560a39..1090748b 100644
--- a/src/main/java/com/hyperfactions/manager/AnnouncementManager.java
+++ b/src/main/java/com/hyperfactions/manager/AnnouncementManager.java
@@ -2,6 +2,7 @@
import com.hyperfactions.config.ConfigManager;
import com.hyperfactions.config.modules.AnnouncementConfig;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import com.hyperfactions.util.CommonKeys;
import com.hyperfactions.util.MessageUtil;
@@ -168,7 +169,7 @@ private void broadcast(@NotNull java.util.function.FunctionV2", e);
return false;
}
}
diff --git a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV2ToV3Migration.java b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV2ToV3Migration.java
index ddeb161d..0b96e048 100644
--- a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV2ToV3Migration.java
+++ b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV2ToV3Migration.java
@@ -86,7 +86,7 @@ public boolean isApplicable(@NotNull Path dataDir) {
}
return root.get("configVersion").getAsInt() == 2;
} catch (Exception e) {
- Logger.warn("[Migration] Failed to check config version: %s", e.getMessage());
+ ErrorHandler.report("[Migration] Failed to check config version for V2->V3", e);
return false;
}
}
diff --git a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV3ToV4Migration.java b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV3ToV4Migration.java
index 8ccbd067..e416b045 100644
--- a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV3ToV4Migration.java
+++ b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV3ToV4Migration.java
@@ -106,7 +106,7 @@ public boolean isApplicable(@NotNull Path dataDir) {
}
return root.get("configVersion").getAsInt() == 3;
} catch (Exception e) {
- Logger.warn("[Migration] Failed to check config version: %s", e.getMessage());
+ ErrorHandler.report("[Migration] Failed to check config version for V3->V4", e);
return false;
}
}
diff --git a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV4ToV5Migration.java b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV4ToV5Migration.java
index fbb2a929..69ce2030 100644
--- a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV4ToV5Migration.java
+++ b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV4ToV5Migration.java
@@ -80,7 +80,7 @@ public boolean isApplicable(@NotNull Path dataDir) {
}
return root.get("configVersion").getAsInt() == 4;
} catch (Exception e) {
- Logger.warn("[Migration] Failed to check config version: %s", e.getMessage());
+ ErrorHandler.report("[Migration] Failed to check config version for V4->V5", e);
return false;
}
}
diff --git a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV5ToV6Migration.java b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV5ToV6Migration.java
index c72a336b..5613acb9 100644
--- a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV5ToV6Migration.java
+++ b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV5ToV6Migration.java
@@ -92,7 +92,7 @@ public boolean isApplicable(@NotNull Path dataDir) {
}
return root.get("configVersion").getAsInt() == 5;
} catch (Exception e) {
- Logger.warn("[Migration] Failed to check config version: %s", e.getMessage());
+ ErrorHandler.report("[Migration] Failed to check config version for V5->V6", e);
return false;
}
}
diff --git a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV6ToV7Migration.java b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV6ToV7Migration.java
index 5eeabe6a..1e79f140 100644
--- a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV6ToV7Migration.java
+++ b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV6ToV7Migration.java
@@ -101,7 +101,7 @@ public boolean isApplicable(@NotNull Path dataDir) {
}
return root.get("configVersion").getAsInt() == 6;
} catch (Exception e) {
- Logger.warn("[Migration] Failed to check config version: %s", e.getMessage());
+ ErrorHandler.report("[Migration] Failed to check config version for V6->V7", e);
return false;
}
}
@@ -227,7 +227,7 @@ public MigrationResult execute(@NotNull Path dataDir, @NotNull MigrationOptions
} catch (Exception e) {
warnings.add("Failed to restructure economy.json: " + e.getMessage()
+ " (will be auto-fixed on next save)");
- Logger.warn("[Migration] Failed to restructure economy.json: %s", e.getMessage());
+ ErrorHandler.report("[Migration] Failed to restructure economy.json", e);
}
}
diff --git a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV7ToV8Migration.java b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV7ToV8Migration.java
index e225a825..90d04c6a 100644
--- a/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV7ToV8Migration.java
+++ b/src/main/java/com/hyperfactions/migration/migrations/config/ConfigV7ToV8Migration.java
@@ -86,7 +86,7 @@ public boolean isApplicable(@NotNull Path dataDir) {
}
return root.get("configVersion").getAsInt() == 7;
} catch (Exception e) {
- Logger.warn("[Migration] Failed to check config version: %s", e.getMessage());
+ ErrorHandler.report("[Migration] Failed to check config version for V7->V8", e);
return false;
}
}
diff --git a/src/main/java/com/hyperfactions/platform/EventRegistration.java b/src/main/java/com/hyperfactions/platform/EventRegistration.java
index cbe98bc5..1f643e1b 100644
--- a/src/main/java/com/hyperfactions/platform/EventRegistration.java
+++ b/src/main/java/com/hyperfactions/platform/EventRegistration.java
@@ -17,6 +17,7 @@
import com.hyperfactions.protection.ecs.PvPProtectionSystem;
import com.hyperfactions.protection.ecs.TeleportCancelOnDamageSystem;
import com.hyperfactions.territory.TerritoryTickingSystem;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import com.hypixel.hytale.component.system.ISystem;
import com.hypixel.hytale.event.EventPriority;
@@ -28,7 +29,7 @@
import com.hypixel.hytale.server.core.event.events.player.PlayerReadyEvent;
import com.hypixel.hytale.server.core.universe.world.events.AddWorldEvent;
import com.hypixel.hytale.server.core.universe.world.events.RemoveWorldEvent;
-import java.util.logging.Level;
+
/**
* Handles registration of all event listeners and ECS systems for HyperFactions.
@@ -169,7 +170,7 @@ private void registerBlockProtectionSystems(ProtectionListener protectionListene
Logger.debug("Registered block, item, and player ECS protection systems");
} catch (Exception e) {
- plugin.getLogger().at(Level.WARNING).withCause(e).log("Failed to register block protection systems");
+ ErrorHandler.report("Failed to register block protection systems", e);
}
}
@@ -186,7 +187,7 @@ private void registerHarvestPickupProtection(ProtectionListener protectionListen
plugin.getEntityStoreRegistry().registerSystem(new HarvestPickupProtectionSystem(hyperFactions, protectionListener));
Logger.debug("Registered harvest pickup ECS protection system");
} catch (Exception e) {
- plugin.getLogger().at(Level.WARNING).withCause(e).log("Failed to register harvest pickup protection system");
+ ErrorHandler.report("Failed to register harvest pickup protection system", e);
}
}
@@ -200,7 +201,7 @@ public void registerTeleportSystems() {
Logger.debug("Registered teleport cancel-on-damage ECS system");
} catch (Exception e) {
- plugin.getLogger().at(Level.WARNING).withCause(e).log("Failed to register teleport systems");
+ ErrorHandler.report("Failed to register teleport systems", e);
}
}
@@ -218,7 +219,7 @@ public void registerTerritorySystems() {
Logger.debug("Registered territory ticking ECS system");
} catch (Exception e) {
- plugin.getLogger().at(Level.WARNING).withCause(e).log("Failed to register territory ticking system");
+ ErrorHandler.report("Failed to register territory ticking system", e);
}
}
diff --git a/src/main/java/com/hyperfactions/platform/HyperFactionsPlugin.java b/src/main/java/com/hyperfactions/platform/HyperFactionsPlugin.java
index f4400b5c..e53ac4c9 100644
--- a/src/main/java/com/hyperfactions/platform/HyperFactionsPlugin.java
+++ b/src/main/java/com/hyperfactions/platform/HyperFactionsPlugin.java
@@ -29,7 +29,7 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
+
/**
* Main Hytale plugin class for HyperFactions.
@@ -203,7 +203,7 @@ protected void shutdown() {
hyperFactions.shutdownKyuubiSoftIntegration();
}
} catch (Exception e) {
- getLogger().at(java.util.logging.Level.WARNING).withCause(e).log("Failed to shutdown KyuubiSoft integration");
+ ErrorHandler.report("Failed to shutdown KyuubiSoft integration", e);
}
// Clean up territory ticking system
@@ -301,7 +301,7 @@ private void registerCommands() {
getCommandRegistry().registerCommand(new FactionCommand(hyperFactions, this));
Logger.debug("Registered command: /faction (/f, /hf)");
} catch (Exception e) {
- getLogger().at(Level.SEVERE).withCause(e).log("Failed to register commands");
+ ErrorHandler.report("Failed to register commands", e);
}
}
@@ -337,7 +337,7 @@ private void registerInteractionCodecs() {
Logger.debug("Registered interaction codecs (fluid place/pickup) — crop harvest handled by mixin system");
}
} catch (Exception e) {
- getLogger().at(Level.WARNING).log("Failed to register interaction codecs: %s", e.getMessage());
+ ErrorHandler.report("Failed to register interaction codecs", e);
}
}
diff --git a/src/main/java/com/hyperfactions/platform/WorldSetup.java b/src/main/java/com/hyperfactions/platform/WorldSetup.java
index 8b2321c9..6b2f6d7d 100644
--- a/src/main/java/com/hyperfactions/platform/WorldSetup.java
+++ b/src/main/java/com/hyperfactions/platform/WorldSetup.java
@@ -14,7 +14,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.logging.Level;
+
/**
* Handles world map provider registration, spawn suppression initialization,
@@ -46,7 +46,7 @@ public void registerWorldMapProvider() {
);
Logger.debug("Registered world map provider (ID: %s)", HyperFactionsWorldMapProvider.ID);
} catch (Exception e) {
- plugin.getLogger().at(Level.WARNING).withCause(e).log("Failed to register world map provider");
+ ErrorHandler.report("Failed to register world map provider", e);
}
}
@@ -80,8 +80,6 @@ public void applyToExistingWorlds() {
}
hyperFactions.getWorldMapService().registerProviderIfNeeded(world);
} catch (Exception e) {
- Logger.warn("Failed to register world map for world %s: %s",
- world.getName(), e.getMessage());
ErrorHandler.report("Failed to register world map for world " + world.getName(), e);
}
}
@@ -93,7 +91,6 @@ public void applyToExistingWorlds() {
hyperFactions.getMapPlayerFilterService().applyToAll();
} catch (Exception e) {
- Logger.warn("Failed to apply world map provider to existing worlds: %s", e.getMessage());
ErrorHandler.report("Failed to apply world map provider to existing worlds", e);
}
}
@@ -154,7 +151,7 @@ List applySpawnSuppressionToAllWorlds() {
}
}
} catch (Exception e) {
- Logger.warn("Failed to apply spawn suppression to worlds: %s", e.getMessage());
+ ErrorHandler.report("Failed to apply spawn suppression to worlds", e);
}
return failedWorlds;
}
@@ -168,7 +165,7 @@ public void initializeMobClearing() {
hyperFactions.getZoneMobClearManager().initialize();
Logger.info("[Startup] Mob clearing initialized");
} catch (Exception e) {
- plugin.getLogger().at(Level.WARNING).withCause(e).log("Failed to initialize mob clearing");
+ ErrorHandler.report("Failed to initialize mob clearing", e);
}
}
@@ -194,9 +191,7 @@ public void onWorldAdd(AddWorldEvent event) {
// Apply spawn suppression to the new world
hyperFactions.getSpawnSuppressionManager().applyToWorld(world);
} catch (Exception e) {
- plugin.getLogger().at(Level.WARNING).log("Error in AddWorldEvent handler for %s: %s",
- world.getName(), e.getMessage());
- ErrorHandler.report(String.format("AddWorldEvent error for %s", world.getName()), e);
+ ErrorHandler.report("AddWorldEvent error for " + world.getName(), e);
}
}
@@ -208,8 +203,7 @@ public void onWorldRemove(RemoveWorldEvent event) {
try {
hyperFactions.getWorldMapService().unregisterProvider(world.getName());
} catch (Exception e) {
- plugin.getLogger().at(Level.WARNING).log("Error in RemoveWorldEvent handler for %s: %s",
- world.getName(), e.getMessage());
+ ErrorHandler.report("RemoveWorldEvent error for " + world.getName(), e);
}
}
diff --git a/src/main/java/com/hyperfactions/protection/ecs/PlayerDeathSystem.java b/src/main/java/com/hyperfactions/protection/ecs/PlayerDeathSystem.java
index b9525518..9c96d0d1 100644
--- a/src/main/java/com/hyperfactions/protection/ecs/PlayerDeathSystem.java
+++ b/src/main/java/com/hyperfactions/protection/ecs/PlayerDeathSystem.java
@@ -138,7 +138,7 @@ public void onComponentAdded(@NotNull Ref ref,
return;
}
} catch (Exception e) {
- Logger.warn("Zone check failed for %s, defaulting to no power loss: %s", victimUuid, e.getMessage());
+ ErrorHandler.report("Zone check failed for " + victimUuid + ", defaulting to no power loss", e);
announceDeathLocation(victimUuid, playerRef, store, commandBuffer, ref);
return;
}
diff --git a/src/main/java/com/hyperfactions/storage/StorageUtils.java b/src/main/java/com/hyperfactions/storage/StorageUtils.java
index b7ee6161..5f2193b8 100644
--- a/src/main/java/com/hyperfactions/storage/StorageUtils.java
+++ b/src/main/java/com/hyperfactions/storage/StorageUtils.java
@@ -104,7 +104,7 @@ public static WriteResult writeAtomic(@NotNull Path targetFile, @NotNull String
try {
Files.copy(targetFile, backupFile, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
- Logger.warn("[Storage] Could not create backup for %s: %s", targetFile, e.getMessage());
+ ErrorHandler.report(String.format("[Storage] Could not create backup for %s", targetFile), e);
// Continue anyway - backup is best-effort
}
}
@@ -290,7 +290,7 @@ public static boolean deleteWithBackup(@NotNull Path targetFile) {
try {
mainDeleted = Files.deleteIfExists(targetFile);
} catch (IOException e) {
- Logger.warn("[Storage] Failed to delete %s: %s", targetFile.getFileName(), e.getMessage());
+ ErrorHandler.report(String.format("[Storage] Failed to delete %s", targetFile.getFileName()), e);
}
try {
@@ -298,7 +298,7 @@ public static boolean deleteWithBackup(@NotNull Path targetFile) {
Logger.debug("[Storage] Deleted backup file: %s", backupFile.getFileName());
}
} catch (IOException e) {
- Logger.warn("[Storage] Failed to delete backup %s: %s", backupFile.getFileName(), e.getMessage());
+ ErrorHandler.report(String.format("[Storage] Failed to delete backup %s", backupFile.getFileName()), e);
}
return mainDeleted;
@@ -339,7 +339,7 @@ public static int cleanupOrphanedFiles(@NotNull Path directory) {
cleaned++;
Logger.debug("[Storage] Cleaned orphaned temp file: %s", fileName);
} catch (IOException e) {
- Logger.warn("[Storage] Failed to clean temp file %s: %s", fileName, e.getMessage());
+ ErrorHandler.report(String.format("[Storage] Failed to clean temp file %s", fileName), e);
}
continue;
}
@@ -354,13 +354,13 @@ public static int cleanupOrphanedFiles(@NotNull Path directory) {
cleaned++;
Logger.debug("[Storage] Cleaned orphaned backup file: %s", fileName);
} catch (IOException e) {
- Logger.warn("[Storage] Failed to clean backup file %s: %s", fileName, e.getMessage());
+ ErrorHandler.report(String.format("[Storage] Failed to clean backup file %s", fileName), e);
}
}
}
}
} catch (IOException e) {
- Logger.warn("[Storage] Failed to scan directory for cleanup: %s", e.getMessage());
+ ErrorHandler.report("[Storage] Failed to scan directory for cleanup", e);
}
if (cleaned > 0) {
diff --git a/src/main/java/com/hyperfactions/territory/TerritoryNotifier.java b/src/main/java/com/hyperfactions/territory/TerritoryNotifier.java
index df4857fc..0287c642 100644
--- a/src/main/java/com/hyperfactions/territory/TerritoryNotifier.java
+++ b/src/main/java/com/hyperfactions/territory/TerritoryNotifier.java
@@ -12,6 +12,7 @@
import com.hyperfactions.storage.PlayerStorage;
import com.hyperfactions.territory.TerritoryInfo.TerritoryType;
import com.hyperfactions.util.ChunkUtil;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.universe.PlayerRef;
@@ -186,7 +187,7 @@ private void sendTerritoryNotification(@NotNull PlayerRef playerRef, @NotNull Te
} catch (Exception e) {
// Fallback to chat message if notification fails
- Logger.warn("Failed to send territory notification, falling back to chat: %s", e.getMessage());
+ ErrorHandler.report("Failed to send territory notification, falling back to chat", e);
sendChatFallback(playerRef, territory);
}
}
@@ -209,7 +210,7 @@ private void sendChatFallback(@NotNull PlayerRef playerRef, @NotNull TerritoryIn
playerRef.sendMessage(message);
} catch (Exception e) {
- Logger.warn("Failed to send territory chat fallback: %s", e.getMessage());
+ ErrorHandler.report("Failed to send territory chat fallback", e);
}
}
diff --git a/src/main/java/com/hyperfactions/update/UpdateChecker.java b/src/main/java/com/hyperfactions/update/UpdateChecker.java
index 46d37f04..6c20b84f 100644
--- a/src/main/java/com/hyperfactions/update/UpdateChecker.java
+++ b/src/main/java/com/hyperfactions/update/UpdateChecker.java
@@ -280,7 +280,7 @@ public CompletableFuture downloadUpdate(@NotNull UpdateInfo info) {
Logger.info("[Update:%s] Backed up current JAR to %s", artifactName, backupFile.getFileName());
} catch (java.nio.file.FileSystemException e) {
// Windows locks loaded JARs - can't backup while running
- Logger.warn("[Update:%s] Could not backup old JAR (file in use). Please delete %s manually after restart.", artifactName, currentJar.getFileName());
+ ErrorHandler.report(String.format("[Update:%s] Could not backup old JAR (file in use): %s", artifactName, currentJar.getFileName()), e);
}
}
@@ -326,7 +326,7 @@ public int cleanupOldBackups(@Nullable String keepVersion) {
backupFiles.add(file);
}
} catch (IOException e) {
- Logger.warn("[Update:%s] Failed to list backup files: %s", artifactName, e.getMessage());
+ ErrorHandler.report(String.format("[Update:%s] Failed to list backup files", artifactName), e);
return 0;
}
@@ -368,7 +368,7 @@ public int cleanupOldBackups(@Nullable String keepVersion) {
deleted++;
Logger.info("[Update:%s] Cleanup: Removed old backup %s", artifactName, backupFile.getFileName());
} catch (IOException e) {
- Logger.warn("[Update:%s] Failed to delete backup %s: %s", artifactName, backupFile.getFileName(), e.getMessage());
+ ErrorHandler.report(String.format("[Update:%s] Failed to delete backup %s", artifactName, backupFile.getFileName()), e);
}
}
@@ -481,7 +481,7 @@ public void createRollbackMarker(@NotNull String fromVersion, @NotNull String to
Files.writeString(markerFile, content);
Logger.debug("[Update:%s] Created rollback marker: %s -> %s", artifactName, fromVersion, toVersion);
} catch (IOException e) {
- Logger.warn("[Update:%s] Failed to create rollback marker: %s", artifactName, e.getMessage());
+ ErrorHandler.report(String.format("[Update:%s] Failed to create rollback marker", artifactName), e);
}
}
@@ -496,7 +496,7 @@ public void clearRollbackMarker() {
Logger.debug("[Update:%s] Cleared rollback marker (server restarted with new version)", artifactName);
}
} catch (IOException e) {
- Logger.warn("[Update:%s] Failed to clear rollback marker: %s", artifactName, e.getMessage());
+ ErrorHandler.report(String.format("[Update:%s] Failed to clear rollback marker", artifactName), e);
}
}
@@ -539,7 +539,7 @@ public RollbackInfo getRollbackInfo() {
return new RollbackInfo(fromVersion, toVersion, true);
}
} catch (IOException e) {
- Logger.warn("[Update:%s] Failed to read rollback marker: %s", artifactName, e.getMessage());
+ ErrorHandler.report(String.format("[Update:%s] Failed to read rollback marker", artifactName), e);
}
return null;
@@ -560,7 +560,7 @@ public Path findLatestBackup() {
backupFiles.add(file);
}
} catch (IOException e) {
- Logger.warn("[Update:%s] Failed to list backup files: %s", artifactName, e.getMessage());
+ ErrorHandler.report(String.format("[Update:%s] Failed to list backup files for findLatestBackup", artifactName), e);
return null;
}
diff --git a/src/main/java/com/hyperfactions/update/UpdateNotificationListener.java b/src/main/java/com/hyperfactions/update/UpdateNotificationListener.java
index 957f40e6..48ccf4e0 100644
--- a/src/main/java/com/hyperfactions/update/UpdateNotificationListener.java
+++ b/src/main/java/com/hyperfactions/update/UpdateNotificationListener.java
@@ -4,6 +4,7 @@
import com.hyperfactions.Permissions;
import com.hyperfactions.integration.PermissionManager;
import com.hyperfactions.util.AdminKeys;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.HFMessages;
import com.hyperfactions.util.Logger;
import com.hypixel.hytale.event.EventRegistry;
@@ -95,8 +96,7 @@ private void onPlayerConnect(PlayerConnectEvent event) {
try {
checkAndNotify(playerRef);
} catch (Exception e) {
- Logger.warn("[UpdateNotify] Failed to send notification to %s: %s",
- playerRef.getUsername(), e.getMessage());
+ ErrorHandler.report("[UpdateNotify] Failed to send notification to " + playerRef.getUsername(), e);
}
}, NOTIFICATION_DELAY_MS, TimeUnit.MILLISECONDS);
}
diff --git a/src/main/java/com/hyperfactions/update/UpdateNotificationPreferences.java b/src/main/java/com/hyperfactions/update/UpdateNotificationPreferences.java
index 2f97ab81..d43e179a 100644
--- a/src/main/java/com/hyperfactions/update/UpdateNotificationPreferences.java
+++ b/src/main/java/com/hyperfactions/update/UpdateNotificationPreferences.java
@@ -3,6 +3,7 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
+import com.hyperfactions.util.ErrorHandler;
import com.hyperfactions.util.Logger;
import com.hyperfactions.util.UuidUtil;
import java.io.IOException;
@@ -65,7 +66,7 @@ public void load() {
}
Logger.debug("[UpdatePrefs] Loaded %d preferences", preferences.size());
} catch (IOException e) {
- Logger.warn("[UpdatePrefs] Failed to load preferences: %s", e.getMessage());
+ ErrorHandler.report("[UpdatePrefs] Failed to load preferences", e);
}
}
@@ -80,7 +81,7 @@ public void save() {
Files.writeString(filePath, json, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
Logger.debug("[UpdatePrefs] Saved %d preferences", preferences.size());
} catch (IOException e) {
- Logger.warn("[UpdatePrefs] Failed to save preferences: %s", e.getMessage());
+ ErrorHandler.report("[UpdatePrefs] Failed to save preferences", e);
}
}
diff --git a/src/main/java/com/hyperfactions/util/PlayerDBService.java b/src/main/java/com/hyperfactions/util/PlayerDBService.java
index e19689d4..e02dd66a 100644
--- a/src/main/java/com/hyperfactions/util/PlayerDBService.java
+++ b/src/main/java/com/hyperfactions/util/PlayerDBService.java
@@ -90,7 +90,7 @@ public record PlayerInfo(@NotNull UUID uuid, @NotNull String username) {}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
- Logger.warn("PlayerDB lookup failed for '%s': %s", name, e.getMessage());
+ ErrorHandler.report("PlayerDB lookup failed for '" + name + "'", e);
}
return null;
});
diff --git a/src/main/java/com/hyperfactions/worldmap/MapPlayerFilterService.java b/src/main/java/com/hyperfactions/worldmap/MapPlayerFilterService.java
index f6496bb3..d10610b3 100644
--- a/src/main/java/com/hyperfactions/worldmap/MapPlayerFilterService.java
+++ b/src/main/java/com/hyperfactions/worldmap/MapPlayerFilterService.java
@@ -173,7 +173,7 @@ public void applyFilter(@NotNull Player player) {
cfgShowFactionless, cfgShowFactionlessToFactionless);
} catch (Exception e) {
- Logger.warn("Failed to apply map player filter: %s", e.getMessage());
+ ErrorHandler.report("[MapFilter] Failed to apply map player filter", e);
}
}
@@ -268,7 +268,7 @@ private void updateHiddenPlayers(Player player, PlayerRef viewerRef, UUID viewer
factionHiddenPairs.put(viewerUuid, nowHidden);
}
} catch (Exception e) {
- Logger.warn("Failed to update HiddenPlayersManager for viewer: %s", e.getMessage());
+ ErrorHandler.report("[MapFilter] Failed to update HiddenPlayersManager for viewer", e);
}
}
@@ -289,7 +289,7 @@ private void clearHiddenPlayers(PlayerRef viewerRef) {
hiddenManager.showPlayer(targetUuid);
}
} catch (Exception e) {
- Logger.warn("Failed to clear hidden players for viewer: %s", e.getMessage());
+ ErrorHandler.report("[MapFilter] Failed to clear hidden players for viewer", e);
}
}
@@ -323,8 +323,6 @@ public void applyToAll() {
applyFilter(player);
}
} catch (Exception e) {
- Logger.warn("Error applying map filters in world %s: %s",
- world.getName(), e.getMessage());
ErrorHandler.report("[MapFilter] Error applying filters in world " + world.getName(), e);
}
});
@@ -334,7 +332,6 @@ public void applyToAll() {
}
}
} catch (Exception e) {
- Logger.warn("Error applying map filters to all worlds: %s", e.getMessage());
ErrorHandler.report("[MapFilter] Error applying filters to all worlds", e);
}
}
@@ -393,7 +390,6 @@ public void resetAll() {
Logger.debugWorldMap("[MapFilter] resetAll: cleared filters for %d players in %s",
players.size(), world.getName());
} catch (Exception e) {
- Logger.warn("Error resetting map filters in world: %s", e.getMessage());
ErrorHandler.report("[MapFilter] Error resetting filters in world " + world.getName(), e);
}
});
@@ -403,7 +399,6 @@ public void resetAll() {
}
}
} catch (Exception e) {
- Logger.warn("Error resetting map filters: %s", e.getMessage());
ErrorHandler.report("[MapFilter] Error resetting filters across all worlds", e);
}
diff --git a/src/main/java/com/hyperfactions/worldmap/WorldMapService.java b/src/main/java/com/hyperfactions/worldmap/WorldMapService.java
index 6e8b9dfc..59e4fe7c 100644
--- a/src/main/java/com/hyperfactions/worldmap/WorldMapService.java
+++ b/src/main/java/com/hyperfactions/worldmap/WorldMapService.java
@@ -148,7 +148,6 @@ public void registerProviderIfNeeded(@NotNull World world) {
worldName, currentGeneratorName, betterMapActive);
} catch (Exception e) {
- Logger.warn("Failed to register world map for world %s: %s", worldName, e.getMessage());
ErrorHandler.report("[WorldMap] Failed to register world map for world " + worldName, e);
}
}
@@ -227,7 +226,6 @@ public void refreshWorldMap(@NotNull World world) {
try {
player.getWorldMapTracker().clear();
} catch (Exception e) {
- Logger.warn("Failed to clear world map tracker for player: %s", e.getMessage());
ErrorHandler.report("[WorldMap] Failed to clear world map tracker for player", e);
}
}
@@ -235,7 +233,6 @@ public void refreshWorldMap(@NotNull World world) {
Logger.debugWorldMap("Cleared world map images for world: %s (%d players)",
world.getName(), world.getPlayers().size());
} catch (Exception e) {
- Logger.warn("Failed to refresh world map for world %s: %s", world.getName(), e.getMessage());
ErrorHandler.report("[WorldMap] Failed to refresh world map for world " + world.getName(), e);
}
}
@@ -263,7 +260,6 @@ public void refreshAllWorldMaps() {
}
Logger.debugWorldMap("Refreshed world maps for %d/%d worlds", refreshed, registeredWorlds.size());
} catch (Exception e) {
- Logger.warn("Failed to refresh all world maps: %s", e.getMessage());
ErrorHandler.report("[WorldMap] Failed to refresh all world maps", e);
}
}
@@ -400,13 +396,12 @@ public void reapplySettings() {
try {
player.getWorldMapTracker().sendSettings(world);
} catch (Exception e) {
- Logger.warn("Failed to send map settings to player: %s", e.getMessage());
+ ErrorHandler.report("[WorldMap] Failed to send map settings to player", e);
}
}
Logger.debug("[WorldMap] Reapplied settings for world: %s", worldName);
} catch (Exception e) {
- Logger.warn("Failed to reapply map settings for world %s: %s", worldName, e.getMessage());
ErrorHandler.report("[WorldMap] Failed to reapply settings for world " + worldName, e);
}
}