// // Created by koncord on 04.01.16. // #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwclass/npc.hpp" #include "../mwmechanics/combat.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwstate/statemanagerimp.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include #include "Networking.hpp" #include "Main.hpp" #include "ProcessorInitializer.hpp" #include "PlayerProcessor.hpp" #include "WorldProcessor.hpp" #include "ActorProcessor.hpp" #include "GUIController.hpp" #include "CellController.hpp" using namespace std; using namespace mwmp; Networking::Networking(): peer(RakNet::RakPeerInterface::GetInstance()), playerPacketController(peer), actorPacketController(peer), worldPacketController(peer) { RakNet::SocketDescriptor sd; sd.port=0; RakNet::StartupResult b = peer->Startup(1,&sd, 1); RakAssert(b==RAKNET_STARTED); playerPacketController.SetStream(0, &bsOut); actorPacketController.SetStream(0, &bsOut); worldPacketController.SetStream(0, &bsOut); connected = 0; ProcessorInitializer(); } Networking::~Networking() { peer->Shutdown(100); peer->CloseConnection(peer->GetSystemAddressFromIndex(0), true, 0); RakNet::RakPeerInterface::DestroyInstance(peer); } void Networking::update() { RakNet::Packet *packet; std::string errmsg = ""; for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive()) { switch (packet->data[0]) { case ID_REMOTE_DISCONNECTION_NOTIFICATION: LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Another client has disconnected."); break; case ID_REMOTE_CONNECTION_LOST: LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Another client has lost connection."); break; case ID_REMOTE_NEW_INCOMING_CONNECTION: LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Another client has connected."); break; case ID_CONNECTION_REQUEST_ACCEPTED: LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Our connection request has been accepted."); break; case ID_NEW_INCOMING_CONNECTION: LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "A connection is incoming."); break; case ID_NO_FREE_INCOMING_CONNECTIONS: errmsg = "The server is full."; break; case ID_DISCONNECTION_NOTIFICATION: errmsg = "We have been disconnected."; break; case ID_CONNECTION_LOST: errmsg = "Connection lost."; break; default: receiveMessage(packet); //LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Message with identifier %i has arrived.", packet->data[0]); break; } } if (!errmsg.empty()) { LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, errmsg.c_str()); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "tes3mp", errmsg.c_str(), 0); MWBase::Environment::get().getStateManager()->requestQuit(); } } void Networking::connect(const std::string &ip, unsigned short port, std::vector &content, Files::Collections &collections) { RakNet::SystemAddress master; master.SetBinaryAddress(ip.c_str()); master.SetPortHostOrder(port); std::string errmsg = ""; stringstream sstr(TES3MP_VERSION); sstr << TES3MP_PROTO_VERSION; if (peer->Connect(master.ToString(false), master.GetPort(), sstr.str().c_str(), (int) sstr.str().size(), 0, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED) errmsg = "Connection attempt failed.\n"; bool queue = true; while (queue) { for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket( packet), packet = peer->Receive()) { switch (packet->data[0]) { case ID_CONNECTION_ATTEMPT_FAILED: { errmsg = "Connection failed.\n" "Either the IP address is wrong or a firewall on either system is blocking\n" "UDP packets on the port you have chosen."; queue = false; break; } case ID_INVALID_PASSWORD: { errmsg = "Connection failed.\n" "The client or server is outdated."; queue = false; break; } case ID_CONNECTION_REQUEST_ACCEPTED: { serverAddr = packet->systemAddress; PlayerProcessor::SetServerAddr(packet->systemAddress); WorldProcessor::SetServerAddr(packet->systemAddress); ActorProcessor::SetServerAddr(packet->systemAddress); connected = true; queue = false; LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_CONNECTION_REQUESTED_ACCEPTED from %s", serverAddr.ToString()); break; } case ID_DISCONNECTION_NOTIFICATION: throw runtime_error("ID_DISCONNECTION_NOTIFICATION.\n"); case ID_CONNECTION_BANNED: throw runtime_error("ID_CONNECTION_BANNED.\n"); case ID_CONNECTION_LOST: throw runtime_error("ID_CONNECTION_LOST.\n"); default: LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Connection message with identifier %i has arrived in initialization.", packet->data[0]); } } } if (!errmsg.empty()) { LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, errmsg.c_str()); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "tes3mp", errmsg.c_str(), 0); } else preInit(content, collections); } void Networking::preInit(std::vector &content, Files::Collections &collections) { std::string errmsg = ""; PacketPreInit::PluginContainer checksums; vector::const_iterator it(content.begin()); for (int idx = 0; it != content.end(); ++it, ++idx) { boost::filesystem::path filename(*it); const Files::MultiDirCollection& col = collections.getCollection(filename.extension().string()); if (col.doesExist(*it)) { unsigned int crc32 = Utils::crc32checksum(col.getPath(*it).string()); checksums.push_back(make_pair(*it, crc32)); printf("idx: %d\tchecksum: %X\tfile: %s\n", idx, crc32, col.getPath(*it).string().c_str()); } else throw std::runtime_error("Plugin doesn't exist."); } PacketPreInit packetPreInit(peer); RakNet::BitStream bs; RakNet::RakNetGUID guid; packetPreInit.setChecksums(&checksums); packetPreInit.setGUID(guid); packetPreInit.SetSendStream(&bs); packetPreInit.Send(serverAddr); bool done = false; PacketPreInit::PluginContainer checksumsResponse; /*while (!done) { for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive()) { if(packet->data[0] == ID_GAME_PREINIT) { RakNet::BitStream bsIn(&packet->data[0], packet->length, false); packetPreInit.Packet(&bsIn, guid, false, checksumsResponse); done = true; } } }*/ if(!checksumsResponse.empty()) // something wrong { errmsg = "Your plugins\tShould be\n"; for(int i = 0; i < checksumsResponse.size(); i++) { errmsg += checksums[i].first + " " + MyGUI::utility::toString(checksums[i].second) + "\t"; errmsg += checksumsResponse[i].first + " " + MyGUI::utility::toString(checksumsResponse[i].second) + "\n"; } } if (!errmsg.empty()) { LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, errmsg.c_str()); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "tes3mp", errmsg.c_str(), 0); } } void Networking::processPlayerPacket(RakNet::Packet *packet) { if(!PlayerProcessor::Process(*packet)) LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled PlayerPacket with identifier %i has arrived", packet->data[0]); } void Networking::processActorPacket(RakNet::Packet *packet) { if (!ActorProcessor::Process(*packet, actorList)) LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ActorPacket with identifier %i has arrived", packet->data[0]); } void Networking::processWorldPacket(RakNet::Packet *packet) { if (!WorldProcessor::Process(*packet, worldEvent)) LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled WorldPacket with identifier %i has arrived", packet->data[0]); } void Networking::receiveMessage(RakNet::Packet *packet) { if (packet->length < 2) return; if (playerPacketController.ContainsPacket(packet->data[0])) { processPlayerPacket(packet); } else if (actorPacketController.ContainsPacket(packet->data[0])) { processActorPacket(packet); } else if (worldPacketController.ContainsPacket(packet->data[0])) { processWorldPacket(packet); } } PlayerPacket *Networking::getPlayerPacket(RakNet::MessageID id) { return playerPacketController.GetPacket(id); } ActorPacket *Networking::getActorPacket(RakNet::MessageID id) { return actorPacketController.GetPacket(id); } WorldPacket *Networking::getWorldPacket(RakNet::MessageID id) { return worldPacketController.GetPacket(id); } LocalPlayer *Networking::getLocalPlayer() { return mwmp::Main::get().getLocalPlayer(); } ActorList *Networking::getActorList() { return &actorList; } WorldEvent *Networking::getWorldEvent() { return &worldEvent; } bool Networking::isDedicatedPlayer(const MWWorld::Ptr &ptr) { if (ptr.mRef == 0) return 0; DedicatedPlayer *pl = PlayerList::getPlayer(ptr); return pl != 0; } bool Networking::attack(const MWWorld::Ptr &ptr) { DedicatedPlayer *pl = PlayerList::getPlayer(ptr); if (pl == 0) return false; return pl->attack.pressed; } bool Networking::isConnected() { return connected; }