Parser de fichier .log de serveur de jeux comme Quake III, Urban Terror, Open Arena, Nexuiz ! Il permet de récupérer les points totaux de chaque Joueurs qui peuvent par la suite être utiliser comme sur GameTracker pour des stats de tout les joueurs ! Voir même une possibilité par la suite de créer un espace membre sur votre serveur pour sauver les stats de ceux qui sont inscrits !!!
Source / Exemple :
<?Php
/****************************************************
- Auteur : MEYER Thibault
- Date : Vendredi 25 juillet 2008
- fichier : score.php
?>
<?Php
function EstPresent($Pseudo, $Table)
{
for($i=0; $i < count($Table); $i++)
{
if($Table[$i]["Joueur"] == $Pseudo)
{
return true;
}
}
return false;
}
function LireLog($Fichier)
{
$Log = "";
$hFile = fopen($Fichier, "r");
while(!feof($hFile))
{
$Log .= fgets($hFile);
}
fclose($hFile);
if(strstr($Log, "score:") == NULL) return false;
$answer = array();
preg_match_all('(.*)', $Log, $answer, PREG_SET_ORDER);
$Log = "";
for($i=0,$j=0; $i<count($answer); $i++)
{
if(strstr($answer[$i][0], "score:") != null)
{
$Log[$j++] = $answer[$i][0];
}
}
$Stats = array();
for($i=0; $i<count($Log); $i++)
{
if($i == 0) sscanf($Log[$i], "%d:%d score: %d ping: %d client: %d %s %s %s %s" , $NULL, $NULL, $Stats[$i]["score"], $NULL, $NULL, $Stats[$i]["Joueur"], $Stats[$i]["nom2"], $Stats[$i]["nom3"], $Stats[$i]["nom4"]);
else sscanf($Log[$i], "%d:%d score: %d ping: %d client: %d %s %s %s %s" , $NULL, $NULL, $Stats[$i]["score"], $NULL, $NULL, $Stats[$i]["Joueur"], $Stats[$i]["nom2"], $Stats[$i]["nom3"], $Stats[$i]["nom4"]);
}
for($i=0; $i<count($Stats); $i++)
{
if(@$Stats[$i]["nom2"] != NULL) $Stats[$i]["Joueur"] .= " ".$Stats[$i]["nom2"];
if(@$Stats[$i]["nom3"] != NULL) $Stats[$i]["Joueur"] .= " ".$Stats[$i]["nom3"];
if(@$Stats[$i]["nom4"] != NULL) $Stats[$i]["Joueur"] .= " ".$Stats[$i]["nom4"];
}
//Cration de la liste des joueurs (regroupe les noms en double)
$Stats2[0]["Joueur"] = "";
for($i=0, $j=0; $i < count($Stats); $i++)
{
if(EstPresent($Stats[$i]["Joueur"], $Stats2) == false)
{
$Stats2[$j]["Joueur"] = $Stats[$i]["Joueur"];
$Stats2[$j++]["score"] = 0;
}
}
//Rregroupement des points par Joueurs
for($i=0; $i < count($Stats2); $i++)
{
for($j=0; $j < count($Stats); $j++)
{
if($Stats[$j]["Joueur"] == $Stats2[$i]["Joueur"]) $Stats2[$i]["score"] += $Stats[$j]["score"];
}
}
//Retourne la tableau
return $Stats2;
}
function EffaceLog($Fichier)
{
if(!file_exists($Fichier)) return false;
$hFile = fopen($Fichier, "w");
if(!$hFile) return false;
return true;
}
function array_sort($array, $key)
{
for ($i = 0; $i < sizeof($array); $i++)
{
$sort_values[$i] = $array[$i][$key];
}
asort ($sort_values);
reset ($sort_values);
while (list ($arr_key, $arr_val) = each ($sort_values))
{
$sorted_arr[] = $array[$arr_key];
}
return $sorted_arr;
}
?>
<?
//EXEMPLE
$mesStats = LireLog("games3.log");
$mesStats = array_sort($mesStats, "score");
if($mesStats == false) echo "AUCUNE PARTIE A TRAITER";
else{
for($i=count($mesStats)-1; $i>=0; $i--)
{
echo $mesStats[$i]["Joueur"]." : ".$mesStats[$i]["score"];
echo "<br>";
}
}
//QUAND MISE A JOUR DES JOUEURS SUR LA BASE DE DONNéES, ON VIDE LE JOURNAL
echo "<br>";
//if(EffaceLog("games3.log") == true) echo "EFFACER OK";
//else echo "EFFACER ERREUR";
?>
Conclusion :
Les éléments du tableau "nom2", "nom3"... C'est pour si jamais les gars on des pseudo du genre "Vieux Troll Des Bois" au lieu de "VieuxTrollDesBois"
31 juil. 2008 à 09:05
30 juil. 2008 à 12:05
Voila le code plus clair: http://pastebin.org/58433
Sur ce, bonne journée à vous, et bonne nuit à moi :]
30 juil. 2008 à 12:02
Voilà, on pase de 90 à 28 lignes et de 1.65secondes à 0.35 pour 100 lectures et tri de games3.log (sur mon pc)
function LireLog($Fichier) {
$hFile = fopen($Fichier, "r");
while(!feof($hFile)) {
fscanf($hFile, '%d:%d score: %d ping: %d client: %d %[^$]s',$h,$m,$score,$ping,$client,$nom);
if ($nom !== null && $score !== null){
$Stats[$nom] += $score;
$nom=null;
$score = null;
}
}
fclose($hFile);
//Retourne le tableau
return $Stats;
}
function array_sort($array, $key) {
switch($key) {
case 'score':
arsort($array);
break;
case 'joueur':
$array = array_flip($array);
arsort($array);
$array = array_flip($array);
break;
}
return $array;
}
PS: J'ai pris 10 minutes à faire ça, il peut encore être optimisé, mais pour moi c'est l'heure de dormir ;p
PPS: Si un admin croit que je pollue le sujet, libre à lui de supprimer mon post...
30 juil. 2008 à 10:34
Oui, mais faut comprendre qu'il y a de meilleures habitudes que d'autre. Enfin, moi, j'ai l'habitude de chercher à faire un code optimisé avant tout. D'ailleurs je passe sans doute plus de temps à comparer/bencher des façons de faire que de coder à proprement dit, tout ça dans le but de sauver quelques cycles CPU.
Il est évident que sur ton code seul on ne ressentira pas la différence, mais quelques dix millièmes de seconde par ci par là, ça finit par donner des secondes. Et puis, le but principal d'un vrai programmeur, est d'avoir un code le plus optimisé possible. C'est sans doute la différence entre l'amateur et le vrai passionné(je ne dis pas professionnel, parce que tristement professionnel veut rarement dire compétent): Un veut simplement que ça fonctionne, l'autre veux que ça fonctionne, et que ça soit bien codé :P.
Le count, comme disait STAILER, est en effet calculer à chaque itération puisque la condition est comparé à chaque itération. Ça devient vite très lourd, surtout avec de gros tableaux. Le sortir de là va donc, forcément, sauver beaucoup de temps d'exécution.
D'ailleurs ça ne rajoute pas forcément une ligne, tu peux l'inclure dans la définition de la boucle.
for($i=0, $j=0; $i < count($Stats); $i++)
devient
for($i=0, $j=0, $cntStats = count($Stats); $i < $cntStats; $i++)
D'ailleurs beaucoup d'autres choses pouraient être améliorées:
# $hFile = fopen($Fichier, "r");
# while(!feof($hFile))
# {
# $Log .= fgets($hFile);
# }
# fclose($hFile);
Pourrait utilisé file_get_contents, mais je ne comprends même pas pourquoi tu procède de cette façon, faire le traitement directement dans cette boucle serait BEAUCOUP plus rapide que de tout charger, et de le rediviser après. En fait ça permettrait de remplacer les lignes 23 à 58 par 5 ou 6 petites lignes.
Ensuite, tu sembles utiliser strstr pour savoir si une chaine est dans une autre. Si c'est le cas tu devrais utiliser strpos qui va être plus rapide pour l'usage que tu en fais.
Dans EffaceLog tu ne fermes pas le pointeur de fichier. Php le fait automatiquement à la fin du script, mais c'est une mauvaise habitude de pas fermer ses fichiers.
Voilà, garde à l'esprit que nous sommes sur codes-sources, et que tous ces commentaires ont pour but de t'aider à t'améliorer, non pas de dénigrer ton code ;p
Sinon l'idée de codefalse est intéressante, une classe serait sans doute sympatique :)
29 juil. 2008 à 10:02
Sortir le maximum de fonction des boucles et une des règles de base de l'optimisation du code, chose à ne jamais négliger.
Par rapport à ton code, je l'aurai beaucoup plus apprécié en tant que classe. En effet, vu l'usage, ca aurait été sympa un truc du genre :
$obj = new GameLogParser ('./quake3.log');
echo 'Statistiques du joueur Ilsundal" :
';
echo 'Nombre de matchs gagnés :' . $obj->getWinnedMatches ('Ilsundal').'
';
...
etc :)
Voir jouer avec les méthodes magiques __get et __set, du genre $obj->Winner; te retourne le grand gagnant, à toi de voir comment tu implémente le tout ! :)
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.