Compare commits

...

2 Commits

Author SHA1 Message Date
krzys-h a30202d884 Fixed loading userlevels 2014-12-25 14:51:52 +01:00
krzys-h 9fda292e01 Basic multiplayer infrastructure 2014-12-25 14:31:53 +01:00
28 changed files with 1718 additions and 25 deletions

View File

@ -140,9 +140,15 @@ endif()
# The flags are used throughout src/ and test/ subdirs
# Special flags for boost
set(Boost_FLAGS "-DBOOST_NO_SCOPED_ENUMS -DBOOST_NO_CXX11_SCOPED_ENUMS")
set(Boost_FLAGS "-DBOOST_NO_SCOPED_ENUMS -DBOOST_NO_CXX11_SCOPED_ENUMS -DBOOST_THREAD_USE_LIB")
set(COLOBOT_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wold-style-cast ${CXX11_FLAGS} ${Boost_FLAGS}")
if(PLATFORM_WINDOWS)
set(BOOST_ASIO_FLAGS "-D_WIN32_WINNT=0x0601")
elseif()
set(BOOST_ASIO_FLAGS "")
endif()
set(COLOBOT_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wold-style-cast ${CXX11_FLAGS} ${Boost_FLAGS} ${BOOST_ASIO_FLAGS}")
set(COLOBOT_CXX_FLAGS_RELEASE "-O2")
set(COLOBOT_CXX_FLAGS_DEBUG "-g -O0")
@ -216,7 +222,14 @@ set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
set(Boost_ADDITIONALVERSION "1.51" "1.51.0")
find_package(Boost COMPONENTS system filesystem regex REQUIRED)
if(PLATFORM_WINDOWS)
set(BOOST_THREAD_COMPONENT "thread_win32")
set(BOOST_ASIO_ADDITIONAL_LIBS ws2_32 wsock32)
else()
set(BOOST_THREAD_COMPONENT "thread")
set(BOOST_ASIO_ADDITIONAL_LIBS "")
endif()
find_package(Boost COMPONENTS system filesystem regex ${BOOST_THREAD_COMPONENT} serialization REQUIRED)
find_package(GLEW REQUIRED)

View File

@ -97,6 +97,12 @@ set(BASE_SOURCES
graphics/engine/text.cpp
graphics/engine/water.cpp
graphics/opengl/gldevice.cpp
net/connections/conn.cpp
net/connections/connclient.cpp
net/connections/connserver.cpp
net/services/client.cpp
net/services/netservice.cpp
net/services/server.cpp
object/auto/auto.cpp
object/auto/autobase.cpp
object/auto/autoconvert.cpp
@ -203,6 +209,10 @@ set(MAIN_SOURCES
${RES_FILES}
)
set(DEDICATED_SERVER_SOURCES
app/main_server.cpp
)
# Libraries
set(LIBS
@ -216,6 +226,7 @@ set(LIBS
${PNG_LIBRARIES}
${GLEW_LIBRARY}
${Boost_LIBRARIES}
${BOOST_ASIO_ADDITIONAL_LIBS}
${LIBSNDFILE_LIBRARY}
${OPTIONAL_LIBS}
${PLATFORM_LIBS}
@ -263,6 +274,7 @@ link_directories(
add_library(colobotbase STATIC ${BASE_SOURCES})
add_executable(colobot ${MAIN_SOURCES})
target_link_libraries(colobot colobotbase ${LIBS})
@ -270,3 +282,7 @@ install(TARGETS colobot RUNTIME DESTINATION ${COLOBOT_INSTALL_BIN_DIR})
if(NOT CBOT_STATIC)
set_target_properties(colobot PROPERTIES INSTALL_RPATH ${COLOBOT_INSTALL_LIB_DIR})
endif()
add_executable(colobot_server ${DEDICATED_SERVER_SOURCES})
target_link_libraries(colobot_server colobotbase ${LIBS})

View File

@ -100,7 +100,7 @@ struct ApplicationPrivate
CApplication::CApplication()
CApplication::CApplication(bool dedicatedServer)
{
m_private = new ApplicationPrivate();
m_iMan = new CInstanceManager();
@ -115,6 +115,8 @@ CApplication::CApplication()
m_controller = nullptr;
m_sound = nullptr;
m_dedicatedServer = dedicatedServer;
m_exitCode = 0;
m_active = false;
m_debugModes = 0;
@ -167,6 +169,8 @@ CApplication::CApplication()
m_runSceneName = "";
m_runSceneRank = 0;
m_serverAddress = "";
m_sceneTest = false;
m_headless = false;
@ -234,7 +238,8 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
OPT_MOD,
OPT_VBO,
OPT_RESOLUTION,
OPT_HEADLESS
OPT_HEADLESS,
OPT_CONNECT
};
option options[] =
@ -252,6 +257,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
{ "vbo", required_argument, nullptr, OPT_VBO },
{ "resolution", required_argument, nullptr, OPT_RESOLUTION },
{ "headless", no_argument, nullptr, OPT_HEADLESS },
{ "connect", required_argument, nullptr, OPT_CONNECT },
{ nullptr, 0, nullptr, 0}
};
@ -295,6 +301,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
GetLogger()->Message(" -vbo mode set OpenGL VBO mode (one of: auto, enable, disable)\n");
GetLogger()->Message(" -resolution WxH set resolution\n");
GetLogger()->Message(" -headless headless mode - disables graphics, sound and user interaction\n");
GetLogger()->Message(" -connect ip connect to multiplayer server\n");
return PARSE_ARGS_HELP;
}
case OPT_DEBUG:
@ -415,6 +422,11 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
m_headless = true;
break;
}
case OPT_CONNECT:
{
m_serverAddress = optarg;
break;
}
default:
assert(false); // should never get here
}
@ -429,6 +441,13 @@ bool CApplication::Create()
bool defaultValues = false;
GetLogger()->Info("Creating CApplication\n");
if(m_dedicatedServer && m_runSceneName.empty()) {
GetLogger()->Error("Mission file for dedicated server not specified\n");
GetLogger()->Info("Please use -runscene scenename, e.g. -runscene freemissions901\n");
m_exitCode = 1;
return false;
}
boost::filesystem::path dataPath(m_dataPath);
if (! (boost::filesystem::exists(dataPath) && boost::filesystem::is_directory(dataPath)) )
@ -473,7 +492,7 @@ bool CApplication::Create()
//Create the sound instance.
#ifdef OPENAL_SOUND
if(!m_headless) {
if(!m_headless && !m_dedicatedServer) {
m_sound = static_cast<CSoundInterface *>(new ALSound());
} else {
m_sound = new CSoundInterface();
@ -522,7 +541,7 @@ bool CApplication::Create()
return false;
}
if(!m_headless) {
if(!m_headless && !m_dedicatedServer) {
// load settings from profile
int iValue;
if ( GetProfile().GetIntProperty("Setup", "Resolution", iValue) && !m_resolutionOverride )
@ -560,7 +579,7 @@ bool CApplication::Create()
// Don't generate joystick events
SDL_JoystickEventState(SDL_IGNORE);
if(!m_headless) {
if(!m_headless && !m_dedicatedServer) {
// The video is ready, we can create and initalize the graphics device
m_device = new Gfx::CGLDevice(m_deviceConfig);
} else {
@ -591,12 +610,29 @@ bool CApplication::Create()
// Create the robot application.
m_controller = new CController(this, !defaultValues);
if (m_runSceneName.empty())
m_controller->StartApp();
else {
if(m_runSceneName == "custom")
{
m_controller->GetRobotMain()->ChangePhase(PHASE_USER); // To load userlevel list - TODO: this is ugly
}
if(m_dedicatedServer)
{
m_controller->GetRobotMain()->SetExitAfterMission(true);
m_controller->StartGame(m_runSceneName, m_runSceneRank/100, m_runSceneRank%100);
m_controller->StartMPServer(m_runSceneName, m_runSceneRank/100, m_runSceneRank%100);
} else {
if(!m_serverAddress.empty())
{
m_controller->GetRobotMain()->SetExitAfterMission(true);
m_controller->StartMPClient(m_serverAddress);
} else {
if(m_runSceneName.empty())
{
m_controller->StartApp();
} else {
m_controller->GetRobotMain()->SetExitAfterMission(true);
m_controller->StartSP(m_runSceneName, m_runSceneRank/100, m_runSceneRank%100);
}
}
}
return true;

View File

@ -186,7 +186,7 @@ class CApplication : public CSingleton<CApplication>
{
public:
//! Constructor (can only be called once!)
CApplication();
CApplication(bool dedicatedServer = false);
//! Destructor
~CApplication();
@ -379,6 +379,9 @@ protected:
CProfile* m_profile;
//! Input manager
CInput* m_input;
//! Is it a dedicated server?
bool m_dedicatedServer;
//! Code to return at exit
int m_exitCode;
@ -451,6 +454,9 @@ protected:
int m_runSceneRank;
//@}
//! Server to connect to
std::string m_serverAddress;
//! Scene test mode
bool m_sceneTest;

View File

@ -20,6 +20,9 @@
#include "app/controller.h"
#include "net/services/client.h"
#include "net/services/server.h"
#include "object/robotmain.h"
#include "ui/maindialog.h"
@ -32,7 +35,7 @@ CController::CController(CApplication* app, bool loadProfile)
{
m_app = app;
m_main = new CRobotMain(this);
m_dialog = new Ui::CMainDialog();
m_dialog = new Ui::CMainDialog(this);
m_main->Create(loadProfile);
m_dialog->Create();
@ -40,10 +43,16 @@ CController::CController(CApplication* app, bool loadProfile)
m_main->CreateIni();
else
m_main->LoadIni();
m_server = nullptr;
m_client = nullptr;
m_multiplayer = false;
}
CController::~CController()
{
StopGame();
delete m_dialog;
m_dialog = nullptr;
@ -68,20 +77,94 @@ Ui::CMainDialog* CController::GetMainDialog()
return m_dialog;
}
CClient* CController::GetClient()
{
return m_client;
}
CServer* CController::GetServer()
{
return m_server;
}
void CController::StartApp()
{
m_main->ChangePhase(PHASE_WELCOME1);
}
void CController::StartGame(std::string cat, int chap, int lvl)
void CController::StartSP(std::string cat, int chap, int lvl)
{
m_dialog->SetSceneName(cat.c_str());
m_dialog->SetSceneRank(chap*100+lvl);
m_main->ChangePhase(PHASE_LOADING);
m_multiplayer = false;
}
void CController::StartMPServer(std::string cat, int chap, int lvl)
{
m_dialog->SetSceneName(cat.c_str());
m_dialog->SetSceneRank(chap*100+lvl);
m_main->ChangePhase(PHASE_LOADING);
m_server = new CServer();
m_client = nullptr;
m_multiplayer = true;
}
void CController::StartMPClient(std::string server)
{
m_server = nullptr;
m_client = new CClient(server);
m_multiplayer = true;
}
void CController::StopGame()
{
if(m_client != nullptr)
{
m_client->Stop();
delete m_client;
m_client = nullptr;
}
if(m_server != nullptr)
{
m_server->Stop();
delete m_server;
m_server = nullptr;
}
m_multiplayer = false;
}
bool CController::IsMultiplayer()
{
return m_multiplayer;
}
bool CController::IsServer()
{
return m_server != nullptr || !m_multiplayer;
}
bool CController::IsClient()
{
return m_client != nullptr || !m_multiplayer;
}
void CController::ProcessEvent(Event& event)
{
if(event.type == EVENT_FRAME)
{
if(m_client != nullptr)
{
m_client->ProcessEvent(event);
}
if(m_server != nullptr)
{
m_server->ProcessEvent(event);
}
}
bool passOn = m_dialog->EventProcess(event);
if(passOn)
m_main->ProcessEvent(event);

View File

@ -32,6 +32,8 @@ class CRobotMain;
namespace Ui {
class CMainDialog;
}
class CServer;
class CClient;
/**
* \class CController
@ -50,16 +52,39 @@ public:
//! Return CMainDialog instance
Ui::CMainDialog* GetMainDialog();
//! Return CClient instance
CClient* GetClient();
//! Return CServer instance
CServer* GetServer();
//! Event processing
void ProcessEvent(Event &event);
//! Start the application
void StartApp();
//! Starts the simulation, loading the given scene
void StartGame(std::string cat, int chap, int lvl);
void StartSP(std::string cat, int chap, int lvl);
//! Starts multiplayer server
void StartMPServer(std::string cat, int chap, int lvl);
//! Starts multiplayer client
void StartMPClient(std::string server);
//! Stops the simulation
void StopGame();
//! Is it a multiplayer game?
bool IsMultiplayer();
//! Is it a server?
bool IsServer();
//! Is it a client?
bool IsClient();
private:
CApplication* m_app;
CRobotMain* m_main;
Ui::CMainDialog* m_dialog;
CServer* m_server;
CClient* m_client;
bool m_multiplayer;
};

97
src/app/main_server.cpp Normal file
View File

@ -0,0 +1,97 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
/**
* \file app/main_server.cpp
* \brief Entry point of dedicated server
*/
#include "app/app.h"
#include "app/system.h"
#include "common/config.h"
#include "common/logger.h"
#include "common/misc.h"
#include "common/restext.h"
/* Doxygen main page */
#include "common/resources/resourcemanager.h"
//! Entry point to the program
extern "C"
{
int SDL_MAIN_FUNC(int argc, char *argv[])
{
CLogger logger; // single istance of logger
CResourceManager manager(argv[0]);
// Initialize static string arrays
InitializeRestext();
InitializeEventTypeTexts();
logger.Info("%s dedicated server starting\n", COLOBOT_FULLNAME);
int code = 0;
while(true) {
CSystemUtils* systemUtils = CSystemUtils::Create(); // platform-specific utils
systemUtils->Init();
CApplication* app = new CApplication(true); // single instance of the application
ParseArgsStatus status = app->ParseArguments(argc, argv);
if (status == PARSE_ARGS_FAIL)
{
logger.Error("Invalid commandline arguments!\n");
return app->GetExitCode();
}
else if (status == PARSE_ARGS_HELP)
{
return app->GetExitCode();
}
if (! app->Create())
{
app->Destroy(); // ensure a clean exit
code = app->GetExitCode();
if ( code != 0 && !app->GetErrorMessage().empty() )
{
logger.Error("%s\n", app->GetErrorMessage().c_str());
}
logger.Info("Didn't run main loop. Exiting with code %d\n", code);
return code;
}
code = app->Run();
bool restarting = app->IsRestarting();
delete app;
delete systemUtils;
if(!restarting) break;
}
logger.Info("Exiting with code %d\n", code);
return code;
}
} // extern "C"

31
src/common/windowsfix.h Normal file
View File

@ -0,0 +1,31 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "common/config.h"
#if WIN32
#include <windows.h> // Just to make sure windows.h won't be included AFTER we remove it's macros
// If windows.h is included somewhere, it defines these macros to point to CreateDirectoryA and RemoveDirectoryA. This makes some of our class members inaccessible
#undef CreateDirectory
#undef RemoveDirectory
// This conflicts with one of Gfx::CText members
#undef GetCharWidth
#endif

View File

@ -24,7 +24,6 @@
#pragma once
#include "graphics/core/color.h"
#include "math/point.h"

View File

@ -0,0 +1,123 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "net/connections/conn.h"
#include "common/event.h"
#include "common/logger.h"
#include "net/packets/packettype.h"
#include <boost/bind.hpp>
CConnection::CConnection(boost::asio::io_service& service) : m_socket(service)
{
}
tcp::socket& CConnection::GetSocket()
{
return m_socket;
}
void CConnection::Init()
{
StartReceiveHeader();
Start();
}
void CConnection::Send(PacketType packet, std::string data)
{
char* buf = new char[sizeof(PacketHeader)+data.length()];
PacketHeader* p = reinterpret_cast<PacketHeader*>(buf);
p->magic = PACKET_MAGIC;
p->type = packet;
p->len = data.length();
memcpy(buf+sizeof(PacketHeader), data.c_str(), data.length());
boost::asio::async_write(m_socket, boost::asio::buffer(buf, sizeof(PacketHeader)+data.length()), boost::bind(&CConnection::HandleWriteFinished, this, buf));
}
void CConnection::HandleWriteFinished(char* buffer)
{
delete[] buffer;
}
void CConnection::StartReceiveHeader()
{
char* buf = new char[sizeof(PacketHeader)];
boost::asio::async_read(m_socket, boost::asio::buffer(buf, sizeof(PacketHeader)), boost::bind(&CConnection::HandleReceiveHeader, this, buf, boost::asio::placeholders::error));
}
void CConnection::HandleReceiveHeader(char* buffer, const boost::system::error_code& err)
{
if(!err)
{
PacketHeader* p = reinterpret_cast<PacketHeader*>(buffer);
if(p->magic != PACKET_MAGIC)
{
CLogger::GetInstancePointer()->Error("Received bad packet (invalid magic), disconnecting...\n");
// TODO: Drop the connection
// For now just keep going:
StartReceiveHeader();
} else {
memcpy(&m_packetHeader, p, sizeof(PacketHeader));
if(m_packetHeader.len > 0)
{
StartReceiveContent(m_packetHeader.len);
} else {
// This makes sure Receive() is called on the main thread
m_recvQueue.push(ReceivedPacket(m_packetHeader.type));
StartReceiveHeader();
}
}
} else {
CLogger::GetInstancePointer()->Error("Socket read error: %s\n", err.message().c_str());
}
delete[] buffer;
}
void CConnection::StartReceiveContent(unsigned int len)
{
char* buf = new char[len];
m_socket.async_receive(boost::asio::buffer(buf, len), boost::bind(&CConnection::HandleReceiveContent, this, buf, len, boost::asio::placeholders::error));
}
void CConnection::HandleReceiveContent(char* buffer, unsigned int len, const boost::system::error_code& err)
{
if(!err)
{
// This makes sure Receive() is called on the main thread
m_recvQueue.push(ReceivedPacket(m_packetHeader.type, std::string(buffer, len)));
StartReceiveHeader();
} else {
CLogger::GetInstancePointer()->Error("Socket read error: %s\n", err.message().c_str());
}
delete[] buffer;
}
void CConnection::EventUpdate()
{
while(!m_recvQueue.empty())
{
ReceivedPacket p = m_recvQueue.front();
Receive(p.packet, p.data);
m_recvQueue.pop();
}
}

View File

@ -0,0 +1,63 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "src/net/packets/packettype.h"
#include <queue>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class CPacketData;
struct ReceivedPacket
{
PacketType packet;
std::string data;
ReceivedPacket(PacketType p, std::string d = "") : packet(p), data(d) {};
};
class CConnection
{
public:
CConnection(boost::asio::io_service& service);
tcp::socket& GetSocket();
void Init();
virtual void Start() = 0;
virtual void Receive(PacketType packet, std::string data = "") = 0;
void Send(PacketType packet, std::string data = "");
void EventUpdate();
private:
void HandleWriteFinished(char* buffer);
void StartReceiveHeader();
void HandleReceiveHeader(char* buffer, const boost::system::error_code& err);
void StartReceiveContent(unsigned int len);
void HandleReceiveContent(char* buffer, unsigned int len, const boost::system::error_code& err);
tcp::socket m_socket;
PacketHeader m_packetHeader;
std::queue<ReceivedPacket> m_recvQueue;
};

View File

@ -0,0 +1,149 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "net/connections/connclient.h"
#include "app/controller.h"
#include "common/config.h"
#include "common/logger.h"
#include "net/packets/packettype.h"
#include "net/services/client.h"
#include "object/objman.h"
#include "object/robotmain.h"
#include "ui/maindialog.h"
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/vector.hpp>
CClientConnection::CClientConnection(boost::asio::io_service& service, CClient* client) : CConnection(service), m_client(client)
{
}
void CClientConnection::Start()
{
}
void CClientConnection::Receive(PacketType packet, std::string data)
{
switch(packet) {
case PACKET_S_HANDSHAKE:
PacketHandshake(data);
break;
case PACKET_S_LEVEL_DATA:
PacketLevelData(data);
break;
case PACKET_S_OBJECT_UPDATE:
PacketObjectUpdate(data);
break;
case PACKET_S_DISCONNECT:
PacketDisconnect(data);
break;
default:
CLogger::GetInstancePointer()->Error("Received bad packet (unknown type %d), disconnecting...\n", packet);
// TODO: Drop the connection
break;
}
}
void CClientConnection::PacketHandshake(std::string data)
{
CLogger::GetInstancePointer()->Debug("Received server info packet\n");
std::istringstream si(data);
boost::archive::text_iarchive ar(si);
std::string serverName; ar & serverName;
std::string serverVersion; ar & serverVersion;
if(serverVersion != COLOBOT_FULLNAME)
CLogger::GetInstancePointer()->Warn("Version mismatch! %s != %s\n", COLOBOT_FULLNAME, serverVersion.c_str());
ar & m_mapName;
ar & m_mapRank;
CLogger::GetInstancePointer()->Info("Connecting to server '%s', running map '%s%d'\n", serverName.c_str(), m_mapName.c_str(), m_mapRank);
std::ostringstream so;
boost::archive::text_oarchive ar_out(so);
std::string playerName = CRobotMain::GetInstancePointer()->GetGamerName(); ar_out & playerName;
Send(PACKET_C_CONNECT, so.str());
}
void CClientConnection::PacketLevelData(std::string data)
{
CLogger::GetInstancePointer()->Debug("Received level data packet\n");
std::istringstream si(data);
boost::archive::text_iarchive ar(si);
ar & m_levelData;
// Load the map now
if(m_mapName == "custom")
{
CController::GetInstancePointer()->GetRobotMain()->ChangePhase(PHASE_USER); // To load userlevel list - TODO: this is ugly
}
CController::GetInstancePointer()->GetMainDialog()->SetSceneName(m_mapName.c_str());
CController::GetInstancePointer()->GetMainDialog()->SetSceneRank(m_mapRank);
CController::GetInstancePointer()->GetRobotMain()->ChangePhase(PHASE_LOADING);
}
const std::vector<ObjectParams>& CClientConnection::GetLevelData()
{
return m_levelData;
}
void CClientConnection::PacketObjectUpdate(std::string data)
{
//CLogger::GetInstancePointer()->Debug("Received object update packet\n");
std::istringstream si(data);
boost::archive::text_iarchive ar(si);
std::vector<ObjectParams> objects;
ar & objects;
for(ObjectParams op : objects)
{
CObject* obj = CObjectManager::GetInstancePointer()->GetObjectById(op.id);
if(obj == nullptr)
{
CLogger::GetInstancePointer()->Error("Update about unknown object %d!\n", op.id);
continue;
}
CObject* sel = CRobotMain::GetInstancePointer()->GetSelect();
if(sel != nullptr && (obj == sel || obj == sel->GetPower() || obj == sel->GetFret())) continue;
if(obj == m_client->m_lastLoad || obj == m_client->m_lastPower) continue;
op.Apply();
}
}
void CClientConnection::PacketDisconnect(std::string data)
{
CLogger::GetInstancePointer()->Info("The server kicked us out :(\n");
}

View File

@ -0,0 +1,52 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "net/connections/conn.h"
#include "net/packets/objectparams.h"
#include <boost/asio.hpp>
class CClient;
class CClientConnection : public CConnection
{
public:
CClientConnection(boost::asio::io_service& service, CClient* client);
void Start();
void Receive(PacketType packet, std::string data = "");
//! Returns initial object positions
const std::vector<ObjectParams>& GetLevelData();
private:
void PacketHandshake(std::string data);
void PacketLevelData(std::string data);
void PacketObjectUpdate(std::string data);
void PacketDisconnect(std::string data);
CClient* m_client;
std::string m_mapName;
int m_mapRank;
std::vector<ObjectParams> m_levelData;
};

View File

@ -0,0 +1,121 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "net/connections/connserver.h"
#include "app/controller.h"
#include "common/config.h"
#include "common/logger.h"
#include "net/packets/objectparams.h"
#include "net/packets/packettype.h"
#include "net/services/server.h"
#include "object/object.h"
#include "object/objman.h"
#include "ui/maindialog.h"
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
CServerConnection::CServerConnection(boost::asio::io_service& service, CServer* server) : CConnection(service), m_server(server)
{
}
void CServerConnection::Start()
{
CLogger::GetInstancePointer()->Debug("New connection...\n");
std::ostringstream s;
boost::archive::text_oarchive ar(s);
std::string serverName = "Test server"; ar & serverName;
std::string serverVersion = COLOBOT_FULLNAME; ar & serverVersion;
std::string mapName = CController::GetInstancePointer()->GetMainDialog()->GetSceneName(); ar & mapName;
int mapRank = CController::GetInstancePointer()->GetMainDialog()->GetSceneRank(); ar & mapRank;
Send(PACKET_S_HANDSHAKE, s.str());
}
void CServerConnection::Receive(PacketType packet, std::string data)
{
switch(packet) {
case PACKET_C_CONNECT:
PacketConnect(data);
break;
case PACKET_C_OBJECT_UPDATE:
PacketObjectUpdate(data);
break;
case PACKET_C_DISCONNECT:
PacketDisconnect(data);
break;
default:
CLogger::GetInstancePointer()->Error("Received bad packet (unknown type %d), disconnecting...\n", packet);
// TODO: Drop the connection
break;
}
}
void CServerConnection::PacketConnect(std::string data)
{
std::istringstream si(data);
boost::archive::text_iarchive ar(si);
ar & m_playerName;
CLogger::GetInstancePointer()->Info("Player '%s' is connecting...\n", m_playerName.c_str());
std::ostringstream so;
boost::archive::text_oarchive ar_out(so);
std::vector<ObjectParams> objects;
for(auto it : CObjectManager::GetInstancePointer()->GetAllObjects())
{
CObject* o = it.second;
ObjectParams op = ObjectParams::FromObject(o);
objects.push_back(op);
}
ar_out & objects;
Send(PACKET_S_LEVEL_DATA, so.str());
}
void CServerConnection::PacketObjectUpdate(std::string data)
{
std::istringstream si(data);
boost::archive::text_iarchive ar(si);
std::vector<ObjectParams> objects;
ar & objects;
for(ObjectParams op : objects)
{
op.Apply();
}
}
void CServerConnection::PacketDisconnect(std::string data)
{
CLogger::GetInstancePointer()->Info("'%s' disconnected\n", m_playerName.c_str());
m_server->Broadcast(PACKET_S_DISCONNECT_NOTIFY);
//TODO: remove from CServer's list
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "net/connections/conn.h"
#include <boost/asio.hpp>
class CServer;
class CServerConnection : public CConnection
{
public:
CServerConnection(boost::asio::io_service& service, CServer* server);
void Start();
void Receive(PacketType packet, std::string data = "");
private:
void PacketConnect(std::string data);
void PacketObjectUpdate(std::string data);
void PacketDisconnect(std::string data);
CServer* m_server;
std::string m_playerName;
};

View File

@ -0,0 +1,128 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "net/packets/serialize.h"
#include "object/object.h"
#include "object/objman.h"
#include "physics/physics.h"
#include <boost/serialization/array.hpp>
struct ObjectParams {
int id;
ObjectType type;
Math::Vector pos[OBJECTMAXPART];
Math::Vector angle[OBJECTMAXPART];
Math::Vector speed;
float energy;
float shield;
int power;
int load;
int loadLink;
template<typename Archive>
void serialize(Archive& ar, unsigned int version)
{
ar & id & type & pos & angle & speed & energy & shield & power & load & loadLink;
}
static ObjectParams FromObject(CObject* o)
{
ObjectParams op;
op.id = o->GetID();
op.type = o->GetType();
for(int i = 0; i < OBJECTMAXPART; i++)
{
op.pos[i] = o->GetPosition(i);
op.angle[i] = o->GetAngle(i);
}
if(o->GetPhysics() != nullptr)
op.speed = o->GetPhysics()->GetMotorSpeed();
else
op.speed = Math::Vector();
op.energy = o->GetEnergy();
op.shield = o->GetShield();
if(o->GetPower() != nullptr)
op.power = o->GetPower()->GetID();
else
op.power = -1;
if(o->GetFret() != nullptr)
op.load = o->GetFret()->GetID();
else
op.load = -1;
op.loadLink = o->GetTruckPart();
return op;
}
bool Apply(bool linkObjects = true)
{
CObject* obj = CObjectManager::GetInstancePointer()->GetObjectById(id);
if(obj == nullptr) return false;
for(int i = 0; i < OBJECTMAXPART; i++)
{
obj->SetPosition(i, pos[i]);
obj->SetAngle(i, angle[i]);
}
if(obj->GetPhysics() != nullptr && !obj->GetSelect())
obj->GetPhysics()->SetMotorSpeed(speed);
obj->SetEnergy(energy);
obj->SetShield(shield);
if(linkObjects)
LinkObjects();
return true;
}
void LinkObjects()
{
CObjectManager* objman = CObjectManager::GetInstancePointer();
CObject* obj = objman->GetObjectById(id);
CObject* old_power = obj->GetPower();
CObject* new_power = power < 0 ? nullptr : objman->GetObjectById(power);
if(old_power != new_power)
{
if(old_power != nullptr) {
old_power->SetTruck(nullptr);
}
obj->SetPower(new_power);
if(new_power != nullptr) {
new_power->SetTruck(obj);
}
}
CObject* old_load = obj->GetFret();
CObject* new_load = load < 0 ? nullptr : objman->GetObjectById(load);
if(old_load != new_load)
{
if(old_load != nullptr) {
old_load->SetTruck(nullptr);
}
obj->SetFret(new_load);
if(new_load != nullptr) {
new_load->SetTruck(obj);
}
}
obj->SetTruckPart(loadLink);
}
};

View File

@ -0,0 +1,60 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include <stdint.h>
//! Packet magic identifier - try to guess what this value means ;)
static const uint32_t PACKET_MAGIC = 0x444c4f47;
enum PacketType {
PACKET_S_HANDSHAKE, // [S] Hello, this is server '%s' and we're running map '%s' on version '%s'. Do you want to connect? (disconnect now if you only wanted info to display in server list)
PACKET_C_CONNECT, // [C] Hi, I'm '%s' and I'd like to connect. This (is|isn't) a local game.
PACKET_S_LEVEL_DATA, // [S] (a set of CreateObject instructions)
PACKET_C_MAP_LOADED, // (*) [C] OK, level is loaded
PACKET_S_JOINED, // (*) [S] Welcome on the server! (broadcast to all players)
PACKET_C_OBJECT_UPDATE, // [C] I've updated an object
PACKET_S_OBJECT_UPDATE, // [S] Object %d has been updated. It's now at position ..., angle ..., and it's velocity is ...
PACKET_C_TASK_START, // (*) [C] Start task ...
PACKET_S_TASK_START, // (*) [S] Task ... has been started on object ...
PACKET_S_TASK_UPDATE, // (*) [S] Task has been updated
PACKET_C_TASK_ABORT, // (*) [C] Abort task ...
PACKET_S_TASK_ABORT, // (*) [S] Task ... has been aborted,
PACKET_C_CBOT_SCRIPT, // (*) [C] I've edited the script
PACKET_S_CBOT_SCRIPT, // (*) [S] The script has been edited
PACKET_C_CBOT_STARTSTOP, // (*) [C] Start/stop the script (see params)
PACKET_S_CBOT_STARTSTOP, // (*) [S] The script has been started/stopped (updates blinking lights etc., DOESN'T start the script clientside)
PACKET_C_DISCONNECT, // [C] I'm disconnecting, bye
PACKET_S_DISCONNECT, // [S] You are a cheater, I'm disconnecting you! (or the server is just shutting down... :P)
PACKET_S_DISCONNECT_NOTIFY, // [S] %s has disconnected
};
struct PacketHeader {
//! Packet magic number
uint32_t magic;
//! Packet type
PacketType type;
//! Size of additional packet data (after the header)
uint32_t len;
} __attribute__((__packed__));

View File

@ -0,0 +1,33 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "math/vector.h"
namespace boost {
namespace serialization {
template<typename Archive>
void serialize(Archive& ar, Math::Vector& o, const unsigned int version) {
ar & o.x & o.y & o.z;
}
}
}

167
src/net/services/client.cpp Normal file
View File

@ -0,0 +1,167 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "net/services/client.h"
#include "common/logger.h"
#include "net/connections/connclient.h"
#include "object/robotmain.h"
#include <boost/lexical_cast.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/vector.hpp>
CClient::CClient(std::string server) : CNetworkService(), m_resolver(m_service), m_connection(nullptr)
{
Connect(server);
StartIOThread();
m_updateTimer = UPDATE_INTERVAL;
m_lastPower = nullptr;
m_lastLoad = nullptr;
}
void CClient::Stop()
{
m_connection->Send(PACKET_C_DISCONNECT);
m_connection->GetSocket().cancel();
StopIOThread();
}
CClientConnection* CClient::GetConnection()
{
return m_connection;
}
void CClient::Connect(std::string server)
{
tcp::resolver::query query(server, boost::lexical_cast<std::string>(PORT));
m_resolver.async_resolve(query, boost::bind(&CClient::HandleResolve, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator));
}
void CClient::HandleResolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator)
{
if (!err)
{
m_connection = new CClientConnection(m_resolver.get_io_service(), this);
// Try to connect to first endpoint in the list
tcp::endpoint endpoint = *endpoint_iterator;
m_connection->GetSocket().async_connect(endpoint, boost::bind(&CClient::HandleConnect, this, boost::asio::placeholders::error, ++endpoint_iterator));
} else {
CLogger::GetInstancePointer()->Error("Failed to resolve hostname: %s\n", err.message().c_str());
}
}
void CClient::HandleConnect(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator)
{
if (!err)
{
// The connection was successful
m_connection->Init();
}
else if (endpoint_iterator != tcp::resolver::iterator())
{
// The connection failed. Try the next endpoint in the list
m_connection->GetSocket().close();
tcp::endpoint endpoint = *endpoint_iterator;
m_connection->GetSocket().async_connect(endpoint, boost::bind(&CClient::HandleConnect, this, boost::asio::placeholders::error, ++endpoint_iterator));
} else {
// The connection failed on all endpoints, report an error
CLogger::GetInstancePointer()->Error("Failed to connect: %s\n", err.message().c_str());
}
}
void CClient::ProcessEvent(Event& event)
{
if(event.type == EVENT_FRAME)
{
m_updateTimer -= event.rTime;
if(m_updateTimer <= 0.0f)
{
m_updateTimer += UPDATE_INTERVAL;
SendObjectUpdate();
}
if(m_connection != nullptr)
{
m_connection->EventUpdate();
}
}
}
void CClient::SendObjectUpdate()
{
if(m_connection == nullptr) return;
std::ostringstream so;
boost::archive::text_oarchive ar_out(so);
std::vector<ObjectParams> objects;
CObject* o = CRobotMain::GetInstancePointer()->GetSelect();
if(o == nullptr) return;
ObjectParams op = ObjectParams::FromObject(o);
objects.push_back(op);
CObject* power = o->GetPower();
if(power != nullptr)
{
ObjectParams op_power = ObjectParams::FromObject(power);
objects.push_back(op_power);
}
if(power != m_lastPower)
{
if(m_lastPower != nullptr)
{
ObjectParams op_power = ObjectParams::FromObject(m_lastPower);
objects.push_back(op_power);
if(m_lastPower->GetTruck() != nullptr && m_lastPower->GetTruck() != o) {
ObjectParams op_truck = ObjectParams::FromObject(m_lastPower->GetTruck());
objects.push_back(op_truck);
}
}
m_lastPower = power;
}
CObject* load = o->GetFret();
if(load != nullptr)
{
ObjectParams op_load = ObjectParams::FromObject(load);
objects.push_back(op_load);
}
if(load != m_lastLoad)
{
if(m_lastLoad != nullptr)
{
ObjectParams op_load = ObjectParams::FromObject(m_lastLoad);
objects.push_back(op_load);
if(m_lastLoad->GetTruck() != nullptr && m_lastLoad->GetTruck() != o) {
ObjectParams op_truck = ObjectParams::FromObject(m_lastPower->GetTruck());
objects.push_back(op_truck);
}
}
m_lastLoad = load;
}
ar_out & objects;
m_connection->Send(PACKET_C_OBJECT_UPDATE, so.str());
}

56
src/net/services/client.h Normal file
View File

@ -0,0 +1,56 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "net/services/netservice.h"
#include <boost/asio.hpp>
class CClientConnection;
class CController;
class CObject;
class CClient : public CNetworkService {
friend class CClientConnection;
friend class CController;
const float UPDATE_INTERVAL = 1.0f/30.0f;
public:
CClient(std::string server);
void Stop();
CClientConnection* GetConnection();
void ProcessEvent(Event& event);
private:
void Connect(std::string server);
void HandleResolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator);
void HandleConnect(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator);
void SendObjectUpdate();
private:
tcp::resolver m_resolver;
CClientConnection* m_connection;
float m_updateTimer;
CObject* m_lastPower;
CObject* m_lastLoad;
};

View File

@ -0,0 +1,45 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "net/services/netservice.h"
CNetworkService::CNetworkService()
{
m_thread = nullptr;
}
CNetworkService::~CNetworkService()
{
if(m_thread != nullptr) {
StopIOThread();
}
}
void CNetworkService::StartIOThread()
{
assert(m_thread == nullptr);
m_thread = new boost::thread(boost::bind(&boost::asio::io_service::run, &m_service));
}
void CNetworkService::StopIOThread()
{
m_service.stop();
m_thread->join(); //TODO: try to remove this
m_thread = nullptr;
}

View File

@ -0,0 +1,44 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "common/event.h"
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
class CNetworkService {
public:
const int PORT = 1234;
CNetworkService();
virtual ~CNetworkService();
virtual void ProcessEvent(Event& event) = 0;
protected:
void StartIOThread();
void StopIOThread();
protected:
boost::asio::io_service m_service;
boost::thread* m_thread;
};

222
src/net/services/server.cpp Normal file
View File

@ -0,0 +1,222 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "net/services/server.h"
#include "common/logger.h"
#include "net/connections/connserver.h"
#include "net/packets/objectparams.h"
#include "net/packets/packettype.h"
#include "object/objman.h"
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
CServer::CServer() : CNetworkService(), m_acceptor(m_service, tcp::endpoint(tcp::v4(), PORT))
{
StartAccept();
StartIOThread();
m_updateTimer = UPDATE_INTERVAL;
}
void CServer::Stop()
{
Broadcast(PACKET_S_DISCONNECT);
for(auto connection : m_connections)
{
connection->GetSocket().cancel();
}
StopIOThread();
}
void CServer::Broadcast(PacketType packet, std::string data)
{
for(auto connection : m_connections)
{
connection->Send(packet, data);
}
}
void CServer::StartAccept()
{
CServerConnection* new_connection = new CServerConnection(m_acceptor.get_io_service(), this);
m_acceptor.async_accept(new_connection->GetSocket(), boost::bind(&CServer::HandleAccept, this, new_connection, boost::asio::placeholders::error));
}
void CServer::HandleAccept(CServerConnection* new_connection, const boost::system::error_code& err)
{
if(!err)
{
m_connections.push_back(new_connection);
new_connection->Init();
} else {
CLogger::GetInstancePointer()->Warn("Failed to accept connection: %s\n", err.message().c_str());
}
StartAccept();
}
void CServer::ProcessEvent(Event& event)
{
if(event.type == EVENT_FRAME)
{
m_updateTimer -= event.rTime;
if(m_updateTimer <= 0.0f)
{
m_updateTimer += UPDATE_INTERVAL;
SendObjectsUpdate();
}
for(auto connection : m_connections)
{
connection->EventUpdate();
}
}
}
void CServer::SendObjectsUpdate()
{
std::ostringstream so;
boost::archive::text_oarchive ar_out(so);
std::vector<ObjectParams> objects;
for(auto it : CObjectManager::GetInstancePointer()->GetAllObjects())
{
CObject* o = it.second;
ObjectType type = o->GetType();
//TODO: We need a function to tell us if this is a stationary object
if(type == OBJECT_PLANT0 ||
type == OBJECT_PLANT1 ||
type == OBJECT_PLANT2 ||
type == OBJECT_PLANT3 ||
type == OBJECT_PLANT4 ||
type == OBJECT_PLANT5 ||
type == OBJECT_PLANT6 ||
type == OBJECT_PLANT7 ||
type == OBJECT_PLANT8 ||
type == OBJECT_PLANT9 ||
type == OBJECT_PLANT10 ||
type == OBJECT_PLANT11 ||
type == OBJECT_PLANT12 ||
type == OBJECT_PLANT13 ||
type == OBJECT_PLANT14 ||
type == OBJECT_PLANT15 ||
type == OBJECT_PLANT16 ||
type == OBJECT_PLANT17 ||
type == OBJECT_PLANT18 ||
type == OBJECT_PLANT19 ||
type == OBJECT_TREE0 ||
type == OBJECT_TREE1 ||
type == OBJECT_TREE2 ||
type == OBJECT_TREE3 ||
type == OBJECT_TREE4 ||
type == OBJECT_TREE5 ||
type == OBJECT_BARRIER0 ||
type == OBJECT_BARRIER1 ||
type == OBJECT_BARRIER2 ||
type == OBJECT_BARRIER3 ||
type == OBJECT_RUINmobilew1 ||
type == OBJECT_RUINmobilew2 ||
type == OBJECT_RUINmobilet1 ||
type == OBJECT_RUINmobilet2 ||
type == OBJECT_RUINmobiler1 ||
type == OBJECT_RUINmobiler2 ||
type == OBJECT_RUINfactory ||
type == OBJECT_RUINdoor ||
type == OBJECT_RUINsupport ||
type == OBJECT_RUINradar ||
type == OBJECT_RUINconvert ||
type == OBJECT_RUINbase ||
type == OBJECT_RUINhead ||
type == OBJECT_TEEN0 ||
type == OBJECT_TEEN1 ||
type == OBJECT_TEEN2 ||
type == OBJECT_TEEN3 ||
type == OBJECT_TEEN4 ||
type == OBJECT_TEEN5 ||
type == OBJECT_TEEN6 ||
type == OBJECT_TEEN7 ||
type == OBJECT_TEEN8 ||
type == OBJECT_TEEN9 ||
type == OBJECT_TEEN10 ||
type == OBJECT_TEEN11 ||
type == OBJECT_TEEN12 ||
type == OBJECT_TEEN13 ||
type == OBJECT_TEEN14 ||
type == OBJECT_TEEN15 ||
type == OBJECT_TEEN16 ||
type == OBJECT_TEEN17 ||
type == OBJECT_TEEN18 ||
type == OBJECT_TEEN19 ||
type == OBJECT_TEEN20 ||
type == OBJECT_TEEN21 ||
type == OBJECT_TEEN22 ||
type == OBJECT_TEEN23 ||
type == OBJECT_TEEN24 ||
type == OBJECT_TEEN25 ||
type == OBJECT_TEEN26 ||
type == OBJECT_TEEN27 ||
type == OBJECT_TEEN28 ||
type == OBJECT_TEEN29 ||
type == OBJECT_TEEN30 ||
type == OBJECT_TEEN31 ||
type == OBJECT_TEEN32 ||
type == OBJECT_TEEN33 ||
type == OBJECT_TEEN34 ||
type == OBJECT_TEEN35 ||
type == OBJECT_TEEN36 ||
type == OBJECT_TEEN37 ||
type == OBJECT_TEEN38 ||
type == OBJECT_TEEN39 ||
type == OBJECT_TEEN40 ||
type == OBJECT_TEEN41 ||
type == OBJECT_TEEN42 ||
type == OBJECT_TEEN43 ||
type == OBJECT_TEEN44 ||
type == OBJECT_QUARTZ0 ||
type == OBJECT_QUARTZ1 ||
type == OBJECT_QUARTZ2 ||
type == OBJECT_QUARTZ3 ||
type == OBJECT_ROOT0 ||
type == OBJECT_ROOT1 ||
type == OBJECT_ROOT2 ||
type == OBJECT_ROOT3 ||
type == OBJECT_ROOT4 ||
type == OBJECT_ROOT5 ||
type == OBJECT_MUSHROOM1 ||
type == OBJECT_MUSHROOM2 ||
type == OBJECT_APOLLO1 ||
type == OBJECT_APOLLO2 ||
type == OBJECT_APOLLO3 ||
type == OBJECT_APOLLO4 ||
type == OBJECT_APOLLO5 ||
type == OBJECT_HOME1) continue;
ObjectParams op = ObjectParams::FromObject(o);
objects.push_back(op);
}
ar_out & objects;
Broadcast(PACKET_S_OBJECT_UPDATE, so.str());
}

55
src/net/services/server.h Normal file
View File

@ -0,0 +1,55 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include "net/services/netservice.h"
#include "net/packets/packettype.h"
#include <boost/asio.hpp>
#include <map>
class CServerConnection;
class CController;
class CServer : public CNetworkService {
friend class CServerConnection;
friend class CController;
const float UPDATE_INTERVAL = 1.0f/30.0f;
public:
CServer();
void Stop();
void ProcessEvent(Event& event);
void Broadcast(PacketType packet, std::string data = "");
private:
void StartAccept();
void HandleAccept(CServerConnection* new_connection, const boost::system::error_code& err);
void SendObjectsUpdate();
private:
tcp::acceptor m_acceptor;
std::vector<CServerConnection*> m_connections;
float m_updateTimer;
};

View File

@ -66,6 +66,7 @@ bool CObjectManager::DeleteObject(CObject* instance)
CObject* CObjectManager::GetObjectById(unsigned int id)
{
if(m_table.find(id) == m_table.end()) return nullptr;
return m_table[id];
}

View File

@ -33,6 +33,7 @@
#include "common/misc.h"
#include "common/profile.h"
#include "common/restext.h"
#include "common/windowsfix.h"
#include "common/resources/resourcemanager.h"
#include "common/resources/inputstream.h"
@ -54,6 +55,10 @@
#include "math/const.h"
#include "math/geometry.h"
#include "net/connections/connclient.h"
#include "net/packets/objectparams.h"
#include "net/services/client.h"
#include "object/auto/auto.h"
#include "object/auto/autobase.h"
#include "object/brain.h"
@ -3432,8 +3437,24 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
InitEye();
SetMovieLock(false);
if (read[0] != 0) // loading file ?
sel = IOReadScene(read, stack);
if(!m_ctrl->IsServer())
{
for(ObjectParams op : m_ctrl->GetClient()->GetConnection()->GetLevelData())
{
CLogger::GetInstancePointer()->Debug("Received object %d from server\n", op.id); //TODO: Change to Trace later
g_id = op.id-1; //TODO: Well, this is a bit hacky solution
CObjectManager::GetInstancePointer()->CreateObject(op.pos[0], op.angle[0].y, op.type, -1.0f);
op.Apply(false);
}
for(ObjectParams op : m_ctrl->GetClient()->GetConnection()->GetLevelData())
{
op.LinkObjects();
}
} else {
if (read[0] != 0) // loading file ?
sel = IOReadScene(read, stack);
}
continue;
}
@ -3456,6 +3477,7 @@ void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
if (line->GetCommand() == "CreateObject" && read[0] == 0)
{
if(!m_ctrl->IsServer()) continue;
ObjectType type = line->GetParam("type")->AsObjectType();
int gadget = line->GetParam("gadget")->AsInt(-1);

View File

@ -21,6 +21,7 @@
#include "ui/maindialog.h"
#include "app/app.h"
#include "app/controller.h"
#include "app/input.h"
#include "app/system.h"
@ -114,8 +115,9 @@ static int perso_color[3*10*3] =
// Constructor of robot application.
CMainDialog::CMainDialog()
CMainDialog::CMainDialog(CController* controller)
{
m_controller = controller;
m_app = nullptr;
m_eventQueue = nullptr;
m_sound = nullptr;
@ -189,10 +191,10 @@ CMainDialog::CMainDialog()
void CMainDialog::Create()
{
m_app = CApplication::GetInstancePointer();
m_app = m_controller->GetApplication();
m_eventQueue = m_app->GetEventQueue();
m_sound = m_app->GetSound();
m_main = CRobotMain::GetInstancePointer();
m_main = m_controller->GetRobotMain();
m_interface = m_main->GetInterface();
m_camera = m_main->GetCamera();
m_engine = Gfx::CEngine::GetInstancePointer();
@ -2259,9 +2261,8 @@ bool CMainDialog::EventProcess(const Event &event)
break;
case EVENT_INTERFACE_PLAY:
m_sceneRank = (m_chap[m_index]+1)*100+(m_sel[m_index]+1);
m_phaseTerm = m_phase;
m_main->ChangePhase(PHASE_LOADING);
m_controller->StartSP(m_sceneName, m_chap[m_index]+1, m_sel[m_index]+1);
break;
case EVENT_INTERFACE_READ:

View File

@ -31,6 +31,7 @@
#include <vector>
class CController;
class CEventQueue;
class CSoundInterface;
@ -70,7 +71,7 @@ struct GamerPerso
class CMainDialog
{
public:
CMainDialog();
CMainDialog(CController* controller);
~CMainDialog();
void Create();
@ -182,6 +183,7 @@ protected:
void ChangeKey(EventType event);
protected:
CController* m_controller;
CApplication* m_app;
CRobotMain* m_main;
CEventQueue* m_eventQueue;