Added a more user friendly messagebox when network errors are encountered.

This commit is contained in:
Mark Vejvoda 2010-03-17 06:25:19 +00:00
parent c925665d31
commit 89d3c66471
9 changed files with 2103 additions and 18 deletions

View File

@ -0,0 +1,205 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2001-2008 Martiño Figueroa
//
// You can redistribute this code and/or modify it under
// the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version
// ==============================================================
#include "main.h"
#include <string>
#include <cstdlib>
#include "game.h"
#include "main_menu.h"
#include "program.h"
#include "config.h"
#include "metrics.h"
#include "game_util.h"
#include "platform_util.h"
#include "platform_main.h"
#include "leak_dumper.h"
#include "network_interface.h"
using namespace std;
using namespace Shared::Platform;
using namespace Shared::Util;
namespace Glest{ namespace Game{
// =====================================================
// class ExceptionHandler
// =====================================================
class ExceptionHandler: public PlatformExceptionHandler{
public:
virtual void handle(){
string msg = "An error ocurred and Glest will close.\nPlease report this bug to "+mailString+", attaching the generated "+getCrashDumpFileName()+" file.";
Program *program = Program::getInstance();
if(program) {
program->showMessage(msg.c_str());
}
message(msg.c_str());
}
static void handleRuntimeError(const char *msg) {
Program *program = Program::getInstance();
if(program) {
program->showMessage(msg);
}
else {
message("An error ocurred and Glest will close.\nPlease report this bug to "+mailString+", attaching the generated "+getCrashDumpFileName()+" file.");
}
exit(0);
}
static int DisplayMessage(const char *msg, bool exitApp) {
Program *program = Program::getInstance();
if(program) {
program->showMessage(msg);
}
else {
message(msg);
}
if(exitApp == true) {
exit(0);
}
return 0;
}
};
// =====================================================
// class MainWindow
// =====================================================
MainWindow::MainWindow(Program *program){
this->program= program;
}
MainWindow::~MainWindow(){
delete program;
}
void MainWindow::eventMouseDown(int x, int y, MouseButton mouseButton){
switch(mouseButton){
case mbLeft:
program->mouseDownLeft(x, getH() - y);
break;
case mbRight:
program->mouseDownRight(x, getH() - y);
break;
default:
break;
}
}
void MainWindow::eventMouseUp(int x, int y, MouseButton mouseButton){
if(mouseButton==mbLeft){
program->mouseUpLeft(x, getH() - y);
}
}
void MainWindow::eventMouseDoubleClick(int x, int y, MouseButton mouseButton){
if(mouseButton == mbLeft){
program->mouseDoubleClickLeft(x, getH() - y);
}
}
void MainWindow::eventMouseMove(int x, int y, const MouseState *ms){
program->mouseMove(x, getH() - y, ms);
}
void MainWindow::eventKeyDown(char key){
program->keyDown(key);
}
void MainWindow::eventKeyUp(char key){
program->keyUp(key);
}
void MainWindow::eventKeyPress(char c){
program->keyPress(c);
}
void MainWindow::eventActivate(bool active){
if(!active){
//minimize();
}
}
void MainWindow::eventResize(SizeState sizeState){
program->resize(sizeState);
}
void MainWindow::eventClose(){
delete program;
program= NULL;
}
// =====================================================
// Main
// =====================================================
int glestMain(int argc, char** argv){
MainWindow *mainWindow= NULL;
Program *program= NULL;
ExceptionHandler exceptionHandler;
exceptionHandler.install( getCrashDumpFileName() );
try{
Config &config = Config::getInstance();
Socket::enableNetworkDebugInfo = (config.getBool("DebugMode","0") || config.getBool("DebugNetwork","0"));
NetworkInterface::setDisplayMessageFunction(ExceptionHandler::DisplayMessage);
showCursor(config.getBool("Windowed"));
program= new Program();
mainWindow= new MainWindow(program);
//parse command line
if(argc==2 && string(argv[1])=="-server"){
program->initServer(mainWindow);
}
else if(argc==3 && string(argv[1])=="-client"){
program->initClient(mainWindow, Ip(argv[2]));
}
else{
program->initNormal(mainWindow);
}
// test
//Shared::Platform::MessageBox(NULL,"Mark's test.","Test",0);
//throw runtime_error("test!");
//ExceptionHandler::DisplayMessage("test!", false);
//main loop
while(Window::handleEvent()){
program->loop();
}
}
catch(const exception &e){
restoreVideoMode();
//exceptionMessage(e);
ExceptionHandler::handleRuntimeError(e.what());
}
delete mainWindow;
return 0;
}
}}//end namespace
MAIN_FUNCTION(Glest::Game::glestMain)

View File

