diff --git a/.local/AGENTS.md b/.local/AGENTS.md deleted file mode 100644 index fac533a..0000000 --- a/.local/AGENTS.md +++ /dev/null @@ -1,27 +0,0 @@ -# AGENTS.md — Arena Mod - -## Mission -Build a server-first NeoForge 1.21.1 PvE arena mod. The mod must remain usable without Cobblemon, EasyNPC, Create, or any boss mod. - -## Working rules -1. `SPEC.md` is the functional source of truth. `ARCHITECTURE.md` is the technical source of truth. -2. Work only on the current unchecked phase in `TODO.md`, unless explicitly asked otherwise. -3. Inspect the existing project and dependency versions before coding. Never invent an API. -4. For optional integrations (Cobblemon/EasyNPC/third-party entities), verify the actual installed API/source before writing integration code. -5. Keep optional integrations isolated; absence of an optional mod MUST NOT prevent startup. -6. Prefer Minecraft/NeoForge registries and resource IDs (`namespace:path`) over hard-coded vanilla lists. -7. Content must be data-driven where specified. Invalid content disables only the affected definition and produces a useful diagnostic; it must not crash the server. -8. Inventory safety is critical. Never clear a player's original inventory until its recovery snapshot has been durably persisted. -9. Avoid unrelated refactors. Keep commits/changes scoped and easy to review. -10. Compile/test after each phase. Fix compilation errors before declaring a phase complete. -11. Add focused tests where practical, especially for pure logic (wave budgeting, rank thresholds, validation, state transitions). -12. If implementation must diverge from these docs, document the reason in `ARCHITECTURE.md` before proceeding. -13. Update `TODO.md` only for work actually completed. - -## Definition of done for a phase -- Project compiles. -- Existing tests still pass. -- New behavior has a minimal test or reproducible manual test procedure. -- No optional dependency became mandatory. -- No known inventory-loss/duplication path was introduced. -- `TODO.md` reflects reality. diff --git a/.local/ARCHITECTURE.md b/.local/ARCHITECTURE.md deleted file mode 100644 index 7ad47ea..0000000 --- a/.local/ARCHITECTURE.md +++ /dev/null @@ -1,81 +0,0 @@ -# Architecture — NeoForge 1.21.1 Arena - -This document fixes responsibilities, not exact Java package names. - -## Modules/components -### ArenaManager -Owns registered arena definitions/controllers and active sessions. V1 may enforce one active arena, but APIs should accept arena/session IDs. - -### ArenaDefinition / ArenaControllerBlockEntity -Persistent spatial configuration: relative bounds, locations, tagged spawn points, water baseline/max, presentation/config metadata. Controller is the coordinate origin. - -### ArenaSession -Runtime state machine. Holds immutable starting roster/config snapshot plus mutable participant states, wave state, lives, tracked entities, environment transaction and reward progress. - -### ParticipantState -UUID, class, ready/eliminated status, remaining arena kit state, reputation progress, recovery reference. - -### InventoryRecoveryService -Durable, idempotent recovery snapshots. Owns save-before-clear, restore-on-exit/login, unfinished-session recovery and anti-duplication state transitions. Keep this isolated and heavily tested. - -### ContentRepository -Loads/reloads/validates JSON definitions: classes, ranks, themes, mob variants, bosses, progression/balance config. Registry-aware validation produces diagnostics and disabled-definition lists. - -### ItemStackCodec / CaptureService -Uses Minecraft's supported ItemStack serialization/data-component mechanisms for target version. In-game capture normalizes durability only, preserves stack count and other components, then writes canonical JSON. - -### WaveDirector -Pure-ish deterministic logic where possible. Inputs: wave index, target budget, eligible themes, recent theme history, player scaling, RNG seed. Output: planned composition. Does not directly spawn entities. - -### SpawnService -Turns planned entries into entities at validated tagged spawn points. Supports equipment, attributes, scale, vehicles/passengers and session tagging. - -### ArenaIsolationService -Event-driven boundary/protection rules: entry/exit, teleport containment, block break/place, explosions, endermen, targeting, projectile crossing, loot/XP, interaction rules. - -### EnvironmentService -Transactional temporary water modifications and restoration. Persist enough recovery information to undo interrupted flooding. - -### Reward/ReputationService -UUID reputation/ranks, wave validation, victory currency, admin configuration. - -### Presentation/UI -Titles/action bar, intuitive registration/class/ready screens or interactions. Player commands are not required. Admin commands remain separate. - -### Optional integrations -Separate compatibility adapters loaded only when the mod is present: -- Cobblemon: block/allow Pokémon actions according to arena rule. -- EasyNPC: optional hooks/commands/API bridge if verified and useful. -No Create dependency. - -## Persistence -Use NeoForge/Minecraft-supported persistent data facilities appropriate to 1.21.1. Do not invent ad-hoc unsafe serialization when native codecs/registries are available. - -Persist at least: -- controller/arena definition; -- reputation/ranks data as appropriate; -- inventory recovery records; -- enough active-session recovery marker to detect an interrupted session; -- temporary environment restoration data while modifications exist. - -## State machine invariants -- Only registered participants may enter participant states. -- Inventory snapshot success precedes inventory clearing. -- Starting roster, average reputation tier, wave count and mode are frozen at countdown start. -- Session-created entities carry session identity. -- Cleanup is idempotent: running it twice must not duplicate rewards/items or damage the world. -- Reward payout is idempotent. -- Recovery is safe after abrupt server termination. - -## Data-driven compatibility -Use ResourceLocations/registries for Item, EntityType, Attribute, enchantment/data component references supported by the target APIs. A definition may reference another mod without compile-time linkage. - -## Networking -Keep authority server-side. Client packets are requests/UI actions only; server validates class unlocks, readiness, arena membership, admin permissions and configuration changes. - -## Performance -- Do not scan the entire arena every tick. -- Track session entities directly. -- Boundary checks should be event-driven where possible; low-frequency sanity checks may repair missed crossings. -- Water changes should be batched/throttled. -- Spawn validation should inspect only candidate locations/entity dimensions. diff --git a/.local/CODEX_PROMPTS.md b/.local/CODEX_PROMPTS.md deleted file mode 100644 index 14c08ea..0000000 --- a/.local/CODEX_PROMPTS.md +++ /dev/null @@ -1,13 +0,0 @@ -# Minimal Codex prompts - -## First run -Read `AGENTS.md`, `SPEC.md`, `ARCHITECTURE.md`, and `TODO.md`. Inspect the repository and implement **Phase 0 only**. Do not implement later phases. Compile/test, fix errors, then update `TODO.md` only for work actually completed. - -## Subsequent run -Implement the next unchecked phase in `TODO.md`. Follow `AGENTS.md`; read only the relevant sections of `SPEC.md` and `ARCHITECTURE.md`, then inspect existing code before editing. Do not implement later phases. Compile/test, fix errors, and update `TODO.md` accurately. - -## Bug-fix run -Investigate this bug against `SPEC.md` and `ARCHITECTURE.md`: . Reproduce or identify the failing path first. Make the smallest safe fix, add a regression test when practical, compile/test, and do not refactor unrelated code. - -## Integration run -Implement only after inspecting the exact installed dependency/API version. Do not guess API names. Keep the integration optional and verify the mod still starts without that dependency. diff --git a/.local/SPEC.md b/.local/SPEC.md deleted file mode 100644 index 6cfdede..0000000 --- a/.local/SPEC.md +++ /dev/null @@ -1,213 +0,0 @@ -# Functional Specification — PvE Arena - -Target: Minecraft Java 1.21.1, NeoForge. Server-first multiplayer PvE arena, initially designed for up to ~4 simultaneous participants. - -## 1. Core principles -- One arena is required for V1, but core code SHOULD avoid an irreversible singleton design. -- A placed **Arena Controller** is the spatial reference for the arena. -- Arena geometry is builder-owned. The mod manages combat/session rules, not arena construction. -- Coordinates for arena points are stored relative to the controller. -- Player-facing flow MUST be intuitive and MUST NOT require player commands. -- Admin commands/tools MAY be used for setup and content editing. -- Vanilla and modded items/entities are referenced by registry IDs. Unknown IDs invalidate only the affected definition. - -## 2. Arena Controller and bounds -The controller stores/configures: -- asymmetric 3D bounding box (`x-`, `x+`, `y-`, `y+`, `z-`, `z+`); -- lobby/hall location; -- participant entry/ready location(s); -- respawn location; -- exit location; -- boss spawn; -- tagged mob spawn points (`GROUND`, `WATER`, optional future tags); -- water baseline/max level; -- optional environment/redstone hooks. - -Admin MUST be able to visualize bounds and configured points using temporary client-visible effects/particles. Bounds MUST be adjustable without rebuilding the arena. - -Spawn validation MUST reject points that cannot fit the entity's actual dimensions (including scale), are obstructed, lack appropriate support/fluid, or are dangerously close to a participant. The director retries another compatible point. - -## 3. Session lifecycle -Nominal states: -`IDLE -> REGISTRATION -> CLASS_SELECTION -> READY -> COUNTDOWN -> WAVES -> BOSS -> VICTORY -> CLEANUP -> IDLE` - -Exceptional endings: -`DEFEAT`, `ABORTED`, `SERVER_RECOVERY` -> `CLEANUP`. - -Flow: -1. Player registers. -2. Original player state is durably snapshotted. -3. Only after successful persistence, arena-controlled inventory/state is applied. -4. Player enters hall and chooses an unlocked class. -5. Player enters ready area and marks Ready. -6. When all registered participants are ready, registration closes and countdown starts. -7. Waves run. -8. Final boss encounter runs. -9. Victory/defeat/abort cleans all arena entities/environment and restores players. - -Late joining after countdown is forbidden. - -## 4. Inventory safety -This is a hard requirement. -- Persist recovery data BEFORE clearing/changing the player's original inventory. -- Snapshot at minimum: main inventory, hotbar, armor, offhand; include other player state only if implementation deliberately modifies it. -- On normal exit/elimination: remove arena kit, restore original snapshot, then mark recovery complete. -- On disconnect/crash: remove player from active session. If restoration cannot happen while offline, restore automatically on next login. -- On server restart with an unfinished session: cancel/recover the session. Online participants are moved outside and restored; offline participants are restored on next login. -- A failed snapshot MUST prevent arena entry and MUST NOT clear inventory. -- Restoration logic MUST be designed to avoid both loss and duplication. - -## 5. Classes -Classes are JSON-defined and include: -- ID/display name; -- required reputation rank; -- armor slots, offhand and inventory; -- optional descriptive metadata. - -Item definitions MUST support full `ItemStack` data/components so vanilla or modded items can retain enchantments, custom name/lore, dyes, banner/shield patterns, armor trims, attributes and other registered data components. - -Admin editing: -- `addItemFromHand` (or equivalent) copies the full held stack into the class JSON. -- Captured stack quantity MUST equal held quantity for stackable items. -- Captured damage/durability MUST be normalized to fully repaired. -- Capture MUST NOT consume the admin's held item. -- Armor/offhand slot capture commands SHOULD exist. -- JSON remains the source of truth; in-game editing updates the JSON and revalidates/reloads the class. -- `/arena class give [player]` gives a copy of the complete class kit for testing/display (e.g. armor racks), without registering a session. - -Class choice locks when the player becomes Ready. Multiple players MAY choose the same class unless later configured otherwise. - -On participant death with lives remaining: -- arena kit does not drop; -- consumed items are NOT restocked; -- durability/remaining stack counts are preserved; -- player respawns in the arena respawn zone with the same remaining kit state. - -## 6. Reputation and ranks -- Reputation is permanent, per-player progression keyed by UUID. -- Currency is separate and spendable. -- Ranks are JSON-defined thresholds, e.g. Recruit/Fighter/Veteran/Champion. -- Classes SHOULD require a rank ID rather than duplicate numeric thresholds. -- Average reputation/rank of the starting group determines the session's configured wave-count tier. This is frozen at session start and never recalculated after disconnect/elimination. -- Reputation is earned per validated wave. -- In clear-to-progress mode, wave N validates when its required mobs are defeated. -- In timed-pressure mode, wave N validates when wave N+2 is reached; victory validates any remaining pending waves. -- Eliminated players keep reputation already earned. - -## 7. Lives and modes -Keep "mode" separate from wave power/budget. - -Default modes: -### Clear-to-progress (easy-style) -- Lives are individual. -- Next wave begins after all required arena mobs for current wave are defeated. -- Wave validation occurs on clear. - -### Timed-pressure (hard-style) -- Lives are shared by the team. -- Each wave starts after a configured timer even if previous-wave enemies remain. -- Wave N reputation validates when N+2 is reached. - -Exact life counts/timers/multipliers are configurable balancing values. - -When a player has no applicable life remaining, they are eliminated: -- removed from session participation; -- teleported outside; -- original inventory restored. -Remaining participants continue. Defeat occurs when no active participants remain (subject to mode rules). - -## 8. Wave Director -Wave difficulty is budget/score-driven, not encoded as `easy/hard` on themes. - -Each theme JSON defines: -- ID/display name; -- selection weight; -- mob/variant pool; -- each entry: cost, weight, optional min/max, compatible spawn tags; -- optional environmental requirement such as water level. - -For each wave: -1. Determine target score/budget from wave progression, player-count scaling and configurable session scaling. -2. Select a valid theme. -3. A theme used recently is excluded for the next 5 waves by default (`themeCooldown`, configurable). -4. If too few themes exist, gracefully relax cooldown using least-recently-used fallback. -5. Compose mobs until near target budget while respecting weights/min/max. -6. Spawn progressively through validated compatible spawn points. - -Player-count scaling SHOULD primarily increase mob budget/count. Health scaling MAY increase modestly and MUST be separately configurable; avoid turning normal mobs into excessive HP sponges. - -At wave start, participants receive a Title: -- title: `Vague ` -- subtitle: theme display name. -Action bar MAY show useful mode-specific status (remaining enemies or next-wave timer). - -## 9. Mob variants, equipment, mounts -Mob definitions MAY include equipment/full item stack components and configurable registered attributes such as max health, attack damage, armor, movement and `minecraft:scale`. - -Composite spawns MUST support vehicles/passengers (e.g. spider jockey, mounted skeleton). Treat a composite as one director selection with one total cost. Limit nesting depth. - -Third-party registered entity IDs MAY be used without a compile-time dependency. Missing entity/item/attribute IDs invalidate the affected definition with diagnostics, not server startup. - -## 10. Bosses -- Wither MAY be the default boss but is not mandatory. -- Boss definitions are data-driven and MAY reference vanilla or modded registered entities. -- Pseudo-bosses MAY apply name, scale, attributes, equipment, boss bar and optional adds/minions. -- Third-party boss compatibility is best-effort; arena containment and cleanup still apply. -- Boss rewards scale with completed session/wave tier. -- Optional boss mods must never become required dependencies. - -## 11. Arena isolation and protection -During an active session, the arena bounding box is a logical sealed boundary. -- Participants cannot leave except through arena-controlled teleport/exit. -- Non-participant players cannot enter. -- Arena mobs cannot leave; outside mobs cannot enter. -- Projectiles/effects must not cross the boundary in either direction where technically interceptable; unsafe crossing projectiles should be cancelled/removed. -- Arena mobs SHOULD NOT target non-participants. -- Endermen cannot move arena blocks and cannot teleport outside. -- Explosions can retain combat effects but MUST NOT destroy arena blocks. -- Participants cannot place or break arena blocks. -- Existing arena interactions remain usable where safe: ladders, slime blocks, doors/trapdoors/buttons, wind charges, etc. -- Arena mobs SHOULD drop no normal loot/XP unless explicitly configured. -- All session-spawned entities (mobs, mounts, passengers, minions, relevant projectiles) are tagged/tracked by session and cleaned on end/recovery. -- Spectator stands may exist immediately outside the bounding box; spectators must not influence combat. - -## 12. Water environment -No dynamic obstacle system in V1. Builders own obstacles. - -Themes MAY request temporary arena flooding by 0..N block layers above a configured water baseline. -- Only safe/replaceable positions may be temporarily filled. -- Never overwrite permanent arena geometry blindly. -- Track every modified position needed for exact restoration. -- Water MAY rise/fall progressively for presentation. -- Restore on theme transition as designed, session end, abort and recovery. -- Recovery MUST handle server restart without leaving permanent unintended flooding. -- `WATER` spawn points become eligible when appropriate. - -## 13. Cobblemon and other integrations -Cobblemon support is OPTIONAL. -- If absent, arena works normally. -- If present, arena rules can allow or block Pokémon use/sending/battling during a session. -- Integration code must be isolated and written only against verified APIs for the installed target version. - -EasyNPC is OPTIONAL presentation/integration only. Core registration/readiness/session behavior must work without it. -Create is NOT a dependency. Builders may use Create/redstone around the arena independently. - -## 14. Rewards and forfeit -- Currency reward is equal per qualifying starting participant; do not create a pot that grows for survivors when teammates are eliminated. -- Default: currency is awarded on final victory; amount depends on completed wave/session tier and configurable scaling. -- Reward item/currency can be configured in-game from a held ItemStack and persisted in configuration. -- Reputation is progression-based as described above. -- Collective forfeit is available through an in-chat vote among active participants. -- Default vote: absolute majority; solo = immediate. Duration/config is configurable. -- Successful forfeit -> ABORTED -> cleanup, exit and inventory restoration; no victory currency. - -## 15. Validation philosophy -Provide useful startup/reload/admin diagnostics for: -- missing registry IDs; -- malformed class/theme/boss definitions; -- impossible min/max/cost values; -- missing required arena points; -- invalid bounds/spawn points; -- incompatible boss size warning. - -Invalid content should be disabled locally whenever safe instead of crashing the server. diff --git a/.local/TODO.md b/.local/TODO.md deleted file mode 100644 index c6b16ec..0000000 --- a/.local/TODO.md +++ /dev/null @@ -1,132 +0,0 @@ -# Implementation Plan - -Do one phase at a time. Keep the project compiling after every phase. - -- [x] **Phase 0 — Bootstrap** - - Confirm exact NeoForge/Minecraft/Java versions and mappings from the project. - - Create minimal mod entrypoint/config/logging. - - Establish packages and basic test setup. - - No gameplay yet. - - Completed: `arena` entrypoint, server config/startup logging, `com.shinuwa.arena` - and `config` packages; removed template gameplay/client examples. - - Versions confirmed: Minecraft 1.21.1, NeoForge 21.1.235, Java 21 - (local JDK 21.0.8), NeoGradle 7.1.38, Gradle 9.2.1, - NeoForm 1.21.1-20240808.144430, Parchment 1.21.1 / 2024.11.17. - - Validation: `./gradlew build test --offline` succeeded; 3 JUnit 5.11.4 - configuration tests passed. Rebuilt and inspected `arena-1.0.0.jar`: - correct metadata, no template content, only Minecraft/NeoForge dependencies. - - README documents build instructions and dedicated/integrated server smoke - tests. Full server/client startup remains a manual check, not executed here. - -- [ ] **Phase 1 — Data model + content loading** - - Resource IDs, ranks, class definitions, themes, mob variants, bosses, balance/progression. - - JSON loading/reload and validation diagnostics. - - Add example built-in/default data. - - Unit-test pure validators where practical. - -- [ ] **Phase 2 — Arena Controller + admin setup** - - Controller block/block entity. - - Relative asymmetric bounding box. - - Configurator/admin commands for bounds and points. - - Particle visualization of bounds/points. - - Tagged spawn points and validation diagnostics. - - Export/import arena definition if cleanly supportable. - -- [ ] **Phase 3 — Inventory recovery foundation** - - Durable snapshot-before-clear transaction. - - Restore and idempotency. - - Restore-on-login for disconnected/offline players. - - Interrupted-session recovery marker. - - Test failure paths before adding full sessions. - -- [ ] **Phase 4 — Session state machine** - - Registration, class selection, ready, countdown, active, victory/defeat/abort, cleanup. - - Starting roster/config freeze. - - Disconnect = remove participant + pending restoration. - - Server restart = cancel/recover. - - No waves yet; use simple admin-driven state transitions for tests. - -- [ ] **Phase 5 — Classes + reputation** - - Class unlock by JSON rank. - - Full kit application. - - Death handling: no drops, preserve remaining kit state, respawn if life remains. - - Elimination restores original inventory. - - `addItemFromHand`/slot capture writes JSON, preserves components/count, repairs durability. - - `/arena class give`. - - Reputation persistence/ranks. - -- [ ] **Phase 6 — Wave Director** - - Budget-driven composition. - - Theme weights, 5-wave default cooldown + graceful fallback. - - Entry cost/weight/min/max. - - Player-count budget scaling and modest configurable health scaling. - - Deterministic tests with seeded RNG. - - Wave Title/subtitle. - -- [ ] **Phase 7 — Spawning + advanced mob variants** - - Spawn-point validation. - - Equipment/components/attributes/scale. - - Vehicles/passengers with nesting limit. - - Session entity tagging/tracking. - - Modded registry entity support without compile dependency. - -- [ ] **Phase 8 — Modes, lives, progression** - - Clear-to-progress individual lives. - - Timed-pressure shared lives. - - N+2 reputation validation for timed mode. - - Wave count tier from frozen group average reputation. - - Action bar status. - - Defeat conditions. - -- [ ] **Phase 9 — Arena isolation** - - Block break/place protection. - - Explosion terrain protection. - - Entity/player boundary entry/exit. - - Enderman restrictions. - - Projectile crossing. - - Targeting isolation. - - Loot/XP suppression. - - Cleanup of all tracked arena entities. - - Verify spectator stands outside bounds cannot influence session. - -- [ ] **Phase 10 — Boss system + rewards** - - Data-driven vanilla/modded boss EntityType. - - Pseudo-boss attributes/scale/equipment/boss bar/adds. - - Victory detection. - - Equal per-player victory currency, scaled by completed tier. - - Held-item currency configuration. - - Idempotent payout. - -- [ ] **Phase 11 — Water environment** - - Configurable baseline/max. - - Safe temporary fill for requested layers. - - Progressive batched rise/fall. - - Exact transactional restoration. - - Crash/restart recovery. - - WATER spawn integration. - -- [ ] **Phase 12 — Player UX + forfeit** - - Intuitive registration/class/ready UX; no player commands required. - - Participant/class/ready recap. - - In-chat forfeit vote and timeout. - - Polish titles/action bar/errors. - - Accessibility/readability pass. - -- [ ] **Phase 13 — Optional Cobblemon integration** - - Verify actual target Cobblemon API first. - - Optional adapter only. - - Configurable allow/block Pokémon use/battles during arena session. - - Test startup with Cobblemon absent and present. - -- [ ] **Phase 14 — Optional EasyNPC bridge** - - Only if useful and actual API is verified. - - NPC may initiate/open arena services but must not own core logic. - - Test startup with EasyNPC absent. - -- [ ] **Phase 15 — Hardening/release** - - Multiplayer tests (1–4 players). - - Disconnect/reconnect, kill server mid-session, abort, repeated cleanup. - - Duplication/loss tests. - - Invalid JSON/mod removed between restarts. - - Performance profiling with high mob counts. - - Admin documentation and example configs. diff --git a/.local/examples/bosses/raider_lord.json b/.local/examples/bosses/raider_lord.json deleted file mode 100644 index 7576b10..0000000 --- a/.local/examples/bosses/raider_lord.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "id": "raider_lord", - "display_name": "Seigneur des Pillards", - "entity": "minecraft:ravager", - "spawn_tag": "BOSS", - "attributes": { - "minecraft:scale": 1.25, - "minecraft:max_health": 180.0, - "minecraft:armor": 12.0, - "minecraft:attack_damage": 16.0 - }, - "boss_bar": true -} diff --git a/.local/examples/classes/warrior.json b/.local/examples/classes/warrior.json deleted file mode 100644 index 8e2b6e9..0000000 --- a/.local/examples/classes/warrior.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "id": "warrior", - "display_name": "Guerrier", - "required_rank": "recruit", - "equipment": { - "head": { - "item": "minecraft:iron_helmet" - }, - "chest": { - "item": "minecraft:iron_chestplate" - }, - "legs": { - "item": "minecraft:iron_leggings" - }, - "feet": { - "item": "minecraft:iron_boots" - }, - "offhand": { - "item": "minecraft:shield" - } - }, - "inventory": [ - { - "item": "minecraft:iron_sword", - "count": 1 - }, - { - "item": "minecraft:cooked_beef", - "count": 16 - } - ] -} diff --git a/.local/examples/progression.json b/.local/examples/progression.json deleted file mode 100644 index 3d1836d..0000000 --- a/.local/examples/progression.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "theme_cooldown": 5, - "party_scaling": { - "1": 1.0, - "2": 1.75, - "3": 2.5, - "4": 3.25 - }, - "health_scaling": { - "1": 1.0, - "2": 1.1, - "3": 1.2, - "4": 1.3 - }, - "reputation_wave_tiers": [ - { - "min_average_reputation": 0, - "wave_count": 10 - }, - { - "min_average_reputation": 500, - "wave_count": 15 - }, - { - "min_average_reputation": 1500, - "wave_count": 20 - } - ], - "note": "Illustrative balancing values; tune during playtesting." -} diff --git a/.local/examples/ranks.json b/.local/examples/ranks.json deleted file mode 100644 index 0c7a828..0000000 --- a/.local/examples/ranks.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "ranks": [ - { - "id": "recruit", - "display_name": "Recrue", - "min_reputation": 0 - }, - { - "id": "fighter", - "display_name": "Combattant", - "min_reputation": 250 - }, - { - "id": "veteran", - "display_name": "Vétéran", - "min_reputation": 750 - }, - { - "id": "champion", - "display_name": "Champion", - "min_reputation": 2000 - } - ] -} diff --git a/.local/examples/themes/aquatic.json b/.local/examples/themes/aquatic.json deleted file mode 100644 index 15e6e86..0000000 --- a/.local/examples/themes/aquatic.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "id": "aquatic", - "display_name": "Les profondeurs", - "weight": 6, - "environment": { - "water_level": 3 - }, - "mobs": [ - { - "entity": "minecraft:drowned", - "cost": 2.0, - "weight": 10, - "min": 2, - "max": 18, - "spawn_tags": [ - "WATER" - ] - }, - { - "entity": "minecraft:guardian", - "cost": 5.0, - "weight": 3, - "min": 0, - "max": 4, - "spawn_tags": [ - "WATER" - ] - } - ] -} diff --git a/.local/examples/themes/cavern.json b/.local/examples/themes/cavern.json deleted file mode 100644 index e6d4e05..0000000 --- a/.local/examples/themes/cavern.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "id": "cavern", - "display_name": "Monstres des cavernes", - "weight": 10, - "environment": { - "water_level": 0 - }, - "mobs": [ - { - "entity": "minecraft:zombie", - "cost": 1.0, - "weight": 10, - "min": 0, - "max": 20, - "spawn_tags": [ - "GROUND" - ] - }, - { - "entity": "minecraft:spider", - "cost": 1.5, - "weight": 8, - "min": 0, - "max": 12, - "spawn_tags": [ - "GROUND" - ] - }, - { - "entity": "minecraft:cave_spider", - "cost": 2.5, - "weight": 4, - "min": 0, - "max": 5, - "spawn_tags": [ - "GROUND" - ] - } - ] -} diff --git a/README.md b/README.md index e9269bc..b421122 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,25 @@ -# Arena of Shinuwa +Installation information +======= -Server-first PvE arena mod. Phase 0 provides only the bootstrap: mod loading, -server configuration, logging and a test setup. No gameplay is implemented yet. +This template repository can be directly cloned to get you started with a new +mod. Simply create a new repository cloned from this one, by following the +instructions provided by [GitHub](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template). -## Development versions +Once you have your clone, simply open the repository in the IDE of your choice. The usual recommendation for an IDE is either IntelliJ IDEA or Eclipse. -Versions retained from the repository: +If at any point you are missing libraries in your IDE, or you've run into problems you can +run `gradlew --refresh-dependencies` to refresh the local cache. `gradlew clean` to reset everything +{this does not affect your code} and then start the process again. -| Component | Version | -| --- | --- | -| Minecraft | 1.21.1 | -| NeoForge | 21.1.235 | -| Java toolchain | 21 | -| Gradle wrapper | 9.2.1 | -| NeoGradle userdev | 7.1.38 | -| NeoForm / official Mojang mappings | 1.21.1-20240808.144430 | -| Parchment mappings | 1.21.1 / 2024.11.17 | -| JUnit Jupiter | 5.11.4 | +Mapping Names: +============ +By default, the MDK is configured to use the official mapping names from Mojang for methods and fields +in the Minecraft codebase. These names are covered by a specific license. All modders should be aware of this +license. For the latest license text, refer to the mapping file itself, or the reference copy here: +https://github.com/NeoForged/NeoForm/blob/main/Mojang.md -The local verification environment uses Java 21.0.8. Mojang mapping names are -subject to [Mojang's mapping license](https://github.com/NeoForged/NeoForm/blob/main/Mojang.md). - -## Layout - -- `src/main/java/com/shinuwa/arena`: common mod entrypoint and logging. -- `src/main/java/com/shinuwa/arena/config`: NeoForge server configuration. -- `src/main/resources/META-INF/neoforge.mods.toml`: mod metadata. -- `src/test/java/com/shinuwa/arena`: JUnit tests. -- `.local`: specification, architecture, phased checklist and reference examples. - -Mod ID: `arena`. Only Minecraft and NeoForge are required. Cobblemon, EasyNPC, -Create and boss mods are not dependencies. Further component packages will be -introduced with their implementation phases in `.local/TODO.md`. - -## Build and test - -Use JDK 21 and the checked-in wrapper: - -```sh -./gradlew build test -``` - -The mod JAR is written to `build/libs/arena-1.0.0.jar`. Tests check configuration -defaults, invalid-value correction and preservation of an explicit opt-out. -The existing GitHub Actions build runs the Gradle `build` checks as well. - -## Manual bootstrap smoke test - -1. Run `./gradlew runServer` with no optional mods installed. On first launch, - review Minecraft's EULA and, if you agree, accept it in `run/server/eula.txt`, - then run the command again. -2. Wait for the server's `Done` message. Confirm the log contains - `Arena of Shinuwa initialized` and `Arena bootstrap ready on Minecraft 1.21.1`. -3. Confirm `run/server/world/serverconfig/arena-server.toml` contains - `logServerStartup = true`. Stop the server using `stop`. -4. Set that option to `false` and restart. The initialization log should remain, - and the `Arena bootstrap ready` message should be absent. Stop the server. -5. Run `./gradlew runClient`, then create a single-player world to repeat the - startup check on an integrated server. Its config is under that world's - `serverconfig` directory. No example blocks, items or creative tab should - appear under this mod. - -These are manual procedures; passing compilation/unit tests alone does not -verify a graphical client or a complete server startup. +Additional Resources: +========== +Community Documentation: https://docs.neoforged.net/ +NeoForged Discord: https://discord.neoforged.net/ diff --git a/build.gradle b/build.gradle index 6cc3ab4..31616d8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java-library' id 'maven-publish' - id 'net.neoforged.gradle.userdev' version '7.1.38' + id 'net.neoforged.gradle.userdev' version '7.1.26' } tasks.named('wrapper', Wrapper).configure { @@ -26,7 +26,6 @@ sourceSets.main.resources { } repositories { - mavenCentral() // Add here additional repositories if required by some of the dependencies below. } @@ -105,10 +104,6 @@ dependencies { // For all intends and purposes: You can treat this dependency as if it is a normal library you would use. implementation "net.neoforged:neoforge:${neo_version}" - testImplementation platform('org.junit:junit-bom:5.11.4') - testImplementation 'org.junit.jupiter:junit-jupiter' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - // Example optional mod dependency with JEI // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" @@ -172,15 +167,6 @@ tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation } -tasks.named('test', Test).configure { - useJUnitPlatform() - // Keep NeoForge/Log4j test output inside the ignored build directory. - workingDir = layout.buildDirectory.dir('test-work').get().asFile - doFirst { - workingDir.mkdirs() - } -} - // IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior. idea { module { diff --git a/gradle.properties b/gradle.properties index 80be1ba..698e84c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ minecraft_version=1.21.1 # as they do not follow standard versioning conventions. minecraft_version_range=[1.21.1] # The Neo version must agree with the Minecraft version to get a valid artifact -neo_version=21.1.235 +neo_version=21.1.228 # The loader version range can only use the major version of FML as bounds loader_version_range=[1,) @@ -26,9 +26,9 @@ loader_version_range=[1,) # The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} # Must match the String constant located in the main mod class annotated with @Mod. -mod_id=arena +mod_id=examplemod # The human-readable display name for the mod. -mod_name=Arena of Shinuwa +mod_name=Example Mod # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=All Rights Reserved # The mod version. See https://semver.org/ @@ -36,4 +36,4 @@ mod_version=1.0.0 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html -mod_group_id=com.shinuwa.arena +mod_group_id=com.example.examplemod diff --git a/src/main/java/com/example/examplemod/Config.java b/src/main/java/com/example/examplemod/Config.java new file mode 100644 index 0000000..358706c --- /dev/null +++ b/src/main/java/com/example/examplemod/Config.java @@ -0,0 +1,42 @@ +package com.example.examplemod; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.config.ModConfigEvent; +import net.neoforged.neoforge.common.ModConfigSpec; + +// An example config class. This is not required, but it's a good idea to have one to keep your config organized. +// Demonstrates how to use Neo's config APIs +public class Config { + private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); + + public static final ModConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER + .comment("Whether to log the dirt block on common setup") + .define("logDirtBlock", true); + + public static final ModConfigSpec.IntValue MAGIC_NUMBER = BUILDER + .comment("A magic number") + .defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE); + + public static final ModConfigSpec.ConfigValue MAGIC_NUMBER_INTRODUCTION = BUILDER + .comment("What you want the introduction message to be for the magic number") + .define("magicNumberIntroduction", "The magic number is... "); + + // a list of strings that are treated as resource locations for items + public static final ModConfigSpec.ConfigValue> ITEM_STRINGS = BUILDER + .comment("A list of items to log on common setup.") + .defineListAllowEmpty("items", List.of("minecraft:iron_ingot"), () -> "", Config::validateItemName); + + static final ModConfigSpec SPEC = BUILDER.build(); + + private static boolean validateItemName(final Object obj) { + return obj instanceof String itemName && BuiltInRegistries.ITEM.containsKey(ResourceLocation.parse(itemName)); + } +} diff --git a/src/main/java/com/example/examplemod/ExampleMod.java b/src/main/java/com/example/examplemod/ExampleMod.java new file mode 100644 index 0000000..db1e6de --- /dev/null +++ b/src/main/java/com/example/examplemod/ExampleMod.java @@ -0,0 +1,117 @@ +package com.example.examplemod; + +import org.slf4j.Logger; + +import com.mojang.logging.LogUtils; + +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.world.food.FoodProperties; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.CreativeModeTabs; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.material.MapColor; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.config.ModConfig; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; +import net.neoforged.neoforge.event.server.ServerStartingEvent; +import net.neoforged.neoforge.registries.DeferredBlock; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredItem; +import net.neoforged.neoforge.registries.DeferredRegister; + +// The value here should match an entry in the META-INF/neoforge.mods.toml file +@Mod(ExampleMod.MODID) +public class ExampleMod { + // Define mod id in a common place for everything to reference + public static final String MODID = "examplemod"; + // Directly reference a slf4j logger + public static final Logger LOGGER = LogUtils.getLogger(); + // Create a Deferred Register to hold Blocks which will all be registered under the "examplemod" namespace + public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(MODID); + // Create a Deferred Register to hold Items which will all be registered under the "examplemod" namespace + public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID); + // Create a Deferred Register to hold CreativeModeTabs which will all be registered under the "examplemod" namespace + public static final DeferredRegister CREATIVE_MODE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID); + + // Creates a new Block with the id "examplemod:example_block", combining the namespace and path + public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerSimpleBlock("example_block", BlockBehaviour.Properties.of().mapColor(MapColor.STONE)); + // Creates a new BlockItem with the id "examplemod:example_block", combining the namespace and path + public static final DeferredItem EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem("example_block", EXAMPLE_BLOCK); + + // Creates a new food item with the id "examplemod:example_id", nutrition 1 and saturation 2 + public static final DeferredItem EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item", new Item.Properties().food(new FoodProperties.Builder() + .alwaysEdible().nutrition(1).saturationModifier(2f).build())); + + // Creates a creative tab with the id "examplemod:example_tab" for the example item, that is placed after the combat tab + public static final DeferredHolder EXAMPLE_TAB = CREATIVE_MODE_TABS.register("example_tab", () -> CreativeModeTab.builder() + .title(Component.translatable("itemGroup.examplemod")) //The language key for the title of your CreativeModeTab + .withTabsBefore(CreativeModeTabs.COMBAT) + .icon(() -> EXAMPLE_ITEM.get().getDefaultInstance()) + .displayItems((parameters, output) -> { + output.accept(EXAMPLE_ITEM.get()); // Add the example item to the tab. For your own tabs, this method is preferred over the event + }).build()); + + // The constructor for the mod class is the first code that is run when your mod is loaded. + // FML will recognize some parameter types like IEventBus or ModContainer and pass them in automatically. + public ExampleMod(IEventBus modEventBus, ModContainer modContainer) { + // Register the commonSetup method for modloading + modEventBus.addListener(this::commonSetup); + + // Register the Deferred Register to the mod event bus so blocks get registered + BLOCKS.register(modEventBus); + // Register the Deferred Register to the mod event bus so items get registered + ITEMS.register(modEventBus); + // Register the Deferred Register to the mod event bus so tabs get registered + CREATIVE_MODE_TABS.register(modEventBus); + + // Register ourselves for server and other game events we are interested in. + // Note that this is necessary if and only if we want *this* class (ExampleMod) to respond directly to events. + // Do not add this line if there are no @SubscribeEvent-annotated functions in this class, like onServerStarting() below. + NeoForge.EVENT_BUS.register(this); + + // Register the item to a creative tab + modEventBus.addListener(this::addCreative); + + // Register our mod's ModConfigSpec so that FML can create and load the config file for us + modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC); + } + + private void commonSetup(FMLCommonSetupEvent event) { + // Some common setup code + LOGGER.info("HELLO FROM COMMON SETUP"); + + if (Config.LOG_DIRT_BLOCK.getAsBoolean()) { + LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT)); + } + + LOGGER.info("{}{}", Config.MAGIC_NUMBER_INTRODUCTION.get(), Config.MAGIC_NUMBER.getAsInt()); + + Config.ITEM_STRINGS.get().forEach((item) -> LOGGER.info("ITEM >> {}", item)); + } + + // Add the example block item to the building blocks tab + private void addCreative(BuildCreativeModeTabContentsEvent event) { + if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS) { + event.accept(EXAMPLE_BLOCK_ITEM); + } + } + + // You can use SubscribeEvent and let the Event Bus discover methods to call + @SubscribeEvent + public void onServerStarting(ServerStartingEvent event) { + // Do something when the server starts + LOGGER.info("HELLO from server starting"); + } +} diff --git a/src/main/java/com/example/examplemod/ExampleModClient.java b/src/main/java/com/example/examplemod/ExampleModClient.java new file mode 100644 index 0000000..bf9b4c6 --- /dev/null +++ b/src/main/java/com/example/examplemod/ExampleModClient.java @@ -0,0 +1,31 @@ +package com.example.examplemod; + +import net.minecraft.client.Minecraft; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.neoforge.client.gui.ConfigurationScreen; +import net.neoforged.neoforge.client.gui.IConfigScreenFactory; + +// This class will not load on dedicated servers. Accessing client side code from here is safe. +@Mod(value = ExampleMod.MODID, dist = Dist.CLIENT) +// You can use EventBusSubscriber to automatically register all static methods in the class annotated with @SubscribeEvent +@EventBusSubscriber(modid = ExampleMod.MODID, value = Dist.CLIENT) +public class ExampleModClient { + public ExampleModClient(ModContainer container) { + // Allows NeoForge to create a config screen for this mod's configs. + // The config screen is accessed by going to the Mods screen > clicking on your mod > clicking on config. + // Do not forget to add translations for your config options to the en_us.json file. + container.registerExtensionPoint(IConfigScreenFactory.class, ConfigurationScreen::new); + } + + @SubscribeEvent + static void onClientSetup(FMLClientSetupEvent event) { + // Some client setup code + ExampleMod.LOGGER.info("HELLO FROM CLIENT SETUP"); + ExampleMod.LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName()); + } +} diff --git a/src/main/java/com/shinuwa/arena/ArenaMod.java b/src/main/java/com/shinuwa/arena/ArenaMod.java deleted file mode 100644 index c6f02c3..0000000 --- a/src/main/java/com/shinuwa/arena/ArenaMod.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.shinuwa.arena; - -import com.mojang.logging.LogUtils; -import com.shinuwa.arena.config.ArenaConfig; -import net.neoforged.fml.ModContainer; -import net.neoforged.fml.common.Mod; -import net.neoforged.fml.config.ModConfig; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.server.ServerStartedEvent; -import org.slf4j.Logger; - -/** Common bootstrap, safe to load on both dedicated and integrated servers. */ -@Mod(ArenaMod.MOD_ID) -public final class ArenaMod { - public static final String MOD_ID = "arena"; - public static final Logger LOGGER = LogUtils.getLogger(); - - public ArenaMod(ModContainer container) { - container.registerConfig(ModConfig.Type.SERVER, ArenaConfig.SPEC); - NeoForge.EVENT_BUS.addListener(this::onServerStarted); - LOGGER.info("Arena of Shinuwa initialized"); - } - - private void onServerStarted(ServerStartedEvent event) { - // SERVER configuration is loaded by this lifecycle stage. - if (ArenaConfig.LOG_SERVER_STARTUP.getAsBoolean()) { - LOGGER.info("Arena bootstrap ready on Minecraft {}", event.getServer().getServerVersion()); - } - } -} diff --git a/src/main/java/com/shinuwa/arena/config/ArenaConfig.java b/src/main/java/com/shinuwa/arena/config/ArenaConfig.java deleted file mode 100644 index 3b77cbc..0000000 --- a/src/main/java/com/shinuwa/arena/config/ArenaConfig.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.shinuwa.arena.config; - -import net.neoforged.neoforge.common.ModConfigSpec; - -/** World-specific server configuration; gameplay settings belong to later phases. */ -public final class ArenaConfig { - public static final ModConfigSpec SPEC; - public static final ModConfigSpec.BooleanValue LOG_SERVER_STARTUP; - - static { - ModConfigSpec.Builder builder = new ModConfigSpec.Builder(); - LOG_SERVER_STARTUP = builder - .comment("Log when the arena bootstrap is ready after the server starts.") - .define("logServerStartup", true); - SPEC = builder.build(); - } - - private ArenaConfig() {} -} diff --git a/src/main/java/com/shinuwa/arena/package-info.java b/src/main/java/com/shinuwa/arena/package-info.java deleted file mode 100644 index d908618..0000000 --- a/src/main/java/com/shinuwa/arena/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Server-first arena mod bootstrap. Gameplay components will be introduced in - * their corresponding phases, following .local/ARCHITECTURE.md. - */ -package com.shinuwa.arena; diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index ed5cede..cc5e2d2 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -36,7 +36,7 @@ displayName="${mod_name}" #mandatory #displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional # A file name (in the root of the mod JAR) containing a logo for display -#logoFile="arena.png" #optional +#logoFile="examplemod.png" #optional # A text field displayed in the mod UI #credits="" #optional @@ -46,7 +46,7 @@ displayName="${mod_name}" #mandatory # The description text for the mod (multi line!) (#mandatory) description=''' -Server-first PvE arena mod. Currently provides the bootstrap foundation only. +Example mod description. ''' # The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. diff --git a/src/main/resources/assets/examplemod/lang/en_us.json b/src/main/resources/assets/examplemod/lang/en_us.json new file mode 100644 index 0000000..4f93bf6 --- /dev/null +++ b/src/main/resources/assets/examplemod/lang/en_us.json @@ -0,0 +1,13 @@ +{ + "itemGroup.examplemod": "Example Mod Tab", + "block.examplemod.example_block": "Example Block", + "item.examplemod.example_item": "Example Item", + + "examplemod.configuration.title": "Example Mod Configs", + "examplemod.configuration.section.examplemod.common.toml": "Example Mod Configs", + "examplemod.configuration.section.examplemod.common.toml.title": "Example Mod Configs", + "examplemod.configuration.items": "Item List", + "examplemod.configuration.logDirtBlock": "Log Dirt Block", + "examplemod.configuration.magicNumberIntroduction": "Magic Number Text", + "examplemod.configuration.magicNumber": "Magic Number" +} diff --git a/src/test/java/com/shinuwa/arena/config/ArenaConfigTest.java b/src/test/java/com/shinuwa/arena/config/ArenaConfigTest.java deleted file mode 100644 index be28f47..0000000 --- a/src/test/java/com/shinuwa/arena/config/ArenaConfigTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.shinuwa.arena.config; - -import com.electronwill.nightconfig.core.CommentedConfig; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class ArenaConfigTest { - @Test - void missingConfigurationGetsValidDefaults() { - CommentedConfig config = CommentedConfig.inMemory(); - - ArenaConfig.SPEC.correct(config); - - assertEquals(Boolean.TRUE, config.get("logServerStartup")); - assertTrue(ArenaConfig.SPEC.isCorrect(config)); - } - - @Test - void disabledStartupLoggingSurvivesCorrection() { - CommentedConfig config = CommentedConfig.inMemory(); - config.set("logServerStartup", false); - - ArenaConfig.SPEC.correct(config); - - assertEquals(Boolean.FALSE, config.get("logServerStartup")); - assertTrue(ArenaConfig.SPEC.isCorrect(config)); - } - - @Test - void invalidStartupLoggingValueIsRepaired() { - CommentedConfig config = CommentedConfig.inMemory(); - config.set("logServerStartup", 42); - - ArenaConfig.SPEC.correct(config); - - assertEquals(Boolean.TRUE, config.get("logServerStartup")); - assertTrue(ArenaConfig.SPEC.isCorrect(config)); - } -}