Compare commits
3 Commits
c5bcafce2f
...
a61596a6fb
| Author | SHA1 | Date | |
|---|---|---|---|
| a61596a6fb | |||
| be613f8eeb | |||
| 301ab48216 |
@@ -1,160 +1,166 @@
|
||||
---
|
||||
title: "How to build your first Hytale plugin: a step-by-step guide"
|
||||
description: "Learn to build your first Hytale plugin in Kotlin: project setup, event listener, custom command — with the complete source code."
|
||||
description: "Learn to build your first Hytale plugin in Java: IntelliJ + Gradle setup, manifest.json, event listener — with the complete source code."
|
||||
date: "2026-04-22"
|
||||
tags: ["hytale", "tutorial", "kotlin"]
|
||||
tags: ["hytale", "tutorial", "java"]
|
||||
draft: false
|
||||
---
|
||||
|
||||
## Why Hytale, why now
|
||||
|
||||
The first time I booted a local Hytale server, I realized this platform was about to replay exactly what Minecraft did with Bukkit back in 2012 — except that in 2026, we start with Kotlin, a typed API, and an SDK designed from day one for plugin developers. Translation: wide-open window for anyone who wants to get in early.
|
||||
The first time I booted a local Hytale server, I realized this platform was about to replay exactly what Minecraft did with Bukkit back in 2012 — except that in 2026, we start with Java 25, an official API shipped by Hypixel, and a GitHub plugin template maintained by the HytaleModding community. Translation: wide-open window for anyone who wants to get in early during the early-access phase.
|
||||
|
||||
In this guide, I'll walk you through building my first Hytale plugin — a minimal module that listens for a player joining and adds a `/hello` command. Nothing spectacular, but it's exactly the skeleton you need to iterate on more ambitious features. If you'd rather [commission a Hytale plugin](/en/hytale) instead of writing it yourself, that works too — but if you're here, you probably want to get your hands dirty.
|
||||
In this guide, I'll walk you through building my first Hytale plugin — a minimal module that listens for a player joining and logs the event. Nothing spectacular, but it's exactly the skeleton you need to iterate on more ambitious features. If you'd rather [commission a Hytale plugin](/en/hytale) instead of writing it yourself, that works too — but if you're here, you probably want to get your hands dirty.
|
||||
|
||||
::alert{type="info"}
|
||||
**API note** — The API names referenced here are based on the public Hytale SDK 2026 documentation. They may evolve at official launch — adapt based on the most current docs at the time you're reading this.
|
||||
**API note** — Hytale is in early access in 2026. The plugin API (package `com.hypixel.hytale.plugin`) is officially provided by Hypixel, but the official GitBook documentation is still being written. The reference community resources are `hytalemodding.dev` and `britakee-studios.gitbook.io/hytale-modding-documentation`. Exact event class names may still evolve — double-check against the latest docs whenever you're reading this.
|
||||
::
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before cloning anything, make sure you have:
|
||||
|
||||
- **JDK 17+** (I recommend Temurin 21 — the Hytale 2026 SDK runs on it without issue)
|
||||
- **IntelliJ IDEA Community Edition** — free, and the Gradle + Kotlin integration is excellent
|
||||
- **Gradle 8.x** (IntelliJ bundles it, no need to install separately)
|
||||
- Solid basics in **Kotlin**: classes, lambdas, annotations, nullability
|
||||
- **JDK 25** — the version assumed by the current Hytale plugin docs. Temurin works great.
|
||||
- **IntelliJ IDEA Community Edition** — free, it's the IDE recommended by HytaleModding, and its Gradle + Java integration is flawless
|
||||
- **Gradle** (bundled with IntelliJ, no separate install needed)
|
||||
- Solid basics in **modern Java**: classes, annotations, generics, lambdas
|
||||
|
||||
I'm assuming you already have a local Hytale server that boots. If not, the official server docs are your starting point — this guide focuses on the plugin, not on hosting.
|
||||
I'm assuming you already have a local Hytale server that boots. If not, the plugin template available on `hytalemodding.dev` points you to the right server version — this guide focuses on the plugin, not on hosting.
|
||||
|
||||
## Project scaffold
|
||||
|
||||
The minimal tree looks like this:
|
||||
The easiest path is to start from the official HytaleModding template. The minimal tree looks like this:
|
||||
|
||||
```
|
||||
my-first-plugin/
|
||||
├── build.gradle.kts
|
||||
├── src/
|
||||
│ └── main/
|
||||
│ ├── kotlin/
|
||||
│ │ └── com/example/myplugin/
|
||||
│ │ └── MyPlugin.kt
|
||||
│ └── resources/
|
||||
│ └── plugin.toml
|
||||
├── build.gradle
|
||||
├── settings.gradle
|
||||
├── gradle.properties
|
||||
└── src/
|
||||
└── main/
|
||||
├── java/
|
||||
│ └── com/example/myplugin/
|
||||
│ └── MyPlugin.java
|
||||
└── resources/
|
||||
└── manifest.json
|
||||
```
|
||||
|
||||
The minimal `build.gradle.kts` I use:
|
||||
The `settings.gradle` simply declares the project name:
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
kotlin("jvm") version "2.0.0"
|
||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||
}
|
||||
```groovy
|
||||
rootProject.name = 'my-first-plugin'
|
||||
```
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.hytale.io/public")
|
||||
}
|
||||
The `gradle.properties` sets the group and version:
|
||||
|
||||
dependencies {
|
||||
compileOnly("io.hytale:hytale-api:1.0.0")
|
||||
}
|
||||
```properties
|
||||
group=com.example
|
||||
version=1.0.0
|
||||
```
|
||||
|
||||
tasks {
|
||||
build { dependsOn("shadowJar") }
|
||||
The `manifest.json` file — **this is what replaces the `plugin.yml` from the Bukkit world** — declares your plugin to the Hytale server. It lives under `src/main/resources/` and minimally contains:
|
||||
|
||||
```json
|
||||
{
|
||||
"Group": "com.example",
|
||||
"Name": "MyFirstPlugin",
|
||||
"Main": "com.example.myplugin.MyPlugin",
|
||||
"Version": "1.0.0",
|
||||
"Description": "My first Hytale plugin",
|
||||
"Authors": ["you"],
|
||||
"ServerVersion": "*"
|
||||
}
|
||||
```
|
||||
|
||||
The `plugin.toml` manifest declares your plugin to the server:
|
||||
|
||||
```toml
|
||||
name = "MyPlugin"
|
||||
version = "0.1.0"
|
||||
main = "com.example.myplugin.MyPlugin"
|
||||
authors = ["you"]
|
||||
```
|
||||
|
||||
That's it. No boilerplate stands between you and the first useful line of code.
|
||||
No useless boilerplate. The `Main` field must point to the class that extends `JavaPlugin` — that's literally the only required runtime configuration.
|
||||
|
||||
## First event listener — the heart of the plugin
|
||||
|
||||
Here's the main class. This is the file you'll spend the most time in during the first weeks, so treat it seriously:
|
||||
Here's the main class. It extends `JavaPlugin` from the official `com.hypixel.hytale.plugin` package, with the constructor signature **exactly** as required by the API:
|
||||
|
||||
```kotlin
|
||||
package com.example.myplugin
|
||||
```java
|
||||
package com.example.myplugin;
|
||||
|
||||
import io.hytale.api.HytalePlugin
|
||||
import io.hytale.api.event.EventHandler
|
||||
import io.hytale.api.event.player.PlayerJoinEvent
|
||||
import com.hypixel.hytale.plugin.JavaPlugin;
|
||||
import com.hypixel.hytale.plugin.JavaPluginInit;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
class MyPlugin : HytalePlugin() {
|
||||
public class MyPlugin extends JavaPlugin {
|
||||
|
||||
override fun onEnable() {
|
||||
logger.info("MyPlugin enabled")
|
||||
server.events.register(this)
|
||||
public MyPlugin(@Nonnull JavaPluginInit init) {
|
||||
super(init);
|
||||
}
|
||||
|
||||
override fun onDisable() {
|
||||
logger.info("MyPlugin disabled — cleaning up")
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getLogger().info("MyPlugin enabled");
|
||||
getServer().getPluginManager().registerEvents(new JoinListener(), this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerJoin(event: PlayerJoinEvent) {
|
||||
val player = event.player
|
||||
player.sendMessage("Welcome to the server, ${player.name}!")
|
||||
logger.info("Player ${player.name} joined at ${event.timestamp}")
|
||||
@Override
|
||||
public void onDisable() {
|
||||
getLogger().info("MyPlugin disabled — cleaning up");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Quick breakdown:
|
||||
|
||||
- `HytalePlugin` is the base class provided by the API. It exposes `logger`, `server`, and the lifecycle hooks (`onEnable` / `onDisable`).
|
||||
- `server.events.register(this)` tells the server that this instance holds `@EventHandler` methods. Without that line, your listener will never fire — classic mistake I made the first time around.
|
||||
- The `@EventHandler` annotation marks the method as an event target. The `PlayerJoinEvent` parameter type acts as a filter: only that event triggers the method.
|
||||
- `event.player` exposes a `Player` object with `sendMessage`, `teleport`, `inventory`, and so on.
|
||||
- `JavaPlugin` is the base class provided by the Hypixel API. It exposes `getLogger()`, `getServer()`, and the lifecycle hooks (`onEnable` / `onDisable`).
|
||||
- The **constructor taking `@Nonnull JavaPluginInit init`** is mandatory — without that exact signature, the plugin manager cannot instantiate your class at load time. That's the mistake I made the first time around: forget the constructor, and spend 30 minutes debugging a `NoSuchMethodException`.
|
||||
- `getServer().getPluginManager().registerEvents(...)` registers an external listener with the server. The API shape is deliberately close to Bukkit — devs coming from Spigot/Paper feel right at home, even though the package and implementation are Hypixel's own.
|
||||
|
||||
Compile, start the server, connect: you should see the welcome message in chat. If not, check the server logs — a `ClassNotFoundException` usually means the shadow jar isn't packed correctly.
|
||||
The listener itself lives in its own class — cleaner and more testable:
|
||||
|
||||
## Adding a custom command
|
||||
```java
|
||||
package com.example.myplugin;
|
||||
|
||||
Listening to events is half the job. The other half is letting players interact with your plugin through commands. Add this method in the same class:
|
||||
import com.hypixel.hytale.plugin.event.EventHandler;
|
||||
import com.hypixel.hytale.plugin.event.Listener;
|
||||
import com.hypixel.hytale.plugin.event.player.PlayerJoinEvent;
|
||||
|
||||
```kotlin
|
||||
import io.hytale.api.command.Command
|
||||
import io.hytale.api.command.CommandSender
|
||||
public class JoinListener implements Listener {
|
||||
|
||||
@Command(name = "hello", description = "Says hello back")
|
||||
fun onHelloCommand(sender: CommandSender, args: List<String>) {
|
||||
val target = args.firstOrNull() ?: sender.name
|
||||
sender.sendMessage("Hello, $target!")
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
var player = event.getPlayer();
|
||||
player.sendMessage("Welcome to the server, " + player.getName() + "!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can now type `/hello` or `/hello Killian` in-game. The `@Command` annotation registers the command automatically — no need to list it in `plugin.toml`. If you want strict argument validation, `CommandSender` exposes `hasPermission(node)` to gate access.
|
||||
::alert{type="warning"}
|
||||
**Approximate event names** — `PlayerJoinEvent` is a plausible name that follows the Bukkit-like style the docs hint at, but not every exact event class is publicly catalogued yet. Verify the actual class available in your Hytale SDK version before shipping to production.
|
||||
::
|
||||
|
||||
Compile, start the server, connect: you should see the welcome message in chat. If not, check the server logs — a `NoSuchMethodException` on the constructor almost always means you forgot the `(@Nonnull JavaPluginInit init)` signature.
|
||||
|
||||
## Build + local deploy
|
||||
|
||||
The loop I run 20 times a day:
|
||||
|
||||
```bash
|
||||
./gradlew shadowJar
|
||||
cp build/libs/my-first-plugin-all.jar ~/hytale-server/plugins/
|
||||
# Then in the server console:
|
||||
> reload MyPlugin
|
||||
./gradlew build
|
||||
cp build/libs/my-first-plugin-1.0.0.jar ~/hytale-server/plugins/
|
||||
# Then restart the server, or use the reload command if available
|
||||
```
|
||||
|
||||
The `shadowJar` task packs all your runtime dependencies into a single `.jar`, which avoids classpath headaches. On more ambitious plugins — SQLite persistence, embedded REST API, Discord integrations — it quickly becomes essential. If that kind of scope resonates but you'd rather delegate the development side, you can always [commission a custom Hytale plugin](/en/hytale) from someone doing it day in, day out.
|
||||
The `.jar` produced by `./gradlew build` under `build/libs/` drops directly into your Hytale server's `plugins/` folder. The name follows the `{rootProject.name}-{version}.jar` pattern defined in your Gradle files. Once the plugin grows — persistence, external integrations, third-party libs — you'll switch to a `shadowJar` to bundle dependencies, but for a first plugin the base config is plenty. If that kind of scope resonates but you'd rather delegate the development side, you can always [commission a custom Hytale plugin](/en/hytale) from someone doing it day in, day out.
|
||||
|
||||
## Next steps
|
||||
|
||||
Once your first plugin is running, the natural paths forward are:
|
||||
|
||||
- Listen to more events: `BlockBreakEvent`, `PlayerChatEvent`, `EntityDamageEvent` — the full list lives in the `io.hytale.api.event` package.
|
||||
- Persist data: start with a simple `JsonFile` in your plugin folder, switch to SQLite once you cross 50 KB of state.
|
||||
- Add permissions: the Hytale API ships with a node system like `myplugin.admin.reload` that integrates with server groups.
|
||||
- Profile your handlers: a slow event listener directly impacts the server's TPS. `logger.info` with timestamps is your first tool, then Flight Recorder when you get serious.
|
||||
- Listen to more events — the exact list of available event classes is easiest to discover through IntelliJ autocomplete on the `com.hypixel.hytale.plugin.event` package.
|
||||
- Persist data: start with a simple JSON file in your plugin folder, switch to SQLite once you cross 50 KB of state.
|
||||
- Structure your code: a growing plugin deserves a clean listener / service / repository separation the moment you cross 300 lines.
|
||||
- Profile your handlers: a slow event listener directly impacts the server's TPS. `getLogger().info` with timestamps is your first tool, then Flight Recorder when you go to production.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [hytalemodding.dev](https://hytalemodding.dev) — plugin template and FR+EN guides
|
||||
- [britakee-studios.gitbook.io/hytale-modding-documentation](https://britakee-studios.gitbook.io/hytale-modding-documentation) — community GitBook, the most up-to-date source on the API
|
||||
|
||||
## Wrapping up
|
||||
|
||||
A Hytale plugin is essentially a Kotlin class that extends `HytalePlugin`, registers listeners, and exposes commands. Everything else — persistence, UI, integrations — builds on top of that 50-line foundation. If you've followed along this far, you already have the technical base to ship any idea you have in mind. Code an ugly first thing, get it running, iterate. That's always how it starts.
|
||||
A Hytale plugin is essentially a Java class that extends `JavaPlugin`, exposes the right constructor, registers listeners via the `PluginManager`, and describes itself in a `manifest.json`. Everything else — persistence, integrations, UI — builds on top of that 50-line foundation. If you've followed along this far, you already have the technical base to ship any idea you have in mind during this early-access window. Code an ugly first thing, get it running, iterate. That's always how it starts.
|
||||
|
||||
@@ -1,160 +1,166 @@
|
||||
---
|
||||
title: "Créer son premier plugin Hytale : guide pas à pas"
|
||||
description: "Apprends à coder ton premier plugin Hytale en Kotlin : setup, event listener, et commande custom — avec le code source complet."
|
||||
description: "Apprends à coder ton premier plugin Hytale en Java : setup IntelliJ + Gradle, manifest.json, event listener — avec le code source complet."
|
||||
date: "2026-04-22"
|
||||
tags: ["hytale", "tutorial", "kotlin"]
|
||||
tags: ["hytale", "tutorial", "java"]
|
||||
draft: false
|
||||
---
|
||||
|
||||
## Pourquoi Hytale, pourquoi maintenant
|
||||
|
||||
La première fois que j'ai branché un serveur Hytale en local, j'ai compris que cette plateforme allait rejouer exactement ce que Minecraft a fait avec Bukkit en 2012 — sauf qu'en 2026, on démarre avec Kotlin, une API typée, et un SDK pensé dès le jour 1 pour les développeurs de plugins. Autrement dit : fenêtre d'opportunité grande ouverte pour qui veut se positionner tôt.
|
||||
La première fois que j'ai branché un serveur Hytale en local, j'ai compris que cette plateforme allait rejouer exactement ce que Minecraft a fait avec Bukkit en 2012 — sauf qu'en 2026, on démarre avec Java 25, une API officielle fournie par Hypixel, et un template GitHub maintenu par la communauté HytaleModding. Autrement dit : fenêtre d'opportunité grande ouverte pour qui veut se positionner tôt, pendant l'early access.
|
||||
|
||||
Dans ce guide, je te montre comment j'ai construit mon premier plugin Hytale : un module minimal qui écoute l'arrivée d'un joueur et ajoute une commande `/hello`. Rien de spectaculaire, mais c'est exactement le squelette dont tu as besoin pour itérer sur des features plus ambitieuses. Si tu préfères déléguer et faire [commissionner un plugin Hytale sur-mesure](/hytale) plutôt que de l'écrire toi-même, c'est aussi une option — mais si tu es là, tu as probablement envie de mettre les mains dedans.
|
||||
Dans ce guide, je te montre comment j'ai construit mon premier plugin Hytale : un module minimal qui écoute l'arrivée d'un joueur et loggue l'événement. Rien de spectaculaire, mais c'est exactement le squelette dont tu as besoin pour itérer sur des features plus ambitieuses. Si tu préfères déléguer et faire [commissionner un plugin Hytale sur-mesure](/hytale) plutôt que de l'écrire toi-même, c'est aussi une option — mais si tu es là, tu as probablement envie de mettre les mains dedans.
|
||||
|
||||
::alert{type="info"}
|
||||
**Note API** — Les noms d'API cités ici sont basés sur la documentation publique du SDK Hytale 2026. Ils peuvent évoluer au lancement officiel — adapte selon la doc la plus récente au moment où tu lis ceci.
|
||||
**Note API** — Hytale est en early access en 2026. L'API plugin (package `com.hypixel.hytale.plugin`) est officiellement fournie par Hypixel, mais la doc officielle GitBook est encore en cours de rédaction. Les ressources communautaires de référence sont `hytalemodding.dev` et `britakee-studios.gitbook.io/hytale-modding-documentation`. Les noms d'events exacts peuvent évoluer — adapte selon la doc la plus récente au moment où tu lis ceci.
|
||||
::
|
||||
|
||||
## Prérequis
|
||||
|
||||
Avant de cloner quoi que ce soit, assure-toi d'avoir :
|
||||
|
||||
- **JDK 17+** (je recommande Temurin 21 — le SDK Hytale 2026 tourne dessus sans broncher)
|
||||
- **IntelliJ IDEA Community Edition** — gratuit, et l'intégration Gradle + Kotlin y est excellente
|
||||
- **Gradle 8.x** (IntelliJ le bundle, pas besoin de l'installer séparément)
|
||||
- Des bases solides en **Kotlin** : classes, lambdas, annotations, nullabilité
|
||||
- **JDK 25** — la version assumée par la doc plugin Hytale actuelle. Temurin fait très bien l'affaire.
|
||||
- **IntelliJ IDEA Community Edition** — gratuit, c'est l'IDE recommandé par HytaleModding, et l'intégration Gradle + Java y est irréprochable
|
||||
- **Gradle** (bundlé par IntelliJ, pas besoin d'install séparée)
|
||||
- Des bases solides en **Java moderne** : classes, annotations, génériques, lambdas
|
||||
|
||||
Je pars du principe que tu as déjà un serveur Hytale local qui démarre. Si ce n'est pas le cas, la doc officielle du serveur est ton point de départ — ce guide se concentre sur le plugin, pas sur l'hébergement.
|
||||
Je pars du principe que tu as déjà un serveur Hytale local qui démarre. Si ce n'est pas le cas, le template plugin disponible sur `hytalemodding.dev` te pointe vers la bonne version serveur à utiliser — ce guide se concentre sur le plugin, pas sur l'hébergement.
|
||||
|
||||
## Scaffold du projet
|
||||
|
||||
L'arborescence minimale ressemble à ceci :
|
||||
Le plus simple est de partir du template officiel HytaleModding. L'arborescence minimale ressemble à ceci :
|
||||
|
||||
```
|
||||
my-first-plugin/
|
||||
├── build.gradle.kts
|
||||
├── src/
|
||||
│ └── main/
|
||||
│ ├── kotlin/
|
||||
│ │ └── com/example/myplugin/
|
||||
│ │ └── MyPlugin.kt
|
||||
│ └── resources/
|
||||
│ └── plugin.toml
|
||||
├── build.gradle
|
||||
├── settings.gradle
|
||||
├── gradle.properties
|
||||
└── src/
|
||||
└── main/
|
||||
├── java/
|
||||
│ └── com/example/myplugin/
|
||||
│ └── MyPlugin.java
|
||||
└── resources/
|
||||
└── manifest.json
|
||||
```
|
||||
|
||||
Le fichier `build.gradle.kts` minimal que j'utilise :
|
||||
Le `settings.gradle` déclare simplement le nom du projet :
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
kotlin("jvm") version "2.0.0"
|
||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||
}
|
||||
```groovy
|
||||
rootProject.name = 'my-first-plugin'
|
||||
```
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.hytale.io/public")
|
||||
}
|
||||
Le `gradle.properties` fixe le group et la version :
|
||||
|
||||
dependencies {
|
||||
compileOnly("io.hytale:hytale-api:1.0.0")
|
||||
}
|
||||
```properties
|
||||
group=com.example
|
||||
version=1.0.0
|
||||
```
|
||||
|
||||
tasks {
|
||||
build { dependsOn("shadowJar") }
|
||||
Le fichier `manifest.json` — **c'est ce fichier qui remplace le `plugin.yml` du monde Bukkit** — déclare ton plugin au serveur Hytale. Il vit dans `src/main/resources/` et contient au minimum :
|
||||
|
||||
```json
|
||||
{
|
||||
"Group": "com.example",
|
||||
"Name": "MyFirstPlugin",
|
||||
"Main": "com.example.myplugin.MyPlugin",
|
||||
"Version": "1.0.0",
|
||||
"Description": "Mon premier plugin Hytale",
|
||||
"Authors": ["toi"],
|
||||
"ServerVersion": "*"
|
||||
}
|
||||
```
|
||||
|
||||
Le manifest `plugin.toml` déclare ton plugin au serveur :
|
||||
|
||||
```toml
|
||||
name = "MyPlugin"
|
||||
version = "0.1.0"
|
||||
main = "com.example.myplugin.MyPlugin"
|
||||
authors = ["toi"]
|
||||
```
|
||||
|
||||
C'est tout. Aucun boilerplate ne t'attend avant de pouvoir écrire la première ligne utile.
|
||||
Pas de boilerplate inutile. Le champ `Main` doit pointer vers la classe qui étend `JavaPlugin` — c'est littéralement la seule configuration obligatoire côté runtime.
|
||||
|
||||
## Premier event listener — le cœur du plugin
|
||||
|
||||
Voici la classe principale. C'est le fichier sur lequel tu vas passer le plus de temps dans les premières semaines, donc prends-le au sérieux :
|
||||
Voici la classe principale. Elle étend `JavaPlugin` du package officiel `com.hypixel.hytale.plugin`, avec la signature de constructeur **exactement** telle qu'exigée par l'API :
|
||||
|
||||
```kotlin
|
||||
package com.example.myplugin
|
||||
```java
|
||||
package com.example.myplugin;
|
||||
|
||||
import io.hytale.api.HytalePlugin
|
||||
import io.hytale.api.event.EventHandler
|
||||
import io.hytale.api.event.player.PlayerJoinEvent
|
||||
import com.hypixel.hytale.plugin.JavaPlugin;
|
||||
import com.hypixel.hytale.plugin.JavaPluginInit;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
class MyPlugin : HytalePlugin() {
|
||||
public class MyPlugin extends JavaPlugin {
|
||||
|
||||
override fun onEnable() {
|
||||
logger.info("MyPlugin enabled")
|
||||
server.events.register(this)
|
||||
public MyPlugin(@Nonnull JavaPluginInit init) {
|
||||
super(init);
|
||||
}
|
||||
|
||||
override fun onDisable() {
|
||||
logger.info("MyPlugin disabled — cleaning up")
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getLogger().info("MyPlugin enabled");
|
||||
getServer().getPluginManager().registerEvents(new JoinListener(), this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerJoin(event: PlayerJoinEvent) {
|
||||
val player = event.player
|
||||
player.sendMessage("Welcome to the server, ${player.name}!")
|
||||
logger.info("Player ${player.name} joined at ${event.timestamp}")
|
||||
@Override
|
||||
public void onDisable() {
|
||||
getLogger().info("MyPlugin disabled — cleaning up");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Décortique rapidement :
|
||||
|
||||
- `HytalePlugin` est la classe de base fournie par l'API. Elle expose `logger`, `server`, et les hooks de cycle de vie (`onEnable` / `onDisable`).
|
||||
- `server.events.register(this)` indique au serveur que cette instance contient des méthodes `@EventHandler`. Sans cette ligne, ton listener ne sera jamais appelé — erreur classique que j'ai faite la première fois.
|
||||
- L'annotation `@EventHandler` marque la méthode comme cible d'un event. Le type `PlayerJoinEvent` en paramètre fait office de filtre : seul cet event déclenche la méthode.
|
||||
- `event.player` expose un objet `Player` avec `sendMessage`, `teleport`, `inventory`, etc.
|
||||
- `JavaPlugin` est la classe de base fournie par l'API Hypixel. Elle expose `getLogger()`, `getServer()`, et les hooks de cycle de vie (`onEnable` / `onDisable`).
|
||||
- Le **constructeur avec `@Nonnull JavaPluginInit init`** est obligatoire — sans cette signature exacte, le plugin manager ne parvient pas à instancier ta classe au chargement. C'est l'erreur que j'ai faite la première fois : oublier le constructeur et passer 30 minutes à debugger un `NoSuchMethodException`.
|
||||
- `getServer().getPluginManager().registerEvents(...)` enregistre un listener externe auprès du serveur. L'API est volontairement proche de Bukkit dans sa forme — les devs qui viennent de Spigot/Paper retrouvent leurs repères, même si le package et l'implémentation sont propres à Hypixel.
|
||||
|
||||
Compile, démarre le serveur, connecte-toi : tu devrais voir le message de bienvenue dans le chat. Si ce n'est pas le cas, vérifie les logs serveur — une `ClassNotFoundException` est généralement un souci de shadow jar mal configuré.
|
||||
Le listener lui-même vit dans une classe séparée — plus propre et plus testable :
|
||||
|
||||
## Ajouter une commande custom
|
||||
```java
|
||||
package com.example.myplugin;
|
||||
|
||||
Écouter des events, c'est la moitié du job. L'autre moitié, c'est de laisser les joueurs interagir avec ton plugin via des commandes. Ajoute cette méthode dans la même classe :
|
||||
import com.hypixel.hytale.plugin.event.EventHandler;
|
||||
import com.hypixel.hytale.plugin.event.Listener;
|
||||
import com.hypixel.hytale.plugin.event.player.PlayerJoinEvent;
|
||||
|
||||
```kotlin
|
||||
import io.hytale.api.command.Command
|
||||
import io.hytale.api.command.CommandSender
|
||||
public class JoinListener implements Listener {
|
||||
|
||||
@Command(name = "hello", description = "Says hello back")
|
||||
fun onHelloCommand(sender: CommandSender, args: List<String>) {
|
||||
val target = args.firstOrNull() ?: sender.name
|
||||
sender.sendMessage("Hello, $target!")
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
var player = event.getPlayer();
|
||||
player.sendMessage("Welcome to the server, " + player.getName() + "!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Tu peux maintenant taper `/hello` ou `/hello Killian` en jeu. L'annotation `@Command` enregistre la commande automatiquement — pas besoin de l'inscrire dans `plugin.toml`. Si tu veux de la validation stricte sur les arguments, `CommandSender` expose `hasPermission(node)` pour restreindre l'accès.
|
||||
::alert{type="warning"}
|
||||
**Noms d'events approximatifs** — `PlayerJoinEvent` est un nom plausible et conforme au style Bukkit-like que la doc laisse entrevoir, mais tous les events exacts ne sont pas encore catalogués publiquement. Vérifie la classe exacte disponible dans ta version du SDK Hytale avant d'expédier en prod.
|
||||
::
|
||||
|
||||
Compile, démarre le serveur, connecte-toi : tu devrais voir le message de bienvenue dans le chat. Si ce n'est pas le cas, vérifie les logs serveur — un `NoSuchMethodException` sur le constructeur est quasi certain si tu as oublié la signature `(@Nonnull JavaPluginInit init)`.
|
||||
|
||||
## Build + deploy local
|
||||
|
||||
Le cycle que je fais 20 fois par jour :
|
||||
|
||||
```bash
|
||||
./gradlew shadowJar
|
||||
cp build/libs/my-first-plugin-all.jar ~/hytale-server/plugins/
|
||||
# Puis dans la console serveur :
|
||||
> reload MyPlugin
|
||||
./gradlew build
|
||||
cp build/libs/my-first-plugin-1.0.0.jar ~/hytale-server/plugins/
|
||||
# Puis redémarre le serveur, ou utilise la commande de reload disponible
|
||||
```
|
||||
|
||||
Le flag `shadowJar` empaquette toutes tes dépendances runtime dans un seul `.jar`, ce qui évite les galères de classpath. Sur des plugins plus ambitieux — persistance SQLite, API REST embarquée, intégrations Discord — c'est vite indispensable. Si ce genre de scope te parle mais que tu préfères déléguer la partie développement, tu peux toujours [commissionner un plugin Hytale sur-mesure](/hytale) auprès de quelqu'un qui fait ça au quotidien.
|
||||
Le `.jar` généré par `./gradlew build` dans `build/libs/` est directement déposable dans le dossier `plugins/` de ton serveur Hytale. Le nom suit le pattern `{rootProject.name}-{version}.jar` défini dans tes fichiers Gradle. Si ton plugin grossit — persistance, intégrations externes, libs tierces — tu passeras sur un `shadowJar` pour embarquer les dépendances, mais pour un premier plugin la config de base suffit largement. Si ce genre de scope te parle mais que tu préfères déléguer la partie développement, tu peux toujours [commissionner un plugin Hytale sur-mesure](/hytale) auprès de quelqu'un qui fait ça au quotidien.
|
||||
|
||||
## Prochaines étapes
|
||||
|
||||
Une fois ton premier plugin qui tourne, les pistes naturelles sont :
|
||||
|
||||
- Écouter plus d'events : `BlockBreakEvent`, `PlayerChatEvent`, `EntityDamageEvent` — la liste complète est dans le package `io.hytale.api.event`.
|
||||
- Persister de la donnée : commence avec un simple `JsonFile` dans le dossier plugin, passe à SQLite quand tu dépasses 50 Ko de state.
|
||||
- Ajouter des permissions : l'API Hytale embarque un système de nodes type `myplugin.admin.reload` qui s'intègre avec les groupes serveur.
|
||||
- Profiler tes handlers : un event listener lent impacte directement le TPS du serveur. `logger.info` avec timestamps est ton premier outil, puis Flight Recorder pour le sérieux.
|
||||
- Écouter plus d'events — la liste exacte des classes d'events disponibles est à récupérer via l'auto-complétion IntelliJ sur le package `com.hypixel.hytale.plugin.event`.
|
||||
- Persister de la donnée : commence avec un simple fichier JSON dans le dossier du plugin, passe à SQLite quand tu dépasses 50 Ko de state.
|
||||
- Structurer ton code : un plugin qui grossit mérite une séparation claire listener / service / repository dès que tu passes les 300 lignes.
|
||||
- Profiler tes handlers : un event listener lent impacte directement le TPS du serveur. `getLogger().info` avec timestamps est ton premier outil, puis Flight Recorder quand tu passes en prod.
|
||||
|
||||
## Pour aller plus loin
|
||||
|
||||
- [hytalemodding.dev](https://hytalemodding.dev) — template plugin et guides FR+EN
|
||||
- [britakee-studios.gitbook.io/hytale-modding-documentation](https://britakee-studios.gitbook.io/hytale-modding-documentation) — GitBook communautaire, la source la plus à jour sur l'API
|
||||
|
||||
## Conclusion
|
||||
|
||||
Un plugin Hytale, c'est essentiellement une classe Kotlin qui hérite de `HytalePlugin`, enregistre des listeners, et expose des commandes. Tout le reste — persistance, UI, intégrations — se construit sur ce socle de 50 lignes. Si tu as suivi jusqu'ici, tu as déjà la base technique pour livrer n'importe quelle idée que tu as en tête. Code un premier truc moche, fais-le tourner, itère. C'est toujours comme ça que ça commence.
|
||||
Un plugin Hytale, c'est essentiellement une classe Java qui étend `JavaPlugin`, expose le bon constructeur, enregistre des listeners via le `PluginManager`, et se décrit dans un `manifest.json`. Tout le reste — persistance, intégrations, UI — se construit sur ce socle de 50 lignes. Si tu as suivi jusqu'ici, tu as déjà la base technique pour livrer n'importe quelle idée que tu as en tête pendant l'early access. Code un premier truc moche, fais-le tourner, itère. C'est toujours comme ça que ça commence.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "Développement de plugins Hytale en 2026 : état de l'art et perspectives"
|
||||
description: "Tour d'horizon de l'écosystème plugin Hytale en 2026 : stack technique, patterns modernes, et ce qui attend la communauté."
|
||||
description: "Tour d'horizon de l'écosystème plugin Hytale en 2026 : choix du Java, API officielle Hypixel, patterns modernes et perspectives."
|
||||
date: "2026-04-21"
|
||||
tags: ["hytale", "industry", "analysis"]
|
||||
draft: false
|
||||
@@ -8,87 +8,110 @@ draft: false
|
||||
|
||||
## Hytale en 2026, où en est-on ?
|
||||
|
||||
Il y a quelques années, parler de « dev plugin Hytale » signifiait bricoler sur des builds préliminaires, relire trois fois les release notes du SDK avant d'oser toucher à une API, et prier pour qu'un event ne change pas de nom la semaine suivante. En 2026, le paysage a changé de texture : le SDK officiel s'est stabilisé, les patterns de production ont émergé, et la ligne entre serveur communautaire et studio indie commercial s'est brouillée.
|
||||
Il y a quelques années, parler de « dev plugin Hytale » signifiait bricoler sur des builds préliminaires, relire trois fois les release notes avant d'oser toucher à une API, et prier pour qu'un event ne change pas de nom la semaine suivante. En 2026, le paysage a changé de texture : Hytale est entré en early access, Hypixel a publié son API plugin officielle (package `com.hypixel.hytale.plugin`), et la communauté a fini par converger autour d'un template GitHub maintenu et d'une GitBook communautaire qui sert de doc de référence en attendant la doc officielle.
|
||||
|
||||
Je développe moi-même [des plugins Hytale sur commande](/hytale) depuis les premières betas, et ce que je constate chez mes clients ressemble de moins en moins à du scripting de hobbyiste. Les serveurs qui ambitionnent une audience réelle — économie, PvP compétitif, RP structuré — demandent aujourd'hui la même rigueur que n'importe quelle codebase Kotlin côté serveur : tests, CI, versionnage, revues.
|
||||
Je développe moi-même [des plugins Hytale sur commande](/hytale) depuis les premières previews, et ce que je constate chez mes clients ressemble de moins en moins à du scripting de hobbyiste. Les serveurs qui ambitionnent une audience réelle — économie, PvP compétitif, RP structuré — demandent aujourd'hui la même rigueur que n'importe quelle codebase JVM côté serveur : tests, CI, versionnage, revues.
|
||||
|
||||
La thèse de cet article est simple : 2026 est l'année où le développement Hytale devient un vrai métier, avec ses conventions, ses outils, et ses pièges. Voici ce que j'observe en prod, ce qui marche, et ce qu'il faut arrêter de faire.
|
||||
La thèse de cet article est simple : 2026, c'est l'année où Hytale passe d'un objet de spéculation à une plateforme de dev concrète, avec son langage officiel (Java), ses conventions d'API, et ses premiers patterns stabilisés. Voici ce que j'observe.
|
||||
|
||||
## La stack 2026 : Kotlin, coroutines, et outillage mature
|
||||
## Le choix du Java et ce qu'il signale
|
||||
|
||||
Kotlin est la lingua franca côté plugin. Le Java résiduel survit dans les vieilles codebases portées depuis d'autres écosystèmes, mais tout nouveau projet sérieux démarre en Kotlin/JVM. Le tooling suit : Gradle Kotlin DSL est devenu la norme, IntelliJ IDEA reste l'IDE de référence, et la chaîne de test JUnit 5 + MockK couvre la majorité des besoins unitaires et d'intégration.
|
||||
Hypixel a tranché : l'API plugin Hytale est en **Java**, pas en Kotlin. Ce choix, à la lecture du template officiel `hytalemodding.dev` et de la GitBook communautaire, est délibéré et cohérent. La classe de base `JavaPlugin` (package `com.hypixel.hytale.plugin`) reprend visuellement le pattern que tout dev venant de Bukkit/Spigot reconnaît au premier coup d'œil : `onEnable()`, `onDisable()`, enregistrement de listeners via `getServer().getPluginManager()`. La ressemblance n'est pas un accident — c'est un choix d'onboarding. Un développeur Paper habitué peut lire le template et être productif en quelques heures.
|
||||
|
||||
Le changement de texture le plus visible, c'est l'adoption systématique des coroutines pour tout ce qui touche à de l'I/O. Les anciens réflexes hérités du monde Bukkit — tout synchrone, tout sur le thread principal, quelques `Thread` manuels pour les trucs lents — ont laissé la place à des scopes dédiés, proprement cancellés au shutdown du plugin.
|
||||
Autre signal fort : la signature exacte du constructeur est **imposée** (`public YourPlugin(@Nonnull JavaPluginInit init)`), et le manifest vit dans un `manifest.json` (pas `plugin.yml`) avec des champs capitalisés (`Group`, `Name`, `Main`, `Version`, `Authors`, `ServerVersion`). Ce sont les deux endroits où Hypixel s'écarte volontairement du legacy Bukkit — assez pour marquer une identité, pas assez pour perdre la base de devs existante.
|
||||
|
||||
Voici un pattern typique que je pousse chez mes clients : un event handler qui déclenche une lecture de profil async sans jamais bloquer le tick principal.
|
||||
Le Java 25 assumé par la doc permet aussi à l'API de s'appuyer sur les features modernes du langage. Records pour modéliser les events, sealed interfaces pour les hiérarchies fermées, pattern matching dans les switch, et virtual threads (stables depuis Java 21) pour l'I/O async sans ramener une lib coroutines. C'est un Java 2026, pas un Java 2016 — et ça se sent dans la qualité des signatures d'API que la communauté documente.
|
||||
|
||||
```kotlin
|
||||
package com.example.ecoplugin
|
||||
## Un squelette de plugin moderne
|
||||
|
||||
import io.hytale.api.HytalePlugin
|
||||
import io.hytale.api.event.EventHandler
|
||||
import io.hytale.api.event.player.PlayerJoinEvent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
Voici le genre de squelette que je pousse chez mes clients qui démarrent un projet ambitieux : un plugin qui tire parti des records, des sealed interfaces et des virtual threads pour traiter de l'I/O sans jamais bloquer le tick serveur.
|
||||
|
||||
class EcoPlugin : HytalePlugin() {
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
```java
|
||||
package com.example.ecoplugin;
|
||||
|
||||
@EventHandler
|
||||
fun onJoin(event: PlayerJoinEvent) {
|
||||
scope.launch {
|
||||
val profile = profileRepo.fetch(event.player.uuid)
|
||||
event.player.sendMessage("Welcome back, balance: ${profile.balance}")
|
||||
}
|
||||
import com.hypixel.hytale.plugin.JavaPlugin;
|
||||
import com.hypixel.hytale.plugin.JavaPluginInit;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class EcoPlugin extends JavaPlugin {
|
||||
|
||||
// Type fermé et modélisé — pas d'énum stringly-typed
|
||||
sealed interface EcoEvent permits Deposit, Withdraw, Transfer {}
|
||||
record Deposit(String playerId, long amount) implements EcoEvent {}
|
||||
record Withdraw(String playerId, long amount) implements EcoEvent {}
|
||||
record Transfer(String from, String to, long amount) implements EcoEvent {}
|
||||
|
||||
// Virtual threads : un pool « infini » pour l'I/O, zéro coût par tâche
|
||||
private final ExecutorService io = Executors.newVirtualThreadPerTaskExecutor();
|
||||
|
||||
public EcoPlugin(@Nonnull JavaPluginInit init) {
|
||||
super(init);
|
||||
}
|
||||
|
||||
override fun onDisable() {
|
||||
scope.cancel()
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getLogger().info("EcoPlugin enabled");
|
||||
getServer().getPluginManager().registerEvents(new EcoListener(this), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
io.shutdown();
|
||||
}
|
||||
|
||||
public void dispatch(EcoEvent event) {
|
||||
io.submit(() -> {
|
||||
switch (event) {
|
||||
case Deposit d -> getLogger().info("deposit " + d.amount() + " for " + d.playerId());
|
||||
case Withdraw w -> getLogger().info("withdraw " + w.amount() + " for " + w.playerId());
|
||||
case Transfer t -> getLogger().info("transfer " + t.amount() + " " + t.from() + "→" + t.to());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Trois détails qui changent tout : le `SupervisorJob` qui empêche une coroutine foireuse de tuer ses sœurs, le `Dispatchers.IO` pour ne pas squatter le pool par défaut, et le `scope.cancel()` dans `onDisable()` qui garantit qu'on laisse la JVM propre au reload. Rien de révolutionnaire — juste les bases d'une coroutine-hygiene que l'écosystème a fini par intérioriser.
|
||||
Trois choses méritent qu'on s'y arrête. D'abord, la **sealed interface** `EcoEvent` avec ses trois records : le compilateur vérifie l'exhaustivité du `switch` expression, et l'ajout d'un nouveau type d'event casse la compilation là où il faut — c'est le genre de filet que Kotlin offrait déjà avec `sealed class`, et que Java a fini par ramener proprement. Ensuite, les **records** : zéro boilerplate, égalité structurelle par défaut, immutabilité, lisibilité parfaite. Enfin, `newVirtualThreadPerTaskExecutor()` : un exécuteur qui spawne un virtual thread par tâche, quasi-gratuit, parfait pour de l'I/O qui ne doit jamais toucher le tick serveur principal.
|
||||
|
||||
::alert{type="tip"}
|
||||
**Astuce** — L'API Hytale publique continue d'évoluer ; les noms exacts de classes (`HytalePlugin`, `PlayerJoinEvent`) peuvent bouger entre versions majeures. Le pattern — scope lifecycle-aware, dispatcher I/O, cancel au shutdown — reste valide indépendamment du naming.
|
||||
**Astuce** — Les noms exacts de classes d'events (`PlayerJoinEvent`, etc.) peuvent encore bouger en early access. Le pattern — exécuteur virtual-thread pour l'I/O, records pour la donnée immutable, sealed interface pour les hiérarchies d'events — reste valide indépendamment du naming final.
|
||||
::
|
||||
|
||||
## Patterns modernes : ce qui a remplacé les mauvaises habitudes Bukkit-era
|
||||
|
||||
Les trois changements de pratique les plus nets que je vois sur les codebases sérieuses :
|
||||
|
||||
**Injection de dépendances explicite.** Plus de singletons globaux accessibles depuis n'importe où. Soit on utilise un micro-container type `Koin`, soit on injecte par constructeur à la main. Les event handlers reçoivent leurs collaborateurs plutôt que de les récupérer via une variable statique — ce qui rend le code testable sans monkey-patching.
|
||||
**Injection de dépendances explicite.** Plus de singletons globaux accessibles depuis n'importe où. Soit on utilise un micro-container (Guice reste populaire côté Java), soit on injecte par constructeur à la main. Les listeners reçoivent leurs collaborateurs plutôt que de les récupérer via un champ statique — ce qui rend le code testable sans monkey-patching.
|
||||
|
||||
**Séparation handler / logique métier.** Un `@EventHandler` devient un adaptateur fin : il extrait les données pertinentes de l'event, appelle un service métier pur, et applique le résultat. La logique vit dans des classes qu'on teste sans instancier la moitié du SDK.
|
||||
**Séparation listener / logique métier.** Un `@EventHandler` devient un adaptateur fin : il extrait les données pertinentes de l'event, appelle un service métier pur, et applique le résultat. La logique vit dans des classes qu'on teste sans instancier la moitié du SDK.
|
||||
|
||||
**Config typée.** Fini le parsing manuel de YAML dans une `Map<String, Any>`. `kotlinx.serialization` lit le fichier en une data class, et toute clé manquante ou typée incorrectement pète au chargement — pas en prod trois jours plus tard quand un joueur trigger le bon path.
|
||||
**Config typée.** Fini le parsing manuel dans une `Map<String, Object>`. Jackson ou Gson désérialise vers des records, et toute clé manquante ou mal typée pète au chargement — pas en prod trois jours plus tard quand un joueur trigger le bon path.
|
||||
|
||||
**Tests.** Unitaires sur la logique métier, intégration sur les handlers avec un SDK mocké. Ce n'est plus une excentricité ; c'est ce qui distingue un plugin commercial d'un script du weekend.
|
||||
**Tests.** JUnit 5 sur la logique métier, tests d'intégration sur les listeners avec un SDK mocké (Mockito). Ce n'est plus une excentricité — c'est ce qui distingue un plugin commercial d'un script du weekend.
|
||||
|
||||
## Écosystème : libs et SDKs qui comptent
|
||||
## Écosystème : libs et ressources qui comptent
|
||||
|
||||
Le SDK officiel Hytale reste le socle. Autour, l'écosystème est plus fragmenté que celui de Paper/Spigot à maturité équivalente, mais quelques hubs GitHub actifs et des Discord de devs font le tri : wrappers Kotlin-idiomatiques sur les API Java-first, DSLs pour la config de commandes, helpers pour la persistence.
|
||||
L'API officielle Hypixel est le socle. Autour, l'écosystème est plus jeune que Paper/Spigot à maturité équivalente, mais deux hubs tiennent la route : `hytalemodding.dev` (template plugin maintenu, guides FR+EN) et la GitBook `britakee-studios.gitbook.io/hytale-modding-documentation` (la doc communautaire la plus à jour tant que la doc officielle est encore en construction). Entre les deux, un dev motivé trouve de quoi démarrer proprement.
|
||||
|
||||
Les anti-patterns récurrents que j'observe en audit de codebase client : handlers qui font du blocking I/O sur le thread principal, gestion d'état global partagé sans synchronisation, config en `HashMap` non typée, absence totale de logs structurés. Rien de neuf — ce sont les mêmes plaies que dans tout écosystème plugin JVM, avec la même solution : appliquer discipline, typage, et isolation.
|
||||
Les anti-patterns récurrents que j'observe en audit de codebase client : handlers qui font du blocking I/O sur le thread principal, gestion d'état global partagé sans synchronisation, config en `HashMap` non typée, absence totale de logs structurés. Rien de neuf — ce sont les mêmes plaies que dans tout écosystème plugin JVM, avec la même solution : discipline, typage, isolation.
|
||||
|
||||
## Ce que l'avenir apporte
|
||||
|
||||
Quelques tendances qui me semblent robustes pour les 12-18 prochains mois :
|
||||
|
||||
**Scripting côté client.** Si la plateforme étend ses hooks côté client comme elle l'a laissé entendre, toute une classe de plugins cosmétiques / UX devient possible sans hack côté serveur. À surveiller.
|
||||
**Stabilisation de la doc officielle.** La GitBook Hypixel officielle reste en rédaction ; à mesure qu'elle se remplit, on peut s'attendre à voir converger les nomenclatures d'events, les API de commandes, et les conventions de packaging.
|
||||
|
||||
**Formats de packaging plus rigoureux.** Les plugins publiés commencent à ressembler à de vrais artefacts Gradle : métadonnées riches, dépendances déclarées, signatures. Les plateformes de distribution suivent.
|
||||
**Émergence de gameplay loops signature.** Hytale en early access laisse voir les premières grosses expériences serveur — économies cross-world, mini-games persistants, modes compétitifs. Les plugins qui les portent sont les laboratoires où les patterns de demain vont se forger.
|
||||
|
||||
**Professionnalisation des modèles économiques.** La commission one-shot reste dominante, mais je vois émerger des arrangements rev-share sur serveurs monétisés, des contrats de maintenance annuels, et des licences B2B pour les features complexes. Si tu veux externaliser le dev d'un plugin ambitieux plutôt que l'empiler dans un backlog interne, je propose [du développement Hytale sur commande](/hytale) — patterns modernes et config typée inclus par défaut.
|
||||
**Professionnalisation des modèles économiques.** La commission one-shot reste dominante, mais je vois émerger des arrangements rev-share sur serveurs monétisés, des contrats de maintenance annuels, et des licences B2B pour les features complexes. Si tu veux externaliser le dev d'un plugin ambitieux plutôt que l'empiler dans un backlog interne, je propose [du développement Hytale sur commande](/hytale) — patterns modernes et API officielle maîtrisée inclus par défaut.
|
||||
|
||||
**Outillage de debug / profiling.** Encore le parent pauvre. Les meilleurs teams écrivent leur propre tooling ; attendez-vous à voir des libs publiques combler ce vide.
|
||||
**Outillage de debug / profiling.** Encore le parent pauvre. Les meilleures teams écrivent leur propre tooling ; attendez-vous à voir des libs publiques combler ce vide au fil de l'early access.
|
||||
|
||||
## Conclusion
|
||||
|
||||
2026 n'est pas l'année du grand bouleversement Hytale — c'est l'année de la consolidation. Les patterns existent, les outils sont là, les attentes des serveurs clients ont monté d'un cran. Le plugin « weekend script » continuera d'exister et d'amuser ses auteurs, mais la couche au-dessus — plugins qu'on vend, qu'on maintient, qu'on intègre à un serveur commercial — suit désormais les mêmes standards que n'importe quel service Kotlin sérieux.
|
||||
2026 n'est pas l'année du grand bouleversement Hytale — c'est l'année de la consolidation. L'API officielle est là, le Java est entériné comme langage de référence, les patterns modernes (records, sealed, virtual threads) redonnent au langage une expressivité qui justifie pleinement le choix. Les outils et la documentation communautaire comblent les trous que la doc officielle laisse encore.
|
||||
|
||||
Pour les devs qui hésitent à sauter le pas : c'est le bon moment. L'API s'est calmée, la communauté sait ce qu'elle fait, et les serveurs sont demandeurs. Ce qui était un hobby obscur il y a trois ans est devenu une niche technique légitime — avec tout ce que ça implique de rigueur, mais aussi d'opportunités.
|
||||
Pour les devs qui hésitent à sauter le pas : c'est le bon moment. L'API a une identité claire, la communauté sait ce qu'elle fait, et les serveurs en early access sont demandeurs. Ce qui était un hobby obscur il y a trois ans est devenu une niche technique légitime — avec tout ce que ça implique de rigueur, mais aussi d'opportunités.
|
||||
|
||||
Reference in New Issue
Block a user