25 KiB
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
- Authentification & Sécurité JWT
- Gestion d'échec d'authentification serveur
- Transport QUIC - Optimisations réseau
- Timeout Setup Handler
- Physique des Items - Collisions latérales
- Nouvelle raison d'arrêt MISSING_ASSETS
- Validation des Asset Packs
- Télémétrie Sentry enrichie
- 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)
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) :
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) :
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 :
- Auth Token (JWTClaims) : Authentification client, contient username, IP, certificate fingerprint
- Identity Token (IdentityTokenClaims) : Identité du joueur, requiert scopes
hytale:clientouhytale:editor - 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 :
.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 :
.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 :
- Validation Identity Token : Vérifie le token du client, UUID, scopes
- Demande Auth Grant : Récupère le server session token, appel async au
SessionServiceClient - Traitement Token Client : Vérifie JWT signature, certificate binding, UUID/username
- Échange Server Token : Échange auth grant contre token avec fingerprint serveur
- Complétion : Crée
PlayerAuthentication, passe auSetupPacketHandler
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
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
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
// 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
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
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) :
-
Calcul du produit scalaire :
dot = velocity · normal- Mesure la composante de vélocité perpendiculaire à la surface
-
Calcul du vecteur à annuler :
velocityToCancel = normal × dot- Vecteur perpendiculaire à la surface avec magnitude égale à la composante perpendiculaire
-
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
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
// 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
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 :
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
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 :
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-sentryn'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 :
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 :
if (!itemResourceType.id.equals(slotCraftingMaterial.getResourceTypeId())) continue;
Après :
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 :
// 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 |