L'extension de l'inventaire Metin2 est l'une des fonctionnalités les plus demandées sur un serveur : dès que le sac par défaut est plein, le farm, les drops et la gestion du dépôt deviennent pénibles. L'essentiel est de comprendre que l'inventaire n'est pas défini à un seul endroit — le nombre de slots vit à la fois dans la source serveur et côté client, et ces deux valeurs doivent correspondre exactement. Dans ce guide, je détaille pas à pas, et en toute sécurité, comment ajouter des slots de sac et des pages supplémentaires.
Comment fonctionne l'inventaire : slots, pages et la constante partagée
Dans Metin2, l'inventaire est un tableau de slots. Dans la disposition classique, chaque page est composée de 45 slots (9 colonnes × 5 lignes), et le nombre total de slots doit être un multiple de la taille de page. La valeur par défaut dans la plupart des sources est 90, c'est-à-dire deux pages. Si tu veux quatre pages, tu mets le total à 180.
- Slots totaux = nombre de pages × taille de page (45).
- Les positions des slots (
pos) commencent à 0 ; les slots d'équipement se trouvent dans un bloc séparé au-dessus de cette plage. - Si le serveur et le client ne partagent pas la même constante, les positions des objets se décalent, ce qui provoque des pertes d'objets et des crashs.
Côté serveur : length.h et INVENTORY_MAX_NUM
Le cœur de l'extension est la constante du fichier common/length.h. Tu y trouves le nombre total de slots et la taille de page :
// common/length.h
enum EInventoryConstants
{
INVENTORY_MAX_NUM = 90, // 2 pages × 45 -> mets 180 pour 4 pages
INVENTORY_PAGE_SIZE = 45,
};
Change INVENTORY_MAX_NUM et assure-toi que la valeur reste un multiple propre de 45. Recompile ensuite le binaire game :
cd Server/game/src
make clean && make -j$(nproc)
Cette constante est utilisée à de nombreux endroits qui valident les objets : IsValidItemPosition dans char_item.cpp et les fonctions de déplacement/dépôt d'objets, ainsi que les vérifications de paquets dans input_main.cpp. Comme ils s'appuient tous sur INVENTORY_MAX_NUM, modifier la seule source suffit — pas besoin de traquer des if supplémentaires.
Base de données : la table item et les contrôles de position
Les objets ne sont pas stockés sur le joueur mais dans une table item séparée ; chaque ligne indique son emplacement avec les colonnes window (inventaire/équipement) et pos. Tu n'as pas besoin de modifier le schéma pour une extension, car pos est déjà un champ entier capable de contenir les nouvelles valeurs, plus grandes. Vérifie tout de même deux choses :
- Sauvegarde d'abord : exporte les tables
playeretitem. Un décalage de position peut provoquer une perte d'objets. - Teste avec un compte de test : place des objets sur les nouvelles pages et relog le personnage ; vérifie que les nouvelles valeurs de
pos(par ex. 90–179) sont bien écrites dans la tableitem.
Il est aussi judicieux de recompiler le cache DB, car db et game partagent les mêmes en-têtes de protocole.
Source client : garder la constante synchronisée
La règle la plus critique : le client doit connaître le même nombre total de slots. Dans la source client, les tableaux d'inventaire et les structures de paquets sont dimensionnés par leur propre constante dans le projet UserInterface. Si tu es passé de 90 à 180 sur le serveur, mets à jour la même valeur côté client et recompile UserInterface :
// Client UserInterface (par ex. Packet.h / Locale_inc.h)
#define INVENTORY_MAX_NUM 180
#define INVENTORY_PAGE_SIZE 45
Si tu n'as pas la source client, agrandir l'interface uniquement avec python ne suffit pas : le binaire est toujours compilé avec l'ancien nombre de slots, donc les objets placés sur les pages supplémentaires restent invisibles ou la connexion est coupée. C'est pourquoi la source client est indispensable pour une extension d'inventaire.
Python client : des pages supplémentaires avec uiInventory.py
Une fois le binaire compilé à la bonne taille, c'est au tour de l'interface. Dans root/uiInventory.py, la fenêtre d'inventaire est dessinée avec ses boutons de page et sa grille de slots. Voici ce qu'il faut faire :
- Augmente le nombre de pages : mets à jour la boucle et la logique de
GetInventoryPageCountpour que la fenêtre affiche les boutons/onglets de page supplémentaires. - Calcule les positions des slots à partir de la constante
player.INVENTORY_PAGE_SIZE; nettoie tout nombre codé en dur comme 90. - Si la fenêtre est trop petite, ajuste la largeur/hauteur dans le
.pyou modifie les images.subconcernées.
Le côté python ne fait que dessiner ; la vraie limite de slots est fixée par le binaire. Le nombre de pages que tu affiches en python doit donc correspondre au INVENTORY_MAX_NUM compilé — si tu en affiches davantage, les clics tombent dans le vide.
Tests et erreurs fréquentes
Après le changement, teste toujours de bout en bout :
- Incohérence serveur/client : l'erreur la plus courante. Si les deux côtés sont compilés avec un
INVENTORY_MAX_NUMdifférent, les positions des objets se décalent. Mets à jour et compile les deux en même temps. - Une valeur qui n'est pas un multiple de 45 : la page reste à moitié dessinée et les dernières lignes ne sont pas cliquables. Garde toujours le total divisible par 45.
- Contrôle d'exploit : des fenêtres séparées comme le pet ou le dragon soul utilisent aussi des positions ; en agrandissant la constante d'inventaire, vérifie que leurs plages de pos n'entrent pas en collision.
- Une sauvegarde stable : avant la mise en production, conserve un dump DB et les anciens binaires pour garder ton chemin de rollback ouvert.
Questions fréquentes
Puis-je étendre l'inventaire sans la source client ?
Non. Avec python, tu agrandis seulement le visuel, mais le binaire est compilé avec l'ancien nombre de slots ; les slots supplémentaires ne fonctionnent pas et l'incohérence avec le serveur provoque des crashs. Il te faut à la fois la source serveur et la source client.
Les joueurs existants vont-ils perdre leurs objets ?
Si tu augmentes le nombre total de slots, les positions existantes (0–89) restent en place et seuls de nouveaux slots vides sont ajoutés ; aucune perte d'objets. Mais une mauvaise constante ou une réduction peut entraîner des pertes, alors exporte d'abord la DB.
Pourquoi doit-ce être un multiple de 45 ?
Une page d'inventaire fait 9×5 = 45 slots. Si le total n'est pas un multiple de cela, la dernière page est dessinée de manière incomplète et les index de slots entre l'interface et le binaire ne correspondent plus.
Tu veux une extension d'inventaire propre et sans exploit dans ton projet de serveur ? Je peux t'aider sur le développement de systèmes côté source et client Metin2 — discutons de ton projet : contacte-moi.