Seite 1 von 1

Levels hochladen, bewerten und Online-Hiscoreliste

Verfasst: 24.06.2011 22:10
von GPI
Ich hab mal ein paar allgemeine Funktionen, die die oben genannten Themen relativ einfach ermöglichen.
Allerdings ist der Einsatz nicht ganz einfach, man benötigt wissen in PHP, SQL und natürlich in PureBasic.
Die Routinen sind rein in PureBasic unter Windows geschrieben, sie sollten aber auch unter Linux und MacOs laufen - ist aber ungetestet. Als Web&SQL-Server fungiert eine XAMPP-Installation und damit MySQL und APACHE - aber das läuft eh auf den meisten Webservern, von daher sollte das kein Problem sein.

WICHTIG
Der Code ist zwar getestet, aber ist halt die erste Beta-Version. Es ist bei weiten nicht 100% fehlerfrei!

Zum Konzept
Der Grundgedanke ist, das eine einfach PHP-Seite Daten entgegenimmt und auswertet. Ein Webspace mit PHP sollte heutzutage kein Problem sein, gibts ja schon ab ca. 10 Euro in Jahr (man sollte nur regelmäßig manuell Backups machen ;).

Meine Idee war, weg zu kommen von den üblichen Benutzername & Passwort-System. Gerade nach solchen hacks wie bei PSN stellt sich ja die Frage, ob sowas wirklich überall nötig ist - wenn Sony es nicht schafft seine Daten zu sichern (und mittlerweile wirklich viele andere Firmen auch) - wieso sollte dann mein System so sicher sein.

Die Registierung erfolgt hier wesentlich einfacher, man Teil den System seinen Wunschnamen mit und das System antwortet mit einer ID und einer 20-stelligen Identifizierungsnummer. Diese werden lokal auf der HDD gespeichert (Unter windows am besten unter %appdata%) und damit hat es sich. Sollte da wirklich mal wer in die Datenbank eindringen, hat er höchstens diese Identifizierungsnummer und mehr nicht. Da diese 100% nirgends woanders genutzt wird, wäre es absolut nicht schlimm wenn die wer bekommen würde. Das schlimmste was derjenige dann machen könnte wäre, Levels löschen und Bewertungen abgeben :)

Man kann auch eine E-Mail Adresse angeben, diese wird benötigt, wenn man seine Zugangsdaten gelöscht hat - man kann sie dann einfach per E-Mail neu anfordern. Der E-Mail-Parameter ist optional.

Damit die Kommunikation nicht so einfach abgefangen werden können, erfolgt diese komplett Verschlüsselt (AES 256-Key). Als Handshake wird der InitializationVector genutzt. Das Spiel sendet 8 Byte und somit die erste Hälfte, die Webseite antwortet mit 8 weiteren Bytes. Da diese leicht abfangbar sind, sollte man diese weiter verrechnen, damit es nicht so einfach wird. In Beispielcode wird hierfür einfach die MD5-Funktion verwendet (sollte man ändern).

Einschränkungen

Der Code unterstützt keinen Proxy-Server. Das dürfte aber eher ein Problem von Firmen sein, und die haben eh was dagegen, das man spielt. Theoretisch ist es auch aktuell möglich, das jemand in gleichen Netzwerk die Session-Daten klauen und die Session übernehmen. Allerdings wozu... da sind keine Wichtigen Daten auf den Server gespeichert...

Die Datenbank

Es werden 3 Tabellen benötigt: User, Level, Rating, Hiscore. Diese Tabellen können einen beliebigen Namen haben, man muss nur die Config-Dateien anpassen. Die User-Datenbank kann man sogar für verschiedene Spiele genutzt werden, damit kann man quasi mit ein und den selben Userdaten einfach für verschiedene Spiele nutzen. Die anderen Tabellen sollten aber alle eindeutig für jedes Spiel exestieren.

Bei meiner Testinstalltion unter XAMPP hab ich eine Datenbank "crillion" erstellt und dort meine Tabellen erzeugt.

Fangen wir mit der Userdatenbank an (der Name in Beispiel cril_user):

Code: Alles auswählen

