feat(hytale): add HytaleDemoGrid component and demo data

- Introduced HytaleDemoGrid.vue to showcase live Hytale plugins with a responsive layout.
- Created hytaleDemos.ts to manage demo data, including details for VotePipe and GravityFlip plugins.
- Updated Hytale page to include the new demo grid section.
- Enhanced AppFooter and ServicesSection with i18n support for better localization.
- Added new blog post detailing the development process of the GravityFlip plugin, available in both English and French.

This commit enhances the visibility of Hytale plugins and improves the overall user experience on the site.
This commit is contained in:
2026-04-25 15:39:53 +02:00
parent 1282d0e350
commit bc0b0ea01d
13 changed files with 646 additions and 40 deletions
@@ -0,0 +1,138 @@
---
title: "GravityFlip: from client brief to a production-grade Hytale plugin"
description: "Lessons learned shipping GravityFlip — how a vague request (\"I want to flip gravity\") became an architected, configurable Hytale plugin that builders can use without touching code."
date: "2026-04-25"
tags: ["hytale", "case-study", "gravity-flip", "consulting"]
draft: false
---
> **TL;DR** — A client pinged me to "invert gravity in part of my map". Five questions later we were building a **multi-region plugin** with an in-game wand, JSON persistence, and three visualization modes. It's now live in production: [GravityFlip on Modtale](https://modtale.net/mod/gravity-flip).
## The original brief
The Discord message ran two sentences long:
> *"Hi, I'd like a plugin that flips gravity on a zone of my server. It's for an event this weekend, I can pay."*
This is exactly the most dangerous brief shape. Read fast, it sounds clear (*"flip gravity"* + *"zone"* — that's enough to start coding, right?). Read carefully, it says **nothing** specific:
- *"a zone"* — how many? one, configured via YAML? several, managed in-game? server-wide?
- *"flip gravity"* — for whom? players only? falling items? mobs? projectiles?
- *"for an event this weekend"* — one-off, or a reusable tool for the server's builders later?
- *"I can pay"* — what scope, what budget? Essential plugin at €149 or full system at €349?
I've made it a rule to **never write code until I've exhausted ambiguity**. It feels counter-intuitive when the client is in a hurry, but 30 minutes of questions saves 10 hours of rewrites.
## The five questions that changed everything
### 1. "How many zones, and who defines them?"
Answer: *"At first I thought just one, but actually I'd like my builders to create more without asking me each time."*
Immediate decision: **multi-region system** with persistence. Hardcoded YAML is out (too much friction). We pivot to an **in-game wand** plus `/gravityflip define <name>` commands. Classic Bukkit/Spigot community pattern, ported to Hytale.
### 2. "Who flips? Players only, or everything?"
Answer: *"Players for sure, but flipping dropped items would be sweet too. Mobs I'm not sure."*
Decision: **three booleans per region**`AffectPlayers`, `AffectItems`, `AffectNpcs`. Default: all on, but the builder can disable mobs on a "jump arena" zone if floating mobs ruin the gameplay. The marginal cost of those three toggles in the JSON codec was zero — optionality offered for free.
### 3. "What happens when a player enters the zone at full free-fall speed?"
Answer (after a pause): *"Uhh... I hadn't thought about it. They shouldn't take fall damage, right?"*
Decision: **configurable `GracePeriodMs` (default 2500ms)**. During the transition, we smooth the gravity flip instead of an instant binary swap that produces brutal acceleration and absurd fall damage. Bonus: a `FallDamage` toggle (default false) for zones where falling should still cost something gameplay-wise.
That's the kind of detail **a written brief never surfaces**. A real conversation does.
### 4. "Do you want to see the zones when you're inside one?"
Answer: *"Yeah in build mode I have to, otherwise I lose track. But in player mode nothing should show."*
Decision: **three visualization modes**.
- `Outline` — configurable wireframe color (`VisualColor`, default `#00FFFF`) — build mode
- `Particles` — edge-emitting particles (`Torch_Fire` default), more subtle but visible
- `None` — invisible, production mode
Per-region toggle via the `/gravityflip toggle` command. Build and live mode coexist on the same map without re-deploying the plugin.
### 5. "Is this one-shot or are you reusing it?"
Answer: *"One-shot for the event, but if it's well done I'll keep it."*
Pivotal decision: we treat this project as **a production plugin**, not a script. Concretely:
- JSON persistence in `Server/mods/Mythlane_GravityFlip/regions.json` (not memory-only)
- 10 Hz tick loop with concurrent snapshots (lock-free reads)
- Unit tests on pure logic (codec, AABB geometry)
- Auto-seeded demo region on first run for instant onboarding
- Polished EN README, distributable
**Cost vs "quick & dirty"** : about 30 % more time. **Benefit** : the client moved from "I need it by Saturday" to "I still use it, my builders love it". The plugin is now publishable, monetizable, and so it became a shared asset.
## The architecture that emerged
```
Player / NPC / Item
▼ (each tick, 10 Hz)
RegionTickLoop ◄─── snapshots from regions.json
GravityApplier + FallDamageGuard
per-region effect
```
The wand follows its own cycle:
```
Player click → WandSelectionStore
/gravityflip define <name>
Region registry → regions.json (auto-save)
```
Stack: **Java 25**, official Hytale Plugin API (`com.hypixel.hytale.plugin`), Gradle Shadow to relocate Gson (avoiding stdlib conflicts), JUnit 5 for tests. Source ~2,500 lines, 30 % of which is tests.
### The piece of code that took the most thinking
The **GracePeriodMs**. Not the code itself but the idea behind it: instead of a binary flip, we interpolate the vertical velocity component over a sliding window. Naively:
```java
// naive version — produces absurd fall damage
if (region.contains(entity)) {
entity.velocity.y = -entity.velocity.y; // instant flip
}
```
With a grace period, smooth transition:
```java
// production version — gradual entry
final long timeInRegion = now - entry.enteredAt();
final double gracePct = Math.min(1.0, timeInRegion / region.gracePeriodMs());
final double targetVy = -region.verticalForce(); // antigrav
entity.velocity.y = lerp(entity.velocity.y, targetVy, gracePct);
```
Three extra lines, but that's what makes the difference between a plugin that's "fun for 30 seconds" and a plugin that players actually use without rage-quitting.
## What this project taught me (or reminded me)
**First**: senior dev value isn't in code-typing speed. It's in the **series of questions** that turn a vague brief into an actionable spec. Without question 3 (free-fall), the client would have shipped the event with absurd fall damage, and the plugin would've been dropped after the weekend.
**Second**: a Hytale plugin that sells isn't a script. It's a **production-grade Java codebase** with persistence, tests, docs, and sub-5-minute onboarding. The client paid €349 (the "Custom System" tier), not €50 Fiverr — and the premium is justified by the quality the client will exploit for months.
**Third**: every plugin I write ends up published. GravityFlip is freely available on [Modtale](https://modtale.net/mod/gravity-flip) and soon on CurseForge. This doesn't dilute the value of paid commissions — it **boosts** my credibility to future prospects looking for a developer who can ship clean code, not just glue StackOverflow snippets.
## Got a Hytale project in mind?
The pattern is always the same: we talk for 30 minutes, I ask the 5-10 questions that kill ambiguity, I send a firm quote, I deliver. Pricing is public on [/hytale](/hytale) — €149 for an essential plugin, €349 for a custom system, €790+ for custom MMO infrastructure.
No Fiverr, no race-to-the-bottom. Just a senior dev shipping code you'll still use six months later.
[Request a quote](/contact) · [See GravityFlip in action](https://modtale.net/mod/gravity-flip) · [See my other plugins](/projects)
@@ -0,0 +1,138 @@
---
title: "GravityFlip : du brief client au plugin Hytale en production"
description: "Retour d'expérience sur le développement de GravityFlip — comment une demande floue (\"je veux inverser la gravité\") devient un plugin Hytale architecturé, configurable, et utilisable par des builders sans toucher au code."
date: "2026-04-25"
tags: ["hytale", "case-study", "gravity-flip", "consulting"]
draft: false
---
> **TL;DR** — Un client m'a contacté pour "inverser la gravité dans une partie de la map". Cinq questions plus tard, on était sur un plugin de **régions paramétrables** avec wand in-game, persistence JSON, et trois modes de visualisation. Le résultat tourne aujourd'hui en production : [GravityFlip sur Modtale](https://modtale.net/mod/gravity-flip).
## Le brief initial
Le message Discord d'origine tient en deux phrases :
> *"Salut, j'aimerais un plugin qui inverse la gravité sur une zone de mon serveur. C'est pour un event ce week-end, je peux payer."*
C'est exactement la forme de brief la plus dangereuse. Lue rapidement, elle donne l'illusion d'être claire (*"inverser la gravité"* + *"zone"* = ça suffit pour coder, non ?). Lue posément, elle ne dit **rien** de précis :
- *"une zone"* — combien ? une seule, configurée par YAML ? plusieurs, gérées en jeu ? globale au serveur ?
- *"inverser la gravité"* — pour qui ? les joueurs uniquement ? les items qui tombent ? les mobs ? les projectiles ?
- *"pour un event ce week-end"* — usage one-shot ou outil réutilisable par les builders du serveur après ?
- *"je peux payer"* — quel scope, quel budget ? Plugin essentiel à 149€ ou système complet à 349€ ?
J'ai pris l'habitude de **ne jamais coder avant d'avoir épuisé les ambiguïtés**. Ça paraît contre-intuitif quand le client est pressé, mais 30 minutes de questions économisent 10 heures de réécriture.
## Les cinq questions qui ont tout changé
### 1. "Combien de zones, et qui les définit ?"
Réponse : *"Au début je pensais une seule, mais en fait j'aimerais que mes builders puissent en créer plusieurs sans me demander à chaque fois."*
Décision immédiate : **système multi-régions** avec persistence. On exclut le YAML hardcodé (trop friction) et on s'oriente vers un **wand in-game** + commandes `/gravityflip define <name>`. Pattern classique de la communauté Bukkit/Spigot, repris pour Hytale.
### 2. "Pour qui la gravité s'inverse ? Joueurs uniquement, ou tout ?"
Réponse : *"Les joueurs ouais, mais aussi les items dropés ce serait stylé. Les mobs je sais pas."*
Décision : **trois booleans configurables par région**`AffectPlayers`, `AffectItems`, `AffectNpcs`. Default = tout activé, mais le builder peut désactiver les mobs sur une zone "salle de saut" si les mobs flottants gâchent le gameplay. Le coût marginal de ces 3 toggles dans le codec JSON était nul, l'optionalité a été offerte.
### 3. "Que se passe-t-il quand un joueur entre dans la zone à pleine vitesse en chute libre ?"
Réponse (après une pause) : *"Heu... j'avais pas pensé. Il devrait pas se prendre les dégâts de chute non ?"*
Décision : **`GracePeriodMs` configurable (default 2500ms)**. Pendant la transition, on lisse l'inversion de gravité au lieu d'un flip instantané qui produit une accélération brutale et des dégâts de chute aberrants. En bonus : un toggle `FallDamage` (default false) pour les zones où on veut quand même que tomber ait un coût gameplay.
C'est le genre de détail qu'**un brief écrit ne fait jamais émerger**. Une vraie discussion, oui.
### 4. "Tu veux voir les zones quand tu es dedans ?"
Réponse : *"Oui en build c'est obligé sinon je sais plus où elles sont. Mais en live joueur faut rien voir."*
Décision : **trois modes de visualisation**.
- `Outline` — wireframe couleur configurable (`VisualColor`, default `#00FFFF`), pour le mode build
- `Particles` — bordures émettrices de particules (`Torch_Fire` par défaut), plus discret mais visible
- `None` — invisible, mode production
Toggle par région via la commande `/gravityflip toggle`. Build mode et live mode sur la même map sans rebuild du plugin.
### 5. "C'est un one-shot ou tu réutilises ?"
Réponse : *"One-shot pour l'event, mais si c'est bien fait je le garde."*
Décision déterminante : on traite ce projet comme **un plugin de production**, pas comme un script. Concrètement :
- Persistance JSON dans `Server/mods/Mythlane_GravityFlip/regions.json` (pas mémoire seule)
- Tick loop 10x/sec avec snapshots concurrents (lecture lock-free)
- Tests unitaires sur la logique pure (codec, géométrie AABB)
- Demo region auto-seed au premier démarrage pour onboarding instantané
- Documentation README EN pro, distribuable
**Surcoût vs version "quick & dirty"** : environ 30 % de temps en plus. **Bénéfice** : le client est passé de "j'ai besoin pour samedi" à "je l'utilise toujours, mes builders l'adorent". Le plugin est désormais publiable, monétisable, et donc devenu un actif partagé.
## L'architecture qui en est sortie
```
Player / NPC / Item
▼ (chaque tick, 10 Hz)
RegionTickLoop ◄─── snapshots de regions.json
GravityApplier + FallDamageGuard
effet par région
```
Le wand suit son propre cycle :
```
Player click → WandSelectionStore
/gravityflip define <name>
Region registry → regions.json (auto-save)
```
Stack technique : **Java 25**, Hytale Plugin API officielle (`com.hypixel.hytale.plugin`), Gradle Shadow pour relocalisation Gson (évite les conflits stdlib), JUnit 5 pour les tests. Code source ~2 500 lignes, dont 30 % de tests.
### Le bout de code qui m'a coûté le plus de réflexion
Le **GracePeriodMs**. Pas du code, mais l'idée derrière : au lieu d'un flip binaire, on interpole la composante verticale de la vélocité sur une fenêtre glissante. Naïvement on écrit :
```java
// version naïve — produit des dégâts de chute aberrants
if (region.contains(entity)) {
entity.velocity.y = -entity.velocity.y; // flip instantané
}
```
Avec grace period, on lisse la transition :
```java
// version production — entrée progressive
final long timeInRegion = now - entry.enteredAt();
final double gracePct = Math.min(1.0, timeInRegion / region.gracePeriodMs());
final double targetVy = -region.verticalForce(); // antigrav
entity.velocity.y = lerp(entity.velocity.y, targetVy, gracePct);
```
Trois lignes de plus, mais c'est ce qui fait la différence entre un plugin "marrant 30 secondes" et un plugin que les joueurs utilisent sans rage-quit.
## Ce que ce projet m'a appris (ou rappelé)
**Premièrement** : la valeur du dev senior n'est pas dans la rapidité de code. C'est dans la **série de questions** qui transforme un brief flou en spec actionnable. Si je n'avais pas posé la question 3 (chute libre), le client aurait livré l'event avec des dégâts de chute aberrants, et le plugin aurait été abandonné après le week-end.
**Deuxièmement** : un plugin Hytale qui se vend, ce n'est pas un script. C'est une **codebase Java production-grade** avec persistence, tests, doc, et un onboarding sub-5 minutes. Le client a payé 349€ (tier "Système Sur-Mesure"), pas 50€ Fiverr — et le surcoût est justifié par la qualité que le client va exploiter pendant des mois.
**Troisièmement** : chaque plugin que je code finit publié. GravityFlip est dispo gratuitement sur [Modtale](https://modtale.net/mod/gravity-flip) et bientôt sur CurseForge. Ça ne dilue pas la valeur de la commande client — ça **augmente** ma crédibilité auprès des futurs prospects qui cherchent un dev capable de livrer du code propre, et pas juste de coller des snippets StackOverflow.
## Tu as un projet Hytale en tête ?
Le pattern est toujours le même : on parle 30 minutes, je pose les 5-10 questions qui tuent les ambiguïtés, je te donne un devis ferme, et je livre. Les tarifs sont publics sur [/hytale](/hytale) — entre 149€ pour un plugin essentiel, 349€ pour un système complet, et 790€+ pour une infrastructure MMO custom.
Pas de Fiverr, pas de race-to-the-bottom. Juste un dev senior qui livre du code que tu utilises encore six mois plus tard.
[Demander un devis](/contact) · [Voir GravityFlip en action](https://modtale.net/mod/gravity-flip) · [Voir mes autres plugins](/projects)