@ -40,11 +40,71 @@ using namespace Shared::Graphics::Gl;
namespace Glest{ namespace Game{
const int Program::maxTimes= 10;
Program *Program::singleton = NULL;
// =====================================================
// class Program::CrashProgramState
// =====================================================
Program::ShowMessageProgramState::ShowMessageProgramState(Program *program, const char *msg) :
ProgramState(program) {
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
msgBox.init("Ok");
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
if(msg) {
fprintf(stderr, "%s\n", msg);
msgBox.setText(msg);
} else {
msgBox.setText("Mega-Glest has crashed.");
}
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
mouse2dAnim = mouseY = mouseX = 0;
this->msg = (msg ? msg : "");
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
}
void Program::ShowMessageProgramState::render() {
Renderer &renderer= Renderer::getInstance();
renderer.clearBuffers();
renderer.reset2d();
renderer.renderMessageBox(&msgBox);
renderer.renderMouse2d(mouseX, mouseY, mouse2dAnim);
renderer.swapBuffers();
}
void Program::ShowMessageProgramState::mouseDownLeft(int x, int y) {
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
if(msgBox.mouseClick(x,y)) {
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
program->exit();
}
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
}
void Program::ShowMessageProgramState::mouseMove(int x, int y, const MouseState &mouseState) {
mouseX = x;
mouseY = y;
msgBox.mouseMove(x, y);
}
void Program::ShowMessageProgramState::update() {
mouse2dAnim = (mouse2dAnim +1) % Renderer::maxMouse2dAnim;
}
// ===================== PUBLIC ========================
Program::Program(){
Program::Program() {
programState= NULL;
singleton = this;
}
void Program::initNormal(WindowGl *window){
@ -72,11 +132,13 @@ void Program::initClient(WindowGl *window, const Ip &serverIp){
Program::~Program(){
delete programState;
programState = NULL;
Renderer::getInstance().end();
//restore video mode
restoreDisplaySettings();
singleton = NULL;
}
void Program::mouseDownLeft(int x, int y){
@ -160,30 +222,35 @@ void Program::resize(SizeState sizeState){
void Program::setState(ProgramState *programState)
{
if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
//if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
delete this->programState;
if(Socket::enableDebugText) printf("In [%s::%s] %d\n",__FILE__,__FUNCTION__,__LINE__);
//if(Socket::enableDebugText) printf("In [%s::%s] %d\n",__FILE__,__FUNCTION__,__LINE__);
this->programState= programState;
programState->load();
if(Socket::enableDebugText) printf("In [%s::%s] %d\n",__FILE__,__FUNCTION__,__LINE__);
//if(Socket::enableDebugText) printf("In [%s::%s] %d\n",__FILE__,__FUNCTION__,__LINE__);
programState->init();
if(Socket::enableDebugText) printf("In [%s::%s] %d\n",__FILE__,__FUNCTION__,__LINE__);
//if(Socket::enableDebugText) printf("In [%s::%s] %d\n",__FILE__,__FUNCTION__,__LINE__);
updateTimer.reset();
updateCameraTimer.reset();
fpsTimer.reset();
if(Socket::enableDebugText) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
//if(Socket::enableDebugText) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
}
void Program::exit(){
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
window->destroy();
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
}
// ==================== PRIVATE ====================
@ -267,4 +334,35 @@ void Program::restoreDisplaySettings(){
}
}
void Program::showMessage(const char *msg) {
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
ProgramState *originalState = NULL;
if(programState) {
//delete programState;
originalState = programState;
}
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
programState = new ShowMessageProgramState(this, msg);
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
while(Window::handleEvent()) {
loop();
}
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
delete programState;
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
programState = originalState;
//if(Socket::enableDebugText) printf("In [%s::%s %d]\n",__FILE__,__FUNCTION__,__LINE__);
}
}}//end namespace

View File

@ -0,0 +1,135 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2001-2008 Martiño Figueroa
//
// You can redistribute this code and/or modify it under
// the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version
// ==============================================================
#ifndef _GLEST_GAME_PROGRAM_H_
#define _GLEST_GAME_PROGRAM_H_
#include "context.h"
#include "platform_util.h"
#include "window_gl.h"
#include "socket.h"
#include "components.h"
using Shared::Graphics::Context;
using Shared::Platform::WindowGl;
using Shared::Platform::SizeState;
using Shared::Platform::MouseState;
using Shared::Platform::PerformanceTimer;
using Shared::Platform::Ip;
namespace Glest{ namespace Game{
class Program;
class MainWindow;
// =====================================================
// class ProgramState
//
/// Base class for all program states:
/// Intro, MainMenu, Game, BattleEnd (State Design pattern)
// =====================================================
class ProgramState{
protected:
Program *program;
public:
ProgramState(Program *program) {this->program= program;}
virtual ~ProgramState(){};
virtual void render()=0;
virtual void update(){};
virtual void updateCamera(){};
virtual void tick(){};
virtual void init(){};
virtual void load(){};
virtual void end(){};
virtual void mouseDownLeft(int x, int y){};
virtual void mouseUpLeft(int x, int y){};
virtual void mouseDownRight(int x, int y){};
virtual void mouseDoubleClickLeft(int x, int y){};
virtual void mouseMove(int x, int y, const MouseState *mouseState){};
virtual void keyDown(char key){};
virtual void keyUp(char key){};
virtual void keyPress(char c){};
};
// ===============================
// class Program
// ===============================
class Program{
private:
static const int maxTimes;
class ShowMessageProgramState : public ProgramState {
GraphicMessageBox msgBox;
int mouseX;
int mouseY;
int mouse2dAnim;
string msg;
public:
ShowMessageProgramState(Program *program, const char *msg);
virtual void render();
virtual void mouseDownLeft(int x, int y);
virtual void mouseMove(int x, int y, const MouseState &mouseState);
virtual void update();
};
private:
ProgramState *programState;
PerformanceTimer fpsTimer;
PerformanceTimer updateTimer;
PerformanceTimer updateCameraTimer;
WindowGl *window;
static Program *singleton;
public:
Program();
~Program();
static Program *getInstance() {return singleton;}
void initNormal(WindowGl *window);
void initServer(WindowGl *window);
void initClient(WindowGl *window, const Ip &serverIp);
//main
void mouseDownLeft(int x, int y);
void mouseUpLeft(int x, int y);
void mouseDownRight(int x, int y);
void mouseDoubleClickLeft(int x, int y);
void mouseMove(int x, int y, const MouseState *mouseState);
void keyDown(char key);
void keyUp(char key);
void keyPress(char c);
void loop();
void resize(SizeState sizeState);
void showMessage(const char *msg);
//misc
void setState(ProgramState *programState);
void exit();
private:
void init(WindowGl *window);
void setDisplaySettings();
void restoreDisplaySettings();
};
}} //end namespace
#endif

View File

@ -139,11 +139,14 @@ void ClientInterface::updateLobby()
//check consistency
if(Config::getInstance().getBool("NetworkConsistencyChecks"))
{
if(networkMessageIntro.getVersionString()!=getNetworkVersionString())
if(networkMessageIntro.getVersionString() != getNetworkVersionString())
{
string sErr = "Server and client versions do not match (" + networkMessageIntro.getVersionString() + "). You have to use the same binaries.";
string sErr = "Server and client binary versions do not match [" + networkMessageIntro.getVersionString() + "]. You have to use the same binaries.";
printf("%s\n",sErr.c_str());
throw runtime_error(sErr);
sendTextMessage("Server and client binary mismatch [" + networkMessageIntro.getVersionString() + "]",-1);
DisplayErrorMessage(sErr);
return;
}
}
@ -319,7 +322,12 @@ void ClientInterface::updateLobby()
break;
default:
throw runtime_error(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType));
{
string sErr = string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType);
//throw runtime_error(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType));
sendTextMessage("Unexpected network message: " + intToStr(networkMessageType),-1);
DisplayErrorMessage(sErr);
}
}
}
@ -349,7 +357,11 @@ void ClientInterface::updateKeyframe(int frameCount)
//check that we are in the right frame
if(networkMessageCommandList.getFrameCount()!=frameCount)
{
throw runtime_error("Network synchronization error, frame counts do not match");
string sErr = "Network synchronization error, frame counts do not match";
//throw runtime_error("Network synchronization error, frame counts do not match");
sendTextMessage(sErr,-1);
DisplayErrorMessage(sErr);
return;
}
// give all commands
@ -388,7 +400,12 @@ void ClientInterface::updateKeyframe(int frameCount)
break;
default:
throw runtime_error(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected message in client interface: " + intToStr(networkMessageType));
{
//throw runtime_error(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected message in client interface: " + intToStr(networkMessageType));
sendTextMessage("Unexpected message in client interface: " + intToStr(networkMessageType),-1);
DisplayErrorMessage(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected message in client interface: " + intToStr(networkMessageType));
}
}
}
}
@ -426,7 +443,11 @@ void ClientInterface::waitUntilReady(Checksum* checksum)
{
if(chrono.getMillis() > readyWaitTimeout)
{
throw runtime_error("Timeout waiting for server");
//throw runtime_error("Timeout waiting for server");
string sErr = "Timeout waiting for server";
sendTextMessage(sErr,-1);
DisplayErrorMessage(sErr);
return;
}
else
{
@ -442,7 +463,10 @@ void ClientInterface::waitUntilReady(Checksum* checksum)
}
else
{
throw runtime_error(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType) );
//throw runtime_error(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType) );
sendTextMessage("Unexpected network message: " + intToStr(networkMessageType),-1);
DisplayErrorMessage(string(__FILE__) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType));
return;
}
// sleep a bit
@ -456,8 +480,9 @@ void ClientInterface::waitUntilReady(Checksum* checksum)
{
string sErr = "Checksum error, you don't have the same data as the server";
//throw runtime_error("Checksum error, you don't have the same data as the server");
printf("%s\n",sErr.c_str());
throw runtime_error(sErr);
sendTextMessage(sErr,-1);
DisplayErrorMessage(sErr);
return;
}
}
@ -485,12 +510,18 @@ void ClientInterface::waitForMessage()
{
if(!isConnected())
{
throw runtime_error("Disconnected");
//throw runtime_error("Disconnected");
sendTextMessage("Disconnected",-1);
DisplayErrorMessage("Disconnected");
return;
}
if(chrono.getMillis()>messageWaitTimeout)
{
throw runtime_error("Timeout waiting for message");
//throw runtime_error("Timeout waiting for message");
sendTextMessage("Timeout waiting for message",-1);
DisplayErrorMessage("Timeout waiting for message");
return;
}
sleep(waitSleepTime);

View File

@ -0,0 +1,330 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2001-2008 Martiño Figueroa
//
// You can redistribute this code and/or modify it under
// the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version
// ==============================================================
#include "connection_slot.h"
#include <stdexcept>
#include "conversion.h"
#include "game_util.h"
#include "config.h"
#include "server_interface.h"
#include "network_message.h"
#include "leak_dumper.h"
#include "platform_util.h"
#include "map.h"
using namespace std;
using namespace Shared::Util;
//using namespace Shared::Platform;
namespace Glest{ namespace Game{
// =====================================================
// class ClientConnection
// =====================================================
ConnectionSlot::ConnectionSlot(ServerInterface* serverInterface, int playerIndex)
{
this->serverInterface= serverInterface;
this->playerIndex= playerIndex;
socket= NULL;
ready= false;
networkGameDataSynchCheckOkMap = false;
networkGameDataSynchCheckOkTile = false;
networkGameDataSynchCheckOkTech = false;
networkGameDataSynchCheckOkFogOfWar = false;
chatText.clear();
chatSender.clear();
chatTeamIndex= -1;
}
ConnectionSlot::~ConnectionSlot()
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
close();
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
}
void ConnectionSlot::update()
{
update(true);
}
void ConnectionSlot::update(bool checkForNewClients)
{
if(socket == NULL)
{
if(networkGameDataSynchCheckOkMap) networkGameDataSynchCheckOkMap = false;
if(networkGameDataSynchCheckOkTile) networkGameDataSynchCheckOkTile = false;
if(networkGameDataSynchCheckOkTech) networkGameDataSynchCheckOkTech = false;
if(networkGameDataSynchCheckOkFogOfWar) networkGameDataSynchCheckOkFogOfWar = false;
// Is the listener socket ready to be read?
//if(serverInterface->getServerSocket()->isReadable() == true)
if(checkForNewClients == true)
{
socket = serverInterface->getServerSocket()->accept();
//send intro message when connected
if(socket != NULL)
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] accepted new client connection\n",__FILE__,__FUNCTION__);
chatText.clear();
chatSender.clear();
chatTeamIndex= -1;
NetworkMessageIntro networkMessageIntro(getNetworkVersionString(), socket->getHostName(), playerIndex);
sendMessage(&networkMessageIntro);
}
}
}
else
{
if(socket->isConnected())
{
chatText.clear();
chatSender.clear();
chatTeamIndex= -1;
NetworkMessageType networkMessageType= getNextMessageType();
//process incoming commands
switch(networkMessageType){
case nmtInvalid:
break;
case nmtText:
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] got nmtText\n",__FILE__,__FUNCTION__);
NetworkMessageText networkMessageText;
if(receiveMessage(&networkMessageText))
{
chatText = networkMessageText.getText();
chatSender = networkMessageText.getSender();
chatTeamIndex = networkMessageText.getTeamIndex();
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] chatText [%s] chatSender [%s] chatTeamIndex = %d\n",__FILE__,__FUNCTION__,chatText.c_str(),chatSender.c_str(),chatTeamIndex);
}
}
break;
//command list
case nmtCommandList: {
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] got nmtCommandList\n",__FILE__,__FUNCTION__);
NetworkMessageCommandList networkMessageCommandList;
if(receiveMessage(&networkMessageCommandList))
{
for(int i= 0; i<networkMessageCommandList.getCommandCount(); ++i)
{
serverInterface->requestCommand(networkMessageCommandList.getCommand(i));
}
}
}
break;
//process intro messages
case nmtIntro:
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] got nmtIntro\n",__FILE__,__FUNCTION__);
NetworkMessageIntro networkMessageIntro;
if(receiveMessage(&networkMessageIntro))
{
name= networkMessageIntro.getName();
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] got name [%s]\n",__FILE__,__FUNCTION__,name.c_str());
if(getAllowGameDataSynchCheck() == true && serverInterface->getGameSettings() != NULL)
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] sending NetworkMessageSynchNetworkGameData\n",__FILE__,__FUNCTION__);
NetworkMessageSynchNetworkGameData networkMessageSynchNetworkGameData(serverInterface->getGameSettings());
sendMessage(&networkMessageSynchNetworkGameData);
}
}
}
break;
//process datasynch messages
case nmtSynchNetworkGameDataStatus:
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] got nmtSynchNetworkGameDataStatus\n",__FILE__,__FUNCTION__);
NetworkMessageSynchNetworkGameDataStatus networkMessageSynchNetworkGameDataStatus;
if(receiveMessage(&networkMessageSynchNetworkGameDataStatus))
{
receivedNetworkGameStatus = true;
int32 tilesetCRC = getFolderTreeContentsCheckSumRecursively(string(GameConstants::folder_path_tilesets) + "/" + serverInterface->getGameSettings()->getTileset() + "/*", ".xml", NULL);
int32 techCRC = getFolderTreeContentsCheckSumRecursively(string(GameConstants::folder_path_techs) + "/" + serverInterface->getGameSettings()->getTech() + "/*", ".xml", NULL);
Checksum checksum;
string file = Map::getMapPath(serverInterface->getGameSettings()->getMap());
checksum.addFile(file);
int32 mapCRC = checksum.getSum();
networkGameDataSynchCheckOkMap = (networkMessageSynchNetworkGameDataStatus.getMapCRC() == mapCRC);
networkGameDataSynchCheckOkTile = (networkMessageSynchNetworkGameDataStatus.getTilesetCRC() == tilesetCRC);
networkGameDataSynchCheckOkTech = (networkMessageSynchNetworkGameDataStatus.getTechCRC() == techCRC);
networkGameDataSynchCheckOkFogOfWar = (networkMessageSynchNetworkGameDataStatus.getFogOfWar() == serverInterface->getFogOfWar() == true);
// For testing
//techCRC++;
if( networkGameDataSynchCheckOkMap == true &&
networkGameDataSynchCheckOkTile == true &&
networkGameDataSynchCheckOkTech == true &&
networkGameDataSynchCheckOkFogOfWar == true)
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] client data synch ok\n",__FILE__,__FUNCTION__);
}
else
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] mapCRC = %d, remote = %d\n",__FILE__,__FUNCTION__,mapCRC,networkMessageSynchNetworkGameDataStatus.getMapCRC());
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] tilesetCRC = %d, remote = %d\n",__FILE__,__FUNCTION__,tilesetCRC,networkMessageSynchNetworkGameDataStatus.getTilesetCRC());
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] techCRC = %d, remote = %d\n",__FILE__,__FUNCTION__,techCRC,networkMessageSynchNetworkGameDataStatus.getTechCRC());
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] serverInterface->getFogOfWar() = %d, remote = %d\n",__FILE__,__FUNCTION__,serverInterface->getFogOfWar(),networkMessageSynchNetworkGameDataStatus.getFogOfWar());
if(allowDownloadDataSynch == true)
{
// Now get all filenames with their CRC values and send to the client
vctFileList.clear();
if(networkGameDataSynchCheckOkTile == false)
{
if(tilesetCRC == 0)
{
vctFileList = getFolderTreeContentsCheckSumListRecursively(string(GameConstants::folder_path_tilesets) + "/" + serverInterface->getGameSettings()->getTileset() + "/*", "", &vctFileList);
}
else
{
vctFileList = getFolderTreeContentsCheckSumListRecursively(string(GameConstants::folder_path_tilesets) + "/" + serverInterface->getGameSettings()->getTileset() + "/*", ".xml", &vctFileList);
}
}
if(networkGameDataSynchCheckOkTech == false)
{
if(techCRC == 0)
{
vctFileList = getFolderTreeContentsCheckSumListRecursively(string(GameConstants::folder_path_techs) + "/" + serverInterface->getGameSettings()->getTech() + "/*", "", &vctFileList);
}
else
{
vctFileList = getFolderTreeContentsCheckSumListRecursively(string(GameConstants::folder_path_techs) + "/" + serverInterface->getGameSettings()->getTech() + "/*", ".xml", &vctFileList);
}
}
if(networkGameDataSynchCheckOkMap == false)
{
vctFileList.push_back(std::pair<string,int32>(Map::getMapPath(serverInterface->getGameSettings()->getMap()),mapCRC));
}
//for(int i = 0; i < vctFileList.size(); i++)
//{
NetworkMessageSynchNetworkGameDataFileCRCCheck networkMessageSynchNetworkGameDataFileCRCCheck(vctFileList.size(), 1, vctFileList[0].second, vctFileList[0].first);
sendMessage(&networkMessageSynchNetworkGameDataFileCRCCheck);
//}
}
}
}
}
break;
case nmtSynchNetworkGameDataFileCRCCheck:
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] got nmtSynchNetworkGameDataFileCRCCheck\n",__FILE__,__FUNCTION__);
NetworkMessageSynchNetworkGameDataFileCRCCheck networkMessageSynchNetworkGameDataFileCRCCheck;
if(receiveMessage(&networkMessageSynchNetworkGameDataFileCRCCheck))
{
int fileIndex = networkMessageSynchNetworkGameDataFileCRCCheck.getFileIndex();
NetworkMessageSynchNetworkGameDataFileCRCCheck networkMessageSynchNetworkGameDataFileCRCCheck(vctFileList.size(), fileIndex, vctFileList[fileIndex-1].second, vctFileList[fileIndex-1].first);
sendMessage(&networkMessageSynchNetworkGameDataFileCRCCheck);
}
}
break;
case nmtSynchNetworkGameDataFileGet:
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] got nmtSynchNetworkGameDataFileGet\n",__FILE__,__FUNCTION__);
NetworkMessageSynchNetworkGameDataFileGet networkMessageSynchNetworkGameDataFileGet;
if(receiveMessage(&networkMessageSynchNetworkGameDataFileGet))
{
FileTransferInfo fileInfo;
fileInfo.hostType = eServer;
//fileInfo.serverIP = this->ip.getString();
fileInfo.serverPort = GameConstants::serverPort;
fileInfo.fileName = networkMessageSynchNetworkGameDataFileGet.getFileName();
FileTransferSocketThread *fileXferThread = new FileTransferSocketThread(fileInfo);
fileXferThread->start();
}
}
break;
default:
{
//throw runtime_error("Unexpected message in connection slot: " + intToStr(networkMessageType));
string sErr = "Unexpected message in connection slot: " + intToStr(networkMessageType);
//sendTextMessage(sErr,-1);
DisplayErrorMessage(sErr);
return;
}
}
}
else
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] calling close...\n",__FILE__,__FUNCTION__);
close();
}
}
}
void ConnectionSlot::close()
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
delete socket;
socket= NULL;
chatText.clear();
chatSender.clear();
chatTeamIndex= -1;
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
}
bool ConnectionSlot::getFogOfWar()
{
return networkGameDataSynchCheckOkFogOfWar;
}
bool ConnectionSlot::hasValidSocketId()
{
bool result = (socket != NULL && socket->getSocketId() > 0);
return result;
}
}}//end namespace

