Files
Documentation/content/patch-note/2026.01.13-50e69c385-2026.01.15-c04fdfe10-patch-note.md
2026-01-20 20:33:59 +01:00

762 lines
25 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.
# Patch Notes: Hytale Server
## Version 2026.01.13-50e69c385 → 2026.01.15-c04fdfe10
**Date de release** : 15 janvier 2026
**Build précédent** : `50e69c385` (13 janvier 2026)
**Build actuel** : `c04fdfe10` (15 janvier 2026)
---
### Statistiques
| Métrique | Valeur |
|----------|--------|
| Fichiers modifiés | 9 |
| Insertions | +55 |
| Suppressions | -18 |
| Delta net | +37 lignes |
---
## Table des matières
1. [Authentification & Sécurité JWT](#1-authentification--sécurité-jwt)
2. [Gestion d'échec d'authentification serveur](#2-gestion-déchec-dauthentification-serveur)
3. [Transport QUIC - Optimisations réseau](#3-transport-quic---optimisations-réseau)
4. [Timeout Setup Handler](#4-timeout-setup-handler)
5. [Physique des Items - Collisions latérales](#5-physique-des-items---collisions-latérales)
6. [Nouvelle raison d'arrêt MISSING_ASSETS](#6-nouvelle-raison-darrêt-missing_assets)
7. [Validation des Asset Packs](#7-validation-des-asset-packs)
8. [Télémétrie Sentry enrichie](#8-télémétrie-sentry-enrichie)
9. [Fix NullPointerException CraftingManager](#9-fix-nullpointerexception-craftingmanager)
---
## 1. Authentification & Sécurité JWT
**Fichier** : `com/hypixel/hytale/server/core/auth/JWTValidator.java`
**Lignes modifiées** : 31, 87-99, 293-309, 355-372
### 1.1 Nouvelle constante (non utilisée)
```java
private static final long CLOCK_SKEW_SECONDS = 300L;
```
Une constante a été ajoutée pour centraliser la valeur du clock skew, mais elle n'est **pas encore utilisée** dans le code - les valeurs `300L` sont hardcodées directement dans les méthodes de validation.
### 1.2 Augmentation du Clock Skew : 60s → 300s
Le **clock skew** (tolérance de désynchronisation d'horloge entre client et serveur) passe de **1 minute à 5 minutes**. Cette modification affecte la validation de trois types de tokens JWT :
#### Méthode `validateToken()` (lignes 87-99)
| Claim | Avant | Après | Comportement |
|-------|-------|-------|--------------|
| `exp` (expires at) | `now >= exp + 60` | `now >= exp + 300` | Token accepté jusqu'à 5 min après expiration |
| `nbf` (not before) | `now < nbf - 60` | `now < nbf - 300` | Token accepté 5 min avant validité |
| `iat` (issued at) | **Non vérifié** | `iat > now + 300` | **NOUVEAU** : Rejette tokens du futur |
**Code ajouté** (lignes 96-99) :
```java
if (claims.issuedAt != null && claims.issuedAt > nowSeconds + 300L) {
LOGGER.at(Level.WARNING).log("Token issued in the future (iat: %d, now: %d)",
(Object)claims.issuedAt, nowSeconds);
return null;
}
```
#### Méthode `validateIdentityToken()` (lignes 293-309)
| Claim | Avant | Après |
|-------|-------|-------|
| `exp` | `now >= exp + 60` | `now >= exp + 300` |
| `nbf` | `now < nbf - 60` | `now < nbf - 300` |
| `iat` | `iat > now + 60` | `iat > now + 300` |
La validation `iat` existait déjà pour les identity tokens, seule la valeur du clock skew a changé.
#### Méthode `validateSessionToken()` (lignes 355-372)
| Claim | Avant | Après | Comportement |
|-------|-------|-------|--------------|
| `exp` | `now >= exp + 60` | `now >= exp + 300` | Token accepté jusqu'à 5 min après expiration |
| `nbf` | `now < nbf - 60` | `now < nbf - 300` | Token accepté 5 min avant validité |
| `iat` | **Non vérifié** | `iat > now + 300` | **NOUVEAU** : Rejette tokens du futur |
**Code ajouté** (lignes 368-371) :
```java
if (claims.issuedAt != null && claims.issuedAt > nowSeconds + 300L) {
LOGGER.at(Level.WARNING).log("Session token issued in the future (iat: %d, now: %d)",
(Object)claims.issuedAt, nowSeconds);
return null;
}
```
### 1.3 Impact
| Aspect | Effet |
|--------|-------|
| **Compatibilité** | Meilleure tolérance pour clients avec horloge désynchronisée |
| **Sécurité** | Protection contre tokens pré-générés pour utilisation future |
| **Stabilité** | Réduction des erreurs de validation dues au décalage horaire |
### 1.4 Contexte technique
Le `JWTValidator` gère trois types de tokens avec des rôles distincts :
1. **Auth Token (JWTClaims)** : Authentification client, contient username, IP, certificate fingerprint
2. **Identity Token (IdentityTokenClaims)** : Identité du joueur, requiert scopes `hytale:client` ou `hytale:editor`
3. **Session Token (SessionTokenClaims)** : Sessions serveur, validation minimale
Le système utilise un cache JWKS (JSON Web Key Set) avec durée de 1 heure et mécanisme de retry intelligent en cas d'échec de vérification de signature.
---
## 2. Gestion d'échec d'authentification serveur
**Fichier** : `com/hypixel/hytale/server/core/io/handlers/login/HandshakeHandler.java`
**Lignes modifiées** : 291-298 (dans le callback `exceptionally`)
### 2.1 Modification du comportement
Dans la méthode `exchangeServerAuthGrant()`, le callback `.exceptionally()` (gestion des erreurs async) a été modifié. Lorsqu'une exception se produit pendant l'échange de server token (`AuthState.EXCHANGING_SERVER_TOKEN`) :
**Avant** :
```java
.exceptionally(ex -> {
LOGGER.at(Level.WARNING).withCause(ex).log("Error exchanging server auth grant");
this.channel.eventLoop().execute(() -> {
if (this.authState != AuthState.EXCHANGING_SERVER_TOKEN) {
return;
}
byte[] passwordChallenge = this.generatePasswordChallengeIfNeeded();
this.completeAuthentication(passwordChallenge);
});
return null;
});
```
**Après** :
```java
.exceptionally(ex -> {
LOGGER.at(Level.WARNING).withCause(ex).log("Error exchanging server auth grant");
this.channel.eventLoop().execute(() -> {
if (this.authState != AuthState.EXCHANGING_SERVER_TOKEN) {
return;
}
this.disconnect("Server authentication failed - please try again later");
});
return null;
});
```
### 2.2 Impact sur la sécurité
| Aspect | Avant | Après |
|--------|-------|-------|
| Échec server token exchange | Authentification continuait silencieusement | Déconnexion immédiate |
| Bypass potentiel | Possible dans certains cas de timeout | Impossible |
| Message utilisateur | Aucun | Message explicite |
### 2.3 Contexte : Flow d'authentification mutuelle
Le `HandshakeHandler` orchestre un flow d'authentification mutuelle (mTLS) avec les états suivants :
```
REQUESTING_AUTH_GRANT (30s timeout)
AWAITING_AUTH_TOKEN (30s timeout)
PROCESSING_AUTH_TOKEN
EXCHANGING_SERVER_TOKEN (15s timeout) ← Changement ici
AUTHENTICATED
```
**Étapes détaillées** :
1. **Validation Identity Token** : Vérifie le token du client, UUID, scopes
2. **Demande Auth Grant** : Récupère le server session token, appel async au `SessionServiceClient`
3. **Traitement Token Client** : Vérifie JWT signature, certificate binding, UUID/username
4. **Échange Server Token** : Échange auth grant contre token avec fingerprint serveur
5. **Complétion** : Crée `PlayerAuthentication`, passe au `SetupPacketHandler`
Le changement garantit qu'un échec à l'étape 4 ne permet plus de compléter l'authentification.
---
## 3. Transport QUIC - Optimisations réseau
**Fichier** : `com/hypixel/hytale/server/core/io/transport/QUICTransport.java`
**Lignes modifiées** : 30, 128
### 3.1 Nouvel import
```java
import io.netty.handler.codec.quic.QuicCongestionControlAlgorithm;
```
### 3.2 Activation de PMTU Discovery
**Ajout** : `.discoverPmtu(true)`
Le **Path MTU Discovery** permet au serveur de :
| Fonctionnalité | Description |
|----------------|-------------|
| Détection automatique | Découvre la taille maximale de paquet sur chaque chemin réseau |
| Évitement fragmentation | Prévient la fragmentation IP qui dégrade les performances |
| Optimisation throughput | Utilise des paquets de taille optimale par connexion |
### 3.3 Changement d'algorithme de congestion : BBR
**Ajout** : `.congestionControlAlgorithm(QuicCongestionControlAlgorithm.BBR)`
#### Comparaison des algorithmes
| Aspect | Cubic (implicite avant) | BBR (nouveau) |
|--------|-------------------------|---------------|
| **Approche** | Loss-based | Model-based |
| **Réaction aux pertes** | Réduit agressivement le débit | Maintient le débit estimé |
| **Latence** | Variable, peut augmenter | Minimisée activement |
| **Réseaux lossy** | Performances dégradées | Meilleures performances |
| **Bufferbloat** | Peut remplir les buffers | Évite le bufferbloat |
| **WiFi/Mobile** | Sous-optimal | Optimisé |
**BBR (Bottleneck Bandwidth and Round-trip propagation time)** est l'algorithme développé par Google qui :
- Estime continuellement la bande passante disponible
- Maintient un RTT minimal
- Offre de meilleures performances sur réseaux modernes
### 3.4 Configuration QUIC complète après modification
```java
QuicServerCodecBuilder()
.sslContext(sslContext)
.tokenHandler(InsecureQuicTokenHandler.INSTANCE)
.maxIdleTimeout(playTimeout.toMillis(), TimeUnit.MILLISECONDS)
.ackDelayExponent(3L) // 8ms entre acks
.initialMaxData(524288L) // 512 KB total
.initialMaxStreamDataUnidirectional(0L) // Désactivé
.initialMaxStreamsUnidirectional(0L) // Désactivé
.initialMaxStreamDataBidirectionalLocal(131072L) // 128 KB par stream
.initialMaxStreamDataBidirectionalRemote(131072L) // 128 KB par stream
.initialMaxStreamsBidirectional(1L) // 1 stream
.discoverPmtu(true) // NOUVEAU
.congestionControlAlgorithm(QuicCongestionControlAlgorithm.BBR) // NOUVEAU
```
### 3.5 Contexte technique
Le `QUICTransport` configure également :
- **mTLS obligatoire** : `ClientAuth.REQUIRE`
- **Certificat auto-signé** : Généré au démarrage
- **IPv4/IPv6 dual-stack** : Bootstraps séparés
- **Options socket** : `SO_REUSEADDR`, `IP_DONTFRAGMENT`
- **Certificate fingerprinting** : Pour validation JWT
---
## 4. Timeout Setup Handler
**Fichier** : `com/hypixel/hytale/server/core/io/handlers/SetupPacketHandler.java`
**Ligne modifiée** : 102
### 4.1 Modification du timeout
```java
// Avant
this.setTimeout("send-world-settings", () -> this.assets != null, 1L, TimeUnit.SECONDS);
// Après
this.setTimeout("send-world-settings", () -> this.assets != null, 10L, TimeUnit.SECONDS);
```
### 4.2 Contexte
Ce timeout attend que les assets soient prêts (`this.assets != null`) avant d'envoyer les paramètres du monde (`WorldSettings`) au client.
### 4.3 Raisons du changement
| Scénario | Impact avec 1s | Impact avec 10s |
|----------|----------------|-----------------|
| Connexion haute latence | Timeout fréquent | Toléré |
| Serveur avec nombreux asset packs | Timeout possible | Toléré |
| Charge serveur élevée | Timeout possible | Toléré |
| Démarrage lent des assets | Déconnexion | Attente correcte |
### 4.4 Autres timeouts dans le SetupPacketHandler
| Timeout | Durée | Condition | Usage |
|---------|-------|-----------|-------|
| `send-world-settings` | **10s** (modifié) | `this.assets != null` | Attente chargement assets |
| `receive-assets-request` | 120s | Packet `RequestAssets` reçu | Négociation d'assets |
| `send-assets` | 120s | `SendCommonAssetsEvent` terminé | Envoi assets + translations |
| `add-to-universe` | 60s | `Universe.addPlayer()` terminé | Création joueur dans monde |
### 4.5 Phases du Setup Handler
```
[Post-Auth] → registered0()
[WorldSettings + ServerInfo envoyés]
[Client envoie RequestAssets] (120s max)
[Envoi CommonAssets + Translations] (120s max)
[Client envoie PlayerOptions]
[AddToUniverse] (60s max)
[Joueur connecté au monde]
```
---
## 5. Physique des Items - Collisions latérales
**Fichier** : `com/hypixel/hytale/server/core/modules/entity/item/ItemPhysicsSystem.java`
**Lignes modifiées** : 84-97
### 5.1 Architecture du système
| Élément | Description |
|---------|-------------|
| **Classe parente** | `EntityTickingSystem<EntityStore>` |
| **Pattern** | Entity Component System (ECS) |
| **Tick rate** | Appelé à chaque tick du monde |
### 5.2 Composants utilisés
| Composant | Rôle |
|-----------|------|
| `ItemPhysicsComponent` | État physique spécifique aux items |
| `BoundingBox` | Boîte de collision 3D |
| `Velocity` (VelocityComponent) | Vecteur de vélocité |
| `TransformComponent` | Position et rotation |
### 5.3 Modification de la gestion des collisions
**Avant** : Seules les collisions verticales (sol) étaient gérées
```java
BlockCollisionData blockCollisionData = collisionResult.getFirstBlockCollision();
if (blockCollisionData != null && blockCollisionData.collisionNormal.equals(Vector3d.UP)) {
velocityComponent.setZero();
position.assign(blockCollisionData.collisionPoint);
} else {
velocityComponent.assignVelocityTo(scaledVelocity).scale(dt);
position.add(scaledVelocity);
}
```
**Après** : Gestion complète de toutes les directions de collision
```java
BlockCollisionData blockCollisionData = collisionResult.getFirstBlockCollision();
if (blockCollisionData != null) {
if (blockCollisionData.collisionNormal.equals(Vector3d.UP)) {
// Collision avec le sol : arrêt complet
velocityComponent.setZero();
position.assign(blockCollisionData.collisionPoint);
} else {
// Collision latérale : annulation de la composante perpendiculaire
Vector3d velocity = velocityComponent.getVelocity();
double dot = velocity.dot(blockCollisionData.collisionNormal);
Vector3d velocityToCancel = blockCollisionData.collisionNormal.clone().scale(dot);
velocity.subtract(velocityToCancel);
}
} else {
velocityComponent.assignVelocityTo(scaledVelocity).scale(dt);
position.add(scaledVelocity);
}
```
### 5.4 Explication mathématique
Pour une collision latérale (mur, escalier, pente) :
1. **Calcul du produit scalaire** : `dot = velocity · normal`
- Mesure la composante de vélocité perpendiculaire à la surface
2. **Calcul du vecteur à annuler** : `velocityToCancel = normal × dot`
- Vecteur perpendiculaire à la surface avec magnitude égale à la composante perpendiculaire
3. **Soustraction** : `velocity = velocity - velocityToCancel`
- Projette la vélocité sur le plan tangent à la surface
**Formule vectorielle** : `v' = v - (v · n) × n`
### 5.5 Impact sur le gameplay
| Comportement | Avant | Après |
|--------------|-------|-------|
| Item lancé contre un mur | Traversait ou s'arrêtait net | Glisse le long du mur |
| Item tombant sur une pente | Comportement incohérent | Roule correctement |
| Item dans un coin | Pouvait se bloquer/traverser | Réagit physiquement |
### 5.6 Méthodes de détection de collision
Le système utilise deux méthodes selon la magnitude de vitesse :
| Méthode | Condition | Usage |
|---------|-----------|-------|
| `findBlockCollisionsShortDistance()` | Mouvement faible | Optimisation pour petits mouvements |
| `findBlockCollisionsIterative()` | Mouvement important | Précision pour grandes vitesses |
---
## 6. Nouvelle raison d'arrêt MISSING_ASSETS
**Fichier** : `com/hypixel/hytale/server/core/ShutdownReason.java`
**Ligne ajoutée** : 17
### 6.1 Nouvelle constante
```java
public static final ShutdownReason MISSING_ASSETS = new ShutdownReason(7);
```
### 6.2 Liste complète des ShutdownReason
| Constante | Exit Code | Description | Utilisation |
|-----------|-----------|-------------|-------------|
| `SIGINT` | 130 | Signal d'interruption | Ctrl+C ou SIGINT reçu |
| `SHUTDOWN` | 0 | Arrêt gracieux | Commande `/stop` ou shutdown normal |
| `CRASH` | 1 | Crash serveur | Erreur non gérée pendant le boot |
| `AUTH_FAILED` | 2 | Échec authentification | Impossible de s'authentifier auprès des services |
| `WORLD_GEN` | 3 | Erreur génération monde | WorldGenLoadException |
| `CLIENT_GONE` | 4 | Client singleplayer disparu | Mode singleplayer, client déconnecté |
| `MISSING_REQUIRED_PLUGIN` | 5 | Plugin core manquant | Plugin requis non trouvé |
| `VALIDATE_ERROR` | 6 | Validation assets échouée | Erreur de validation des assets |
| **`MISSING_ASSETS`** | **7** | **Aucun asset pack** | **NOUVEAU : Aucun pack chargé** |
### 6.3 Pattern d'utilisation
```java
// Utilisation simple
HytaleServer.get().shutdownServer(ShutdownReason.MISSING_ASSETS);
// Avec message personnalisé
HytaleServer.get().shutdownServer(
ShutdownReason.MISSING_ASSETS.withMessage("Failed to load any asset packs")
);
```
La méthode `withMessage(String)` crée une nouvelle instance de `ShutdownReason` avec le même code de sortie mais un message additionnel pour les logs.
---
## 7. Validation des Asset Packs
**Fichier** : `com/hypixel/hytale/server/core/asset/AssetModule.java`
**Lignes modifiées** : 24, 104-106
### 7.1 Nouvel import
```java
import com.hypixel.hytale.server.core.ShutdownReason;
```
### 7.2 Nouvelle vérification
Après le chargement des asset packs depuis tous les répertoires de mods :
```java
for (Path modsPath : Options.getOptionSet().valuesOf(Options.MODS_DIRECTORIES)) {
this.loadPacksFromDirectory(modsPath);
}
// NOUVEAU : Vérification des assets
if (this.assetPacks.isEmpty()) {
HytaleServer.get().shutdownServer(
ShutdownReason.MISSING_ASSETS.withMessage("Failed to load any asset packs")
);
return;
}
```
### 7.3 Cycle de vie de l'AssetModule
```
setup()
[Création AssetMonitor si --disable-file-watcher absent]
[Chargement packs CLI]
[Chargement packs mods/]
[NOUVEAU: Vérification assetPacks.isEmpty()] ← Shutdown si vide
[Registration event listeners]
LoadAssetEvent dispatch
[AssetRegistryLoader charge tous les assets]
BootEvent
[Log nombre total d'assets]
```
### 7.4 Sources de chargement des asset packs
| Source | Méthode | Description |
|--------|---------|-------------|
| CLI `--assets` | `loadPacksFromDirectory()` | Chemins spécifiés en ligne de commande |
| Dossier `mods/` | `loadPacksFromDirectory()` | Packs dans le dossier mods par défaut |
| Autres `--mods-directories` | `loadPacksFromDirectory()` | Répertoires additionnels |
### 7.5 Types de packs supportés
| Type | Détection | Immutabilité |
|------|-----------|--------------|
| ZIP/JAR | Contient `manifest.json` | Toujours immutable |
| Répertoire | Contient `manifest.json` | Immutable si `CommonAssetsIndex.hashes` présent |
| Répertoire sans hash | Contient `manifest.json` | Mutable (rechargement possible) |
### 7.6 AssetMonitor (rechargement temps réel)
Si `--disable-file-watcher` n'est pas passé, un `AssetMonitor` est créé pour :
- Surveiller les modifications de fichiers dans les packs mutables
- Déclencher un rechargement automatique des assets modifiés
- Permettre le développement itératif sans redémarrage serveur
---
## 8. Télémétrie Sentry enrichie
**Fichier** : `com/hypixel/hytale/server/core/HytaleServer.java`
**Lignes modifiées** : 30, 51, 155-170
### 8.1 Nouveaux imports
```java
import com.hypixel.hytale.server.core.auth.SessionServiceClient;
import io.sentry.protocol.User;
```
### 8.2 Nouvelles données utilisateur collectées
Dans le callback `beforeSend` de Sentry, un objet `User` est maintenant créé et attaché à chaque événement :
```java
User user = new User();
HashMap<String, Object> unknown = new HashMap<String, Object>();
user.setUnknown(unknown);
// 1. Hardware UUID
UUID hardwareUUID = HardwareUtil.getUUID();
if (hardwareUUID != null) {
unknown.put("hardware-uuid", hardwareUUID.toString());
}
// 2. Mode d'authentification
ServerAuthManager authManager = ServerAuthManager.getInstance();
unknown.put("auth-mode", authManager.getAuthMode().toString());
// 3. Profil utilisateur (si authentifié)
SessionServiceClient.GameProfile profile = authManager.getSelectedProfile();
if (profile != null) {
user.setUsername(profile.username);
user.setId(profile.uuid.toString());
}
// 4. Adresse IP (auto-détection Sentry)
user.setIpAddress("{{auto}}");
event.setUser(user);
```
### 8.3 Données collectées par Sentry
#### Données utilisateur (nouvelles)
| Champ Sentry | Source | Type | Description |
|--------------|--------|------|-------------|
| `user.username` | `GameProfile.username` | String | Nom d'utilisateur du compte |
| `user.id` | `GameProfile.uuid` | UUID String | Identifiant unique du compte |
| `user.ip_address` | Auto-détecté | IP | Adresse IP du serveur |
| `user.unknown.hardware-uuid` | `HardwareUtil.getUUID()` | UUID String | Identifiant matériel unique |
| `user.unknown.auth-mode` | `ServerAuthManager.getAuthMode()` | String | Mode d'auth (ONLINE/OFFLINE/etc.) |
#### Données contextuelles (existantes)
| Contexte | Données |
|----------|---------|
| **server** | Nom, max-players, listeners |
| **universe** | Chemin, nombre joueurs, liste worlds |
| **plugins** | Version et état de chaque plugin |
### 8.4 Conditions de collecte
Les données Sentry sont collectées **uniquement si** :
- Aucun early plugin avec transformer n'est chargé
- Le flag `--disable-sentry` n'est pas passé
- Une erreur se produit (beforeSend callback)
### 8.5 Filtrage des erreurs
Les erreurs provenant de plugins tiers sont filtrées avant envoi :
```java
if (PluginClassLoader.isFromThirdPartyPlugin(event.getThrowable())) {
return null; // Ne pas envoyer
}
```
### 8.6 Configuration Sentry
| Paramètre | Valeur |
|-----------|--------|
| DSN | `https://...@sentry.hytale.com/4` |
| Environnement | `release` |
| Tags | `patchline` |
| Server name | Hostname du serveur |
---
## 9. Fix NullPointerException CraftingManager
**Fichier** : `com/hypixel/hytale/builtin/crafting/component/CraftingManager.java`
**Ligne modifiée** : 500
### 9.1 Modification
**Avant** :
```java
if (!itemResourceType.id.equals(slotCraftingMaterial.getResourceTypeId())) continue;
```
**Après** :
```java
if (!slotCraftingMaterial.getResourceTypeId().equals(itemResourceType.id)) continue;
```
### 9.2 Contexte du code
Cette ligne se trouve dans une méthode de comparaison des matériaux de craft, dans une boucle imbriquée :
```java
// Ligne ~497-502
if (slotCraftingMaterial.getResourceTypeId() == null ||
slotItemStack.getItem().getResourceTypes() == null) continue;
for (ItemResourceType itemResourceType : slotItemStack.getItem().getResourceTypes()) {
if (!slotCraftingMaterial.getResourceTypeId().equals(itemResourceType.id)) continue;
return true;
}
```
### 9.3 Analyse du problème
| Variable | Peut être null ? | Raison |
|----------|------------------|--------|
| `slotCraftingMaterial.getResourceTypeId()` | Non | Vérifié 2 lignes avant |
| `itemResourceType.id` | **Oui** | Pas de vérification préalable |
**Problème** : Appeler `.equals()` sur `itemResourceType.id` quand celui-ci est `null` lance un `NullPointerException`.
**Solution** : Inverser l'ordre de comparaison pour appeler `.equals()` sur la valeur garantie non-null.
### 9.4 Architecture du CraftingManager
| Élément | Description |
|---------|-------------|
| **Interface** | `Component<EntityStore>` (ECS) |
| **Attaché à** | Joueurs |
| **Rôle** | Gestion complète du crafting |
### 9.5 Classes internes
| Classe | Rôle |
|--------|------|
| `CraftingJob` | Représente un travail de crafting en cours |
| `BenchUpgradingJob` | Représente une amélioration d'établi |
### 9.6 Flow de crafting
```
queueCraft()
[Job ajouté à BlockingQueue]
tick() [appelé chaque tick]
[Vérifie matériaux disponibles] ← Fix NPE ici
[Retire matériaux de l'inventaire]
[Accumule temps]
[Si terminé: giveOutput()]
[Items craftés donnés au joueur]
```
### 9.7 Impact
| Aspect | Avant | Après |
|--------|-------|-------|
| Items avec `ResourceType.id = null` | Crash du crafting | Comparaison correcte |
| Stabilité serveur | NPE possible | Stable |
| Crafting avec ressources custom | Potentiellement cassé | Fonctionnel |
---
## Résumé des impacts
### Performance
| Changement | Impact |
|------------|--------|
| BBR congestion control | Amélioration significative du débit réseau |
| PMTU Discovery | Réduction de la fragmentation, meilleur throughput |
| Timeout setup 10s | Négligeable |
### Sécurité
| Changement | Impact |
|------------|--------|
| Clock skew 300s | Plus permissif mais toujours sécurisé |
| Validation iat | Protection contre tokens futurs |
| Échec auth → disconnect | Fermeture d'une faille potentielle |
### Stabilité
| Changement | Impact |
|------------|--------|
| MISSING_ASSETS shutdown | Erreur claire au lieu de comportement indéfini |
| Fix NPE CraftingManager | Élimination d'un crash potentiel |
| Collisions latérales items | Comportement physique cohérent |
### Télémétrie
| Changement | Impact |
|------------|--------|
| User data Sentry | Meilleur debugging des erreurs |
| Hardware UUID | Identification des problèmes matériels |
| Auth mode tracking | Corrélation erreurs/mode d'auth |
---
## Fichiers modifiés
| Fichier | Insertions | Suppressions |
|---------|------------|--------------|
| `server/core/auth/JWTValidator.java` | +11 | -6 |
| `server/core/HytaleServer.java` | +18 | 0 |
| `server/core/ShutdownReason.java` | +1 | 0 |
| `server/core/asset/AssetModule.java` | +5 | 0 |
| `server/core/io/handlers/SetupPacketHandler.java` | +1 | -1 |
| `server/core/io/handlers/login/HandshakeHandler.java` | +1 | -2 |
| `server/core/io/transport/QUICTransport.java` | +2 | -1 |
| `server/core/modules/entity/item/ItemPhysicsSystem.java` | +11 | -3 |
| `builtin/crafting/component/CraftingManager.java` | +1 | -1 |
| **Total** | **+55** | **-18** |