Files
hytale-gravity-flip/README.md
T
kayjaydee 24ee43b3a3 docs: replace SHOWCASE.md with comprehensive README.md
- Replace portfolio-only SHOWCASE with unified README as sole project entry page
- Add full user guide: commands table, wand flow, region anatomy (all 15 config fields sourced from GravityFlipRegion.CODEC), visual modes, regions.json path
- Add developer section: prerequisites (JDK 25, Hytale API), project structure, build/test/deploy commands, extension snippets
- Add architecture overview with ASCII diagram (tick loop, wand flow, persistence)
- Correct subcommand list (wand/define/list/delete/toggle/tp) to match GravityFlipCommand source
- Preserve showcase content: video placeholder, screenshot grid (HTML table), 12 showcase regions table, feature list
- Add shields.io badges, tech-stack, roadmap, credits, license sections
2026-04-24 17:35:14 +02:00

314 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<h1 align="center">Hytale Gravity Flip</h1>
<p align="center">
<em>Builder-friendly anti-gravity zones for Hytale servers.<br/>
Define a region with a wand, name it, walk on the ceiling — no code, no restarts.</em>
</p>
<p align="center">
<img src="https://img.shields.io/badge/Java-25-orange" alt="Java 25"/>
<img src="https://img.shields.io/badge/Gradle-Shadow-green" alt="Gradle Shadow"/>
<img src="https://img.shields.io/badge/Hytale-2026.03.26-blueviolet" alt="Hytale 2026.03.26"/>
<img src="https://img.shields.io/badge/v1-shipped-brightgreen" alt="v1 shipped"/>
<img src="https://img.shields.io/badge/license-TBD-lightgrey" alt="License"/>
</p>
---
## Showcase
<!-- Replace VIDEO_ID with the YouTube video id once uploaded -->
<p align="center">
<a href="https://www.youtube.com/watch?v=VIDEO_ID">
<img src="https://img.youtube.com/vi/VIDEO_ID/maxresdefault.jpg" alt="Gravity Flip — video demo" width="720"/>
</a>
</p>
<p align="center"><sub>Click to watch on YouTube</sub></p>
### Screenshots
<table>
<tr>
<td><img src="docs/screenshots/01-wand-selection.png" alt="Wand selection on two corners"/></td>
<td><img src="docs/screenshots/02-region-outline.png" alt="Defined region with cyan outline overlay"/></td>
<td><img src="docs/screenshots/03-walk-on-ceiling.png" alt="Player walking on the ceiling"/></td>
</tr>
<tr>
<td><img src="docs/screenshots/04-item-fountain.png" alt="Items rising inside the fountain zone"/></td>
<td><img src="docs/screenshots/05-dense-particles.png" alt="Dense particle visualization"/></td>
<td><img src="docs/screenshots/06-showcase-arena.png" alt="Showcase arena, wide shot"/></td>
</tr>
</table>
---
## Features
- **Builder-first workflow** — define regions entirely in-game with a custom wand. No config editing required for the common case.
- **Six subcommands** — `wand`, `define`, `list`, `delete`, `toggle`, `tp`.
- **Per-UUID wand selection store** — each builder owns their own pair of corners; survives rejoin.
- **Persistent regions** — every zone serialised to `regions.json`, reloaded on boot via the Hytale `Config<T>` API.
- **Two visualization modes** — `Outline` (AABB wireframe) and `Particles` (volumetric emitters on edges) with configurable color, opacity, refresh rate, and density. `None` for production.
- **Per-entity affect toggles** — target players only, items only, NPCs only, or any combination.
- **Configurable physics** — `VerticalForce` (lift strength) and `GracePeriodMs` (smooth entry transition) per region.
- **Optional fall-damage immunity** — suppress fall damage when leaving a zone so demos don't hurt.
- **Tick-loop detection** — efficient AABB containment check each tick (~100 ms), scales to dozens of active regions.
- **First-run bootstrap** — the plugin auto-seeds a demo region on first boot so the mechanic works out of the box.
---
## Quick Start
1. **Install** — drop `hytale-gravity-flip-<version>.jar` into `<HytaleServer>/Server/mods/` and start the server.
2. **Get the wand** — run `/gravityflip wand` in-game. You receive the *Gravity Flip Wand* item.
3. **Pick two corners** — primary-click block A (pos1), secondary-click block B (pos2).
4. **Name the region**`/gravityflip define my_zone`. Gravity inverts inside the box immediately.
5. **Fly up. Congratulations.**
---
## User Guide
### Commands
All commands are sub-commands of `/gravityflip`.
| Command | Description | Example |
| ----------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------- |
| `/gravityflip wand` | Gives the caller a *Gravity Flip Wand* item. | `/gravityflip wand` |
| `/gravityflip define <name>` | Creates a region from the caller's current wand selection (both corners required). | `/gravityflip define arena` |
| `/gravityflip list` | Prints every registered region with its AABB coordinates and enabled state. | `/gravityflip list` |
| `/gravityflip delete <name>` | Removes a region from disk and memory. | `/gravityflip delete arena` |
| `/gravityflip toggle <name>` | Enables or disables a region without deleting its config. | `/gravityflip toggle arena` |
| `/gravityflip tp <name>` | Teleports the caller to the center of a region (handy for large or hidden zones). | `/gravityflip tp arena` |
### The Wand
The *Gravity Flip Wand* is a custom item registered through Hytale's item asset pack and `Interaction.CODEC`.
- **Primary click** on a block → sets **pos1** (min corner candidate).
- **Secondary click** on a block → sets **pos2** (max corner candidate).
- Selection is stored **per player UUID** in an in-memory `WandSelectionStore`. Rejoining the server preserves the selection for the session.
- Once both corners are set, `/gravityflip define <name>` normalizes them into an axis-aligned `Box(min, max)` and persists the region.
### Region Anatomy
Every region is serialised to `regions.json` via `GravityFlipRegion.CODEC`. Only `Name` and `Box` are mandatory; every other field has a sensible default and can be omitted.
| Field | Type | Default | Description |
| ----------------------- | --------- | --------------- | ------------------------------------------------------------------------------------------------ |
| `Name` | string | *(required)* | Unique region identifier used by commands. |
| `Box` | AABB | *(required)* | Axis-aligned box `{ "min": [x,y,z], "max": [x,y,z] }`. Min must be component-wise ≤ Max. |
| `Enabled` | boolean | `true` | If `false`, the region is persisted but dormant (no detection, no visualization). |
| `FallDamage` | boolean | `false` | If `true`, vanilla fall damage is preserved. `false` suppresses fall damage on exit. |
| `GracePeriodMs` | integer | `2500` | Smooth entry transition window (ms). `0` = instant flip. |
| `VerticalForce` | double | `0.1` | Upward force applied per tick. Gentle lift ≈ `0.05`. Launch pad ≈ `0.3`. |
| `AffectPlayers` | boolean | `true` | Flip players inside the region. |
| `AffectNpcs` | boolean | `true` | Flip NPCs / mobs inside the region. |
| `AffectItems` | boolean | `true` | Flip dropped items inside the region. |
| `VisualColor` | string | `#00FFFF` | Hex color for the outline / particle tint. |
| `VisualMode` | string | `Outline` | One of `Outline`, `Particles`, `None`. |
| `VisualRefreshMs` | integer | `1000` | How often the visualization is re-emitted (ms). Lower = smoother, heavier. |
| `VisualOpacity` | double | `0.5` | Overlay opacity, `0.0``1.0`. |
| `VisualParticleId` | string | `Torch_Fire` | Hytale particle asset id used in `Particles` mode. `Dust_Sparkles_Fine` is invisible in-world. |
| `VisualParticleDensity` | double | `0.3` | Particles per edge unit. `1.0` saturates; `0.1` is sparse. |
### Visual Modes
- **`Outline`** — colored AABB wireframe, refreshed every `VisualRefreshMs`. Best for builders and debugging.
- **`Particles`** — particle emitters along the box edges, density and id configurable. Best for gameplay / showcase.
- **`None`** — region is invisible. Use in production once builders are done.
### Configuration File
The region file lives at `<HytaleServer>/Server/mods/Mythlane_GravityFlip/regions.json` and is written by the plugin whenever a command mutates the registry.
- **Hot-reload:** not currently exposed as a subcommand — restart the server (or reload the plugin host) after hand-edits.
- **Auto-save contract:** command handlers save immediately after mutations. External edits must restart.
- **First-run:** if `regions.json` is empty/missing, the plugin seeds one showcase region (`demo-gravity-flip`, box `(0,100,0)..(10,120,10)`, particles on).
---
## Showcase Regions
A set of curated regions ships with the dev server's `regions.json`. They collectively exercise every plugin capability. Spawn is `(0, 81, 0)` on a superflat surface.
| Region | Min | Max | What it demonstrates |
| -------------------------- | ---------------- | ----------------- | --------------------------------------------------------------- |
| `tutorial_walk_on_ceiling` | (10, 81, 10) | (15, 86, 15) | Baseline player-only flip with cyan outline. |
| `item_fountain` | (20, 80, 10) | (23, 100, 13) | Tall vertical zone, items only — drop stuff and watch it rise. |
| `mob_chamber` | (30, 81, 10) | (40, 89, 20) | NPC-only flip with red outline. |
| `full_chaos` | (-15, 81, 10) | (0, 91, 25) | Players + NPCs + items, purple particles. |
| `gentle_lift` | (-40, 80, -20) | (-20, 85, 0) | Soft `VerticalForce=0.05` — slow drift. |
| `strong_launch` | (-15, 80, -15) | (-10, 83, -10) | Aggressive `VerticalForce=0.3` — launch pad. |
| `grace_period_demo` | (15, 81, -20) | (23, 87, -12) | `GracePeriodMs=5000` — smooth fade-in. |
| `no_grace` | (25, 81, -20) | (33, 87, -12) | `GracePeriodMs=0` — instant flip, side-by-side comparison. |
| `fall_damage_off` | (40, 110, 0) | (50, 120, 10) | Elevated zone with `FallDamage=false` — safe exit. |
| `disabled_example` | (-30, 81, 0) | (-25, 86, 5) | `Enabled=false` — persisted but inert, proves state handling. |
| `showcase_arena` | (-80, 81, -80) | (-50, 96, -50) | 30×15×30 gold-outlined arena — hero shot for video. |
| `dense_particles` | (50, 81, 50) | (58, 89, 58) | `VisualParticleDensity=1.0` — maxed visual saturation. |
---
## Installation
```bash
# 1. Build the plugin jar
./gradlew clean shadowJar
# 2. Copy the fat-jar into your Hytale server's mods folder
cp build/libs/hytale-gravity-flip-*.jar "<HytaleServer>/Server/mods/"
# 3. (Re)start the server — the plugin auto-registers its item, interaction, and tick loop.
```
On first boot the plugin creates `Server/mods/Mythlane_GravityFlip/regions.json` and seeds a demo region. Edit in-game via commands, or hand-edit the file and restart.
---
## Development
### Prerequisites
- **JDK 25** (toolchain enforced by Gradle).
- **Hytale Plugin API** — pulled from `https://maven.hytale.com/release` as `com.hypixel.hytale:Server`. Version pinned via `hytaleServerVersion` in `gradle.properties`.
- A local Hytale dev server (for the optional `copyJarToDevServer` deploy task).
### Project Structure
```
src/main/java/com/mythlane/gravityflip/
GravityFlipPlugin.java # Entry point (setup / start / shutdown lifecycle)
command/ # /gravityflip root + 6 sub-commands
config/ # GravityFlipConfig — persisted region list codec
region/ # GravityFlipRegion, RegionRegistry, RegionSnapshot
wand/ # WandSelectionStore + Interaction (CODEC-registered)
tick/ # RegionTickLoop — per-tick AABB detection
physics/ # GravityApplier, FallDamageGuard, FallDamageSuppressorSystem
viz/ # RegionVisualizer + ParticleEdgeEmitter
src/main/resources/
manifest.json # Hytale plugin manifest (filtered by processResources)
Server/Item/Items/
gravityflip_wand.json # Custom wand item asset
Server/Interaction/RootInteractions/
gravityflip_wand_root.json # Wand root interaction definition
src/test/java/... # JUnit 5 unit tests (pure-data stores, codec round-trips)
docs/screenshots/ # Portfolio assets
```
### Build
```bash
# Fat-jar (shadow) with gson relocated to com.mythlane.gravityflip.libs.gson
./gradlew clean shadowJar
# Run tests
./gradlew test
# Build + auto-deploy to the configured dev server
./gradlew shadowJar
# The shadowJar task is finalized by copyJarToDevServer, which copies to:
# C:/Users/minit/Desktop/HYTALE SERVER/Server/mods (default)
# Override with: -PdevServerMods=<path> or set in gradle.properties
# Disable with: -PdevServerMods=disabled
```
### Extending the Plugin
Adding a new subcommand is three lines:
```java
// In GravityFlipCommand.java
this.addSubCommand(new GravityFlipMySubCommand(plugin));
```
Adding a new wand-triggered interaction: register an additional `Interaction.CODEC` entry in `GravityFlipPlugin#setup()` and reference it from your item's `Interactions` block in `src/main/resources/Server/Item/Items/<item>.json`.
Adding a new region field: append a `KeyedCodec<>` entry to `GravityFlipRegion.CODEC` with a setter/getter pair. Absence in existing `regions.json` preserves the Java default (no migration needed).
---
## Architecture
```
+-----------------------+ +--------------------------+
| Player / NPC / Item | | regions.json (Config<T>)|
+-----------+-----------+ +-------------+------------+
| |
| per-tick AABB check | loaded at start()
v v
+-------+--------+ +--------+---------+
| RegionTickLoop | <------------ | RegionRegistry |
+-------+--------+ +------------------+
|
| entity in enabled region?
v
+-------+---------+ +-------------------+
| GravityApplier | ----> | y-velocity invert |
| + FallDamageGrd | | + fall-dmg guard |
+-----------------+ +-------------------+
Wand flow (independent, command-driven):
Player primary/secondary click
|
v
GravityFlipWandInteraction (Interaction.CODEC registered)
|
v
WandSelectionStore (keyed by UUID)
|
v (on /gravityflip define <name>)
RegionRegistry.add(...) -> configHolder.save()
```
Key technical notes:
- **Plugin entrypoint** (`GravityFlipPlugin`) owns the lifecycle and all singletons. Wand interaction and item codec are registered in `setup()`; tick loop and command registry are wired in `start()`.
- **Persistence** uses `Config<GravityFlipConfig>` from the Hytale core API — typed JSON codec, atomic save semantics. The `get()` call is deferred past `setup()` because it blocks on `preLoad()`.
- **Tick loop** runs every ~100 ms with a 2 s startup delay to let `Universe.get()` settle. `World` is resolved lazily each tick, so the tick is a no-op until the world is ready.
- **Wand interaction** is registered through `Interaction.CODEC` under the key `"GravityFlipWand"`, matching the `gravityflip_wand_root.json` root interaction that the item references.
- **Asset pack** is bundled inside the jar (`IncludesAssetPack: true` in `manifest.json`) and includes the wand model, icon, interaction root, and item definition.
- **Concurrency** — `Config<T>#get()` returns a shared mutable reference; `RegionRegistry` snapshots it into an `AtomicReference` for lock-free tick-loop reads.
---
## Tech Stack
- **Java 25** — modern records, pattern matching, sealed types.
- **Gradle 8 + `com.gradleup.shadow` 9.3.1** — fat-jar build with gson relocation.
- **Hytale Plugin API** (`com.hypixel.hytale:Server`) — `JavaPlugin`, `Config<T>`, `Interaction.CODEC`, `BuilderCodec`, `KeyedCodec`, `Universe` / `World` access.
- **Gson 2.10.1** — relocated under `com.mythlane.gravityflip.libs.gson` to avoid classpath clashes.
- **JUnit 5.10** — pure-data unit tests for selection store and codec round-trips.
---
## Roadmap
**v1****shipped** — custom wand, 6 subcommands, outline + particle visualization, per-region physics tuning, per-entity affect toggles, JSON persistence, first-run bootstrap, dev-server auto-deploy task.
**v2 — planned:**
- Hot-reload subcommand (`/gravityflip reload`) without restart.
- HUD indicator when entering/leaving a zone.
- Border visualization improvements (antialiased, beveled corners).
- Multi-world support (regions keyed by world id).
- Custom gravity profiles per region (horizontal push, directional gravity, zero-G).
- Import / export region sets as shareable JSON bundles.
- Permissions integration (who can create / edit / delete).
---
## Credits
Built by the **[Mythlane](https://mythlane.com)** team.
Plugin structure and conventions informed by the `VotePipe` and `myth_*` Hytale plugin family (Kotlin/Java scaffolding, CamelCase manifests, `gradle.properties` versioning).
---
## License
License TBD. Contact **socials@mythlane.com** for usage inquiries.