highscores can now be submitted to and retrieved from a server

This commit is contained in:
2011-03-16 17:03:41 +01:00
parent f57a4e9fb9
commit 21239c9962
22 changed files with 2264 additions and 61 deletions
-2
View File
@@ -9,8 +9,6 @@ namespace asteroids {
int Controller::OnInit (int argc, char *argv[]) {
Engine::ControllerBase::OnInit (argc, argv);
mBindings[SDLK_q] = "quit";
mBindings[SDLK_UP] = "+forward";
mBindings[SDLK_LEFT] = "+turnleft";
mBindings[SDLK_RIGHT] = "+turnright";
+258 -35
View File
@@ -2,6 +2,7 @@
#include <fstream>
#include <algorithm>
#include <boost/filesystem.hpp>
#include <SDL/SDL_net.h>
#include "Model.h"
#include "Physics.h"
@@ -17,6 +18,10 @@ namespace asteroids {
static Model* ModelInstance = NULL;
Engine::Variable Model::HighscoreServerName ("highscore_server_name", "asteroids.fysx.org");
Engine::Variable Model::HighscoreServerPath ("highscore_server_path", "/highscore/highscore.php?format=raw");
Engine::Variable Model::UseServerHighscore ("use_server_highscore", "false");
/*
* Inherited Module functions
*/
@@ -40,20 +45,20 @@ int Model::OnInit (int argc, char* argv[]) {
// First we reset the highscore list
mHighscoreList.clear();
// then we try to load values from the file
LoadHighscoreList();
// LoadHighscoreList();
// if we have less than the usual number of entries we add default values
if (mHighscoreList.size() < 10) {
AddHighscoreEntry ("Imperator", 1000000);
AddHighscoreEntry ("Darth Vader", 800000);
AddHighscoreEntry ("Luke Skywalker", 600000);
AddHighscoreEntry ("Han Solo", 400000);
AddHighscoreEntry ("Princess Leia", 200000);
AddHighscoreEntry ("C3PO", 100000);
AddHighscoreEntry ("R2-D2", 50000);
AddHighscoreEntry ("Chewy", 10000);
AddHighscoreEntry ("Mr. Ewok", 5000);
AddHighscoreEntry ("Jabba the Hutt", 1000);
AddLocalHighscoreEntry ("Imperator", 1000000);
AddLocalHighscoreEntry ("Darth Vader", 800000);
AddLocalHighscoreEntry ("Luke Skywalker", 600000);
AddLocalHighscoreEntry ("Han Solo", 400000);
AddLocalHighscoreEntry ("Princess Leia", 200000);
AddLocalHighscoreEntry ("C3PO", 100000);
AddLocalHighscoreEntry ("R2-D2", 50000);
AddLocalHighscoreEntry ("Chewy", 10000);
AddLocalHighscoreEntry ("Mr. Ewok", 5000);
AddLocalHighscoreEntry ("Jabba the Hutt", 1000);
}
// Reset the newest highscore entry index which may be used for highlighting
@@ -69,12 +74,28 @@ int Model::OnInit (int argc, char* argv[]) {
mLevelAuthor = "";
mLevelTitle = "";
// initialize SDL_net to be able to retrieve highscore from the internet
if (SDLNet_Init() == -1) {
Engine::LogError("SDLNet_Init: %s\n", SDLNet_GetError());
}
SubmitGlobalHigscoreEntry ("asteroids_full", 321321321);
Engine::RegisterListener (this, EventAccelerateStart);
Engine::RegisterListener (this, EventAccelerateStop);
return result;
}
void Model::OnDestroy() {
Engine::ModelBase::OnDestroy();
mAsteroids.clear();
mLevelList.clear();
SaveLocalHighscoreList();
SDLNet_Quit();
}
bool Model::OnReceiveEvent (const Engine::EventBasePtr &event) {
switch (event->mEventType) {
case EventAccelerateStart:
@@ -151,27 +172,13 @@ unsigned int Model::InitLevelList () {
return mLevelList.size();
}
void Model::LoadHighscoreList () {
Engine::LogDebug ("Loading highscore file");
boost::filesystem::path highscore_file(Engine::GetUserDirFullPath("/highscore.dat"));
void Model::ParseHighscoreStream (std::istream &highscore_stream) {
std::string line;
// if the file does not exist, we create it and write standard values into
// it.
if (!boost::filesystem::exists(highscore_file))
return;
if (!boost::filesystem::is_regular_file(highscore_file)) {
Engine::LogError ("Could not load highscore file: %s is not a regular file!", highscore_file.filename().c_str());
}
std::ifstream score_stream (highscore_file.filename().c_str());
while (!score_stream.eof()) {
while (getline(highscore_stream, line)) {
std::string name;
unsigned int points;
std::string line;
getline (score_stream, line);
std::string::size_type delimiter = line.find ('\t');
if (delimiter == std::string::npos)
break;
@@ -182,11 +189,48 @@ void Model::LoadHighscoreList () {
points_stream >> points;
Engine::LogDebug ("Read Highscore Entry Name: %s Points: %d", name.c_str(), points);
AddHighscoreEntry (name, points);
AddLocalHighscoreEntry (name, points);
}
}
void Model::SaveHighscoreList () {
void Model::LoadHighscoreList () {
mHighscoreList.clear();
if (Model::UseServerHighscore.GetBoolValue()) {
Engine::LogMessage ("Retrieving Highscore from server");
std::stringstream global_highscore_stream;
if (PullGlobalHighscore(global_highscore_stream)) {
ParseHighscoreStream(global_highscore_stream);
return;
}
}
LoadLocalHighscoreList();
}
void Model::LoadLocalHighscoreList () {
Engine::LogDebug ("Loading local highscore file");
boost::filesystem::path highscore_file(Engine::GetUserDirFullPath("/highscore.dat"));
// if the file does not exist, we create it and write standard values into
// it.
if (!boost::filesystem::exists(highscore_file))
return;
if (!boost::filesystem::is_regular_file(highscore_file)) {
Engine::LogError ("Could not load highscore file: %s is not a regular file!", highscore_file.filename().c_str());
}
std::ifstream score_stream (highscore_file.filename().c_str());
ParseHighscoreStream (score_stream);
score_stream.close();
}
void Model::SaveLocalHighscoreList () {
Engine::LogDebug ("Saving local highscore file");
std::ofstream highscore_file (Engine::GetUserDirFullPath("/highscore.dat").c_str());
std::list<HighscoreEntry>::iterator iter = mHighscoreList.begin();
@@ -207,7 +251,7 @@ bool highscore_cmp (Model::HighscoreEntry a, Model::HighscoreEntry b) {
*
* \TODO Re-think usage of mNewestHighscoreEntryIndex variable in this function
*/
unsigned int Model::AddHighscoreEntry(const std::string &name, const unsigned int points) {
unsigned int Model::AddLocalHighscoreEntry(const std::string &name, const unsigned int points) {
HighscoreEntry entry;
entry.name = name;
entry.points = points;
@@ -239,13 +283,192 @@ unsigned int Model::AddHighscoreEntry(const std::string &name, const unsigned in
// if we have all 10 entries then we can save
// the highscore
SaveHighscoreList();
SaveLocalHighscoreList();
mNewestHighscoreEntryIndex = 99999;
return 99999;
}
bool Model::PullGlobalHighscore(std::stringstream &highscore_stream) {
highscore_stream.str("");
IPaddress server_address;
if (SDLNet_ResolveHost (&server_address, Model::HighscoreServerName.GetStringValue().c_str(), 80) == -1) {
Engine::LogWarning ("SDL_net resolve host: %s", SDLNet_GetError());
return false;
}
char ip_address_char[4];
memcpy (&ip_address_char[0], &server_address.host, sizeof(char) * 4);
int ip_address[4];
ip_address[0] = static_cast<int>(ip_address_char[0]);
ip_address[1] = static_cast<int>(ip_address_char[1]);
ip_address[2] = static_cast<int>(ip_address_char[2]);
ip_address[3] = static_cast<int>(ip_address_char[3]);
Engine::LogMessage ("Pulling global highscore from server %s (%d.%d.%d.%d)",
Model::HighscoreServerName.GetStringValue().c_str(),
ip_address[0],
ip_address[1],
ip_address[2],
ip_address[3]
);
TCPsocket server_socket = SDLNet_TCP_Open (&server_address);
if (!server_socket) {
Engine::LogError ("SDL_net tcp open: %s", SDLNet_GetError());
return false;
}
std::string http_query_string;
http_query_string = std::string ("GET ") + Model::HighscoreServerPath.GetStringValue() + std::string(" HTTP/1.1\r\nHost: asteroids.fysx.org\r\nConnection: close\r\n\r\n");
int bytes_sent;
bytes_sent = SDLNet_TCP_Send (server_socket, http_query_string.c_str(), http_query_string.size());
if (bytes_sent != http_query_string.size()) {
Engine::LogError ("SDL_net tcp send: %s", SDLNet_GetError());
return false;
}
char receive_buffer[255];
std::string http_result;
bool receiving = true;
while (receiving) {
// reset the buffer
bzero (&receive_buffer[0], 255);
// read the data
int received_bytes = SDLNet_TCP_Recv (server_socket, receive_buffer, 255);
if (received_bytes <= 0) {
receiving = false;
}
http_result.append (receive_buffer, received_bytes);
}
// we have to strip the whitespaces twice to cut out the content
http_result = strip_whitespaces (http_result);
http_result = http_result.substr (http_result.find ("\n\r"), http_result.size());
http_result = strip_whitespaces (http_result);
Engine::LogDebug ("Received Highscore: %s", http_result.c_str());
SDLNet_TCP_Close (server_socket);
highscore_stream.str(http_result);
return true;
}
bool Model::SubmitGlobalHigscoreEntry (const std::string &name, const unsigned int points) {
IPaddress server_address;
if (SDLNet_ResolveHost (&server_address, Model::HighscoreServerName.GetStringValue().c_str(), 80) == -1) {
Engine::LogWarning ("SDL_net resolve host: %s", SDLNet_GetError());
return false;
}
char ip_address_char[4];
memcpy (&ip_address_char[0], &server_address.host, sizeof(char) * 4);
int ip_address[4];
ip_address[0] = static_cast<int>(ip_address_char[0]);
ip_address[1] = static_cast<int>(ip_address_char[1]);
ip_address[2] = static_cast<int>(ip_address_char[2]);
ip_address[3] = static_cast<int>(ip_address_char[3]);
Engine::LogDebug ("Submitting highscore player_name='%s' score_value='%d' to server %s (%d.%d.%d.%d)",
name.c_str(),
points,
Model::HighscoreServerName.GetStringValue().c_str(),
ip_address[0],
ip_address[1],
ip_address[2],
ip_address[3]
);
TCPsocket server_socket = SDLNet_TCP_Open (&server_address);
if (!server_socket) {
Engine::LogError ("SDL_net tcp open: %s", SDLNet_GetError());
return false;
}
std::stringstream points_stringstream;
points_stringstream << points;
std::stringstream hash_input;
hash_input << name << ":" << points << ":" << sha256_hash ("asteroids rule");
std::string key = sha256_hash (hash_input.str());
std::string http_query_string;
http_query_string = std::string ("GET ")
+ Model::HighscoreServerPath.GetStringValue()
+ std::string("&player_name=") + name
+ std::string("&score_value=") + points_stringstream.str()
+ std::string("&key=") + key
+ std::string(" HTTP/1.1\r\nHost: asteroids.fysx.org\r\nConnection: close\r\n\r\n");
int bytes_sent;
bytes_sent = SDLNet_TCP_Send (server_socket, http_query_string.c_str(), http_query_string.size());
if (bytes_sent != http_query_string.size()) {
Engine::LogError ("SDL_net tcp send: %s", SDLNet_GetError());
return false;
}
char receive_buffer[255];
std::string http_result;
bool receiving = true;
while (receiving) {
// reset the buffer
bzero (&receive_buffer[0], 255);
// read the data
int received_bytes = SDLNet_TCP_Recv (server_socket, receive_buffer, 255);
if (received_bytes <= 0) {
receiving = false;
}
http_result.append (receive_buffer, received_bytes);
}
// we have to strip the whitespaces twice to cut out the content
http_result = strip_whitespaces (http_result);
http_result = http_result.substr (http_result.find ("\n\r"), http_result.size());
http_result = strip_whitespaces (http_result);
SDLNet_TCP_Close (server_socket);
if (http_result == "OK") {
Engine::LogDebug ("Submission successful: %s", http_result.c_str());
LoadHighscoreList();
return true;
} else {
Engine::LogMessage ("Submission unsuccessful: %s", http_result.c_str());
}
return false;
}
void Model::SubmitHighscoreEntry (const std::string &name, const unsigned int points) {
if (Model::UseServerHighscore.GetBoolValue()) {
Engine::LogMessage ("Sending highscore entry to the server");
SubmitGlobalHigscoreEntry (name, points);
} else {
AddLocalHighscoreEntry (name, points);
}
}
int Model::DoLoadLevel (const char* filename) {
Engine::LogMessage ("Loading level from %s", filename);
std::fstream level_file (filename, std::ios::in);
@@ -403,11 +626,11 @@ void Model::SetGameState (const unsigned int &state) {
}
bool Model::OnGameOver() {
Engine::LogMessage ("Points = %d lowest = %d", mPoints, mHighscoreList.back().points );
Engine::LogDebug ("Points = %d lowest = %d", mPoints, mHighscoreList.back().points );
SubmitHighscoreEntry (mPlayerName, mPoints);
if (mPoints > mHighscoreList.back().points) {
Engine::LogMessage ("New Highscore!");
AddHighscoreEntry (mPlayerName, mPoints);
}
return false;
@@ -425,7 +648,7 @@ void Model::OnNewGame() {
}
void Model::OnShipExplode () {
Engine::PlaySound(Engine::GetResourceFullPath("/data/sounds/rock_destroyed.wav"));
Engine::PlaySound(Engine::GetResourceFullPath("/data/sounds/ship_destroyed.wav"));
mPlayerLives --;
+14 -9
View File
@@ -63,22 +63,22 @@ class Model : public Engine::ModelBase {
unsigned int points;
};
void ParseHighscoreStream (std::istream &highscore_stream);
void LoadHighscoreList ();
void SaveHighscoreList ();
unsigned int AddHighscoreEntry(const std::string &name, const unsigned int points);
bool HighscoreCmp (HighscoreEntry a, HighscoreEntry b);
bool PullGlobalHighscore (std::stringstream &highscore_stream);
bool SubmitGlobalHigscoreEntry (const std::string &name, const unsigned int points);
void SubmitHighscoreEntry (const std::string &name, const unsigned int points);
unsigned int AddLocalHighscoreEntry(const std::string &name, const unsigned int points);
void LoadLocalHighscoreList ();
void SaveLocalHighscoreList ();
std::list<HighscoreEntry> mHighscoreList;
unsigned int mNewestHighscoreEntryIndex;
protected:
/** \brief Initializes the system */
virtual int OnInit (int argc, char* argv[]);
virtual void OnDestroy() {
Engine::ModelBase::OnDestroy();
mAsteroids.clear();
mLevelList.clear();
SaveHighscoreList();
};
virtual void OnDestroy();
virtual void OnRegisterCommands ();
virtual void OnCreateEntity (const int type, const unsigned int id);
@@ -100,6 +100,11 @@ class Model : public Engine::ModelBase {
std::string mLevelAuthor;
std::string mLevelTitle;
static Engine::Variable HighscoreServerName;
static Engine::Variable HighscoreServerPath;
static Engine::Variable UseServerHighscore;
static Engine::Variable UseServerHighscoreAsked;
virtual bool OnReceiveEvent (const Engine::EventBasePtr &event);
friend class View;
+17 -8
View File
@@ -710,8 +710,8 @@ void View::DrawUiHighscore() {
while (highscore_iter != GetModel()->mHighscoreList.end()) {
// Check whether we have to highlight an entry (such as when entering
// the name)
if (GetModel()->mNewestHighscoreEntryIndex < GetModel()->mHighscoreList.size()
&& GetModel()->mNewestHighscoreEntryIndex == i) {
if (GetModel()->GetPlayerName() == highscore_iter->name
&& GetModel()->GetPoints() == highscore_iter->points) {
// we highlight the newest entry
SelectFont("console.ttf color=#e8d500 size=23");
} else {
@@ -726,7 +726,8 @@ void View::DrawUiHighscore() {
highscore_iter++;
}
if (Engine::GUI::Button (1, "Back to Menu", screen_right * 0.5 - 250 * 0.5, y + 16, button_width, button_height)) {
if (Engine::GUI::Button (1, "Back to Menu", screen_right * 0.5 - 250 * 0.5, y + 16, button_width, button_height)
|| Engine::GUI::CheckKeyPressed(SDLK_ESCAPE) ) {
PopViewState();
}
}
@@ -752,7 +753,8 @@ void View::DrawUiOptions() {
}
if (Engine::GUI::Button (5, "Back", screen_right * 0.5 - 100, 380, button_width, button_height)) {
if (Engine::GUI::Button (5, "Back", screen_right * 0.5 - 100, 380, button_width, button_height)
|| Engine::GUI::CheckKeyPressed(SDLK_ESCAPE) ) {
PopViewState();
}
}
@@ -773,7 +775,7 @@ _Music\r\
DJad - Space Exploration\r\
\r\
_Sounds\r\
Marcus Zetterquist\r\
Martin Felis\r\
\r\
_Libraries\r\
libSDL\r\
@@ -782,11 +784,15 @@ _Libraries\r\
freetype2\r\
boost\r\
libpng\r\
\r\
Aaron D. Gifford's sha2 code\r\
\r\
_Tools\r\
GIMP\r\
Blender\r\
CMake\r\
sfxr-sdl\r\
Audacity\r\
\r\
_Special Thanks\r\
to my wonderful wife Katrina\r\
@@ -795,7 +801,6 @@ _Special Thanks\r\
\r\
\r\
\r\
\r\
_http://www.fysx.org\r\
\r\
\r\
@@ -804,6 +809,7 @@ _http://www.fysx.org\r\
\r\
\r\
\r\
\r\
:created with vim.\r\
::wq\r\
\r\
@@ -876,7 +882,9 @@ void View::DrawUiEnterPlayername() {
SelectFont("console.ttf size=23");
Engine::GUI::Label (1, "Enter your name: ", screen_right * 0.5 - 220, 250);
if (Engine::GUI::LineEdit (2, screen_right * 0.5 + 20, 238, player_name, 16)) {
std::string valid_chars ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890-_!.");
if (Engine::GUI::LineEditMasked (2, screen_right * 0.5 + 20, 238, player_name, 16, valid_chars)) {
GetModel()->SetPlayerName(player_name);
}
@@ -887,7 +895,8 @@ void View::DrawUiEnterPlayername() {
GetModel()->SetGameState(GameStateRunning);
}
if (Engine::GUI::Button (5, "Back", 20, 500, 180, 40)) {
if (Engine::GUI::Button (5, "Back", 20, 500, 180, 40)
|| Engine::GUI::CheckKeyPressed(SDLK_ESCAPE) ) {
PopViewState();
}
}
+4
View File
@@ -8,6 +8,7 @@
#include "Model.h"
#include "Physics.h"
#include "EntityFactory.h"
#include "Game.h"
#include <boost/filesystem.hpp>
@@ -164,12 +165,15 @@ int main (int argc, char* argv[]) {
Engine::PlayMusic (Engine::GetResourceFullPath("/data/sounds/intro_music.ogg"));
asteroids::GetModel()->LoadHighscoreList();
engine.MainLoop ();
// save the configuration
std::ofstream config_file (engine.GetUserDirFullPath("/config.rc").c_str());
config_file << "set effects_volume " << Engine::GetEffectsVolume() << std::endl;
config_file << "set music_volume " << Engine::GetMusicVolume() << std::endl;
config_file << "set use_server_highscore " << Engine::GetVariableString("use_server_highscore") << std::endl;
config_file.close();
SDL_WM_SetIcon(NULL,NULL);