From 89d3c664717cfb7caaf6bc43cf9d1fb45b6c3e6b Mon Sep 17 00:00:00 2001 From: Mark Vejvoda Date: Wed, 17 Mar 2010 06:25:19 +0000 Subject: [PATCH] Added a more user friendly messagebox when network errors are encountered. --- source/glest_game/main/main.cpp | 205 +++++++ source/glest_game/main/program.cpp | 110 +++- source/glest_game/main/program.h | 135 ++++ .../glest_game/network/client_interface.cpp | 55 +- source/glest_game/network/connection_slot.cpp | 330 ++++++++++ .../glest_game/network/network_interface.cpp | 294 +++++++++ source/glest_game/network/network_interface.h | 5 + source/glest_game/network/network_message.cpp | 409 +++++++++++++ .../glest_game/network/server_interface.cpp | 578 ++++++++++++++++++ 9 files changed, 2103 insertions(+), 18 deletions(-) create mode 100644 source/glest_game/main/main.cpp create mode 100644 source/glest_game/main/program.h create mode 100644 source/glest_game/network/connection_slot.cpp create mode 100644 source/glest_game/network/network_interface.cpp create mode 100644 source/glest_game/network/network_message.cpp create mode 100644 source/glest_game/network/server_interface.cpp diff --git a/source/glest_game/main/main.cpp b/source/glest_game/main/main.cpp new file mode 100644 index 00000000..a6cb9fb5 --- /dev/null +++ b/source/glest_game/main/main.cpp @@ -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 +#include + +#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) diff --git a/source/glest_game/main/program.cpp b/source/glest_game/main/program.cpp index 50992e99..c5ec68c4 100644 --- a/source/glest_game/main/program.cpp +++ b/source/glest_game/main/program.cpp @@ -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 diff --git a/source/glest_game/main/program.h b/source/glest_game/main/program.h new file mode 100644 index 00000000..ae75debd --- /dev/null +++ b/source/glest_game/main/program.h @@ -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 diff --git a/source/glest_game/network/client_interface.cpp b/source/glest_game/network/client_interface.cpp index 30917433..2660548e 100755 --- a/source/glest_game/network/client_interface.cpp +++ b/source/glest_game/network/client_interface.cpp @@ -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); diff --git a/source/glest_game/network/connection_slot.cpp b/source/glest_game/network/connection_slot.cpp new file mode 100644 index 00000000..c02f2779 --- /dev/null +++ b/source/glest_game/network/connection_slot.cpp @@ -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 + +#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; irequestCommand(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(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 diff --git a/source/glest_game/network/network_interface.cpp b/source/glest_game/network/network_interface.cpp new file mode 100644 index 00000000..09268075 --- /dev/null +++ b/source/glest_game/network/network_interface.cpp @@ -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 +#include + +#include "types.h" +#include "conversion.h" +#include "platform_util.h" + +#include "leak_dumper.h" +#include + +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(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 diff --git a/source/glest_game/network/network_interface.h b/source/glest_game/network/network_interface.h index a077a365..7f362fed 100644 --- a/source/glest_game/network/network_interface.h +++ b/source/glest_game/network/network_interface.h @@ -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();} diff --git a/source/glest_game/network/network_message.cpp b/source/glest_game/network/network_message.cpp new file mode 100644 index 00000000..c3277802 --- /dev/null +++ b/source/glest_game/network/network_message.cpp @@ -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 +#include + +#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(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; igetFactionTypeName(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; isetFactionTypeName(i, data.factionTypeNames[i].getString()); + gameSettings->setFactionControl(i, static_cast(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(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 diff --git a/source/glest_game/network/server_interface.cpp b/source/glest_game/network/server_interface.cpp new file mode 100644 index 00000000..5b354b3a --- /dev/null +++ b/source/glest_game/network/server_interface.cpp @@ -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 +#include + +#include "platform_util.h" +#include "conversion.h" +#include "config.h" +#include "lang.h" + +#include "leak_dumper.h" +#include "logger.h" +#include + +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=0 && playerIndex 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 vctTeamMessages; + + //update all slots + bool checkForNewClients = true; + for(int i= 0; igetSocket() != 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 waitingForHosts; + allReady= true; + for(int i= 0; iisConnected() == 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; iisConnected()){ + + 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; igetAllowDownloadDataSynch() == 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; iisConnected()) + { + + 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; iisConnected()){ + + 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; iisConnected() == 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; iisConnected()) + { + if(connectionSlot->getReceivedNetworkGameStatus() == false) + { + gotAckFromAllClients = false; + } + + connectionSlot->update(); + } + } + } + } + + for(int i= 0; iisConnected()) + { + 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; iisConnected()) + { + 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