View File

@ -0,0 +1,294 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2001-2008 Martiño Figueroa
//
// You can redistribute this code and/or modify it under
// the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version
// ==============================================================
#include "network_interface.h"
#include <exception>
#include <cassert>
#include "types.h"
#include "conversion.h"
#include "platform_util.h"
#include "leak_dumper.h"
#include <fstream>
using namespace Shared::Platform;
using namespace Shared::Util;
using namespace std;
namespace Glest{ namespace Game{
// =====================================================
// class NetworkInterface
// =====================================================
const int NetworkInterface::readyWaitTimeout= 60000; //1 minute
bool NetworkInterface::allowGameDataSynchCheck = false;
bool NetworkInterface::allowDownloadDataSynch = false;
DisplayMessageFunction NetworkInterface::pCB_DisplayMessage = NULL;
void NetworkInterface::sendMessage(const NetworkMessage* networkMessage){
Socket* socket= getSocket();
networkMessage->send(socket);
}
NetworkMessageType NetworkInterface::getNextMessageType(bool checkHasDataFirst)
{
Socket* socket= getSocket();
int8 messageType= nmtInvalid;
if(checkHasDataFirst == false ||
(checkHasDataFirst == true &&
socket != NULL &&
socket->hasDataToRead() == true))
{
//peek message type
int dataSize = socket->getDataToRead();
if(dataSize >= sizeof(messageType)){
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] socket->getDataToRead() dataSize = %d\n",__FILE__,__FUNCTION__,dataSize);
int iPeek = socket->peek(&messageType, sizeof(messageType));
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] socket->getDataToRead() iPeek = %d, messageType = %d\n",__FILE__,__FUNCTION__,iPeek,messageType);
}
//sanity check new message type
if(messageType<0 || messageType>=nmtCount){
throw runtime_error("Invalid message type: " + intToStr(messageType));
}
}
return static_cast<NetworkMessageType>(messageType);
}
bool NetworkInterface::receiveMessage(NetworkMessage* networkMessage){
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s]\n",__FILE__,__FUNCTION__);
Socket* socket= getSocket();
return networkMessage->receive(socket);
}
bool NetworkInterface::isConnected(){
//if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
bool result = (getSocket()!=NULL && getSocket()->isConnected());
//if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
return result;
}
void NetworkInterface::DisplayErrorMessage(string sErr, bool closeSocket) {
if(pCB_DisplayMessage != NULL) {
pCB_DisplayMessage(sErr.c_str(), false);
}
else {
throw runtime_error(sErr);
}
if(closeSocket == true && getSocket() != NULL)
{
close();
}
}
// =====================================================
// class GameNetworkInterface
// =====================================================
GameNetworkInterface::GameNetworkInterface(){
quit= false;
}
// =====================================================
// class FileTransferSocketThread
// =====================================================
const int32 SEND_FILE = 0x20;
const int32 ACK = 0x47;
FileTransferSocketThread::FileTransferSocketThread(FileTransferInfo fileInfo)
{
this->info = fileInfo;
this->info.serverPort += 100;
}
void FileTransferSocketThread::execute()
{
if(info.hostType == eServer)
{
ServerSocket serverSocket;
//serverSocket.setBlock(false);
serverSocket.bind(this->info.serverPort);
serverSocket.listen(1);
Socket *clientSocket = serverSocket.accept();
char data[513]="";
memset(data, 0, 256);
clientSocket->receive(data,256);
if(*data == SEND_FILE)
{
FileInfo file;
memcpy(&file, data+1, sizeof(file));
*data=ACK;
clientSocket->send(data,256);
Checksum checksum;
checksum.addFile(file.fileName);
file.filecrc = checksum.getSum();
ifstream infile(file.fileName.c_str(), ios::in | ios::binary | ios::ate);
if(infile.is_open() == true)
{
file.filesize = infile.tellg();
infile.seekg (0, ios::beg);
memset(data, 0, 256);
*data=SEND_FILE;
memcpy(data+1,&file,sizeof(file));
clientSocket->send(data,256);
clientSocket->receive(data,256);
if(*data != ACK)
;//transfer error
int remain=file.filesize % 512 ;
int packs=(file.filesize-remain)/512;
while(packs--)
{
infile.read(data,512);
//if(!ReadFile(file,data,512,&read,NULL))
// ; //read error
//if(written!=pack)
// ; //read error
clientSocket->send(data,512);
clientSocket->receive(data,256);
if(*data!=ACK)
;//transfer error
}
infile.read(data,remain);
//if(!ReadFile(file,data,remain,&read,NULL))
// ; //read error
//if(written!=pack)
// ; //read error
clientSocket->send(data,remain);
clientSocket->receive(data,256);
if(*data!=ACK)
;//transfer error
infile.close();
}
}
delete clientSocket;
}
else
{
Ip ip(this->info.serverIP);
ClientSocket clientSocket;
//clientSocket.setBlock(false);
clientSocket.connect(this->info.serverIP, this->info.serverPort);
if(clientSocket.isConnected() == true)
{
FileInfo file;
file.fileName = this->info.fileName;
//file.filesize =
//file.filecrc = this->info.
string path = extractDirectoryPathFromFile(file.fileName);
createDirectoryPaths(path);
ofstream outFile(file.fileName.c_str(), ios_base::binary | ios_base::out);
if(outFile.is_open() == true)
{
char data[513]="";
memset(data, 0, 256);
*data=SEND_FILE;
memcpy(data+1,&file,sizeof(file));
clientSocket.send(data,256);
clientSocket.receive(data,256);
if(*data!=ACK)
;//transfer error
clientSocket.receive(data,256);
if(*data == SEND_FILE)
{
memcpy(&file, data+1, sizeof(file));
*data=ACK;
clientSocket.send(data,256);
int remain = file.filesize % 512 ;
int packs = (file.filesize-remain) / 512;
while(packs--)
{
clientSocket.receive(data,512);
outFile.write(data, 512);
if(outFile.bad())
{
int ii = 0;
}
//if(!WriteFile(file,data,512,&written,NULL))
// ; //write error
//if(written != pack)
// ; //write error
*data=ACK;
clientSocket.send(data,256);
}
clientSocket.receive(data,remain);
outFile.write(data, remain);
if(outFile.bad())
{
int ii = 0;
}
//if(!WriteFile(file,data,remain,&written,NULL))
// ; //write error
//if(written!=pack)
// ; //write error
*data=ACK;
clientSocket.send(data,256);
Checksum checksum;
checksum.addFile(file.fileName);
int32 crc = checksum.getSum();
if(file.filecrc != crc)
{
int ii = 0;
}
//if(calc_crc(file)!=info.crc)
// ; //transfeer error
}
outFile.close();
}
}
}
}
}}//end namespace

