Init
This commit is contained in:
@@ -0,0 +1,761 @@
|
||||
# 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** |
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user