Releases: Loutreee/StatCraft
2.3.1
2.3.0
1. Configuration et Intégration de l'API REST
- Endpoints REST supplémentaires
- Création d’un ScoreController avec :
/api/score/:playerName
pour obtenir le score total d’un joueur./api/score/history/:playerName
pour obtenir l’historique complet (tous les snapshots avec timestamp et score total) d’un joueur./api/scores
pour récupérer l’historique des scores de tous les snapshots.
- Création d’un ScoreController avec :
2. Gestion des Statistiques en Jeu et Calcul du Score
-
Gestion des événements du jeu :
- Bloc miné : Événement
BlockBreakEvent
incrémente le compteur de blocs minés et ajoute des points selon la config. - Item crafté : Passage de
PrepareItemCraftEvent
àCraftItemEvent
pour n’incrémenter le compteur qu’une fois l’item réellement crafté.- Gestion particulière du shift-click pour multiplier la quantité (ex. pour les sticks qui produisent 4 unités).
- Mob tué : Événement
EntityDeathEvent
incrémente le compteur de mobs tués par un joueur et ajoute des points selon la config.
- Bloc miné : Événement
-
Calcul du score par catégorie :
- Ajout dans la classe
PlayerStats
de champs pour chaque sous-score :blockScore
,craftScore
,mobScore
, ettimeScore
- Le
totalScore
est la somme de tous ces sous-scores. - Un point est attribué par minute de jeu (calculé via
Statistic.PLAY_ONE_MINUTE
).
- Ajout dans la classe
-
Mise à jour des compteurs et scores :
- À chaque action, en plus d'incrémenter le compteur (dans des maps pour blocs, items et mobs), le score correspondant est ajouté via des méthodes telles que
addBlockScore(amount)
. - Les scores sont définis selon les valeurs présentes dans le fichier de config, accessibles via le
ScoreService
.
- À chaque action, en plus d'incrémenter le compteur (dans des maps pour blocs, items et mobs), le score correspondant est ajouté via des méthodes telles que
3. Snapshots et Persistance des Données
-
Insertion régulière de snapshots :
- Une tâche planifiée (toutes les 30 secondes / 600 ticks) parcourt tous les joueurs connectés et insère un snapshot dans la collection
"playerStatsSnapshots"
. - Chaque snapshot contient :
- Les compteurs détaillés (maps de blocs minés, items craftés, mobs tués).
- Le temps de jeu actuel.
- Les sous-scores (
blockScore
,craftScore
,mobScore
,timeScore
) et letotalScore
. - Un timestamp (format ISO) pour tracer l’évolution dans le temps.
- Une tâche planifiée (toutes les 30 secondes / 600 ticks) parcourt tous les joueurs connectés et insère un snapshot dans la collection
-
Insertion lors de la déconnexion :
- Lors du
PlayerQuitEvent
, un snapshot est inséré immédiatement pour garantir que l’état final du joueur est sauvegardé. - Les données en mémoire pour le joueur sont ensuite supprimées pour libérer des ressources.
- Lors du
-
Restauration des statistiques depuis le dernier snapshot :
- Lorsqu’un joueur se connecte, la méthode
StatsManager.initializePlayerStats(Player)
récupère le snapshot le plus récent (trié par timestamp) et restaure :- Les maps de statistiques (blocs, items, mobs).
- Les sous-scores (
blockScore
,craftScore
,mobScore
,timeScore
) et letotalScore
.
- Ceci assure une continuité des données et évite de repartir de zéro après un redémarrage ou une déconnexion.
- Lorsqu’un joueur se connecte, la méthode
4. Visualisation et API pour l’Historique
-
Leaderboard et graphique :
- Un composant React LeaderboardChart récupère les snapshots via l’API REST.
- Fusion et tri chronologique des snapshots :
- Les snapshots sont fusionnés par timestamp (utilisation de la valeur ISO) et triés pour afficher des lignes continues.
- Une imputation des valeurs manquantes est réalisée pour assurer une courbe continue, reliant chaque point dans le temps.
-
Modification du graphique :
- Utilisation de
<LineChart>
avecdot={false}
pour tracer des lignes continues reliant tous les points. - Correction pour que les données soient affichées dans l’ordre chronologique, en imputant la dernière valeur connue pour les joueurs absents dans un snapshot donné.
- Utilisation de
-
Routes API supplémentaires pour les scores :
- Ajout d’un endpoint pour obtenir l’historique complet des scores d’un joueur (
/api/score/history/:playerName
).
- Ajout d’un endpoint pour obtenir l’historique complet des scores d’un joueur (
5. Développement et Intégration Front-End
-
Intégration de l’application React :
- L’application React est buildée et servie par Javalin à partir du dossier
/web
. - En développement, un proxy est configuré (ou CORS est géré proprement) pour que l’application React puisse accéder à l’API REST qui tourne dans le plugin.
- Le composant PlayersList et PlayerStats ont été adaptés pour utiliser les endpoints API plutôt que des données statiques.
- L’application React est buildée et servie par Javalin à partir du dossier
-
Exemple d’appel API dans PlayerStats :
- Le composant récupère via
fetch("/api/stats/{player}/latest")
le snapshot le plus récent et affiche les informations clés (timestamp, temps de jeu, scores par catégorie et score total). - Un nouveau composant LeaderboardChart affiche l’évolution des scores dans un graphique linéaire continu.
- Le composant récupère via
2.2.0
1. Gestion des Statistiques et Calcul du Score
a. Score par catégorie et score global
-
Nouveaux champs dans PlayerStats :
- Ajout de sous-scores pour chaque catégorie :
blockScore
pour les blocs minéscraftScore
pour les items craftésmobScore
pour les mobs tuéstimeScore
pour le temps de jeu (1 point par minute)
- Ajout d’un champ
totalScore
qui est la somme des sous-scores.
- Ajout de sous-scores pour chaque catégorie :
-
Méthodes d'incrémentation :
- Ajout des méthodes
addBlockScore(int amount)
,addCraftScore(int amount)
,addMobScore(int amount)
, etaddTimeScore(int amount)
dans PlayerStats. - Lors des événements (bloc cassé, item crafté, mob tué), on récupère la valeur du score depuis la config via ScoreService et on incrémente le sous-score correspondant ET le
totalScore
.
- Ajout des méthodes
b. Gestion du temps de jeu
- Lors de l'insertion d'un snapshot, le temps de jeu actuel (exprimé en minutes via
Statistic.PLAY_ONE_MINUTE
) est comparé avec le dernier temps enregistré (lastPlayTime
). - La différence (différence en minutes) est ajoutée au
timeScore
et autotalScore
. - Mise à jour du champ
lastPlayTime
pour assurer la continuité.
2. Chargement de la Configuration
-
ConfigLoader :
- Lecture du fichier
config.yml
pour récupérer :- Le port à utiliser pour l’API REST (
Web.port
). - Les listes de scores pour les blocs, mobs et crafts (chaque entrée définit un nom et un score).
- Le port à utiliser pour l’API REST (
- Fournit des méthodes telles que
getBlockScore(Material)
,getMobScore(EntityType)
etgetCraftScore(Material)
qui sont utilisées par le ScoreService pour incrémenter les scores.
- Lecture du fichier
-
ScoreService :
- Une couche intermédiaire qui encapsule l’accès aux scores via le ConfigLoader.
- Fournit notamment une méthode
getTimeScore()
renvoyant 1 point par minute.
3. Insertion et Restauration des Snapshots
a. Insertion des snapshots
-
StatsSnapshotService :
- La méthode
insertPlayerSnapshot(...)
a été mise à jour pour enregistrer dans le document les maps de statistiques ainsi que les sous-scores (blockScore
,craftScore
,mobScore
,timeScore
) et letotalScore
. - Chaque snapshot inclut également un timestamp (format ISO) pour tracer l'évolution dans le temps.
- La méthode
-
Insertion programmée et à la déconnexion :
- Une tâche planifiée (toutes les 30 secondes) insère un snapshot pour tous les joueurs connectés.
- Lors du PlayerQuitEvent, un snapshot est inséré immédiatement pour sauvegarder l'état final des statistiques du joueur.
b. Restauration depuis le dernier snapshot
- StatsManager.initializePlayerStats(Player) :
- Lorsqu’un joueur se connecte, la méthode interroge la collection
"playerStatsSnapshots"
pour récupérer le snapshot le plus récent (trié partimestamp
décroissant). - Les maps (blocs minés, items craftés, mobs tués) ainsi que les sous-scores (
blockScore
,craftScore
,mobScore
,timeScore
) et letotalScore
sont réinjectés dans l’objet PlayerStats. - Ceci permet à l'accumulation des statistiques de reprendre là où elle s'était arrêtée avant redémarrage ou déconnexion.
- Lorsqu’un joueur se connecte, la méthode interroge la collection
4. Correction du Comptage lors du Craft
- Problème des sticks :
- Lors du craft d’un item comme des sticks, le joueur reçoit un lot (par exemple, 4 sticks) au lieu d’un seul.
- Solution :
- Dans l'événement
onCraftItem
, on récupère le résultat de la recette viaevent.getRecipe().getResult()
et on multiplie la quantité de base par le nombre de fois que le craft est effectué (en cas de shift-click, par exemple). - La méthode
incrementItemCrafted
dans StatsManager a été modifiée pour accepter une quantité en paramètre.
- Dans l'événement
5. API REST et Serveur Web
-
Utilisation de Javalin 6 :
- Le port utilisé pour démarrer Javalin est désormais chargé depuis le fichier de configuration (
config.yml
) viaconfigLoader.getWebPort()
. - Javalin est configuré pour servir les fichiers statiques depuis le dossier
/web
du classpath et exposer les endpoints REST.
- Le port utilisé pour démarrer Javalin est désormais chargé depuis le fichier de configuration (
-
Endpoints API supplémentaires :
- Ajout de contrôleurs pour exposer les statistiques, tels que :
/api/allstats
pour obtenir tous les snapshots.- D’autres endpoints pour interroger les statistiques par catégorie (blocs, items, mobs, temps de jeu).
- Ajout de contrôleurs pour exposer les statistiques, tels que :
2.1.0
Ce changelog récapitule l'ensemble des modifications récentes apportées au plugin StatCraft pour :
- Accumuler les statistiques de jeu en mémoire (blocs minés, items craftés, mobs tués).
- Snapshoter ces statistiques à intervalles réguliers et lors de la déconnexion d'un joueur.
- Exposer ces données via une API REST (Javalin) et servir une application React.
1. Système de Statistiques en Mémoire
Création du StatsManager & PlayerStats
-
StatsManager :
- Gestion d'une map en mémoire associant l'UUID du joueur à un objet
PlayerStats
. - Méthodes pour incrémenter les compteurs lors des événements (bloc cassé, item crafté, mob tué).
- Gestion d'une map en mémoire associant l'UUID du joueur à un objet
-
PlayerStats :
- Stocke trois maps :
blocksMined
: nombre de blocs minés par type.itemsCrafted
: nombre d'items craftés par type.mobsKilled
: nombre de mobs tués par type.
- Ajout de méthodes
setBlocksMined()
,setItemsCrafted()
, etsetMobsKilled()
pour initialiser les compteurs à partir d'un snapshot (permet de reprendre après redémarrage).
- Stocke trois maps :
2. Gestion des Événements de Jeu
Événements Utilisés
-
onBlockBreak (BlockBreakEvent)
- Incrémente le compteur correspondant au type de bloc cassé.
- Log détaillé de l'événement (ex: "Alex a miné un bloc de STONE").
-
onCraftItem (CraftItemEvent)
- Incrémente le compteur pour l'item crafté (uniquement lorsque le craft est validé, pas lors de la préparation).
- Log pour confirmer l'item crafté.
-
onEntityDeath (EntityDeathEvent)
- Si un joueur tue un mob, incrémente le compteur du type de mob.
- Log pour indiquer quel type de mob a été tué.
-
onPlayerJoin (PlayerJoinEvent)
- Vérifie et insère le joueur dans la base Nitrite s'il n'y est pas déjà.
- Initialise les compteurs en mémoire à partir du dernier snapshot enregistré dans la base, pour garantir la continuité des statistiques.
-
onPlayerQuit (PlayerQuitEvent)
- Lorsqu'un joueur se déconnecte, un snapshot est immédiatement inséré dans la base pour préserver l'état final des statistiques.
- Supprime les données en mémoire pour libérer des ressources.
3. Snapshots des Statistiques
Insertion Périodique et à la Déconnexion
-
Snapshots Périodiques
- Une tâche planifiée (toutes les 30 secondes / 600 ticks) parcourt tous les joueurs connectés.
- Pour chaque joueur, les compteurs (blocs minés, items craftés, mobs tués) sont lus depuis le
StatsManager
et combinés avec le temps de jeu (viaStatistic.PLAY_ONE_MINUTE
). - Un snapshot est créé avec un timestamp (format ISO) et inséré dans la collection
"playerStatsSnapshots"
dans Nitrite. - Logs détaillés affichant, pour chaque joueur, le détail par type et le total de chaque catégorie.
-
Insertion lors de la Déconnexion
- Lors du
PlayerQuitEvent
, un snapshot est inséré immédiatement pour le joueur concerné. - Ceci permet de s'assurer que l'état final des statistiques est sauvegardé, évitant ainsi la réinitialisation à zéro lors d'un redémarrage.
- Lors du
4. API REST et Intégration de l'Application React
API REST avec Javalin
- Javalin 6 est utilisé pour exposer une API REST et servir les fichiers statiques de l'application React.
- Endpoints :
- /api/players : Gère la récupération et l'insertion de données sur les joueurs.
- /api/allstats : Expose l'ensemble des snapshots de statistiques en JSON.
- Configuration des Fichiers Statique :
- Les fichiers issus du build de l'application React sont placés dans le dossier
/web
du classpath. - Javalin est configuré pour servir ces fichiers via le chemin
/
(ou un autre chemin dédié).
- Les fichiers issus du build de l'application React sont placés dans le dossier
Application React
- L'application React (buildée avec Create React App) est intégrée dans les ressources du plugin.
- La configuration du champ
"homepage"
dans lepackage.json
a été ajustée pour correspondre au chemin de déploiement. - L'application communique avec l'API REST pour afficher les statistiques et réaliser des graphiques d'évolution.
5. Persistance et Reprise après Redémarrage
- Persistance des Snapshots
- Les snapshots sont stockés de manière persistante dans Nitrite.
- Reprise des Données en Mémoire
- Lors de la connexion d'un joueur, le dernier snapshot est récupéré pour initialiser les compteurs dans le
StatsManager
. - Ceci permet que les compteurs ne repartent pas de zéro après un redémarrage, assurant la continuité des statistiques.
- Lors de la connexion d'un joueur, le dernier snapshot est récupéré pour initialiser les compteurs dans le
2.0.0
Intégration de la base de données Nitrite
-
Mise en place de Nitrite DB :
- Intégration de Nitrite 4.3.0 pour stocker les données des joueurs.
- Création de la classe
NitriteBuilder
qui initialise la base de données avec MVStore. - Configuration du fichier de base de données dans le dossier
player_statistics/player_statistic.db
. - Gestion de l'ouverture (
openOrCreate("user", "password")
) et fermeture de la base viaNitriteBuilder.close()
lors de l'activation/désactivation du plugin.
-
Gestion du mapping d'entités :
- Création de la classe
PlayerData
pour représenter les joueurs. - Annotation de
PlayerData
avec@Entity
et@Id
provenant deorg.dizitart.no2.repository.annotations
pour permettre à Nitrite de mapper automatiquement les objets. - Résolution d'erreurs de mapping en enregistrant un convertisseur personnalisé.
- Création de la classe
-
Implémentation d’un EntityConverter personnalisé :
- Création de la classe
PlayerDataConverter
implémentantEntityConverter<PlayerData>
. - Utilisation de
Document.createDocument()
pour instancier concrètement les documents. - Enregistrement du convertisseur via la méthode en chaîne :
db = Nitrite.builder() .loadModule(storeModule) .registerEntityConverter(new PlayerDataConverter()) .openOrCreate("user", "password");
- Résolution des erreurs "Invalid repository type" et "Can't convert Document to type PlayerData".
- Création de la classe
Développement de l'API REST avec Javalin
-
Choix de Javalin 6 :
- Utilisation de Javalin 6 pour créer une API REST intégrée au plugin.
- Configuration de Javalin pour démarrer sur le port
28700
.
-
Configuration des fichiers statiques :
- Les fichiers statiques (build de l'application React) sont servis depuis le dossier
/web
(dans les ressources du plugin). - Configuration Javalin pour servir ces fichiers statiques sous le chemin
/static
:app = Javalin.create(config -> { config.staticFiles.add(staticFileConfig -> { staticFileConfig.directory = "/web"; // Dossier dans le classpath staticFileConfig.location = Location.CLASSPATH; staticFileConfig.hostedPath = "/static"; // Accessible via http://localhost:7000/static }); }).start(27800);
- Cela permet de dissocier les endpoints de l'API REST (ex.
/api/players
) du contenu statique.
- Les fichiers statiques (build de l'application React) sont servis depuis le dossier
-
Implémentation des endpoints REST :
- Création de la classe
PlayerController
qui expose les endpoints :- GET
/api/players
: renvoie la liste de tous les joueurs enregistrés. - POST
/api/players
: permet d'ajouter un nouveau joueur via un payload JSON.
- GET
- Utilisation de Javalin et de Jackson (intégré dans Javalin) pour gérer la conversion JSON ⇄ objets Java.
- Exemple d'endpoint dans
PlayerController
:app.get("/api/players", ctx -> { List<PlayerData> players = playerService.getAllPlayers(); ctx.json(players); });
- Création de la classe
PlayerService
pour encapsuler la logique d'accès à Nitrite.
- Création de la classe
Intégration de l'application React
-
Développement de l'application React :
- Création d'une application React avec Create React App.
- Installation d'Axios pour effectuer les requêtes HTTP vers l'API REST.
- Conception d'un composant
PlayersList
qui récupère et affiche la liste des joueurs via une requête GET à l'API (http://localhost:27800/api/players
).
-
Build et intégration dans le plugin :
- Build de l'application React (généralement avec
npm run build
). - Copie du contenu du dossier
build
dans le dossier/web
des ressources du plugin. - Mise à jour du champ
"homepage"
dans lepackage.json
de React pour qu'il corresponde au chemin/
(ex."homepage": "/"
) afin d'assurer la bonne résolution des chemins relatifs aux ressources (JS, CSS, images).
- Build de l'application React (généralement avec
-
Accès à l'application React via Javalin :
- L'application React est accessible à l'URL
http://localhost:7000/
et se charge automatiquement grâce au fichierindex.html
placé dans le dossier/web
.
- L'application React est accessible à l'URL
Autres améliorations et fonctionnalités
-
Tâche planifiée pour le log des joueurs :
- Mise en place d'une tâche planifiée (tous les 5 secondes) avec
BukkitRunnable
pour afficher la liste de tous les joueurs enregistrés dans la console du serveur. - Code exemple dans
StatCraft.java
dansonEnable()
:new BukkitRunnable() { @Override public void run() { logAllPlayers(); } }.runTaskTimer(this, 0L, 100L);
- La méthode
logAllPlayers()
parcourt le repository Nitrite et affiche chaque nom dans la console.
- Mise en place d'une tâche planifiée (tous les 5 secondes) avec
-
Intégration complète dans le plugin Minecraft :
- Toutes les classes Java (NitriteBuilder, PlayerData, PlayerDataConverter, PlayerService, PlayerController et l'intégration Javalin) sont contenues dans le package
me.loutreee.statCraft
. - L'API REST et le contenu statique (application React) sont servis via Javalin intégré dans le plugin.
- Le plugin gère à la fois la logique de jeu (ex. lors de la connexion des joueurs) et la persistance des données (via Nitrite DB).
- Toutes les classes Java (NitriteBuilder, PlayerData, PlayerDataConverter, PlayerService, PlayerController et l'intégration Javalin) sont contenues dans le package
1.2.0
Added
- Intégration initiale de l'application React :
- Mise en place d'un front-end React pour l'affichage des données du plugin.
- Build de l'application React et copie des fichiers statiques dans
src/main/resources/web
. - Configuration de Javalin pour servir les fichiers statiques depuis le dossier
web
, accessible à la racine de l'URL. - L'interface web réagit désormais sur l'URL racine (
http://localhost:<port>/index.html
), offrant une expérience utilisateur moderne.
Changed
- Mise à jour de la configuration du serveur Javalin pour intégrer le front-end React sans conflit avec les endpoints existants.
1.0.1
1.0.0
v5.1.0-beta
Changed
- Unification des chemins de stockage des statistiques
- Mise à jour du code pour utiliser un chemin cohérent (
player_statistics
) à la fois pour l'écriture et la lecture des fichiers XML. - Ajout d'un log supplémentaire (
System.out.println(...)
) permettant de vérifier le chemin absolu utilisé lors de la lecture des données. - Retrait de la référence au dossier
data/player_statistics
, qui causait un problème de lecture (« Aucune donnée trouvée »).
- Mise à jour du code pour utiliser un chemin cohérent (
Fixed
- Lecture des fichiers de statistiques
- Correction de l'erreur où le plugin ne trouvait pas les sessions et joueurs dans
data/player_statistics
alors qu'ils étaient écrits dansplayer_statistics
. - L'endpoint
/
de l'interface web Javalin fonctionne désormais correctement et affiche les scores issus des derniers fichiers XML de chaque joueur.
- Correction de l'erreur où le plugin ne trouvait pas les sessions et joueurs dans
v5.0.0-beta
WEB INTEGRATION !
Added
- Interface Web avec Javalin :
- Intégration de Javalin pour fournir une interface web.
- Ajout des méthodes
start()
etstop()
dans la classeWeb
pour démarrer et arrêter le serveur web sur le port 7070. - Démarrage automatique de Javalin lors de l'activation du plugin via la méthode
onEnable()
de la classe principale.
- Logging amélioré :
- Affichage d'informations de démarrage du serveur Javalin dans les logs.
- Journalisation détaillée des actions et de la configuration du plugin.
- Gestion des tâches asynchrones :
- Mise en place de tâches périodiques pour la journalisation des statistiques des joueurs.
Fixed
- Dépendances et packaging :
- Correction de l'erreur
NoClassDefFoundError: io/javalin/Javalin
en s'assurant que Javalin est bien embarqué dans le jar via le Maven Shade Plugin. - Vérification que le jar ombré (shaded jar) est bien utilisé lors du déploiement du plugin.
- Correction de l'erreur
- Accès réseau :
- S'assurer que Javalin écoute sur toutes les interfaces (0.0.0.0) afin de permettre l'accès à l'interface web via l'IP du serveur.
Changed
- Migration de SparkJava vers Javalin :
- Remplacement de SparkJava par Javalin pour la gestion de l'interface web, apportant ainsi une meilleure flexibilité et une syntaxe moderne.
- Configuration du plugin :
- Mise à jour du
pom.xml
pour intégrer correctement les dépendances et réduire les conflits lors du packaging.
- Mise à jour du
- Utilisation des logs :
- Modification de certains
System.out.println
pour utiliser le logger du plugin, réduisant ainsi les avertissements liés à l'utilisation de la sortie standard.
- Modification de certains