diff --git a/source/main/CMakeLists.txt b/source/main/CMakeLists.txt index b90998ff5b..d87d7d533e 100644 --- a/source/main/CMakeLists.txt +++ b/source/main/CMakeLists.txt @@ -171,10 +171,6 @@ set( SOURCE_FILES gui/panels/GUI_VehicleDescription.{h,cpp} gui/panels/GUI_VehicleDescriptionLayout.{h,cpp} network/Network.{h,cpp} - network/NetworkStreamManager.{h,cpp} - network/Streamable.{h,cpp} - network/StreamableFactory.h - network/StreamableFactoryInterface.h physics/ApproxMath.h physics/Beam.{h,cpp} physics/BeamData.h diff --git a/source/main/ForwardDeclarations.h b/source/main/ForwardDeclarations.h index 063274c584..c71a819244 100644 --- a/source/main/ForwardDeclarations.h +++ b/source/main/ForwardDeclarations.h @@ -80,7 +80,9 @@ struct hook_t; struct ground_model_t; struct client_t; struct authorinfo_t; +struct header_t; struct user_info_t; +struct stream_register_t; namespace MOC { diff --git a/source/main/GlobalEnvironment.h b/source/main/GlobalEnvironment.h index 3c277a8bbc..da241ac1ec 100644 --- a/source/main/GlobalEnvironment.h +++ b/source/main/GlobalEnvironment.h @@ -33,7 +33,7 @@ class GlobalEnvironment , frameListener(0) , mainCamera(0) , mrTime(0) - , network(0) + , multiplayer(false) , player(0) , sceneManager(0) , sky(0) @@ -50,11 +50,11 @@ class GlobalEnvironment Character *player; Collisions *collisions; SurveyMapManager *surveyMap; - Network *network; RoRFrameListener *frameListener; SkyManager *sky; TerrainManager *terrainManager; ThreadPool *threadPool; float mrTime; + bool multiplayer; }; diff --git a/source/main/MainThread.cpp b/source/main/MainThread.cpp index 801c928919..364500e207 100644 --- a/source/main/MainThread.cpp +++ b/source/main/MainThread.cpp @@ -284,53 +284,31 @@ void MainThread::Go() else strncpy(gEnv->frameListener->m_screenshot_format, screenshotFormatString.c_str(), 10); - bool enable_network = BSETTING("Network enable", false); + gEnv->multiplayer = BSETTING("Network enable", false); String preselected_map = SSETTING("Preselected Map", ""); // initiate player colours PlayerColours::getSingleton(); - // you always need that, even if you are not using the network - NetworkStreamManager::getSingleton(); - // new factory for characters, net is INVALID, will be set later new CharacterFactory(); - new ChatSystemFactory(); + + new BeamFactory(); // notice: all factories must be available before starting the network! #ifdef USE_SOCKETW - if (enable_network) + if (gEnv->multiplayer) { RoR::Application::GetContentManager()->AddResourcePack(ContentManager::ResourcePack::MESHES); RoR::Application::GetContentManager()->AddResourcePack(ContentManager::ResourcePack::MATERIALS); - - std::string server_name = SSETTING("Server name", "").c_str(); - - long server_port = ISETTING("Server port", 1337); - - if (server_port==0) - { - ErrorUtils::ShowError(_L("A network error occured"), _L("Bad server port")); - exit(123); - return; - } - LOG("trying to join server '" + String(server_name) + "' on port " + TOSTRING(server_port) + "'..."); + RoR::Application::GetContentManager()->AddResourcePack(ContentManager::ResourcePack::FLAGS); + RoR::Application::GetContentManager()->AddResourcePack(ContentManager::ResourcePack::ICONS); LoadingWindow::getSingleton().setAutotrack(_L("Trying to connect to server ...")); - // important note: all new network code is written in order to allow also the old network protocol to further exist. - // at some point you need to decide with what type of server you communicate below and choose the correct class - - gEnv->network = new Network(server_name, server_port); - - bool connres = gEnv->network->connect(); - - LoadingWindow::getSingleton().hide(); - - new GUI_Multiplayer(); - GUI_Multiplayer::getSingleton().update(); + bool connres = RoR::Networking::Connect(); if (!connres) { @@ -339,40 +317,26 @@ void MainThread::Go() //fatal exit(1); } - char *terrn = gEnv->network->getTerrainName(); - bool isAnyTerrain = (terrn && !strcmp(terrn, "any")); - if (preselected_map.empty() && isAnyTerrain) - { - // so show the terrain selection - preselected_map = ""; - } - else if (!isAnyTerrain) - { - preselected_map = getASCIIFromCharString(terrn, 255); - } - // -------------------------------------------------------------------- - // network chat stuff - int colourNum = 0; - if (gEnv->network->getLocalUserData()) + LoadingWindow::getSingleton().hide(); + + new GUI_Multiplayer(); + GUI_Multiplayer::getSingleton().update(); + + String terrain_name = RoR::Networking::GetTerrainName(); + if (terrain_name != "any") { - colourNum = gEnv->network->getLocalUserData()->colournum; + preselected_map = terrain_name; } - ChatSystem* net_chat = ChatSystemFactory::getSingleton().createLocal(colourNum); + RoR::ChatSystem::SendStreamSetup(); - // TODO: separate console and chatbox. - - Application::GetGuiManager()->SetNetChat(net_chat); #ifdef USE_MUMBLE new MumbleIntegration(); #endif // USE_MUMBLE - - } + } #endif //SOCKETW - new BeamFactory(); - // ======================================================================== // Main loop (switches application states) // ======================================================================== @@ -427,7 +391,7 @@ void MainThread::Go() SoundScriptManager::getSingleton().trigStart(-1, SS_TRIG_MAIN_MENU); } - if (gEnv->network != nullptr || BSETTING("SkipMainMenu", false)) + if (gEnv->multiplayer || BSETTING("SkipMainMenu", false)) { // Multiplayer started from configurator / MainMenu disabled -> go directly to map selector (traditional behavior) RoR::Application::GetGuiManager()->getMainSelector()->Show(LT_Terrain); @@ -448,7 +412,7 @@ void MainThread::Go() // Simulation // ================================================================ - if (SetupGameplayLoop(enable_network, preselected_map)) + if (SetupGameplayLoop(preselected_map)) { previous_application_state = Application::STATE_SIMULATION; EnterGameplayLoop(); @@ -488,11 +452,9 @@ void MainThread::Go() RoR::Application::GetGuiManager()->getMainSelector()->~MainSelector(); #ifdef USE_SOCKETW - if (gEnv->network) + if (gEnv->multiplayer) { - gEnv->network->disconnect(); - delete gEnv->network; - gEnv->network = nullptr; + RoR::Networking::Disconnect(); } #endif //SOCKETW @@ -532,7 +494,7 @@ void MainThread::Go() } -bool MainThread::SetupGameplayLoop(bool enable_network, Ogre::String preselected_map) +bool MainThread::SetupGameplayLoop(Ogre::String preselected_map) { if (!m_base_resource_loaded) { @@ -609,29 +571,19 @@ bool MainThread::SetupGameplayLoop(bool enable_network, Ogre::String preselected new DustManager(); // setup particle manager singleton. TODO: Move under Application } - if (enable_network) + int colourNum = -1; + if (gEnv->multiplayer) { wchar_t tmp[255] = L""; UTFString format = _L("Press %ls to start chatting"); swprintf(tmp, 255, format.asWStr_c_str(), ANSI_TO_WCHAR(RoR::Application::GetInputEngine()->getKeyForCommand(EV_COMMON_ENTER_CHATMODE)).c_str()); Application::GetGuiManager()->pushMessageChatBox(UTFString(tmp)); - // NOTE: create player _AFTER_ network, important - int colourNum = 0; - if (gEnv->network->getLocalUserData()) - { - colourNum = gEnv->network->getLocalUserData()->colournum; - } - gEnv->player = (Character *)CharacterFactory::getSingleton().createLocal(colourNum); - } - else - { - gEnv->player = (Character *)CharacterFactory::getSingleton().createLocal(-1); - if (gEnv->player != nullptr) - { - gEnv->player->setVisible(false); - } + user_info_t info = RoR::Networking::GetLocalUserData(); + colourNum = info.colournum; } + // NOTE: create player _AFTER_ network, important + gEnv->player = CharacterFactory::getSingleton().createLocal(colourNum); // heathaze effect if (BSETTING("HeatHaze", false) && RoR::Application::GetContentManager()->isLoaded(ContentManager::ResourcePack::HEATHAZE.mask)) @@ -934,23 +886,12 @@ void MainThread::MainMenuLoopUpdate(float seconds_since_last_frame) return; } - // update GUI - RoR::Application::GetInputEngine()->Capture(); - -#ifdef USE_SOCKETW -#ifdef USE_MYGUI - // Update network gui every two seconds - if (gEnv->network) + if (gEnv->multiplayer) { - netcheck_gui_timer += seconds_since_last_frame; - if (netcheck_gui_timer > 2.0f) - { - GUI_Multiplayer::getSingleton().update(); - netcheck_gui_timer = 0.0f; - } + GUI_Multiplayer::getSingleton().update(); } -#endif // USE_MYGUI -#endif // USE_SOCKETW + + RoR::Application::GetInputEngine()->Capture(); MainMenuLoopUpdateEvents(seconds_since_last_frame); diff --git a/source/main/MainThread.h b/source/main/MainThread.h index 65fa710250..895bb3f9a5 100644 --- a/source/main/MainThread.h +++ b/source/main/MainThread.h @@ -70,7 +70,7 @@ class MainThread /** * @return True if everything was prepared OK and simulation may start. */ - bool SetupGameplayLoop(bool enable_network, Ogre::String preselected_map); + bool SetupGameplayLoop(Ogre::String preselected_map); void UnloadTerrain(); @@ -97,8 +97,6 @@ class MainThread Application::State m_application_state; bool m_base_resource_loaded; - float netcheck_gui_timer; - std::map isLoadedMap; }; diff --git a/source/main/gameplay/Character.cpp b/source/main/gameplay/Character.cpp index f3ed726c89..4903da2fe3 100644 --- a/source/main/gameplay/Character.cpp +++ b/source/main/gameplay/Character.cpp @@ -25,18 +25,16 @@ along with Rigs of Rods. If not, see . #include "Collisions.h" #include "IHeightFinder.h" #include "InputEngine.h" -#include "SurveyMapManager.h" -#include "SurveyMapEntity.h" #include "Network.h" -#include "NetworkStreamManager.h" #include "PlayerColours.h" +#include "SurveyMapEntity.h" +#include "SurveyMapManager.h" #include "TerrainManager.h" #include "Utils.h" #include "Water.h" using namespace Ogre; - unsigned int Character::characterCounter = 0; Character::Character(int source, unsigned int streamid, int colourNumber, bool remote) : @@ -46,7 +44,6 @@ Character::Character(int source, unsigned int streamid, int colourNumber, bool r , characterSpeed(2.0f) , characterVSpeed(0.0f) , colourNumber(colourNumber) - , last_net_time(0) , mAnimState(0) , mCamera(gEnv->mainCamera) , mCharacterNode(0) @@ -57,8 +54,8 @@ Character::Character(int source, unsigned int streamid, int colourNumber, bool r , networkUsername("") , physicsEnabled(true) , remote(remote) - , source(source) - , streamid(streamid) + , m_source_id(source) + , m_stream_id(streamid) , isCoupled(0) { myNumber = characterCounter++; @@ -80,7 +77,7 @@ Character::Character(int source, unsigned int streamid, int colourNumber, bool r mCharacterNode->setScale(0.02f, 0.02f, 0.02f); mAnimState = entity->getAllAnimationStates(); - if (gEnv->network) + if (gEnv->multiplayer) { sendStreamSetup(); } @@ -94,7 +91,7 @@ Character::Character(int source, unsigned int streamid, int colourNumber, bool r entity->setMaterialName("tracks/"+myName); #ifdef USE_SOCKETW - if (gEnv->network && (remote || !mHideOwnNetLabel)) + if (gEnv->multiplayer && (remote || !mHideOwnNetLabel)) { mMoveableText = new MovableText("netlabel-"+myName, ""); mCharacterNode->attachObject(mMoveableText); @@ -105,7 +102,7 @@ Character::Character(int source, unsigned int streamid, int colourNumber, bool r mMoveableText->setCharacterHeight(8); mMoveableText->setColor(ColourValue::Black); - updateNetLabel(); + updateLabels(); } #endif //SOCKETW } @@ -145,34 +142,30 @@ void Character::updateCharacterColour() PlayerColours::getSingleton().updateMaterial(colourNumber, matName, 2); } -void Character::setUID(int uid) +void Character::updateLabels() { - this->source = uid; -} - -void Character::updateNetLabel() -{ - if (!gEnv->network) return; + if (!gEnv->multiplayer) return; #ifdef USE_SOCKETW + user_info_t info; + if (remote) { - client_t *info = gEnv->network->getClientInfo(this->source); - if (!info) return; - if (tryConvertUTF(info->user.username).empty()) return; - this->colourNumber = info->user.colournum; - networkUsername = tryConvertUTF(info->user.username); - networkAuthLevel = info->user.authstatus; + if (!RoR::Networking::GetUserInfo(m_source_id, info)) + return; } else { - user_info_t *info = gEnv->network->getLocalUserData(); - if (!info) return; - if (String(info->username).empty()) return; - this->colourNumber = info->colournum; - networkUsername = tryConvertUTF(info->username); - networkAuthLevel = info->authstatus; + info = RoR::Networking::GetLocalUserData(); } + networkAuthLevel = info.authstatus; + + colourNumber = info.colournum; + updateCharacterColour(); + + if (String(info.username).empty()) return; + networkUsername = tryConvertUTF(info.username); + if (mMoveableText) { mMoveableText->setCaption(networkUsername); @@ -191,7 +184,7 @@ void Character::updateNetLabel() } */ - updateCharacterColour(); + updateNetLabelSize(); #endif //SOCKETW } @@ -497,7 +490,7 @@ void Character::update(float dt) } #ifdef USE_SOCKETW - if (gEnv->network && !remote) + if (gEnv->multiplayer && !remote) { sendStreamData(); } @@ -529,7 +522,7 @@ void Character::move(Vector3 offset) void Character::sendStreamSetup() { if (remote) return; - // new local stream + stream_register_t reg; memset(®, 0, sizeof(reg)); reg.status = 1; @@ -537,20 +530,17 @@ void Character::sendStreamSetup() reg.type = 1; reg.data[0] = 2; - NetworkStreamManager::getSingleton().addLocalStream(this, ®); + RoR::Networking::AddLocalStream(®, sizeof(stream_register_t)); + + m_source_id = reg.origin_sourceid; + m_stream_id = reg.origin_streamid; } void Character::sendStreamData() { - int t = netTimer.getMilliseconds(); - if (t-last_net_time < 100) - return; - // do not send position data if coupled to a truck already if (beamCoupling) return; - last_net_time = t; - pos_netdata_t data; data.command = CHARCMD_POSITION; data.posx = mCharacterNode->getPosition().x; @@ -563,27 +553,25 @@ void Character::sendStreamData() strncpy(data.animationMode, mLastAnimMode.c_str(), 254); data.animationTime = mAnimState->getAnimationState(mLastAnimMode)->getTimePosition(); - //LOG("sending character stream data: " + TOSTRING(net->getUserID()) + ":"+ TOSTRING(streamid)); - this->addPacket(MSG2_STREAM_DATA, sizeof(pos_netdata_t), (char*)&data); + //LOG("sending character stream data: " + TOSTRING(RoR::Networking::GetUID()) + ":" + TOSTRING(m_stream_id)); + RoR::Networking::AddPacket(m_stream_id, MSG2_STREAM_DATA, sizeof(pos_netdata_t), (char*)&data); } -void Character::receiveStreamData(unsigned int &type, int &source, unsigned int &streamid, char *buffer, unsigned int &len) +void Character::receiveStreamData(unsigned int &type, int &source, unsigned int &streamid, char *buffer) { - if (type == MSG2_STREAM_DATA && this->source == source && this->streamid == streamid) + if (type == MSG2_STREAM_DATA && m_source_id == source && m_stream_id == streamid) { header_netdata_t *header = (header_netdata_t *)buffer; if (header->command == CHARCMD_POSITION) { - // position pos_netdata_t *data = (pos_netdata_t *)buffer; Vector3 pos(data->posx, data->posy, data->posz); - this->setPosition(pos); + setPosition(pos); Quaternion rot(data->rotw, data->rotx, data->roty, data->rotz); mCharacterNode->setOrientation(rot); setAnimationMode(getASCIIFromCharString(data->animationMode, 255), data->animationTime); } else if (header->command == CHARCMD_ATTACH) { - // attach attach_netdata_t *data = (attach_netdata_t *)buffer; if (data->enabled) { @@ -600,6 +588,7 @@ void Character::receiveStreamData(unsigned int &type, int &source, unsigned int void Character::updateNetLabelSize() { if (!mMoveableText) return; + if (networkUsername.empty()) return; float camDist = (mCharacterNode->getPosition() - mCamera->getPosition()).length(); float h = std::max(9.0f, camDist * 1.2f); @@ -625,14 +614,14 @@ void Character::setBeamCoupling(bool enabled, Beam *truck /* = 0 */) { mMoveableText->setVisible(false); } - if (gEnv->network && !remote) + if (gEnv->multiplayer && !remote) { attach_netdata_t data; data.command = CHARCMD_ATTACH; data.enabled = true; - data.source_id = beamCoupling->getSourceID(); - data.stream_id = beamCoupling->getStreamID(); - this->addPacket(MSG2_STREAM_DATA, sizeof(attach_netdata_t), (char*)&data); + data.source_id = beamCoupling->m_source_id; + data.stream_id = beamCoupling->m_stream_id; + RoR::Networking::AddPacket(m_stream_id, MSG2_STREAM_DATA, sizeof(attach_netdata_t), (char*)&data); } // do not cast shadows inside of a truck @@ -654,12 +643,12 @@ void Character::setBeamCoupling(bool enabled, Beam *truck /* = 0 */) { mMoveableText->setVisible(true); } - if (gEnv->network && !remote) + if (gEnv->multiplayer && !remote) { attach_netdata_t data; data.command = CHARCMD_ATTACH; data.enabled = false; - this->addPacket(MSG2_STREAM_DATA, sizeof(attach_netdata_t), (char*)&data); + RoR::Networking::AddPacket(m_stream_id, MSG2_STREAM_DATA, sizeof(attach_netdata_t), (char*)&data); } // show character diff --git a/source/main/gameplay/Character.h b/source/main/gameplay/Character.h index ea913e38e7..b181199211 100644 --- a/source/main/gameplay/Character.h +++ b/source/main/gameplay/Character.h @@ -24,13 +24,9 @@ along with Rigs of Rods. If not, see . #include "RoRPrerequisites.h" #include "MovableText.h" -#include "Streamable.h" -class Character : public Streamable, public ZeroedMemoryAllocator +class Character : public ZeroedMemoryAllocator { - friend class CharacterFactory; - friend class Network; - public: Character(int source=-1, unsigned int streamid=0, int colourNumber=0, bool remote=true); @@ -40,8 +36,11 @@ class Character : public Streamable, public ZeroedMemoryAllocator Ogre::Vector3 getPosition(); bool getPhysicsEnabled() { return physicsEnabled; }; bool getVisible(); - int getUID() { return source; }; - + + void receiveStreamData(unsigned int &type, int &source, unsigned int &streamid, char *buffer); + + int getSourceID() { return m_source_id; }; + bool isRemote() { return remote; }; bool getBeamCoupling() { return isCoupled; }; @@ -58,13 +57,14 @@ class Character : public Streamable, public ZeroedMemoryAllocator void updateCharacterColour(); void updateCharacterRotation(); void updateMapIcon(); - void updateNetLabel(); + void updateLabels(); static unsigned int characterCounter; protected: void createMapEntity(); + void updateNetLabelSize(); Beam *beamCoupling; bool isCoupled; @@ -78,9 +78,11 @@ class Character : public Streamable, public ZeroedMemoryAllocator Ogre::Real characterSpeed; Ogre::Real characterVSpeed; + unsigned int myNumber; int colourNumber; int networkAuthLevel; - int source; + int m_stream_id; + int m_source_id; Ogre::AnimationStateSet *mAnimState; Ogre::Camera *mCamera; @@ -91,9 +93,6 @@ class Character : public Streamable, public ZeroedMemoryAllocator Ogre::UTFString networkUsername; Ogre::Vector3 mLastPosition; - unsigned int myNumber; - unsigned int streamid; - void setAnimationMode(Ogre::String mode, float time=0); // network stuff @@ -122,18 +121,12 @@ class Character : public Streamable, public ZeroedMemoryAllocator enum {CHARCMD_POSITION, CHARCMD_ATTACH}; - // overloaded from Streamable: - Ogre::Timer netTimer; - int last_net_time; + Ogre::Timer mNetTimer; bool mHideOwnNetLabel; - void receiveStreamData(unsigned int &type, int &source, unsigned int &streamid, char *buffer, unsigned int &len); void sendStreamData(); void sendStreamSetup(); - void setUID(int uid); - - void updateNetLabelSize(); }; #endif // __Character_H_ diff --git a/source/main/gameplay/CharacterFactory.cpp b/source/main/gameplay/CharacterFactory.cpp index 77e3dc3848..5b0a54611b 100644 --- a/source/main/gameplay/CharacterFactory.cpp +++ b/source/main/gameplay/CharacterFactory.cpp @@ -2,6 +2,7 @@ This source file is part of Rigs of Rods Copyright 2005-2012 Pierre-Michel Ricordel Copyright 2007-2012 Thomas Fischer +Copyright 2013-2016 Petr Ohlidal For more information, see http://www.rigsofrods.com/ @@ -18,126 +19,72 @@ You should have received a copy of the GNU General Public License along with Rigs of Rods. If not, see . */ -// created by Thomas Fischer thomas{AT}thomasfischer{DOT}biz, 7th of August 2009 - #include "CharacterFactory.h" #include "Character.h" -using namespace Ogre; - - -template<> CharacterFactory *StreamableFactory < CharacterFactory, Character >::_instance = 0; - -CharacterFactory::CharacterFactory() -{ -} - -CharacterFactory::~CharacterFactory() -{ -} - Character *CharacterFactory::createLocal(int playerColour) { Character *ch = new Character(-1, 0, playerColour, false); - - lockStreams(); - std::map < int, std::map < unsigned int, Character *> > &streamables = getStreams(); - streamables[-1][0] = ch; - unlockStreams(); - return ch; -} - -Character *CharacterFactory::createRemoteInstance(stream_reg_t *reg) -{ - // NO LOCKS IN HERE, already locked - //lockStreams(); - std::map < int, std::map < unsigned int, Character *> > &streamables = getStreams(); - - if (streamables[reg->sourceid][reg->streamid] != nullptr) - { - // TODO: Find out why this can happen. - return nullptr; - } - - LOG(" new character for " + TOSTRING(reg->sourceid) + ":" + TOSTRING(reg->streamid) + ", colour: " + TOSTRING(reg->colour)); - Character *ch = new Character(reg->sourceid, reg->streamid, reg->colour, true); - - streamables[reg->sourceid][reg->streamid] = ch; - //unlockStreams(); return ch; } -void CharacterFactory::localUserAttributesChanged(int newid) +void CharacterFactory::createRemoteInstance(int sourceid, int streamid) { - lockStreams(); - std::map < int, std::map < unsigned int, Character *> > &streamables = getStreams(); - std::map < int, std::map < unsigned int, Character *> >::iterator it1; - std::map < unsigned int, Character *>::iterator it2; + user_info_t info; + RoR::Networking::GetUserInfo(sourceid, info); + int colour = info.colournum; - if (streamables.find(-1) == streamables.end()) - { - unlockStreams(); - return; - } + LOG(" new character for " + TOSTRING(sourceid) + ":" + TOSTRING(streamid) + ", colour: " + TOSTRING(colour)); - Character *c = streamables[-1][0]; - streamables[newid][0] = streamables[-1][0]; // add alias :) - c->setUID(newid); - c->updateNetLabel(); - unlockStreams(); + m_characters.push_back(std::unique_ptr(new Character(sourceid, streamid, colour, true))); } -void CharacterFactory::netUserAttributesChanged(int source, int streamid) +void CharacterFactory::removeStreamSource(int sourceid) { - lockStreams(); - std::map < int, std::map < unsigned int, Character *> > &streamables = getStreams(); - std::map < int, std::map < unsigned int, Character *> >::iterator it1; - std::map < unsigned int, Character *>::iterator it2; - - for (it1=streamables.begin(); it1!=streamables.end();it1++) + for (auto it = m_characters.begin(); it != m_characters.end(); it++) { - for (it2=it1->second.begin(); it2!=it1->second.end();it2++) + if ((*it)->getSourceID() == sourceid) { - Character *c = dynamic_cast(it2->second); - if (c) c->updateNetLabel(); + (*it).reset(); + m_characters.erase(it); + break; } } - unlockStreams(); } -void CharacterFactory::updateCharacters(float dt) +void CharacterFactory::update(float dt) { - lockStreams(); - std::map < int, std::map < unsigned int, Character *> > &streamables = getStreams(); - std::map < int, std::map < unsigned int, Character *> >::iterator it1; - std::map < unsigned int, Character *>::iterator it2; + gEnv->player->update(dt); + gEnv->player->updateLabels(); - for (it1=streamables.begin(); it1!=streamables.end();it1++) + for (auto& c : m_characters) { - for (it2=it1->second.begin(); it2!=it1->second.end();it2++) - { - Character *c = dynamic_cast(it2->second); - if (c) c->update(dt); - } + c->update(dt); + c->updateLabels(); } - unlockStreams(); } -void CharacterFactory::updateLabels() +void CharacterFactory::handleStreamData(std::vector packet_buffer) { - lockStreams(); - std::map < int, std::map < unsigned int, Character *> > &streamables = getStreams(); - std::map < int, std::map < unsigned int, Character *> >::iterator it1; - std::map < unsigned int, Character *>::iterator it2; - - for (it1=streamables.begin(); it1!=streamables.end();it1++) + for (auto packet : packet_buffer) { - for (it2=it1->second.begin(); it2!=it1->second.end();it2++) + if (packet.header.command == MSG2_STREAM_REGISTER) + { + stream_register_t *reg = (stream_register_t *)packet.buffer; + if (reg->type == 1) + { + createRemoteInstance(packet.header.source, packet.header.streamid); + } + } else if (packet.header.command == MSG2_USER_LEAVE) + { + removeStreamSource(packet.header.source); + } else { - Character *c = dynamic_cast(it2->second); - if (c) c->updateNetLabelSize(); + for (auto& c : m_characters) + { + c->receiveStreamData(packet.header.command, packet.header.source, packet.header.streamid, packet.buffer); + } } } - unlockStreams(); } diff --git a/source/main/gameplay/CharacterFactory.h b/source/main/gameplay/CharacterFactory.h index 4675f34a74..7f70e57407 100644 --- a/source/main/gameplay/CharacterFactory.h +++ b/source/main/gameplay/CharacterFactory.h @@ -2,6 +2,7 @@ This source file is part of Rigs of Rods Copyright 2005-2012 Pierre-Michel Ricordel Copyright 2007-2012 Thomas Fischer +Copyright 2013-2016 Petr Ohlidal For more information, see http://www.rigsofrods.com/ @@ -18,8 +19,6 @@ You should have received a copy of the GNU General Public License along with Rigs of Rods. If not, see . */ -// created by Thomas Fischer thomas{AT}thomasfischer{DOT}biz, 17th of August 2009 - #pragma once #ifndef __CharacterFactory_H_ #define __CharacterFactory_H_ @@ -27,28 +26,26 @@ along with Rigs of Rods. If not, see . #include "RoRPrerequisites.h" #include "Character.h" -#include "StreamableFactory.h" +#include "Network.h" +#include "Singleton.h" -class CharacterFactory : public StreamableFactory < CharacterFactory, Character >, public ZeroedMemoryAllocator -{ - friend class Network; +#include +class CharacterFactory : public RoRSingleton< CharacterFactory >, public ZeroedMemoryAllocator +{ public: - CharacterFactory(); - ~CharacterFactory(); - Character *createLocal(int playerColour); - Character *createRemoteInstance(stream_reg_t *reg); - void updateCharacters(float dt); - void updateLabels(); + void update(float dt); + void handleStreamData(std::vector packet); + +private: -protected: + std::vector> m_characters; - // functions used by friends - void netUserAttributesChanged(int source, int streamid); - void localUserAttributesChanged(int newid); + void createRemoteInstance(int sourceid, int streamid); + void removeStreamSource(int sourceid); }; #endif // __CharacterFactory_H_ diff --git a/source/main/gameplay/ChatSystem.cpp b/source/main/gameplay/ChatSystem.cpp index 77492268ba..7d09213e5e 100644 --- a/source/main/gameplay/ChatSystem.cpp +++ b/source/main/gameplay/ChatSystem.cpp @@ -1,328 +1,186 @@ -/* -This source file is part of Rigs of Rods -Copyright 2005-2012 Pierre-Michel Ricordel -Copyright 2007-2012 Thomas Fischer - -For more information, see http://www.rigsofrods.com/ - -Rigs of Rods is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3, as -published by the Free Software Foundation. - -Rigs of Rods 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 Rigs of Rods. If not, see . -*/ -#include "ChatSystem.h" - -#include "Application.h" -#include "Console.h" -#include "GUIManager.h" -#include "Language.h" -#include "Network.h" -#include "PlayerColours.h" -#include "Utils.h" - -#ifdef USE_MYGUI -#include "GUIMp.h" -#endif // USE_MYGUI - -using namespace Ogre; -using namespace RoR; - -/////////////////////////////////// -// ChatSystemFactory - -template<> ChatSystemFactory *StreamableFactory < ChatSystemFactory, ChatSystem >::_instance = 0; - -ChatSystemFactory::ChatSystemFactory() -{ -} - -ChatSystemFactory::~ChatSystemFactory() -{ -} - -ChatSystem *ChatSystemFactory::createLocal(int playerColour) -{ - lockStreams(); - std::map < int, std::map < unsigned int, ChatSystem *> > &streamables = getStreams(); - ChatSystem *ch = new ChatSystem(-1, 0, playerColour, false); - streamables[-1][0] = ch; - unlockStreams(); - return ch; -} - -ChatSystem *ChatSystemFactory::createRemoteInstance(stream_reg_t *reg) -{ - // NO LOCKS IN HERE, already locked - - LOG(" new chat system for " + TOSTRING(reg->sourceid) + ":" + TOSTRING(reg->streamid) + ", colour: " + TOSTRING(reg->colour)); - ChatSystem *ch = new ChatSystem(reg->sourceid, reg->streamid, reg->colour, true); - - // already locked - //lockStreams(); - std::map < int, std::map < unsigned int, ChatSystem *> > &streamables = getStreams(); - streamables[reg->sourceid][reg->streamid] = ch; - //unlockStreams(); - return ch; -} - -bool ChatSystemFactory::syncRemoteStreams() -{ - // we override this here, so we know if something changed and could update the player list - bool changes = StreamableFactory ::syncRemoteStreams(); - -#ifdef USE_MYGUI -#ifdef USE_SOCKETW - if (changes) - GUI_Multiplayer::getSingleton().update(); -#endif // USE_SOCKETW -#endif // USE_MYGUI - return changes; -} - -ChatSystem *ChatSystemFactory::getFirstChatSystem() -{ - lockStreams(); - std::map < int, std::map < unsigned int, ChatSystem *> > &streamables = getStreams(); - if (streamables.empty() || streamables.begin()->second.empty()) return 0; - ChatSystem *r = streamables.begin()->second.begin()->second; - unlockStreams(); - return r; -} - -/////////////////////////////////// -// ChatSystem - -const UTFString ChatSystem::commandColour = U("#00FF00"); -const UTFString ChatSystem::normalColour = U("#FFFFFF"); -const UTFString ChatSystem::whisperColour = U("#FFCC00"); -const UTFString ChatSystem::scriptCommandColour = U("#0099FF"); - - - - -ChatSystem::ChatSystem(int source, unsigned int streamid, int colourNumber, bool remote) : - source(source), - streamid(streamid), - colourNumber(colourNumber), - remote(remote), - username("unknown"), - mNickColour("") -{ - sendStreamSetup(); -#ifdef USE_SOCKETW - if (remote) - { - client_t *c = gEnv->network->getClientInfo(source); - if (c) - { - username = getColouredName(*c); - } - -#ifdef USE_MYGUI - String msg = username + commandColour + _L(" joined the game"); - //RoR::Application::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_NETWORK, Console::CONSOLE_LOGMESSAGE, msg, "user_add.png"); - //RoR::Application::GetGuiManager()->PushNotification("Server info:", msg); - RoR::Application::GetGuiManager()->pushMessageChatBox(msg); -#endif //USE_MYGUI - } -#endif //SOCKETW -} - -ChatSystem::~ChatSystem() -{ -#ifdef USE_MYGUI - if (remote) - { - String msg = username + commandColour + _L(" left the game"); - //RoR::Application::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_NETWORK, Console::CONSOLE_LOGMESSAGE, msg, "user_delete.png"); - //RoR::Application::GetGuiManager()->PushNotification("Server info:", msg); - RoR::Application::GetGuiManager()->pushMessageChatBox(msg); - } -#endif //USE_MYGUI -} - -void ChatSystem::sendStreamSetup() -{ - if (remote) return; - - stream_register_t reg; - reg.status = 1; - strcpy(reg.name, "chat"); - reg.type = 3; - NetworkStreamManager::getSingleton().addLocalStream(this, ®); -} - -void ChatSystem::sendStreamData() -{ - // we send data synchronously to prevent lag -} - -void ChatSystem::receiveStreamData(unsigned int &type, int &source, unsigned int &streamid, char *buffer, unsigned int &len) -{ -#ifdef USE_MYGUI - if (type == MSG2_UTF_CHAT) - { - // some chat code - if (source == -1) - { - // server said something - UTFString msg = tryConvertUTF(buffer); - RoR::Application::GetGuiManager()->pushMessageChatBox(msg); - } else if (source == (int)this->source && (int)streamid == this->streamid) - { - UTFString msg = username + normalColour + ": " + tryConvertUTF(buffer); - RoR::Application::GetGuiManager()->pushMessageChatBox(msg); - } -#ifdef USE_SOCKETW - else if (source == (int)gEnv->network->getUID()) - { - // our message bounced back :D - UTFString msg = gEnv->network->getNickname(true) + normalColour + ": " + tryConvertUTF(buffer); - RoR::Application::GetGuiManager()->pushMessageChatBox(msg); - } -#endif // USE_SOCKETW - } else if (type == MSG2_UTF_PRIVCHAT) - { - // some private chat message - if (source == -1) - { - // server said something - String msg = whisperColour + _L(" [whispered] ") + normalColour + tryConvertUTF(buffer); - RoR::Application::GetGuiManager()->pushMessageChatBox(msg); - } else if (source == (int)this->source && (int)streamid == this->streamid) - { - UTFString msg = username + _L(" [whispered] ") + normalColour + ": " + tryConvertUTF(buffer); - RoR::Application::GetGuiManager()->pushMessageChatBox(msg); - } - } -#endif //USE_MYGUI -} - -void ChatSystem::sendChat(UTFString chatline) -{ - const char *utf8_line = chatline.asUTF8_c_str(); - this->addPacket(MSG2_UTF_CHAT, (unsigned int)strlen(utf8_line), (char *)utf8_line); -} - -int ChatSystem::getChatUserNames(std::vector &names) -{ -#ifdef USE_SOCKETW - client_t c[MAX_PEERS]; - if (gEnv->network->getClientInfos(c)) return 0; - - for (int i = 0; i < MAX_PEERS; i++) - { - names.push_back(c[i].user.username); - } - return (int)names.size(); -#else - return 0; -#endif // USE_SOCKETW -} - -void ChatSystem::sendPrivateChat(UTFString targetUsername, UTFString chatline) -{ -#ifdef USE_SOCKETW - // first: find id to username: - client_t c[MAX_PEERS]; - if (gEnv->network->getClientInfos(c)) - return; - int target_uid = -1, target_index = -1; - for (int i = 0; i < MAX_PEERS; i++) - { - if (UTFString(c[i].user.username) == targetUsername) - { - // found it :) - target_uid = c[i].user.uniqueid; - target_index = i; - break; - } - } - - if (target_uid < 0) - { -#ifdef USE_MYGUI - RoR::Application::GetGuiManager()->pushMessageChatBox(ChatSystem::commandColour + _L("user not found: ") + targetUsername); - //RoR::Application::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE, ChatSystem::commandColour + _L("user not found: ") + targetUsername, "error.png"); -#endif // USE_MYGUI - return; - } - - sendPrivateChat(target_uid, chatline, getColouredName(c[target_index])); -#endif // USE_SOCKETW -} - - - -void ChatSystem::sendPrivateChat(int target_uid, UTFString chatline, UTFString username) -{ -#ifdef USE_SOCKETW - char buffer[MAX_MESSAGE_LENGTH] = ""; - - const char *chat_msg = (const char *)chatline.asUTF8_c_str(); - - // format: int of UID, then chat message - memcpy(buffer, &target_uid, sizeof(int)); - strncpy(buffer + sizeof(int), chat_msg, MAX_MESSAGE_LENGTH - sizeof(int)); - - size_t len = sizeof(int) + chatline.size() * sizeof(wchar_t); - buffer[len] = 0; - - this->addPacket(MSG2_UTF_PRIVCHAT, (unsigned int)len, buffer); - - if (username.empty()) - { - client_t *c = gEnv->network->getClientInfo(target_uid); - if (c) username = getColouredName(*c); - } - - // add local visual -#ifdef USE_MYGUI - UTFString nmsg = gEnv->network->getNickname(true) + normalColour + whisperColour + _L(" [whispered to ") + normalColour + username + whisperColour + "]" + normalColour + ": " + chatline; - //RoR::Application::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_NETWORK, Console::CONSOLE_LOGMESSAGE, nmsg, "script_key.png"); - RoR::Application::GetGuiManager()->pushMessageChatBox(nmsg); -#endif // USE_MYGUI -#endif // USE_SOCKETW -} - -UTFString ChatSystem::getColouredName(client_t &c) -{ - return getColouredName(c.user); -} - -UTFString ChatSystem::getColouredName(user_info_t &u) -{ - return ChatSystem::getColouredName(UTFString(u.username), u.authstatus, u.colournum); -} - -UTFString ChatSystem::getColouredName(UTFString nick, int auth, int colourNumber) -{ - ColourValue col_val = PlayerColours::getSingleton().getColour(colourNumber); - char tmp[255] = ""; - sprintf(tmp, "#%02X%02X%02X", (unsigned int)(col_val.r * 255.0f), (unsigned int)(col_val.g * 255.0f), (unsigned int)(col_val.b * 255.0f)); - - // replace # with X in nickname so the user cannot fake the colour - for (unsigned int i=0; i. +*/ +#include "ChatSystem.h" + +#include "Application.h" +#include "GUIManager.h" +#include "Language.h" +#include "PlayerColours.h" +#include "Utils.h" + +namespace RoR { +namespace ChatSystem { + +static int m_stream_id; + +void SendStreamSetup() +{ + stream_register_t reg; + memset(®, 0, sizeof(stream_register_t)); + reg.status = 1; + strcpy(reg.name, "chat"); + reg.type = 3; + + RoR::Networking::AddLocalStream(®, sizeof(stream_register_t)); + + m_stream_id = reg.origin_streamid; +} + +Ogre::UTFString GetColouredName(Ogre::UTFString nick, int colour_number) +{ + Ogre::ColourValue col_val = PlayerColours::getSingleton().getColour(colour_number); + char tmp[255] = {0}; + sprintf(tmp, "#%02X%02X%02X", (unsigned int)(col_val.r * 255.0f), (unsigned int)(col_val.g * 255.0f), (unsigned int)(col_val.b * 255.0f)); + + // replace # with X in nickname so the user cannot fake the colour + for (unsigned int i=0; ipushMessageChatBox(msg); +#endif //USE_MYGUI +#endif // USE_SOCKETW +} + +void HandleStreamData(std::vector packet_buffer) +{ + for (auto packet : packet_buffer) + { + ReceiveStreamData(packet.header.command, packet.header.source, packet.buffer); + } +} + +void SendChat(Ogre::UTFString chatline) +{ +#ifdef USE_SOCKETW + const char *utf8_line = chatline.asUTF8_c_str(); + RoR::Networking::AddPacket(m_stream_id, MSG2_UTF_CHAT, (unsigned int)strlen(utf8_line), (char *)utf8_line); +#endif // USE_SOCKETW +} + +void SendPrivateChat(int target_uid, Ogre::UTFString chatline, Ogre::UTFString target_username) +{ +#ifdef USE_SOCKETW + char buffer[MAX_MESSAGE_LENGTH] = {0}; + + const char *chat_msg = (const char *)chatline.asUTF8_c_str(); + + // format: int of UID, then chat message + memcpy(buffer, &target_uid, sizeof(int)); + strncpy(buffer + sizeof(int), chat_msg, MAX_MESSAGE_LENGTH - sizeof(int)); + + size_t len = sizeof(int) + chatline.size() * sizeof(wchar_t); + buffer[len] = 0; + + RoR::Networking::AddPacket(m_stream_id, MSG2_UTF_PRIVCHAT, (unsigned int)len, buffer); + + if (target_username.empty()) + { + user_info_t user; + if (RoR::Networking::GetUserInfo(target_uid, user)) + { + target_username = GetColouredName(user.username, user.colournum); + } + } + + // add local visual + Ogre::UTFString local_username = GetColouredName(RoR::Networking::GetUsername(), RoR::Networking::GetUserColor()); + Ogre::UTFString nmsg = local_username + RoR::Color::WhisperColour + _L(" [whispered to ") + RoR::Color::NormalColour + target_username + RoR::Color::WhisperColour + "]" + RoR::Color::NormalColour + ": " + chatline; +#ifdef USE_MYGUI + RoR::Application::GetGuiManager()->pushMessageChatBox(nmsg); +#endif // USE_MYGUI +#endif // USE_SOCKETW +} + +void SendPrivateChat(Ogre::UTFString target_username, Ogre::UTFString chatline) +{ +#ifdef USE_SOCKETW + // first: find id to username: + user_info_t target_user; + std::vector users = RoR::Networking::GetUserInfos(); + + bool found_target = false; + for (auto user : users) + { + if (Ogre::UTFString(user.username) == target_username) + { + found_target = true; + target_user = user; + break; + } + } + + if (!found_target) + { +#ifdef USE_MYGUI + RoR::Application::GetGuiManager()->pushMessageChatBox(RoR::Color::CommandColour + _L("user not found: ") + target_username); +#endif // USE_MYGUI + return; + } + + SendPrivateChat(target_user.uniqueid, chatline, GetColouredName(target_user.username, target_user.colournum)); +#endif // USE_SOCKETW +} + +} // namespace ChatSystem +} // namespace RoR diff --git a/source/main/gameplay/ChatSystem.h b/source/main/gameplay/ChatSystem.h index 6114b16181..01d9d07669 100644 --- a/source/main/gameplay/ChatSystem.h +++ b/source/main/gameplay/ChatSystem.h @@ -1,85 +1,44 @@ -/* -This source file is part of Rigs of Rods -Copyright 2005-2012 Pierre-Michel Ricordel -Copyright 2007-2012 Thomas Fischer - -For more information, see http://www.rigsofrods.com/ - -Rigs of Rods is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3, as -published by the Free Software Foundation. - -Rigs of Rods 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 Rigs of Rods. If not, see . -*/ -#pragma once -#ifndef __ChatSystem_H_ -#define __ChatSystem_H_ - -#include "RoRPrerequisites.h" - -#include "Streamable.h" -#include "StreamableFactory.h" - -class ChatSystem : public Streamable, public ZeroedMemoryAllocator -{ - friend class ChatSystemFactory; - friend class Network; - -public: - - ChatSystem(int source=-1, unsigned int streamid=0, int colourNumber=0, bool remote=true); - ~ChatSystem(); - - void sendChat(Ogre::UTFString chatline); - void sendPrivateChat(Ogre::UTFString targetUsername, Ogre::UTFString chatline); - void sendPrivateChat(int targetUID, Ogre::UTFString chatline, Ogre::UTFString username = ""); - - static Ogre::UTFString getColouredName(Ogre::UTFString nick, int auth, int colourNumber); - static Ogre::UTFString getColouredName(client_t &c); - static Ogre::UTFString getColouredName(user_info_t &u); - - int getChatUserNames(std::vector &names); - - static const Ogre::UTFString commandColour, normalColour, whisperColour, scriptCommandColour; - -protected: - int source; - int streamid; - int colourNumber; - bool remote; - Ogre::UTFString username; - Ogre::String mNickColour; - void sendStreamSetup(); - void sendStreamData(); - void receiveStreamData(unsigned int &type, int &source, unsigned int &streamid, char *buffer, unsigned int &len); -}; - -class ChatSystemFactory : public StreamableFactory < ChatSystemFactory, ChatSystem >, public ZeroedMemoryAllocator -{ - friend class Network; - -public: - - ChatSystemFactory(); - ~ChatSystemFactory(); - - ChatSystem *createLocal(int playerColour); - ChatSystem *createRemoteInstance(stream_reg_t *reg); - - ChatSystem *getFirstChatSystem(); - -protected: - // functions used by friends - void netUserAttributesChanged(int source, int streamid) {}; - void localUserAttributesChanged(int newid) {}; - - bool syncRemoteStreams(); -}; - -#endif // __ChatSystem_H_ +/* + This source file is part of Rigs of Rods + Copyright 2005-2012 Pierre-Michel Ricordel + Copyright 2007-2012 Thomas Fischer + Copyright 2013-2016 Petr Ohlidal + + For more information, see http://www.rigsofrods.com/ + + Rigs of Rods is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License version 3, as + published by the Free Software Foundation. + + Rigs of Rods 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 Rigs of Rods. If not, see . +*/ +#pragma once +#ifndef __ChatSystem_H_ +#define __ChatSystem_H_ + +#include "RoRPrerequisites.h" + +#include "Network.h" + +namespace RoR { +namespace ChatSystem { + +void SendChat(Ogre::UTFString chatline); +void SendPrivateChat(Ogre::UTFString target_username, Ogre::UTFString chatline); + +void SendStreamSetup(); + +void HandleStreamData(std::vector packet); + +Ogre::UTFString GetColouredName(Ogre::UTFString nick, int colour_number); + +} // namespace Chatsystem +} // namespace RoR + +#endif // __ChatSystem_H_ diff --git a/source/main/gameplay/RoRFrameListener.cpp b/source/main/gameplay/RoRFrameListener.cpp index 9fa4c77d3a..0af89a912c 100644 --- a/source/main/gameplay/RoRFrameListener.cpp +++ b/source/main/gameplay/RoRFrameListener.cpp @@ -44,7 +44,6 @@ along with Rigs of Rods. If not, see . #include "Language.h" #include "MainThread.h" #include "MumbleIntegration.h" -#include "Network.h" #include "OgreSubsystem.h" #include "OutProtocol.h" #include "OverlayWrapper.h" @@ -152,6 +151,7 @@ RoRFrameListener::RoRFrameListener() : m_last_simulation_speed(0.1f), m_last_skin_selection(nullptr), m_loading_state(NONE_LOADED), + m_netcheck_gui_timer(0.0f), m_pressure_pressed(false), m_race_bestlap_time(0), m_race_in_progress(false), @@ -288,20 +288,12 @@ bool RoRFrameListener::updateEvents(float dt) #endif //USE_MYGUI #ifdef USE_MYGUI - if (RoR::Application::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_ENTER_CHATMODE, 0.5f) && !m_hide_gui && gEnv->network) + if (RoR::Application::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_ENTER_CHATMODE, 0.5f) && !m_hide_gui && gEnv->multiplayer) { RoR::Application::GetInputEngine()->resetKeys(); RoR::Application::GetGuiManager()->ShowChatBox(); } #endif //USE_MYGUI - // update characters - if (m_loading_state==ALL_LOADED && gEnv->network) - { - CharacterFactory::getSingleton().updateCharacters(dt); - } else if (m_loading_state==ALL_LOADED && !gEnv->network) - { - gEnv->player->update(dt); - } if (RoR::Application::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_SCREENSHOT, 0.25f)) { @@ -509,8 +501,9 @@ bool RoRFrameListener::updateEvents(float dt) } - if (m_loading_state==ALL_LOADED) + if (m_loading_state == ALL_LOADED) { + CharacterFactory::getSingleton().update(dt); if (gEnv->cameraManager && !gEnv->cameraManager->gameControlsLocked()) { if (!curr_truck) @@ -887,7 +880,7 @@ bool RoRFrameListener::updateEvents(float dt) RoR::Application::GetOverlayWrapper()->showPressureOverlay(false); } - if (RoR::Application::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_RESCUE_TRUCK, 0.5f) && !gEnv->network && curr_truck->driveable != AIRPLANE) + if (RoR::Application::GetInputEngine()->getEventBoolValueBounce(EV_COMMON_RESCUE_TRUCK, 0.5f) && !gEnv->multiplayer && curr_truck->driveable != AIRPLANE) { if (!BeamFactory::getSingleton().enterRescueTruck()) { @@ -1245,11 +1238,17 @@ bool RoRFrameListener::frameStarted(const FrameEvent& evt) BeamFactory::getSingleton().SyncWithSimThread(); #ifdef USE_SOCKETW - if (gEnv->network) + if (gEnv->multiplayer) { - // process all packets and streams received - NetworkStreamManager::getSingleton().update(); - CharacterFactory::getSingleton().updateLabels(); + RoR::Networking::HandleStreamData(); +#ifdef USE_MYGUI + m_netcheck_gui_timer += dt; + if (m_netcheck_gui_timer > 2.0f) + { + GUI_Multiplayer::getSingleton().update(); + m_netcheck_gui_timer = 0.0f; + } +#endif // USE_MYGUI } #endif //SOCKETW @@ -1268,7 +1267,7 @@ bool RoRFrameListener::frameStarted(const FrameEvent& evt) } // update network gui if required, at most every 2 seconds - if (gEnv->network) + if (gEnv->multiplayer) { // now update mumble 3d audio things #ifdef USE_MUMBLE @@ -1453,7 +1452,7 @@ bool RoRFrameListener::frameStarted(const FrameEvent& evt) if (!m_is_sim_paused) { BeamFactory::getSingleton().joinFlexbodyTasks(); // Waits until all flexbody tasks are finished - BeamFactory::getSingleton().calcPhysics(dt); + BeamFactory::getSingleton().update(dt); BeamFactory::getSingleton().updateFlexbodiesFinal(); // Updates the harware buffers } } @@ -1478,7 +1477,7 @@ void RoRFrameListener::showLoad(int type, const Ogre::String &instance, const Og Beam **trucks = BeamFactory::getSingleton().getTrucks(); // first, test if the place if clear, BUT NOT IN MULTIPLAYER - if (!gEnv->network) + if (!gEnv->multiplayer) { collision_box_t *spawnbox = gEnv->collisions->getBox(instance, box); for (int t=0; t < free_truck; t++) @@ -1561,7 +1560,8 @@ void RoRFrameListener::windowMoved(Ogre::RenderWindow* rw) void RoRFrameListener::windowFocusChange(Ogre::RenderWindow* rw) { - LOG("*** windowFocusChange"); + // Too verbose + //LOG("*** windowFocusChange"); RoR::Application::GetInputEngine()->resetKeys(); } @@ -1573,7 +1573,7 @@ void RoRFrameListener::hideGUI(bool hidden) if (curr_truck && curr_truck->getReplay()) curr_truck->getReplay()->setHidden(hidden); #ifdef USE_SOCKETW - if (gEnv->network) GUI_Multiplayer::getSingleton().setVisible(!hidden); + if (gEnv->multiplayer) GUI_Multiplayer::getSingleton().setVisible(!hidden); #endif // USE_SOCKETW if (hidden) diff --git a/source/main/gameplay/RoRFrameListener.h b/source/main/gameplay/RoRFrameListener.h index e5d5ee898f..7a7efabcf1 100644 --- a/source/main/gameplay/RoRFrameListener.h +++ b/source/main/gameplay/RoRFrameListener.h @@ -100,6 +100,8 @@ class RoRFrameListener: public Ogre::FrameListener, public Ogre::WindowEventList bool m_truck_info_on; bool m_pressure_pressed; bool m_is_sim_paused; + + float m_netcheck_gui_timer; collision_box_t *m_reload_box; diff --git a/source/main/gfx/PreviewRenderer.cpp b/source/main/gfx/PreviewRenderer.cpp index a0042df76d..d280abc9ab 100644 --- a/source/main/gfx/PreviewRenderer.cpp +++ b/source/main/gfx/PreviewRenderer.cpp @@ -97,7 +97,7 @@ void PreviewRenderer::render() while(time < 10) { // run the engine for ten virtual seconds - BeamFactory::getSingleton().calcPhysics(dt); + BeamFactory::getSingleton().update(dt); time += dt; } BeamFactory::getSingleton().updateVisual(dt); diff --git a/source/main/gfx/Water.cpp b/source/main/gfx/Water.cpp index 0196da09b6..627423eae4 100644 --- a/source/main/gfx/Water.cpp +++ b/source/main/gfx/Water.cpp @@ -112,7 +112,7 @@ Water::Water(const Ogre::ConfigFile &mTerrainConfig) : mScale = 1.5f; // disable waves in multiplayer - if(gEnv->network) + if (gEnv->multiplayer) haswaves = false; // and the type diff --git a/source/main/gui/Console.cpp b/source/main/gui/Console.cpp index 66e9830f75..dacda85198 100644 --- a/source/main/gui/Console.cpp +++ b/source/main/gui/Console.cpp @@ -27,7 +27,6 @@ #include "Beam.h" #include "BeamFactory.h" #include "Character.h" -#include "ChatSystem.h" #include "GUIManager.h" #include "GUIMenu.h" #include "IHeightFinder.h" @@ -331,7 +330,7 @@ void Console::eventCommandAccept(MyGUI::Edit* _sender) { if (args.size() != 4) { - putMessage(CONSOLE_MSGTYPE_INFO, CONSOLE_HELP, ChatSystem::commandColour + _L("usage: /goto x y z"), "information.png"); + putMessage(CONSOLE_MSGTYPE_INFO, CONSOLE_HELP, RoR::Color::CommandColour + _L("usage: /goto x y z"), "information.png"); return; } @@ -398,7 +397,7 @@ void Console::eventCommandAccept(MyGUI::Edit* _sender) StringUtil::trim(command); if (command.empty()) return; - String nmsg = ChatSystem::scriptCommandColour + ">>> " + ChatSystem::normalColour + command; + String nmsg = RoR::Color::ScriptCommandColour + ">>> " + RoR::Color::NormalColour + command; putMessage(CONSOLE_MSGTYPE_SCRIPT, CONSOLE_LOCAL_SCRIPT, nmsg, "script_go.png"); int res = ScriptEngine::getSingleton().executeString(command); return; diff --git a/source/main/gui/GUIManager.cpp b/source/main/gui/GUIManager.cpp index daccbd1d04..068def4a53 100644 --- a/source/main/gui/GUIManager.cpp +++ b/source/main/gui/GUIManager.cpp @@ -473,14 +473,6 @@ void GUIManager::pushMessageChatBox(Ogre::String txt) m_gui_ChatBox->pushMsg(txt); } -void GUIManager::SetNetChat(ChatSystem *c) -{ - if (m_gui_ChatBox.get() == nullptr) - m_gui_ChatBox = std::unique_ptr(new GUI::GameChatBox()); - - m_gui_ChatBox->setNetChat(c); -} - void GUIManager::ShowVehicleDescription() { if (m_vehicle_description.get() == nullptr) diff --git a/source/main/gui/GUIManager.h b/source/main/gui/GUIManager.h index ac57b7029a..8d60e6119a 100644 --- a/source/main/gui/GUIManager.h +++ b/source/main/gui/GUIManager.h @@ -104,7 +104,6 @@ class GUIManager : void ShowChatBox(); void pushMessageChatBox(Ogre::String txt); - void SetNetChat(ChatSystem *c); bool GetPauseMenuVisible(); diff --git a/source/main/gui/GUIMenu.cpp b/source/main/gui/GUIMenu.cpp index 6aee3af38e..c76dc5ab28 100644 --- a/source/main/gui/GUIMenu.cpp +++ b/source/main/gui/GUIMenu.cpp @@ -184,7 +184,7 @@ GUI_MainMenu::~GUI_MainMenu() UTFString GUI_MainMenu::getUserString(user_info_t &user, int num_vehicles) { - UTFString tmp = ChatSystem::getColouredName(user); + UTFString tmp = RoR::ChatSystem::GetColouredName(user.username, user.colournum); tmp = tmp + U(": "); @@ -227,7 +227,7 @@ void GUI_MainMenu::addUserToMenu(user_info_t &user) { if (!trucks[j]) continue; - if (trucks[j]->getSourceID() == user.uniqueid) + if (trucks[j]->m_source_id == user.uniqueid) { // match, found truck :) matches.push_back(j); @@ -258,7 +258,7 @@ void GUI_MainMenu::vehiclesListUpdate() { m_vehicles_menu_widget->removeAllItems(); - if (!gEnv->network) + if (!gEnv->multiplayer) { // single player mode: add vehicles simply, no users int numTrucks = BeamFactory::getSingleton().getTruckCount(); @@ -280,18 +280,13 @@ void GUI_MainMenu::vehiclesListUpdate() { // sort the list according to the network users - // add self first - user_info_t *local_user = gEnv->network->getLocalUserData(); - addUserToMenu(*local_user); + user_info_t local_user = RoR::Networking::GetLocalUserData(); + addUserToMenu(local_user); - // get network clients - client_t c[MAX_PEERS]; - gEnv->network->getClientInfos(c); - // iterate over them - for (int i = 0; i < MAX_PEERS; i++) + auto users = RoR::Networking::GetUserInfos(); + for (auto user : users) { - if (!c[i].used) continue; - addUserToMenu(c[i].user); + addUserToMenu(user); } } } @@ -322,7 +317,7 @@ void GUI_MainMenu::onMenuBtn(MyGUI::MenuCtrlPtr _sender, MyGUI::MenuItemPtr _ite int user_uid = PARSEINT(id.substr(5)); // cannot whisper with self... - if (user_uid == gEnv->network->getUID()) return; + if (user_uid == RoR::Networking::GetUID()) return; //RoR::Application::GetConsole()->startPrivateChat(user_uid); //TODO: Separate Chat and console diff --git a/source/main/gui/GUIMp.cpp b/source/main/gui/GUIMp.cpp index 90658ad780..5bbc5904ea 100644 --- a/source/main/gui/GUIMp.cpp +++ b/source/main/gui/GUIMp.cpp @@ -28,7 +28,6 @@ along with Rigs of Rods. If not, see . #include "Language.h" #include "Network.h" #include "PlayerColours.h" -#include "RoRFrameListener.h" using namespace Ogre; @@ -169,21 +168,21 @@ GUI_Multiplayer::~GUI_Multiplayer() } } -void GUI_Multiplayer::updateSlot(player_row_t *row, user_info_t *c, bool self) +void GUI_Multiplayer::updateSlot(player_row_t *row, user_info_t c, bool self) { - if (!row || !c) return; + if (!row) return; int x = 100; int y = row->playername->getPosition().top; // name - row->playername->setCaption(c->username); - ColourValue col = PlayerColours::getSingleton().getColour(c->colournum); + row->playername->setCaption(c.username); + ColourValue col = PlayerColours::getSingleton().getColour(c.colournum); row->playername->setTextColour(MyGUI::Colour(col.r, col.g, col.b, col.a)); row->playername->setVisible(true); x -= 18; // flag - StringVector parts = StringUtil::split(String(c->language), "_"); + StringVector parts = StringUtil::split(String(c.language), "_"); if (parts.size() == 2) { String lang = parts[1]; @@ -200,10 +199,10 @@ void GUI_Multiplayer::updateSlot(player_row_t *row, user_info_t *c, bool self) UTFString tmp; // auth - if (c->authstatus == AUTH_NONE) + if (c.authstatus == AUTH_NONE) { row->statimg->setVisible(false); - } else if (c->authstatus & AUTH_ADMIN) + } else if (c.authstatus & AUTH_ADMIN) { row->statimg->setVisible(true); row->statimg->setImageTexture("flag_red.png"); @@ -211,7 +210,7 @@ void GUI_Multiplayer::updateSlot(player_row_t *row, user_info_t *c, bool self) row->statimg->setUserString("tooltip", tmp.asUTF8()); row->statimg->setPosition(x, y); x -= 18; - } else if (c->authstatus & AUTH_MOD) + } else if (c.authstatus & AUTH_MOD) { row->statimg->setVisible(true); row->statimg->setImageTexture("flag_blue.png"); @@ -219,7 +218,7 @@ void GUI_Multiplayer::updateSlot(player_row_t *row, user_info_t *c, bool self) row->statimg->setUserString("tooltip", tmp.asUTF8()); row->statimg->setPosition(x, y); x -= 18; - } else if (c->authstatus & AUTH_RANKED) + } else if (c.authstatus & AUTH_RANKED) { row->statimg->setVisible(true); row->statimg->setImageTexture("flag_green.png"); @@ -234,14 +233,14 @@ void GUI_Multiplayer::updateSlot(player_row_t *row, user_info_t *c, bool self) { row->userTruckOKImg->setVisible(true); row->userTruckOKRemoteImg->setVisible(true); - row->userTruckOKImg->setUserString("uid", TOSTRING(c->uniqueid)); - row->userTruckOKRemoteImg->setUserString("uid", TOSTRING(c->uniqueid)); + row->userTruckOKImg->setUserString("uid", TOSTRING(c.uniqueid)); + row->userTruckOKRemoteImg->setUserString("uid", TOSTRING(c.uniqueid)); row->userTruckOKImg->setPosition(x, y); x -= 10; row->userTruckOKRemoteImg->setPosition(x, y); x -= 10; - int ok = BeamFactory::getSingleton().checkStreamsOK(c->uniqueid); + int ok = BeamFactory::getSingleton().checkStreamsOK(c.uniqueid); if (ok == 0) { row->userTruckOKImg->setImageTexture("arrow_down_red.png"); @@ -259,7 +258,7 @@ void GUI_Multiplayer::updateSlot(player_row_t *row, user_info_t *c, bool self) row->userTruckOKImg->setUserString("tooltip", tmp.asUTF8()); } - int rok = BeamFactory::getSingleton().checkStreamsRemoteOK(c->uniqueid); + int rok = BeamFactory::getSingleton().checkStreamsRemoteOK(c.uniqueid); if (rok == 0) { row->userTruckOKRemoteImg->setImageTexture("arrow_up_red.png"); @@ -286,7 +285,7 @@ void GUI_Multiplayer::updateSlot(player_row_t *row, user_info_t *c, bool self) row->usergoimg->setVisible(false); } -int GUI_Multiplayer::update() +void GUI_Multiplayer::update() { int slotid = 0; @@ -295,49 +294,42 @@ int GUI_Multiplayer::update() mpPanel->setPosition(x,y); // add local player to first slot always - user_info_t *lu = gEnv->network->getLocalUserData(); + user_info_t lu = RoR::Networking::GetLocalUserData(); updateSlot(&player_rows[slotid], lu, true); slotid++; // add remote players - int res = gEnv->network->getClientInfos(clients); - if (res) return 1; - for (int i = 0; i < MAX_PEERS; i++) + std::vector users = RoR::Networking::GetUserInfos(); + for (user_info_t user : users) { - client_t *c = &clients[i]; player_row_t *row = &player_rows[slotid]; - // only count up slotid for used slots, so there are no gap in the list - if (c->used) + slotid++; + try { - // used - slotid++; - try - { - updateSlot(row, &c->user, false); - } catch(...) - { - } - } else + updateSlot(row, user, false); + } catch(...) { - // not used, hide everything - row->flagimg->setVisible(false); - row->playername->setVisible(false); - row->statimg->setVisible(false); - row->usergoimg->setVisible(false); - row->userTruckOKImg->setVisible(false); - row->userTruckOKRemoteImg->setVisible(false); } + + } + for (int i = slotid; i < MAX_PEERS; i++) + { + player_row_t *row = &player_rows[i]; + // not used, hide everything + row->flagimg->setVisible(false); + row->playername->setVisible(false); + row->statimg->setVisible(false); + row->usergoimg->setVisible(false); + row->userTruckOKImg->setVisible(false); + row->userTruckOKRemoteImg->setVisible(false); } + netmsgwin->setVisible(RoR::Networking::GetNetQuality() != 0); + int height = lineheight * (slotid + 1); mpPanel->setSize(sidebarWidth, height); - - netmsgwin->setVisible(gEnv->network->getNetQuality() != 0); - - return 0; } - void GUI_Multiplayer::clickUserGoIcon(MyGUI::WidgetPtr sender) { //int uid = StringConverter::parseInt(sender->getUserString("uid")); diff --git a/source/main/gui/GUIMp.h b/source/main/gui/GUIMp.h index df38f8b8c0..2bf3fd011c 100644 --- a/source/main/gui/GUIMp.h +++ b/source/main/gui/GUIMp.h @@ -39,9 +39,10 @@ class GUI_Multiplayer : public RoRSingletonNoCreation< GUI_Multiplayer > GUI_Multiplayer(); ~GUI_Multiplayer(); - int update(); - void setVisible(bool value); + void update(); + bool getVisible(); + void setVisible(bool value); protected: @@ -68,7 +69,7 @@ class GUI_Multiplayer : public RoRSingletonNoCreation< GUI_Multiplayer > MyGUI::WindowPtr netmsgwin; MyGUI::StaticTextPtr netmsgtext; - void updateSlot(player_row_t *row, user_info_t *c, bool self); + void updateSlot(player_row_t *row, user_info_t c, bool self); client_t *clients; int lineheight; diff --git a/source/main/gui/panels/GUI_GameChatBox.cpp b/source/main/gui/panels/GUI_GameChatBox.cpp index c56c67291b..95d2b75b07 100644 --- a/source/main/gui/panels/GUI_GameChatBox.cpp +++ b/source/main/gui/panels/GUI_GameChatBox.cpp @@ -34,9 +34,7 @@ #include "RoRPrerequisites.h" #include "ChatSystem.h" -#include "Network.h" #include "Utils.h" -#include "rornet.h" #include "Language.h" #include "GUIManager.h" #include "Application.h" @@ -50,7 +48,6 @@ using namespace GUI; CLASS::CLASS() : alpha(1.0f) - , netChat(0) , newMsg(false) { MyGUI::Gui::getInstance().eventFrameStart += MyGUI::newDelegate(this, &CLASS::Update); @@ -72,11 +69,6 @@ CLASS::~CLASS() { } -void CLASS::setNetChat(ChatSystem *c) -{ - netChat = c; -} - void CLASS::Show() { MAIN_WIDGET->setVisible(true); @@ -96,7 +88,7 @@ bool CLASS::IsVisible() void CLASS::pushMsg(Ogre::String txt) { - mHistory += ChatSystem::normalColour + txt + " \n"; + mHistory += RoR::Color::NormalColour + txt + " \n"; newMsg = true; m_Chatbox_MainBox->setCaptionWithReplacing(mHistory); } @@ -126,14 +118,14 @@ void CLASS::eventCommandAccept(MyGUI::Edit* _sender) pushMsg(trmsg); return; } - netChat->sendPrivateChat(args[1], args[2]); + RoR::ChatSystem::SendPrivateChat(args[1], args[2]); return; } } - if (gEnv->network && netChat) + if (gEnv->multiplayer) { - netChat->sendChat(msg.c_str()); + RoR::ChatSystem::SendChat(msg.c_str()); return; } diff --git a/source/main/gui/panels/GUI_GameChatBox.h b/source/main/gui/panels/GUI_GameChatBox.h index 56387bd45b..bcdddff774 100644 --- a/source/main/gui/panels/GUI_GameChatBox.h +++ b/source/main/gui/panels/GUI_GameChatBox.h @@ -38,8 +38,8 @@ namespace GUI class GameChatBox: public GameChatBoxLayout { - public: + GameChatBox(); ~GameChatBox(); @@ -48,12 +48,10 @@ class GameChatBox: public GameChatBoxLayout bool IsVisible(); void pushMsg(Ogre::String txt); void Update(float dt); - void setNetChat(ChatSystem *c); private: - void eventCommandAccept(MyGUI::Edit* _sender); - ChatSystem *netChat; + void eventCommandAccept(MyGUI::Edit* _sender); Ogre::String mHistory; bool newMsg; diff --git a/source/main/gui/panels/GUI_GamePauseMenu.cpp b/source/main/gui/panels/GUI_GamePauseMenu.cpp index dcad58b8b7..1448ed3a80 100644 --- a/source/main/gui/panels/GUI_GamePauseMenu.cpp +++ b/source/main/gui/panels/GUI_GamePauseMenu.cpp @@ -96,7 +96,7 @@ void CLASS::Show() m_rig_editor->setEnabled(false); - if (gEnv->network) + if (gEnv->multiplayer) { m_back_to_menu->setEnabled(false); m_change_map->setEnabled(false); diff --git a/source/main/gui/panels/GUI_MainSelector.cpp b/source/main/gui/panels/GUI_MainSelector.cpp index 504c9541cd..9299cdc518 100644 --- a/source/main/gui/panels/GUI_MainSelector.cpp +++ b/source/main/gui/panels/GUI_MainSelector.cpp @@ -934,7 +934,7 @@ void CLASS::Show(LoaderType type) BindKeys(); - if (type == LT_Terrain && gEnv->network) + if (type == LT_Terrain && gEnv->multiplayer) m_Cancel->setEnabled(false); else m_Cancel->setEnabled(true); diff --git a/source/main/network/Network.cpp b/source/main/network/Network.cpp index 445ac021b8..1c7ef8897d 100644 --- a/source/main/network/Network.cpp +++ b/source/main/network/Network.cpp @@ -1,687 +1,662 @@ -/* -This source file is part of Rigs of Rods -Copyright 2005-2012 Pierre-Michel Ricordel -Copyright 2007-2012 Thomas Fischer - -For more information, see http://www.rigsofrods.com/ - -Rigs of Rods is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3, as -published by the Free Software Foundation. - -Rigs of Rods 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 Rigs of Rods. If not, see . -*/ -#ifdef USE_SOCKETW - -#include "Network.h" - -#include "BeamFactory.h" -#include "CharacterFactory.h" -#include "ChatSystem.h" -#include "Console.h" -#include "ErrorUtils.h" -#include "GUIMenu.h" -#include "GUIMp.h" -#include "Language.h" -#include "RoRVersion.h" -#include "Scripting.h" -#include "Settings.h" -#include "SHA1.h" -#include "Utils.h" - -#include -#include - -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE -//#include -#endif - -using namespace Ogre; - -Timer Network::timer = Ogre::Timer(); - -Network::Network(String servername, long server_port) : - lagDataClients() - , initiated(false) - , myuid(0) - , net_quality(0) -{ - - // update factories network objects - - memset(&server_settings, 0, sizeof(server_info_t)); - memset(&userdata, 0, sizeof(user_info_t)); - shutdown=false; - - m_server_name = servername; - m_server_port = server_port; - myauthlevel = AUTH_NONE; - nickname = ""; - - speed_time=0; - speed_bytes_sent = speed_bytes_sent_tmp = speed_bytes_recv = speed_bytes_recv_tmp = 0; - - rconauthed=0; - last_time=0; - send_buffer=0; - - // direct start, no vehicle required - initiated = true; - - // reset client list - std::lock_guard lock(clients_mutex); - for (int i=0; i0) - { - RoR::CSHA1 sha1; - sha1.UpdateHash((uint8_t *)pwbuffer, (uint32_t)strnlen(pwbuffer, 250)); - sha1.Final(); - sha1.ReportHash(sha1pwresult, RoR::CSHA1::REPORT_HEX_SHORT); - } - - String usertokenhash = SSETTING("User Token Hash", ""); - - // construct user credentials - // beware of the wchar_t converted to UTF8 for networking - user_info_t c; - memset(&c, 0, sizeof(user_info_t)); - // cut off the UTF string on the highest level, otherwise you will break UTF info - strncpy((char *)c.username, nickname.substr(0, MAX_USERNAME_LEN * 0.5f).asUTF8_c_str(), MAX_USERNAME_LEN); - strncpy(c.serverpassword, sha1pwresult, 40); - strncpy(c.usertoken, usertokenhash.c_str(), 40); - strncpy(c.clientversion, ROR_VERSION_STRING, strnlen(ROR_VERSION_STRING, 25)); - strcpy(c.clientname, "RoR"); - String lang = SSETTING("Language Short", "en"); - strncpy(c.language, lang.c_str(), std::min((int)lang.size(), 10)); - String guid = SSETTING("GUID", ""); - strncpy(c.clientGUID, guid.c_str(), std::min((int)guid.size(), 10)); - strcpy(c.sessiontype, "normal"); - if (sendmessage(MSG2_USER_INFO, 0, sizeof(user_info_t), (char*)&c)) - { - //this is an error! - netFatalError(_L("Establishing network session: error sending user info"), false); - return false; - } - //now this is important, getting authorization - if (receivemessage(&header, buffer, 255)) - { - //this is an error! - netFatalError(_L("Establishing network session: error getting server authorization"), false); - return false; - } - if (header.command==MSG2_FULL) - { - //this is an error! - netFatalError(_L("Establishing network session: sorry, server has too many players"), false); - return false; - } - else if (header.command==MSG2_BANNED) - { - wchar_t tmp[512]; - memset(tmp, 0, 512); - if (buffer && strnlen(buffer, 20)>0) - { - buffer[header.size]=0; - UTFString tmp2 = _L("Establishing network session: sorry, you are banned:\n%s"); - swprintf(tmp, 512, tmp2.asWStr_c_str(), buffer); - netFatalError(UTFString(tmp)); - } else - { - netFatalError(_L("Establishing network session: sorry, you are banned!"), false); - } - - return false; - } - else if (header.command==MSG2_WRONG_PW) - { - //this is an error! - netFatalError(_L("Establishing network session: sorry, wrong password!"), false); - return false; - } - else if (header.command==MSG2_WRONG_VER) - { - //this is an error! - netFatalError(_L("Establishing network session: sorry, wrong protocol version!"), false); - return false; - } - if (header.command!=MSG2_WELCOME) - { - //this is an error! - netFatalError(_L("Establishing network session: sorry, unknown server response"), false); - return false; - } - //okay keep our uid - myuid = header.source; - - // we get our userdata back - memcpy(&userdata, buffer, std::min(sizeof(user_info_t), header.size)); - - //start the handling threads - std::thread ([this](){ this->sendthreadstart(); }).detach(); - std::thread ([this](){ this->receivethreadstart(); }).detach(); - - return true; -} - -Ogre::UTFString Network::getNickname(bool colour) -{ - if (colour) - return ChatSystem::getColouredName(nickname, myauthlevel, userdata.colournum); - else - return nickname; -} - -int Network::sendMessageRaw(char *buffer, unsigned int msgsize) -{ - //LOG("* sending raw message: " + TOSTRING(msgsize)); - - std::lock_guard lock(msgsend_mutex); - SWBaseSocket::SWBaseError error; - - int rlen = 0; - speed_bytes_sent_tmp += msgsize; - while (rlen < (int)msgsize) - { - int sendnum = socket.send(buffer+rlen, msgsize-rlen, &error); - if (sendnum < 0) - { - LOG("NET send error: " + TOSTRING(sendnum)); - return -1; - } - rlen += sendnum; - } - calcSpeed(); - return 0; -} - -int Network::sendmessage(int type, unsigned int streamid, unsigned int len, char* content) -{ - header_t head; - memset(&head, 0, sizeof(header_t)); - head.command = type; - head.source = myuid; - head.size = len; - head.streamid = streamid; - - const int msgsize = sizeof(header_t) + len; - - if (msgsize >= MAX_MESSAGE_LENGTH) - { - return -2; - } - - char buffer[MAX_MESSAGE_LENGTH]; - memset(buffer, 0, MAX_MESSAGE_LENGTH); - memcpy(buffer, (char *)&head, sizeof(header_t)); - memcpy(buffer+sizeof(header_t), content, len); - - return sendMessageRaw(buffer, msgsize); -} - -int Network::receivemessage(header_t *head, char* content, unsigned int bufferlen) -{ - SWBaseSocket::SWBaseError error; - - char buffer[MAX_MESSAGE_LENGTH]; - //ensure that the buffer is clean after each received message! - memset(buffer, 0, MAX_MESSAGE_LENGTH); - - int hlen=0; - while (hlen<(int)sizeof(header_t)) - { - int recvnum=socket.recv(buffer+hlen, sizeof(header_t)-hlen,&error); - if (recvnum<0) - { - LOG("NET receive error 1: " + TOSTRING(recvnum)); - return -1; - } - hlen+=recvnum; - } - - memcpy(head, buffer, sizeof(header_t)); - - if (head->size >= MAX_MESSAGE_LENGTH) - { - return -3; - } - - if (head->size>0) - { - if ((int)sizeof(header_t) > 0) - { - //read the rest - while (hlen<(int)sizeof(header_t)+(int)head->size) - { - int recvnum=socket.recv(buffer+hlen, (head->size+sizeof(header_t))-hlen,&error); - if (recvnum<0) - { - LOG("NET receive error 2: "+ TOSTRING(recvnum)); - return -1; - } - hlen+=recvnum; - } - } - } - speed_bytes_recv_tmp += head->size + sizeof(header_t); - - memcpy(content, buffer+sizeof(header_t), bufferlen); - calcSpeed(); - return 0; -} - - -int Network::getSpeedUp() -{ - return speed_bytes_sent; -} - -int Network::getSpeedDown() -{ - return speed_bytes_recv; -} - -void Network::calcSpeed() -{ - int t = timer.getMilliseconds(); - if (t - speed_time > 1000) - { - // we measure bytes / second - speed_bytes_sent = speed_bytes_sent_tmp; - speed_bytes_sent_tmp = 0; - speed_bytes_recv = speed_bytes_recv_tmp; - speed_bytes_recv_tmp = 0; - speed_time = t; - } -} - -void Network::sendthreadstart() -{ - LOG("Sendthread starting"); - while (!shutdown) - { - // wait for data... - NetworkStreamManager::getSingleton().sendStreams(this); - } -} - -void Network::disconnect() -{ - shutdown=true; - sendmessage(MSG2_USER_LEAVE, 0, 0, 0); - SWBaseSocket::SWBaseError error; - socket.set_timeout(1, 1000); - socket.disconnect(&error); - LOG("Network error while disconnecting: "); -} - -int Network::sendScriptMessage(char* content, unsigned int len) -{ - int result = sendmessage(MSG2_GAME_CMD, 0, len, content); - if (result<0) LOG("An error occurred while sending a script message to the server."); - return result; -} - -unsigned long Network::getNetTime() -{ - return timer.getMilliseconds(); -} - -void Network::receivethreadstart() -{ - header_t header; - - char *buffer=(char*)malloc(MAX_MESSAGE_LENGTH); - //bool autoDl = (BSETTING("AutoDownload", false)); - std::deque < stream_reg_t > streamCreationResults; - LOG("Receivethread starting"); - // unlimited timeout, important! - - // wait for beamfactory to be existant before doing anything - // otherwise you can get runtime conditions - while(!BeamFactory::getSingletonPtr()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - }; - - - socket.set_timeout(0,0); - while (!shutdown) - { - //get one message - int err=receivemessage(&header, buffer, MAX_MESSAGE_LENGTH); - //LOG("received data: " + TOSTRING(header.command) + ", source: "+TOSTRING(header.source) + ":"+TOSTRING(header.streamid) + ", size: "+TOSTRING(header.size)); - if (err) - { - //this is an error! - char errmsg[256]; - sprintf(errmsg, "Error %i while receiving data", err); - netFatalError(errmsg); - return; - } - - // check for stream registration errors and notify the remote client - if (BeamFactory::getSingletonPtr() && BeamFactory::getSingletonPtr()->getStreamRegistrationResults(&streamCreationResults)) - { - while (!streamCreationResults.empty()) - { - stream_reg_t r = streamCreationResults.front(); - stream_register_t reg = r.reg; - sendmessage(MSG2_STREAM_REGISTER_RESULT, 0, sizeof(stream_register_t), (char *)®); - streamCreationResults.pop_front(); - } - } - - // TODO: produce new streamable classes when required - if (header.command == MSG2_STREAM_REGISTER) - { - if (header.source == (int)myuid) - // our own stream, ignore - continue; - stream_register_t *reg = (stream_register_t *)buffer; - client_t *client = getClientInfo(header.source); - int playerColour = 0; - if (client) playerColour = client->user.colournum; - - String typeStr = "unknown"; - switch(reg->type) - { - case 0: typeStr="truck"; break; - case 1: typeStr="character"; break; - case 3: typeStr="chat"; break; - }; - LOG(" * received stream registration: " + TOSTRING(header.source) + ": "+TOSTRING(header.streamid) + ", type: "+typeStr); - - if (reg->type == 0) - { - // truck - BeamFactory::getSingleton().createRemote(header.source, header.streamid, reg, playerColour); - } else if (reg->type == 1) - { - // person - CharacterFactory::getSingleton().createRemote(header.source, header.streamid, reg, playerColour); - } else if (reg->type == 2) - { - // previously AITRAFFIC, unused for now - } else if (reg->type == 3) - { - // chat stream - ChatSystemFactory::getSingleton().createRemote(header.source, header.streamid, reg, playerColour); - } - continue; - } - else if (header.command == MSG2_STREAM_REGISTER_RESULT) - { - stream_register_t *reg = (stream_register_t *)buffer; - BeamFactory::getSingleton().addStreamRegistrationResults(header.source, reg); - LOG(" * received stream registration result: " + TOSTRING(header.source) + ": "+TOSTRING(header.streamid)); - continue; - } - else if (header.command == MSG2_STREAM_UNREGISTER) - { - NetworkStreamManager::getSingleton().removeStream(header.source, header.streamid); - LOG(" * received stream deregistration: " + TOSTRING(header.source) + ": "+TOSTRING(header.streamid)); - continue; - } - else if (header.source == -1 && (header.command == MSG2_UTF_CHAT || header.command == MSG2_UTF_PRIVCHAT)) - { - // NOTE: this is only a shortcut for server messages, other UIDs propagate over the standard way - ChatSystem *cs = ChatSystemFactory::getSingleton().getFirstChatSystem(); - if (cs) cs->addReceivedPacket(header, buffer); - continue; - } - else if (header.command == MSG2_NETQUALITY && header.source == -1) - { - if (header.size != sizeof(int)) - continue; - int quality = *(int *)buffer; - setNetQuality(quality); - continue; - } - else if (header.command == MSG2_USER_LEAVE) - { - if (header.source == (int)myuid) - { - netFatalError(_L("disconnected: remote side closed the connection"), false); - return; - } - - // remove all things that belong to that user - client_t *client = getClientInfo(header.source); - if (client) - client->used = false; - - // now remove all possible streams - NetworkStreamManager::getSingleton().removeUser(header.source); - -#ifdef USE_MYGUI - if (GUI_MainMenu::getSingletonPtr() != nullptr) // Does menubar exist yet? - // Executed in main menu, but menubar isn't created until simulation starts. - { - // we can trigger this in the network thread as the function is thread safe. - GUI_MainMenu::getSingleton().triggerUpdateVehicleList(); - } -#endif // USE_MYGUI - continue; - } - else if (header.command == MSG2_USER_INFO || header.command == MSG2_USER_JOIN) - { - if (header.source == (int)myuid) - { - // we got data about ourself! - memcpy(&userdata, buffer, sizeof(user_info_t)); - CharacterFactory::getSingleton().localUserAttributesChanged(myuid); - // update our nickname - nickname = UTFString(userdata.username); - // save it in the Settings as well - SETTINGS.setUTFSetting(L"Nickname", nickname); - // update auth status - myauthlevel = userdata.authstatus; - } else - { - user_info_t *cinfo = (user_info_t*) buffer; - // data about someone else, try to update the array - client_t *client = getClientInfo(header.source); - if (client) - { - memcpy(&client->user, cinfo, sizeof(user_info_t)); - - // inform the streamfactories of a attribute change - CharacterFactory::getSingleton().netUserAttributesChanged(header.source, -1); - BeamFactory::getSingleton().netUserAttributesChanged(header.source, -1); - } else - { - // find a free entry - std::lock_guard lock(clients_mutex); - for (int i=0; i lock(clients_mutex); - for (int i=0;i lock(clients_mutex); - client_t *c = 0; - for (int i=0; isize); - sha1.Final(); - sha1.ReportHash(sha1result, RoR::CSHA1::REPORT_HEX_SHORT); - } - - char tmp[256]=""; - sprintf(tmp, "++ %s: %d:%d, %d, %d, hash: %s", name, header->source, header->streamid, header->command, header->size, sha1result); - LOG(tmp); - //String hex = hexdump(buffer, header->size); - //LOG(hex); -} - -void Network::setNetQuality(int q) -{ - net_quality = q; -} - -int Network::getNetQuality() -{ - return net_quality; -} - -#endif // USE_SOCKETW +/* + This source file is part of Rigs of Rods + Copyright 2005-2012 Pierre-Michel Ricordel + Copyright 2007-2012 Thomas Fischer + Copyright 2013-2016 Petr Ohlidal + + For more information, see http://www.rigsofrods.com/ + + Rigs of Rods is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License version 3, as + published by the Free Software Foundation. + + Rigs of Rods 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 Rigs of Rods. If not, see . +*/ +#ifdef USE_SOCKETW + +#include "Network.h" + +#include "BeamFactory.h" +#include "CharacterFactory.h" +#include "ChatSystem.h" +#include "ErrorUtils.h" +#include "Language.h" +#include "RoRVersion.h" +#include "SHA1.h" +#include "ScriptEngine.h" +#include "Settings.h" +#include "Utils.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace RoR { +namespace Networking { + +typedef struct send_packet_t +{ + char buffer[MAX_MESSAGE_LENGTH]; + int size; +} send_packet_t; + +static Ogre::UTFString m_server_name; +static int m_server_port; +static server_info_t m_server_settings; + +static Ogre::UTFString m_username; +static int m_uid; +static int m_authlevel; +static user_info_t m_userdata; + +static int m_stream_id = 10; + +static std::atomic m_net_quality; + +static std::vector m_users; + +static SWInetSocket socket; + +static std::thread m_send_thread; +static std::thread m_recv_thread; + +static std::atomic m_shutdown; + +static std::mutex m_users_mutex; +static std::mutex m_userdata_mutex; +static std::mutex m_recv_packetqueue_mutex; +static std::mutex m_send_packetqueue_mutex; + +static std::condition_variable m_send_packet_available_cv; + +static std::vector m_recv_packet_buffer; +static std::deque m_send_packet_buffer; + +static const unsigned int m_packet_buffer_size = 20; + +void DebugPacket(const char *name, header_t *header, char *buffer) +{ + char sha1result[250] = {0}; + if (buffer) + { + RoR::CSHA1 sha1; + sha1.UpdateHash((uint8_t *)buffer, header->size); + sha1.Final(); + sha1.ReportHash(sha1result, RoR::CSHA1::REPORT_HEX_SHORT); + } + char tmp[256] = {0}; + sprintf(tmp, "++ %s: %d:%d, %d, %d, hash: %s", name, header->source, header->streamid, header->command, header->size, sha1result); + LOG(tmp); +} + +void NetFatalError(Ogre::UTFString errormsg, bool exit_program) +{ + if (m_shutdown) + return; + + socket.set_timeout(1, 1000); + ErrorUtils::ShowError(_L("Network Connection Problem"), _L("Network fatal error: ") + errormsg); + + socket.disconnect(); + + if (exit_program) + exit(124); +} + +void SetNetQuality(int quality) +{ + m_net_quality = quality; +} + +int GetNetQuality() +{ + return m_net_quality; +} + +int GetUID() +{ + return m_uid; +} + +bool SendMessageRaw(char *buffer, int msgsize) +{ + SWBaseSocket::SWBaseError error; + + int rlen = 0; + while (rlen < msgsize) + { + int sendnum = socket.send(buffer + rlen, msgsize - rlen, &error); + if (sendnum < 0) + { + LOG("NET send error: " + TOSTRING(sendnum)); + return false; + } + rlen += sendnum; + } + + return true; +} + +bool SendMessage(int type, unsigned int streamid, int len, char* content) +{ + header_t head; + memset(&head, 0, sizeof(header_t)); + head.command = type; + head.source = m_uid; + head.size = len; + head.streamid = streamid; + + const int msgsize = sizeof(header_t) + len; + + if (msgsize >= MAX_MESSAGE_LENGTH) + { + return false; + } + + char buffer[MAX_MESSAGE_LENGTH] = {0}; + memcpy(buffer, (char *)&head, sizeof(header_t)); + memcpy(buffer + sizeof(header_t), content, len); + + return SendMessageRaw(buffer, msgsize); +} + +void QueueStreamData(header_t &header, char *buffer) +{ + recv_packet_t packet; + packet.header = header; + memcpy(packet.buffer, buffer, MAX_MESSAGE_LENGTH); + + std::lock_guard lock(m_recv_packetqueue_mutex); + m_recv_packet_buffer.push_back(packet); +} + +int ReceiveMessage(header_t *head, char* content, int bufferlen) +{ + SWBaseSocket::SWBaseError error; + + char buffer[MAX_MESSAGE_LENGTH] = {0}; + + int hlen = 0; + while (hlen < (int)sizeof(header_t)) + { + int recvnum = socket.recv(buffer + hlen, sizeof(header_t) - hlen, &error); + if (recvnum < 0 && !m_shutdown) + { + LOG("NET receive error 1: " + TOSTRING(recvnum)); + return -1; + } + hlen += recvnum; + } + + memcpy(head, buffer, sizeof(header_t)); + + if (head->size >= MAX_MESSAGE_LENGTH) + { + return -3; + } + + if (head->size > 0) + { + // Read the packet content + while (hlen < (int)sizeof(header_t) + (int)head->size) + { + int recvnum = socket.recv(buffer + hlen, (head->size + sizeof(header_t)) - hlen, &error); + if (recvnum < 0 && !m_shutdown) + { + LOG("NET receive error 2: "+ TOSTRING(recvnum)); + return -1; + } + hlen += recvnum; + } + } + + memcpy(content, buffer + sizeof(header_t), bufferlen); + + return 0; +} + +void SendThread() +{ + LOG("SendThread started"); + while (!m_shutdown) + { + std::unique_lock queue_lock(m_send_packetqueue_mutex); + while (m_send_packet_buffer.empty()) + { + if (m_shutdown) { return; } + m_send_packet_available_cv.wait(queue_lock); + } + + while (!m_send_packet_buffer.empty()) + { + send_packet_t packet = m_send_packet_buffer.front(); + m_send_packet_buffer.pop_front(); + + queue_lock.unlock(); + SendMessageRaw(packet.buffer, packet.size); + queue_lock.lock(); + } + queue_lock.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + queue_lock.lock(); + } + LOG("SendThread stopped"); +} + +void RecvThread() +{ + LOG("RecvThread started"); + + header_t header; + + char *buffer = (char*)malloc(MAX_MESSAGE_LENGTH); + + while (!m_shutdown) + { + int err = ReceiveMessage(&header, buffer, MAX_MESSAGE_LENGTH); + //LOG("Received data: " + TOSTRING(header.command) + ", source: " + TOSTRING(header.source) + ":" + TOSTRING(header.streamid) + ", size: " + TOSTRING(header.size)); + if (err) + { + char errmsg[256] = {0}; + sprintf(errmsg, "Error %i while receiving data", err); + NetFatalError(errmsg, true); + return; + } + + if (header.command == MSG2_STREAM_REGISTER) + { + if (header.source == m_uid) + continue; + + stream_register_t *reg = (stream_register_t *)buffer; + + LOG(" * received stream registration: " + TOSTRING(header.source) + ": " + TOSTRING(header.streamid) + ", type: " + TOSTRING(reg->type)); + } else if (header.command == MSG2_STREAM_REGISTER_RESULT) + { + stream_register_t *reg = (stream_register_t *)buffer; + LOG(" * received stream registration result: " + TOSTRING(header.source) + ": " + TOSTRING(header.streamid) + ", status: " + TOSTRING(reg->status)); + } else if (header.command == MSG2_STREAM_UNREGISTER) + { + LOG(" * received stream deregistration: " + TOSTRING(header.source) + ": " + TOSTRING(header.streamid)); + } else if (header.command == MSG2_UTF_CHAT || header.command == MSG2_UTF_PRIVCHAT) + { + // Chat message + } else if (header.command == MSG2_NETQUALITY && header.source == -1) + { + if (header.size != sizeof(int)) + continue; + int quality = *(int *)buffer; + SetNetQuality(quality); + continue; + } else if (header.command == MSG2_USER_LEAVE) + { + if (header.source == m_uid) + { + NetFatalError(_L("disconnected: remote side closed the connection"), false); + return; + } + + { + std::lock_guard lock(m_users_mutex); + auto user = std::find_if(m_users.begin(), m_users.end(), [header](const user_info_t u) { return static_cast(u.uniqueid) == header.source; }); + if (user != m_users.end()) + { + Ogre::UTFString msg = RoR::ChatSystem::GetColouredName(user->username, user->colournum) + RoR::Color::CommandColour + _L(" left the game"); + const char *utf8_line = msg.asUTF8_c_str(); + header_t head; + head.command = MSG2_UTF_CHAT; + head.source = -1; + head.size = (int)strlen(utf8_line); + QueueStreamData(head, (char *)utf8_line); + LOG(Ogre::UTFString(user->username) + _L(" left the game")); + m_users.erase(user); + } + } + } else if (header.command == MSG2_USER_INFO || header.command == MSG2_USER_JOIN) + { + if (header.source == m_uid) + { + std::lock_guard lock(m_userdata_mutex); + memcpy(&m_userdata, buffer, sizeof(user_info_t)); + m_authlevel = m_userdata.authstatus; + m_username = Ogre::UTFString(m_userdata.username); + SETTINGS.setUTFSetting(L"Nickname", m_username); + } else + { + user_info_t user_info; + memcpy(&user_info, buffer, sizeof(user_info_t)); + + bool user_exists = false; + { + std::lock_guard lock(m_users_mutex); + for (user_info_t &user : m_users) + { + if ((int)user.uniqueid == header.source) + { + user = user_info; + user_exists = true; + break; + } + } + if (!user_exists) + { + m_users.push_back(user_info); + Ogre::UTFString msg = RoR::ChatSystem::GetColouredName(user_info.username, user_info.colournum) + RoR::Color::CommandColour + _L(" joined the game"); + const char *utf8_line = msg.asUTF8_c_str(); + header_t head; + head.command = MSG2_UTF_CHAT; + head.source = -1; + head.size = (int)strlen(utf8_line); + QueueStreamData(head, (char *)utf8_line); + LOG(Ogre::UTFString(user_info.username) + _L(" joined the game")); + } + } + } + continue; + } else if (header.command == MSG2_GAME_CMD) + { +#ifdef USE_ANGELSCRIPT + ScriptEngine::getSingleton().queueStringForExecution(Ogre::String(buffer)); +#endif // USE_ANGELSCRIPT + continue; + } + //DebugPacket("receive-1", &header, buffer); + + QueueStreamData(header, buffer); + } + LOG("RecvThread stopped"); +} + +bool Connect() +{ + m_server_name = SSETTING("Server name", ""); + m_server_port = ISETTING("Server port", 0); + m_username = SSETTING("Nickname", "Anonymous"); + + LOG("Trying to join server '" + m_server_name + "' on port " + TOSTRING(m_server_port) + "'..."); + + SWBaseSocket::SWBaseError error; + + socket.set_timeout(10, 10000); + socket.connect(m_server_port, m_server_name, &error); + if (error != SWBaseSocket::ok) + { + NetFatalError(_L("Establishing network session: "), false); + return false; + } + if (!SendMessage(MSG2_HELLO, 0, (int)strlen(RORNET_VERSION), (char *)RORNET_VERSION)) + { + NetFatalError(_L("Establishing network session: error sending hello"), false); + return false; + } + + header_t header; + char buffer[MAX_MESSAGE_LENGTH] = {0}; + + // Receive server (rornet protocol) version + if (ReceiveMessage(&header, buffer, 255)) + { + NetFatalError(_L("Establishing network session: error getting server version"), false); + return false; + } + if (header.command == MSG2_WRONG_VER) + { + NetFatalError(_L("server uses a different protocol version"), true); + return false; + } + if (header.command != MSG2_HELLO) + { + NetFatalError(_L("Establishing network session: error getting server hello"), true); + return false; + } + + // Save server settings + memcpy(&m_server_settings, buffer, sizeof(server_info_t)); + + if (strncmp(m_server_settings.protocolversion, RORNET_VERSION, strlen(RORNET_VERSION))) + { + wchar_t tmp[512] = L""; + Ogre::UTFString tmp2 = _L("Establishing network session: wrong server version, you are using version '%s' and the server is using '%s'"); + swprintf(tmp, 512, tmp2.asWStr_c_str(), RORNET_VERSION, m_server_settings.protocolversion); + NetFatalError(Ogre::UTFString(tmp), true); + return false; + } + + // First handshake done, increase the timeout, important! + socket.set_timeout(0, 0); + + // Send credentials + char pwbuffer[250] = {0}; + strncpy(pwbuffer, SSETTING("Server password", "").c_str(), 250); + + char sha1pwresult[250] = {0}; + if (strnlen(pwbuffer, 250) > 0) + { + RoR::CSHA1 sha1; + sha1.UpdateHash((uint8_t *)pwbuffer, (uint32_t)strnlen(pwbuffer, 250)); + sha1.Final(); + sha1.ReportHash(sha1pwresult, RoR::CSHA1::REPORT_HEX_SHORT); + } + + Ogre::String usertokenhash = SSETTING("User Token Hash", ""); + + // Construct user credentials + // Beware of the wchar_t converted to UTF8 for networking + user_info_t c; + memset(&c, 0, sizeof(user_info_t)); + // Cut off the UTF string on the highest level, otherwise you will break UTF info + strncpy((char *)c.username, m_username.substr(0, MAX_USERNAME_LEN * 0.5f).asUTF8_c_str(), MAX_USERNAME_LEN); + strncpy(c.serverpassword, sha1pwresult, 40); + strncpy(c.usertoken, usertokenhash.c_str(), 40); + strncpy(c.clientversion, ROR_VERSION_STRING, strnlen(ROR_VERSION_STRING, 25)); + strcpy(c.clientname, "RoR"); + Ogre::String lang = SSETTING("Language Short", "en"); + strncpy(c.language, lang.c_str(), std::min((int)lang.size(), 10)); + Ogre::String guid = SSETTING("GUID", ""); + strncpy(c.clientGUID, guid.c_str(), std::min((int)guid.size(), 10)); + strcpy(c.sessiontype, "normal"); + if (!SendMessage(MSG2_USER_INFO, 0, sizeof(user_info_t), (char*)&c)) + { + NetFatalError(_L("Establishing network session: error sending user info"), false); + return false; + } + + // Getting authorization + if (ReceiveMessage(&header, buffer, 255)) + { + NetFatalError(_L("Establishing network session: error getting server authorization"), false); + return false; + } + if (header.command==MSG2_FULL) + { + NetFatalError(_L("Establishing network session: sorry, server has too many players"), false); + return false; + } else if (header.command==MSG2_BANNED) + { + wchar_t tmp[512]; + memset(tmp, 0, 512); + if (strnlen(buffer, 20) > 0) + { + buffer[header.size] = {0}; + Ogre::UTFString tmp2 = _L("Establishing network session: sorry, you are banned:\n%s"); + swprintf(tmp, 512, tmp2.asWStr_c_str(), buffer); + NetFatalError(Ogre::UTFString(tmp), true); + } else + { + NetFatalError(_L("Establishing network session: sorry, you are banned!"), false); + } + return false; + } else if (header.command==MSG2_WRONG_PW) + { + NetFatalError(_L("Establishing network session: sorry, wrong password!"), false); + return false; + } else if (header.command==MSG2_WRONG_VER) + { + NetFatalError(_L("Establishing network session: sorry, wrong protocol version!"), false); + return false; + } + if (header.command!=MSG2_WELCOME) + { + NetFatalError(_L("Establishing network session: sorry, unknown server response"), false); + return false; + } + + m_uid = header.source; + + // we get our userdata back + memcpy(&m_userdata, buffer, std::min(sizeof(user_info_t), header.size)); + + m_shutdown = false; + + m_send_thread = std::thread(SendThread); + m_recv_thread = std::thread(RecvThread); + + return true; +} + +void Disconnect() +{ + m_shutdown = true; + m_send_packet_available_cv.notify_one(); + m_send_thread.join(); + m_recv_thread.detach(); + + SendMessage(MSG2_USER_LEAVE, 0, 0, 0); + socket.set_timeout(1, 1000); + socket.disconnect(); +} + +void AddPacket(int streamid, int type, int len, char *content) +{ + if (len > MAX_MESSAGE_LENGTH) + // packet too big, discarded + return; + + send_packet_t packet; + memset(&packet, 0, sizeof(send_packet_t)); + + char *buffer = (char*)(packet.buffer); + + header_t *head = (header_t *)buffer; + head->command = type; + head->source = m_uid; + head->size = len; + head->streamid = streamid; + + // then copy the contents + char *bufferContent = (char *)(buffer + sizeof(header_t)); + memcpy(bufferContent, content, len); + + // record the packet size + packet.size = len + sizeof(header_t); + + { + std::lock_guard lock(m_send_packetqueue_mutex); + if (type == MSG2_STREAM_DATA) + { + if (m_send_packet_buffer.size() > m_packet_buffer_size) + { + // buffer full, discard unimportant data packets + return; + } + auto search = std::find_if(m_send_packet_buffer.begin(), m_send_packet_buffer.end(), [packet](const send_packet_t& p) { return memcmp(packet.buffer, p.buffer, sizeof(header_t)) == 0; }); + if (search != m_send_packet_buffer.end()) + { + // Found an older packet with the same header -> replace it + (*search) = packet; + return; + } + } + m_send_packet_buffer.push_back(packet); + } + + m_send_packet_available_cv.notify_one(); +} + +void AddLocalStream(stream_register_t *reg, int size) +{ + reg->origin_sourceid = m_uid; + reg->origin_streamid = m_stream_id; + reg->status = 0; + + AddPacket(m_stream_id, MSG2_STREAM_REGISTER, size, (char*)reg); + LOG("adding local stream: " + TOSTRING(m_uid) + ":"+ TOSTRING(m_stream_id) + ", type: " + TOSTRING(reg->type)); + + m_stream_id++; +} + +void HandleStreamData() +{ + std::vector packet_buffer; + + { + std::lock_guard lock(m_recv_packetqueue_mutex); + packet_buffer = m_recv_packet_buffer; + m_recv_packet_buffer.clear(); + } + + RoR::ChatSystem::HandleStreamData(packet_buffer); + BeamFactory::getSingleton().handleStreamData(packet_buffer); + // Updates characters last (or else beam coupling might fail) + CharacterFactory::getSingleton().handleStreamData(packet_buffer); +} + +Ogre::String GetTerrainName() +{ + return m_server_settings.terrain; +} + +int GetUserColor() +{ + std::lock_guard lock(m_userdata_mutex); + return m_userdata.colournum; +} + +Ogre::UTFString GetUsername() +{ + std::lock_guard lock(m_userdata_mutex); + return m_username; +} + +user_info_t GetLocalUserData() +{ + std::lock_guard lock(m_userdata_mutex); + return m_userdata; +} + +std::vector GetUserInfos() +{ + std::lock_guard lock(m_users_mutex); + return m_users; +} + +bool GetUserInfo(int uid, user_info_t &result) +{ + std::lock_guard lock(m_users_mutex); + for (user_info_t user : m_users) + { + if ((int)user.uniqueid == uid) + { + result = user; + return true; + } + } + return false; +} + +} // namespace Networking +} // namespace RoR + +#endif // USE_SOCKETW diff --git a/source/main/network/Network.h b/source/main/network/Network.h index fb359a4740..772a886d75 100644 --- a/source/main/network/Network.h +++ b/source/main/network/Network.h @@ -1,109 +1,66 @@ -/* -This source file is part of Rigs of Rods -Copyright 2005-2012 Pierre-Michel Ricordel -Copyright 2007-2012 Thomas Fischer - -For more information, see http://www.rigsofrods.com/ - -Rigs of Rods is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3, as -published by the Free Software Foundation. - -Rigs of Rods 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 Rigs of Rods. If not, see . -*/ -#ifdef USE_SOCKETW - -#pragma once -#ifndef __Network_H_ -#define __Network_H_ - -#include "RoRPrerequisites.h" - -#include "rornet.h" -#include "SocketW.h" - -#include -#include -#include - -class Network : public ZeroedMemoryAllocator -{ -public: - - Network(Ogre::String servername, long server_port); - ~Network(); - - // messaging functions - int sendMessageRaw(char *content, unsigned int msgsize); - int sendmessage(int type, unsigned int streamid, unsigned int len, char* content); - int sendScriptMessage(char* content, unsigned int len); - int receivemessage(header_t *header, char* content, unsigned int bufferlen); - - // methods - bool connect(); - void disconnect(); - void netFatalError(Ogre::UTFString error, bool exit=true); - - void sendthreadstart(); - void receivethreadstart(); - - Ogre::UTFString getNickname(bool colour=false); - char *getTerrainName() { return server_settings.terrain; }; - client_t *getClientInfo(unsigned int uid); - int getClientInfos(client_t c[MAX_PEERS]); - int getNetQuality(); - int getSpeedDown(); - int getSpeedUp(); - static unsigned long getNetTime(); - unsigned int getUID() { return myuid; }; - user_info_t *getLocalUserData() { return &userdata; }; - - static void debugPacket(const char *name, header_t *header, char *buffer); - -private: - - Ogre::UTFString m_server_name; - Ogre::UTFString nickname; - SWInetSocket socket; - bool initiated; - bool shutdown; - char sendthreadstart_buffer[MAX_MESSAGE_LENGTH]; - char* send_buffer; - client_t clients[MAX_PEERS]; - int last_time; - int myauthlevel; - int rconauthed; - int send_buffer_len; - int speed_bytes_sent, speed_bytes_sent_tmp, speed_bytes_recv, speed_bytes_recv_tmp; - int speed_time; - long m_server_port; - std::condition_variable send_work_cv; - std::mutex clients_mutex; - std::mutex dl_data_mutex; - std::mutex msgsend_mutex; - std::mutex send_work_mutex; - server_info_t server_settings; - static Ogre::Timer timer; - unsigned int myuid; - std::map lagDataClients; - user_info_t userdata; - - void calcSpeed(); - void updatePlayerList(); - - Ogre::UTFString getUserChatName(client_t *c); - - std::atomic net_quality; - - void setNetQuality(int q); -}; - -#endif // __Network_H_ - -#endif // USE_SOCKETW +/* + This source file is part of Rigs of Rods + Copyright 2005-2012 Pierre-Michel Ricordel + Copyright 2007-2012 Thomas Fischer + Copyright 2013-2016 Petr Ohlidal + + For more information, see http://www.rigsofrods.com/ + + Rigs of Rods is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License version 3, as + published by the Free Software Foundation. + + Rigs of Rods 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 Rigs of Rods. If not, see . +*/ +#ifdef USE_SOCKETW + +#pragma once +#ifndef __Network_H_ +#define __Network_H_ + +#include "RoRPrerequisites.h" + +#include "SocketW.h" +#include "rornet.h" + +namespace RoR { +namespace Networking { + +typedef struct recv_packet_t +{ + header_t header; + char buffer[MAX_MESSAGE_LENGTH]; +} recv_packet_t; + +bool Connect(); +void Disconnect(); + +void AddPacket(int streamid, int type, int len, char *content); +void AddLocalStream(stream_register_t *reg, int size); + +void HandleStreamData(); + +int GetUID(); +int GetNetQuality(); + +Ogre::String GetTerrainName(); + +int GetUserColor(); +Ogre::UTFString GetUsername(); +user_info_t GetLocalUserData(); + +std::vector GetUserInfos(); +bool GetUserInfo(int uid, user_info_t &result); + +} // namespace Networking +} // namespace RoR + +#endif // __Network_H_ + +#endif // USE_SOCKETW diff --git a/source/main/network/NetworkStreamManager.cpp b/source/main/network/NetworkStreamManager.cpp deleted file mode 100644 index aaf24012af..0000000000 --- a/source/main/network/NetworkStreamManager.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* -This source file is part of Rigs of Rods -Copyright 2005-2012 Pierre-Michel Ricordel -Copyright 2007-2012 Thomas Fischer - -For more information, see http://www.rigsofrods.com/ - -Rigs of Rods is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3, as -published by the Free Software Foundation. - -Rigs of Rods 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 Rigs of Rods. If not, see . -*/ -// created by Thomas Fischer thomas{AT}thomasfischer{DOT}biz, 7th of August 2009 - -#include "NetworkStreamManager.h" - -#include "Network.h" -#include "Streamable.h" -#include "StreamableFactoryInterface.h" - -using namespace Ogre; - -NetworkStreamManager::NetworkStreamManager() : - send_start(false) - , streamid(10) -{ -} - -NetworkStreamManager::~NetworkStreamManager() -{ -} - -void NetworkStreamManager::addLocalStream(Streamable *stream, stream_register_t *reg, unsigned int size) -{ -#ifdef USE_SOCKETW - int mysourceid = gEnv->network->getUID(); - - stream->setSourceID(mysourceid); - stream->setStreamID(streamid); - - reg->origin_sourceid = mysourceid; - reg->origin_streamid = streamid; - reg->status = 0; - - stream->isOrigin = true; - - { - std::lock_guard lock(m_stream_mutex); - - streams[mysourceid][streamid] = stream; - - if (size == 0) size = sizeof(stream_register_t); - stream->addPacket(MSG2_STREAM_REGISTER, size, (char*)reg); - LOG("adding local stream: " + TOSTRING(mysourceid) + ":"+ TOSTRING(streamid) + ", type: " + TOSTRING(reg->type)); - } - - streamid++; -#endif // USE_SOCKETW -} - -void NetworkStreamManager::addRemoteStream(Streamable *stream, int rsource, int rstreamid) -{ - std::lock_guard lock(m_stream_mutex); - - streams[rsource][rstreamid] = stream; - LOG("adding remote stream: " + TOSTRING(rsource) + ":"+ TOSTRING(rstreamid)); -} - -void NetworkStreamManager::removeLocalStream(Streamable *stream) -{ -#ifdef USE_SOCKETW - gEnv->network->sendmessage(MSG2_STREAM_UNREGISTER, stream->streamid, 0, 0); - - this->removeStream(stream->sourceid, stream->streamid); -#endif // USE_SOCKETW -} - -void NetworkStreamManager::removeStream(int sourceid, int streamid) -{ -#ifdef USE_SOCKETW - int mysourceid = gEnv->network->getUID(); - - if (sourceid == -1) - { - sourceid = mysourceid; - } - - { - std::lock_guard lock(m_stream_mutex); - - std::map < int, std::map < unsigned int, Streamable *> >::iterator it_source = streams.find(sourceid); - std::map < unsigned int, Streamable *>::iterator it_stream; - - if (it_source != streams.end() && !it_source->second.empty()) - { - it_stream = it_source->second.find(streamid); - if (it_stream != it_source->second.end()) - streams[sourceid].erase(it_stream); - } - } - - if (sourceid != mysourceid) - { - // now iterate over all factories and remove their instances (only triggers) - std::vector < StreamableFactoryInterface * >::iterator it; - for (it=factories.begin(); it!=factories.end(); it++) - { - (*it)->deleteRemote(sourceid, streamid); - } - } -#endif // USE_SOCKETW -} - - -void NetworkStreamManager::pauseStream(Streamable *stream) -{ -} - -void NetworkStreamManager::resumeStream(Streamable *stream) -{ -} - -#ifdef USE_SOCKETW -void NetworkStreamManager::removeUser(int sourceID) -{ - { - std::lock_guard lock(m_stream_mutex); - if (streams.find(sourceID) == streams.end()) - { - // no such stream?! - return; - } - // found and deleted - streams.erase(streams.find(sourceID)); - } - - // now iterate over all factories and remove their instances (only triggers) - std::vector < StreamableFactoryInterface * >::iterator it; - for (it=factories.begin(); it!=factories.end(); it++) - { - (*it)->deleteRemote(sourceID, -1); // -1 = all streams - } -} -#endif // USE_SOCKETW - -void NetworkStreamManager::pushReceivedStreamMessage(header_t header, char *buffer) -{ - std::lock_guard lock(m_stream_mutex); - - if (streams.find(header.source) == streams.end()) - { - // no such stream?! - LOG("EEE Source not found: "+TOSTRING(header.source)+":"+TOSTRING(header.streamid)); - return; - } - if (streams.find(header.source)->second.find(header.streamid) == streams.find(header.source)->second.end()) - { - // no such stream?! - - // removed: too verbose - //if (header.streamid != 0) - // LOG("EEE Stream not found: "+TOSTRING(header.source)+":"+TOSTRING(header.streamid)); - - return; - } - streams[header.source][header.streamid]->addReceivedPacket(header, buffer); -} - -void NetworkStreamManager::triggerSend() -{ - { - std::lock_guard lock(m_send_work_mutex); - send_start = true; - } - m_send_work_cv.notify_all(); -} - -void NetworkStreamManager::sendStreams(Network *net) -{ - { - std::unique_lock ss_lock(m_send_work_mutex); - m_send_work_cv.wait(ss_lock, [this]{ return send_start; }); - send_start = false; - } - - std::lock_guard lock(m_stream_mutex); - - std::map < int, std::map < unsigned int, Streamable *> >::iterator it; - for (it=streams.begin(); it!=streams.end(); it++) - { - std::map::iterator it2; - for (it2=it->second.begin(); it2!=it->second.end(); it2++) - { - if (!it2->second) continue; - it2->second->sendStream(net); - } - } -} - -#ifdef USE_SOCKETW -void NetworkStreamManager::update() -{ - syncRemoteStreams(); - receiveStreams(); -} -#endif // USE_SOCKETW - -void NetworkStreamManager::syncRemoteStreams() -{ - // iterate over all factories - std::vector < StreamableFactoryInterface * >::iterator it; - for (it=factories.begin(); it!=factories.end(); it++) - { - (*it)->syncRemoteStreams(); - } -} - -void NetworkStreamManager::receiveStreams() -{ - std::lock_guard lock(m_stream_mutex); - - std::map < int, std::map < unsigned int, Streamable *> >::iterator it; - for (it=streams.begin(); it!=streams.end(); it++) - { - std::map::iterator it2; - for (it2=it->second.begin(); it2!=it->second.end(); it2++) - { - if (!it2->second) continue; - it2->second->receiveStream(); - } - } -} - -void NetworkStreamManager::addFactory(StreamableFactoryInterface *factory) -{ - this->factories.push_back(factory); -} - diff --git a/source/main/network/NetworkStreamManager.h b/source/main/network/NetworkStreamManager.h deleted file mode 100644 index 6ca70eeffd..0000000000 --- a/source/main/network/NetworkStreamManager.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -This source file is part of Rigs of Rods -Copyright 2005-2012 Pierre-Michel Ricordel -Copyright 2007-2012 Thomas Fischer - -For more information, see http://www.rigsofrods.com/ - -Rigs of Rods is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3, as -published by the Free Software Foundation. - -Rigs of Rods 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 Rigs of Rods. If not, see . -*/ - -// created by Thomas Fischer thomas{AT}thomasfischer{DOT}biz, 7th of August 2009 - -#pragma once -#ifndef __NetworkStreamManager_H_ -#define __NetworkStreamManager_H_ - -#include "RoRPrerequisites.h" - -#include "rornet.h" -#include "Singleton.h" - -#include -#include - -class Streamable; -class StreamableFactoryInterface; - -class NetworkStreamManager : public RoRSingleton< NetworkStreamManager >, public ZeroedMemoryAllocator -{ - friend class Network; - -public: - - NetworkStreamManager(); - ~NetworkStreamManager(); - - void addLocalStream(Streamable *stream, stream_register_t *reg, unsigned int size=0); - void addRemoteStream(Streamable *stream, int source=-1, int streamid=-1); - void removeLocalStream(Streamable *stream); - - void pauseStream(Streamable *stream); - void resumeStream(Streamable *stream); - - void triggerSend(); - - void sendStreams(Network *net); - - void update(); - - void removeUser(int sourceID); - - void addFactory(StreamableFactoryInterface *factory); - -protected: - - std::mutex m_stream_mutex; - std::mutex m_send_work_mutex; - std::condition_variable m_send_work_cv; - - bool send_start; - - std::map < int, std::map < unsigned int, Streamable *> > streams; - std::vector < StreamableFactoryInterface * > factories; - - unsigned int streamid; - - void pushReceivedStreamMessage(header_t header, char *buffer); - - void syncRemoteStreams(); - void receiveStreams(); - - void removeStream(int sourceid, int streamid); -}; - -#endif // __NetworkStreamManager_H_ diff --git a/source/main/network/Streamable.cpp b/source/main/network/Streamable.cpp deleted file mode 100644 index 5d34d9006f..0000000000 --- a/source/main/network/Streamable.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* -This source file is part of Rigs of Rods -Copyright 2005-2012 Pierre-Michel Ricordel -Copyright 2007-2012 Thomas Fischer - -For more information, see http://www.rigsofrods.com/ - -Rigs of Rods is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3, as -published by the Free Software Foundation. - -Rigs of Rods 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 Rigs of Rods. If not, see . -*/ -// created by Thomas Fischer thomas{AT}thomasfischer{DOT}biz, 7th of August 2009 - -#include "Streamable.h" - -#include "Language.h" -#include "Network.h" -#include "NetworkStreamManager.h" - -using namespace Ogre; - -Streamable::Streamable() : isOrigin(false) -{ - //NetworkStreamManager::getSingleton().addStream(this); -} - -Streamable::~Streamable() -{ -} - -void Streamable::addPacket(int type, unsigned int len, char* content) -{ -#ifdef USE_SOCKETW - if (len > maxPacketLen) - // packet too big, discarded - return; - - int uid = gEnv->network->getUID(); - unsigned int streamid = this->streamid; //we stored the streamid upon stream registration in this class - - bufferedPacket_t packet; - memset(&packet, 0, sizeof(bufferedPacket_t)); - - // allocate buffer - //packet.packetBuffer = (char*)calloc(len, sizeof(char)); - - char *buffer = (char*)(packet.packetBuffer); - - // write header in buffer - header_t *head = (header_t *)buffer; - head->command = type; - head->source = uid; - head->size = len; - head->streamid = streamid; - - // then copy the contents - char *bufferContent = (char *)(buffer + sizeof(header_t)); - memcpy(bufferContent, content, len); - - // record the packet size - packet.size = len + sizeof(header_t); - - /* - String header_hex = hexdump(buffer, sizeof(header_t)); - String content_hex = hexdump((buffer + sizeof(header_t)), len); - - LOG("header: " + header_hex); - LOG("content: " + content_hex); - */ - - /* - char hash[256] = ""; - RoR::CSHA1 sha1; - sha1.UpdateHash((uint8_t *)bufferContent, len); - sha1.Final(); - sha1.ReportHash(hash, RoR::CSHA1::REPORT_HEX_SHORT); - LOG("S|HASH: " + String(hash)); - */ - - { - std::lock_guard lock(m_send_work_mutex); - - if (packets.size() > packetBufferSizeDiscardData && type == MSG2_STREAM_DATA) - // discard unimportant data packets for some while - return; - - if (packets.size() > packetBufferSize) - // buffer full, packet discarded - return; - - packets.push_back(packet); - } - - // trigger buffer clearing - NetworkStreamManager::getSingleton().triggerSend(); -#endif //SOCKETW -} - -void Streamable::addReceivedPacket(header_t header, char *buffer) -{ - std::lock_guard lock(m_recv_work_mutex); - - if (receivedPackets.size() > packetBufferSize) - // buffer full, packet discarded - return; - - // construct the data holding struct - recvPacket_t packet; - memset(&packet, 0, sizeof(packet)); // we need to do this, since we use String(buffer) at some point which will crash otherwise - packet.header = header; - memcpy(packet.buffer, buffer, header.size); - - receivedPackets.push_back(packet); -} - -void Streamable::sendStream(Network *net) -{ - if (packets.size() == 0) return; - - std::lock_guard lock(m_send_work_mutex); - - while (!packets.empty()) - { - // remove oldest packet in queue - Streamable::bufferedPacket_t packet = packets.front(); - - int etype = net->sendMessageRaw(packet.packetBuffer, packet.size); - if (etype) - { - wchar_t emsg[256]; - UTFString tmp = _L("Error %i while sending data packet"); - swprintf(emsg, 256, tmp.asWStr_c_str(), etype); - net->netFatalError(UTFString(emsg)); - return; - } - - packets.pop_front(); - } -} - -void Streamable::receiveStream() -{ - if (receivedPackets.size() == 0) return; - - std::lock_guard lock(m_recv_work_mutex); - - while (!receivedPackets.empty()) - { - // remove oldest packet in queue - recvPacket_t packet = receivedPackets.front(); - - //Network::debugPacket("receive-2", &packet.header, (char *)packet.buffer); - - receiveStreamData(packet.header.command, packet.header.source, packet.header.streamid, (char*)packet.buffer, packet.header.size); - - receivedPackets.pop_front(); - } -} - -void Streamable::addStreamRegistrationResult(int sourceid, stream_register_t reg) -{ - mStreamableResults[sourceid] = reg; -} - -int Streamable::getStreamRegisterResultForSource(int sourceid, stream_register_t *reg) -{ - if (mStreamableResults.find(sourceid) == mStreamableResults.end()) - return 1; - *reg = mStreamableResults[sourceid]; - return 0; -} diff --git a/source/main/network/Streamable.h b/source/main/network/Streamable.h deleted file mode 100644 index 133418897d..0000000000 --- a/source/main/network/Streamable.h +++ /dev/null @@ -1,100 +0,0 @@ -/* -This source file is part of Rigs of Rods -Copyright 2005-2012 Pierre-Michel Ricordel -Copyright 2007-2012 Thomas Fischer - -For more information, see http://www.rigsofrods.com/ - -Rigs of Rods is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3, as -published by the Free Software Foundation. - -Rigs of Rods 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 Rigs of Rods. If not, see . -*/ - -// created by Thomas Fischer thomas{AT}thomasfischer{DOT}biz, 7th of August 2009 - -#pragma once -#ifndef __Streamable_H_ -#define __Streamable_H_ - -#include "RoRPrerequisites.h" - -#include "rornet.h" - -#include - -/** - * This class defines a standard interface and a buffer between the actual network code and the class that handles it. - * The buffer must be decoupled from the separately running network thread. - */ -class Streamable -{ - friend class NetworkStreamManager; - -public: - - unsigned int getStreamID() { return this->streamid; }; - unsigned int getSourceID() { return this->sourceid; }; - void setStreamID(unsigned int id) { this->streamid=id; }; - void setSourceID(unsigned int id) { this->sourceid=id; }; - - bool getIsOrigin() { return isOrigin; }; - - void addStreamRegistrationResult(int source, stream_register_t reg); - int getStreamRegisterResultForSource(int sourceid, stream_register_t *reg); - -protected: - // constructor/destructor are protected, so you cannot create instances without using the factory - Streamable(); - ~Streamable(); - - // static const members - static const unsigned int packetBufferSize = 30; //!< hard buffer size limit - static const unsigned int packetBufferSizeDiscardData = 20; //!< we will discard all data packets after hitting this border. Then we have still some packets left for registration purposes. - static const unsigned int maxPacketLen = 8192; - - // virtual interface methods - virtual void sendStreamData() = 0; - virtual void receiveStreamData(unsigned int &type, int &source, unsigned int &streamid, char *buffer, unsigned int &len) = 0; - - // base class methods - void addPacket(int type, unsigned int len, char *content); - void addReceivedPacket(header_t header, char *buffer); - -private: - - void receiveStream(); - void sendStream(Network *net); - - std::mutex m_recv_work_mutex; - std::mutex m_send_work_mutex; - - typedef struct _bufferedPacket - { - char packetBuffer[maxPacketLen]; - unsigned int size; - } bufferedPacket_t; - - typedef struct recvPacket_t - { - header_t header; - char *buffer[MAX_MESSAGE_LENGTH]; - } recvPacket_t; - - std::deque < bufferedPacket_t > packets; - std::deque < recvPacket_t > receivedPackets; - - unsigned int sourceid, streamid; - - std::map < int, stream_register_t > mStreamableResults; - bool isOrigin; -}; - -#endif // __Streamable_H_ diff --git a/source/main/network/StreamableFactory.h b/source/main/network/StreamableFactory.h deleted file mode 100644 index fd9ce7ba81..0000000000 --- a/source/main/network/StreamableFactory.h +++ /dev/null @@ -1,338 +0,0 @@ -/* -This source file is part of Rigs of Rods -Copyright 2005-2012 Pierre-Michel Ricordel -Copyright 2007-2012 Thomas Fischer - -For more information, see http://www.rigsofrods.com/ - -Rigs of Rods is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3, as -published by the Free Software Foundation. - -Rigs of Rods 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 Rigs of Rods. If not, see . -*/ - -// created by Thomas Fischer thomas{AT}thomasfischer{DOT}biz, 12th of August 2009 - -#pragma once -#ifndef __StreamableFactory_H_ -#define __StreamableFactory_H_ - -#include "RoRPrerequisites.h" - -#include "NetworkStreamManager.h" -#include "StreamableFactoryInterface.h" - -#ifdef USE_SOCKETW -#include "SocketW.h" -#endif //SOCKETW - -#include - -template class StreamableFactory : public StreamableFactoryInterface -{ -public: - // constructor, destructor and singleton - StreamableFactory( void ) : locked(false) - { - MYASSERT( !_instance ); - _instance = static_cast< T* >( this ); - - // add self to factory list - NetworkStreamManager::getSingleton().addFactory(this); - } - - ~StreamableFactory( void ) - { - MYASSERT( _instance ); - _instance = 0; - } - - static T& getSingleton( void ) - { - MYASSERT( _instance ); - return ( *_instance ); - } - - static T* getSingletonPtr( void ) - { - return _instance; - } - - // useful functions - virtual X *createLocal(int colour) = 0; - virtual void netUserAttributesChanged(int source, int streamid) = 0; - virtual X *createRemoteInstance(stream_reg_t *reg) = 0; - - // common functions - void createRemote(int sourceid, int streamid, stream_register_t *reg, int colour) - { - lockStreams(); - - stream_reg_t registration; - registration.sourceid = sourceid; - registration.streamid = streamid; - registration.reg = *reg; // really store the data - registration.colour = colour; - stream_registrations.push_back(registration); - - unlockStreams(); - } - - void deleteRemote(int sourceid, int streamid) - { - lockStreams(); - - stream_del_t deletion; - deletion.sourceid = sourceid; - deletion.streamid = streamid; - stream_deletions.push_back(deletion); - - unlockStreams(); - } - - virtual bool syncRemoteStreams() - { - lockStreams(); - // first registrations - int changes = 0; - while (!stream_registrations.empty()) - { - stream_reg_t reg = stream_registrations.front(); - Streamable *s = createRemoteInstance(®); - if (s) - { - // add it to the streams list - NetworkStreamManager::getSingleton().addRemoteStream(s, reg.sourceid, reg.streamid); - reg.reg.status = 1; - } else - { - // error creating stream, tell the sourceid that it failed - reg.reg.status = -1; - } - // fixup the registration information - reg.reg.origin_sourceid = reg.sourceid; - reg.reg.origin_streamid = reg.streamid; - - // only save registration results for beam streams - // TODO: maybe enforce general design to allow all stream types to - // have a feedback channel - if (reg.reg.type == 0) - { - stream_creation_results.push_back(reg); - } - // remove registration from list - stream_registrations.pop_front(); - changes++; - } - - // count the stream creation results into the changes - changes += (int)stream_creation_results.size(); - - // then deletions: - // first registrations - while (!stream_deletions.empty()) - { - stream_del_t del = stream_deletions.front(); - removeInstance(&del); - stream_deletions.pop_front(); - changes++; - } - unlockStreams(); - return (changes > 0); - } - - void removeInstance(stream_del_t *del) - { - // already locked - //lockStreams(); - typename std::map < int, std::map < unsigned int, X *> > &streamables = getStreams(); - typename std::map < int, std::map < unsigned int, X *> >::iterator it1; - typename std::map < unsigned int, X *>::iterator it2; - - for (it1=streamables.begin(); it1!=streamables.end();++it1) - { - if (it1->first != del->sourceid) continue; - - for (it2=it1->second.begin(); it2!=it1->second.end();++it2) - { - if (del->streamid == -1 || del->streamid == (int)it2->first) - { - // deletes the stream - delete it2->second; - it2->second = 0; - } - } - break; - } - //unlockStreams(); - } - - int checkStreamsOK(int sourceid) - { - // walk client and the streams and checks for errors - lockStreams(); - typename std::map < int, std::map < unsigned int, X *> > &streamables = getStreams(); - typename std::map < int, std::map < unsigned int, X *> >::iterator it1; - typename std::map < unsigned int, X *>::iterator it2; - - int ok = 0; - int num = 0; - for (it1=streamables.begin(); it1!=streamables.end();++it1) - { - if (it1->first != sourceid) continue; - for (it2=it1->second.begin(); it2!=it1->second.end();++it2) - { - num++; - if (it2->second != 0) - { - ok = 1; - break; - } - } - break; - } - if (!num) - ok = 2; - unlockStreams(); - return ok; - } - - int checkStreamsRemoteOK(int sourceid) - { - // walk client and the streams and checks for errors - lockStreams(); - typename std::map < int, std::map < unsigned int, X *> > &streamables = getStreams(); - typename std::map < int, std::map < unsigned int, X *> >::iterator it1; - typename std::map < unsigned int, X *>::iterator it2; - - int ok = 0; - int originstreams = 0; - for (it1=streamables.begin(); it1!=streamables.end();++it1) - { - for (it2=it1->second.begin(); it2!=it1->second.end();++it2) - { - if (!it2->second) - continue; - if (!it2->second->getIsOrigin()) - continue; - originstreams++; - stream_register_t reg; - reg.status = -2; - int res = it2->second->getStreamRegisterResultForSource(sourceid, ®); - if (!res) - { - if (reg.status == 1) - ok = 1; - } - break; - } - break; - } - if (!originstreams) - ok = 2; - unlockStreams(); - return ok; - } - - int clearStreamRegistrationResults() - { - lockStreams(); - stream_creation_results.clear(); - unlockStreams(); - return 0; - } - - int getStreamRegistrationResults(std::deque < stream_reg_t > *net_results) - { - lockStreams(); - // move list entries over to the list in the networking thread - int res = 0; - while (!stream_creation_results.empty()) - { - stream_reg_t reg = stream_creation_results.front(); - net_results->push_back(reg); - stream_creation_results.pop_front(); - res++; - } - unlockStreams(); - return res; - } - - int addStreamRegistrationResults(int sourceid, stream_register_t *reg) - { - lockStreams(); - typename std::map < int, std::map < unsigned int, X *> > &streamables = getStreams(); - typename std::map < int, std::map < unsigned int, X *> >::iterator it1; - typename std::map < unsigned int, X *>::iterator it2; - - int res = 0; - for (it1=streamables.begin(); it1!=streamables.end();++it1) - { - for (it2=it1->second.begin(); it2!=it1->second.end();++it2) - { - if (!it2->second) - continue; - if (!it2->second->getIsOrigin()) - continue; - //int sid = it2->second->getSourceID(); // unused - int stid = it2->second->getStreamID(); - // only use our locally created streams - if (stid == reg->origin_streamid) - { - it2->second->addStreamRegistrationResult(sourceid, *reg); - if (reg->status == 1) - LOG("Client " + TOSTRING(sourceid) + " successfully loaded stream " + TOSTRING(reg->origin_streamid) + " with name '" + reg->name + "', result code: " + TOSTRING(reg->status)); - else - LOG("Client " + TOSTRING(sourceid) + " could not load stream " + TOSTRING(reg->origin_streamid) + " with name '" + reg->name + "', result code: " + TOSTRING(reg->status)); - res++; - break; - } - } - break; - } - unlockStreams(); - return res; - } - -protected: - static T* _instance; - std::mutex stream_reg_mutex; - bool locked; - - std::deque < stream_reg_t > stream_registrations; - std::deque < stream_del_t > stream_deletions; - std::deque < stream_reg_t > stream_creation_results; - - std::map < int, std::map < unsigned int, X *> > &getStreams() - { - // ensure we only access the map when we locked it before - MYASSERT(locked); - return mStreamables; - } - - void lockStreams() - { - stream_reg_mutex.lock(); - this->locked=true; - } - - void unlockStreams() - { - stream_reg_mutex.unlock(); - this->locked=false; - } - -private: - // no direct access to it, helps with locking it before using it - std::map < int, std::map < unsigned int, X *> > mStreamables; - -}; - -#endif // __StreamableFactory_H_ diff --git a/source/main/network/StreamableFactoryInterface.h b/source/main/network/StreamableFactoryInterface.h deleted file mode 100644 index 2a62a1e9c4..0000000000 --- a/source/main/network/StreamableFactoryInterface.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -This source file is part of Rigs of Rods -Copyright 2005-2012 Pierre-Michel Ricordel -Copyright 2007-2012 Thomas Fischer - -For more information, see http://www.rigsofrods.com/ - -Rigs of Rods is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3, as -published by the Free Software Foundation. - -Rigs of Rods 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 Rigs of Rods. If not, see . -*/ - -// created by Thomas Fischer thomas{AT}thomasfischer{DOT}biz, 22th of December 2009 - -#pragma once -#ifndef __StreamableFactoryInterface_H_ -#define __StreamableFactoryInterface_H_ - -#include "RoRPrerequisites.h" - -#include "rornet.h" - -class Streamable; - -typedef struct stream_reg_t -{ - int sourceid; - int streamid; - stream_register_t reg; - int colour; -} stream_reg_t; - -typedef struct stream_del_t -{ - int sourceid; - int streamid; -} stream_del_t; - -class StreamableFactoryInterface -{ -public: - - virtual void netUserAttributesChanged(int source, int streamid) = 0; - virtual Streamable *createRemoteInstance(stream_reg_t *reg) = 0; - virtual void createRemote(int sourceid, int streamid, stream_register_t *reg, int colour) = 0; - virtual void deleteRemote(int sourceid, int streamid) = 0; - virtual bool syncRemoteStreams() = 0; - virtual void removeInstance(stream_del_t *del) = 0; -}; - -#endif // __StreamableFactoryInterface_H_ diff --git a/source/main/physics/Beam.cpp b/source/main/physics/Beam.cpp index 36fc9fa48a..2f4aa1fc44 100644 --- a/source/main/physics/Beam.cpp +++ b/source/main/physics/Beam.cpp @@ -1741,11 +1741,10 @@ void Beam::handleTruckPosition(float dt) void Beam::sendStreamSetup() { - // register the local stream stream_register_trucks_t reg; memset(®, 0, sizeof(stream_register_trucks_t)); reg.status = 0; - reg.type = 0; // 0 = truck + reg.type = 0; reg.bufferSize = netbuffersize; strncpy(reg.name, realtruckfilename.c_str(), 128); if (!m_truck_config.empty()) @@ -1755,29 +1754,25 @@ void Beam::sendStreamSetup() strncpy(reg.truckconfig[i], m_truck_config[i].c_str(), 60); } - NetworkStreamManager::getSingleton().addLocalStream(this, (stream_register_t *)®, sizeof(reg)); + RoR::Networking::AddLocalStream((stream_register_t *)®, sizeof(stream_register_trucks_t)); + + m_source_id = reg.origin_sourceid; + m_stream_id = reg.origin_streamid; } void Beam::sendStreamData() { BES_GFX_START(BES_GFX_sendStreamData); #ifdef USE_SOCKETW - int t = netTimer.getMilliseconds(); - if (t - last_net_time < 100) - return; - - last_net_time = t; - //look if the packet is too big first int final_packet_size = sizeof(oob_t) + sizeof(float) * 3 + first_wheel_node * sizeof(float) * 3 + free_wheel * sizeof(float); - if (final_packet_size > (int)maxPacketLen) + if (final_packet_size > 8192) { ErrorUtils::ShowError(_L("Truck is too big to be send over the net."), _L("Network error!")); exit(126); } - char send_buffer[maxPacketLen]; - memset(send_buffer, 0, maxPacketLen); + char send_buffer[8192] = {0}; unsigned int packet_len = 0; @@ -1788,7 +1783,7 @@ void Beam::sendStreamData() send_oob->flagmask = 0; - send_oob->time = Network::getNetTime(); + send_oob->time = netTimer.getMilliseconds(); if (engine) { send_oob->engine_speed = engine->getRPM(); @@ -1842,7 +1837,6 @@ void Beam::sendStreamData() #endif //OPENAL } - // then process the contents { char *ptr = send_buffer + sizeof(oob_t); @@ -1880,17 +1874,17 @@ void Beam::sendStreamData() } } - this->addPacket(MSG2_STREAM_DATA, packet_len, send_buffer); + RoR::Networking::AddPacket(m_stream_id, MSG2_STREAM_DATA, packet_len, send_buffer); #endif //SOCKETW BES_GFX_STOP(BES_GFX_sendStreamData); } -void Beam::receiveStreamData(unsigned int &type, int &source, unsigned int &_streamid, char *buffer, unsigned int &len) +void Beam::receiveStreamData(unsigned int type, int source, unsigned int streamid, char *buffer, unsigned int len) { if (state != NETWORKED) return; BES_GFX_START(BES_GFX_receiveStreamData); - if (type == MSG2_STREAM_DATA && source == (int)this->getSourceID() && _streamid == this->getStreamID()) + if (type == MSG2_STREAM_DATA && source == m_source_id && streamid == m_stream_id) { pushNetwork(buffer, len); } @@ -4652,31 +4646,30 @@ void Beam::updateDebugOverlay() void Beam::updateNetworkInfo() { - if (!gEnv->network) return; + if (!gEnv->multiplayer) return; #ifdef USE_SOCKETW BES_GFX_START(BES_GFX_updateNetworkInfo); - bool remote = (state == NETWORKED); + user_info_t info; - if (remote) + if (state == NETWORKED) { - client_t *c = gEnv->network->getClientInfo(this->getSourceID()); - if (!c) return; - networkUsername = UTFString(c->user.username); - networkAuthlevel = c->user.authstatus; + if (!RoR::Networking::GetUserInfo(m_source_id, info)) + { + return; + } } else { - user_info_t *info = gEnv->network->getLocalUserData(); - if (!info) return; - if (UTFString(info->username).empty()) return; - networkUsername = UTFString(info->username); - networkAuthlevel = info->authstatus; + info = RoR::Networking::GetLocalUserData(); } + networkUsername = UTFString(info.username); + networkAuthlevel = info.authstatus; + +#if 0 if (netMT) { - /* if (networkAuthlevel & AUTH_ADMIN) { netMT->setFontName("highcontrast_red"); @@ -4687,8 +4680,8 @@ void Beam::updateNetworkInfo() { netMT->setFontName("highcontrast_black"); } - */ } +#endif BES_GFX_STOP(BES_GFX_updateNetworkInfo); #endif //SOCKETW @@ -5371,7 +5364,6 @@ Beam::Beam( , interPointCD() , intraPointCD() , isInside(false) - , last_net_time(0) , lastposition(pos) , leftMirrorAngle(0.52) , lights(1) @@ -5386,7 +5378,9 @@ Beam::Beam( , m_request_skeletonview_change(0) , m_reset_request(REQUEST_RESET_NONE) , m_skeletonview_is_active(false) + , m_source_id(0) , m_spawn_rotation(0.0) + , m_stream_id(0) , mTimeUntilNextToggle(0) , meshesVisible(true) , minCameraRadius(-1.0f) diff --git a/source/main/physics/Beam.h b/source/main/physics/Beam.h index 685df4f26a..36740e4355 100644 --- a/source/main/physics/Beam.h +++ b/source/main/physics/Beam.h @@ -28,7 +28,6 @@ along with Rigs of Rods. If not, see . #include "RigDef_Prerequisites.h" #include "BeamData.h" -#include "Streamable.h" #include @@ -40,7 +39,6 @@ class Task; */ class Beam : public rig_t, - public Streamable, public ZeroedMemoryAllocator { friend class RigSpawner; @@ -502,6 +500,12 @@ class Beam : bool m_is_videocamera_disabled; + int m_source_id; + int m_stream_id; + std::map m_stream_results; + + void receiveStreamData(unsigned int type, int source, unsigned int streamid, char *buffer, unsigned int len); + /** * Sets visibility of all beams on this vehicle * @param visible Toggle @@ -727,9 +731,7 @@ class Beam : // overloaded from Streamable: Ogre::Timer netTimer; - int last_net_time; void sendStreamSetup(); - void receiveStreamData(unsigned int &type, int &source, unsigned int &streamid, char *buffer, unsigned int &len); // dustpools DustPool *dustp; diff --git a/source/main/physics/BeamFactory.cpp b/source/main/physics/BeamFactory.cpp index 0eb6f54da0..a8a73a8568 100644 --- a/source/main/physics/BeamFactory.cpp +++ b/source/main/physics/BeamFactory.cpp @@ -40,6 +40,7 @@ along with Rigs of Rods. If not, see . #include "Settings.h" #include "SoundScriptManager.h" #include "ThreadPool.h" +#include "Utils.h" #include "VehicleAI.h" #ifdef _GNU_SOURCE @@ -56,9 +57,9 @@ along with Rigs of Rods. If not, see . #include "DashBoardManager.h" #endif // USE_MYGUI -using namespace Ogre; +#include -template<> BeamFactory *StreamableFactory < BeamFactory, Beam >::_instance = 0; +using namespace Ogre; void cpuID(unsigned i, unsigned regs[4]) { #ifdef _WIN32 @@ -218,7 +219,7 @@ Beam *BeamFactory::CreateLocalRigInstance( fname.c_str(), &rig_loading_profiler, false, // networked - gEnv->network != nullptr, // networking + gEnv->multiplayer, // networking spawnbox, ismachine, truckconfig, @@ -235,17 +236,12 @@ Beam *BeamFactory::CreateLocalRigInstance( b->toggleSlideNodeLock(); } - lockStreams(); - std::map < int, std::map < unsigned int, Beam *> > &streamables = getStreams(); - streamables[-1][10 + truck_num] = b; // 10 streams offset for beam constructions - unlockStreams(); - #ifdef USE_MYGUI GUI_MainMenu::getSingleton().triggerUpdateVehicleList(); #endif // USE_MYGUI // add own username to truck - if (gEnv->network) + if (gEnv->multiplayer) { b->updateNetworkInfo(); } @@ -261,57 +257,38 @@ Beam *BeamFactory::CreateLocalRigInstance( #undef LOADRIG_PROFILER_CHECKPOINT -Beam *BeamFactory::createRemoteInstance(stream_reg_t *reg) +int BeamFactory::CreateRemoteInstance(stream_register_trucks_t *reg) { - // NO LOCKS IN HERE, already locked - - stream_register_trucks_t *treg = (stream_register_trucks_t *)®->reg; - - LOG(" new beam truck for " + TOSTRING(reg->sourceid) + ":" + TOSTRING(reg->streamid)); + LOG(" new beam truck for " + TOSTRING(reg->origin_sourceid) + ":" + TOSTRING(reg->origin_streamid)); #ifdef USE_SOCKETW - // log a message about this - if (gEnv->network) - { - client_t *c = gEnv->network->getClientInfo(reg->sourceid); - if (c) - { - UTFString username = ChatSystem::getColouredName(*c); - UTFString message = username + ChatSystem::commandColour + _L(" spawned a new vehicle: ") + ChatSystem::normalColour + treg->name; + user_info_t info; + RoR::Networking::GetUserInfo(reg->origin_sourceid, info); + + UTFString message = RoR::ChatSystem::GetColouredName(info.username, info.colournum) + RoR::Color::CommandColour + _L(" spawned a new vehicle: ") + RoR::Color::NormalColour + reg->name; #ifdef USE_MYGUI - RoR::Application::GetGuiManager()->pushMessageChatBox(message); + RoR::Application::GetGuiManager()->pushMessageChatBox(message); #endif // USE_MYGUI - } - } #endif // USE_SOCKETW // check if we got this truck installed - String filename = String(treg->name); + String filename = String(reg->name); String group = ""; if (!RoR::Application::GetCacheSystem()->checkResourceLoaded(filename, group)) { LOG("wont add remote stream (truck not existing): '"+filename+"'"); - - // add 0 to the map so we know its stream is existing but not usable for us - // already locked - //lockStreams(); - std::map < int, std::map < unsigned int, Beam *> > &streamables = getStreams(); - streamables[reg->sourceid][reg->streamid] = 0; - //unlockStreams(); - - return 0; + return -1; } // fill truckconfig std::vector truckconfig; for (int t=0; t<10; t++) { - if (!strnlen(treg->truckconfig[t], 60)) + if (!strnlen(reg->truckconfig[t], 60)) break; - truckconfig.push_back(String(treg->truckconfig[t])); + truckconfig.push_back(String(reg->truckconfig[t])); } - // DO NOT spawn the truck far off anywhere // the truck parsing will break flexbodies initialization when using huge numbers here Vector3 pos = Vector3::ZERO; @@ -320,17 +297,17 @@ Beam *BeamFactory::createRemoteInstance(stream_reg_t *reg) if (truck_num == -1) { LOG("ERROR: could not add beam to main list"); - return 0; + return -1; } RoR::RigLoadingProfiler p; // TODO: Placeholder. Use it Beam *b = new Beam( truck_num, pos, Quaternion::ZERO, - reg->reg.name, + reg->name, &p, true, // networked - gEnv->network != nullptr, // networking + gEnv->multiplayer, // networking nullptr, // spawnbox false, // ismachine &truckconfig, @@ -339,71 +316,148 @@ Beam *BeamFactory::createRemoteInstance(stream_reg_t *reg) m_trucks[truck_num] = b; - b->setSourceID(reg->sourceid); - b->setStreamID(reg->streamid); - - // already locked - //lockStreams(); - std::map < int, std::map < unsigned int, Beam *> > &streamables = getStreams(); - streamables[reg->sourceid][reg->streamid] = b; - //unlockStreams(); - + b->m_source_id = reg->origin_sourceid; + b->m_stream_id = reg->origin_streamid; b->updateNetworkInfo(); #ifdef USE_MYGUI GUI_MainMenu::getSingleton().triggerUpdateVehicleList(); #endif // USE_MYGUI - return b; + return 1; } -void BeamFactory::localUserAttributesChanged(int new_id) +void BeamFactory::RemoveStreamSource(int sourceid) { - lockStreams(); - std::map < int, std::map < unsigned int, Beam *> > &streamables = getStreams(); + m_stream_mismatches.erase(sourceid); - if (streamables.find(-1) != streamables.end()) + for (int t=0; t < m_free_truck; t++) { - //Beam *b = streamables[-1][0]; - streamables[new_id][0] = streamables[-1][0]; // add alias :) - //b->setUID(newid); - //b->updateNetLabel(); + if (!m_trucks[t]) continue; + if (m_trucks[t]->state != NETWORKED) continue; + + if (m_trucks[t]->m_source_id == sourceid) + { + this->DeleteTruck(m_trucks[t]); + } } - unlockStreams(); } -void BeamFactory::netUserAttributesChanged(int source_id, int stream_id) +void BeamFactory::handleStreamData(std::vector packet_buffer) { - lockStreams(); - std::map < int, std::map < unsigned int, Beam *> > &streamables = getStreams(); - std::map < int, std::map < unsigned int, Beam *> >::iterator it_source = streamables.find(source_id); - std::map < unsigned int, Beam *>::iterator it_stream; + for (auto packet : packet_buffer) + { + if (packet.header.command == MSG2_STREAM_REGISTER) + { + stream_register_t *reg = (stream_register_t *)packet.buffer; + if (reg->type == 0) + { + reg->status = this->CreateRemoteInstance((stream_register_trucks_t *)packet.buffer); + RoR::Networking::AddPacket(0, MSG2_STREAM_REGISTER_RESULT, sizeof(stream_register_t), (char *)reg); + } + } else if (packet.header.command == MSG2_STREAM_REGISTER_RESULT) + { + stream_register_t *reg = (stream_register_t *)packet.buffer; + for (int t=0; t < m_free_truck; t++) + { + if (!m_trucks[t]) continue; + if (m_trucks[t]->state == NETWORKED) continue; + if (m_trucks[t]->m_stream_id == reg->origin_streamid) + { + int sourceid = packet.header.source; + m_trucks[t]->m_stream_results[sourceid] = reg->status; + + if (reg->status == 1) + LOG("Client " + TOSTRING(sourceid) + " successfully loaded stream " + TOSTRING(reg->origin_streamid) + " with name '" + reg->name + "', result code: " + TOSTRING(reg->status)); + else + LOG("Client " + TOSTRING(sourceid) + " could not load stream " + TOSTRING(reg->origin_streamid) + " with name '" + reg->name + "', result code: " + TOSTRING(reg->status)); - if (it_source != streamables.end() && !it_source->second.empty()) + break; + } + } + } else if (packet.header.command == MSG2_STREAM_UNREGISTER) + { + Beam *b = this->getBeam(packet.header.source, packet.header.streamid); + if (b && b->state == NETWORKED) + { + this->DeleteTruck(b); + } + auto search = m_stream_mismatches.find(packet.header.source); + if (search != m_stream_mismatches.end()) + { + auto &mismatches = search->second; + auto it = std::find(mismatches.begin(), mismatches.end(), packet.header.streamid); + if (it != mismatches.end()) + mismatches.erase(it); + } + } else if (packet.header.command == MSG2_USER_LEAVE) + { + this->RemoveStreamSource(packet.header.source); + } else + { + for (int t=0; t < m_free_truck; t++) + { + if (!m_trucks[t]) continue; + if (m_trucks[t]->state != NETWORKED) continue; + + m_trucks[t]->receiveStreamData(packet.header.command, packet.header.source, packet.header.streamid, packet.buffer, packet.header.size); + } + } + } +} + +int BeamFactory::checkStreamsOK(int sourceid) +{ + if (m_stream_mismatches[sourceid].size() > 0) + return 0; + + for (int t=0; t < m_free_truck; t++) { - it_stream = it_source->second.find(stream_id); - if (it_stream != it_source->second.end() && it_stream->second) - it_stream->second->updateNetworkInfo(); + if (!m_trucks[t]) continue; + if (m_trucks[t]->state != NETWORKED) continue; + + if (m_trucks[t]->m_source_id == sourceid) + { + return 1; + } } - unlockStreams(); + + return 2; } -Beam *BeamFactory::getBeam(int source_id, int stream_id) +int BeamFactory::checkStreamsRemoteOK(int sourceid) { - lockStreams(); - Beam *retVal = 0; - std::map < int, std::map < unsigned int, Beam *> > &streamables = getStreams(); - std::map < int, std::map < unsigned int, Beam *> >::iterator it_source = streamables.find(source_id); - std::map < unsigned int, Beam *>::iterator it_stream; + int result = 2; + + for (int t=0; t < m_free_truck; t++) + { + if (!m_trucks[t]) continue; + if (m_trucks[t]->state == NETWORKED) continue; + + int stream_result = m_trucks[t]->m_stream_results[sourceid]; + if (stream_result == -1) + return 0; + if (stream_result == 1) + result = 1; + } + + return result; +} - if (it_source != streamables.end() && !it_source->second.empty()) +Beam *BeamFactory::getBeam(int source_id, int stream_id) +{ + for (int t=0; t < m_free_truck; t++) { - it_stream = it_source->second.find(stream_id); - if (it_stream != it_source->second.end() && it_stream->second) - retVal = it_stream->second; + if (!m_trucks[t]) continue; + if (m_trucks[t]->state != NETWORKED) continue; + + if (m_trucks[t]->m_source_id == source_id && m_trucks[t]->m_stream_id == stream_id) + { + return m_trucks[t]; + } } - unlockStreams(); - return retVal; + + return nullptr; } bool BeamFactory::truckIntersectionAABB(int a, int b) @@ -670,7 +724,7 @@ void BeamFactory::DeleteTruck(Beam *b) if (b->networking && b->state != NETWORKED && b->state != INVALID) { - NetworkStreamManager::getSingleton().removeLocalStream(b); + RoR::Networking::AddPacket(b->m_stream_id, MSG2_STREAM_UNREGISTER, 0, 0); } if (m_current_truck == b->trucknum) @@ -847,7 +901,7 @@ void BeamFactory::updateVisual(float dt) RoR::Mirrors::Update(getCurrentTruck()); } -void BeamFactory::calcPhysics(float dt) +void BeamFactory::update(float dt) { m_physics_frames++; @@ -940,39 +994,6 @@ void BeamFactory::calcPhysics(float dt) } } } -void BeamFactory::removeInstance(stream_del_t *del) -{ - // we override this here so we can also delete the truck array content - // already locked - // lockStreams(); - std::map < int, std::map < unsigned int, Beam *> > &streamables = getStreams(); - std::map < int, std::map < unsigned int, Beam *> >::iterator it_stream = streamables.find(del->sourceid);; - std::map < unsigned int, Beam *>::iterator it_beam; - - if (it_stream == streamables.end() || it_stream->second.empty()) - // no stream for this source id - return; - - if (del->streamid == -1) - { - // delete all streams - for (it_beam=it_stream->second.begin(); it_beam != it_stream->second.end(); it_beam++) - { - this->DeleteTruck(it_beam->second); - it_beam->second = 0; - } - } else - { - // find the stream matching the streamid - it_beam = it_stream->second.find(del->streamid); - if (it_beam != it_stream->second.end()) - { - this->DeleteTruck(it_beam->second); - it_beam->second = 0; - } - } - // unlockStreams(); -} void BeamFactory::windowResized() { diff --git a/source/main/physics/BeamFactory.h b/source/main/physics/BeamFactory.h index ea0307b77e..0ddba50f00 100644 --- a/source/main/physics/BeamFactory.h +++ b/source/main/physics/BeamFactory.h @@ -27,7 +27,8 @@ along with Rigs of Rods. If not, see . #include "RoRPrerequisites.h" #include "Beam.h" -#include "StreamableFactory.h" +#include "Network.h" +#include "Singleton.h" #define PHYSICS_DT 0.0005 // fixed dt of 0.5 ms @@ -36,20 +37,13 @@ class ThreadPool; /** * Builds and manages vehicles; Manages multithreading. */ -class BeamFactory : public StreamableFactory < BeamFactory, Beam >, public ZeroedMemoryAllocator +class BeamFactory : public RoRSingleton< BeamFactory >, public ZeroedMemoryAllocator { - friend class Network; - public: BeamFactory(); ~BeamFactory(); - /** - * Does nothing; empty implementation of interface function. - */ - Beam *createLocal(int slotid) { return 0; } - /** * @param cache_entry_number Needed for flexbody caching. Pass -1 if unavailable (flexbody caching will be disabled) */ @@ -66,7 +60,11 @@ class BeamFactory : public StreamableFactory < BeamFactory, Beam >, public Zeroe bool preloaded_with_terrain = false ); - Beam *createRemoteInstance(stream_reg_t *reg); + void update(float dt); + + void handleStreamData(std::vector packet); + int checkStreamsOK(int sourceid); + int checkStreamsRemoteOK(int sourceid); int getNumCpuCores() { return m_num_cpu_cores; }; @@ -117,7 +115,6 @@ class BeamFactory : public StreamableFactory < BeamFactory, Beam >, public Zeroe inline unsigned long getPhysFrame() { return m_physics_frames; }; - void calcPhysics(float dt); void recalcGravityMasses(); /** @@ -158,6 +155,12 @@ class BeamFactory : public StreamableFactory < BeamFactory, Beam >, public Zeroe protected: + int CreateRemoteInstance(stream_register_trucks_t *reg); + void RemoveStreamSource(int sourceid); + + // A list of streams without a corresponding truck in the truck array for each stream source + std::map> m_stream_mismatches; + std::unique_ptr m_sim_thread_pool; std::shared_ptr m_sim_task; @@ -190,14 +193,7 @@ class BeamFactory : public StreamableFactory < BeamFactory, Beam >, public Zeroe int GetFreeTruckSlot(); int FindTruckInsideBox(Collisions *collisions, const Ogre::String &inst, const Ogre::String &box); - // functions used by friends - void netUserAttributesChanged(int source, int streamid); - void localUserAttributesChanged(int newid); - void DeleteTruck(Beam *b); - - // Overrides StreamableFactory::removeInstance - void removeInstance(stream_del_t *del); }; #endif // __BeamFactory_H_ diff --git a/source/main/scripting/GameScript.cpp b/source/main/scripting/GameScript.cpp index a316bfc93e..f870ec6826 100644 --- a/source/main/scripting/GameScript.cpp +++ b/source/main/scripting/GameScript.cpp @@ -871,13 +871,13 @@ int GameScript::deleteScriptVariable(const String &arg) int GameScript::sendGameCmd(const String& message) { - if (!gEnv->network) + if (gEnv->multiplayer) { - return -11; - } else - { - return gEnv->network->sendScriptMessage(const_cast(message.c_str()), (unsigned int)message.size()); + RoR::Networking::AddPacket(0, MSG2_GAME_CMD, (int)message.size(), const_cast(message.c_str())); + return 0; } + + return -11; } VehicleAI *GameScript::getCurrentTruckAI() diff --git a/source/main/terrain/TerrainManager.cpp b/source/main/terrain/TerrainManager.cpp index 8c94892010..5c5a21ec22 100644 --- a/source/main/terrain/TerrainManager.cpp +++ b/source/main/terrain/TerrainManager.cpp @@ -718,7 +718,7 @@ void TerrainManager::initScripting() bool loaded = false; // only load terrain scripts while not in multiplayer - if (!gEnv->network) + if (!gEnv->multiplayer) { try { diff --git a/source/main/terrain/TerrainObjectManager.cpp b/source/main/terrain/TerrainObjectManager.cpp index 0de50c4db9..3e5bdc2fa4 100644 --- a/source/main/terrain/TerrainObjectManager.cpp +++ b/source/main/terrain/TerrainObjectManager.cpp @@ -1224,7 +1224,7 @@ bool TerrainObjectManager::updateAnimatedObjects(float dt) void TerrainObjectManager::loadPreloadedTrucks() { // in netmode, don't load other trucks! - if (gEnv->network != nullptr) + if (gEnv->multiplayer) { return; } diff --git a/source/main/utils/Utils.h b/source/main/utils/Utils.h index c8ea1c01ab..41e3b513c0 100644 --- a/source/main/utils/Utils.h +++ b/source/main/utils/Utils.h @@ -115,12 +115,17 @@ void generateHashFromFile(Ogre::String filename, Ogre::String &hash); namespace RoR { - namespace Utils { std::string TrimBlanksAndLinebreaks(std::string const & input); std::string SanitizeUtf8String(std::string const& str_in); } - +namespace Color +{ + const Ogre::UTFString CommandColour = Ogre::UTFString("#00FF00"); + const Ogre::UTFString NormalColour = Ogre::UTFString("#FFFFFF"); + const Ogre::UTFString WhisperColour = Ogre::UTFString("#FFCC00"); + const Ogre::UTFString ScriptCommandColour = Ogre::UTFString("#0099FF"); +} }