View File

@ -38,6 +38,8 @@ namespace Glest{ namespace Game{
// class NetworkInterface
// =====================================================
typedef int (*DisplayMessageFunction)(const char *msg, bool exit);
class NetworkInterface {
protected:
@ -51,6 +53,8 @@ protected:
string chatText;
string chatSender;
int chatTeamIndex;
static DisplayMessageFunction pCB_DisplayMessage;
void DisplayErrorMessage(string sErr, bool closeSocket=true);
public:
static const int readyWaitTimeout;
@ -63,6 +67,7 @@ public:
virtual const Socket* getSocket() const= 0;
virtual void close()= 0;
static void setDisplayMessageFunction(DisplayMessageFunction pDisplayMessage) { pCB_DisplayMessage = pDisplayMessage; }
string getIp() const {return getSocket()->getIp();}
string getHostName() const {return getSocket()->getHostName();}

View File

@ -0,0 +1,409 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2001-2008 Martiño Figueroa
//
// You can redistribute this code and/or modify it under
// the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version
// ==============================================================
#include "network_message.h"
#include <cassert>
#include <stdexcept>
#include "types.h"
#include "util.h"
#include "game_settings.h"
#include "leak_dumper.h"
#include "checksum.h"
#include "map.h"
#include "platform_util.h"
#include "config.h"
using namespace Shared::Platform;
using namespace Shared::Util;
using namespace std;
namespace Glest{ namespace Game{
// =====================================================
// class NetworkMessage
// =====================================================
bool NetworkMessage::receive(Socket* socket, void* data, int dataSize)
{
int ipeekdatalen = socket->getDataToRead();
if(ipeekdatalen >= dataSize)
{
if(socket->receive(data, dataSize)!=dataSize)
{
if(socket != NULL && socket->getSocketId() > 0)
{
throw runtime_error("Error receiving NetworkMessage");
}
else
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] socket has been disconnected\n",__FILE__,__FUNCTION__);
}
}
else
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] dataSize = %d\n",__FILE__,__FUNCTION__,dataSize);
}
return true;
}
else
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] socket->getDataToRead() returned %d\n",__FILE__,__FUNCTION__,ipeekdatalen);
}
return false;
}
void NetworkMessage::send(Socket* socket, const void* data, int dataSize) const
{
if(socket->send(data, dataSize)!=dataSize)
{
if(socket != NULL && socket->getSocketId() > 0)
{
throw runtime_error("Error sending NetworkMessage");
}
else
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] socket has been disconnected\n",__FILE__,__FUNCTION__);
}
}
}
// =====================================================
// class NetworkMessageIntro
// =====================================================
NetworkMessageIntro::NetworkMessageIntro(){
data.messageType= -1;
data.playerIndex= -1;
}
NetworkMessageIntro::NetworkMessageIntro(const string &versionString, const string &name, int playerIndex){
data.messageType=nmtIntro;
data.versionString= versionString;
data.name= name;
data.playerIndex= static_cast<int16>(playerIndex);
}
bool NetworkMessageIntro::receive(Socket* socket){
return NetworkMessage::receive(socket, &data, sizeof(data));
}
void NetworkMessageIntro::send(Socket* socket) const{
assert(data.messageType==nmtIntro);
NetworkMessage::send(socket, &data, sizeof(data));
}
// =====================================================
// class NetworkMessageReady
// =====================================================
NetworkMessageReady::NetworkMessageReady(){
data.messageType= nmtReady;
}
NetworkMessageReady::NetworkMessageReady(int32 checksum){
data.messageType= nmtReady;
data.checksum= checksum;
}
bool NetworkMessageReady::receive(Socket* socket){
return NetworkMessage::receive(socket, &data, sizeof(data));
}
void NetworkMessageReady::send(Socket* socket) const{
assert(data.messageType==nmtReady);
NetworkMessage::send(socket, &data, sizeof(data));
}
// =====================================================
// class NetworkMessageLaunch
// =====================================================
NetworkMessageLaunch::NetworkMessageLaunch(){
data.messageType=-1;
}
NetworkMessageLaunch::NetworkMessageLaunch(const GameSettings *gameSettings){
data.messageType=nmtLaunch;
data.description= gameSettings->getDescription();
data.map= gameSettings->getMap();
data.tileset= gameSettings->getTileset();
data.tech= gameSettings->getTech();
data.factionCount= gameSettings->getFactionCount();
data.thisFactionIndex= gameSettings->getThisFactionIndex();
data.defaultResources= gameSettings->getDefaultResources();
data.defaultUnits= gameSettings->getDefaultUnits();
data.defaultVictoryConditions= gameSettings->getDefaultVictoryConditions();
for(int i= 0; i<data.factionCount; ++i){
data.factionTypeNames[i]= gameSettings->getFactionTypeName(i);
data.factionControls[i]= gameSettings->getFactionControl(i);
data.teams[i]= gameSettings->getTeam(i);
data.startLocationIndex[i]= gameSettings->getStartLocationIndex(i);
}
}
void NetworkMessageLaunch::buildGameSettings(GameSettings *gameSettings) const{
gameSettings->setDescription(data.description.getString());
gameSettings->setMap(data.map.getString());
gameSettings->setTileset(data.tileset.getString());
gameSettings->setTech(data.tech.getString());
gameSettings->setFactionCount(data.factionCount);
gameSettings->setThisFactionIndex(data.thisFactionIndex);
gameSettings->setDefaultResources(data.defaultResources);
gameSettings->setDefaultUnits(data.defaultUnits);
gameSettings->setDefaultVictoryConditions(data.defaultVictoryConditions);
for(int i= 0; i<data.factionCount; ++i){
gameSettings->setFactionTypeName(i, data.factionTypeNames[i].getString());
gameSettings->setFactionControl(i, static_cast<ControlType>(data.factionControls[i]));
gameSettings->setTeam(i, data.teams[i]);
gameSettings->setStartLocationIndex(i, data.startLocationIndex[i]);
}
}
bool NetworkMessageLaunch::receive(Socket* socket){
return NetworkMessage::receive(socket, &data, sizeof(data));
}
void NetworkMessageLaunch::send(Socket* socket) const{
assert(data.messageType==nmtLaunch);
NetworkMessage::send(socket, &data, sizeof(data));
}
// =====================================================
// class NetworkMessageLaunch
// =====================================================
NetworkMessageCommandList::NetworkMessageCommandList(int32 frameCount){
data.messageType= nmtCommandList;
data.frameCount= frameCount;
data.commandCount= 0;
}
bool NetworkMessageCommandList::addCommand(const NetworkCommand* networkCommand){
if(data.commandCount<maxCommandCount){
data.commands[static_cast<int>(data.commandCount)]= *networkCommand;
data.commandCount++;
return true;
}
return false;
}
bool NetworkMessageCommandList::receive(Socket* socket){
//return NetworkMessage::receive(socket, &data, sizeof(data));
// read type, commandCount & frame num first.
if (!NetworkMessage::receive(socket, &data, networkPacketMsgTypeSize)) {
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s %d] NetworkMessage::receive failed!\n",__FILE__,__FUNCTION__,__LINE__);
return false;
}
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s %d] messageType = %d, frameCount = %d, data.commandCount = %d\n",
__FILE__,__FUNCTION__,__LINE__,data.messageType,data.frameCount,data.commandCount);
// read data.commandCount commands.
if (data.commandCount) {
bool result = NetworkMessage::receive(socket, &data.commands, sizeof(NetworkCommand) * data.commandCount);
if(Socket::enableNetworkDebugInfo) {
for(int idx = 0 ; idx < data.commandCount; ++idx) {
const NetworkCommand &cmd = data.commands[idx];
printf("In [%s::%s %d] index = %d, networkCommandType = %d, unitId = %d, commandTypeId = %d, positionX = %d, positionY = %d, unitTypeId = %d, targetId = %d\n",
__FILE__,__FUNCTION__,__LINE__,idx, cmd.getNetworkCommandType(),cmd.getUnitId(), cmd.getCommandTypeId(),
cmd.getPosition().x,cmd.getPosition().y, cmd.getUnitTypeId(), cmd.getTargetId());
}
}
return result;
}
return true;
}
void NetworkMessageCommandList::send(Socket* socket) const{
assert(data.messageType==nmtCommandList);
//NetworkMessage::send(socket, &data, sizeof(data));
NetworkMessage::send(socket, &data, networkPacketMsgTypeSize + sizeof(NetworkCommand) * data.commandCount);
if(Socket::enableNetworkDebugInfo) {
printf("In [%s::%s %d] messageType = %d, frameCount = %d, data.commandCount = %d\n",
__FILE__,__FUNCTION__,__LINE__,data.messageType,data.frameCount,data.commandCount);
if (data.commandCount) {
for(int idx = 0 ; idx < data.commandCount; ++idx) {
const NetworkCommand &cmd = data.commands[idx];
printf("In [%s::%s %d] index = %d, networkCommandType = %d, unitId = %d, commandTypeId = %d, positionX = %d, positionY = %d, unitTypeId = %d, targetId = %d\n",
__FILE__,__FUNCTION__,__LINE__,idx, cmd.getNetworkCommandType(),cmd.getUnitId(), cmd.getCommandTypeId(),
cmd.getPosition().x,cmd.getPosition().y, cmd.getUnitTypeId(), cmd.getTargetId());
}
}
}
}
// =====================================================
// class NetworkMessageText
// =====================================================
NetworkMessageText::NetworkMessageText(const string &text, const string &sender, int teamIndex){
data.messageType= nmtText;
data.text= text;
data.sender= sender;
data.teamIndex= teamIndex;
}
bool NetworkMessageText::receive(Socket* socket){
return NetworkMessage::receive(socket, &data, sizeof(data));
}
void NetworkMessageText::send(Socket* socket) const{
assert(data.messageType==nmtText);
NetworkMessage::send(socket, &data, sizeof(data));
}
// =====================================================
// class NetworkMessageQuit
// =====================================================
NetworkMessageQuit::NetworkMessageQuit(){
data.messageType= nmtQuit;
}
bool NetworkMessageQuit::receive(Socket* socket){
return NetworkMessage::receive(socket, &data, sizeof(data));
}
void NetworkMessageQuit::send(Socket* socket) const{
assert(data.messageType==nmtQuit);
NetworkMessage::send(socket, &data, sizeof(data));
}
// =====================================================
// class NetworkMessageSynchNetworkGameData
// =====================================================
NetworkMessageSynchNetworkGameData::NetworkMessageSynchNetworkGameData(const GameSettings *gameSettings)
{
data.messageType= nmtSynchNetworkGameData;
data.map = gameSettings->getMap();
data.tileset = gameSettings->getTileset();
data.tech = gameSettings->getTech();
//Checksum checksum;
data.tilesetCRC = getFolderTreeContentsCheckSumRecursively(string(GameConstants::folder_path_tilesets) + "/" + gameSettings->getTileset() + "/*", "xml", NULL);
//tech, load before map because of resources
data.techCRC = getFolderTreeContentsCheckSumRecursively(string(GameConstants::folder_path_techs) + "/" + gameSettings->getTech() + "/*", "xml", NULL);
//map
Checksum checksum;
string file = Map::getMapPath(gameSettings->getMap());
checksum.addFile(file);
data.mapCRC = checksum.getSum();
//if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] file = [%s] checksum = %d\n",__FILE__,__FUNCTION__,file.c_str(),data.mapCRC);
data.hasFogOfWar = Config::getInstance().getBool("FogOfWar");;
}
bool NetworkMessageSynchNetworkGameData::receive(Socket* socket)
{
return NetworkMessage::receive(socket, &data, sizeof(data));
}
void NetworkMessageSynchNetworkGameData::send(Socket* socket) const
{
assert(data.messageType==nmtSynchNetworkGameData);
NetworkMessage::send(socket, &data, sizeof(data));
}
// =====================================================
// class NetworkMessageSynchNetworkGameDataStatus
// =====================================================
NetworkMessageSynchNetworkGameDataStatus::NetworkMessageSynchNetworkGameDataStatus(int32 mapCRC, int32 tilesetCRC, int32 techCRC, int8 hasFogOfWar)
{
data.messageType= nmtSynchNetworkGameDataStatus;
data.tilesetCRC = tilesetCRC;
data.techCRC = techCRC;
data.mapCRC = mapCRC;
data.hasFogOfWar = hasFogOfWar;
}
bool NetworkMessageSynchNetworkGameDataStatus::receive(Socket* socket)
{
return NetworkMessage::receive(socket, &data, sizeof(data));
}
void NetworkMessageSynchNetworkGameDataStatus::send(Socket* socket) const
{
assert(data.messageType==nmtSynchNetworkGameDataStatus);
NetworkMessage::send(socket, &data, sizeof(data));
}
// =====================================================
// class NetworkMessageSynchNetworkGameDataFileCRCCheck
// =====================================================
NetworkMessageSynchNetworkGameDataFileCRCCheck::NetworkMessageSynchNetworkGameDataFileCRCCheck(int32 totalFileCount, int32 fileIndex, int32 fileCRC, const string fileName)
{
data.messageType= nmtSynchNetworkGameDataFileCRCCheck;
data.totalFileCount = totalFileCount;
data.fileIndex = fileIndex;
data.fileCRC = fileCRC;
data.fileName = fileName;
}
bool NetworkMessageSynchNetworkGameDataFileCRCCheck::receive(Socket* socket)
{
return NetworkMessage::receive(socket, &data, sizeof(data));
}
void NetworkMessageSynchNetworkGameDataFileCRCCheck::send(Socket* socket) const
{
assert(data.messageType==nmtSynchNetworkGameDataFileCRCCheck);
NetworkMessage::send(socket, &data, sizeof(data));
}
// =====================================================
// class NetworkMessageSynchNetworkGameDataFileGet
// =====================================================
NetworkMessageSynchNetworkGameDataFileGet::NetworkMessageSynchNetworkGameDataFileGet(const string fileName)
{
data.messageType= nmtSynchNetworkGameDataFileGet;
data.fileName = fileName;
}
bool NetworkMessageSynchNetworkGameDataFileGet::receive(Socket* socket)
{
return NetworkMessage::receive(socket, &data, sizeof(data));
}
void NetworkMessageSynchNetworkGameDataFileGet::send(Socket* socket) const
{
assert(data.messageType==nmtSynchNetworkGameDataFileGet);
NetworkMessage::send(socket, &data, sizeof(data));
}
}}//end namespace