CREATE TABLE IF NOT EXISTS `cril_user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET armscii8 NOT NULL,
  `level` int(10) unsigned NOT NULL,
  `key` varchar(20) CHARACTER SET armscii8 COLLATE armscii8_bin NOT NULL,
  `email` varchar(20) CHARACTER SET armscii8 COLLATE armscii8_bin NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB;
Die Tabelle enthält folgende Spalten:
* ID - eine einfach hochzählende Nummer. Identifiziert den Benutzer
* Name - Von Benutzer gewählter Name
* Level - Userlevel. Dieser Wert bestimmt die Rechte des Users. 0:Admin, 1:Levels hochladen,bewerten,Hiscores, 2:nur Bewerten und Hiscores, 3:gebannt.
* Key - der von script erzeugte Identifizierungsnummer
* email - die optionale E-Mail adresse.

Als nächstes die Level-Tabelle (hier cril_level):

Code: Alles auswählen

CREATE TABLE IF NOT EXISTS `cril_level` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `userid` int(10) unsigned NOT NULL,
  `name` varchar(20) CHARACTER SET ascii NOT NULL,
  `md5` varchar(32) CHARACTER SET armscii8 COLLATE armscii8_bin NOT NULL,
  PRIMARY KEY (`id`,`userid`)
) ENGINE=InnoDB;
md5 wird beim hiscore-übertragen wichtig und enthält den MD5-Fingerprint der Leveldatei. Es sollte eigentlich alles selbsterklärend sein.

Die Rating-Tabelle (hier cril_rating):

Code: Alles auswählen

CREATE TABLE IF NOT EXISTS `cril_rating` (
  `levelid` int(10) unsigned NOT NULL,
  `userid` int(10) unsigned NOT NULL,
  `rating` int(11) NOT NULL,
  PRIMARY KEY (`levelid`,`userid`)
) ENGINE=InnoDB;
Sollte auch selbsterklärend sein, jeder Benutzer kann halt für jedes Level nur eine Bewertung abgeben.

Und zum Schluß die Hiscore-Tabelle (hier cril_hiscore):

Code: Alles auswählen

CREATE TABLE IF NOT EXISTS `cril_hiscore` (
  `levelid` int(10) unsigned NOT NULL,
  `userid` int(10) unsigned NOT NULL,
  `score` int(11) NOT NULL,
  PRIMARY KEY (`levelid`,`userid`)
) ENGINE=InnoDB;
sollte auch selbsterklärend sein.

Die PHP-Dateien

diese befinden sich in htdocs in XAMPP-Verzeichnis.

Beginnen wir mit der index.php
in prinzip ist es die Config-Datei, die dann die eigentliche Hauptdatei benutzt.

Code: Alles auswählen

<?php
//Dantenbank-daten
$db_host='localhost';
$db_user='root';
$db_pass='';
$db_name='crillion';

$server_name='Crillion'; // Titel bei E-Mail etc.
$server_Version='1.24'; // Serverversion
$server_key="12345678901234561234567890123456"; // Verschlüsselungskey

define('DOCOMMAND', 'do'); // Name des Do-Command
define('USERTABLE','`cril_user`'); // Tabelle für Userdaten
define('LEVELTABLE','`cril_level`'); // Tabelle für Leveldaten
define('RATETABLE','`cril_rating`'); // Tabelle für Bewertungen
define('HISCORETABLE','`cril_hiscore`'); // Tabelle für Bewertungen
define('LEVELPATH','cril_lev/'); // Pfad, wo Level/Upload gespeichert werden
define('ALLOWEDUSERNAME','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'); // Erlaubte Zeichen für Username
define('ALLOWEDLEVEL','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 !.,-+*=?@'); // Erlaubte Zeichen für Levelname
define('MAXUSERNAME',20); // maximale Anzahl der Zeichen für einen Namen
define('MAXEMAIL',20);  // maximale Anzahl der Zeichen für eine e-mail
define('MAXUPLOAD',64000); //maximale größe eines uploads
define('MAXLEVELNAME',20); //maximale Anzahl der Zeichen für Levelname
define('STARTUSERLEVEL',1); // Neue User bekommen diesen Level. 0:Admin 1:Levelupload+rating 2:rating 3:banned
define('HISCORESORT','DESC'); // sortiert nach DESC (absteigender,max zuerst)  oder ASC (aufsteigend,min zuerst)
define('HISCOREMAX',2147483647); // maximal größter Wert für Hiscores

// Damit wird überprüft, ob die Datei, die hochgeladen wurde, gültig ist.
function CheckUpload($file){
  $datei = fopen($file,"r");
  $check=fgets($datei, 17);
  fclose($datei);
  return ($check=="CrillionScenario");
};
//hex2bin
function hex2bin($in){
  return pack("H*",$in);
}
function CreateIV($parts){
  return hex2bin(md5($parts));
};

require("server.php");

?>
Das meiste wird ja in den Kommentaren erklärt. Die $server_Version wird beim Handshake wichtig, damit kann das Spiel erkennen, welche Version der Server hat und gegebenfalls nach einen Update schreien.

Die Funktion CheckUpload($file) überprüft, ob eine Datei gültig ist. In Beispiel werden die ersten 16 Zeichen eingelesen und überprüft ob dort "CrillionScenario" steht. Wenn ja, gibt man True zurück, ansonsten False. Jede von User hochgeladene Datei muss diesen Check überleben, ansonsten wird sie verworfen.

Die Funktion CreateIV($parts) ist für den Handshake wichtig. In $parts ist ein 16-Byte großer InitializationVector in seinen Rohzustand. Hier wird ein eine 32-Stellige MD5-Hexzahl erzeugt und diese wieder in ein 16-Byte-Code zurückgewandelt. Sinn ist halt, das man nicht einfach mit den beiden Handshake-Teilen die Verbindung abhören kann.

Der $server_key sollte auch für jedes Spiel einzigartig sein. Den sollte man vorallen später in PureBasic-Programm gut versteckt unterbringen.

so, als nächstes die "server.php" - ohne kommentar, das ganze ding wird dann in PureBasic-Teil besprochen.

Code: Alles auswählen

<?php
//Eigentliche Hauptdatei

require("common.php");

// Session und Verschlüsselung starten

session_start();
if (isset($_POST['start'])){
  session_unset();
  $mypart=MakePinC(8);
  $iv=CreateIV(base64_decode($_POST['start']).$mypart);
  if (strlen($iv)==16){
    $_SESSION['iv']=$iv;  // hier wird der IV mittels einen md5 "verschlüsselt" - sollte man ändern!
    echo base64_encode($mypart)."\n";
    echocrypt($server_Version."\n"
             .ALLOWEDUSERNAME."\n"
             .ALLOWEDLEVEL."\n"
             .MAXUSERNAME."\n"
             .MAXEMAIL."\n"
             .MAXUPLOAD."\n"
             .MAXLEVELNAME."\n");
    dieok();
  } else {
    dieerror("4");
  }
}
if (isset($_POST['stop'])){
  session_destroy();
  setcookie(session_name(), '', 0, '/');
  dieerror("0");
};

// ab hier verschlüsselt, also überprüfen, ob das überhaupt geht!
if (!isset($_SESSION['iv'])){
  session_destroy();
  setcookie(session_name(), '', 0, '/');
  dieerror("6");//start fehlt
}

// Daten entschlüsseln falls vorhanden  

$arr=array();
//var_dump($_SESSION);
if (isset($_POST['crypt'])){
  $crypt=$_POST['crypt'];
  $crypt=mydecrypt($server_key,$_SESSION['iv'],$crypt);

  foreach(explode(';',$crypt) as $value){
    $a=explode(':',$value);
    if (isset($a[0],$a[1])){
      $arr[trim($a[0])] = trim($a[1]);
    } else {
      $arr[trim($a[0])] ="";
    };
  };
};

// gibts ein do?

if (!isset($arr[DOCOMMAND])){
  dieerror("8");
}

// letzten Fehler zurückgeben

if ($arr[DOCOMMAND]=="lasterror"){
  if (isset($_SESSION['error'])){
    echocrypt($_SESSION['error']);
    dieok();
  }
  echocrypt("0");
  dieok();
}

// Datenbank öffnen 

$db = @new mysqli($db_host, $db_user,$db_pass,$db_name);
if (mysqli_connect_errno()) {
  dieerror("10"," ".mysqli_connect_error().'('.mysqli_connect_errno().')');
}

// auswerten

switch ($arr[DOCOMMAND]){
  case 'test':
    echocrypt("OK");
    dieok();
    
  // Neuer Benutzer anlegen
  case 'adduser':
    if (!isset($arr['name'],$arr['email'])){
      dieerror("8");
    }
    
    $name=mysql_real_escape_string($arr['name']);
    $email=mysql_real_escape_string($arr['email']);
    $key=makePin(20);

    if (strlen($name)>MAXUSERNAME or strlen($email)>MAXUSERNAME or strlen($name)<3){
      dieerror("16");
    }
    if (!is_allowed($name,ALLOWEDUSERNAME)){
      dieerror("15");
    }
  
    $result= doSQL("SELECT * FROM ".USERTABLE."
                 WHERE `name` = '$name';" );
    
    if ($result->num_rows==0) {
      $result2=doSQL( "INSERT INTO ".USERTABLE." (`name` , `level` , `key` , `email`)
                           VALUES ('$name',".STARTUSERLEVEL.",'$key','$email');" );
      $result2->close();
      $_SESSION['user']=array('name'=>$name,'level'=>STARTUSERLEVEL,'key'=>$key,'email'=>$email,'id'=>($result->insert_id));
      
      SendEmail($email,"Welcome.");
      echocrypt(($result->insert_id)."\n$key");
      $result->close();
      dieok();
    } else {
      $result->close();
      dieerror("14"); //Eintrag gibts schon
    }
    
  // Benutzer identifizieren  
  case 'authuser':
    if (!isset($arr['id'],$arr['key'])){
      dieerror("8");
    }
    $id=mysql_real_escape_string($arr['id']);
    $key=mysql_real_escape_string($arr['key']);
    $user=authuser($id,$key);
    $_SESSION['user']=$user;
    echocrypt($user['name']);  
    dieok();
    
  // E-Mail ändern   
  case 'newmail':
    if (!isset($arr['email'])){
      dieerror("8");
    }
    if (!isset($_SESSION['user'])){
      dieerror("19");
    }
    $email=mysql_real_escape_string($arr['email']);
    if (strlen($email)>MAXUSERNAME){
      dieerror("16");
    }
    
    if ($email != $_SESSION['user']['email']){
      $result = doSQL( "UPDATE ".USERTABLE." SET `email` = '$email' 
                          WHERE `id` = '".$_SESSION ['user']['id']."';" );
      if ($result->affected_rows) {
        SendMail($_SESSION['user']['email'],"New E-Mail: $email");
        SendMail($email,"Welcome");
        $_SESSION['user']['email']=$email;
        echocrypt($email);
        $result->close();
        dieok();
      }
      $result->close();
    } 
    echocrypt("ok");
    dieok();
    
  // Key neu anfordern
  case 'getkey':
    if (!isset($arr['email'])){
      dieerror("8");
    }
    $email=mysql_real_escape_string($arr['email']);
    if (strlen($email)>MAXUSERNAME){
      dieerror("16");
    }
    $result= doSQL("SELECT * FROM ".USERTABLE."
                     WHERE `email` = '$email';" );
    
    if ($result->num_rows) {
      $row=($result->fetch_assoc());
      if ( SendEMail($email,"Enter as Name: #".$row['id']."\nEnter as EMail: ".$row['key']) ) {
        echocrypt("ok");
        $result->close();
        dieok();
      } else {
        $result->close();
        dieerror("20"); //e-Mail gesendet
      }
    }
    $result->close();
    dieerror("12"); //E-Mail gibts nicht
    
  // Datei hochladen     
  case 'upload':
    if (!isset($_SESSION['user'])){
      dieerror("19");
    }
    if (!isset($_FILES['data'])){
      dieerror("8");
    }
    if ($_SESSION['user']['level'] >= 2){
      dieerror("17");
    };
    if ($_FILES['data']['size']>MAXUPLOAD) {
      dieerror("21");
    };
    if ($_FILES['data']['error']) {
      dieerror("22"," ".$_FILES['data']['error']);
    };
       
    $name=mysql_real_escape_string($_FILES['data']['name']);      
    
    if (!is_allowed($name,ALLOWEDLEVEL)){
      dieerror("15");
    }
    if (strlen($name)>MAXLEVELNAME){
      dieerror("16");
    }
    
    if (CheckUpload($_FILES['data']['tmp_name'])) {
      $result= doSQL("INSERT INTO ".LEVELTABLE." (`userid` , `name`, `md5` )
                             VALUES (".$_SESSION['user']['id'].",'$name','".md5_file($_FILES['data']['tmp_name'])."');");
      
      copy($_FILES['data']['tmp_name'],LEVELPATH.$result->insert_id);
      $result->close();            
      echocrypt("ok");
      dieok();
    } else {
      dieerror("23");
    }
    break;
  
  // Levelname ändern.
  case "renupload":
    if (!isset($arr['id'],$arr['name'])){
      dieerror("8");
    }
    if (!isset($_SESSION['user'])){
      dieerror("19");
    }
    if ($_SESSION['user']['level'] >= 2){
      dieerror("17");
    };
    $name=mysql_real_escape_string($arr['name']);      
    if (!is_allowed($name,ALLOWEDLEVEL)){
      dieerror("15");
    }
    if (strlen($name)>MAXLEVELNAME){
      dieerror("16");
    }
    $levelid=mysql_real_escape_string($arr['id']);
    if (!is_numeric($levelid)){
      dieerror("9");
    }

    if($_SESSION['user']['level']==0) {
      $where="";
    } else {
      $where="AND `userid`='".$_SESSION['user']['id']."'";
    };
        
    $result = doSQL("UPDATE ".LEVELTABLE." SET `name` = '$name' 
                      WHERE `id` = '$levelid' $where ;");
        
    if ($result->affected_rows) {
      echocrypt("ok");
      $result->close();
      dieok();
    };
    $result->close();
    dieerror("17");

  // Datei löschen
  case "delupload":
    if (!isset($arr['id'])){
      dieerror("8");
    }
    if (!isset($_SESSION['user'])){
      dieerror("19");
    }
    if ($_SESSION['user']['level'] >= 2){
      dieerror("17");
    };
    $levelid=mysql_real_escape_string($arr['id']);
    if (!is_numeric($levelid)){
      dieerror("9");
    }
    
    $levelid=mysql_real_escape_string($arr['id']);
        
    if($_SESSION['user']['level']==0) {
      $where="";
    } else {
      $where="AND `userid`='".$_SESSION['user']['id']."'";
    };
        
    $result = doSQL("DELETE FROM ".LEVELTABLE." WHERE `id` = '$levelid' $where ;");
    
    if ($result->affected_rows) {
      $result2=doSQL("DELETE FROM ".RATETABLE." WHERE `levelid` = '$levelid' ;");
      $result3=doSQL("DELETE FROM ".LEVELTABLE." WHERE `levelid` = '$levelid' ;");
      @unlink(LEVELPATH.$levelid);
      echocrypt("ok");
      $result->close();
      $result2->close();
      $result3->close();
      dieok();
    } 
    $result->close();
    dieerror("17");

  // Leveliste abfragen
  case "listlevel":
    if (!isset($arr['start'],$arr['length'])){
      dieerror("8");
    }
    $start=$arr['start'];
    $length=$arr['length'];
    if (!is_numeric($start) or !is_numeric($length)) {
      dieerror("15");    	
    }
    $userid="-";
    $userlevel="3";
    if (isset($_SESSION['user'])){
      $userid=$_SESSION['user']['id'];
      $userlevel=$_SESSION['user']['level'];
    }

    $result =dosql("SELECT l.id id, l.name name, u.name 'from', l.userid, avg(r.rating) rate,count(r.rating) count
                      FROM  ".LEVELTABLE." l
                      INNER JOIN ".USERTABLE." u ON u.id=l.userid
                      LEFT JOIN ".RATETABLE." r ON l.id=r.levelid
                      GROUP BY l.id
                      LIMIT $start,$length  ");
    
    $list="";
    while ($row = $result->fetch_assoc()) {
      $list=$list.$row['id'].";".$row['name'].";".$row['from'].";".$row['rate'].";".$row['count'].";";
      if ($userid==$row['userid'] or $userlevel==0){
        $list=$list."y;";
      } else {
        $list=$list."n;";
      }
      $list=$list."\n";    
    };
    $result->close();
    if ($list){
      echocrypt($list);
      dieok(); 
    } 
    dieerror("1");
    
  // Level bewerten  
  case "rate":
    if (!isset($arr['rate'],$arr['id'])){
      dieerror("8");
    }
    if (!isset($_SESSION['user'])){
      dieerror("19");
    }
    $rate=intval($arr['rate']);
    if ($rate<1 or $rate>5){
      dieerror("15");
    }
    $levelid=$arr['id'];
    if (!is_numeric($levelid)){
      dieerror("9");
    }
    
    $result=doSQL( "INSERT INTO ".RATETABLE." (levelid,userid,rating) 
                        VALUES ('$levelid','".$_SESSION['user']['id']."','$rate')
                        ON DUPLICATE KEY UPDATE rating = '$rate';");
    $result->close();
    echocrypt("ok");
    dieok();

  // Persönlichen Hiscore setzen.
  case "sethiscore":
    if (!isset($arr['score'],$arr['id'],$arr['md5'])){
      dieerror("8");
    }
    if (!isset($_SESSION['user'])){
      dieerror("19");
    }
    $score=intval($arr['score']);
    if ($score<0 or $score>HISCOREMAX){
      dieerror("15");
    }
    $levelid=$arr['id'];
    if (!is_numeric($levelid)){
      dieerror("9");
    }
    $md5=$arr['md5'];
    
    //gespeicherten md5-string holen.
    $result=doSQL ("SELECT * FROM ".LEVELTABLE."
                     WHERE ID='$levelid'");
    if ($result->num_rows){
      $row=$result->fetch_assoc();
      $savedmd5=$row['md5'];
    };
    $result->close();
    
    if ($md5!=$savedmd5){
      dieerror("17");
    }; 
    
    
    //erstmal schaun, ob ein hiscore schon da ist
    $result=doSQL ("SELECT * FROM ".HISCORETABLE." 
                     WHERE userid='".$_SESSION['user']['id']."' AND levelid='$levelid';");
    if ($result->num_rows){
      $row=$result->fetch_assoc();
      $oldscore=$row['score'];
    } elseif (HISCORESORT=='DESC') {
      $oldscore=0;
    } else {
      $oldscore=HISCOREMAX;
    }
    $result->close();
    
    if ( (HISCORESORT=='DESC' AND $score>$oldscore) OR (HISCORESORT!='DESC' AND $score<$oldscore)){
      $result=doSQL( "INSERT INTO ".HISCORETABLE." (levelid,userid,score) 
                        VALUES ('$levelid','".$_SESSION['user']['id']."','$score')
                        ON DUPLICATE KEY UPDATE score = '$score';");
      $result->close();
      echocrypt("ok");
      dieok();                  
    }
    dieerror("25");
    
  // Meinen Hiscore und Platz suchen
  case "getmyscore":
    if (!isset($arr['id'])){
      dieerror("8");
    }
    if (!isset($_SESSION['user'])){
      dieerror("19");
    }
    $levelid=$arr['id'];
    if (!is_numeric($levelid)){
      dieerror("9");
    }
    $result=doSQL( "SELECT  pos,score 
                      FROM (SELECT  userid,levelid , score, @rownum:=@rownum+1 pos
                            FROM ".HISCORETABLE." ht,
                                 (SELECT @rownum:=0) rn
                            WHERE levelid='$levelid' 
                            ORDER BY score ".HISCORESORT.") t
                      WHERE userid = '".$_SESSION['user']['id']."'; ");
    if ($result->num_rows){
      $row=$result->fetch_assoc();
      $result->close();
      echocrypt($row['pos']."\n".$row['score']);
      dieok();
    }
    $result->close();
    dieerror("25");
    
  // Komplette Hiscoreliste hohlen
  case "gethiscore":
    if (!isset($arr['start'],$arr['length'],$arr['id'])){
      dieerror("8");
    }
    $start=$arr['start'];
    $length=$arr['length'];
    if (!is_numeric($start) or !is_numeric($length)) {
      dieerror("15");    	
    }
    $levelid=$arr['id'];
    if (!is_numeric($levelid)){
      dieerror("9");
    }
    $result=doSQL( "SELECT u.name name, h.score score
                      FROM  ".HISCORETABLE." h
                      INNER JOIN ".USERTABLE." u ON u.id=h.userid
                      WHERE h.levelid='$levelid'
                      ORDER BY score ".HISCORESORT."
                      LIMIT $start,$length  ");
    
    $list="";
    while ($row = $result->fetch_assoc()) {
      $list=$list.$row['name'].";".$row['score']."\n";
    };
    $result->close();
    if ($list){
      echocrypt($list);
      dieok(); 
    } 
    dieerror("1");                      

  // Level downloaden
  case "getlevel":
    if (!isset($arr['id'])){
      dieerror("8");
    }
    $levelid=$arr['id'];
    if (!is_numeric($levelid)){
      dieerror("9");
    }
    $result=doSQL( "SELECT * FROM ".LEVELTABLE." WHERE id='$levelid';");
    if ($result->num_rows){
      if (readfile(LEVELPATH.$levelid)=== false) {
        header('HTTP/1.0 404 Not Found');
      }
    } else {
      header('HTTP/1.0 404 Not Found');
    }
    
    dieok();

  default:
    dieerror("24",$do);
  break;

}
?>
Weiter in nächsten Post - falls der noch nicht da ist, bitte noch nicht antworten!

Re: Levels hochladen, bewerten und Online-Hiscoreliste

Verfasst: 24.06.2011 22:17
von GPI
zwei PHP-Dateien fehlen noch.

die common.php - sie enthält nur ein paar unterfunktionen

Code: Alles auswählen

<?php
require("validEmail.php");

//Überprüfen ob Zeichen erlaubt
function is_allowed($text, $allowed_chars) {
 
   return (strlen($text) == strspn($text, $allowed_chars));
 
}

//text verschlüsseln
function mydecrypt($key,$vec,$c1){
  $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
  if (mcrypt_generic_init($cipher, $key, $vec) != -1)	{
		//$text = mdecrypt_generic($cipher,hex2bin($c1) );
	  $text = mdecrypt_generic($cipher,base64_decode($c1) );
	  
    mcrypt_generic_deinit($cipher);
	} else {
	  dieerror("7");
  }  
  mcrypt_module_close($cipher);
  return $text;
}

//text entschlüsseln
function mycrypt($key,$vec,$c1){
  $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
  if (mcrypt_generic_init($cipher, $key, $vec) != -1)	{
		//$text = bin2hex(mcrypt_generic($cipher,$c1 ));
		$text = base64_encode(mcrypt_generic($cipher,$c1 ));
		
	  mcrypt_generic_deinit($cipher);
	} else {
	  dieerror("7");
  }  
  mcrypt_module_close($cipher);
  return $text;
}

//Text verschlüsseln und ausgeben
function echocrypt($crypt){
  global $server_key,$_SESSION;
  echo mycrypt($server_key,$_SESSION['iv'],$crypt);
}

//Fehlermeldung speichern und ende
function dieerror($c,$text="") {
  global $_SESSION;
  $_SESSION['error']=$c.$text;
  die($c);
}
function dieok() {
  global $_SESSION;
  $_SESSION['error']="0";
  die();
}


//E-Mail überprüfen und gegebenfalls verschicken
function SendEMail($email,$content){
  global $server_name;  
  if (validEmail($email)){
    return mail($email,$server_name,$content); 
  } else {
    return false;
  }
}

// Zufallsstring erzeugen
function makePin($lenth =5) {
    // makes a random alpha numeric string of a given lenth
    $aZ09 = array_merge(range('A', 'Z'), range('a', 'z'),range(0, 9));
    $out ='';
    for($c=0;$c < $lenth;$c++) {
       $out .= $aZ09[mt_rand(0,count($aZ09)-1)];
    }
    return $out;
} 
function makePinC($lenth =5) {
    $out ='';
    for($c=0;$c < $lenth;$c++) {
       $out .= chr(mt_rand(0,255));
    }
    return $out;
} 

// Anfrage an Datenbank und überprüfung
function doSQL($sql){
  global $db;
  $result = $db->query($sql);
  if (!$result) {
    dieerror("11"," ".$db->error."\n".$sql);
  }
  return $result;
}

// Benutzer identifizieren und Userdaten zurückgeben
function authuser($id,$key) {
  global $db;
  $result= doSQL("SELECT * FROM ".USERTABLE."
                   WHERE `id` = '$id' AND `key` = '$key'  ;" );

  if ($result->num_rows) {
    $user=($result->fetch_assoc());
    if ($user['level']>=3) {
      $result->close();
      dieerror("17");
    } 
  } else {
    $result->close();
    dieerror("18");
  }
  $result->close();
  return $user;  
}
?>
und die validemail.php

Code: Alles auswählen

<?php
/**
Validate an email address.
Provide email address (raw input)
Returns true if the email address has the email 
address format and the domain exists.
http://www.linuxjournal.com/article/9585
*/
function validEmail($email)
{
   $isValid = true;
   $atIndex = strrpos($email, "@");
   if (is_bool($atIndex) && !$atIndex)
   {
      $isValid = false;
   }
   else
   {
      $domain = substr($email, $atIndex+1);
      $local = substr($email, 0, $atIndex);
      $localLen = strlen($local);
      $domainLen = strlen($domain);
      if ($localLen < 1 || $localLen > 64)
      {
         // local part length exceeded
         $isValid = false;
      }
      else if ($domainLen < 1 || $domainLen > 255)
      {
         // domain part length exceeded
         $isValid = false;
      }
      else if ($local[0] == '.' || $local[$localLen-1] == '.')
      {
         // local part starts or ends with '.'
         $isValid = false;
      }
      else if (preg_match('/\\.\\./', $local))
      {
         // local part has two consecutive dots
         $isValid = false;
      }
      else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain))
      {
         // character not valid in domain part
         $isValid = false;
      }
      else if (preg_match('/\\.\\./', $domain))
      {
         // domain part has two consecutive dots
         $isValid = false;
      }
      else if
(!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/',
                 str_replace("\\\\","",$local)))
      {
         // character not valid in local part unless 
         // local part is quoted
         if (!preg_match('/^"(\\\\"|[^"])+"$/',
             str_replace("\\\\","",$local)))
         {
            $isValid = false;
         }
      }
      if ($isValid && !(checkdnsrr($domain,"MX") || checkdnsrr($domain,"A")))
      {
         // domain not found in DNS
         $isValid = false;
      }
   }
   return $isValid;
}
?>
Wie man den Kommentar entnehmen kann, stammt diese Routine nicht von mir!

in nächsten Post kommt die PB-Datei dran. Wie vorhin, wenn es wer liest, dann bitte nicht antworten, der nächste Teil braucht etwas länger.

Re: Levels hochladen, bewerten und Online-Hiscoreliste

Verfasst: 24.06.2011 23:43
von GPI
Die PureBasic-Datei

Nur eine Kleinigkeit, die Serverdatei antwortet entweder mit einen verschlüsselten Text, der minimum 16 Byte lang ist oder mit einer maximalen 2-Stelligen Fehlernummer. An der Länge der Antwort kann man damit erkennen, ob ein Fehler passiert ist oder nicht.

Die PB-Datei zerstückle ich hier mal und bespreche den Abschnitt. Also alle Codefetzen gehören einfach zusammenkopiert.

nennen wir die Datei einfach mal server.pb

Code: Alles auswählen

Global srv_cookie.s
Global srv_host.s
Global srv_url.s
Global srv_port.l
Global srv_version.s
Global *srv_iv=AllocateMemory(16+32+2)
Global *srv_key=*srv_iv+16
Global srv_respond_state$,srv_respond_type$,srv_respond_buffer
Global srv_userdatafile.s
Global srv_userid.s,srv_userkey.s
Global srv_username.s
Global srv_AllowedUserName.s,srv_AllowedLevel.s,srv_MaxUserName,srv_MaxEMail,srv_MaxUpload,srv_MaxLevelName

Structure srv_Level
  id.l
  name.s
  from.s
  editable.l
  rate.f
  count.l
EndStructure
Structure srv_HiScore
  name.s
  score.l
EndStructure

Declare CreateIV(*adr)

#srv_ok=0
#srv_docommand="do"
Es werden einige Globale Variablen erzeugt, die intern genutzt werden. Zudem wird Platz für den Key und IV (InitializationVector) angelegt. die Konstante #srv_docommand bestimmt den Namen des "do". Hier kann man etwas induvidualisieren. Die Entsprechende Konstante in der index.php muss natürlich angepasst werden.

Code: Alles auswählen

;- Allgemeine Funktionen
Procedure is_allowed(str$,chars$)
  ret=#True
  For i=1 To Len(str$)
    If FindString(chars$,Mid(str$,i,1),0)=0
      ret=#False
    EndIf
  Next
  ProcedureReturn ret
EndProcedure
Procedure is_numeric(str$)
  ProcedureReturn is_allowed(str$,"0123456789")
EndProcedure
Procedure is_alphanumeric(str$)
  ProcedureReturn is_allowed(str$,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
EndProcedure

Procedure.s mybase64encoder(*in,inlen)
  If inlen=0
    ProcedureReturn ""
  EndIf
  a=inlen*2+1:If a<64:a=64:EndIf
  *out=AllocateMemory(a)
  done= Base64Encoder(*in, inlen, *out, a)
  ret$=PeekS(*out,done)
  FreeMemory(*out)
  ProcedureReturn ret$
EndProcedure


Procedure.s srv_Crypt(text$)
  If text$=""
    ProcedureReturn ""
  EndIf
  a=Len(text$)
  a=((a+15)>>4)<<4
  *in=AllocateMemory(a*2+4)
  *out=*in+a+2
  PokeS(*in,text$)   
  AESEncoder(*in, *out,a,*srv_key, 256, *srv_iv,#PB_Cipher_CBC)
  ret$=mybase64encoder(*out,a)
  FreeMemory(*in)
  ProcedureReturn ret$
EndProcedure
Procedure.s srv_DeCrypt(text$)
  If text$=""
    ProcedureReturn ""
  EndIf
  a=Len(text$)
  If a<64:a=64:EndIf
  *in=AllocateMemory(a*2+4)
  *out=*in+a+2
  cl=Base64Decoder(@text$, StringByteLength(text$),*in,a)
  If cl>0
    AESDecoder(*in, *out,cl,*srv_key, 256, *srv_iv,#PB_Cipher_CBC)
    ret$=PeekS(*out,a)
  Else
    ret$=text$;Rückfall - keine verschlüsselung? Fehlermeldung?
  EndIf
  FreeMemory(*in)
  ProcedureReturn ret$
EndProcedure
Ein paar allgemeine Funktionen. Die is_*-Funktionen dienen dazu rauszufinden, ob verbotene Zeichen bspw. in Namenstring vorhanden sind. Die beiden Crypt-Routinen sind zum ver- und entschlüsseln der Daten notwendig. Damit man die Daten einfacher übergeben werden können, werden sie mittels Base64 codiert.

Code: Alles auswählen

Procedure srv_SetServer(Host.s,Url.s,port.l,version.s,userdatafile.s)
  srv_host.s=host
  srv_url.s=url
  srv_port.l=port
  srv_version=version
  srv_userdatafile=userdatafile
EndProcedure
Mit diesen Befehl werden die Serverdaten übergeben. Userdatafile gibt die Datei an, wo die Userdaten standardmäßig abgespeichert werden. Als Version sollte man den gleichen Wert übergeben, wie in der index.php datei.

Code: Alles auswählen

Procedure.s srv_addfield(field$,value$)
  c$="--roPK9J3DoG4ZWP6etiDuJ97h-zeNAph"+Chr(13)+Chr(10)
  c$+"Content-Disposition: form-data; name="+Chr(34)+field$+Chr(34)+""+Chr(13)+Chr(10)
  c$+"Content-Type: text/plain; charset=ISO-8859-1"+Chr(13)+Chr(10)
  c$+"Content-Transfer-Encoding: 8bit"+Chr(13)+Chr(10) 
  c$+Chr(13)+Chr(10)
  c$+value$+Chr(13)+Chr(10)
  ProcedureReturn C$
EndProcedure
Procedure.s srv_addcrypt(crypt$)
  ProcedureReturn srv_addfield("crypt",srv_crypt(crypt$))
EndProcedure
Werden nur intern benötigt. Damit wird ein "Post"-Feld in Request für den Server hinzugefügt. addcrypt verschlüsselt zusätzlich den Text. Die genaue Benutzung wird später noch klar:

Code: Alles auswählen

Procedure srv_StartRequest(id,url$,c$,ffield$,ffile$,fbuffer,fsize)
  Static buffer
  Static cookie.s
  
  srv_respond_state$="timeout":lenresult=0
  
  ;Netzwerkid=0 - buffer löschen
  If id=0
    If buffer<>0
      FreeMemory(buffer)
      buffer=0
    EndIf
    ProcedureReturn 0
  EndIf
  
  ;Buffer anlegen, falls noch nicht geschehen.
  If buffer=0
    buffer=AllocateMemory(1024*64)
  EndIf
  
  
  If fsize>0
    c$+"--roPK9J3DoG4ZWP6etiDuJ97h-zeNAph"+Chr(13)+Chr(10)
    c$+"Content-Disposition: form-data; name="+Chr(34)+ffield$+Chr(34)+"; filename="+Chr(34)+ffile$+Chr(34)+""+Chr(13)+Chr(10)
    c$+"Content-Type: application/octet-stream"+Chr(13)+Chr(10)
    c$+"Content-Transfer-Encoding: binary"+Chr(13)+Chr(10)
    c$+Chr(13)+Chr(10)
    len=Len(c$)+fsize+2+33+2;(endtag dazuzählen)
  Else
    len=Len(c$)
  EndIf
  b$="POST "+url$+" HTTP/1.1"+Chr(13)+Chr(10)
  b$+"Host: "+Server$+Chr(13)+Chr(10)
  If cookie 
    b$+"Cookie: "+cookie+Chr(13)+Chr(10)
  EndIf
  b$+"Content-Type: multipart/form-data; boundary=roPK9J3DoG4ZWP6etiDuJ97h-zeNAph"+Chr(13)+Chr(10)
  b$+"Content-Length: "+Str(len)+Chr(13)+Chr(10)
  b$+Chr(13)+Chr(10)
  SendNetworkData(id, @b$,Len(b$))
  SendNetworkData(id, @c$,Len(c$))
  If fsize
    SendNetworkData(id,fbuffer,fsize)
    c$=Chr(13)+Chr(10)+"--roPK9J3DoG4ZWP6etiDuJ97h-zeNAph"+Chr(13)+Chr(10)
    SendNetworkData(id,@c$,Len(c$))
  EndIf
  
  timeout=(2*60)*4 ;2 min timeout
  
  ;Daten empfangen
  erg=0
  Repeat
    Delay(250)
    a=NetworkClientEvent(id)
    If a<>0
      If a=2
        erg=ReceiveNetworkData(id,buffer,1024*64)
        Break
      EndIf
    EndIf
    timeout-1
  Until timeout<=0
  
  ;auswerten
  If erg
    *byte.byte
    *byte=buffer
    a$=""
    
    While *byte<buffer+erg
      If *byte\b=13
        *byte+1
        If *byte\b<>10
          *byte-1
        EndIf
        
        If a$=""
          *byte+1
          lenresult=erg-(*byte-buffer)
          srv_respond_buffer=*byte
          Break
        Else
          If Left(a$,4)="HTTP"
            srv_respond_state$=StringField(a$,2," ")
          ElseIf Left(a$,13)="Content-Type:"
            srv_respond_type$=Right(a$,Len(a$)-14)
          ElseIf Left(a$,11)="Set-Cookie:"
            ;Debug "cookie:"+a$
            If FindString(a$,"PHPSESSID=deleted;",0)
              cookie=""
              ;Debug "delete";
            Else
              cookie=Right(a$,Len(a$)-12)
            EndIf
          EndIf
        EndIf
        a$=""
      Else
        a$+Chr(*byte\b &$FF)
      EndIf
      
      *byte+1
    Wend
    
  EndIf
  ProcedureReturn lenresult
EndProcedure  
Intern benutzt. Damit wird ein Request ausgelöst und die Antwort entgegen genommen. Wichtig: Es wird hier ein Buffer mit einer größe von 64 KB erstellt! Die Antwort von Server muss also definitiv kleiner sein. Das wird bei Leveldaten wichtig. Wenn man größere Levels hat, muss man das hier anpassen! Die Benutzung dürfte gleich in nächsten Fetzen klar sein.

Code: Alles auswählen

Procedure.s srv_Request(c$,ffield$="",ffile$="",fbuffer=0,fsize=0)
  id=OpenNetworkConnection(srv_host,srv_port)
  If id
    l=srv_StartRequest(id,srv_url,c$,ffield$,ffile$,fbuffer,fsize)
    If srv_respond_state$="200"
      If l>0
        ret$=PeekS(srv_respond_buffer,l)
      Else
        ret$="1"; keine Antwort
      EndIf
    Else
      ret$= "2"; Server-Fehler
    EndIf
    CloseNetworkConnection(id)
    srv_StartRequest(0,"","","","",0,0); Buffer löschen
    ProcedureReturn ret$
  Else
    ProcedureReturn "3";Netzwerk-Fehler
  EndIf
EndProcedure
Auch Intern. Hier wird ein kompletter einfacher Request ausgelöst. C$ enthält die Post-Daten, die man mit srv_addfield() erzeugen kann. ffield$,ffile$,fbuffer,fsize werden benötigt, wenn man eine Datei (=Level) hochladen will.
Als Antwort erhält man einen String. Wenn er größer/gleich 16 ist, ist dieser verschlüsselt und muss mit srv_Decrypt() entschlüsselt werden. Ist er kleiner, enthält er eine unverschlüsselte Fehlernummer.
Genau diesen Umstand macht sich diese Routine zu nutzen.

Code: Alles auswählen

Procedure srv_isError(e$)
  If Len(e$)<16
    ret=Val(e$)
    If ret=0
      ret=9
    EndIf
  EndIf
  ProcedureReturn ret
EndProcedure
Sie gibt entweder #srv_ok zurück, oder die Fehlernummer. Was für Fehler es geben kann, sieht man hier:

Code: Alles auswählen

Procedure.s srv_error(nr)
  ret$="unknown"
  Select nr
    Case 0: ret$="ok"
    Case 1: ret$="no respond" ; Keine Antwort erhalten.
    Case 2: ret$="html-error" ; HTML-Error wie 404 etc.
    Case 3: ret$="network error" ; Serververbindung konnte nicht hergestellt werden
    Case 4: ret$="illegal call" ; Irgendwelche falschen Daten wurden übergeben
    Case 5: ret$="wrong version" ; Die Versionsnummer des Servers passt nicht
    Case 6: ret$="missing session start" ; es wurde keine Session gestartet
    Case 7: ret$="crypt error" ; die Verschlüsselten Daten sind fehlerhaft
    Case 8: ret$="missing parameter" ; ein wichtiger Parameter wurde nicht übergeben
    Case 9: ret$="syntax error" ; Allgemeiner fehler
    Case 10:ret$="database error" ; Datenbankzugriff konnte nicht erfolgen (SQL-Server down?), mit srv_getlasterror() mehr informationen
    Case 11:ret$="sql error" ; Fehler bei der Datenbankabfrage, mit srv_getlasterror() mehr informationen
    Case 12:ret$="no user"; Keine User-Daten/Email vorhanden / exestiert nicht
    Case 13:ret$="save error"; konnte nicht speichern
    Case 14:ret$="duplicate entry"; Eintrag schon vorhanden
    Case 15:ret$="not allowed chars"; Verbotene Zeichen wurden verwendet / Rating außerhalb des bereichs
    Case 16:ret$="string size illegal"; Es wurde ein zu langer/kurzer String übergeben
    Case 17:ret$="access denied"; Der Benutzer ist gebannt bzw. rechte fehlen
    Case 18:ret$="auth fail"; Auth ist fehlgeschlagen!
    Case 19:ret$="auth missing"; Benutzer nicht eingelogt!
    Case 20:ret$="can't send email"
    Case 21:ret$="upload too big"; Die hochgeladene Datei ist zu groß
    Case 22:ret$="upload error"; Upload-fehler, mehr infos mit srv_getlasterror()  
    Case 23:ret$="file check fail"; der Upload hat ein falsches Format  
    Case 24:ret$="unknown command"; Unbekannter Befehl geschickt.
    Case 25:ret$="no hiscore"; kein neuer hiscore bzw. kein eintrag bisher
    Case 26:ret$="buffer too small"; Der Buffer ist zu klein.  
  EndSelect
  ProcedureReturn ret$
EndProcedure
die Kommentare sollten alles sagen. Diese Funktion kann man benutzen, um sehr schnell eine Fehlermeldung den Benutzer um die Ohren zu schmeißen :)

WICHTIG! Alle Funktionen, die jetzt folgen, geben entweder 0 für ok oder eine Zahl für einen Fehler zurück! Das heißt das beliebte "if srv_startsession() " um zu überprüfen, ob die Funktion erfolgreich war, wird hier so nicht funktionieren. Stattdessen sollte man immer =#srv_ok verwenden!

Code: Alles auswählen

Procedure srv_StartSession()
  ret=0
  *in=AllocateMemory(1024)
  
  CryptRandomData(*in, 8)
  
  Datas$=srv_addfield("start",mybase64encoder(*in,8))
  p$=srv_Request(datas$)
  
  ret=srv_isError(p$)
  If ret=#srv_ok
    p2$=StringField(p$,1,#LF$)
    a=Base64Decoder(@p2$,Len(p2$),*in+8,8)
    
    If a=8
      
      ;- Session errechnen
      createiv(*in)
            
      a$=srv_decrypt(StringField(P$,2,#LF$))
      ver$=StringField(a$,1,#LF$)
      If ver$<>srv_version
        ret=5; falsche version
      Else
      
        ;einige Variablen übernehmen
        srv_AllowedUserName=    StringField(a$,2,#LF$)
        srv_AllowedLevel   =    StringField(a$,3,#LF$)
        srv_MaxUserName    =Val(StringField(a$,4,#LF$))
        srv_MaxEMail       =Val(StringField(a$,5,#LF$))
        srv_MaxUpload      =Val(StringField(a$,6,#LF$))
        srv_MaxLevelName   =Val(StringField(a$,7,#LF$))
      EndIf
    Else
      ret=4; irgendwas stimmt gewaltig nicht
    EndIf    
  EndIf
  FreeMemory(*in)
  ProcedureReturn ret
EndProcedure
Diese Funktion öffnet eine Seasion mit den Server eröffnet. Gleichzeitig wird der IV mit den Server ausgemacht und die Verschlüsselung initalisiert. Dabei wird auch die Version überprüft. Gleichzeitig werden einige Einstellungen von Server übernommen, wie bspw. welche Zeichen für den Namen erlaubt sind. Damit kann man dann schon, noch bevor man eine Anfrage zum Server schickt, kontrollieren, ob eine Fehlermeldung kommen würde. Pro Session sollte nur ein Benutzer eingelogt werden. Sollte eigentlich keine Probleme geben, aber sicher ist sicher.

Code: Alles auswählen

Procedure srv_StopSession()
  datas$=srv_addfield("stop","ok")
  ret$=srv_Request(datas$)
  srv_userid.s="":srv_userkey.s="":srv_username.s=""  
  ProcedureReturn #True
EndProcedure
Jede Session sollte ordnungsgemäß mit StopSession beendet werden. Wie lange eine Session gültig ist, hängt von Webserver ab. Rechnet nicht damit, das sie länger als 30 Min inaktivität gültig ist. Also sollte man besser bei jeder Aktion eine Session starten und beenden, anstatt an Spielstart einmal zu starten und darauf zu hoffen, das es kein Timeout gibt.

Code: Alles auswählen

Procedure.s srv_GetLastError()
  datas$=srv_addcrypt(#srv_docommand+":lasterror");
  ret$=srv_Request(datas$)
  a=srv_iserror(ret$)
  If a
    ProcedureReturn srv_error(a)
  Else
    ret$=srv_DeCrypt(ret$)
    If Len(ret$)<3
      a=Val(ret$)
      If a>0 Or ret$="0"
        ret$=Str(a)+" "+srv_error(a)
      EndIf
    EndIf
    ProcedureReturn ret$
  EndIf
EndProcedure
Hiermit wird die letzte Fehlermeldung von Server und erweiterter Text zurückgegeben. Das ist mehr für Debug-Zwecke. ACHTUNG! Viele Funktionen erzeugen schon Fehlermeldungen, wo nicht eine einzige Anfrage zum Server geschickt wurde. Diese hierbei *NICHT* berücksichtig. Zudem setzt der Befehl den Serverfehler wieder zurück. Siehe auch Srv_error() oben.

Als nächstes folgen ein paar Dateien, ob die aktuellen Userdaten abzuspeicher, wieder zu laden, die Userdatei zu löschen oder manuell einen User einzutragen.

Code: Alles auswählen

Procedure srv_LoadUserData()
  OpenPreferences(srv_userdatafile)
  srv_userid=ReadPreferenceString("id","")
  srv_userkey=ReadPreferenceString("key","")
  srv_username=""
  ClosePreferences()
  If srv_userid<>"" And srv_userkey<>""
    ProcedureReturn 0
  Else
    ProcedureReturn 12
  EndIf
EndProcedure  
Procedure srv_SaveUserData()
  CreateDirectory(GetPathPart(srv_userdatafile))
  If srv_userid<>"" And srv_userkey<>"" And srv_username<>""
    If CreatePreferences(srv_userdatafile)
      WritePreferenceString("id",srv_userid)
      WritePreferenceString("key",srv_userkey)
      WritePreferenceString("version",srv_version)
      ClosePreferences()
      ProcedureReturn 0
    Else
      ProcedureReturn 13
    EndIf
  Else
    ProcedureReturn 12
  EndIf
EndProcedure
Procedure srv_RemoveUserData()
  DeleteFile(srv_userdatafile)
  srv_userid=""
  srv_userkey=""
  srv_username=""
EndProcedure
Procedure srv_SetUser(id.s,key.s)
  If is_numeric(id) And is_alphanumeric(key)
    srv_userid=id
    srv_userkey=key
    srv_username=""
    ProcedureReturn #srv_ok
  Else
    ProcedureReturn 15
  EndIf
EndProcedure
Wichtig ist - diese Funktionen setzen nur die lokalen Informationen - der User ist *NICHT* eingelogt! Die Userdatei wird mit srv_SetServer() gesetzt.

Code: Alles auswählen

Procedure srv_AddUser(name$,email$)
  If Len(name$)>srv_MaxUserName Or Len(email$)>srv_MaxEMail Or Len(name$)<3
    ProcedureReturn 16
  EndIf
  If is_allowed(name$,srv_AllowedUserName)=#False
    ProcedureReturn 15
  EndIf
  
  datas$=srv_addcrypt(#srv_docommand+":adduser;name:"+name$+";email:"+email$)
  ret$=srv_request(datas$)
  a=srv_iserror(ret$)
  If a=#srv_ok
    ret$=srv_decrypt(ret$)
    srv_userid=StringField(ret$,1,#LF$)
    srv_userkey=StringField(ret$,2,#LF$)
    srv_username=name$
  EndIf
  ProcedureReturn a
EndProcedure
Mit diesen Befehl wird ein User angelegt. Sollte der Befehl Erfolg haben (=#srv_ok!), dann ist der Benutzer zeitgleich eingelogt. Die User-Daten sollte man am besten in Anschluss gleich laden. srv_setUserData()

Code: Alles auswählen

Procedure srv_AuthUser()
  If srv_userid<>"" And srv_userkey<>""
    datas$=srv_addcrypt(#srv_docommand+":authuser;id:"+srv_userid+";key:"+srv_userkey)
    ret$=srv_Request(datas$)
    a=srv_iserror(ret$)
    If a=#srv_ok
      srv_username=srv_decrypt(ret$)
    EndIf
    ProcedureReturn a
  Else
    ProcedureReturn 12
  EndIf
EndProcedure    
Hier wird ein Benutzer eingelogt. Falls erfolgreich, kann man den Namen über die globale Variable "srv_username" abfragen.

Code: Alles auswählen

Procedure srv_NewEmail(NewEmail$)
  If Len(email$)>srv_MaxEMail
    ProcedureReturn 16
  EndIf
  
  If srv_userid<>"" And srv_userkey<>"" And srv_username<>""
    datas$=srv_addcrypt(#srv_docommand+":newmail;email:"+NewEmail$)
    ret$=srv_Request(datas$)
    ProcedureReturn srv_iserror(ret$)
  Else
    ProcedureReturn 12
  EndIf
EndProcedure
Ändert die Email eines eingelogten Users.

Code: Alles auswählen

Procedure srv_getKey(email$)
  If Len(email$)>srv_MaxEMail
    ProcedureReturn 16
  EndIf
  datas$=srv_addcrypt(#srv_docommand+":getkey;email:"+email$)
  ret$=srv_Request(datas$)
  ProcedureReturn srv_iserror(ret$)
EndProcedure
Falls man die Logindaten verliert, dann kann man hiermit die Userdaten anfordern. Einfach die E-Mail, die in der Datenbank hinterlegt wurde, angeben. Logischweise muss man hierfür nicht eingelogt sein.

Code: Alles auswählen

Procedure srv_Upload(name$,*buf,blen)
  If srv_userid="" Or srv_userkey="" Or srv_username=""
    ProcedureReturn 12
  EndIf
  If blen>srv_MaxUpload
    ProcedureReturn 21
  EndIf
  If is_allowed(name$,srv_AllowedLevel)=#False
    ProcedureReturn 15
  EndIf  
  If Len(name$)>srv_MaxLevelName
    ProcedureReturn 16
  EndIf
  
  datas$=srv_addcrypt(#srv_docommand+":upload")
  ret$=srv_Request(datas$,"data",name$,*buf,blen)
  ProcedureReturn srv_iserror(ret$)
EndProcedure
Ein Level mit den angegebenen Namen hochladen.

Code: Alles auswählen

Procedure srv_RenUpload(Levelid,name$)
  If srv_userid="" Or srv_userkey="" Or srv_username=""
    ProcedureReturn 12
  EndIf
  If is_allowed(name$,srv_AllowedLevel)=#False
    ProcedureReturn 15
  EndIf  
  If Len(name$)>srv_MaxLevelName
    ProcedureReturn 16
  EndIf
    
  datas$=srv_addcrypt(#srv_docommand+":renupload;id:"+Str(levelid)+";name:"+name$)
  ret$=srv_Request(datas$)
  ProcedureReturn srv_iserror(ret$)
EndProcedure
Wenn man sich verschrieben hat, kann man hiermit den Namen des Levels ändern. Als Admin kann man auch fremde Levels umbennen, ansonsten gibts eine Fehlermeldung.

Code: Alles auswählen

Procedure srv_DelUpload(Levelid)
  If srv_userid="" Or srv_userkey="" Or srv_username=""
    ProcedureReturn 12
  EndIf
    
  datas$=srv_addcrypt(#srv_docommand+":delupload;id:"+Str(levelid))
  ret$=srv_Request(datas$)
  ;Debug srv_DeCrypt(ret$)
  ProcedureReturn srv_iserror(ret$)
EndProcedure
Falls man was falsches Hochgeladen hatte, kann man es damit löschen - es werden auch die Bewertung und Hiscores für dieses Level gelöscht.
Ein ersetzen des Levels ist nicht vorgesehen!

Code: Alles auswählen

Procedure srv_ListLevel(List level.srv_Level() ,start=0,length=200); gibt Anzahl zurück!
  Datas$=srv_addcrypt(#srv_docommand+":listlevel;start:"+Str(start)+";length:"+Str(length))
  ret$=srv_Request(datas$)
  anzahl=0
  
  If srv_isError(ret$)=#srv_ok    
    i=0
    dc$=srv_DeCrypt(ret$)
    Repeat
      i+1
      l$=StringField(dc$,i,#LF$)
      If l$
        anzahl+1
        AddElement(level())
        level()\id=Val(StringField(l$,1,";"))
        level()\name=StringField(l$,2,";")
        level()\from=StringField(l$,3,";")
        level()\rate=ValF(StringField(l$,4,";"))
        level()\count=Val(StringField(l$,5,";"))
        level()\editable= (StringField(l$,6,";")="y")
      Else
        Break
      EndIf
    ForEver
  EndIf
  ProcedureReturn anzahl
EndProcedure
Damit wird die Levelliste abgefragt (es gibt weiter unten eine bessere Methode um die komplette Liste abzufragen). Daten werden in der LinkedList übergeben, die Structur wurde oben definiert und sollte selbsterklärend sein. Das Feld editable gibt an, ob der Benutzer (sofern eingelogt) dieses Level umbennen/löschen kann. Hier kann man schon Benutzeranfragen abfangen, bevor sie überhaupt nur in die Nähe des Webservers gelangen.
ACHTUNG! Rückgabewert ist hier anders! Es wird die Anzahl der Levels übergeben.

Code: Alles auswählen

Procedure srv_RateLevel(level,rate)
  If rate<1 Or rate>5
    ProcedureReturn 15
  EndIf
  datas$=srv_addCrypt(#srv_docommand+":rate;id:"+Str(level)+";rate:"+Str(rate))
  ret$=srv_Request(datas$)
  ;Debug srv_DeCrypt(ret$)
  ProcedureReturn srv_isError(ret$)
EndProcedure
Damit bewertet man ein Level. Der Benutzer muss natürlich eingelogt sein. Die Wertung muss zwischen einschließlich 1 bis 5 sein. Man kann mit dieser Funktion auch seine Bewertung ändern, falls man für dieses Level schon eine Bewertung abgegeben hat.

Code: Alles auswählen

Procedure srv_SetHiscore(level,score,md5.s)
  datas$=srv_addCrypt(#srv_docommand+":sethiscore;id:"+Str(level)+";score:"+Str(score)+";md5:"+md5)
  ret$=srv_Request(datas$)
  ;Debug srv_DeCrypt(ret$)
  ProcedureReturn srv_isError(ret$)
EndProcedure
Teilt einen Punktestand des Spielers für ein Level mit. Pro Level kann nur ein Punktestand übermittelt werden. Je nach Serverkonfiguration (HISCORESORT) kontrolliert der Server, ob möglicherweise schon vorhandene Punktestand höher/niedriger ist. Bevor man die Abfrage sendet, sollte man vorher schon mal überprüft haben, ob dies der Fall ist, das spart Zeit. MD5 ist der MD5-Fingerprint des Levels. Sinn der Sache ist, das man ja theoretisch das Level auf der HDD zwischenspeichert. Jemand könnte auf die Idee kommen, diese Datei zu manipulieren. Von daher sollte man einen frischen MD5-Fingerprint der Leveldaten erzeugen und übermitteln, dann fällt dieser Betrug sofort auf.

Code: Alles auswählen

Procedure srv_GetMyScore(level,*pos.long,*score.long)
  datas$=srv_addCrypt(#srv_docommand+":getmyscore;id:"+Str(level))
  ret$=srv_Request(datas$)
  If srv_iserror(ret$)=#srv_ok
    a$=srv_DeCrypt(ret$)
    *pos\l=Val(StringField(a$,1,#LF$))
    *score\l=Val(StringField(a$,2,#LF$))
  EndIf
  
  ProcedureReturn srv_isError(ret$)
EndProcedure
Sofern die Funktion #srv_ok zurückmeldet, kann man den aktuellen Punktestand für dieses Level des eingelogten Spielers abfragen. Auch erhält man die aktuelle Position des Scores zurück.

Code: Alles auswählen

Procedure srv_GetHiscore(level,List mylist.srv_hiscore(),start=0,length=10); gibt Anzahl zurück!
  Datas$=srv_addcrypt(#srv_docommand+":gethiscore;id:"+Str(level)+";start:"+Str(start)+";length:"+Str(length))
  ret$=srv_Request(datas$)
  anzahl=0
  If srv_isError(ret$)=#srv_ok    
    i=0
    
    dc$=srv_DeCrypt(ret$)
    Repeat
      i+1
      l$=StringField(dc$,i,#LF$)
      If l$
        anzahl+1
        AddElement(mylist())
        mylist()\name=StringField(l$,1,";")
        mylist()\score=Val(StringField(l$,2,";"))
      Else
        Break
      EndIf
    ForEver
  EndIf
  ProcedureReturn anzahl
EndProcedure
Fragt die Hiscoreliste eines Levels ab. In der Default-Einstellung werden die ersten 10 Positionen abgefragt und in der LinkedList zurückgegeben. Genauso wie Srv_ListLevel() gibt diese Funktion die Anzahl der Einträge zurück! - Eine Hiscoreliste kann auch leer sein oder weniger Einträge beinhalten.

Code: Alles auswählen

Procedure srv_GetLevel(Level,*mem,memlen); negativ=Fehler, 0=fehler, ansonsten länge von buffer
  c$=srv_addcrypt(#srv_docommand+":getlevel;id:"+Str(level))
  ret=0
  id=OpenNetworkConnection(srv_host,srv_port)
  If id
    l=srv_StartRequest(id,srv_url,c$,ffield$,ffile$,fbuffer,fsize)
    If srv_respond_state$="200"
      If l>0
        If l>memlen
          ret=-26
        Else
          CopyMemory(srv_respond_buffer,*mem,l)
          ret=l
        EndIf
      Else
        ret=-1; keine Antwort
      EndIf
    Else
      ret= -2; Server-Fehler
    EndIf
    CloseNetworkConnection(id)
    srv_StartRequest(0,"","","","",0,0)
    ProcedureReturn ret
  Else
    ProcedureReturn -3;Netzwerk-Fehler
  EndIf
EndProcedure
Ganz vergessen, man kann ein Level downloaden. Idealerweise sollte man diese Leveldaten irgendwo cachen und dann diese benutzen anstatt immer wieder beim Server neu anzufragen. Das spart Traffic. Der Rückgabewert ist hier anders - siehe Kommentar.

hier noch zwei Komfor-Routinen:

Code: Alles auswählen

Procedure Srv_NameRequester(win_main,Title$="Register",borderless=#False)
  ret=-1
  ;erstmal schaun, ob schon was da ist
  
  If srv_LoadUserData()=#srv_ok
    ret=srv_authuser()
    Select ret
      Case #srv_ok
        ProcedureReturn #srv_ok
      Case 12
        MessageRequester(title$,"User unknown")
        srv_RemoveUserData()
        ;weiter mitProcedureReturn      Case 17
        MessageRequester(title$,"User banned!")
        srv_removeuserdata()
        ;weiter mit registrieren
      Default
        MessageRequester(Title$,srv_error(ret))
        ProcedureReturn ret
    EndSelect
  EndIf
  
  ;fenster öffnen
  If borderless
    a=#PB_Window_BorderLess
  Else
    a=0
  EndIf
  If win_main
    win= OpenWindow(#PB_Any, 0, 0, 330, 170, title$, a|#PB_Window_WindowCentered,win_main)
  Else
    win= OpenWindow(#PB_Any, 0, 0, 330, 170, title$, a|#PB_Window_ScreenCentered)
  EndIf
  
  If win
    ;{ Fenster aufbauen
    If borderless
      Frame3DGadget(#PB_Any, 0, 0, 330, 170, "", #PB_Frame3D_Double)
    EndIf
    Frame3DGadget(#PB_Any, 10, 10, 310, 50, "Name (3-20 Chars, alphanumeric)")
    Frame3DGadget(#PB_Any, 10, 70, 310, 50, "E-Mail (Optional)")
    
    but_register=ButtonGadget(#PB_Any, 20, 130, 140, 30, "Register", #PB_Button_Default)
    but_cancel=ButtonGadget(#PB_Any, 170, 130, 140, 30, "Cancel")
    str_name=StringGadget(#PB_Any, 20, 30, 290, 20, "")
    str_email=StringGadget(#PB_Any, 20, 90, 290, 20, "")
    
    SetActiveGadget(str_name)
    CompilerIf #PB_Compiler_OS=#PB_OS_Windows ; Unter windows den Username übernehmen
      SetGadgetText(str_name,GetEnvironmentVariable("USERNAME"))
    CompilerEndIf
    AddKeyboardShortcut(win,#PB_Shortcut_Return,1)
    AddKeyboardShortcut(win,#PB_Shortcut_Escape,2)
    ;}
    
    ;{ Hauptschleife
    
    ok=#False
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_Gadget
          Select EventGadget()
            Case but_cancel
              ok=1
            Case but_register
              ok=2
          EndSelect
        Case #PB_Event_Menu
          Select EventMenu()
            Case 1
              Select GetActiveGadget()
                Case str_name     :SetActiveGadget(str_email)
                Case str_email    :ok=2
                Case but_register :ok=2
                Case but_cancel   :ok=1
              EndSelect
            Case 2:ok=1
          EndSelect
      EndSelect
      
      ;{ registrieren auslösen
      If ok=2
        name$=GetGadgetText(str_name)
        email$=GetGadgetText(str_email)
        
        ReplaceString(name$,":"," ",#PB_String_InPlace)
        ReplaceString(name$,";"," ",#PB_String_InPlace)
        ReplaceString(email$,":"," ",#PB_String_InPlace)
        ReplaceString(email$,";"," ",#PB_String_InPlace)
        
        If Left(name$,1)<>"#"
          ;neuer benutzer
          err=srv_adduser(name$,email$)
          If err<>#srv_ok
            Select err
              Case 15: err$="Please use only alphanumeric"
              Case 16: err$="Name is too short/long (3-20)"
              Case 14: err$="Name already in use"
              Default:err$=srv_error(err)
            EndSelect
            MessageRequester(Title$,err$)
            ok=#False
          Else
            If srv_SaveUserData()=#srv_ok
              ret=#srv_ok
            EndIf            
            srv_username=name$
            MessageRequester(Title$,"Account registered")
          EndIf
        Else
          ;bekannter benutzer
          srv_setuser(Right(name$,Len(name$)-1),email$)
          If srv_AuthUser()=#srv_ok
            MessageRequester(Title$,"Account recoverd."+#LF$+"Welcome back "+srv_username)
            If srv_SaveUserData()=#srv_ok
              ret=#srv_ok
            EndIf   
          Else 
            MessageRequester(Title$,"ID or KEY unknown"+#LF$+"or # in name")
            ok=0
          EndIf
        EndIf 
      EndIf  
      ;}
      
    Until ok
    ;}
    
    CloseWindow(win)
  EndIf
  
  
  ProcedureReturn ret
EndProcedure
Wenn schon Benutzerdaten abgespeichert wurden, werden diese geladen und der Benutzer eingelogt, ansonsten wird ein Registrieren-Dialog geöffnet, in der Datenbank angelegt und die Daten abgespeichert. Gibt diese Routine #srv_ok zurück, ist der User eingelogt ansonsten gabs einen Fehler, oder er hat abbruch (-1) geklickt.
Wenn man einen Account recovern will (weil man die Daten per E-Mail zuschicken lassen hat), kann man als Name "#idnummer" und als EMail den Key eingeben.
Als Benutzername wird die Enviromentvariable %USERNAME% hergenommen - sofern man unter Windows arbeitet.

Code: Alles auswählen

Procedure Srv_LoadAllLevel(List mylist.srv_level(),start=0)
  ClearList (mylist())
  k=start
  While srv_listlevel(mylist(),k,100)=100
    k+100
  Wend
EndProcedure
Mit dieser Funktion werden alle Leveldaten von Server abgefragt! Die LinkedList wird vorher gelöscht.

so das wäre die Datei - als nächstes folgt noch ein Beispiel!

Re: Levels hochladen, bewerten und Online-Hiscoreliste

Verfasst: 24.06.2011 23:54
von GPI
Hier ein paar Beispielaufrufe:

Code: Alles auswählen

XIncludeFile "server.pb"

;wir legen mal einen Key fest
PokeS(*srv_key,"12345678901234561234567890123456")

;erstmal ein paar nötige Inits
InitNetwork():OpenCryptRandom()

;serverdaten setzen und wohin Userdaten gespeichert werden sollen
;da wir einen lokalen XAMPP-Server nutzen, natürlich 127.0.0.1
;die Datei heißt "/index.php" - das / an anfang ist wichtig.
;Der Server muss die Version 1.24 haben.
;Die Userdaten sollen unter %appdata%\gpihome.eu\user.dat 
;abgelegt werden - so können auch andere Programme das einfach nutzen.
;Unter Vista/Win7 eignet sich das eigene Verzeichnis nicht - es könnte schreibgeschützt sein! (c:\program files\)
srv_SetServer("127.0.0.1","/index.php",80,"1.24",GetEnvironmentVariable("APPDATA")+"\gpihome.eu\user.dat")


;erstmal eine Session eröffnen
If srv_StartSession()=#srv_ok
  
  ;automatic-funktion. Hier wird entweder ein gespeicherter User geladen oder ein neuer angelegt
  ;anschließend ist der User auch angemeldet.
  If Srv_NameRequester(0)=#srv_ok
    Debug "angemeldet:"+srv_username
    
    ;wir ändern mal spasseshalber die Email-adresse
    a=srv_newemail("test12@adsaf.de")
    If a:Debug "e-mail-error:"+srv_error(a):EndIf
    
    ;Key anfordern (e-mail zuschicken
    a=srv_getKey("test12@adsaf.de")
    If a:Debug "getkey-error:"+srv_error(a):EndIf
    
    ;Simulierter Upload
    testdummy.s="CrillionScenario hallo" ;simulierte Leveldatei
    a=srv_Upload("leveldaten",@testdummy,Len(testdummy))
    If a:Debug "upload-error:"+srv_error(a):EndIf
    
    ;Wir bennen das mal sehr kreativ um
    a=srv_RenUpload(1,"muhahaha")
    If a:Debug "renupload-error:"+srv_error(a):EndIf
    
    ;Natürlich bewerten wir unser Level
    a=srv_RateLevel(1,5)
    If a:Debug "rate-error:"+srv_error(a):EndIf
    
    
    ;Mal die Komplette Levelliste Abfragen
    NewList mylist.srv_level()
    Srv_LoadAllLevel(mylist())
    Debug "Levellist"
    ForEach mylist()
      Debug Str(mylist()\id)+" - "+mylist()\name+" - "+mylist()\from+" - "+Str(mylist()\editable)+" - "+StrF(mylist()\rate,2)+" ("+Str(mylist()\count)+")"
    Next
    
    ;Tragen wir doch mal ein Punktestand ein
    testdummy.s="CrillionScenario hallo"; das sind unsere Leveldaten!
    a=srv_SetHiscore(1,99,MD5Fingerprint(@testdummy,Len(testdummy)))
    If a:Debug "score-error:"+srv_error(a):EndIf
    
    ;Mal schaun, wo wir stehen!
    a=srv_GetMyScore(1,@pos,@score)
    If a:Debug "MyScore-error:"+srv_error(a):EndIf
    If a=#srv_ok
      Debug Str(Pos)+" - "+Str(score)
    Else
      Debug "bisher keine Punkte in diesen Level"
    EndIf
    
    
    ;Und nun mal ein Download
    *mem=AllocateMemory(64000)
    a=srv_GetLevel(1,*mem,64000)
    If a>0 And Val(srv_GetLastError())=#srv_ok ;die Überprüfung ist hier etwas umständlicher
      Debug "level:"+PeekS(*mem,a)
    Else
      a=-a
      If a:Debug "levelfehler:"+srv_error(a):EndIf
    EndIf
    FreeMemory(*mem)
    
    ;Schauen wir uns mal die Hiscoreliste genauer an
    NewList mylist.srv_hiscore()
    ClearList(mylist()) ; ist hier unsinnig, aber srv_GetHiscore löscht die Liste nicht!
    Srv_GetHiscore(1,mylist())
    Debug "Hiscore"
    ForEach mylist()
      Debug mylist()\name+" - "+Str(mylist()\score)
    Next
    
    ;So, schön wars, weg mit den level
    a=srv_DelUpload(1)
    If a:Debug "delupload-error:"+srv_error(a):EndIf
    
  Else
    Debug "Anmeldefehler:"+srv_GetLastError()
  EndIf
  
  
  ;a=srv_adduser("12345678901234567890","123456789012345678901");
  ;If a:Debug "add-error:"+srv_GetLastError():EndIf
  
  ;Zum schluss löschen wir die Session.
  srv_StopSession()
Else
  Debug "starterror:"+srv_GetLastError()
EndIf
Wie gesagt, die Routinen sind nicht perfekt, es ist mehr ein Rohling.

Falls wer Fehler findet oder Verbesserungsvorschläge hat, nur her damit.

Re: Levels hochladen, bewerten und Online-Hiscoreliste

Verfasst: 25.06.2011 00:01
von shadow
:shock:
Da hast du dir ja echt viel Mühe gemacht und ein großes THX.
Aber in diesem Falle wäre es besser, wenn du es als ZIP-Archiv angeboten hättest mit einer README dazu...

Im übrigen, MD5 gilt nicht mehr als "sicher" soweit ich das aus news berichten weiß. Weiß aber grad nicht mehr ob es auf die Eindeutigkeit oder auf das cracken bezieht...

Re: Levels hochladen, bewerten und Online-Hiscoreliste

Verfasst: 25.06.2011 01:17
von GPI
Ich will das ja auch zukünftig bei meinen Programmen einsetzen - ergo muss es etwas universell werden.

Ich denke mal, die Sicherheit von MD5 zur Überprüfung der Dateien auf Veränderung dürfte ausreichen - ich werd die Daten eh nochmal mit einen Key verschlüsseln.

Wer auch immer mein Programm mal zerlegen wird, wird das ganze eh ziemlich leicht nachvollziehen können, was ich da mache - darum gehts ja nicht. Ich speichere ja kein Bankdaten da ab. Das kritischste ist da wirklich die E-Mail. Da ich aber keine Passwörter brauche, kann der User nicht sein Universalpasswort, das er bspw. auch für die E-Mail nutzt, hier eintippen :)

Diese ganzen Mechanismen sind auch mehr dafür da, das kein Script-Kiddie sich in die Hiscoreliste hochschummelt.

Was halt komplett fehlt ist ein Usermanagment - da nehm ich phpmyadmin für her - das tuts für sowas erstmal auch.