View File

@ -0,0 +1,578 @@
// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2001-2008 Martiño Figueroa
//
// You can redistribute this code and/or modify it under
// the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version
// ==============================================================
#include "server_interface.h"
#include <cassert>
#include <stdexcept>
#include "platform_util.h"
#include "conversion.h"
#include "config.h"
#include "lang.h"
#include "leak_dumper.h"
#include "logger.h"
#include <time.h>
using namespace std;
using namespace Shared::Platform;
using namespace Shared::Util;
namespace Glest{ namespace Game{
// =====================================================
// class ServerInterface
// =====================================================
ServerInterface::ServerInterface(){
gameHasBeenInitiated = false;
gameSettingsUpdateCount = 0;
for(int i= 0; i<GameConstants::maxPlayers; ++i){
slots[i]= NULL;
}
serverSocket.setBlock(false);
serverSocket.bind(GameConstants::serverPort);
}
ServerInterface::~ServerInterface(){
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
for(int i= 0; i<GameConstants::maxPlayers; ++i){
delete slots[i];
}
close();
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
}
void ServerInterface::addSlot(int playerIndex){
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
assert(playerIndex>=0 && playerIndex<GameConstants::maxPlayers);
delete slots[playerIndex];
slots[playerIndex]= new ConnectionSlot(this, playerIndex);
updateListen();
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
}
void ServerInterface::removeSlot(int playerIndex){
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
delete slots[playerIndex];
slots[playerIndex]= NULL;
updateListen();
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
}
ConnectionSlot* ServerInterface::getSlot(int playerIndex){
return slots[playerIndex];
}
int ServerInterface::getConnectedSlotCount(){
int connectedSlotCount= 0;
for(int i= 0; i<GameConstants::maxPlayers; ++i){
if(slots[i]!= NULL){
++connectedSlotCount;
}
}
return connectedSlotCount;
}
void ServerInterface::update()
{
std::map<int,bool> socketTriggeredList;
//update all slots
for(int i= 0; i < GameConstants::maxPlayers; ++i)
{
ConnectionSlot* connectionSlot= slots[i];
if(connectionSlot != NULL && connectionSlot->getSocket() != NULL &&
slots[i]->getSocket()->getSocketId() > 0)
{
socketTriggeredList[connectionSlot->getSocket()->getSocketId()] = false;
}
}
chatText.clear();
chatSender.clear();
chatTeamIndex= -1;
if(gameHasBeenInitiated == false || socketTriggeredList.size() > 0)
{
if(gameHasBeenInitiated && Socket::enableNetworkDebugInfo) printf("In [%s::%s] socketTriggeredList.size() = %d\n",__FILE__,__FUNCTION__,socketTriggeredList.size());
bool hasData = Socket::hasDataToRead(socketTriggeredList);
if(hasData && Socket::enableNetworkDebugInfo) printf("In [%s::%s] hasData == true\n",__FILE__,__FUNCTION__);
if(gameHasBeenInitiated == false || hasData == true)
{
//if(gameHasBeenInitiated && Socket::enableNetworkDebugInfo) printf("In [%s::%s] hasData == true\n",__FILE__,__FUNCTION__);
//std::vector<TeamMessageData> vctTeamMessages;
//update all slots
bool checkForNewClients = true;
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
ConnectionSlot* connectionSlot= slots[i];
if(connectionSlot != NULL &&
(gameHasBeenInitiated == false || (connectionSlot->getSocket() != NULL && socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true)))
{
if(connectionSlot->isConnected() == false ||
(socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true))
{
if(gameHasBeenInitiated && Socket::enableNetworkDebugInfo) printf("In [%s::%s] socketTriggeredList[i] = %i\n",__FILE__,__FUNCTION__,(socketTriggeredList[connectionSlot->getSocket()->getSocketId()] ? 1 : 0));
if(connectionSlot->isConnected())
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] calling slots[i]->update() for slot = %d socketId = %d\n",
__FILE__,__FUNCTION__,i,connectionSlot->getSocket()->getSocketId());
}
else
{
if(gameHasBeenInitiated && Socket::enableNetworkDebugInfo) printf("In [%s::%s] slot = %d getSocket() == NULL\n",__FILE__,__FUNCTION__,i);
}
connectionSlot->update(checkForNewClients);
// This means no clients are trying to connect at the moment
if(connectionSlot != NULL && connectionSlot->getSocket() == NULL)
{
checkForNewClients = false;
}
if(connectionSlot != NULL &&
//connectionSlot->isConnected() == true &&
connectionSlot->getChatText().empty() == false)
{
chatText = connectionSlot->getChatText();
chatSender = connectionSlot->getChatSender();
chatTeamIndex = connectionSlot->getChatTeamIndex();
//TeamMessageData teamMessageData;
//teamMessageData.chatSender = connectionSlot->getChatSender();
//teamMessageData.chatText = connectionSlot->getChatText();
//teamMessageData.chatTeamIndex = connectionSlot->getChatTeamIndex();
//teamMessageData.sourceTeamIndex = i;
//vctTeamMessages.push_back(teamMessageData);
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] #1 about to broadcast nmtText chatText [%s] chatSender [%s] chatTeamIndex = %d for SlotIndex# %d\n",__FILE__,__FUNCTION__,chatText.c_str(),chatSender.c_str(),chatTeamIndex,i);
NetworkMessageText networkMessageText(chatText,chatSender,chatTeamIndex);
broadcastMessage(&networkMessageText, i);
break;
}
}
}
}
//process text messages
if(chatText.empty() == true)
{
chatText.clear();
chatSender.clear();
chatTeamIndex= -1;
for(int i= 0; i< GameConstants::maxPlayers; ++i)
{
ConnectionSlot* connectionSlot= slots[i];
if(connectionSlot!= NULL &&
(gameHasBeenInitiated == false || (connectionSlot->getSocket() != NULL && socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true)))
{
if(connectionSlot->isConnected() && socketTriggeredList[connectionSlot->getSocket()->getSocketId()] == true)
{
if(connectionSlot->getSocket() != NULL && Socket::enableNetworkDebugInfo) printf("In [%s::%s] calling connectionSlot->getNextMessageType() for slots[i]->getSocket()->getSocketId() = %d\n",
__FILE__,__FUNCTION__,connectionSlot->getSocket()->getSocketId());
if(connectionSlot->getNextMessageType() == nmtText)
{
NetworkMessageText networkMessageText;
if(connectionSlot->receiveMessage(&networkMessageText))
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] #2 about to broadcast nmtText msg for SlotIndex# %d\n",__FILE__,__FUNCTION__,i);
broadcastMessage(&networkMessageText, i);
chatText= networkMessageText.getText();
chatSender= networkMessageText.getSender();
chatTeamIndex= networkMessageText.getTeamIndex();
break;
}
}
}
}
}
}
}
}
}
void ServerInterface::updateKeyframe(int frameCount){
NetworkMessageCommandList networkMessageCommandList(frameCount);
//build command list, remove commands from requested and add to pending
while(!requestedCommands.empty()){
if(networkMessageCommandList.addCommand(&requestedCommands.back())){
pendingCommands.push_back(requestedCommands.back());
requestedCommands.pop_back();
}
else{
break;
}
}
//broadcast commands
broadcastMessage(&networkMessageCommandList);
}
void ServerInterface::waitUntilReady(Checksum* checksum){
if(Socket::enableNetworkDebugInfo) printf("In [%s] START\n",__FUNCTION__);
Logger &logger= Logger::getInstance();
gameHasBeenInitiated = true;
Chrono chrono;
bool allReady= false;
chrono.start();
//wait until we get a ready message from all clients
while(allReady == false)
{
vector<string> waitingForHosts;
allReady= true;
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
ConnectionSlot* connectionSlot= slots[i];
if(connectionSlot != NULL && connectionSlot->isConnected() == true)
{
if(connectionSlot->isReady() == false)
{
NetworkMessageType networkMessageType= connectionSlot->getNextMessageType(true);
NetworkMessageReady networkMessageReady;
if(networkMessageType == nmtReady &&
connectionSlot->receiveMessage(&networkMessageReady))
{
if(Socket::enableNetworkDebugInfo) printf("In [%s] networkMessageType==nmtReady\n",__FUNCTION__);
connectionSlot->setReady();
}
else if(networkMessageType != nmtInvalid)
{
//throw runtime_error("Unexpected network message: " + intToStr(networkMessageType));
string sErr = "Unexpected network message: " + intToStr(networkMessageType);
sendTextMessage(sErr,-1);
DisplayErrorMessage(sErr);
return;
}
waitingForHosts.push_back(connectionSlot->getHostName());
allReady= false;
}
}
}
//check for timeout
if(allReady == false)
{
if(chrono.getMillis() > readyWaitTimeout)
{
//throw runtime_error("Timeout waiting for clients");
string sErr = "Timeout waiting for clients.";
sendTextMessage(sErr,-1);
DisplayErrorMessage(sErr);
return;
}
else
{
if(chrono.getMillis() % 1000 == 0)
{
string waitForHosts = "";
for(int i = 0; i < waitingForHosts.size(); i++)
{
if(waitForHosts != "")
{
waitForHosts += ", ";
}
waitForHosts += waitingForHosts[i];
}
char szBuf[1024]="";
sprintf(szBuf,"Waiting for network: %d of %d max seconds (waiting for: %s)",int(chrono.getMillis() / 1000),int(readyWaitTimeout / 1000),waitForHosts.c_str());
logger.add(szBuf, true);
}
}
}
}
// FOR TESTING ONLY - delay to see the client count up while waiting
//sleep(5000);
if(Socket::enableNetworkDebugInfo) printf("In [%s] PART B (telling client we are ready!\n",__FUNCTION__);
//send ready message after, so clients start delayed
for(int i= 0; i < GameConstants::maxPlayers; ++i)
{
NetworkMessageReady networkMessageReady(checksum->getSum());
ConnectionSlot* connectionSlot= slots[i];
if(connectionSlot!=NULL)
{
connectionSlot->sendMessage(&networkMessageReady);
}
}
if(Socket::enableNetworkDebugInfo) printf("In [%s] END\n",__FUNCTION__);
}
void ServerInterface::sendTextMessage(const string &text, int teamIndex){
NetworkMessageText networkMessageText(text, getHostName(), teamIndex);
broadcastMessage(&networkMessageText);
}
void ServerInterface::quitGame(bool userManuallyQuit)
{
if(userManuallyQuit == true)
{
string sQuitText = getHostName() + " has chosen to leave the game!";
NetworkMessageText networkMessageText(sQuitText,getHostName(),-1);
broadcastMessage(&networkMessageText, -1);
}
NetworkMessageQuit networkMessageQuit;
broadcastMessage(&networkMessageQuit);
}
string ServerInterface::getNetworkStatus() const{
Lang &lang= Lang::getInstance();
string str;
for(int i= 0; i<GameConstants::maxPlayers; ++i){
ConnectionSlot* connectionSlot= slots[i];
str+= intToStr(i)+ ": ";
if(connectionSlot!= NULL){
if(connectionSlot->isConnected()){
str+= connectionSlot->getName();
}
}
else
{
str+= lang.get("NotConnected");
}
str+= '\n';
}
return str;
}
bool ServerInterface::launchGame(const GameSettings* gameSettings){
bool bOkToStart = true;
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
ConnectionSlot *connectionSlot= slots[i];
if(connectionSlot != NULL &&
connectionSlot->getAllowDownloadDataSynch() == true &&
connectionSlot->isConnected())
{
if(connectionSlot->getNetworkGameDataSynchCheckOk() == false)
{
bOkToStart = false;
break;
}
}
}
if(bOkToStart == true)
{
NetworkMessageLaunch networkMessageLaunch(gameSettings);
broadcastMessage(&networkMessageLaunch);
}
return bOkToStart;
}
void ServerInterface::broadcastMessage(const NetworkMessage* networkMessage, int excludeSlot){
//if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
ConnectionSlot* connectionSlot= slots[i];
if(i != excludeSlot && connectionSlot != NULL)
{
if(connectionSlot->isConnected())
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] before sendMessage\n",__FILE__,__FUNCTION__);
connectionSlot->sendMessage(networkMessage);
}
else if(gameHasBeenInitiated == true)
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] #1 before removeSlot for slot# %d\n",__FILE__,__FUNCTION__,i);
removeSlot(i);
}
}
else if(i == excludeSlot && gameHasBeenInitiated == true &&
connectionSlot != NULL && connectionSlot->isConnected() == false)
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] #2 before removeSlot for slot# %d\n",__FILE__,__FUNCTION__,i);
removeSlot(i);
}
}
//if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
}
void ServerInterface::broadcastMessageToConnectedClients(const NetworkMessage* networkMessage, int excludeSlot){
//if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
for(int i= 0; i<GameConstants::maxPlayers; ++i){
ConnectionSlot* connectionSlot= slots[i];
if(i!= excludeSlot && connectionSlot!= NULL){
if(connectionSlot->isConnected()){
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] before sendMessage\n",__FILE__,__FUNCTION__);
connectionSlot->sendMessage(networkMessage);
}
}
}
//if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
}
void ServerInterface::updateListen()
{
int openSlotCount= 0;
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
if(slots[i] != NULL && slots[i]->isConnected() == false)
{
++openSlotCount;
}
}
serverSocket.listen(openSlotCount);
}
void ServerInterface::setGameSettings(GameSettings *serverGameSettings, bool waitForClientAck)
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] START gameSettingsUpdateCount = %d\n",__FILE__,__FUNCTION__,gameSettingsUpdateCount);
if(getAllowGameDataSynchCheck() == true)
{
if(waitForClientAck == true && gameSettingsUpdateCount > 0)
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] Waiting for client acks #1\n",__FILE__,__FUNCTION__);
time_t tStart = time(NULL);
bool gotAckFromAllClients = false;
while(gotAckFromAllClients == false && difftime(time(NULL),tStart) <= 5)
{
gotAckFromAllClients = true;
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
ConnectionSlot *connectionSlot = slots[i];
if(connectionSlot != NULL && connectionSlot->isConnected())
{
if(connectionSlot->getReceivedNetworkGameStatus() == false)
{
gotAckFromAllClients = false;
}
connectionSlot->update();
}
}
}
}
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
ConnectionSlot *connectionSlot = slots[i];
if(connectionSlot != NULL && connectionSlot->isConnected())
{
connectionSlot->setReceivedNetworkGameStatus(false);
}
}
gameSettings = *serverGameSettings;
NetworkMessageSynchNetworkGameData networkMessageSynchNetworkGameData(getGameSettings());
broadcastMessageToConnectedClients(&networkMessageSynchNetworkGameData);
if(waitForClientAck == true)
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] Waiting for client acks #2\n",__FILE__,__FUNCTION__);
time_t tStart = time(NULL);
bool gotAckFromAllClients = false;
while(gotAckFromAllClients == false && difftime(time(NULL),tStart) <= 5)
{
gotAckFromAllClients = true;
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
ConnectionSlot *connectionSlot = slots[i];
if(connectionSlot != NULL && connectionSlot->isConnected())
{
if(connectionSlot->getReceivedNetworkGameStatus() == false)
{
gotAckFromAllClients = false;
}
connectionSlot->update();
}
}
}
}
gameSettingsUpdateCount++;
}
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__);
}
void ServerInterface::close()
{
if(Socket::enableNetworkDebugInfo) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__);
//serverSocket = ServerSocket();
}
bool ServerInterface::getFogOfWar()
{
return Config::getInstance().getBool("FogOfWar");
}
}}//end namespace