From d933eae3389b6441218680d3f0910247ea62b3fc Mon Sep 17 00:00:00 2001 From: hilnius Date: Sun, 16 Jun 2013 23:54:02 +0000 Subject: [PATCH 02/22] base commit git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12865 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/SocketsBase.cbp | 68 ++++++++++ dev/SocketsBase/SocketsBase.depend | 124 ++++++++++++++++++ dev/SocketsBase/SocketsBase.layout | 84 ++++++++++++ dev/SocketsBase/bin/Debug/SocketsBase | Bin 0 -> 253053 bytes dev/SocketsBase/client_network_manager.cpp | 42 ++++++ dev/SocketsBase/client_network_manager.hpp | 23 ++++ dev/SocketsBase/http_functions.cpp | 47 +++++++ dev/SocketsBase/http_functions.hpp | 17 +++ dev/SocketsBase/main.cpp | 60 +++++++++ dev/SocketsBase/network_manager.cpp | 43 ++++++ dev/SocketsBase/network_manager.hpp | 31 +++++ dev/SocketsBase/packet.cpp | 9 ++ dev/SocketsBase/packet.hpp | 23 ++++ dev/SocketsBase/protocol.cpp | 14 ++ dev/SocketsBase/protocol.hpp | 31 +++++ dev/SocketsBase/protocol_listener.cpp | 9 ++ dev/SocketsBase/protocol_listener.hpp | 26 ++++ dev/SocketsBase/protocol_manager.cpp | 51 +++++++ dev/SocketsBase/protocol_manager.hpp | 22 ++++ .../protocols/get_public_address.cpp | 121 +++++++++++++++++ .../protocols/get_public_address.hpp | 30 +++++ dev/SocketsBase/server_network_manager.cpp | 43 ++++++ dev/SocketsBase/server_network_manager.hpp | 23 ++++ dev/SocketsBase/singleton.hpp | 43 ++++++ dev/SocketsBase/stk_host.cpp | 108 +++++++++++++++ dev/SocketsBase/stk_host.hpp | 37 ++++++ dev/SocketsBase/stk_peer.cpp | 33 +++++ dev/SocketsBase/stk_peer.hpp | 22 ++++ 28 files changed, 1184 insertions(+) create mode 100644 dev/SocketsBase/SocketsBase.cbp create mode 100644 dev/SocketsBase/SocketsBase.depend create mode 100644 dev/SocketsBase/SocketsBase.layout create mode 100755 dev/SocketsBase/bin/Debug/SocketsBase create mode 100644 dev/SocketsBase/client_network_manager.cpp create mode 100644 dev/SocketsBase/client_network_manager.hpp create mode 100644 dev/SocketsBase/http_functions.cpp create mode 100644 dev/SocketsBase/http_functions.hpp create mode 100644 dev/SocketsBase/main.cpp create mode 100644 dev/SocketsBase/network_manager.cpp create mode 100644 dev/SocketsBase/network_manager.hpp create mode 100644 dev/SocketsBase/packet.cpp create mode 100644 dev/SocketsBase/packet.hpp create mode 100644 dev/SocketsBase/protocol.cpp create mode 100644 dev/SocketsBase/protocol.hpp create mode 100644 dev/SocketsBase/protocol_listener.cpp create mode 100644 dev/SocketsBase/protocol_listener.hpp create mode 100644 dev/SocketsBase/protocol_manager.cpp create mode 100644 dev/SocketsBase/protocol_manager.hpp create mode 100644 dev/SocketsBase/protocols/get_public_address.cpp create mode 100644 dev/SocketsBase/protocols/get_public_address.hpp create mode 100644 dev/SocketsBase/server_network_manager.cpp create mode 100644 dev/SocketsBase/server_network_manager.hpp create mode 100644 dev/SocketsBase/singleton.hpp create mode 100644 dev/SocketsBase/stk_host.cpp create mode 100644 dev/SocketsBase/stk_host.hpp create mode 100644 dev/SocketsBase/stk_peer.cpp create mode 100644 dev/SocketsBase/stk_peer.hpp diff --git a/dev/SocketsBase/SocketsBase.cbp b/dev/SocketsBase/SocketsBase.cbp new file mode 100644 index 000000000..447eb8846 --- /dev/null +++ b/dev/SocketsBase/SocketsBase.cbp @@ -0,0 +1,68 @@ + + + + + + diff --git a/dev/SocketsBase/SocketsBase.depend b/dev/SocketsBase/SocketsBase.depend new file mode 100644 index 000000000..999a4cea1 --- /dev/null +++ b/dev/SocketsBase/SocketsBase.depend @@ -0,0 +1,124 @@ +# depslib dependency file v1.0 +1371430952 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/main.cpp + + + "client_network_manager.hpp" + "server_network_manager.hpp" + "protocol_manager.hpp" + "protocols/get_public_address.hpp" + + + +1371308465 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client.cpp + "client.hpp" + + +1371308471 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client.hpp + "stk_peer.h" + + +1371308808 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_peer.h + +1371342078 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client_network_manager.cpp + "client_network_manager.hpp" + + +1371312921 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client_network_manager.hpp + "network_manager.hpp" + +1371431127 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/network_manager.hpp + "stk_peer.hpp" + "stk_host.hpp" + + "protocol_listener.hpp" + +1370655017 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server.hpp + "stk_peer.h" + + +1371431262 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/network_manager.cpp + "network_manager.hpp" + + +1370655140 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server.cpp + "server.hpp" + + +1371340734 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server_network_manager.cpp + "server_network_manager.hpp" + + + + + +1371340658 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server_network_manager.hpp + "network_manager.hpp" + +1371345329 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_peer.cpp + "stk_peer.hpp" + + + +1371342227 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_peer.hpp + "stk_host.hpp" + + +1371431149 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_host.hpp + + +1371430709 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_host.cpp + "stk_host.hpp" + "network_manager.hpp" + + + + +1371429449 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_listener.hpp + + + +1371344039 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/http_functions.cpp + "http_functions.hpp" + + + + +1371343994 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/http_functions.hpp + + +1371428907 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_manager.hpp + "protocol_listener.hpp" + +1371346908 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/packet.cpp + "packet.hpp" + +1371427096 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/packet.hpp + + + +1371429400 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol.cpp + "protocol.hpp" + +1371429637 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol.hpp + "protocol_listener.hpp" + + +1371342576 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_listener.cpp + "protocol_listener.hpp" + +1371429738 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_manager.cpp + "protocol_manager.hpp" + "protocol.hpp" + + +1371431357 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocols/get_public_address.cpp + "get_public_address.hpp" + "../network_manager.hpp" + + + + + +1371427140 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocols/get_public_address.hpp + "../protocol.hpp" + diff --git a/dev/SocketsBase/SocketsBase.layout b/dev/SocketsBase/SocketsBase.layout new file mode 100644 index 000000000..c1fcf01c9 --- /dev/null +++ b/dev/SocketsBase/SocketsBase.layout @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/SocketsBase/bin/Debug/SocketsBase b/dev/SocketsBase/bin/Debug/SocketsBase new file mode 100755 index 0000000000000000000000000000000000000000..b2d1779f7251b765c680fdfca209af251bba28e2 GIT binary patch literal 253053 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 + +ClientNetworkManager::ClientNetworkManager() +{ +} + +ClientNetworkManager::~ClientNetworkManager() +{ +} + +void ClientNetworkManager::run() +{ + if (enet_initialize() != 0) + { + printf("Could not initialize enet.\n"); + return; + } +} + +void ClientNetworkManager::connect(uint32_t ip, uint16_t port) +{ + m_localhost = new STKHost(); + m_localhost->setupClient(1, 2, 0, 0); + m_localhost->startListening(); + + STKPeer* peer = new STKPeer(); + peer->connectToServer(m_localhost, ip, port, 2, 0); + + m_peers.push_back(peer); +} + +void ClientNetworkManager::packetReceived(char* data) +{ + printf("ClientNetworkManager::packetReceived()\n"); + puts(data); +} +void ClientNetworkManager::sendPacket(char* data) +{ + m_peers[0]->sendPacket(data); +} diff --git a/dev/SocketsBase/client_network_manager.hpp b/dev/SocketsBase/client_network_manager.hpp new file mode 100644 index 000000000..cdbe73cb8 --- /dev/null +++ b/dev/SocketsBase/client_network_manager.hpp @@ -0,0 +1,23 @@ +#ifndef CLIENT_NETWORK_MANAGER_HPP +#define CLIENT_NETWORK_MANAGER_HPP + +#include "network_manager.hpp" + + +class ClientNetworkManager : public NetworkManager +{ + public: + ClientNetworkManager(); + virtual ~ClientNetworkManager(); + + virtual void run(); + + void connect(uint32_t ip, uint16_t port); + + virtual void packetReceived(char* data); + virtual void sendPacket(char* data); + protected: + private: +}; + +#endif // CLIENT_NETWORK_MANAGER_HPP diff --git a/dev/SocketsBase/http_functions.cpp b/dev/SocketsBase/http_functions.cpp new file mode 100644 index 000000000..b8246c59b --- /dev/null +++ b/dev/SocketsBase/http_functions.cpp @@ -0,0 +1,47 @@ +#include "http_functions.hpp" + + +#include +#include +#include + +namespace HTTP +{ +CURL *curl; +CURLcode res; + +static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; +} + +void init() +{ + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); + if(!curl) + printf("Error while loading cURL library.\n"); +} + +std::string getPage(std::string url) +{ + std::string readBuffer = ""; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + res = curl_easy_perform(curl); + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + readBuffer.erase(readBuffer.begin()+64, readBuffer.end()); + return readBuffer; +} + +void shutdown() +{ + curl_easy_cleanup(curl); + curl_global_cleanup(); +} + +} diff --git a/dev/SocketsBase/http_functions.hpp b/dev/SocketsBase/http_functions.hpp new file mode 100644 index 000000000..927725c40 --- /dev/null +++ b/dev/SocketsBase/http_functions.hpp @@ -0,0 +1,17 @@ +#ifndef HTTP_FUNCTIONS_HPP +#define HTTP_FUNCTIONS_HPP + +#include + +namespace HTTP +{ + +void init(); +void shutdown(); + +std::string getPage(std::string url); + + +} + +#endif // HTTP_FUNCTIONS_HPP diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp new file mode 100644 index 000000000..763b2cc14 --- /dev/null +++ b/dev/SocketsBase/main.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "client_network_manager.hpp" +#include "server_network_manager.hpp" +#include "protocol_manager.hpp" +#include "protocols/get_public_address.hpp" +#include +#include + +using namespace std; +ProtocolManager* protocolListener; + +void* foo(void* data) +{ + while(1) + { + protocolListener->update(); + } + return NULL; +} + +int main() +{ + std::string answer; + cout << "host or client:"; + cin >> answer; + if (answer == "client") + { + ClientNetworkManager clt; + clt.run(); + clt.connect(0x0100007f, 7000); // addr in little endian, real address is 7f 00 00 01 (127.0.0.1) + + protocolListener = new ProtocolManager(); + + pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(thrd, NULL, foo, NULL); + + // start a retreive stun addr protocol + Protocol* prt = new GetPublicAddress(); + protocolListener->runProtocol(prt); + + char buffer[256]; + while (1) + { + gets(buffer); + if (strlen(buffer) == 0) { continue; } + clt.sendPacket(buffer); + } + } + else if (answer == "host") + { + ServerNetworkManager srv; + srv.run(); + srv.start(); + //srv.protocolListener = new ProtocolManager(); + while(1){} + } + return 0; +} diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp new file mode 100644 index 000000000..e9f76dae6 --- /dev/null +++ b/dev/SocketsBase/network_manager.cpp @@ -0,0 +1,43 @@ +#include "network_manager.hpp" + +#include + +NetworkManager* NetworkManager::instance = NULL; + +NetworkManager::NetworkManager() +{ + if (NetworkManager::instance) + { + printf("Warning : a Newtork Manager is being deleted.\n"); + delete NetworkManager::instance; + } + NetworkManager::instance = this; + printf("New Network Manager created.\n"); +} + +NetworkManager::~NetworkManager() +{ +} + +void NetworkManager::run() +{ +} + + +void NetworkManager::sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort) +{ + instance->getHost()->sendRawPacket(data, length, dstIp, dstPort); +} +uint8_t* NetworkManager::receiveRawPacket() +{ + return instance->getHost()->receiveRawPacket(); +} +void NetworkManager::receptionCallback(char* data) +{ + instance->packetReceived(data); +} + +STKHost* NetworkManager::getHost() +{ + return m_localhost; +} diff --git a/dev/SocketsBase/network_manager.hpp b/dev/SocketsBase/network_manager.hpp new file mode 100644 index 000000000..c6b280235 --- /dev/null +++ b/dev/SocketsBase/network_manager.hpp @@ -0,0 +1,31 @@ +#ifndef NETWORKMANAGER_HPP +#define NETWORKMANAGER_HPP + +#include "stk_peer.hpp" +#include "stk_host.hpp" +#include + +#include "protocol_listener.hpp" + +class NetworkManager +{ + public: + NetworkManager(); + virtual ~NetworkManager(); + + virtual void run() = 0; + + static void sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort); + static uint8_t* receiveRawPacket(); + static void receptionCallback(char* data); + virtual void packetReceived(char* data) = 0; + + STKHost* getHost(); + protected: + std::vector m_peers; + STKHost* m_localhost; + + static NetworkManager* instance; +}; + +#endif // NETWORKMANAGER_HPP diff --git a/dev/SocketsBase/packet.cpp b/dev/SocketsBase/packet.cpp new file mode 100644 index 000000000..6ab1d723c --- /dev/null +++ b/dev/SocketsBase/packet.cpp @@ -0,0 +1,9 @@ +#include "packet.hpp" + +Packet::Packet() +{ +} + +Packet::~Packet() +{ +} diff --git a/dev/SocketsBase/packet.hpp b/dev/SocketsBase/packet.hpp new file mode 100644 index 000000000..5e7f14717 --- /dev/null +++ b/dev/SocketsBase/packet.hpp @@ -0,0 +1,23 @@ +#ifndef PACKET_HPP +#define PACKET_HPP + +#include +#include + +class Packet +{ + public: + Packet(); + virtual ~Packet(); + + virtual std::string getData(); + + protected: + char* m_packetHeader; + unsigned int m_packetHeaderSize; + unsigned int m_packetSize; + Packet* m_parent; + std::vector m_children; +}; + +#endif // PACKET_HPP diff --git a/dev/SocketsBase/protocol.cpp b/dev/SocketsBase/protocol.cpp new file mode 100644 index 000000000..27635e639 --- /dev/null +++ b/dev/SocketsBase/protocol.cpp @@ -0,0 +1,14 @@ +#include "protocol.hpp" + +Protocol::Protocol() +{ +} + +Protocol::~Protocol() +{ +} + +PROTOCOL_TYPE Protocol::getProtocolType() +{ + return m_type; +} diff --git a/dev/SocketsBase/protocol.hpp b/dev/SocketsBase/protocol.hpp new file mode 100644 index 000000000..189266d8c --- /dev/null +++ b/dev/SocketsBase/protocol.hpp @@ -0,0 +1,31 @@ +#ifndef PROTOCOL_HPP +#define PROTOCOL_HPP + +#include "protocol_listener.hpp" + +#include + +enum PROTOCOL_TYPE +{ + GET_PUBLIC_ADDRESS = 0 +}; + +class Protocol +{ + public: + Protocol(); + virtual ~Protocol(); + + virtual void messageReceived(uint8_t* data) = 0; + + virtual void setup() = 0; + virtual void start() = 0; + virtual void update() = 0; + + PROTOCOL_TYPE getProtocolType(); + protected: + ProtocolListener* m_listener; + PROTOCOL_TYPE m_type; +}; + +#endif // PROTOCOL_HPP diff --git a/dev/SocketsBase/protocol_listener.cpp b/dev/SocketsBase/protocol_listener.cpp new file mode 100644 index 000000000..f05e7b08f --- /dev/null +++ b/dev/SocketsBase/protocol_listener.cpp @@ -0,0 +1,9 @@ +#include "protocol_listener.hpp" + +ProtocolListener::ProtocolListener() +{ +} + +ProtocolListener::~ProtocolListener() +{ +} diff --git a/dev/SocketsBase/protocol_listener.hpp b/dev/SocketsBase/protocol_listener.hpp new file mode 100644 index 000000000..ec843d890 --- /dev/null +++ b/dev/SocketsBase/protocol_listener.hpp @@ -0,0 +1,26 @@ +#ifndef PROTOCOL_LISTENER_HPP +#define PROTOCOL_LISTENER_HPP + +#include +#include + +class Protocol; + +class ProtocolListener +{ + public: + ProtocolListener(); + virtual ~ProtocolListener(); + + virtual void messageReceived(uint8_t* data) = 0; + + virtual void runProtocol(Protocol* protocol) = 0; + virtual void stopProtocol(Protocol* protocol) = 0; + + virtual void update() = 0; + + protected: + std::vector m_protocols; +}; + +#endif // PROTOCOL_LISTENER_HPP diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp new file mode 100644 index 000000000..64c2eff3b --- /dev/null +++ b/dev/SocketsBase/protocol_manager.cpp @@ -0,0 +1,51 @@ +#include "protocol_manager.hpp" + +#include "protocol.hpp" +#include + +ProtocolManager::ProtocolManager() : ProtocolListener() +{ +} + +ProtocolManager::~ProtocolManager() +{ +} + +void ProtocolManager::messageReceived(uint8_t* data) +{ + assert(data); + m_messagesToProcess.push_back(data); +} + +void ProtocolManager::runProtocol(Protocol* protocol) +{ + m_protocols.push_back(protocol); + protocol->setup(); + protocol->start(); +} +void ProtocolManager::stopProtocol(Protocol* protocol) +{ + +} + +void ProtocolManager::update() +{ + // before updating, notice protocols that they have received information + int size = m_messagesToProcess.size(); + for (int i = 0; i < size; i++) + { + uint8_t* data = m_messagesToProcess.back(); + PROTOCOL_TYPE searchedProtocol = (PROTOCOL_TYPE)(data[0]); + for (unsigned int i = 0; i < m_protocols.size() ; i++) + { + if (m_protocols[i]->getProtocolType() == searchedProtocol) + m_protocols[i]->messageReceived(data+1); + } + m_messagesToProcess.pop_back(); + } + // now update all protocols + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + m_protocols[i]->update(); + } +} diff --git a/dev/SocketsBase/protocol_manager.hpp b/dev/SocketsBase/protocol_manager.hpp new file mode 100644 index 000000000..586766a4c --- /dev/null +++ b/dev/SocketsBase/protocol_manager.hpp @@ -0,0 +1,22 @@ +#ifndef PROTOCOL_MANAGER_HPP +#define PROTOCOL_MANAGER_HPP + +#include "protocol_listener.hpp" + +class ProtocolManager : public ProtocolListener +{ + public: + ProtocolManager(); + virtual ~ProtocolManager(); + + virtual void messageReceived(uint8_t* data); + + virtual void runProtocol(Protocol* protocol); + virtual void stopProtocol(Protocol* protocol); + + virtual void update(); + protected: + std::vector m_messagesToProcess; +}; + +#endif // PROTOCOL_MANAGER_HPP diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp new file mode 100644 index 000000000..3e7c93786 --- /dev/null +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -0,0 +1,121 @@ +#include "get_public_address.hpp" + +#include "../network_manager.hpp" + +#include +#include +#include +#include + +int stunRand() +{ + static bool init = false; + if (!init) + { + srand(time(NULL)); + init = true; + } + return rand(); +} + +GetPublicAddress::GetPublicAddress() : Protocol() +{ + m_type = GET_PUBLIC_ADDRESS; +} + +GetPublicAddress::~GetPublicAddress() +{ +} + +void GetPublicAddress::messageReceived(uint8_t* data) +{ + assert(data); + if (m_state == TEST_SENT && sizeof(data) >= 20) + { + + m_state = ADDRESS_KNOWN; + } +} + +void GetPublicAddress::setup() +{ + m_state = NOTHING_DONE; +} + +void GetPublicAddress::start() +{ +} + +void GetPublicAddress::update() +{ + if (m_state == NOTHING_DONE) + { + // format : 00MMMMMCMMMCMMMM (cf rfc 5389) + uint16_t message_type = 0b0000000000000001; // binding request + m_stunTransactionID[0] = stunRand(); + m_stunTransactionID[1] = stunRand(); + m_stunTransactionID[2] = stunRand(); + uint16_t message_length = 0x0000; + + uint8_t bytes[21]; // the message to be sent + // bytes 0-1 : the type of the message, + bytes[0] = (uint8_t)(message_type>>8); + bytes[1] = (uint8_t)(message_type); + + // bytes 2-3 : message length added to header (attributes) + bytes[2] = (uint8_t)(message_length>>8); + bytes[3] = (uint8_t)(message_length); + + // bytes 4-7 : magic cookie to recognize the stun protocol + bytes[4] = (uint8_t)(m_stunMagicCookie>>24); + bytes[5] = (uint8_t)(m_stunMagicCookie>>16); + bytes[6] = (uint8_t)(m_stunMagicCookie>>8); + bytes[7] = (uint8_t)(m_stunMagicCookie); + + // bytes 8-19 : the transaction id + bytes[8] = (uint8_t)(m_stunTransactionID[0]>>24); + bytes[9] = (uint8_t)(m_stunTransactionID[0]>>16); + bytes[10] = (uint8_t)(m_stunTransactionID[0]>>8); + bytes[11] = (uint8_t)(m_stunTransactionID[0]); + bytes[12] = (uint8_t)(m_stunTransactionID[1]>>24); + bytes[13] = (uint8_t)(m_stunTransactionID[1]>>16); + bytes[14] = (uint8_t)(m_stunTransactionID[1]>>8); + bytes[15] = (uint8_t)(m_stunTransactionID[1]); + bytes[16] = (uint8_t)(m_stunTransactionID[2]>>24); + bytes[17] = (uint8_t)(m_stunTransactionID[2]>>16); + bytes[18] = (uint8_t)(m_stunTransactionID[2]>>8); + bytes[19] = (uint8_t)(m_stunTransactionID[2]); + bytes[20] = '\0'; + + unsigned int dst = 132*256*256*256+177*256*256+123*256+6; + NetworkManager::sendRawPacket(bytes, 20, dst, 3478); + m_state = TEST_SENT; + + uint8_t* data = NetworkManager::receiveRawPacket(); + assert(data); + // check that the stun response is a response, contains the magic cookie and the transaction ID + if ( data[0] == 0b01 && + data[1] == 0b01 && + data[4] == (uint8_t)(m_stunMagicCookie>>24) && + data[5] == (uint8_t)(m_stunMagicCookie>>16) && + data[6] == (uint8_t)(m_stunMagicCookie>>8) && + data[7] == (uint8_t)(m_stunMagicCookie) && + data[8] == (uint8_t)(m_stunTransactionID[0]>>24) && + data[9] == (uint8_t)(m_stunTransactionID[0]>>16) && + data[10] == (uint8_t)(m_stunTransactionID[0]>>8 ) && + data[11] == (uint8_t)(m_stunTransactionID[0] ) && + data[12] == (uint8_t)(m_stunTransactionID[1]>>24) && + data[13] == (uint8_t)(m_stunTransactionID[1]>>16) && + data[14] == (uint8_t)(m_stunTransactionID[1]>>8 ) && + data[15] == (uint8_t)(m_stunTransactionID[1] ) && + data[16] == (uint8_t)(m_stunTransactionID[2]>>24) && + data[17] == (uint8_t)(m_stunTransactionID[2]>>16) && + data[18] == (uint8_t)(m_stunTransactionID[2]>>8 ) && + data[19] == (uint8_t)(m_stunTransactionID[2] )) + { + printf("the stun server reponded a valid answer\n"); + int messageSize = data[2]*256+data[3]; + printf("the answer is %i bytes long\n", messageSize); + } + } +} diff --git a/dev/SocketsBase/protocols/get_public_address.hpp b/dev/SocketsBase/protocols/get_public_address.hpp new file mode 100644 index 000000000..da97d7887 --- /dev/null +++ b/dev/SocketsBase/protocols/get_public_address.hpp @@ -0,0 +1,30 @@ +#ifndef GET_PUBLIC_ADDRESS_HPP +#define GET_PUBLIC_ADDRESS_HPP + +#include "../protocol.hpp" + +class GetPublicAddress : public Protocol +{ + public: + GetPublicAddress(); + virtual ~GetPublicAddress(); + + virtual void messageReceived(uint8_t* data); + + virtual void setup(); + virtual void start(); + virtual void update(); + + protected: + enum STATE + { + NOTHING_DONE, + TEST_SENT, + ADDRESS_KNOWN + }; + STATE m_state; + uint32_t m_stunTransactionID[3]; + const uint32_t m_stunMagicCookie = 0x2112A442; +}; + +#endif // GET_PUBLIC_ADDRESS_HPP diff --git a/dev/SocketsBase/server_network_manager.cpp b/dev/SocketsBase/server_network_manager.cpp new file mode 100644 index 000000000..939b1d465 --- /dev/null +++ b/dev/SocketsBase/server_network_manager.cpp @@ -0,0 +1,43 @@ +#include "server_network_manager.hpp" + +#include +#include +#include +#include + +ServerNetworkManager::ServerNetworkManager() +{ + m_localhost = NULL; +} + +ServerNetworkManager::~ServerNetworkManager() +{ +} + +void ServerNetworkManager::run() +{ + if (enet_initialize() != 0) + { + printf("Could not initialize enet.\n"); + return; + } +} + +void ServerNetworkManager::start() +{ + m_localhost = new STKHost(); + m_localhost->setupServer(STKHost::HOST_ANY, 7000, 32, 2, 0, 0); + m_localhost->startListening(); + printf("Server now setup.\n"); +} + +void ServerNetworkManager::packetReceived(char* data) +{ + printf("ServerNetworkManager::packetReceived()\n"); + puts(data); + sendPacket(data); +} +void ServerNetworkManager::sendPacket(char* data) +{ + m_localhost->broadcastPacket(data); +} diff --git a/dev/SocketsBase/server_network_manager.hpp b/dev/SocketsBase/server_network_manager.hpp new file mode 100644 index 000000000..f4b33c533 --- /dev/null +++ b/dev/SocketsBase/server_network_manager.hpp @@ -0,0 +1,23 @@ +#ifndef SERVER_NETWORK_MANAGER_HPP +#define SERVER_NETWORK_MANAGER_HPP + +#include "network_manager.hpp" + + +class ServerNetworkManager : public NetworkManager +{ + public: + ServerNetworkManager(); + virtual ~ServerNetworkManager(); + + virtual void run(); + + void start(); + + virtual void packetReceived(char* data); + virtual void sendPacket(char* data); + protected: + +}; + +#endif // SERVER_NETWORK_MANAGER_HPP diff --git a/dev/SocketsBase/singleton.hpp b/dev/SocketsBase/singleton.hpp new file mode 100644 index 000000000..61c7ce121 --- /dev/null +++ b/dev/SocketsBase/singleton.hpp @@ -0,0 +1,43 @@ +#ifndef SINGLETON_HPP +#define SINGLETON_HPP + +#include + +template +class Singleton +{ + protected: + Singleton () { } + ~Singleton () { std::cout << "destroying singleton." << std::endl; } + + public: + static T *getInstance () + { + if (NULL == m_singleton) + { + std::cout << "creating singleton." << std::endl; + m_singleton = new T; + } + else + { + std::cout << "singleton already created!" << std::endl; + } + return (static_cast (m_singleton)); + } + + static void kill () + { + if (NULL != m_singleton) + { + delete m_singleton; + m_singleton = NULL; + } + } + + private: + static T *m_singleton; +}; + +template T *Singleton::_singleton = NULL; + +#endif // SINGLETON_HPP diff --git a/dev/SocketsBase/stk_host.cpp b/dev/SocketsBase/stk_host.cpp new file mode 100644 index 000000000..8ef7fcd01 --- /dev/null +++ b/dev/SocketsBase/stk_host.cpp @@ -0,0 +1,108 @@ +#include "stk_host.hpp" + +#include "network_manager.hpp" + +#include +#include +#include + +void* STKHost::receive_data(void* self) +{ + ENetEvent event; + ENetHost* host = (((STKHost*)(self))->m_host); + while (1) + { + while (enet_host_service(host, &event, 0) != 0) { + printf("message received\n"); + switch (event.type) { + case ENET_EVENT_TYPE_RECEIVE: + NetworkManager::receptionCallback((char*) event.packet->data); + break; + case ENET_EVENT_TYPE_DISCONNECT: + printf("Somebody is now disconnected.\n"); + case ENET_EVENT_TYPE_CONNECT: + printf("A client has just connected.\n"); + break; + case ENET_EVENT_TYPE_NONE: + break; + } + } + } + return NULL; +} + +STKHost::STKHost() +{ + m_host = NULL; +} + +STKHost::~STKHost() +{ +} + +void STKHost::setupServer(uint32_t address, uint16_t port, int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth) +{ + ENetAddress addr; + addr.host = address; + addr.port = port; + + m_host = enet_host_create(&addr, peerCount, channelLimit, maxIncomingBandwidth, maxOutgoingBandwidth); + if (m_host == NULL) + { + fprintf (stderr, "An error occurred while trying to create an ENet server host.\n"); + exit (EXIT_FAILURE); + } + //pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); + //pthread_create(thrd, NULL, &STKHost::receive_data, this); +} + +void STKHost::setupClient(int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth) +{ + m_host = enet_host_create(NULL, peerCount, channelLimit, maxIncomingBandwidth, maxOutgoingBandwidth); + if (m_host == NULL) + { + fprintf (stderr, "An error occurred while trying to create an ENet client host.\n"); + exit (EXIT_FAILURE); + } + //pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); + //pthread_create(thrd, NULL, &STKHost::receive_data, this); +} + +void STKHost::startListening() +{ + pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(thrd, NULL, &STKHost::receive_data, this); +} + +void STKHost::sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort) +{ + struct sockaddr_in to; + int toLen = sizeof(to); + memset(&to,0,toLen); + + to.sin_family = AF_INET; + to.sin_port = htons(dstPort); + to.sin_addr.s_addr = htonl(dstIp); + + sendto(m_host->socket, data, length, 0,(sockaddr*)&to, toLen); +} + +uint8_t* STKHost::receiveRawPacket() +{ + uint8_t* buffer; // max size needed normally (only used for stun) + buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); + int len = recv(m_host->socket, buffer, 2048, 0); + + if ( len == -1 ) // socket error + { + printf("Socket Error while receiving information.\n"); + return NULL; + } + return buffer; +} + +void STKHost::broadcastPacket(char* data) +{ + ENetPacket* packet = enet_packet_create(data, strlen(data)+1,ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(m_host, 0, packet); +} diff --git a/dev/SocketsBase/stk_host.hpp b/dev/SocketsBase/stk_host.hpp new file mode 100644 index 000000000..7d005b39b --- /dev/null +++ b/dev/SocketsBase/stk_host.hpp @@ -0,0 +1,37 @@ +#ifndef STK_HOST_HPP +#define STK_HOST_HPP + +#include + + +class STKHost +{ + friend class STKPeer; + public: + enum HOST_TYPE + { + HOST_ANY = 0, + HOST_BROADCAST = 0xFFFFFFFF, + PORT_ANY = 0 + }; + + STKHost(); + virtual ~STKHost(); + + static void* receive_data(void* self); + + void setupServer(uint32_t address, uint16_t port, int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth); + void setupClient(int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth); + + void startListening(); + + void sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort); + uint8_t* receiveRawPacket(); + void broadcastPacket(char* data); + + protected: + ENetHost* m_host; + +}; + +#endif // STK_HOST_HPP diff --git a/dev/SocketsBase/stk_peer.cpp b/dev/SocketsBase/stk_peer.cpp new file mode 100644 index 000000000..290bfc2d5 --- /dev/null +++ b/dev/SocketsBase/stk_peer.cpp @@ -0,0 +1,33 @@ +#include "stk_peer.hpp" + +#include +#include + +STKPeer::STKPeer() +{ + m_peer = NULL; +} + +STKPeer::~STKPeer() +{ +} + +void STKPeer::connectToServer(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data) +{ + ENetAddress address; + address.host = ip; + address.port = port; + + m_peer = enet_host_connect(host->m_host, &address, 2, 0); + if (m_peer == NULL) + { + printf("Could not connect to server.\n"); + return; + } +} + +void STKPeer::sendPacket(char* data) +{ + ENetPacket* packet = enet_packet_create(data, strlen(data)+1,ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(m_peer, 0, packet); +} diff --git a/dev/SocketsBase/stk_peer.hpp b/dev/SocketsBase/stk_peer.hpp new file mode 100644 index 000000000..eba2dea42 --- /dev/null +++ b/dev/SocketsBase/stk_peer.hpp @@ -0,0 +1,22 @@ +#ifndef STK_PEER_HPP +#define STK_PEER_HPP + +#include "stk_host.hpp" +#include + +class STKPeer +{ + public: + STKPeer(); + virtual ~STKPeer(); + + static void* receive_data(void* self); + + virtual void sendPacket(char* data); + + void connectToServer(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data); + protected: + ENetPeer* m_peer; +}; + +#endif // STK_PEER_HPP From 2469902478d6ce30c27b51329b5a8ea76549cd58 Mon Sep 17 00:00:00 2001 From: hilnius Date: Mon, 17 Jun 2013 12:46:42 +0000 Subject: [PATCH 03/22] stun update git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12866 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/packet.cpp | 9 --------- dev/SocketsBase/packet.hpp | 23 ----------------------- dev/SocketsBase/protocol_listener.cpp | 9 --------- dev/SocketsBase/protocol_listener.hpp | 26 -------------------------- 4 files changed, 67 deletions(-) delete mode 100644 dev/SocketsBase/packet.cpp delete mode 100644 dev/SocketsBase/packet.hpp delete mode 100644 dev/SocketsBase/protocol_listener.cpp delete mode 100644 dev/SocketsBase/protocol_listener.hpp diff --git a/dev/SocketsBase/packet.cpp b/dev/SocketsBase/packet.cpp deleted file mode 100644 index 6ab1d723c..000000000 --- a/dev/SocketsBase/packet.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "packet.hpp" - -Packet::Packet() -{ -} - -Packet::~Packet() -{ -} diff --git a/dev/SocketsBase/packet.hpp b/dev/SocketsBase/packet.hpp deleted file mode 100644 index 5e7f14717..000000000 --- a/dev/SocketsBase/packet.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef PACKET_HPP -#define PACKET_HPP - -#include -#include - -class Packet -{ - public: - Packet(); - virtual ~Packet(); - - virtual std::string getData(); - - protected: - char* m_packetHeader; - unsigned int m_packetHeaderSize; - unsigned int m_packetSize; - Packet* m_parent; - std::vector m_children; -}; - -#endif // PACKET_HPP diff --git a/dev/SocketsBase/protocol_listener.cpp b/dev/SocketsBase/protocol_listener.cpp deleted file mode 100644 index f05e7b08f..000000000 --- a/dev/SocketsBase/protocol_listener.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "protocol_listener.hpp" - -ProtocolListener::ProtocolListener() -{ -} - -ProtocolListener::~ProtocolListener() -{ -} diff --git a/dev/SocketsBase/protocol_listener.hpp b/dev/SocketsBase/protocol_listener.hpp deleted file mode 100644 index ec843d890..000000000 --- a/dev/SocketsBase/protocol_listener.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef PROTOCOL_LISTENER_HPP -#define PROTOCOL_LISTENER_HPP - -#include -#include - -class Protocol; - -class ProtocolListener -{ - public: - ProtocolListener(); - virtual ~ProtocolListener(); - - virtual void messageReceived(uint8_t* data) = 0; - - virtual void runProtocol(Protocol* protocol) = 0; - virtual void stopProtocol(Protocol* protocol) = 0; - - virtual void update() = 0; - - protected: - std::vector m_protocols; -}; - -#endif // PROTOCOL_LISTENER_HPP From 27a7f1da6bff579d12f4e5ad8d4659aa46047290 Mon Sep 17 00:00:00 2001 From: hilnius Date: Mon, 17 Jun 2013 12:48:44 +0000 Subject: [PATCH 04/22] stun working git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12867 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/main.cpp | 21 ++++++++++---- dev/SocketsBase/network_manager.cpp | 9 +++++- dev/SocketsBase/network_manager.hpp | 4 ++- dev/SocketsBase/protocol_manager.cpp | 24 ++++++++++++++-- dev/SocketsBase/protocol_manager.hpp | 14 ++++++++-- .../protocols/get_public_address.cpp | 28 ++++++++++++------- .../protocols/get_public_address.hpp | 2 +- 7 files changed, 78 insertions(+), 24 deletions(-) diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index 763b2cc14..fc58dfe48 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -24,7 +24,8 @@ int main() { std::string answer; cout << "host or client:"; - cin >> answer; + answer = "client"; + //cin >> answer; if (answer == "client") { ClientNetworkManager clt; @@ -33,19 +34,27 @@ int main() protocolListener = new ProtocolManager(); + pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); pthread_create(thrd, NULL, foo, NULL); // start a retreive stun addr protocol Protocol* prt = new GetPublicAddress(); protocolListener->runProtocol(prt); - - char buffer[256]; + + std::string buffer; while (1) { - gets(buffer); - if (strlen(buffer) == 0) { continue; } - clt.sendPacket(buffer); + cin >> buffer; + if (buffer == "protocolsCount") + { + cout << protocolListener->runningProtocolsCount() << " protocols are running." << endl; + continue; + } + if (buffer.size() == 0) { continue; } + char buffer2[256]; + strcpy(buffer2, buffer.c_str()); + clt.sendPacket(buffer2); } } else if (answer == "host") diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp index e9f76dae6..a39f5f5c3 100644 --- a/dev/SocketsBase/network_manager.cpp +++ b/dev/SocketsBase/network_manager.cpp @@ -15,7 +15,7 @@ NetworkManager::NetworkManager() printf("New Network Manager created.\n"); } -NetworkManager::~NetworkManager() +NetworkManager::~NetworkManager() { } @@ -23,6 +23,13 @@ void NetworkManager::run() { } +void NetworkManager::setManualSocketsMode(bool manual) +{ + if (manual) + instance->getHost()->stopListening(); + else + instance->getHost()->startListening(); +} void NetworkManager::sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort) { diff --git a/dev/SocketsBase/network_manager.hpp b/dev/SocketsBase/network_manager.hpp index c6b280235..8820d1aca 100644 --- a/dev/SocketsBase/network_manager.hpp +++ b/dev/SocketsBase/network_manager.hpp @@ -13,10 +13,12 @@ class NetworkManager NetworkManager(); virtual ~NetworkManager(); - virtual void run() = 0; + virtual void run() = 0; + static void setManualSocketsMode(bool manual); static void sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort); static uint8_t* receiveRawPacket(); + static void receptionCallback(char* data); virtual void packetReceived(char* data) = 0; diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp index 64c2eff3b..1902035bb 100644 --- a/dev/SocketsBase/protocol_manager.cpp +++ b/dev/SocketsBase/protocol_manager.cpp @@ -3,7 +3,7 @@ #include "protocol.hpp" #include -ProtocolManager::ProtocolManager() : ProtocolListener() +ProtocolManager::ProtocolManager() { } @@ -14,12 +14,13 @@ ProtocolManager::~ProtocolManager() void ProtocolManager::messageReceived(uint8_t* data) { assert(data); - m_messagesToProcess.push_back(data); + m_messagesToProcess.push_back(data); } void ProtocolManager::runProtocol(Protocol* protocol) { m_protocols.push_back(protocol); + protocol->setListener(this); protocol->setup(); protocol->start(); } @@ -27,6 +28,19 @@ void ProtocolManager::stopProtocol(Protocol* protocol) { } +void ProtocolManager::protocolTerminated(Protocol* protocol) +{ + int offset = 0; + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i-offset] == protocol) + { + delete m_protocols[i]; + m_protocols.erase(m_protocols.begin()+(i-offset), m_protocols.begin()+(i-offset)+1); + offset++; + } + } +} void ProtocolManager::update() { @@ -49,3 +63,9 @@ void ProtocolManager::update() m_protocols[i]->update(); } } + +int ProtocolManager::runningProtocolsCount() +{ + return m_protocols.size(); +} + diff --git a/dev/SocketsBase/protocol_manager.hpp b/dev/SocketsBase/protocol_manager.hpp index 586766a4c..4918d3069 100644 --- a/dev/SocketsBase/protocol_manager.hpp +++ b/dev/SocketsBase/protocol_manager.hpp @@ -1,9 +1,12 @@ #ifndef PROTOCOL_MANAGER_HPP #define PROTOCOL_MANAGER_HPP -#include "protocol_listener.hpp" +#include +#include -class ProtocolManager : public ProtocolListener +class Protocol; + +class ProtocolManager { public: ProtocolManager(); @@ -13,9 +16,14 @@ class ProtocolManager : public ProtocolListener virtual void runProtocol(Protocol* protocol); virtual void stopProtocol(Protocol* protocol); - + virtual void protocolTerminated(Protocol* protocol); + virtual void update(); + + virtual int runningProtocolsCount(); + protected: + std::vector m_protocols; std::vector m_messagesToProcess; }; diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index 3e7c93786..feca4efe2 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -29,12 +29,7 @@ GetPublicAddress::~GetPublicAddress() void GetPublicAddress::messageReceived(uint8_t* data) { - assert(data); - if (m_state == TEST_SENT && sizeof(data) >= 20) - { - - m_state = ADDRESS_KNOWN; - } + } void GetPublicAddress::setup() @@ -45,7 +40,7 @@ void GetPublicAddress::setup() void GetPublicAddress::start() { } - + void GetPublicAddress::update() { if (m_state == NOTHING_DONE) @@ -85,14 +80,19 @@ void GetPublicAddress::update() bytes[17] = (uint8_t)(m_stunTransactionID[2]>>16); bytes[18] = (uint8_t)(m_stunTransactionID[2]>>8); bytes[19] = (uint8_t)(m_stunTransactionID[2]); - bytes[20] = '\0'; + bytes[20] = '\0'; unsigned int dst = 132*256*256*256+177*256*256+123*256+6; + NetworkManager::setManualSocketsMode(true); NetworkManager::sendRawPacket(bytes, 20, dst, 3478); m_state = TEST_SENT; - + } + if (m_state == TEST_SENT) + { uint8_t* data = NetworkManager::receiveRawPacket(); + NetworkManager::setManualSocketsMode(false); assert(data); + // check that the stun response is a response, contains the magic cookie and the transaction ID if ( data[0] == 0b01 && data[1] == 0b01 && @@ -113,9 +113,17 @@ void GetPublicAddress::update() data[18] == (uint8_t)(m_stunTransactionID[2]>>8 ) && data[19] == (uint8_t)(m_stunTransactionID[2] )) { - printf("the stun server reponded a valid answer\n"); + printf("the stun server responded with a valid answer\n"); int messageSize = data[2]*256+data[3]; printf("the answer is %i bytes long\n", messageSize); + + // parse the stun message now: } + m_state = ADDRESS_KNOWN; + } + if (m_state == ADDRESS_KNOWN) + { + // return the information and terminate the protocol + m_listener->protocolTerminated(this); } } diff --git a/dev/SocketsBase/protocols/get_public_address.hpp b/dev/SocketsBase/protocols/get_public_address.hpp index da97d7887..42566c70f 100644 --- a/dev/SocketsBase/protocols/get_public_address.hpp +++ b/dev/SocketsBase/protocols/get_public_address.hpp @@ -10,7 +10,7 @@ class GetPublicAddress : public Protocol virtual ~GetPublicAddress(); virtual void messageReceived(uint8_t* data); - + virtual void setup(); virtual void start(); virtual void update(); From 1cac3c78157712b6b5ed81ce219c3e9b3424fc00 Mon Sep 17 00:00:00 2001 From: hilnius Date: Mon, 17 Jun 2013 18:06:02 +0000 Subject: [PATCH 05/22] improving socket management git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12870 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/SocketsBase.cbp | 8 +- dev/SocketsBase/SocketsBase.depend | 44 ++++++---- dev/SocketsBase/SocketsBase.layout | 74 +++++++++++------ dev/SocketsBase/bin/Debug/SocketsBase | Bin 253053 -> 301838 bytes dev/SocketsBase/client_network_manager.cpp | 7 +- dev/SocketsBase/main.cpp | 7 +- dev/SocketsBase/network_manager.cpp | 2 +- dev/SocketsBase/network_manager.hpp | 2 +- dev/SocketsBase/protocol.cpp | 8 +- dev/SocketsBase/protocol.hpp | 12 ++- dev/SocketsBase/protocol_manager.cpp | 5 ++ .../protocols/get_public_address.cpp | 78 +++++++++++++++--- .../protocols/get_public_address.hpp | 2 +- dev/SocketsBase/stk_host.cpp | 65 ++++++++++++--- dev/SocketsBase/stk_host.hpp | 3 + 15 files changed, 230 insertions(+), 87 deletions(-) diff --git a/dev/SocketsBase/SocketsBase.cbp b/dev/SocketsBase/SocketsBase.cbp index 447eb8846..2ed3adc52 100644 --- a/dev/SocketsBase/SocketsBase.cbp +++ b/dev/SocketsBase/SocketsBase.cbp @@ -33,9 +33,12 @@ - + + + + @@ -43,11 +46,8 @@ - - - diff --git a/dev/SocketsBase/SocketsBase.depend b/dev/SocketsBase/SocketsBase.depend index 999a4cea1..5d831ae76 100644 --- a/dev/SocketsBase/SocketsBase.depend +++ b/dev/SocketsBase/SocketsBase.depend @@ -1,5 +1,5 @@ # depslib dependency file v1.0 -1371430952 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/main.cpp +1371490349 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/main.cpp "client_network_manager.hpp" @@ -19,24 +19,24 @@ 1371308808 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_peer.h -1371342078 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client_network_manager.cpp +1371490431 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client_network_manager.cpp "client_network_manager.hpp" -1371312921 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client_network_manager.hpp +1371480489 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client_network_manager.hpp "network_manager.hpp" -1371431127 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/network_manager.hpp +1371481319 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/network_manager.hpp "stk_peer.hpp" "stk_host.hpp" - "protocol_listener.hpp" + "protocol_manager.hpp" 1370655017 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server.hpp "stk_peer.h" -1371431262 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/network_manager.cpp +1371494752 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/network_manager.cpp "network_manager.hpp" @@ -44,14 +44,14 @@ "server.hpp" -1371340734 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server_network_manager.cpp +1371480479 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server_network_manager.cpp "server_network_manager.hpp" -1371340658 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server_network_manager.hpp +1371480483 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server_network_manager.hpp "network_manager.hpp" 1371345329 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_peer.cpp @@ -63,10 +63,10 @@ "stk_host.hpp" -1371431149 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_host.hpp +1371494269 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_host.hpp -1371430709 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_host.cpp +1371494673 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_host.cpp "stk_host.hpp" "network_manager.hpp" @@ -86,8 +86,9 @@ 1371343994 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/http_functions.hpp -1371428907 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_manager.hpp - "protocol_listener.hpp" +1371480450 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_manager.hpp + + 1371346908 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/packet.cpp "packet.hpp" @@ -96,22 +97,24 @@ -1371429400 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol.cpp +1371485468 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol.cpp "protocol.hpp" -1371429637 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol.hpp - "protocol_listener.hpp" +1371485485 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol.hpp + "protocol_manager.hpp" + "callback_object.hpp" 1371342576 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_listener.cpp "protocol_listener.hpp" -1371429738 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_manager.cpp +1371481407 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_manager.cpp "protocol_manager.hpp" "protocol.hpp" + -1371431357 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocols/get_public_address.cpp +1371494759 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocols/get_public_address.cpp "get_public_address.hpp" "../network_manager.hpp" @@ -119,6 +122,11 @@ -1371427140 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocols/get_public_address.hpp +1371485726 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocols/get_public_address.hpp "../protocol.hpp" +1371485520 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/callback_object.hpp + +1371485516 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/callback_object.cpp + "callback_object.hpp" + diff --git a/dev/SocketsBase/SocketsBase.layout b/dev/SocketsBase/SocketsBase.layout index c1fcf01c9..6a0f9d8e1 100644 --- a/dev/SocketsBase/SocketsBase.layout +++ b/dev/SocketsBase/SocketsBase.layout @@ -1,39 +1,49 @@ - + - + - + - + - + - + - + - + + + + + + + + + + + - + - + - + - + - + @@ -41,9 +51,19 @@ - + - + + + + + + + + + + + @@ -51,9 +71,14 @@ - + - + + + + + + @@ -63,22 +88,17 @@ - + - + - + - + - - - - - - + diff --git a/dev/SocketsBase/bin/Debug/SocketsBase b/dev/SocketsBase/bin/Debug/SocketsBase index b2d1779f7251b765c680fdfca209af251bba28e2..c61948bab816255c8a9bdc8ee632f980bc0047b8 100755 GIT binary patch literal 301838 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

u|O!lW)r$YU%G#%Rv**<&NZ;{El?zuo#LW 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, 2, 0, 0); + m_localhost->startListening(); } void ClientNetworkManager::connect(uint32_t ip, uint16_t port) { - m_localhost = new STKHost(); - m_localhost->setupClient(1, 2, 0, 0); - m_localhost->startListening(); - STKPeer* peer = new STKPeer(); peer->connectToServer(m_localhost, ip, port, 2, 0); diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index fc58dfe48..4e5c8733c 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -25,12 +25,11 @@ int main() std::string answer; cout << "host or client:"; answer = "client"; - //cin >> answer; + cin >> answer; if (answer == "client") { ClientNetworkManager clt; clt.run(); - clt.connect(0x0100007f, 7000); // addr in little endian, real address is 7f 00 00 01 (127.0.0.1) protocolListener = new ProtocolManager(); @@ -39,9 +38,11 @@ int main() pthread_create(thrd, NULL, foo, NULL); // start a retreive stun addr protocol - Protocol* prt = new GetPublicAddress(); + Protocol* prt = new GetPublicAddress(NULL); protocolListener->runProtocol(prt); + clt.connect(0x0100007f, 7000); // addr in little endian, real address is 7f 00 00 01 (127.0.0.1) + std::string buffer; while (1) { diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp index a39f5f5c3..e26fb0c35 100644 --- a/dev/SocketsBase/network_manager.cpp +++ b/dev/SocketsBase/network_manager.cpp @@ -37,7 +37,7 @@ void NetworkManager::sendRawPacket(uint8_t* data, int length, unsigned int dstIp } uint8_t* NetworkManager::receiveRawPacket() { - return instance->getHost()->receiveRawPacket(); + return instance->getHost()->receiveRawPacket(0,0); } void NetworkManager::receptionCallback(char* data) { diff --git a/dev/SocketsBase/network_manager.hpp b/dev/SocketsBase/network_manager.hpp index 8820d1aca..884c02ec7 100644 --- a/dev/SocketsBase/network_manager.hpp +++ b/dev/SocketsBase/network_manager.hpp @@ -5,7 +5,7 @@ #include "stk_host.hpp" #include -#include "protocol_listener.hpp" +#include "protocol_manager.hpp" class NetworkManager { diff --git a/dev/SocketsBase/protocol.cpp b/dev/SocketsBase/protocol.cpp index 27635e639..99196b130 100644 --- a/dev/SocketsBase/protocol.cpp +++ b/dev/SocketsBase/protocol.cpp @@ -1,13 +1,19 @@ #include "protocol.hpp" -Protocol::Protocol() +Protocol::Protocol(CallbackObject* callbackObject) { + m_callbackObject = callbackObject; } Protocol::~Protocol() { } +void Protocol::setListener(ProtocolManager* listener) +{ + m_listener = listener; +} + PROTOCOL_TYPE Protocol::getProtocolType() { return m_type; diff --git a/dev/SocketsBase/protocol.hpp b/dev/SocketsBase/protocol.hpp index 189266d8c..b33339fe9 100644 --- a/dev/SocketsBase/protocol.hpp +++ b/dev/SocketsBase/protocol.hpp @@ -1,7 +1,8 @@ #ifndef PROTOCOL_HPP #define PROTOCOL_HPP -#include "protocol_listener.hpp" +#include "protocol_manager.hpp" +#include "callback_object.hpp" #include @@ -13,19 +14,22 @@ enum PROTOCOL_TYPE class Protocol { public: - Protocol(); - virtual ~Protocol(); + Protocol(CallbackObject* callbackObject); + virtual ~Protocol(); virtual void messageReceived(uint8_t* data) = 0; + void setListener(ProtocolManager* listener); + virtual void setup() = 0; virtual void start() = 0; virtual void update() = 0; PROTOCOL_TYPE getProtocolType(); protected: - ProtocolListener* m_listener; + ProtocolManager* m_listener; PROTOCOL_TYPE m_type; + CallbackObject* m_callbackObject; }; #endif // PROTOCOL_HPP diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp index 1902035bb..f3c22d135 100644 --- a/dev/SocketsBase/protocol_manager.cpp +++ b/dev/SocketsBase/protocol_manager.cpp @@ -1,7 +1,10 @@ #include "protocol_manager.hpp" #include "protocol.hpp" + #include +#include + ProtocolManager::ProtocolManager() { @@ -23,6 +26,7 @@ void ProtocolManager::runProtocol(Protocol* protocol) protocol->setListener(this); protocol->setup(); protocol->start(); + printf("*** PROTOCOL MANAGER *** - A new protocol has been started. There are %i protocols running.\n", m_protocols.size()); } void ProtocolManager::stopProtocol(Protocol* protocol) { @@ -40,6 +44,7 @@ void ProtocolManager::protocolTerminated(Protocol* protocol) offset++; } } + printf("*** PROTOCOL MANAGER *** - A protocol has been terminated. There are %i protocols running.\n", m_protocols.size()); } void ProtocolManager::update() diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index feca4efe2..593a1220a 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -18,7 +18,7 @@ int stunRand() return rand(); } -GetPublicAddress::GetPublicAddress() : Protocol() +GetPublicAddress::GetPublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject) { m_type = GET_PUBLIC_ADDRESS; } @@ -82,6 +82,7 @@ void GetPublicAddress::update() bytes[19] = (uint8_t)(m_stunTransactionID[2]); bytes[20] = '\0'; + printf("Querrying STUN server 132.177.123.6\n"); unsigned int dst = 132*256*256*256+177*256*256+123*256+6; NetworkManager::setManualSocketsMode(true); NetworkManager::sendRawPacket(bytes, 20, dst, 3478); @@ -90,7 +91,6 @@ void GetPublicAddress::update() if (m_state == TEST_SENT) { uint8_t* data = NetworkManager::receiveRawPacket(); - NetworkManager::setManualSocketsMode(false); assert(data); // check that the stun response is a response, contains the magic cookie and the transaction ID @@ -99,7 +99,9 @@ void GetPublicAddress::update() data[4] == (uint8_t)(m_stunMagicCookie>>24) && data[5] == (uint8_t)(m_stunMagicCookie>>16) && data[6] == (uint8_t)(m_stunMagicCookie>>8) && - data[7] == (uint8_t)(m_stunMagicCookie) && + data[7] == (uint8_t)(m_stunMagicCookie)) + { + if( data[8] == (uint8_t)(m_stunTransactionID[0]>>24) && data[9] == (uint8_t)(m_stunTransactionID[0]>>16) && data[10] == (uint8_t)(m_stunTransactionID[0]>>8 ) && @@ -112,14 +114,70 @@ void GetPublicAddress::update() data[17] == (uint8_t)(m_stunTransactionID[2]>>16) && data[18] == (uint8_t)(m_stunTransactionID[2]>>8 ) && data[19] == (uint8_t)(m_stunTransactionID[2] )) - { - printf("the stun server responded with a valid answer\n"); - int messageSize = data[2]*256+data[3]; - printf("the answer is %i bytes long\n", messageSize); - - // parse the stun message now: + { + printf("The STUN server responded with a valid answer\n"); + int messageSize = data[2]*256+data[3]; + + // parse the stun message now: + bool finish = false; + uint8_t* attributes = data+20; + if (messageSize == 0) + { + printf("STUN answer does not contain any information.\n"); + finish = true; + } + if (messageSize < 4) // cannot even read the size + { + printf("STUN message is not valid.\n"); + finish = true; + } + uint16_t port; + uint32_t address; + bool valid = false; + while(!finish) + { + int type = attributes[0]*256+attributes[1]; + int size = attributes[2]*256+attributes[3]; + switch(type) + { + case 0: + case 1: + assert(size == 8); + assert(attributes[5] = 0x01); // IPv4 only + port = attributes[6]*256+attributes[7]; + address = (attributes[8]<<24 & 0xFF000000)+(attributes[9]<<16 & 0x00FF0000)+(attributes[10]<<8 & 0x0000FF00)+(attributes[11] & 0x000000FF); + finish = true; + valid = true; + continue; + break; + default: + break; + } + attributes = attributes + 4 + size; + messageSize -= 4 + size; + if (messageSize == 0) + finish = true; + if (messageSize < 4) // cannot even read the size + { + printf("STUN message is not valid.\n"); + finish = true; + } + } + // finished parsing, we know our public transport address + if (valid) + { + printf("The public address has been found : %i.%i.%i.%i:%i\n", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); + m_state = ADDRESS_KNOWN; + NetworkManager::setManualSocketsMode(false); + } + else + m_state = NOTHING_DONE; // need to re-send the stun request + } + else + { + m_state = NOTHING_DONE; // need to re-send the stun request + } } - m_state = ADDRESS_KNOWN; } if (m_state == ADDRESS_KNOWN) { diff --git a/dev/SocketsBase/protocols/get_public_address.hpp b/dev/SocketsBase/protocols/get_public_address.hpp index 42566c70f..8a204cd7e 100644 --- a/dev/SocketsBase/protocols/get_public_address.hpp +++ b/dev/SocketsBase/protocols/get_public_address.hpp @@ -6,7 +6,7 @@ class GetPublicAddress : public Protocol { public: - GetPublicAddress(); + GetPublicAddress(CallbackObject* callbackObject); virtual ~GetPublicAddress(); virtual void messageReceived(uint8_t* data); diff --git a/dev/SocketsBase/stk_host.cpp b/dev/SocketsBase/stk_host.cpp index 8ef7fcd01..fd8892045 100644 --- a/dev/SocketsBase/stk_host.cpp +++ b/dev/SocketsBase/stk_host.cpp @@ -5,6 +5,7 @@ #include #include #include +#include void* STKHost::receive_data(void* self) { @@ -20,6 +21,8 @@ void* STKHost::receive_data(void* self) break; case ENET_EVENT_TYPE_DISCONNECT: printf("Somebody is now disconnected.\n"); + printf("Disconnected host: %i.%i.%i.%i:%i\n", event.peer->address.host>>24&0xff, event.peer->address.host>>16&0xff, event.peer->address.host>>8&0xff, event.peer->address.host&0xff,event.peer->address.port); + break; case ENET_EVENT_TYPE_CONNECT: printf("A client has just connected.\n"); break; @@ -34,6 +37,7 @@ void* STKHost::receive_data(void* self) STKHost::STKHost() { m_host = NULL; + m_listeningThread = (pthread_t*)(malloc(sizeof(pthread_t))); } STKHost::~STKHost() @@ -52,8 +56,6 @@ void STKHost::setupServer(uint32_t address, uint16_t port, int peerCount, int ch fprintf (stderr, "An error occurred while trying to create an ENet server host.\n"); exit (EXIT_FAILURE); } - //pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); - //pthread_create(thrd, NULL, &STKHost::receive_data, this); } void STKHost::setupClient(int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth) @@ -64,14 +66,15 @@ void STKHost::setupClient(int peerCount, int channelLimit, uint32_t maxIncomingB fprintf (stderr, "An error occurred while trying to create an ENet client host.\n"); exit (EXIT_FAILURE); } - //pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); - //pthread_create(thrd, NULL, &STKHost::receive_data, this); } void STKHost::startListening() { - pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); - pthread_create(thrd, NULL, &STKHost::receive_data, this); + pthread_create(m_listeningThread, NULL, &STKHost::receive_data, this); +} +void STKHost::stopListening() +{ + pthread_cancel(*m_listeningThread); } void STKHost::sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort) @@ -83,21 +86,57 @@ void STKHost::sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsig to.sin_family = AF_INET; to.sin_port = htons(dstPort); to.sin_addr.s_addr = htonl(dstIp); - + sendto(m_host->socket, data, length, 0,(sockaddr*)&to, toLen); } -uint8_t* STKHost::receiveRawPacket() +uint8_t* STKHost::receiveRawPacket() { uint8_t* buffer; // max size needed normally (only used for stun) buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); - int len = recv(m_host->socket, buffer, 2048, 0); - - if ( len == -1 ) // socket error + memset(buffer, 0, 2048); + + int len = recv(m_host->socket,buffer,2048, 0); + int i = 0; + while(len < 0) // wait to receive the message because enet sockets are non-blocking { - printf("Socket Error while receiving information.\n"); - return NULL; + i++; + len = recv(m_host->socket,buffer,2048, 0); + usleep(1000); // wait 1 millisecond between two checks } + printf("Packet received after %i milliseconds\n", i); + return buffer; +} +uint8_t* STKHost::receiveRawPacket(unsigned int dstIp, unsigned short dstPort) +{ + uint8_t* buffer; // max size needed normally (only used for stun) + buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); + memset(buffer, 0, 2048); + + socklen_t fromlen; + struct sockaddr addr; + + fromlen = sizeof(addr); + int len = recvfrom(m_host->socket, buffer, 2048, 0, &addr, &fromlen); + + int i = 0; + while(len < 0 + && (uint8_t)(addr.sa_data[2]) != (dstIp>>24&0xff) + && (uint8_t)(addr.sa_data[3]) != (dstIp>>16&0xff) + && (uint8_t)(addr.sa_data[4]) != (dstIp>>8&0xff) + && (uint8_t)(addr.sa_data[5]) != (dstIp&0xff)) // wait to receive the message because enet sockets are non-blocking + { + i++; + len = recvfrom(m_host->socket, buffer, 2048, 0, &addr, &fromlen); + usleep(1000); // wait 1 millisecond between two checks + } + if (addr.sa_family == AF_INET) + { + char s[20]; + inet_ntop(AF_INET, &(((struct sockaddr_in *)&addr)->sin_addr), s, 20); + printf("IPv4 Address %s\n", s); + } + printf("Packet received after %i milliseconds\n", i); return buffer; } diff --git a/dev/SocketsBase/stk_host.hpp b/dev/SocketsBase/stk_host.hpp index 7d005b39b..9483836c5 100644 --- a/dev/SocketsBase/stk_host.hpp +++ b/dev/SocketsBase/stk_host.hpp @@ -24,13 +24,16 @@ class STKHost void setupClient(int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth); void startListening(); + void stopListening(); void sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort); uint8_t* receiveRawPacket(); + uint8_t* receiveRawPacket(unsigned int dstIp, unsigned short dstPort); void broadcastPacket(char* data); protected: ENetHost* m_host; + pthread_t* m_listeningThread; }; From 9e990e76e0e8878ea8630fd984e997c5fb60db43 Mon Sep 17 00:00:00 2001 From: hilnius Date: Mon, 17 Jun 2013 20:53:14 +0000 Subject: [PATCH 06/22] untrack files that shouldn't be tracked git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12871 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/SocketsBase.cbp | 68 -------------- dev/SocketsBase/SocketsBase.depend | 132 ---------------------------- dev/SocketsBase/SocketsBase.layout | 104 ---------------------- dev/SocketsBase/callback_object.cpp | 9 ++ dev/SocketsBase/callback_object.hpp | 14 +++ 5 files changed, 23 insertions(+), 304 deletions(-) delete mode 100644 dev/SocketsBase/SocketsBase.cbp delete mode 100644 dev/SocketsBase/SocketsBase.depend delete mode 100644 dev/SocketsBase/SocketsBase.layout create mode 100644 dev/SocketsBase/callback_object.cpp create mode 100644 dev/SocketsBase/callback_object.hpp diff --git a/dev/SocketsBase/SocketsBase.cbp b/dev/SocketsBase/SocketsBase.cbp deleted file mode 100644 index 2ed3adc52..000000000 --- a/dev/SocketsBase/SocketsBase.cbp +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - diff --git a/dev/SocketsBase/SocketsBase.depend b/dev/SocketsBase/SocketsBase.depend deleted file mode 100644 index 5d831ae76..000000000 --- a/dev/SocketsBase/SocketsBase.depend +++ /dev/null @@ -1,132 +0,0 @@ -# depslib dependency file v1.0 -1371490349 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/main.cpp - - - "client_network_manager.hpp" - "server_network_manager.hpp" - "protocol_manager.hpp" - "protocols/get_public_address.hpp" - - - -1371308465 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client.cpp - "client.hpp" - - -1371308471 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client.hpp - "stk_peer.h" - - -1371308808 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_peer.h - -1371490431 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client_network_manager.cpp - "client_network_manager.hpp" - - -1371480489 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/client_network_manager.hpp - "network_manager.hpp" - -1371481319 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/network_manager.hpp - "stk_peer.hpp" - "stk_host.hpp" - - "protocol_manager.hpp" - -1370655017 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server.hpp - "stk_peer.h" - - -1371494752 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/network_manager.cpp - "network_manager.hpp" - - -1370655140 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server.cpp - "server.hpp" - - -1371480479 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server_network_manager.cpp - "server_network_manager.hpp" - - - - - -1371480483 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/server_network_manager.hpp - "network_manager.hpp" - -1371345329 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_peer.cpp - "stk_peer.hpp" - - - -1371342227 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_peer.hpp - "stk_host.hpp" - - -1371494269 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_host.hpp - - -1371494673 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/stk_host.cpp - "stk_host.hpp" - "network_manager.hpp" - - - - -1371429449 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_listener.hpp - - - -1371344039 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/http_functions.cpp - "http_functions.hpp" - - - - -1371343994 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/http_functions.hpp - - -1371480450 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_manager.hpp - - - -1371346908 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/packet.cpp - "packet.hpp" - -1371427096 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/packet.hpp - - - -1371485468 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol.cpp - "protocol.hpp" - -1371485485 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol.hpp - "protocol_manager.hpp" - "callback_object.hpp" - - -1371342576 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_listener.cpp - "protocol_listener.hpp" - -1371481407 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocol_manager.cpp - "protocol_manager.hpp" - "protocol.hpp" - - - -1371494759 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocols/get_public_address.cpp - "get_public_address.hpp" - "../network_manager.hpp" - - - - - -1371485726 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/protocols/get_public_address.hpp - "../protocol.hpp" - -1371485520 /home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/callback_object.hpp - -1371485516 source:/home/hilnius/Development/svn/supertuxkart/main/branches/hilnius/dev/SocketsBase/callback_object.cpp - "callback_object.hpp" - diff --git a/dev/SocketsBase/SocketsBase.layout b/dev/SocketsBase/SocketsBase.layout deleted file mode 100644 index 6a0f9d8e1..000000000 --- a/dev/SocketsBase/SocketsBase.layout +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dev/SocketsBase/callback_object.cpp b/dev/SocketsBase/callback_object.cpp new file mode 100644 index 000000000..cbba8acfb --- /dev/null +++ b/dev/SocketsBase/callback_object.cpp @@ -0,0 +1,9 @@ +#include "callback_object.hpp" + +CallbackObject::CallbackObject() +{ +} + +CallbackObject::~CallbackObject() +{ +} diff --git a/dev/SocketsBase/callback_object.hpp b/dev/SocketsBase/callback_object.hpp new file mode 100644 index 000000000..70c4ab486 --- /dev/null +++ b/dev/SocketsBase/callback_object.hpp @@ -0,0 +1,14 @@ +#ifndef CALLBACK_OBJECT_HPP +#define CALLBACK_OBJECT_HPP + + +class CallbackObject +{ + public: + CallbackObject(); + virtual ~CallbackObject(); + + protected: +}; + +#endif // CALLBACK_OBJECT_HPP From 57b7534a6df93526077ed462dfb6b521b09f811e Mon Sep 17 00:00:00 2001 From: hilnius Date: Tue, 18 Jun 2013 00:42:11 +0000 Subject: [PATCH 07/22] basic protocol intrication, sending address info online, a client can set his public transport adress (ip:port) in a sql database git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12873 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/SocketsBase.cbp | 70 +++++++++++++++ dev/SocketsBase/bin/Debug/SocketsBase | Bin 301838 -> 326995 bytes dev/SocketsBase/http_functions.cpp | 42 ++++++++- dev/SocketsBase/main.cpp | 24 +++++- dev/SocketsBase/network_manager.cpp | 10 +-- dev/SocketsBase/network_manager.hpp | 3 +- .../protocols/connect_to_server.cpp | 80 ++++++++++++++++++ .../protocols/connect_to_server.hpp | 37 ++++++++ .../protocols/get_public_address.cpp | 9 +- dev/SocketsBase/stk_host.cpp | 6 +- 10 files changed, 259 insertions(+), 22 deletions(-) create mode 100644 dev/SocketsBase/SocketsBase.cbp create mode 100644 dev/SocketsBase/protocols/connect_to_server.cpp create mode 100644 dev/SocketsBase/protocols/connect_to_server.hpp diff --git a/dev/SocketsBase/SocketsBase.cbp b/dev/SocketsBase/SocketsBase.cbp new file mode 100644 index 000000000..09c432605 --- /dev/null +++ b/dev/SocketsBase/SocketsBase.cbp @@ -0,0 +1,70 @@ + + + + + + diff --git a/dev/SocketsBase/bin/Debug/SocketsBase b/dev/SocketsBase/bin/Debug/SocketsBase index c61948bab816255c8a9bdc8ee632f980bc0047b8..f602705e32ca624edca48a385aedf5edab5ce218 100755 GIT binary patch literal 326995 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ℜkAk%&x9@ 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 z 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%0DfUh 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 diff --git a/dev/SocketsBase/http_functions.cpp b/dev/SocketsBase/http_functions.cpp index b8246c59b..1827713d6 100644 --- a/dev/SocketsBase/http_functions.cpp +++ b/dev/SocketsBase/http_functions.cpp @@ -4,12 +4,46 @@ #include #include #include +#include +#include +#include +#include namespace HTTP { CURL *curl; CURLcode res; +struct string { + char *ptr; + size_t len; +}; + +void init_string(struct string *s) { + s->len = 0; + s->ptr = (char*)(malloc(s->len+1)); + if (s->ptr == NULL) { + fprintf(stderr, "malloc() failed\n"); + exit(EXIT_FAILURE); + } + s->ptr[0] = '\0'; +} + +size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s) +{ + size_t new_len = s->len + size*nmemb; + s->ptr = (char*)(realloc(s->ptr, new_len+1)); + if (s->ptr == NULL) { + fprintf(stderr, "realloc() failed\n"); + exit(EXIT_FAILURE); + } + memcpy(s->ptr+s->len, ptr, size*nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + + return size*nmemb; +} + static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) { ((std::string*)userp)->append((char*)contents, size * nmemb); @@ -26,16 +60,16 @@ void init() std::string getPage(std::string url) { - std::string readBuffer = ""; + struct string readBuffer; + init_string(&readBuffer); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); res = curl_easy_perform(curl); if(res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - readBuffer.erase(readBuffer.begin()+64, readBuffer.end()); - return readBuffer; + return readBuffer.ptr; } void shutdown() diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index 4e5c8733c..0d4061e08 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -5,6 +5,9 @@ #include "server_network_manager.hpp" #include "protocol_manager.hpp" #include "protocols/get_public_address.hpp" +#include "http_functions.hpp" +#include "protocols/connect_to_server.hpp" + #include #include @@ -22,23 +25,40 @@ void* foo(void* data) int main() { + HTTP::init(); + std::string answer; cout << "host or client:"; answer = "client"; cin >> answer; if (answer == "client") { + /// NICKNAME : + std::string nickname; + cout << "Nickname="; + std::cin >> nickname; + /// PASSWORD : + std::string password; + cout << "Password="; + std::cin >> password; + ConnectToServer* connectionProtocol = new ConnectToServer(NULL); + connectionProtocol->setPassword(password); + connectionProtocol->setUsername(nickname); + + ClientNetworkManager clt; clt.run(); protocolListener = new ProtocolManager(); - pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); pthread_create(thrd, NULL, foo, NULL); // start a retreive stun addr protocol - Protocol* prt = new GetPublicAddress(NULL); + Protocol* prt = new GetPublicAddress(connectionProtocol); + prt->setListener(protocolListener); + connectionProtocol->setListener(protocolListener); + protocolListener->runProtocol(prt); clt.connect(0x0100007f, 7000); // addr in little endian, real address is 7f 00 00 01 (127.0.0.1) diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp index e26fb0c35..7feb176fc 100644 --- a/dev/SocketsBase/network_manager.cpp +++ b/dev/SocketsBase/network_manager.cpp @@ -31,14 +31,6 @@ void NetworkManager::setManualSocketsMode(bool manual) instance->getHost()->startListening(); } -void NetworkManager::sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort) -{ - instance->getHost()->sendRawPacket(data, length, dstIp, dstPort); -} -uint8_t* NetworkManager::receiveRawPacket() -{ - return instance->getHost()->receiveRawPacket(0,0); -} void NetworkManager::receptionCallback(char* data) { instance->packetReceived(data); @@ -46,5 +38,5 @@ void NetworkManager::receptionCallback(char* data) STKHost* NetworkManager::getHost() { - return m_localhost; + return instance->m_localhost; } diff --git a/dev/SocketsBase/network_manager.hpp b/dev/SocketsBase/network_manager.hpp index 884c02ec7..b1e2023c7 100644 --- a/dev/SocketsBase/network_manager.hpp +++ b/dev/SocketsBase/network_manager.hpp @@ -17,12 +17,11 @@ class NetworkManager static void setManualSocketsMode(bool manual); static void sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort); - static uint8_t* receiveRawPacket(); static void receptionCallback(char* data); virtual void packetReceived(char* data) = 0; - STKHost* getHost(); + static STKHost* getHost(); protected: std::vector m_peers; STKHost* m_localhost; diff --git a/dev/SocketsBase/protocols/connect_to_server.cpp b/dev/SocketsBase/protocols/connect_to_server.cpp new file mode 100644 index 000000000..37cfa65a6 --- /dev/null +++ b/dev/SocketsBase/protocols/connect_to_server.cpp @@ -0,0 +1,80 @@ +#include "connect_to_server.hpp" + +#include "../http_functions.hpp" + +#include + +ConnectToServer::ConnectToServer(CallbackObject* callbackObject) : Protocol(callbackObject) +{ + m_ownPublicIp = 0; + m_ownPublicPort = 0; +} + +ConnectToServer::~ConnectToServer() +{ +} + +void ConnectToServer::setup() +{ +} + +void ConnectToServer::messageReceived(uint8_t* data) +{ + +} + +void ConnectToServer::start() +{ + if (m_ownPublicIp == 0 || m_ownPublicPort == 0 || m_username == "" || m_password == "") + { + printf("You have to set the public ip:port and username:password before starting this protocol.\n"); + m_listener->protocolTerminated(this); + } +} + +void ConnectToServer::update() +{ + if (m_state == NOTHING) + { + char url[512]; + sprintf(url, "http://stkconnect.freeserver.me/log.php?set&nick=%s&ip=%u&port=%u&pwd=%s", m_username.c_str(), m_ownPublicIp, m_ownPublicPort, m_password.c_str()); + std::string result = HTTP::getPage(url); + //printf("Result from web page is %s\n", result.c_str()); + if (result[0] == 's' && result[1] == 'u' && result[2] == 'c') + { + printf("Address set.\n"); + m_state = ADDRESS_KNOWN_ONLINE; + } + if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i') + { + printf("Login fail.\n"); + m_state = ADDRESS_KNOWN_ONLINE; + } + } + else if (m_state == ADDRESS_KNOWN_ONLINE) + { + } + else if (m_state == PEER_ADDRESS_RETREIVED) + { + } + else if (m_state == CONNECTED) + { + } + +} + +void ConnectToServer::setSelfAddress(uint32_t ip, uint16_t port) +{ + m_ownPublicIp = ip; + m_ownPublicPort = port; +} + +void ConnectToServer::setUsername(std::string username) +{ + m_username = username; +} + +void ConnectToServer::setPassword(std::string password) +{ + m_password = password; +} diff --git a/dev/SocketsBase/protocols/connect_to_server.hpp b/dev/SocketsBase/protocols/connect_to_server.hpp new file mode 100644 index 000000000..02f78b9c8 --- /dev/null +++ b/dev/SocketsBase/protocols/connect_to_server.hpp @@ -0,0 +1,37 @@ +#ifndef CONNECT_TO_SERVER_HPP +#define CONNECT_TO_SERVER_HPP + +#include "../protocol.hpp" +#include + +class ConnectToServer : public Protocol, public CallbackObject +{ + public: + ConnectToServer(CallbackObject* callbackObject); + virtual ~ConnectToServer(); + + virtual void messageReceived(uint8_t* data); + virtual void setup(); + virtual void start(); + virtual void update(); + + void setSelfAddress(uint32_t ip, uint16_t port); + void setUsername(std::string username); + void setPassword(std::string password); + + protected: + uint32_t m_ownPublicIp; + uint16_t m_ownPublicPort; + std::string m_username; + std::string m_password; + enum STATE + { + NOTHING, + ADDRESS_KNOWN_ONLINE, + PEER_ADDRESS_RETREIVED, + CONNECTED + }; + STATE m_state; +}; + +#endif // CONNECT_TO_SERVER_HPP diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index 593a1220a..c5fe8a587 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -1,6 +1,7 @@ #include "get_public_address.hpp" #include "../network_manager.hpp" +#include "connect_to_server.hpp" #include #include @@ -85,12 +86,13 @@ void GetPublicAddress::update() printf("Querrying STUN server 132.177.123.6\n"); unsigned int dst = 132*256*256*256+177*256*256+123*256+6; NetworkManager::setManualSocketsMode(true); - NetworkManager::sendRawPacket(bytes, 20, dst, 3478); + NetworkManager::getHost()->sendRawPacket(bytes, 20, dst, 3478); m_state = TEST_SENT; } if (m_state == TEST_SENT) { - uint8_t* data = NetworkManager::receiveRawPacket(); + unsigned int dst = 132*256*256*256+177*256*256+123*256+6; + uint8_t* data = NetworkManager::getHost()->receiveRawPacket(dst, 3478); assert(data); // check that the stun response is a response, contains the magic cookie and the transaction ID @@ -169,6 +171,9 @@ void GetPublicAddress::update() printf("The public address has been found : %i.%i.%i.%i:%i\n", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); m_state = ADDRESS_KNOWN; NetworkManager::setManualSocketsMode(false); + ConnectToServer* cbObj = static_cast(m_callbackObject); + cbObj->setSelfAddress(address, port); + m_listener->runProtocol(cbObj); } else m_state = NOTHING_DONE; // need to re-send the stun request diff --git a/dev/SocketsBase/stk_host.cpp b/dev/SocketsBase/stk_host.cpp index fd8892045..d601df55b 100644 --- a/dev/SocketsBase/stk_host.cpp +++ b/dev/SocketsBase/stk_host.cpp @@ -120,11 +120,11 @@ uint8_t* STKHost::receiveRawPacket(unsigned int dstIp, unsigned short dstPort) int len = recvfrom(m_host->socket, buffer, 2048, 0, &addr, &fromlen); int i = 0; - while(len < 0 - && (uint8_t)(addr.sa_data[2]) != (dstIp>>24&0xff) + while(len < 0 || ( + (uint8_t)(addr.sa_data[2]) != (dstIp>>24&0xff) && (uint8_t)(addr.sa_data[3]) != (dstIp>>16&0xff) && (uint8_t)(addr.sa_data[4]) != (dstIp>>8&0xff) - && (uint8_t)(addr.sa_data[5]) != (dstIp&0xff)) // wait to receive the message because enet sockets are non-blocking + && (uint8_t)(addr.sa_data[5]) != (dstIp&0xff))) // wait to receive the message because enet sockets are non-blocking { i++; len = recvfrom(m_host->socket, buffer, 2048, 0, &addr, &fromlen); From f63ab88f889e2c44e9d9d0125ad22b6d51f4fa21 Mon Sep 17 00:00:00 2001 From: hilnius Date: Tue, 18 Jun 2013 00:42:54 +0000 Subject: [PATCH 08/22] removing unnecessary files git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12874 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/bin/Debug/SocketsBase | Bin 326995 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 dev/SocketsBase/bin/Debug/SocketsBase diff --git a/dev/SocketsBase/bin/Debug/SocketsBase b/dev/SocketsBase/bin/Debug/SocketsBase deleted file mode 100755 index f602705e32ca624edca48a385aedf5edab5ce218..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 326995 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ℜkAk%&x9@ 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 z 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%0DfUh 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 Date: Tue, 18 Jun 2013 15:31:04 +0000 Subject: [PATCH 09/22] cleaning http function git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12882 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/http_functions.cpp | 37 ++-------------- dev/SocketsBase/main.cpp | 5 +++ dev/SocketsBase/protocol.hpp | 2 + dev/SocketsBase/protocol_manager.cpp | 42 +++++++++++++++---- dev/SocketsBase/protocol_manager.hpp | 9 +++- .../protocols/connect_to_server.cpp | 30 +++++++++---- .../protocols/connect_to_server.hpp | 5 +++ .../protocols/get_public_address.cpp | 8 ++++ .../protocols/get_public_address.hpp | 4 +- dev/SocketsBase/time.hpp | 19 +++++++++ 10 files changed, 110 insertions(+), 51 deletions(-) create mode 100644 dev/SocketsBase/time.hpp diff --git a/dev/SocketsBase/http_functions.cpp b/dev/SocketsBase/http_functions.cpp index 1827713d6..28f65f996 100644 --- a/dev/SocketsBase/http_functions.cpp +++ b/dev/SocketsBase/http_functions.cpp @@ -14,36 +14,6 @@ namespace HTTP CURL *curl; CURLcode res; -struct string { - char *ptr; - size_t len; -}; - -void init_string(struct string *s) { - s->len = 0; - s->ptr = (char*)(malloc(s->len+1)); - if (s->ptr == NULL) { - fprintf(stderr, "malloc() failed\n"); - exit(EXIT_FAILURE); - } - s->ptr[0] = '\0'; -} - -size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s) -{ - size_t new_len = s->len + size*nmemb; - s->ptr = (char*)(realloc(s->ptr, new_len+1)); - if (s->ptr == NULL) { - fprintf(stderr, "realloc() failed\n"); - exit(EXIT_FAILURE); - } - memcpy(s->ptr+s->len, ptr, size*nmemb); - s->ptr[new_len] = '\0'; - s->len = new_len; - - return size*nmemb; -} - static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) { ((std::string*)userp)->append((char*)contents, size * nmemb); @@ -60,16 +30,15 @@ void init() std::string getPage(std::string url) { - struct string readBuffer; - init_string(&readBuffer); + std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); res = curl_easy_perform(curl); if(res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - return readBuffer.ptr; + return readBuffer; } void shutdown() diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index 0d4061e08..6459a5682 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -41,9 +41,14 @@ int main() std::string password; cout << "Password="; std::cin >> password; + /// HOST NICKNAME : + std::string hostNickname; + cout << "Nickname="; + std::cin >> hostNickname; ConnectToServer* connectionProtocol = new ConnectToServer(NULL); connectionProtocol->setPassword(password); connectionProtocol->setUsername(nickname); + connectionProtocol->setHostName(hostNickname); ClientNetworkManager clt; diff --git a/dev/SocketsBase/protocol.hpp b/dev/SocketsBase/protocol.hpp index b33339fe9..186a3f3b4 100644 --- a/dev/SocketsBase/protocol.hpp +++ b/dev/SocketsBase/protocol.hpp @@ -23,6 +23,8 @@ class Protocol virtual void setup() = 0; virtual void start() = 0; + virtual void pause() = 0; + virtual void unpause() = 0; virtual void update() = 0; PROTOCOL_TYPE getProtocolType(); diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp index f3c22d135..f7ce3426a 100644 --- a/dev/SocketsBase/protocol_manager.cpp +++ b/dev/SocketsBase/protocol_manager.cpp @@ -22,29 +22,54 @@ void ProtocolManager::messageReceived(uint8_t* data) void ProtocolManager::runProtocol(Protocol* protocol) { - m_protocols.push_back(protocol); + ProtocolInfo protocolInfo; + protocolInfo.paused = false; + protocolInfo.protocol = protocol; + m_protocols.push_back(protocolInfo); protocol->setListener(this); protocol->setup(); protocol->start(); - printf("*** PROTOCOL MANAGER *** - A new protocol has been started. There are %i protocols running.\n", m_protocols.size()); + printf("*** PROTOCOL MANAGER *** - A new protocol has been started. There are %ld protocols running.\n", m_protocols.size()); } void ProtocolManager::stopProtocol(Protocol* protocol) { +} +void ProtocolManager::pauseProtocol(Protocol* protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol) + { + m_protocols[i].paused = true; + m_protocols[i].protocol->pause(); + } + } +} +void ProtocolManager::unpauseProtocol(Protocol* protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol && m_protocols[i].paused == true) + { + m_protocols[i].paused = false; + m_protocols[i].protocol->unpause(); + } + } } void ProtocolManager::protocolTerminated(Protocol* protocol) { int offset = 0; for (unsigned int i = 0; i < m_protocols.size(); i++) { - if (m_protocols[i-offset] == protocol) + if (m_protocols[i-offset].protocol == protocol) { - delete m_protocols[i]; + delete m_protocols[i].protocol; m_protocols.erase(m_protocols.begin()+(i-offset), m_protocols.begin()+(i-offset)+1); offset++; } } - printf("*** PROTOCOL MANAGER *** - A protocol has been terminated. There are %i protocols running.\n", m_protocols.size()); + printf("*** PROTOCOL MANAGER *** - A protocol has been terminated. There are %ld protocols running.\n", m_protocols.size()); } void ProtocolManager::update() @@ -57,15 +82,16 @@ void ProtocolManager::update() PROTOCOL_TYPE searchedProtocol = (PROTOCOL_TYPE)(data[0]); for (unsigned int i = 0; i < m_protocols.size() ; i++) { - if (m_protocols[i]->getProtocolType() == searchedProtocol) - m_protocols[i]->messageReceived(data+1); + if (m_protocols[i].protocol->getProtocolType() == searchedProtocol) // pass data to them even when paused + m_protocols[i].protocol->messageReceived(data+1); } m_messagesToProcess.pop_back(); } // now update all protocols for (unsigned int i = 0; i < m_protocols.size(); i++) { - m_protocols[i]->update(); + if (m_protocols[i].paused == false) + m_protocols[i].protocol->update(); } } diff --git a/dev/SocketsBase/protocol_manager.hpp b/dev/SocketsBase/protocol_manager.hpp index 4918d3069..f43170133 100644 --- a/dev/SocketsBase/protocol_manager.hpp +++ b/dev/SocketsBase/protocol_manager.hpp @@ -8,6 +8,11 @@ class Protocol; class ProtocolManager { + typedef struct + { + bool paused; + Protocol* protocol; + } ProtocolInfo; public: ProtocolManager(); virtual ~ProtocolManager(); @@ -16,6 +21,8 @@ class ProtocolManager virtual void runProtocol(Protocol* protocol); virtual void stopProtocol(Protocol* protocol); + virtual void pauseProtocol(Protocol* protocol); + virtual void unpauseProtocol(Protocol* protocol); virtual void protocolTerminated(Protocol* protocol); virtual void update(); @@ -23,7 +30,7 @@ class ProtocolManager virtual int runningProtocolsCount(); protected: - std::vector m_protocols; + std::vector m_protocols; std::vector m_messagesToProcess; }; diff --git a/dev/SocketsBase/protocols/connect_to_server.cpp b/dev/SocketsBase/protocols/connect_to_server.cpp index 37cfa65a6..2f90ef842 100644 --- a/dev/SocketsBase/protocols/connect_to_server.cpp +++ b/dev/SocketsBase/protocols/connect_to_server.cpp @@ -3,6 +3,7 @@ #include "../http_functions.hpp" #include +#include ConnectToServer::ConnectToServer(CallbackObject* callbackObject) : Protocol(callbackObject) { @@ -25,13 +26,21 @@ void ConnectToServer::messageReceived(uint8_t* data) void ConnectToServer::start() { - if (m_ownPublicIp == 0 || m_ownPublicPort == 0 || m_username == "" || m_password == "") + if (m_ownPublicIp == 0 || m_ownPublicPort == 0 || m_username == "" || m_password == "" || m_hostName == "") { - printf("You have to set the public ip:port and username:password before starting this protocol.\n"); + printf("You have to set the public ip:port, username:password and the host nickname before starting this protocol.\n"); m_listener->protocolTerminated(this); } } +void ConnectToServer::pause() +{ +} + +void ConnectToServer::unpause() +{ +} + void ConnectToServer::update() { if (m_state == NOTHING) @@ -39,20 +48,22 @@ void ConnectToServer::update() char url[512]; sprintf(url, "http://stkconnect.freeserver.me/log.php?set&nick=%s&ip=%u&port=%u&pwd=%s", m_username.c_str(), m_ownPublicIp, m_ownPublicPort, m_password.c_str()); std::string result = HTTP::getPage(url); - //printf("Result from web page is %s\n", result.c_str()); - if (result[0] == 's' && result[1] == 'u' && result[2] == 'c') + std::cout << "size of answer : " << result.size() << std::endl; + if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') { printf("Address set.\n"); m_state = ADDRESS_KNOWN_ONLINE; } - if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i') + if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') { - printf("Login fail.\n"); - m_state = ADDRESS_KNOWN_ONLINE; + printf("Login fail. Please re-set username:password and restart the protocol.\n"); + m_state = NOTHING; + m_listener->pauseProtocol(this); } } else if (m_state == ADDRESS_KNOWN_ONLINE) { + } else if (m_state == PEER_ADDRESS_RETREIVED) { @@ -78,3 +89,8 @@ void ConnectToServer::setPassword(std::string password) { m_password = password; } + +void ConnectToServer::setHostName(std::string hostName) +{ + m_hostName = hostName; +} diff --git a/dev/SocketsBase/protocols/connect_to_server.hpp b/dev/SocketsBase/protocols/connect_to_server.hpp index 02f78b9c8..4f9a188d7 100644 --- a/dev/SocketsBase/protocols/connect_to_server.hpp +++ b/dev/SocketsBase/protocols/connect_to_server.hpp @@ -13,15 +13,19 @@ class ConnectToServer : public Protocol, public CallbackObject virtual void messageReceived(uint8_t* data); virtual void setup(); virtual void start(); + virtual void pause(); + virtual void unpause(); virtual void update(); void setSelfAddress(uint32_t ip, uint16_t port); void setUsername(std::string username); void setPassword(std::string password); + void setHostName(std::string hostName); protected: uint32_t m_ownPublicIp; uint16_t m_ownPublicPort; + std::string m_hostName; std::string m_username; std::string m_password; enum STATE @@ -32,6 +36,7 @@ class ConnectToServer : public Protocol, public CallbackObject CONNECTED }; STATE m_state; + double firstTime; }; #endif // CONNECT_TO_SERVER_HPP diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index c5fe8a587..78e2c6938 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -41,6 +41,14 @@ void GetPublicAddress::setup() void GetPublicAddress::start() { } + +void GetPublicAddress::pause() +{ +} + +void GetPublicAddress::unpause() +{ +} void GetPublicAddress::update() { diff --git a/dev/SocketsBase/protocols/get_public_address.hpp b/dev/SocketsBase/protocols/get_public_address.hpp index 8a204cd7e..5fafdbd94 100644 --- a/dev/SocketsBase/protocols/get_public_address.hpp +++ b/dev/SocketsBase/protocols/get_public_address.hpp @@ -13,6 +13,8 @@ class GetPublicAddress : public Protocol virtual void setup(); virtual void start(); + virtual void pause(); + virtual void unpause(); virtual void update(); protected: @@ -24,7 +26,7 @@ class GetPublicAddress : public Protocol }; STATE m_state; uint32_t m_stunTransactionID[3]; - const uint32_t m_stunMagicCookie = 0x2112A442; + static const uint32_t m_stunMagicCookie = 0x2112A442; }; #endif // GET_PUBLIC_ADDRESS_HPP diff --git a/dev/SocketsBase/time.hpp b/dev/SocketsBase/time.hpp new file mode 100644 index 000000000..e2e137974 --- /dev/null +++ b/dev/SocketsBase/time.hpp @@ -0,0 +1,19 @@ +#ifndef TIME_HPP_INCLUDED +#define TIME_HPP_INCLUDED + +#include + +namespace Time +{ +double getSeconds() +{ + time_t timer; + time(&timer); + struct tm y2k; + y2k.tm_hour = 0; y2k.tm_min = 0; y2k.tm_sec = 0; + y2k.tm_year = 100; y2k.tm_mon = 0; y2k.tm_mday = 1; + return difftime(timer,mktime(&y2k)); // get the seconds elapsed since january 2000 +} +} + +#endif // TIME_HPP_INCLUDED From 714a85b507d59cc3a1271d3fdeacd8b47db05856 Mon Sep 17 00:00:00 2001 From: hilnius Date: Tue, 18 Jun 2013 22:25:03 +0000 Subject: [PATCH 10/22] Adding protocols to publish/hide information in the SQL database. The protocol that allows a client to identify himself, retreive a server public IP:port and connect to it is finished. Remains the server-protocol for it. Protocol management improved (protocols can be paused/unpaused). git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12883 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/SocketsBase.cbp | 7 +- dev/SocketsBase/client_network_manager.cpp | 2 + dev/SocketsBase/main.cpp | 53 +++++++++++---- dev/SocketsBase/network_manager.hpp | 3 +- dev/SocketsBase/protocol_manager.cpp | 36 ++++++++-- dev/SocketsBase/protocol_manager.hpp | 14 +++- .../protocols/connect_to_server.cpp | 56 +++++++++++++-- .../protocols/connect_to_server.hpp | 3 + .../protocols/get_public_address.cpp | 6 +- .../protocols/hide_public_address.cpp | 68 +++++++++++++++++++ .../protocols/hide_public_address.hpp | 34 ++++++++++ .../protocols/show_public_address.cpp | 68 +++++++++++++++++++ .../protocols/show_public_address.hpp | 34 ++++++++++ dev/SocketsBase/server_network_manager.cpp | 2 +- dev/SocketsBase/stk_peer.cpp | 6 ++ 15 files changed, 358 insertions(+), 34 deletions(-) create mode 100644 dev/SocketsBase/protocols/hide_public_address.cpp create mode 100644 dev/SocketsBase/protocols/hide_public_address.hpp create mode 100644 dev/SocketsBase/protocols/show_public_address.cpp create mode 100644 dev/SocketsBase/protocols/show_public_address.hpp diff --git a/dev/SocketsBase/SocketsBase.cbp b/dev/SocketsBase/SocketsBase.cbp index 09c432605..89b14b124 100644 --- a/dev/SocketsBase/SocketsBase.cbp +++ b/dev/SocketsBase/SocketsBase.cbp @@ -31,6 +31,7 @@ + @@ -48,13 +49,16 @@ - + + + + @@ -62,6 +66,7 @@ + diff --git a/dev/SocketsBase/client_network_manager.cpp b/dev/SocketsBase/client_network_manager.cpp index e27de2e90..461d1acfb 100644 --- a/dev/SocketsBase/client_network_manager.cpp +++ b/dev/SocketsBase/client_network_manager.cpp @@ -37,5 +37,7 @@ void ClientNetworkManager::packetReceived(char* data) } void ClientNetworkManager::sendPacket(char* data) { + if (m_peers.size() > 1) + printf("Ambiguous send of data\n"); m_peers[0]->sendPacket(data); } diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index 6459a5682..19414ea81 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -7,6 +7,7 @@ #include "protocols/get_public_address.hpp" #include "http_functions.hpp" #include "protocols/connect_to_server.hpp" +#include "protocols/hide_public_address.hpp" #include #include @@ -33,6 +34,10 @@ int main() cin >> answer; if (answer == "client") { + protocolListener = new ProtocolManager(); + ClientNetworkManager clt; + clt.run(); + /// NICKNAME : std::string nickname; cout << "Nickname="; @@ -43,18 +48,12 @@ int main() std::cin >> password; /// HOST NICKNAME : std::string hostNickname; - cout << "Nickname="; + cout << "Host Nickname="; std::cin >> hostNickname; - ConnectToServer* connectionProtocol = new ConnectToServer(NULL); + ConnectToServer* connectionProtocol = new ConnectToServer(&clt); connectionProtocol->setPassword(password); connectionProtocol->setUsername(nickname); connectionProtocol->setHostName(hostNickname); - - - ClientNetworkManager clt; - clt.run(); - - protocolListener = new ProtocolManager(); pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); pthread_create(thrd, NULL, foo, NULL); @@ -64,31 +63,57 @@ int main() prt->setListener(protocolListener); connectionProtocol->setListener(protocolListener); - protocolListener->runProtocol(prt); + protocolListener->startProtocol(prt); - clt.connect(0x0100007f, 7000); // addr in little endian, real address is 7f 00 00 01 (127.0.0.1) - + //clt.connect(0x0100007f, 7000); // addr in little endian, real address is 7f 00 00 01 (127.0.0.1) + bool* connected = &connectionProtocol->connected; std::string buffer; while (1) { cin >> buffer; - if (buffer == "protocolsCount") + if (buffer == "cmd=protocolsCount") { cout << protocolListener->runningProtocolsCount() << " protocols are running." << endl; continue; } + if (buffer == "cmd=hideAddress") + { + HidePublicAddress* hideipv4 = new HidePublicAddress(NULL); + hideipv4->setPassword(password); + hideipv4->setNickname(nickname); + protocolListener->startProtocol(hideipv4); + } + if (buffer == "cmd=login") + { + std::cout << "Username="; + std::cin >> nickname; + connectionProtocol->setUsername(nickname); + std::cout << "Password="; + std::cin >> password; + connectionProtocol->setPassword(password); + connectionProtocol->unpause(); + } if (buffer.size() == 0) { continue; } char buffer2[256]; strcpy(buffer2, buffer.c_str()); - clt.sendPacket(buffer2); + if (*connected) + { + clt.sendPacket(buffer2); + } } + + + enet_deinitialize(); } else if (answer == "host") { ServerNetworkManager srv; srv.run(); srv.start(); - //srv.protocolListener = new ProtocolManager(); + srv.protocolListener = new ProtocolManager(); + + GetPublicAddress + while(1){} } return 0; diff --git a/dev/SocketsBase/network_manager.hpp b/dev/SocketsBase/network_manager.hpp index b1e2023c7..959e049a6 100644 --- a/dev/SocketsBase/network_manager.hpp +++ b/dev/SocketsBase/network_manager.hpp @@ -6,8 +6,9 @@ #include #include "protocol_manager.hpp" +#include "callback_object.hpp" -class NetworkManager +class NetworkManager : public CallbackObject { public: NetworkManager(); diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp index f7ce3426a..2390f117b 100644 --- a/dev/SocketsBase/protocol_manager.cpp +++ b/dev/SocketsBase/protocol_manager.cpp @@ -4,7 +4,9 @@ #include #include +#include +#define RAND_MAX 65536 ProtocolManager::ProtocolManager() { @@ -20,16 +22,18 @@ void ProtocolManager::messageReceived(uint8_t* data) m_messagesToProcess.push_back(data); } -void ProtocolManager::runProtocol(Protocol* protocol) +int ProtocolManager::startProtocol(Protocol* protocol) { ProtocolInfo protocolInfo; - protocolInfo.paused = false; + protocolInfo.state = PROTOCOL_STATE_RUNNING; + assignProtocolId(protocolInfo); protocolInfo.protocol = protocol; m_protocols.push_back(protocolInfo); protocol->setListener(this); protocol->setup(); protocol->start(); printf("*** PROTOCOL MANAGER *** - A new protocol has been started. There are %ld protocols running.\n", m_protocols.size()); + return protocolInfo.id; } void ProtocolManager::stopProtocol(Protocol* protocol) { @@ -39,9 +43,9 @@ void ProtocolManager::pauseProtocol(Protocol* protocol) { for (unsigned int i = 0; i < m_protocols.size(); i++) { - if (m_protocols[i].protocol == protocol) + if (m_protocols[i].protocol == protocol && m_protocols[i].state == PROTOCOL_STATE_RUNNING) { - m_protocols[i].paused = true; + m_protocols[i].state = PROTOCOL_STATE_PAUSED; m_protocols[i].protocol->pause(); } } @@ -50,9 +54,9 @@ void ProtocolManager::unpauseProtocol(Protocol* protocol) { for (unsigned int i = 0; i < m_protocols.size(); i++) { - if (m_protocols[i].protocol == protocol && m_protocols[i].paused == true) + if (m_protocols[i].protocol == protocol && m_protocols[i].state == PROTOCOL_STATE_PAUSED) { - m_protocols[i].paused = false; + m_protocols[i].state = PROTOCOL_STATE_RUNNING; m_protocols[i].protocol->unpause(); } } @@ -90,7 +94,7 @@ void ProtocolManager::update() // now update all protocols for (unsigned int i = 0; i < m_protocols.size(); i++) { - if (m_protocols[i].paused == false) + if (m_protocols[i].state == PROTOCOL_STATE_RUNNING) m_protocols[i].protocol->update(); } } @@ -100,3 +104,21 @@ int ProtocolManager::runningProtocolsCount() return m_protocols.size(); } +void ProtocolManager::assignProtocolId(ProtocolInfo& protocolInfo) +{ + uint32_t newId; + bool exists; + do + { + newId = (rand()<<16)+rand(); + exists = false; + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].id == newId) + exists = true; + } + } while (exists); + protocolInfo.id = newId; +} + + diff --git a/dev/SocketsBase/protocol_manager.hpp b/dev/SocketsBase/protocol_manager.hpp index f43170133..87b070b96 100644 --- a/dev/SocketsBase/protocol_manager.hpp +++ b/dev/SocketsBase/protocol_manager.hpp @@ -6,12 +6,20 @@ class Protocol; +enum PROTOCOL_STATE +{ + PROTOCOL_STATE_RUNNING, + PROTOCOL_STATE_PAUSED, + PROTOCOL_STATE_TERMINATED +}; + class ProtocolManager { typedef struct { - bool paused; + PROTOCOL_STATE state; Protocol* protocol; + uint32_t id; } ProtocolInfo; public: ProtocolManager(); @@ -19,7 +27,7 @@ class ProtocolManager virtual void messageReceived(uint8_t* data); - virtual void runProtocol(Protocol* protocol); + virtual int startProtocol(Protocol* protocol); virtual void stopProtocol(Protocol* protocol); virtual void pauseProtocol(Protocol* protocol); virtual void unpauseProtocol(Protocol* protocol); @@ -30,6 +38,8 @@ class ProtocolManager virtual int runningProtocolsCount(); protected: + void assignProtocolId(ProtocolInfo& protocolInfo); + std::vector m_protocols; std::vector m_messagesToProcess; }; diff --git a/dev/SocketsBase/protocols/connect_to_server.cpp b/dev/SocketsBase/protocols/connect_to_server.cpp index 2f90ef842..ac78e9184 100644 --- a/dev/SocketsBase/protocols/connect_to_server.cpp +++ b/dev/SocketsBase/protocols/connect_to_server.cpp @@ -1,14 +1,17 @@ #include "connect_to_server.hpp" #include "../http_functions.hpp" +#include "../time.hpp" +#include "../client_network_manager.hpp" #include -#include +#include ConnectToServer::ConnectToServer(CallbackObject* callbackObject) : Protocol(callbackObject) { m_ownPublicIp = 0; m_ownPublicPort = 0; + connected = false; } ConnectToServer::~ConnectToServer() @@ -35,10 +38,12 @@ void ConnectToServer::start() void ConnectToServer::pause() { + m_listener->pauseProtocol(this); // need to be sure that the protocol manager knows } void ConnectToServer::unpause() { + m_listener->unpauseProtocol(this); // need to be sure that the protocol manager knows } void ConnectToServer::update() @@ -48,7 +53,6 @@ void ConnectToServer::update() char url[512]; sprintf(url, "http://stkconnect.freeserver.me/log.php?set&nick=%s&ip=%u&port=%u&pwd=%s", m_username.c_str(), m_ownPublicIp, m_ownPublicPort, m_password.c_str()); std::string result = HTTP::getPage(url); - std::cout << "size of answer : " << result.size() << std::endl; if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') { printf("Address set.\n"); @@ -56,17 +60,59 @@ void ConnectToServer::update() } if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') { - printf("Login fail. Please re-set username:password and restart the protocol.\n"); + printf("Login fail. Please re-set username:password and unpause the protocol.\n"); m_state = NOTHING; - m_listener->pauseProtocol(this); + pause(); } } else if (m_state == ADDRESS_KNOWN_ONLINE) { - + static double target = 0; + double currentTime = Time::getSeconds(); + if (currentTime < target-1800) // sometimes the getSeconds method forgets 3600 seconds. + currentTime += 3600; + if (currentTime > target) + { + char url[512]; + sprintf(url, "http://stkconnect.freeserver.me/log.php?get&nick=%s", m_hostName.c_str()); + std::string result = HTTP::getPage(url); + if (result == "") + { + printf("The host you try to reach does not exist. Change the host name please.\n"); + m_state = NOTHING; + pause(); + return; + } + std::string ipAddr = result; + ipAddr.erase(ipAddr.find_first_of(':')); + std::string portNb = result; + portNb.erase(0, portNb.find_first_of(':')+1); + uint32_t dstIp = (uint32_t)(atoi(ipAddr.c_str())); + uint16_t dstPort = (uint32_t)(atoi(portNb.c_str())); + if (dstIp == 0 || dstPort == 0) + { + printf("The host you try to reach is not online. There will be a new try in 10 seconds.\n"); + target = currentTime+10; + } + else + { + printf("Public ip of target is %i.%i.%i.%i:%i\n", (dstIp>>24)&0xff, (dstIp>>16)&0xff, (dstIp>>8)&0xff, dstIp&0xff, dstPort); + m_serverIp = ((dstIp&0x000000ff)<<24) // change the server IP to have a network-byte order + + ((dstIp&0x0000ff00)<<8) + + ((dstIp&0x00ff0000)>>8) + + ((dstIp&0xff000000)>>24); + m_serverPort = dstPort; + m_state = PEER_ADDRESS_RETREIVED; + } + } } else if (m_state == PEER_ADDRESS_RETREIVED) { + // we know the distant address:port, just need to connect. + ClientNetworkManager* networkManager = static_cast(m_callbackObject); + networkManager->connect(m_serverIp, m_serverPort); + m_state = CONNECTED; + connected = true; } else if (m_state == CONNECTED) { diff --git a/dev/SocketsBase/protocols/connect_to_server.hpp b/dev/SocketsBase/protocols/connect_to_server.hpp index 4f9a188d7..77fcf9673 100644 --- a/dev/SocketsBase/protocols/connect_to_server.hpp +++ b/dev/SocketsBase/protocols/connect_to_server.hpp @@ -22,9 +22,12 @@ class ConnectToServer : public Protocol, public CallbackObject void setPassword(std::string password); void setHostName(std::string hostName); + bool connected; protected: uint32_t m_ownPublicIp; uint16_t m_ownPublicPort; + uint32_t m_serverIp; + uint16_t m_serverPort; std::string m_hostName; std::string m_username; std::string m_password; diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index 78e2c6938..887f7d028 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -92,14 +92,14 @@ void GetPublicAddress::update() bytes[20] = '\0'; printf("Querrying STUN server 132.177.123.6\n"); - unsigned int dst = 132*256*256*256+177*256*256+123*256+6; + unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; NetworkManager::setManualSocketsMode(true); NetworkManager::getHost()->sendRawPacket(bytes, 20, dst, 3478); m_state = TEST_SENT; } if (m_state == TEST_SENT) { - unsigned int dst = 132*256*256*256+177*256*256+123*256+6; + unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; uint8_t* data = NetworkManager::getHost()->receiveRawPacket(dst, 3478); assert(data); @@ -181,7 +181,7 @@ void GetPublicAddress::update() NetworkManager::setManualSocketsMode(false); ConnectToServer* cbObj = static_cast(m_callbackObject); cbObj->setSelfAddress(address, port); - m_listener->runProtocol(cbObj); + m_listener->startProtocol(cbObj); } else m_state = NOTHING_DONE; // need to re-send the stun request diff --git a/dev/SocketsBase/protocols/hide_public_address.cpp b/dev/SocketsBase/protocols/hide_public_address.cpp new file mode 100644 index 000000000..98c122c44 --- /dev/null +++ b/dev/SocketsBase/protocols/hide_public_address.cpp @@ -0,0 +1,68 @@ +#include "hide_public_address.hpp" + +#include "../http_functions.hpp" + +#include + +HidePublicAddress::HidePublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject) +{ +} + +HidePublicAddress::~HidePublicAddress() +{ +} + +void HidePublicAddress::messageReceived(uint8_t* data) +{ +} + +void HidePublicAddress::setup() +{ + m_state = NONE; +} + +void HidePublicAddress::start() +{ +} + +void HidePublicAddress::pause() +{ +} + +void HidePublicAddress::unpause() +{ +} + +void HidePublicAddress::update() +{ + if (m_state == NONE) + { + char url[512]; + sprintf(url, "http://stkconnect.freeserver.me/log.php?logout&nick=%s&pwd=%s", m_nickname.c_str(), m_password.c_str()); + std::string result = HTTP::getPage(url); + if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') + { + printf("Public address hidden successfully.\n"); + m_state = DONE; + } + if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') + { + printf("Public address still visible. Re-set nick:password and retry.\n"); + m_state = NONE; + m_listener->pauseProtocol(this); + } + } + else if (m_state == DONE) + { + m_listener->protocolTerminated(this); + } +} + +void HidePublicAddress::setNickname(std::string nickname) +{ + m_nickname = nickname; +} +void HidePublicAddress::setPassword(std::string password) +{ + m_password = password; +} diff --git a/dev/SocketsBase/protocols/hide_public_address.hpp b/dev/SocketsBase/protocols/hide_public_address.hpp new file mode 100644 index 000000000..3e9b3beb3 --- /dev/null +++ b/dev/SocketsBase/protocols/hide_public_address.hpp @@ -0,0 +1,34 @@ +#ifndef HIDE_PUBLIC_ADDRESS_HPP +#define HIDE_PUBLIC_ADDRESS_HPP + +#include "../protocol.hpp" +#include + +class HidePublicAddress : public Protocol +{ + public: + HidePublicAddress(CallbackObject* callbackObject); + virtual ~HidePublicAddress(); + + virtual void messageReceived(uint8_t* data); + virtual void setup(); + virtual void start(); + virtual void pause(); + virtual void unpause(); + virtual void update(); + + virtual void setNickname(std::string nickname); + virtual void setPassword(std::string password); + protected: + std::string m_nickname; + std::string m_password; + + enum STATE + { + NONE, + DONE + }; + STATE m_state; +}; + +#endif // HIDE_PUBLIC_ADDRESS_HPP diff --git a/dev/SocketsBase/protocols/show_public_address.cpp b/dev/SocketsBase/protocols/show_public_address.cpp new file mode 100644 index 000000000..18661cc59 --- /dev/null +++ b/dev/SocketsBase/protocols/show_public_address.cpp @@ -0,0 +1,68 @@ +#include "show_public_address.hpp" + +#include "../http_functions.hpp" + +#include + +ShowPublicAddress::ShowPublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject) +{ +} + +ShowPublicAddress::~ShowPublicAddress() +{ +} + +void ShowPublicAddress::messageReceived(uint8_t* data) +{ +} + +void ShowPublicAddress::setup() +{ + m_state = NONE; +} + +void ShowPublicAddress::start() +{ +} + +void ShowPublicAddress::pause() +{ +} + +void ShowPublicAddress::unpause() +{ +} + +void ShowPublicAddress::update() +{ + if (m_state == NONE) + { + char url[512]; + sprintf(url, "http://stkconnect.freeserver.me/log.php?logout&nick=%s&pwd=%s", m_nickname.c_str(), m_password.c_str()); + std::string result = HTTP::getPage(url); + if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') + { + printf("Public address hidden successfully.\n"); + m_state = DONE; + } + if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') + { + printf("Public address still visible. Re-set nick:password and retry.\n"); + m_state = NONE; + m_listener->pauseProtocol(this); + } + } + else if (m_state == DONE) + { + m_listener->protocolTerminated(this); + } +} + +void ShowPublicAddress::setNickname(std::string nickname) +{ + m_nickname = nickname; +} +void ShowPublicAddress::setPassword(std::string password) +{ + m_password = password; +} diff --git a/dev/SocketsBase/protocols/show_public_address.hpp b/dev/SocketsBase/protocols/show_public_address.hpp new file mode 100644 index 000000000..9de38a208 --- /dev/null +++ b/dev/SocketsBase/protocols/show_public_address.hpp @@ -0,0 +1,34 @@ +#ifndef SHOW_PUBLIC_ADDRESS_HPP +#define SHOW_PUBLIC_ADDRESS_HPP + +#include "../protocol.hpp" +#include + +class ShowPublicAddress : public Protocol +{ + public: + ShowPublicAddress(CallbackObject* callbackObject); + virtual ~ShowPublicAddress(); + + virtual void messageReceived(uint8_t* data); + virtual void setup(); + virtual void start(); + virtual void pause(); + virtual void unpause(); + virtual void update(); + + virtual void setNickname(std::string nickname); + virtual void setPassword(std::string password); + protected: + std::string m_nickname; + std::string m_password; + + enum STATE + { + NONE, + DONE + }; + STATE m_state; +}; + +#endif // HIDE_PUBLIC_ADDRESS_HPP diff --git a/dev/SocketsBase/server_network_manager.cpp b/dev/SocketsBase/server_network_manager.cpp index 939b1d465..1c4164d59 100644 --- a/dev/SocketsBase/server_network_manager.cpp +++ b/dev/SocketsBase/server_network_manager.cpp @@ -28,7 +28,7 @@ void ServerNetworkManager::start() m_localhost = new STKHost(); m_localhost->setupServer(STKHost::HOST_ANY, 7000, 32, 2, 0, 0); m_localhost->startListening(); - printf("Server now setup.\n"); + printf("Server now setup, listening on port 7000.\n"); } void ServerNetworkManager::packetReceived(char* data) diff --git a/dev/SocketsBase/stk_peer.cpp b/dev/SocketsBase/stk_peer.cpp index 290bfc2d5..e49834ea2 100644 --- a/dev/SocketsBase/stk_peer.cpp +++ b/dev/SocketsBase/stk_peer.cpp @@ -24,10 +24,16 @@ void STKPeer::connectToServer(STKHost* host, uint32_t ip, uint16_t port, uint32_ printf("Could not connect to server.\n"); return; } + else + { + printf("Connected.\n"); + } } void STKPeer::sendPacket(char* data) { + //printf("sending packet to %i.%i.%i.%i:%i", (m_peer->address.host>>24)&0xff,(m_peer->address.host>>16)&0xff,(m_peer->address.host>>8)&0xff,(m_peer->address.host>>0)&0xff,m_peer->address.port); + ENetPacket* packet = enet_packet_create(data, strlen(data)+1,ENET_PACKET_FLAG_RELIABLE); enet_peer_send(m_peer, 0, packet); } From 6b1e1b94a53c96d087af5a440e438d36f9f4b097 Mon Sep 17 00:00:00 2001 From: hilnius Date: Wed, 19 Jun 2013 11:45:32 +0000 Subject: [PATCH 11/22] adding the network singleton interface, cleaning the protocols code git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12886 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/client_network_manager.hpp | 3 +- dev/SocketsBase/main.cpp | 54 ++++--------------- dev/SocketsBase/protocol.cpp | 10 ++++ dev/SocketsBase/protocol.hpp | 5 +- dev/SocketsBase/protocol_manager.cpp | 15 ++++-- dev/SocketsBase/protocol_manager.hpp | 5 +- .../protocols/connect_to_server.cpp | 19 ++----- .../protocols/connect_to_server.hpp | 3 -- .../protocols/get_public_address.cpp | 33 ++++-------- .../protocols/get_public_address.hpp | 3 -- .../protocols/hide_public_address.cpp | 24 +++------ .../protocols/hide_public_address.hpp | 7 +-- .../protocols/show_public_address.cpp | 36 ++++++------- .../protocols/show_public_address.hpp | 11 ++-- dev/SocketsBase/singleton.hpp | 3 +- 15 files changed, 86 insertions(+), 145 deletions(-) diff --git a/dev/SocketsBase/client_network_manager.hpp b/dev/SocketsBase/client_network_manager.hpp index cdbe73cb8..c9688a842 100644 --- a/dev/SocketsBase/client_network_manager.hpp +++ b/dev/SocketsBase/client_network_manager.hpp @@ -16,8 +16,9 @@ class ClientNetworkManager : public NetworkManager virtual void packetReceived(char* data); virtual void sendPacket(char* data); + protected: - private: + }; #endif // CLIENT_NETWORK_MANAGER_HPP diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index 19414ea81..3e25ccbb4 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -8,21 +8,12 @@ #include "http_functions.hpp" #include "protocols/connect_to_server.hpp" #include "protocols/hide_public_address.hpp" +#include "network_interface.hpp" #include #include using namespace std; -ProtocolManager* protocolListener; - -void* foo(void* data) -{ - while(1) - { - protocolListener->update(); - } - return NULL; -} int main() { @@ -34,10 +25,6 @@ int main() cin >> answer; if (answer == "client") { - protocolListener = new ProtocolManager(); - ClientNetworkManager clt; - clt.run(); - /// NICKNAME : std::string nickname; cout << "Nickname="; @@ -50,56 +37,36 @@ int main() std::string hostNickname; cout << "Host Nickname="; std::cin >> hostNickname; - ConnectToServer* connectionProtocol = new ConnectToServer(&clt); - connectionProtocol->setPassword(password); - connectionProtocol->setUsername(nickname); - connectionProtocol->setHostName(hostNickname); - pthread_t* thrd = (pthread_t*)(malloc(sizeof(pthread_t))); - pthread_create(thrd, NULL, foo, NULL); + NetworkInterface::getInstance()->initNetwork(false); - // start a retreive stun addr protocol - Protocol* prt = new GetPublicAddress(connectionProtocol); - prt->setListener(protocolListener); - connectionProtocol->setListener(protocolListener); - - protocolListener->startProtocol(prt); - + NetworkInterface::getInstance()->setLogin(nickname, password); + NetworkInterface::getInstance()->connectToHost(hostNickname); + //clt.connect(0x0100007f, 7000); // addr in little endian, real address is 7f 00 00 01 (127.0.0.1) - bool* connected = &connectionProtocol->connected; std::string buffer; while (1) { cin >> buffer; if (buffer == "cmd=protocolsCount") { - cout << protocolListener->runningProtocolsCount() << " protocols are running." << endl; + //cout << protocolListener->runningProtocolsCount() << " protocols are running." << endl; continue; } if (buffer == "cmd=hideAddress") { - HidePublicAddress* hideipv4 = new HidePublicAddress(NULL); - hideipv4->setPassword(password); - hideipv4->setNickname(nickname); - protocolListener->startProtocol(hideipv4); } if (buffer == "cmd=login") { std::cout << "Username="; std::cin >> nickname; - connectionProtocol->setUsername(nickname); std::cout << "Password="; std::cin >> password; - connectionProtocol->setPassword(password); - connectionProtocol->unpause(); } if (buffer.size() == 0) { continue; } char buffer2[256]; strcpy(buffer2, buffer.c_str()); - if (*connected) - { - clt.sendPacket(buffer2); - } + //NetworkInterface::getInstance()->sendPacket(buffer2); } @@ -107,12 +74,9 @@ int main() } else if (answer == "host") { - ServerNetworkManager srv; - srv.run(); - srv.start(); - srv.protocolListener = new ProtocolManager(); + NetworkInterface::getInstance()->initNetwork(true); - GetPublicAddress + //GetPublicAddress while(1){} } diff --git a/dev/SocketsBase/protocol.cpp b/dev/SocketsBase/protocol.cpp index 99196b130..88d7efe4a 100644 --- a/dev/SocketsBase/protocol.cpp +++ b/dev/SocketsBase/protocol.cpp @@ -9,6 +9,16 @@ Protocol::~Protocol() { } +void Protocol::pause() +{ + m_listener->pauseProtocol(this); +} +void Protocol::unpause() +{ + m_listener->unpauseProtocol(this); +} + + void Protocol::setListener(ProtocolManager* listener) { m_listener = listener; diff --git a/dev/SocketsBase/protocol.hpp b/dev/SocketsBase/protocol.hpp index 186a3f3b4..f3a96caa7 100644 --- a/dev/SocketsBase/protocol.hpp +++ b/dev/SocketsBase/protocol.hpp @@ -22,9 +22,8 @@ class Protocol void setListener(ProtocolManager* listener); virtual void setup() = 0; - virtual void start() = 0; - virtual void pause() = 0; - virtual void unpause() = 0; + virtual void pause(); + virtual void unpause(); virtual void update() = 0; PROTOCOL_TYPE getProtocolType(); diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp index 2390f117b..fcdc1b54a 100644 --- a/dev/SocketsBase/protocol_manager.cpp +++ b/dev/SocketsBase/protocol_manager.cpp @@ -31,8 +31,7 @@ int ProtocolManager::startProtocol(Protocol* protocol) m_protocols.push_back(protocolInfo); protocol->setListener(this); protocol->setup(); - protocol->start(); - printf("*** PROTOCOL MANAGER *** - A new protocol has been started. There are %ld protocols running.\n", m_protocols.size()); + printf("__ProtocolManager> A new protocol with id=%ud been started. There are %ld protocols running.\n", protocolInfo.id, m_protocols.size()); return protocolInfo.id; } void ProtocolManager::stopProtocol(Protocol* protocol) @@ -73,7 +72,7 @@ void ProtocolManager::protocolTerminated(Protocol* protocol) offset++; } } - printf("*** PROTOCOL MANAGER *** - A protocol has been terminated. There are %ld protocols running.\n", m_protocols.size()); + printf("__ProtocolManager> A protocol has been terminated. There are %ld protocols running.\n", m_protocols.size()); } void ProtocolManager::update() @@ -104,6 +103,16 @@ int ProtocolManager::runningProtocolsCount() return m_protocols.size(); } +PROTOCOL_STATE ProtocolManager::getProtocolState(uint32_t id) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].id == id) + return m_protocols[i].state; + } + return PROTOCOL_STATE_TERMINATED; +} + void ProtocolManager::assignProtocolId(ProtocolInfo& protocolInfo) { uint32_t newId; diff --git a/dev/SocketsBase/protocol_manager.hpp b/dev/SocketsBase/protocol_manager.hpp index 87b070b96..df50c2d9e 100644 --- a/dev/SocketsBase/protocol_manager.hpp +++ b/dev/SocketsBase/protocol_manager.hpp @@ -3,6 +3,7 @@ #include #include +#include "singleton.hpp" class Protocol; @@ -13,8 +14,9 @@ enum PROTOCOL_STATE PROTOCOL_STATE_TERMINATED }; -class ProtocolManager +class ProtocolManager : public Singleton { + friend class Singleton; typedef struct { PROTOCOL_STATE state; @@ -36,6 +38,7 @@ class ProtocolManager virtual void update(); virtual int runningProtocolsCount(); + virtual PROTOCOL_STATE getProtocolState(uint32_t id); protected: void assignProtocolId(ProtocolInfo& protocolInfo); diff --git a/dev/SocketsBase/protocols/connect_to_server.cpp b/dev/SocketsBase/protocols/connect_to_server.cpp index ac78e9184..b09d5a732 100644 --- a/dev/SocketsBase/protocols/connect_to_server.cpp +++ b/dev/SocketsBase/protocols/connect_to_server.cpp @@ -3,6 +3,7 @@ #include "../http_functions.hpp" #include "../time.hpp" #include "../client_network_manager.hpp" +#include "show_public_address.hpp" #include #include @@ -18,16 +19,12 @@ ConnectToServer::~ConnectToServer() { } -void ConnectToServer::setup() -{ -} - void ConnectToServer::messageReceived(uint8_t* data) { } -void ConnectToServer::start() +void ConnectToServer::setup() { if (m_ownPublicIp == 0 || m_ownPublicPort == 0 || m_username == "" || m_password == "" || m_hostName == "") { @@ -36,16 +33,6 @@ void ConnectToServer::start() } } -void ConnectToServer::pause() -{ - m_listener->pauseProtocol(this); // need to be sure that the protocol manager knows -} - -void ConnectToServer::unpause() -{ - m_listener->unpauseProtocol(this); // need to be sure that the protocol manager knows -} - void ConnectToServer::update() { if (m_state == NOTHING) @@ -64,6 +51,8 @@ void ConnectToServer::update() m_state = NOTHING; pause(); } + ShowPublicAddress* showAddr = new ShowPublicAddress(NULL); + showAddr->setUsername(m_username); } else if (m_state == ADDRESS_KNOWN_ONLINE) { diff --git a/dev/SocketsBase/protocols/connect_to_server.hpp b/dev/SocketsBase/protocols/connect_to_server.hpp index 77fcf9673..bcdafd833 100644 --- a/dev/SocketsBase/protocols/connect_to_server.hpp +++ b/dev/SocketsBase/protocols/connect_to_server.hpp @@ -12,9 +12,6 @@ class ConnectToServer : public Protocol, public CallbackObject virtual void messageReceived(uint8_t* data); virtual void setup(); - virtual void start(); - virtual void pause(); - virtual void unpause(); virtual void update(); void setSelfAddress(uint32_t ip, uint16_t port); diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index 887f7d028..4711311d4 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -2,6 +2,7 @@ #include "../network_manager.hpp" #include "connect_to_server.hpp" +#include "../network_interface.hpp" #include #include @@ -37,18 +38,6 @@ void GetPublicAddress::setup() { m_state = NOTHING_DONE; } - -void GetPublicAddress::start() -{ -} - -void GetPublicAddress::pause() -{ -} - -void GetPublicAddress::unpause() -{ -} void GetPublicAddress::update() { @@ -91,7 +80,7 @@ void GetPublicAddress::update() bytes[19] = (uint8_t)(m_stunTransactionID[2]); bytes[20] = '\0'; - printf("Querrying STUN server 132.177.123.6\n"); + printf("__GetPublicAddress> Querrying STUN server 132.177.123.6\n"); unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; NetworkManager::setManualSocketsMode(true); NetworkManager::getHost()->sendRawPacket(bytes, 20, dst, 3478); @@ -125,7 +114,7 @@ void GetPublicAddress::update() data[18] == (uint8_t)(m_stunTransactionID[2]>>8 ) && data[19] == (uint8_t)(m_stunTransactionID[2] )) { - printf("The STUN server responded with a valid answer\n"); + printf("__GetPublicAddress> The STUN server responded with a valid answer\n"); int messageSize = data[2]*256+data[3]; // parse the stun message now: @@ -133,12 +122,12 @@ void GetPublicAddress::update() uint8_t* attributes = data+20; if (messageSize == 0) { - printf("STUN answer does not contain any information.\n"); + printf("__GetPublicAddress> STUN answer does not contain any information.\n"); finish = true; } if (messageSize < 4) // cannot even read the size { - printf("STUN message is not valid.\n"); + printf("__GetPublicAddress> STUN message is not valid.\n"); finish = true; } uint16_t port; @@ -169,19 +158,19 @@ void GetPublicAddress::update() finish = true; if (messageSize < 4) // cannot even read the size { - printf("STUN message is not valid.\n"); + printf("__GetPublicAddress> STUN message is not valid.\n"); finish = true; } } // finished parsing, we know our public transport address if (valid) { - printf("The public address has been found : %i.%i.%i.%i:%i\n", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); + printf("__The public address has been found : %i.%i.%i.%i:%i\n", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); m_state = ADDRESS_KNOWN; NetworkManager::setManualSocketsMode(false); - ConnectToServer* cbObj = static_cast(m_callbackObject); - cbObj->setSelfAddress(address, port); - m_listener->startProtocol(cbObj); + TransportAddress* addr = static_cast(m_callbackObject); + addr->ip = address; + addr->port = port; } else m_state = NOTHING_DONE; // need to re-send the stun request @@ -194,7 +183,7 @@ void GetPublicAddress::update() } if (m_state == ADDRESS_KNOWN) { - // return the information and terminate the protocol + // terminate the protocol m_listener->protocolTerminated(this); } } diff --git a/dev/SocketsBase/protocols/get_public_address.hpp b/dev/SocketsBase/protocols/get_public_address.hpp index 5fafdbd94..5382c3a25 100644 --- a/dev/SocketsBase/protocols/get_public_address.hpp +++ b/dev/SocketsBase/protocols/get_public_address.hpp @@ -12,9 +12,6 @@ class GetPublicAddress : public Protocol virtual void messageReceived(uint8_t* data); virtual void setup(); - virtual void start(); - virtual void pause(); - virtual void unpause(); virtual void update(); protected: diff --git a/dev/SocketsBase/protocols/hide_public_address.cpp b/dev/SocketsBase/protocols/hide_public_address.cpp index 98c122c44..204a24bfe 100644 --- a/dev/SocketsBase/protocols/hide_public_address.cpp +++ b/dev/SocketsBase/protocols/hide_public_address.cpp @@ -21,35 +21,23 @@ void HidePublicAddress::setup() m_state = NONE; } -void HidePublicAddress::start() -{ -} - -void HidePublicAddress::pause() -{ -} - -void HidePublicAddress::unpause() -{ -} - void HidePublicAddress::update() { if (m_state == NONE) { char url[512]; - sprintf(url, "http://stkconnect.freeserver.me/log.php?logout&nick=%s&pwd=%s", m_nickname.c_str(), m_password.c_str()); + sprintf(url, "http://stkconnect.freeserver.me/log.php?logout&nick=%s&pwd=%s", m_username.c_str(), m_password.c_str()); std::string result = HTTP::getPage(url); if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') { - printf("Public address hidden successfully.\n"); + printf("__HidePublicAddress> Public address hidden successfully.\n"); m_state = DONE; } if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') { - printf("Public address still visible. Re-set nick:password and retry.\n"); + printf("__HidePublicAddress> Public address still visible. Re-set nick:password and retry.\n"); m_state = NONE; - m_listener->pauseProtocol(this); + pause(); } } else if (m_state == DONE) @@ -58,9 +46,9 @@ void HidePublicAddress::update() } } -void HidePublicAddress::setNickname(std::string nickname) +void HidePublicAddress::setUsername(std::string username) { - m_nickname = nickname; + m_username = username; } void HidePublicAddress::setPassword(std::string password) { diff --git a/dev/SocketsBase/protocols/hide_public_address.hpp b/dev/SocketsBase/protocols/hide_public_address.hpp index 3e9b3beb3..0ad887030 100644 --- a/dev/SocketsBase/protocols/hide_public_address.hpp +++ b/dev/SocketsBase/protocols/hide_public_address.hpp @@ -12,15 +12,12 @@ class HidePublicAddress : public Protocol virtual void messageReceived(uint8_t* data); virtual void setup(); - virtual void start(); - virtual void pause(); - virtual void unpause(); virtual void update(); - virtual void setNickname(std::string nickname); + virtual void setUsername(std::string username); virtual void setPassword(std::string password); protected: - std::string m_nickname; + std::string m_username; std::string m_password; enum STATE diff --git a/dev/SocketsBase/protocols/show_public_address.cpp b/dev/SocketsBase/protocols/show_public_address.cpp index 18661cc59..27fcf4273 100644 --- a/dev/SocketsBase/protocols/show_public_address.cpp +++ b/dev/SocketsBase/protocols/show_public_address.cpp @@ -19,37 +19,30 @@ void ShowPublicAddress::messageReceived(uint8_t* data) void ShowPublicAddress::setup() { m_state = NONE; -} - -void ShowPublicAddress::start() -{ -} - -void ShowPublicAddress::pause() -{ -} - -void ShowPublicAddress::unpause() -{ + if (m_publicIp == 0 || m_publicPort == 0 || m_username == "" || m_password == "") + { + printf("__ShowPublicAddress> You have to set the public ip:port, username:password and the host nickname before starting this protocol.\n"); + m_listener->protocolTerminated(this); + } } void ShowPublicAddress::update() { if (m_state == NONE) { - char url[512]; - sprintf(url, "http://stkconnect.freeserver.me/log.php?logout&nick=%s&pwd=%s", m_nickname.c_str(), m_password.c_str()); + char url[512]; + sprintf(url, "http://stkconnect.freeserver.me/log.php?set&nick=%s&ip=%u&port=%u&pwd=%s", m_username.c_str(), m_publicIp, m_publicPort, m_password.c_str()); std::string result = HTTP::getPage(url); if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') { - printf("Public address hidden successfully.\n"); + printf("__ShowPublicAddress> Address set.\n"); m_state = DONE; } if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') { - printf("Public address still visible. Re-set nick:password and retry.\n"); + printf("__ShowPublicAddress> Login fail. Please re-set username:password and unpause the protocol.\n"); m_state = NONE; - m_listener->pauseProtocol(this); + pause(); } } else if (m_state == DONE) @@ -58,11 +51,16 @@ void ShowPublicAddress::update() } } -void ShowPublicAddress::setNickname(std::string nickname) +void ShowPublicAddress::setUsername(std::string username) { - m_nickname = nickname; + m_username = username; } void ShowPublicAddress::setPassword(std::string password) { m_password = password; } +void ShowPublicAddress::setPublicAddress(uint32_t ip, uint16_t port) +{ + m_publicIp = ip; + m_publicPort = port; +} diff --git a/dev/SocketsBase/protocols/show_public_address.hpp b/dev/SocketsBase/protocols/show_public_address.hpp index 9de38a208..2ea727fa1 100644 --- a/dev/SocketsBase/protocols/show_public_address.hpp +++ b/dev/SocketsBase/protocols/show_public_address.hpp @@ -12,16 +12,17 @@ class ShowPublicAddress : public Protocol virtual void messageReceived(uint8_t* data); virtual void setup(); - virtual void start(); - virtual void pause(); - virtual void unpause(); virtual void update(); - virtual void setNickname(std::string nickname); + virtual void setUsername(std::string username); virtual void setPassword(std::string password); + virtual void setPublicAddress(uint32_t ip, uint16_t port); + protected: - std::string m_nickname; + std::string m_username; std::string m_password; + uint32_t m_publicIp; + uint16_t m_publicPort; enum STATE { diff --git a/dev/SocketsBase/singleton.hpp b/dev/SocketsBase/singleton.hpp index 61c7ce121..9b69fd3df 100644 --- a/dev/SocketsBase/singleton.hpp +++ b/dev/SocketsBase/singleton.hpp @@ -20,7 +20,6 @@ class Singleton } else { - std::cout << "singleton already created!" << std::endl; } return (static_cast (m_singleton)); } @@ -38,6 +37,6 @@ class Singleton static T *m_singleton; }; -template T *Singleton::_singleton = NULL; +template T *Singleton::m_singleton = NULL; #endif // SINGLETON_HPP From 3d62fd1214adcd6e1afd2144285d623e529c5bd2 Mon Sep 17 00:00:00 2001 From: hilnius Date: Wed, 19 Jun 2013 14:01:25 +0000 Subject: [PATCH 12/22] creating inherited singletons git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12887 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/client_network_manager.hpp | 10 +++++--- dev/SocketsBase/main.cpp | 3 +++ dev/SocketsBase/network_manager.cpp | 20 +++------------- dev/SocketsBase/network_manager.hpp | 24 ++++++++----------- dev/SocketsBase/protocol_manager.hpp | 12 +++++++--- .../protocols/connect_to_server.cpp | 2 -- .../protocols/get_public_address.cpp | 9 +++---- dev/SocketsBase/server_network_manager.hpp | 9 +++++-- dev/SocketsBase/singleton.hpp | 24 ++++++++++++------- dev/SocketsBase/stk_host.cpp | 2 +- 10 files changed, 60 insertions(+), 55 deletions(-) diff --git a/dev/SocketsBase/client_network_manager.hpp b/dev/SocketsBase/client_network_manager.hpp index c9688a842..668dd9718 100644 --- a/dev/SocketsBase/client_network_manager.hpp +++ b/dev/SocketsBase/client_network_manager.hpp @@ -6,9 +6,12 @@ class ClientNetworkManager : public NetworkManager { + friend class Singleton; public: - ClientNetworkManager(); - virtual ~ClientNetworkManager(); + static ClientNetworkManager* getInstance() + { + return Singleton::getInstance(); + } virtual void run(); @@ -18,7 +21,8 @@ class ClientNetworkManager : public NetworkManager virtual void sendPacket(char* data); protected: - + ClientNetworkManager(); + virtual ~ClientNetworkManager(); }; #endif // CLIENT_NETWORK_MANAGER_HPP diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index 3e25ccbb4..9f168a15b 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -17,6 +17,7 @@ using namespace std; int main() { + HTTP::init(); std::string answer; @@ -25,6 +26,8 @@ int main() cin >> answer; if (answer == "client") { + ClientNetworkManager::getInstance(); + NetworkManager::getInstance()->packetReceived("test"); /// NICKNAME : std::string nickname; cout << "Nickname="; diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp index 7feb176fc..dff01b835 100644 --- a/dev/SocketsBase/network_manager.cpp +++ b/dev/SocketsBase/network_manager.cpp @@ -2,17 +2,8 @@ #include -NetworkManager* NetworkManager::instance = NULL; - NetworkManager::NetworkManager() { - if (NetworkManager::instance) - { - printf("Warning : a Newtork Manager is being deleted.\n"); - delete NetworkManager::instance; - } - NetworkManager::instance = this; - printf("New Network Manager created.\n"); } NetworkManager::~NetworkManager() @@ -26,17 +17,12 @@ void NetworkManager::run() void NetworkManager::setManualSocketsMode(bool manual) { if (manual) - instance->getHost()->stopListening(); + m_localhost->stopListening(); else - instance->getHost()->startListening(); -} - -void NetworkManager::receptionCallback(char* data) -{ - instance->packetReceived(data); + m_localhost->startListening(); } STKHost* NetworkManager::getHost() { - return instance->m_localhost; + return m_localhost; } diff --git a/dev/SocketsBase/network_manager.hpp b/dev/SocketsBase/network_manager.hpp index 959e049a6..e8574b51b 100644 --- a/dev/SocketsBase/network_manager.hpp +++ b/dev/SocketsBase/network_manager.hpp @@ -6,28 +6,24 @@ #include #include "protocol_manager.hpp" -#include "callback_object.hpp" +#include "singleton.hpp" -class NetworkManager : public CallbackObject +class NetworkManager : public Singleton { + friend class Singleton; public: + virtual void run() = 0; + + virtual void setManualSocketsMode(bool manual); + virtual void packetReceived(char* data) = 0; + + STKHost* getHost(); + protected: NetworkManager(); virtual ~NetworkManager(); - virtual void run() = 0; - - static void setManualSocketsMode(bool manual); - static void sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort); - - static void receptionCallback(char* data); - virtual void packetReceived(char* data) = 0; - - static STKHost* getHost(); - protected: std::vector m_peers; STKHost* m_localhost; - - static NetworkManager* instance; }; #endif // NETWORKMANAGER_HPP diff --git a/dev/SocketsBase/protocol_manager.hpp b/dev/SocketsBase/protocol_manager.hpp index df50c2d9e..fc9ee7272 100644 --- a/dev/SocketsBase/protocol_manager.hpp +++ b/dev/SocketsBase/protocol_manager.hpp @@ -24,8 +24,10 @@ class ProtocolManager : public Singleton uint32_t id; } ProtocolInfo; public: - ProtocolManager(); - virtual ~ProtocolManager(); + static ProtocolManager* getInstance() + { + return Singleton::getInstance(); + } virtual void messageReceived(uint8_t* data); @@ -41,8 +43,12 @@ class ProtocolManager : public Singleton virtual PROTOCOL_STATE getProtocolState(uint32_t id); protected: + // protected functions + ProtocolManager(); + virtual ~ProtocolManager(); void assignProtocolId(ProtocolInfo& protocolInfo); - + + // protected members std::vector m_protocols; std::vector m_messagesToProcess; }; diff --git a/dev/SocketsBase/protocols/connect_to_server.cpp b/dev/SocketsBase/protocols/connect_to_server.cpp index b09d5a732..64d8e03bd 100644 --- a/dev/SocketsBase/protocols/connect_to_server.cpp +++ b/dev/SocketsBase/protocols/connect_to_server.cpp @@ -98,8 +98,6 @@ void ConnectToServer::update() else if (m_state == PEER_ADDRESS_RETREIVED) { // we know the distant address:port, just need to connect. - ClientNetworkManager* networkManager = static_cast(m_callbackObject); - networkManager->connect(m_serverIp, m_serverPort); m_state = CONNECTED; connected = true; } diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index 4711311d4..b1f51a7c2 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -1,6 +1,7 @@ #include "get_public_address.hpp" #include "../network_manager.hpp" +#include "../client_network_manager.hpp" #include "connect_to_server.hpp" #include "../network_interface.hpp" @@ -82,14 +83,14 @@ void GetPublicAddress::update() printf("__GetPublicAddress> Querrying STUN server 132.177.123.6\n"); unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; - NetworkManager::setManualSocketsMode(true); - NetworkManager::getHost()->sendRawPacket(bytes, 20, dst, 3478); + ClientNetworkManager::getInstance()->setManualSocketsMode(true); + ClientNetworkManager::getInstance()->getHost()->sendRawPacket(bytes, 20, dst, 3478); m_state = TEST_SENT; } if (m_state == TEST_SENT) { unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; - uint8_t* data = NetworkManager::getHost()->receiveRawPacket(dst, 3478); + uint8_t* data = ClientNetworkManager::getInstance()->getHost()->receiveRawPacket(dst, 3478); assert(data); // check that the stun response is a response, contains the magic cookie and the transaction ID @@ -167,7 +168,7 @@ void GetPublicAddress::update() { printf("__The public address has been found : %i.%i.%i.%i:%i\n", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); m_state = ADDRESS_KNOWN; - NetworkManager::setManualSocketsMode(false); + ClientNetworkManager::getInstance()->setManualSocketsMode(false); TransportAddress* addr = static_cast(m_callbackObject); addr->ip = address; addr->port = port; diff --git a/dev/SocketsBase/server_network_manager.hpp b/dev/SocketsBase/server_network_manager.hpp index f4b33c533..96e5ed4b9 100644 --- a/dev/SocketsBase/server_network_manager.hpp +++ b/dev/SocketsBase/server_network_manager.hpp @@ -6,9 +6,12 @@ class ServerNetworkManager : public NetworkManager { + friend class Singleton; public: - ServerNetworkManager(); - virtual ~ServerNetworkManager(); + static ServerNetworkManager* getInstance() + { + return Singleton::getInstance(); + } virtual void run(); @@ -17,6 +20,8 @@ class ServerNetworkManager : public NetworkManager virtual void packetReceived(char* data); virtual void sendPacket(char* data); protected: + ServerNetworkManager(); + virtual ~ServerNetworkManager(); }; diff --git a/dev/SocketsBase/singleton.hpp b/dev/SocketsBase/singleton.hpp index 9b69fd3df..c6fc7e530 100644 --- a/dev/SocketsBase/singleton.hpp +++ b/dev/SocketsBase/singleton.hpp @@ -8,20 +8,26 @@ class Singleton { protected: Singleton () { } - ~Singleton () { std::cout << "destroying singleton." << std::endl; } + virtual ~Singleton () { std::cout << "destroying singleton." << std::endl; delete m_singleton; } public: - static T *getInstance () + template + static S *getInstance () { - if (NULL == m_singleton) + if (m_singleton == NULL) + m_singleton = new S; + + S* result = (dynamic_cast (m_singleton)); + if (result == NULL) { - std::cout << "creating singleton." << std::endl; - m_singleton = new T; + std::cout << "BE CAREFUL : Reallocating singleton" << std::endl; + m_singleton = new S; } - else - { - } - return (static_cast (m_singleton)); + return (dynamic_cast (m_singleton)); + } + static T *getInstance() + { + return (dynamic_cast (m_singleton)); } static void kill () diff --git a/dev/SocketsBase/stk_host.cpp b/dev/SocketsBase/stk_host.cpp index d601df55b..7a735a6bd 100644 --- a/dev/SocketsBase/stk_host.cpp +++ b/dev/SocketsBase/stk_host.cpp @@ -17,7 +17,7 @@ void* STKHost::receive_data(void* self) printf("message received\n"); switch (event.type) { case ENET_EVENT_TYPE_RECEIVE: - NetworkManager::receptionCallback((char*) event.packet->data); + //NetworkManager::receptionCallback((char*) event.packet->data); break; case ENET_EVENT_TYPE_DISCONNECT: printf("Somebody is now disconnected.\n"); From 1b12026b194eaf3e6ae456d66c3ea859321b545e Mon Sep 17 00:00:00 2001 From: hilnius Date: Wed, 19 Jun 2013 16:07:51 +0000 Subject: [PATCH 13/22] Working mutliple-user connection trough enet, using STUN protocol. Needs to be tested on WAN to be sure it works. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12888 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/callback_object.cpp | 9 -- dev/SocketsBase/callback_object.hpp | 14 --- dev/SocketsBase/client_network_manager.cpp | 81 ++++++++++++- dev/SocketsBase/client_network_manager.hpp | 5 +- dev/SocketsBase/main.cpp | 38 +++---- dev/SocketsBase/network_interface.cpp | 10 ++ dev/SocketsBase/network_interface.hpp | 27 +++++ dev/SocketsBase/network_manager.cpp | 38 +++++++ dev/SocketsBase/network_manager.hpp | 16 ++- dev/SocketsBase/protocol.cpp | 3 +- dev/SocketsBase/protocol.hpp | 8 +- dev/SocketsBase/protocol_manager.cpp | 6 + dev/SocketsBase/protocol_manager.hpp | 5 +- .../protocols/connect_to_server.cpp | 106 ++++-------------- .../protocols/connect_to_server.hpp | 19 +--- .../protocols/get_peer_address.cpp | 80 +++++++++++++ .../protocols/get_peer_address.hpp | 29 +++++ .../protocols/get_public_address.cpp | 1 - dev/SocketsBase/stk_host.cpp | 2 +- dev/SocketsBase/stk_peer.cpp | 18 +-- dev/SocketsBase/stk_peer.hpp | 4 +- dev/SocketsBase/time.cpp | 14 +++ dev/SocketsBase/time.hpp | 14 +-- dev/SocketsBase/types.hpp | 32 ++++++ 24 files changed, 400 insertions(+), 179 deletions(-) delete mode 100644 dev/SocketsBase/callback_object.cpp delete mode 100644 dev/SocketsBase/callback_object.hpp create mode 100644 dev/SocketsBase/network_interface.cpp create mode 100644 dev/SocketsBase/network_interface.hpp create mode 100644 dev/SocketsBase/protocols/get_peer_address.cpp create mode 100644 dev/SocketsBase/protocols/get_peer_address.hpp create mode 100644 dev/SocketsBase/time.cpp create mode 100644 dev/SocketsBase/types.hpp diff --git a/dev/SocketsBase/callback_object.cpp b/dev/SocketsBase/callback_object.cpp deleted file mode 100644 index cbba8acfb..000000000 --- a/dev/SocketsBase/callback_object.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "callback_object.hpp" - -CallbackObject::CallbackObject() -{ -} - -CallbackObject::~CallbackObject() -{ -} diff --git a/dev/SocketsBase/callback_object.hpp b/dev/SocketsBase/callback_object.hpp deleted file mode 100644 index 70c4ab486..000000000 --- a/dev/SocketsBase/callback_object.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef CALLBACK_OBJECT_HPP -#define CALLBACK_OBJECT_HPP - - -class CallbackObject -{ - public: - CallbackObject(); - virtual ~CallbackObject(); - - protected: -}; - -#endif // CALLBACK_OBJECT_HPP diff --git a/dev/SocketsBase/client_network_manager.cpp b/dev/SocketsBase/client_network_manager.cpp index 461d1acfb..36f2dc8c4 100644 --- a/dev/SocketsBase/client_network_manager.cpp +++ b/dev/SocketsBase/client_network_manager.cpp @@ -1,5 +1,11 @@ #include "client_network_manager.hpp" +#include "protocols/get_public_address.hpp" +#include "protocols/hide_public_address.hpp" +#include "protocols/show_public_address.hpp" +#include "protocols/get_peer_address.hpp" +#include "protocols/connect_to_server.hpp" + #include ClientNetworkManager::ClientNetworkManager() @@ -20,14 +26,78 @@ void ClientNetworkManager::run() m_localhost = new STKHost(); m_localhost->setupClient(1, 2, 0, 0); m_localhost->startListening(); + + NetworkManager::run(); } -void ClientNetworkManager::connect(uint32_t ip, uint16_t port) +bool ClientNetworkManager::connect(uint32_t ip, uint16_t port) { STKPeer* peer = new STKPeer(); - peer->connectToServer(m_localhost, ip, port, 2, 0); + bool success = peer->connectToServer(m_localhost, ip, port, 2, 0); + if (success) + m_peers.push_back(peer); + return success; +} + +bool ClientNetworkManager::connectToHost(std::string serverNickname) +{ + printf("_NetworkInterface>Starting the connection to host protocol\n"); + // step 1 : retreive public address + int id = ProtocolManager::getInstance()->startProtocol(new GetPublicAddress(&m_publicAddress)); + while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address is known.\n"); - m_peers.push_back(peer); + // step 2 : show the public address for others (here, the server) + ShowPublicAddress* spa = new ShowPublicAddress(NULL); + spa->setPassword(m_playerLogin.password); + spa->setUsername(m_playerLogin.username); + spa->setPublicAddress(m_publicAddress.ip, m_publicAddress.port); + id = ProtocolManager::getInstance()->startProtocol(spa); + while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address is being shown online.\n"); + + // step 3 : get the server's addres. + TransportAddress addr; + GetPeerAddress* gpa = new GetPeerAddress(&addr); + gpa->setPeerName(serverNickname); + id = ProtocolManager::getInstance()->startProtocol(gpa); + while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address of the server is known.\n"); + + // step 4 : connect to the server + ConnectToServer* cts = new ConnectToServer(NULL); + cts->setServerAddress(addr.ip, addr.port); + id = ProtocolManager::getInstance()->startProtocol(cts); + while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + { + } + bool success = false; + if (m_peers[0]->isConnected()) + { + success = true; + printf("_NetworkInterface> CONNECTION SUCCES : YOU ARE NOW CONNECTED TO A SERVER.\n"); + } + else + { + printf("_NetworkInterface> We are NOT connected to the server.\n"); + } + + // step 5 : hide our public address + HidePublicAddress* hpa = new HidePublicAddress(NULL); + hpa->setPassword(m_playerLogin.password); + hpa->setUsername(m_playerLogin.username); + id = ProtocolManager::getInstance()->startProtocol(hpa); + while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address is now hidden online.\n"); + return success; } void ClientNetworkManager::packetReceived(char* data) @@ -41,3 +111,8 @@ void ClientNetworkManager::sendPacket(char* data) printf("Ambiguous send of data\n"); m_peers[0]->sendPacket(data); } + +STKPeer* ClientNetworkManager::getPeer() +{ + return m_peers[0]; +} diff --git a/dev/SocketsBase/client_network_manager.hpp b/dev/SocketsBase/client_network_manager.hpp index 668dd9718..b8d5fbb65 100644 --- a/dev/SocketsBase/client_network_manager.hpp +++ b/dev/SocketsBase/client_network_manager.hpp @@ -15,11 +15,14 @@ class ClientNetworkManager : public NetworkManager virtual void run(); - void connect(uint32_t ip, uint16_t port); + bool connect(uint32_t ip, uint16_t port); + bool connectToHost(std::string serverNickname); virtual void packetReceived(char* data); virtual void sendPacket(char* data); + STKPeer* getPeer(); + protected: ClientNetworkManager(); virtual ~ClientNetworkManager(); diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index 9f168a15b..7bfebb3fc 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -26,50 +26,41 @@ int main() cin >> answer; if (answer == "client") { - ClientNetworkManager::getInstance(); - NetworkManager::getInstance()->packetReceived("test"); - /// NICKNAME : + ClientNetworkManager::getInstance()->run(); + std::string nickname; - cout << "Nickname="; - std::cin >> nickname; - /// PASSWORD : std::string password; - cout << "Password="; - std::cin >> password; - /// HOST NICKNAME : std::string hostNickname; - cout << "Host Nickname="; - std::cin >> hostNickname; - NetworkInterface::getInstance()->initNetwork(false); + //NetworkManager::getInstance()->run(); - NetworkInterface::getInstance()->setLogin(nickname, password); - NetworkInterface::getInstance()->connectToHost(hostNickname); - + NetworkManager::getInstance()->setLogin(nickname, password); + bool connected = false; //clt.connect(0x0100007f, 7000); // addr in little endian, real address is 7f 00 00 01 (127.0.0.1) std::string buffer; while (1) { cin >> buffer; - if (buffer == "cmd=protocolsCount") + if (buffer == "cmd=connect") { - //cout << protocolListener->runningProtocolsCount() << " protocols are running." << endl; + cout << "Host Nickname="; + std::cin >> hostNickname; + connected = ClientNetworkManager::getInstance()->connectToHost(hostNickname); continue; } - if (buffer == "cmd=hideAddress") - { - } if (buffer == "cmd=login") { std::cout << "Username="; std::cin >> nickname; std::cout << "Password="; std::cin >> password; + NetworkManager::getInstance()->setLogin(nickname, password); } if (buffer.size() == 0) { continue; } char buffer2[256]; strcpy(buffer2, buffer.c_str()); - //NetworkInterface::getInstance()->sendPacket(buffer2); + if (connected) + ClientNetworkManager::getInstance()->sendPacket(buffer2); } @@ -77,8 +68,9 @@ int main() } else if (answer == "host") { - NetworkInterface::getInstance()->initNetwork(true); - + //NetworkInterface::getInstance()->initNetwork(true); + ServerNetworkManager::getInstance()->run(); + ServerNetworkManager::getInstance()->start(); //GetPublicAddress while(1){} diff --git a/dev/SocketsBase/network_interface.cpp b/dev/SocketsBase/network_interface.cpp new file mode 100644 index 000000000..7af2d5975 --- /dev/null +++ b/dev/SocketsBase/network_interface.cpp @@ -0,0 +1,10 @@ +#include "network_interface.hpp" + + +NetworkInterface::NetworkInterface() +{ +} + +NetworkInterface::~NetworkInterface() +{ +} diff --git a/dev/SocketsBase/network_interface.hpp b/dev/SocketsBase/network_interface.hpp new file mode 100644 index 000000000..5551caee7 --- /dev/null +++ b/dev/SocketsBase/network_interface.hpp @@ -0,0 +1,27 @@ +#ifndef NETWORK_INTERFACE_H +#define NETWORK_INTERFACE_H + +#include "singleton.hpp" +#include "types.hpp" +#include "network_manager.hpp" + +#include +#include +#include + + +class NetworkInterface : public Singleton +{ + friend class Singleton; + public: + + void initNetwork(bool server); + + protected: + // protected functions + NetworkInterface(); + virtual ~NetworkInterface(); + +}; + +#endif // NETWORK_INTERFACE_H diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp index dff01b835..b41180cfd 100644 --- a/dev/SocketsBase/network_manager.cpp +++ b/dev/SocketsBase/network_manager.cpp @@ -1,9 +1,32 @@ #include "network_manager.hpp" +#include "protocols/hide_public_address.hpp" +#include "protocols/show_public_address.hpp" +#include "protocols/get_public_address.hpp" + +#include "protocol_manager.hpp" +#include "client_network_manager.hpp" +#include "server_network_manager.hpp" + #include +void* protocolManagerUpdate(void* data) +{ + ProtocolManager* manager = static_cast(data); + while(1) + { + manager->update(); + } + return NULL; +} + + NetworkManager::NetworkManager() { + m_publicAddress.ip = 0; + m_publicAddress.port = 0; + m_networkManager = NULL; + m_protocolManagerUpdateThread = NULL; } NetworkManager::~NetworkManager() @@ -12,6 +35,9 @@ NetworkManager::~NetworkManager() void NetworkManager::run() { + ProtocolManager::getInstance(); + m_protocolManagerUpdateThread = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_protocolManagerUpdateThread, NULL, protocolManagerUpdate, ProtocolManager::getInstance()); } void NetworkManager::setManualSocketsMode(bool manual) @@ -22,6 +48,18 @@ void NetworkManager::setManualSocketsMode(bool manual) m_localhost->startListening(); } +void NetworkManager::setLogin(std::string username, std::string password) +{ + m_playerLogin.username = username; + m_playerLogin.password = password; +} + +void NetworkManager::setPublicAddress(uint32_t ip, uint16_t port) +{ + m_publicAddress.ip = ip; + m_publicAddress.port = port; +} + STKHost* NetworkManager::getHost() { return m_localhost; diff --git a/dev/SocketsBase/network_manager.hpp b/dev/SocketsBase/network_manager.hpp index e8574b51b..8eef2675e 100644 --- a/dev/SocketsBase/network_manager.hpp +++ b/dev/SocketsBase/network_manager.hpp @@ -7,23 +7,37 @@ #include "protocol_manager.hpp" #include "singleton.hpp" +#include "types.hpp" class NetworkManager : public Singleton { friend class Singleton; public: - virtual void run() = 0; + virtual void run(); + // network management functions virtual void setManualSocketsMode(bool manual); virtual void packetReceived(char* data) = 0; + + // raw data management + void setLogin(std::string username, std::string password); + void setPublicAddress(uint32_t ip, uint16_t port); + // getters STKHost* getHost(); protected: NetworkManager(); virtual ~NetworkManager(); + // protected members std::vector m_peers; STKHost* m_localhost; + + TransportAddress m_publicAddress; + PlayerLogin m_playerLogin; + + NetworkManager* m_networkManager; + pthread_t* m_protocolManagerUpdateThread; }; #endif // NETWORKMANAGER_HPP diff --git a/dev/SocketsBase/protocol.cpp b/dev/SocketsBase/protocol.cpp index 88d7efe4a..9f2933e87 100644 --- a/dev/SocketsBase/protocol.cpp +++ b/dev/SocketsBase/protocol.cpp @@ -1,8 +1,9 @@ #include "protocol.hpp" -Protocol::Protocol(CallbackObject* callbackObject) +Protocol::Protocol(CallbackObject* callbackObject, PROTOCOL_TYPE type) { m_callbackObject = callbackObject; + m_type = type; } Protocol::~Protocol() diff --git a/dev/SocketsBase/protocol.hpp b/dev/SocketsBase/protocol.hpp index f3a96caa7..e3b4414ef 100644 --- a/dev/SocketsBase/protocol.hpp +++ b/dev/SocketsBase/protocol.hpp @@ -2,19 +2,21 @@ #define PROTOCOL_HPP #include "protocol_manager.hpp" -#include "callback_object.hpp" +#include "types.hpp" #include enum PROTOCOL_TYPE { - GET_PUBLIC_ADDRESS = 0 + PROTOCOL_NOT_CONCERNED = 0, + PROTOCOL_CONNECTION = 1 + }; class Protocol { public: - Protocol(CallbackObject* callbackObject); + Protocol(CallbackObject* callbackObject, PROTOCOL_TYPE type = PROTOCOL_NOT_CONCERNED); virtual ~Protocol(); virtual void messageReceived(uint8_t* data) = 0; diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp index fcdc1b54a..9c8d1a2d7 100644 --- a/dev/SocketsBase/protocol_manager.cpp +++ b/dev/SocketsBase/protocol_manager.cpp @@ -22,6 +22,12 @@ void ProtocolManager::messageReceived(uint8_t* data) m_messagesToProcess.push_back(data); } +void ProtocolManager::sendMessage(std::string message) +{ + std::string newMessage = " " + message; // add one byte + newMessage[0] = (char)(0); +} + int ProtocolManager::startProtocol(Protocol* protocol) { ProtocolInfo protocolInfo; diff --git a/dev/SocketsBase/protocol_manager.hpp b/dev/SocketsBase/protocol_manager.hpp index fc9ee7272..a1021136f 100644 --- a/dev/SocketsBase/protocol_manager.hpp +++ b/dev/SocketsBase/protocol_manager.hpp @@ -24,12 +24,9 @@ class ProtocolManager : public Singleton uint32_t id; } ProtocolInfo; public: - static ProtocolManager* getInstance() - { - return Singleton::getInstance(); - } virtual void messageReceived(uint8_t* data); + virtual void sendMessage(std::string message); virtual int startProtocol(Protocol* protocol); virtual void stopProtocol(Protocol* protocol); diff --git a/dev/SocketsBase/protocols/connect_to_server.cpp b/dev/SocketsBase/protocols/connect_to_server.cpp index 64d8e03bd..3da201ba6 100644 --- a/dev/SocketsBase/protocols/connect_to_server.cpp +++ b/dev/SocketsBase/protocols/connect_to_server.cpp @@ -1,18 +1,15 @@ #include "connect_to_server.hpp" -#include "../http_functions.hpp" -#include "../time.hpp" #include "../client_network_manager.hpp" -#include "show_public_address.hpp" +#include "../time.hpp" #include #include ConnectToServer::ConnectToServer(CallbackObject* callbackObject) : Protocol(callbackObject) { - m_ownPublicIp = 0; - m_ownPublicPort = 0; - connected = false; + m_serverIp = 0; + m_serverPort = 0; } ConnectToServer::~ConnectToServer() @@ -21,109 +18,50 @@ ConnectToServer::~ConnectToServer() void ConnectToServer::messageReceived(uint8_t* data) { - + printf("data received\n"); + printf("%s", data); + m_state = NONE; // we received a message, we are connected } void ConnectToServer::setup() { - if (m_ownPublicIp == 0 || m_ownPublicPort == 0 || m_username == "" || m_password == "" || m_hostName == "") + if (m_serverIp == 0 || m_serverPort == 0 ) { - printf("You have to set the public ip:port, username:password and the host nickname before starting this protocol.\n"); + printf("You have to set the server's public ip:port of the server.\n"); m_listener->protocolTerminated(this); } + m_state = NONE; } void ConnectToServer::update() { - if (m_state == NOTHING) - { - char url[512]; - sprintf(url, "http://stkconnect.freeserver.me/log.php?set&nick=%s&ip=%u&port=%u&pwd=%s", m_username.c_str(), m_ownPublicIp, m_ownPublicPort, m_password.c_str()); - std::string result = HTTP::getPage(url); - if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') - { - printf("Address set.\n"); - m_state = ADDRESS_KNOWN_ONLINE; - } - if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') - { - printf("Login fail. Please re-set username:password and unpause the protocol.\n"); - m_state = NOTHING; - pause(); - } - ShowPublicAddress* showAddr = new ShowPublicAddress(NULL); - showAddr->setUsername(m_username); - } - else if (m_state == ADDRESS_KNOWN_ONLINE) + if (m_state == NONE) { static double target = 0; double currentTime = Time::getSeconds(); - if (currentTime < target-1800) // sometimes the getSeconds method forgets 3600 seconds. + while (currentTime < target-1800) // sometimes the getSeconds method forgets 3600 seconds. currentTime += 3600; if (currentTime > target) { - char url[512]; - sprintf(url, "http://stkconnect.freeserver.me/log.php?get&nick=%s", m_hostName.c_str()); - std::string result = HTTP::getPage(url); - if (result == "") - { - printf("The host you try to reach does not exist. Change the host name please.\n"); - m_state = NOTHING; - pause(); + ClientNetworkManager::getInstance()->connect(m_serverIp, m_serverPort); + if (ClientNetworkManager::getInstance()->getPeer()->isConnected()) + { + m_state = DONE; return; } - std::string ipAddr = result; - ipAddr.erase(ipAddr.find_first_of(':')); - std::string portNb = result; - portNb.erase(0, portNb.find_first_of(':')+1); - uint32_t dstIp = (uint32_t)(atoi(ipAddr.c_str())); - uint16_t dstPort = (uint32_t)(atoi(portNb.c_str())); - if (dstIp == 0 || dstPort == 0) - { - printf("The host you try to reach is not online. There will be a new try in 10 seconds.\n"); - target = currentTime+10; - } - else - { - printf("Public ip of target is %i.%i.%i.%i:%i\n", (dstIp>>24)&0xff, (dstIp>>16)&0xff, (dstIp>>8)&0xff, dstIp&0xff, dstPort); - m_serverIp = ((dstIp&0x000000ff)<<24) // change the server IP to have a network-byte order - + ((dstIp&0x0000ff00)<<8) - + ((dstIp&0x00ff0000)>>8) - + ((dstIp&0xff000000)>>24); - m_serverPort = dstPort; - m_state = PEER_ADDRESS_RETREIVED; - } + target = currentTime+5; + printf("Retrying to connect in 5 seconds.\n"); } } - else if (m_state == PEER_ADDRESS_RETREIVED) + else if (m_state == DONE) { - // we know the distant address:port, just need to connect. - m_state = CONNECTED; - connected = true; + m_listener->protocolTerminated(this); } - else if (m_state == CONNECTED) - { - } - } -void ConnectToServer::setSelfAddress(uint32_t ip, uint16_t port) +void ConnectToServer::setServerAddress(uint32_t ip, uint16_t port) { - m_ownPublicIp = ip; - m_ownPublicPort = port; + m_serverIp = ip; + m_serverPort = port; } -void ConnectToServer::setUsername(std::string username) -{ - m_username = username; -} - -void ConnectToServer::setPassword(std::string password) -{ - m_password = password; -} - -void ConnectToServer::setHostName(std::string hostName) -{ - m_hostName = hostName; -} diff --git a/dev/SocketsBase/protocols/connect_to_server.hpp b/dev/SocketsBase/protocols/connect_to_server.hpp index bcdafd833..d5d4c6c97 100644 --- a/dev/SocketsBase/protocols/connect_to_server.hpp +++ b/dev/SocketsBase/protocols/connect_to_server.hpp @@ -14,29 +14,18 @@ class ConnectToServer : public Protocol, public CallbackObject virtual void setup(); virtual void update(); - void setSelfAddress(uint32_t ip, uint16_t port); - void setUsername(std::string username); - void setPassword(std::string password); - void setHostName(std::string hostName); + void setServerAddress(uint32_t ip, uint16_t port); - bool connected; protected: - uint32_t m_ownPublicIp; - uint16_t m_ownPublicPort; uint32_t m_serverIp; uint16_t m_serverPort; - std::string m_hostName; - std::string m_username; - std::string m_password; + enum STATE { - NOTHING, - ADDRESS_KNOWN_ONLINE, - PEER_ADDRESS_RETREIVED, - CONNECTED + NONE, + DONE }; STATE m_state; - double firstTime; }; #endif // CONNECT_TO_SERVER_HPP diff --git a/dev/SocketsBase/protocols/get_peer_address.cpp b/dev/SocketsBase/protocols/get_peer_address.cpp new file mode 100644 index 000000000..36dc8ac75 --- /dev/null +++ b/dev/SocketsBase/protocols/get_peer_address.cpp @@ -0,0 +1,80 @@ +#include "get_peer_address.hpp" + +#include "../time.hpp" +#include "../http_functions.hpp" + +#include +#include + +GetPeerAddress::GetPeerAddress(CallbackObject* callbackObject) : Protocol(callbackObject) +{ +} + +GetPeerAddress::~GetPeerAddress() +{ +} + +void GetPeerAddress::messageReceived(uint8_t* data) +{ +} + +void GetPeerAddress::setup() +{ + m_state = NONE; +} + +void GetPeerAddress::update() +{ + if (m_state == NONE) + { + static double target = 0; + double currentTime = Time::getSeconds(); + while (currentTime < target-1800) // sometimes the getSeconds method forgets 3600 seconds. + currentTime += 3600; + if (currentTime > target) + { + char url[512]; + sprintf(url, "http://stkconnect.freeserver.me/log.php?get&nick=%s", m_peerName.c_str()); + std::string result = HTTP::getPage(url); + if (result == "") + { + printf("__GetPeerAddress> The host you try to reach does not exist. Change the host name please.\n"); + pause(); + return; + } + std::string ipAddr = result; + ipAddr.erase(ipAddr.find_first_of(':')); + std::string portNb = result; + portNb.erase(0, portNb.find_first_of(':')+1); + uint32_t dstIp = (uint32_t)(atoi(ipAddr.c_str())); + uint16_t dstPort = (uint32_t)(atoi(portNb.c_str())); + if (dstIp == 0 || dstPort == 0) + { + printf("__GetPeerAddress> The host you try to reach is not online. There will be a new try in 10 seconds.\n"); + target = currentTime+10; + } + else + { + printf("__GetPeerAddress> Public ip of target is %i.%i.%i.%i:%i\n", (dstIp>>24)&0xff, (dstIp>>16)&0xff, (dstIp>>8)&0xff, dstIp&0xff, dstPort); + uint32_t serverIp = ((dstIp&0x000000ff)<<24) // change the server IP to have a network-byte order + + ((dstIp&0x0000ff00)<<8) + + ((dstIp&0x00ff0000)>>8) + + ((dstIp&0xff000000)>>24); + uint16_t serverPort = dstPort; + TransportAddress* addr = static_cast(m_callbackObject); + addr->ip = serverIp; + addr->port = serverPort; + m_state = DONE; + } + } + } + else if (m_state == DONE) + { + m_listener->protocolTerminated(this); + } +} + +void GetPeerAddress::setPeerName(std::string peerName) +{ + m_peerName = peerName; +} diff --git a/dev/SocketsBase/protocols/get_peer_address.hpp b/dev/SocketsBase/protocols/get_peer_address.hpp new file mode 100644 index 000000000..418a38271 --- /dev/null +++ b/dev/SocketsBase/protocols/get_peer_address.hpp @@ -0,0 +1,29 @@ +#ifndef GET_PEER_ADDRESS_HPP +#define GET_PEER_ADDRESS_HPP + +#include "../protocol.hpp" + +class GetPeerAddress : public Protocol +{ + public: + GetPeerAddress(CallbackObject* callbackObject); + virtual ~GetPeerAddress(); + + virtual void messageReceived(uint8_t* data); + virtual void setup(); + virtual void update(); + + void setPeerName(std::string peerName); + protected: + std::string m_peerName; + + enum STATE + { + NONE, + DONE + }; + STATE m_state; + +}; + +#endif // GET_PEER_ADDRESS_HPP diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index b1f51a7c2..40c6e4db0 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -23,7 +23,6 @@ int stunRand() GetPublicAddress::GetPublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject) { - m_type = GET_PUBLIC_ADDRESS; } GetPublicAddress::~GetPublicAddress() diff --git a/dev/SocketsBase/stk_host.cpp b/dev/SocketsBase/stk_host.cpp index 7a735a6bd..2e3958a53 100644 --- a/dev/SocketsBase/stk_host.cpp +++ b/dev/SocketsBase/stk_host.cpp @@ -17,7 +17,7 @@ void* STKHost::receive_data(void* self) printf("message received\n"); switch (event.type) { case ENET_EVENT_TYPE_RECEIVE: - //NetworkManager::receptionCallback((char*) event.packet->data); + NetworkManager::getInstance()->packetReceived((char*) event.packet->data); break; case ENET_EVENT_TYPE_DISCONNECT: printf("Somebody is now disconnected.\n"); diff --git a/dev/SocketsBase/stk_peer.cpp b/dev/SocketsBase/stk_peer.cpp index e49834ea2..490425536 100644 --- a/dev/SocketsBase/stk_peer.cpp +++ b/dev/SocketsBase/stk_peer.cpp @@ -12,7 +12,7 @@ STKPeer::~STKPeer() { } -void STKPeer::connectToServer(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data) +bool STKPeer::connectToServer(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data) { ENetAddress address; address.host = ip; @@ -21,13 +21,11 @@ void STKPeer::connectToServer(STKHost* host, uint32_t ip, uint16_t port, uint32_ m_peer = enet_host_connect(host->m_host, &address, 2, 0); if (m_peer == NULL) { - printf("Could not connect to server.\n"); - return; - } - else - { - printf("Connected.\n"); + printf("Could not try to connect to server.\n"); + return false; } + printf("Connecting to %i.%i.%i.%i:%i.\n", (m_peer->address.host>>0)&0xff,(m_peer->address.host>>8)&0xff,(m_peer->address.host>>16)&0xff,(m_peer->address.host>>24)&0xff,m_peer->address.port); + return true; } void STKPeer::sendPacket(char* data) @@ -37,3 +35,9 @@ void STKPeer::sendPacket(char* data) ENetPacket* packet = enet_packet_create(data, strlen(data)+1,ENET_PACKET_FLAG_RELIABLE); enet_peer_send(m_peer, 0, packet); } + +bool STKPeer::isConnected() +{ + printf("PEER STATE %i\n", m_peer->state); + return (m_peer->state == ENET_PEER_STATE_CONNECTED); +} diff --git a/dev/SocketsBase/stk_peer.hpp b/dev/SocketsBase/stk_peer.hpp index eba2dea42..f908bac6e 100644 --- a/dev/SocketsBase/stk_peer.hpp +++ b/dev/SocketsBase/stk_peer.hpp @@ -14,7 +14,9 @@ class STKPeer virtual void sendPacket(char* data); - void connectToServer(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data); + bool connectToServer(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data); + + bool isConnected(); protected: ENetPeer* m_peer; }; diff --git a/dev/SocketsBase/time.cpp b/dev/SocketsBase/time.cpp new file mode 100644 index 000000000..f482bbc16 --- /dev/null +++ b/dev/SocketsBase/time.cpp @@ -0,0 +1,14 @@ +#include "time.hpp" + +namespace Time +{ +double getSeconds() +{ + time_t timer; + time(&timer); + struct tm y2k; + y2k.tm_hour = 0; y2k.tm_min = 0; y2k.tm_sec = 0; + y2k.tm_year = 100; y2k.tm_mon = 0; y2k.tm_mday = 1; + return difftime(timer,mktime(&y2k)); // get the seconds elapsed since january 2000 +} +} diff --git a/dev/SocketsBase/time.hpp b/dev/SocketsBase/time.hpp index e2e137974..7d5ac32d8 100644 --- a/dev/SocketsBase/time.hpp +++ b/dev/SocketsBase/time.hpp @@ -1,19 +1,11 @@ -#ifndef TIME_HPP_INCLUDED -#define TIME_HPP_INCLUDED +#ifndef TIME_HPP +#define TIME_HPP #include namespace Time { -double getSeconds() -{ - time_t timer; - time(&timer); - struct tm y2k; - y2k.tm_hour = 0; y2k.tm_min = 0; y2k.tm_sec = 0; - y2k.tm_year = 100; y2k.tm_mon = 0; y2k.tm_mday = 1; - return difftime(timer,mktime(&y2k)); // get the seconds elapsed since january 2000 -} +double getSeconds(); } #endif // TIME_HPP_INCLUDED diff --git a/dev/SocketsBase/types.hpp b/dev/SocketsBase/types.hpp new file mode 100644 index 000000000..307db1f93 --- /dev/null +++ b/dev/SocketsBase/types.hpp @@ -0,0 +1,32 @@ +#ifndef TYPES_HPP +#define TYPES_HPP + +#include +#include + +class CallbackObject +{ + public: + CallbackObject() {} + +}; + +class TransportAddress : public CallbackObject +{ + public: + TransportAddress() {} + + uint32_t ip; + uint16_t port; +}; + +class PlayerLogin : public CallbackObject +{ + public: + PlayerLogin() {} + + std::string username; + std::string password; +}; + +#endif // TYPES_HPP From 97394b5ac997b03f62e6e344518c1c13376ce4b6 Mon Sep 17 00:00:00 2001 From: hilnius Date: Thu, 20 Jun 2013 13:04:32 +0000 Subject: [PATCH 14/22] a client can connect trough a host behind a NAT (tested git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12902 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/SocketsBase.cbp | 8 ++- dev/SocketsBase/client_network_manager.cpp | 9 --- dev/SocketsBase/client_network_manager.hpp | 1 - dev/SocketsBase/main.cpp | 41 +++++++++++- dev/SocketsBase/network_manager.cpp | 32 +++++++++ dev/SocketsBase/network_manager.hpp | 3 + .../protocols/connect_to_server.cpp | 4 +- .../protocols/get_public_address.cpp | 8 +-- dev/SocketsBase/server_network_manager.cpp | 65 ++++++++++++++++++- dev/SocketsBase/server_network_manager.hpp | 1 + dev/SocketsBase/stk_host.cpp | 32 +++++++-- dev/SocketsBase/stk_host.hpp | 2 + dev/SocketsBase/stk_peer.cpp | 12 +++- dev/SocketsBase/stk_peer.hpp | 5 +- 14 files changed, 195 insertions(+), 28 deletions(-) diff --git a/dev/SocketsBase/SocketsBase.cbp b/dev/SocketsBase/SocketsBase.cbp index 89b14b124..78874c4d2 100644 --- a/dev/SocketsBase/SocketsBase.cbp +++ b/dev/SocketsBase/SocketsBase.cbp @@ -38,13 +38,13 @@ - - + + @@ -53,6 +53,8 @@ + + @@ -66,7 +68,9 @@ + + diff --git a/dev/SocketsBase/client_network_manager.cpp b/dev/SocketsBase/client_network_manager.cpp index 36f2dc8c4..c4d8c93a2 100644 --- a/dev/SocketsBase/client_network_manager.cpp +++ b/dev/SocketsBase/client_network_manager.cpp @@ -30,15 +30,6 @@ void ClientNetworkManager::run() NetworkManager::run(); } -bool ClientNetworkManager::connect(uint32_t ip, uint16_t port) -{ - STKPeer* peer = new STKPeer(); - bool success = peer->connectToServer(m_localhost, ip, port, 2, 0); - if (success) - m_peers.push_back(peer); - return success; -} - bool ClientNetworkManager::connectToHost(std::string serverNickname) { printf("_NetworkInterface>Starting the connection to host protocol\n"); diff --git a/dev/SocketsBase/client_network_manager.hpp b/dev/SocketsBase/client_network_manager.hpp index b8d5fbb65..c9160a6d8 100644 --- a/dev/SocketsBase/client_network_manager.hpp +++ b/dev/SocketsBase/client_network_manager.hpp @@ -15,7 +15,6 @@ class ClientNetworkManager : public NetworkManager virtual void run(); - bool connect(uint32_t ip, uint16_t port); bool connectToHost(std::string serverNickname); virtual void packetReceived(char* data); diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index 7bfebb3fc..635146826 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -40,7 +40,7 @@ int main() std::string buffer; while (1) { - cin >> buffer; + getline(std::cin, buffer); if (buffer == "cmd=connect") { cout << "Host Nickname="; @@ -68,12 +68,49 @@ int main() } else if (answer == "host") { + std::string nickname; + std::string password; + std::cout << "Username="; + std::cin >> nickname; + std::cout << "Password="; + std::cin >> password; //NetworkInterface::getInstance()->initNetwork(true); + ServerNetworkManager::getInstance()->setLogin(nickname, password); ServerNetworkManager::getInstance()->run(); ServerNetworkManager::getInstance()->start(); //GetPublicAddress - while(1){} + std::string buffer; + while (1) + { + getline(std::cin, buffer); + if (buffer == "cmd=connect") + { + std::string peer; + std::cout << "Peer nickname="; + std::cin >> peer; + ServerNetworkManager::getInstance()->connectToPeer(peer); + continue; + } + if (buffer == "cmd=talkto") + { + uint32_t peer; + std::cout << "Peer ip="; + std::cin >> peer; + uint32_t port; + std::cout << "Peer port="; + std::cin >> port; + ServerNetworkManager::getInstance()->setManualSocketsMode(true); + char msg[] = "test"; + ServerNetworkManager::getInstance()->getHost()->sendRawPacket((uint8_t*)(msg), sizeof(msg), peer, port); + ServerNetworkManager::getInstance()->setManualSocketsMode(false); + continue; + } + if (buffer.size() == 0) { continue; } + char buffer2[256]; + strcpy(buffer2, buffer.c_str()); + + } } return 0; } diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp index b41180cfd..7cc3c7e2e 100644 --- a/dev/SocketsBase/network_manager.cpp +++ b/dev/SocketsBase/network_manager.cpp @@ -40,6 +40,18 @@ void NetworkManager::run() pthread_create(m_protocolManagerUpdateThread, NULL, protocolManagerUpdate, ProtocolManager::getInstance()); } +bool NetworkManager::connect(uint32_t ip, uint16_t port) +{ + if (peerExists(ip, port)) + return isConnectedTo(ip, port); + + STKPeer* peer = new STKPeer(); + bool success = peer->connectToHost(m_localhost, ip, port, 2, 0); + if (success) + m_peers.push_back(peer); + return success; +} + void NetworkManager::setManualSocketsMode(bool manual) { if (manual) @@ -60,6 +72,26 @@ void NetworkManager::setPublicAddress(uint32_t ip, uint16_t port) m_publicAddress.port = port; } +bool NetworkManager::peerExists(uint32_t ip, uint16_t port) +{ + for (unsigned int i = 0; i < m_peers.size(); i++) + if (m_peers[i]->getAddress() == ip && m_peers[i]->getPort() == port) + return true; + return m_localhost->peerExists(ip, port); +} + +bool NetworkManager::isConnectedTo(uint32_t ip, uint16_t port) +{ + for (unsigned int i = 0; i < m_peers.size(); i++) + { + if (m_peers[i]->getAddress() == ip && m_peers[i]->getPort() == port && m_peers[i]->isConnected()) + { + return true; + } + } + return m_localhost->isConnectedTo(ip, port); +} + STKHost* NetworkManager::getHost() { return m_localhost; diff --git a/dev/SocketsBase/network_manager.hpp b/dev/SocketsBase/network_manager.hpp index 8eef2675e..15faaefd7 100644 --- a/dev/SocketsBase/network_manager.hpp +++ b/dev/SocketsBase/network_manager.hpp @@ -16,6 +16,7 @@ class NetworkManager : public Singleton virtual void run(); // network management functions + virtual bool connect(uint32_t ip, uint16_t port); virtual void setManualSocketsMode(bool manual); virtual void packetReceived(char* data) = 0; @@ -24,6 +25,8 @@ class NetworkManager : public Singleton void setPublicAddress(uint32_t ip, uint16_t port); // getters + virtual bool peerExists(uint32_t ip, uint16_t port); + virtual bool isConnectedTo(uint32_t ip, uint16_t port); STKHost* getHost(); protected: NetworkManager(); diff --git a/dev/SocketsBase/protocols/connect_to_server.cpp b/dev/SocketsBase/protocols/connect_to_server.cpp index 3da201ba6..10c1d51e9 100644 --- a/dev/SocketsBase/protocols/connect_to_server.cpp +++ b/dev/SocketsBase/protocols/connect_to_server.cpp @@ -43,8 +43,8 @@ void ConnectToServer::update() currentTime += 3600; if (currentTime > target) { - ClientNetworkManager::getInstance()->connect(m_serverIp, m_serverPort); - if (ClientNetworkManager::getInstance()->getPeer()->isConnected()) + NetworkManager::getInstance()->connect(m_serverIp, m_serverPort); + if (NetworkManager::getInstance()->isConnectedTo(m_serverIp, m_serverPort)) { m_state = DONE; return; diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index 40c6e4db0..3777b1102 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -82,14 +82,14 @@ void GetPublicAddress::update() printf("__GetPublicAddress> Querrying STUN server 132.177.123.6\n"); unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; - ClientNetworkManager::getInstance()->setManualSocketsMode(true); - ClientNetworkManager::getInstance()->getHost()->sendRawPacket(bytes, 20, dst, 3478); + NetworkManager::getInstance()->setManualSocketsMode(true); + NetworkManager::getInstance()->getHost()->sendRawPacket(bytes, 20, dst, 3478); m_state = TEST_SENT; } if (m_state == TEST_SENT) { unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; - uint8_t* data = ClientNetworkManager::getInstance()->getHost()->receiveRawPacket(dst, 3478); + uint8_t* data = NetworkManager::getInstance()->getHost()->receiveRawPacket(dst, 3478); assert(data); // check that the stun response is a response, contains the magic cookie and the transaction ID @@ -167,7 +167,7 @@ void GetPublicAddress::update() { printf("__The public address has been found : %i.%i.%i.%i:%i\n", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); m_state = ADDRESS_KNOWN; - ClientNetworkManager::getInstance()->setManualSocketsMode(false); + NetworkManager::getInstance()->setManualSocketsMode(false); TransportAddress* addr = static_cast(m_callbackObject); addr->ip = address; addr->port = port; diff --git a/dev/SocketsBase/server_network_manager.cpp b/dev/SocketsBase/server_network_manager.cpp index 1c4164d59..1dffa739d 100644 --- a/dev/SocketsBase/server_network_manager.cpp +++ b/dev/SocketsBase/server_network_manager.cpp @@ -1,5 +1,11 @@ #include "server_network_manager.hpp" +#include "protocols/get_public_address.hpp" +#include "protocols/hide_public_address.hpp" +#include "protocols/show_public_address.hpp" +#include "protocols/get_peer_address.hpp" +#include "protocols/connect_to_server.hpp" + #include #include #include @@ -21,14 +27,69 @@ void ServerNetworkManager::run() printf("Could not initialize enet.\n"); return; } + NetworkManager::run(); } void ServerNetworkManager::start() { m_localhost = new STKHost(); - m_localhost->setupServer(STKHost::HOST_ANY, 7000, 32, 2, 0, 0); + m_localhost->setupServer(STKHost::HOST_ANY, 7321, 32, 2, 0, 0); m_localhost->startListening(); - printf("Server now setup, listening on port 7000.\n"); + printf("Server now setup, listening on port 7321.\n"); + + printf("_NetworkInterface>Starting the global protocol\n"); + // step 1 : retreive public address + int id = ProtocolManager::getInstance()->startProtocol(new GetPublicAddress(&m_publicAddress)); + while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address is known.\n"); + + // step 2 : show the public address for others (here, the server) + ShowPublicAddress* spa = new ShowPublicAddress(NULL); + spa->setPassword(m_playerLogin.password); + spa->setUsername(m_playerLogin.username); + spa->setPublicAddress(m_publicAddress.ip, m_publicAddress.port); + id = ProtocolManager::getInstance()->startProtocol(spa); + while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address is being shown online.\n"); +} + +bool ServerNetworkManager::connectToPeer(std::string peerUsername) +{ + printf("_NetworkInterface>Starting the connection to host protocol\n"); + + // step 3 : get the peer's addres. + TransportAddress addr; + GetPeerAddress* gpa = new GetPeerAddress(&addr); + gpa->setPeerName(peerUsername); + uint32_t id = ProtocolManager::getInstance()->startProtocol(gpa); + while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address of the peer is known.\n"); + + // step 2 : connect to the peer + ConnectToServer* cts = new ConnectToServer(NULL); + cts->setServerAddress(addr.ip, addr.port); + id = ProtocolManager::getInstance()->startProtocol(cts); + while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + { + } + bool success = false; + if (isConnectedTo(addr.ip, addr.port)) + { + success = true; + printf("_NetworkInterface> CONNECTION SUCCES : YOU ARE NOW CONNECTED TO A PEER.\n"); + } + else + { + printf("_NetworkInterface> We are NOT connected to the server.\n"); + } + + return success; } void ServerNetworkManager::packetReceived(char* data) diff --git a/dev/SocketsBase/server_network_manager.hpp b/dev/SocketsBase/server_network_manager.hpp index 96e5ed4b9..cd0d88be0 100644 --- a/dev/SocketsBase/server_network_manager.hpp +++ b/dev/SocketsBase/server_network_manager.hpp @@ -16,6 +16,7 @@ class ServerNetworkManager : public NetworkManager virtual void run(); void start(); + bool connectToPeer(std::string peerUsername); virtual void packetReceived(char* data); virtual void sendPacket(char* data); diff --git a/dev/SocketsBase/stk_host.cpp b/dev/SocketsBase/stk_host.cpp index 2e3958a53..ccddd13ef 100644 --- a/dev/SocketsBase/stk_host.cpp +++ b/dev/SocketsBase/stk_host.cpp @@ -17,6 +17,7 @@ void* STKHost::receive_data(void* self) printf("message received\n"); switch (event.type) { case ENET_EVENT_TYPE_RECEIVE: + printf("Sender : %ld", event.peer->address.host); NetworkManager::getInstance()->packetReceived((char*) event.packet->data); break; case ENET_EVENT_TYPE_DISCONNECT: @@ -46,11 +47,11 @@ STKHost::~STKHost() void STKHost::setupServer(uint32_t address, uint16_t port, int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth) { - ENetAddress addr; - addr.host = address; - addr.port = port; + ENetAddress* addr = (ENetAddress*)(malloc(sizeof(ENetAddress))); + addr->host = address; + addr->port = port; - m_host = enet_host_create(&addr, peerCount, channelLimit, maxIncomingBandwidth, maxOutgoingBandwidth); + m_host = enet_host_create(addr, peerCount, channelLimit, maxIncomingBandwidth, maxOutgoingBandwidth); if (m_host == NULL) { fprintf (stderr, "An error occurred while trying to create an ENet server host.\n"); @@ -145,3 +146,26 @@ void STKHost::broadcastPacket(char* data) ENetPacket* packet = enet_packet_create(data, strlen(data)+1,ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(m_host, 0, packet); } + +bool STKHost::peerExists(uint32_t ip, uint16_t port) +{ + for (unsigned int i = 0; i < m_host->peerCount; i++) + { + if (m_host->peers[i].address.host == ip && m_host->peers[i].address.port == port) + { + return true; + } + } + return false; +} +bool STKHost::isConnectedTo(uint32_t ip, uint16_t port) +{ + for (unsigned int i = 0; i < m_host->peerCount; i++) + { + if (m_host->peers[i].address.host == ip && m_host->peers[i].address.port == port && m_host->peers[i].state == ENET_PEER_STATE_CONNECTED) + { + return true; + } + } + return false; +} diff --git a/dev/SocketsBase/stk_host.hpp b/dev/SocketsBase/stk_host.hpp index 9483836c5..14378f4bf 100644 --- a/dev/SocketsBase/stk_host.hpp +++ b/dev/SocketsBase/stk_host.hpp @@ -31,6 +31,8 @@ class STKHost uint8_t* receiveRawPacket(unsigned int dstIp, unsigned short dstPort); void broadcastPacket(char* data); + bool peerExists(uint32_t ip, uint16_t port); + bool isConnectedTo(uint32_t ip, uint16_t port); protected: ENetHost* m_host; pthread_t* m_listeningThread; diff --git a/dev/SocketsBase/stk_peer.cpp b/dev/SocketsBase/stk_peer.cpp index 490425536..98ee53ca0 100644 --- a/dev/SocketsBase/stk_peer.cpp +++ b/dev/SocketsBase/stk_peer.cpp @@ -12,7 +12,7 @@ STKPeer::~STKPeer() { } -bool STKPeer::connectToServer(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data) +bool STKPeer::connectToHost(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data) { ENetAddress address; address.host = ip; @@ -36,6 +36,16 @@ void STKPeer::sendPacket(char* data) enet_peer_send(m_peer, 0, packet); } +uint32_t STKPeer::getAddress() +{ + return m_peer->address.host; +} + +uint16_t STKPeer::getPort() +{ + return m_peer->address.port; +} + bool STKPeer::isConnected() { printf("PEER STATE %i\n", m_peer->state); diff --git a/dev/SocketsBase/stk_peer.hpp b/dev/SocketsBase/stk_peer.hpp index f908bac6e..f094457ad 100644 --- a/dev/SocketsBase/stk_peer.hpp +++ b/dev/SocketsBase/stk_peer.hpp @@ -14,9 +14,12 @@ class STKPeer virtual void sendPacket(char* data); - bool connectToServer(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data); + bool connectToHost(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data); bool isConnected(); + + uint32_t getAddress(); + uint16_t getPort(); protected: ENetPeer* m_peer; }; From bd1afde0297ab4983374a7f1ae813e5175583368 Mon Sep 17 00:00:00 2001 From: hilnius Date: Fri, 21 Jun 2013 00:26:19 +0000 Subject: [PATCH 15/22] added thread-safety, starting to implement a clean way to transmit packets from lower to upper layers git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12909 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/client_network_manager.cpp | 2 +- dev/SocketsBase/main.cpp | 4 +++ dev/SocketsBase/network_manager.cpp | 35 +++++++++++++++------ dev/SocketsBase/network_manager.hpp | 3 ++ dev/SocketsBase/protocol_manager.cpp | 36 ++++++++++++++++++---- dev/SocketsBase/protocol_manager.hpp | 12 ++++++-- dev/SocketsBase/stk_host.cpp | 18 ++--------- dev/SocketsBase/stk_peer.cpp | 4 +++ dev/SocketsBase/stk_peer.hpp | 2 ++ dev/SocketsBase/types.hpp | 1 + 10 files changed, 81 insertions(+), 36 deletions(-) diff --git a/dev/SocketsBase/client_network_manager.cpp b/dev/SocketsBase/client_network_manager.cpp index c4d8c93a2..b8b6be1e9 100644 --- a/dev/SocketsBase/client_network_manager.cpp +++ b/dev/SocketsBase/client_network_manager.cpp @@ -69,7 +69,7 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) { } bool success = false; - if (m_peers[0]->isConnected()) + if (m_localhost->isConnectedTo(addr.ip, addr.port)) { success = true; printf("_NetworkInterface> CONNECTION SUCCES : YOU ARE NOW CONNECTED TO A SERVER.\n"); diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index 635146826..4e93a0a21 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -31,6 +31,10 @@ int main() std::string nickname; std::string password; std::string hostNickname; + std::cout << "Username="; + std::cin >> nickname; + std::cout << "Password="; + std::cin >> password; //NetworkManager::getInstance()->run(); diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp index 7cc3c7e2e..7d22428e4 100644 --- a/dev/SocketsBase/network_manager.cpp +++ b/dev/SocketsBase/network_manager.cpp @@ -60,6 +60,26 @@ void NetworkManager::setManualSocketsMode(bool manual) m_localhost->startListening(); } +void NetworkManager::notifyEvent(Event* event) +{ + printf("EVENT received\n"); + switch (event->type) + { + case EVENT_TYPE_MESSAGE: + printf("Message, Sender : %ld\n", event->peer->getAddress()); + break; + case EVENT_TYPE_DISCONNECTED: + printf("Somebody is now disconnected.\n"); + printf("Disconnected host: %i.%i.%i.%i:%i\n", event->peer->getAddress()>>24&0xff, event->peer->getAddress()>>16&0xff, event->peer->getAddress()>>8&0xff, event->peer->getAddress()&0xff,event->peer->getPort()); + break; + case EVENT_TYPE_CONNECTED: + printf("A client has just connected.\n"); + break; + } + ProtocolManager::getInstance()->notifyEvent(event); + delete event; // event won't be use again +} + void NetworkManager::setLogin(std::string username, std::string password) { m_playerLogin.username = username; @@ -74,21 +94,11 @@ void NetworkManager::setPublicAddress(uint32_t ip, uint16_t port) bool NetworkManager::peerExists(uint32_t ip, uint16_t port) { - for (unsigned int i = 0; i < m_peers.size(); i++) - if (m_peers[i]->getAddress() == ip && m_peers[i]->getPort() == port) - return true; return m_localhost->peerExists(ip, port); } bool NetworkManager::isConnectedTo(uint32_t ip, uint16_t port) { - for (unsigned int i = 0; i < m_peers.size(); i++) - { - if (m_peers[i]->getAddress() == ip && m_peers[i]->getPort() == port && m_peers[i]->isConnected()) - { - return true; - } - } return m_localhost->isConnectedTo(ip, port); } @@ -96,3 +106,8 @@ STKHost* NetworkManager::getHost() { return m_localhost; } + +std::vector NetworkManager::getPeers() +{ + return m_peers; +} diff --git a/dev/SocketsBase/network_manager.hpp b/dev/SocketsBase/network_manager.hpp index 15faaefd7..426b7ddc2 100644 --- a/dev/SocketsBase/network_manager.hpp +++ b/dev/SocketsBase/network_manager.hpp @@ -8,6 +8,7 @@ #include "protocol_manager.hpp" #include "singleton.hpp" #include "types.hpp" +#include "event.hpp" class NetworkManager : public Singleton { @@ -18,6 +19,7 @@ class NetworkManager : public Singleton // network management functions virtual bool connect(uint32_t ip, uint16_t port); virtual void setManualSocketsMode(bool manual); + virtual void notifyEvent(Event* event); virtual void packetReceived(char* data) = 0; // raw data management @@ -28,6 +30,7 @@ class NetworkManager : public Singleton virtual bool peerExists(uint32_t ip, uint16_t port); virtual bool isConnectedTo(uint32_t ip, uint16_t port); STKHost* getHost(); + std::vector getPeers(); protected: NetworkManager(); virtual ~NetworkManager(); diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp index 9c8d1a2d7..45dea4b6f 100644 --- a/dev/SocketsBase/protocol_manager.cpp +++ b/dev/SocketsBase/protocol_manager.cpp @@ -10,16 +10,22 @@ ProtocolManager::ProtocolManager() { + m_messagesMutex = PTHREAD_MUTEX_INITIALIZER; + m_protocolsMutex = PTHREAD_MUTEX_INITIALIZER; } ProtocolManager::~ProtocolManager() { } -void ProtocolManager::messageReceived(uint8_t* data) +void ProtocolManager::notifyEvent(Event* event) { - assert(data); - m_messagesToProcess.push_back(data); + if (event->type == EVENT_TYPE_MESSAGE) + { + pthread_mutex_lock(&m_messagesMutex); + m_messagesToProcess.push_back(event->data); + pthread_mutex_unlock(&m_messagesMutex); + } } void ProtocolManager::sendMessage(std::string message) @@ -30,14 +36,19 @@ void ProtocolManager::sendMessage(std::string message) int ProtocolManager::startProtocol(Protocol* protocol) { + ProtocolInfo protocolInfo; protocolInfo.state = PROTOCOL_STATE_RUNNING; assignProtocolId(protocolInfo); protocolInfo.protocol = protocol; + pthread_mutex_lock(&m_protocolsMutex); m_protocols.push_back(protocolInfo); + pthread_mutex_unlock(&m_protocolsMutex); protocol->setListener(this); protocol->setup(); printf("__ProtocolManager> A new protocol with id=%ud been started. There are %ld protocols running.\n", protocolInfo.id, m_protocols.size()); + + return protocolInfo.id; } void ProtocolManager::stopProtocol(Protocol* protocol) @@ -50,8 +61,10 @@ void ProtocolManager::pauseProtocol(Protocol* protocol) { if (m_protocols[i].protocol == protocol && m_protocols[i].state == PROTOCOL_STATE_RUNNING) { + pthread_mutex_lock(&m_protocolsMutex); m_protocols[i].state = PROTOCOL_STATE_PAUSED; m_protocols[i].protocol->pause(); + pthread_mutex_unlock(&m_protocolsMutex); } } } @@ -61,8 +74,10 @@ void ProtocolManager::unpauseProtocol(Protocol* protocol) { if (m_protocols[i].protocol == protocol && m_protocols[i].state == PROTOCOL_STATE_PAUSED) { + pthread_mutex_lock(&m_protocolsMutex); m_protocols[i].state = PROTOCOL_STATE_RUNNING; m_protocols[i].protocol->unpause(); + pthread_mutex_unlock(&m_protocolsMutex); } } } @@ -73,9 +88,11 @@ void ProtocolManager::protocolTerminated(Protocol* protocol) { if (m_protocols[i-offset].protocol == protocol) { + pthread_mutex_lock(&m_protocolsMutex); delete m_protocols[i].protocol; m_protocols.erase(m_protocols.begin()+(i-offset), m_protocols.begin()+(i-offset)+1); offset++; + pthread_mutex_unlock(&m_protocolsMutex); } } printf("__ProtocolManager> A protocol has been terminated. There are %ld protocols running.\n", m_protocols.size()); @@ -84,18 +101,20 @@ void ProtocolManager::protocolTerminated(Protocol* protocol) void ProtocolManager::update() { // before updating, notice protocols that they have received information + pthread_mutex_lock(&m_messagesMutex); // secure threads int size = m_messagesToProcess.size(); for (int i = 0; i < size; i++) { - uint8_t* data = m_messagesToProcess.back(); + std::string data = m_messagesToProcess.back(); PROTOCOL_TYPE searchedProtocol = (PROTOCOL_TYPE)(data[0]); for (unsigned int i = 0; i < m_protocols.size() ; i++) { if (m_protocols[i].protocol->getProtocolType() == searchedProtocol) // pass data to them even when paused - m_protocols[i].protocol->messageReceived(data+1); + m_protocols[i].protocol->messageReceived((uint8_t*)(&data[1])); } m_messagesToProcess.pop_back(); } + pthread_mutex_unlock(&m_messagesMutex); // release the mutex // now update all protocols for (unsigned int i = 0; i < m_protocols.size(); i++) { @@ -114,7 +133,10 @@ PROTOCOL_STATE ProtocolManager::getProtocolState(uint32_t id) for (unsigned int i = 0; i < m_protocols.size(); i++) { if (m_protocols[i].id == id) - return m_protocols[i].state; + { + PROTOCOL_STATE state = m_protocols[i].state; + return state; + } } return PROTOCOL_STATE_TERMINATED; } @@ -129,8 +151,10 @@ void ProtocolManager::assignProtocolId(ProtocolInfo& protocolInfo) exists = false; for (unsigned int i = 0; i < m_protocols.size(); i++) { + pthread_mutex_lock(&m_protocolsMutex); if (m_protocols[i].id == newId) exists = true; + pthread_mutex_unlock(&m_protocolsMutex); } } while (exists); protocolInfo.id = newId; diff --git a/dev/SocketsBase/protocol_manager.hpp b/dev/SocketsBase/protocol_manager.hpp index a1021136f..7a3b7ef5f 100644 --- a/dev/SocketsBase/protocol_manager.hpp +++ b/dev/SocketsBase/protocol_manager.hpp @@ -1,9 +1,11 @@ #ifndef PROTOCOL_MANAGER_HPP #define PROTOCOL_MANAGER_HPP +#include "singleton.hpp" +#include "event.hpp" + #include #include -#include "singleton.hpp" class Protocol; @@ -25,7 +27,7 @@ class ProtocolManager : public Singleton } ProtocolInfo; public: - virtual void messageReceived(uint8_t* data); + virtual void notifyEvent(Event* event); virtual void sendMessage(std::string message); virtual int startProtocol(Protocol* protocol); @@ -47,7 +49,11 @@ class ProtocolManager : public Singleton // protected members std::vector m_protocols; - std::vector m_messagesToProcess; + std::vector m_messagesToProcess; + + // mutexes: + pthread_mutex_t m_messagesMutex; + pthread_mutex_t m_protocolsMutex; }; #endif // PROTOCOL_MANAGER_HPP diff --git a/dev/SocketsBase/stk_host.cpp b/dev/SocketsBase/stk_host.cpp index ccddd13ef..b8a553f82 100644 --- a/dev/SocketsBase/stk_host.cpp +++ b/dev/SocketsBase/stk_host.cpp @@ -14,22 +14,8 @@ void* STKHost::receive_data(void* self) while (1) { while (enet_host_service(host, &event, 0) != 0) { - printf("message received\n"); - switch (event.type) { - case ENET_EVENT_TYPE_RECEIVE: - printf("Sender : %ld", event.peer->address.host); - NetworkManager::getInstance()->packetReceived((char*) event.packet->data); - break; - case ENET_EVENT_TYPE_DISCONNECT: - printf("Somebody is now disconnected.\n"); - printf("Disconnected host: %i.%i.%i.%i:%i\n", event.peer->address.host>>24&0xff, event.peer->address.host>>16&0xff, event.peer->address.host>>8&0xff, event.peer->address.host&0xff,event.peer->address.port); - break; - case ENET_EVENT_TYPE_CONNECT: - printf("A client has just connected.\n"); - break; - case ENET_EVENT_TYPE_NONE: - break; - } + Event* evt = new Event(&event); + NetworkManager::getInstance()->notifyEvent(evt); } } return NULL; diff --git a/dev/SocketsBase/stk_peer.cpp b/dev/SocketsBase/stk_peer.cpp index 98ee53ca0..bc923d2c9 100644 --- a/dev/SocketsBase/stk_peer.cpp +++ b/dev/SocketsBase/stk_peer.cpp @@ -51,3 +51,7 @@ bool STKPeer::isConnected() printf("PEER STATE %i\n", m_peer->state); return (m_peer->state == ENET_PEER_STATE_CONNECTED); } +bool STKPeer::operator==(ENetPeer* peer) +{ + return peer==m_peer; +} diff --git a/dev/SocketsBase/stk_peer.hpp b/dev/SocketsBase/stk_peer.hpp index f094457ad..c36a8b7a6 100644 --- a/dev/SocketsBase/stk_peer.hpp +++ b/dev/SocketsBase/stk_peer.hpp @@ -10,6 +10,7 @@ class STKPeer STKPeer(); virtual ~STKPeer(); + static void* receive_data(void* self); virtual void sendPacket(char* data); @@ -20,6 +21,7 @@ class STKPeer uint32_t getAddress(); uint16_t getPort(); + bool operator==(ENetPeer* peer); protected: ENetPeer* m_peer; }; diff --git a/dev/SocketsBase/types.hpp b/dev/SocketsBase/types.hpp index 307db1f93..8c4f08321 100644 --- a/dev/SocketsBase/types.hpp +++ b/dev/SocketsBase/types.hpp @@ -29,4 +29,5 @@ class PlayerLogin : public CallbackObject std::string password; }; + #endif // TYPES_HPP From cd19ef2d78c1ba73821a7e750140ad44306d8a4c Mon Sep 17 00:00:00 2001 From: hilnius Date: Sat, 22 Jun 2013 23:45:46 +0000 Subject: [PATCH 16/22] cleaning the way events come out from Enet, improving peer management (removing double peers) git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12916 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/SocketsBase.cbp | 4 ++ dev/SocketsBase/client_network_manager.cpp | 4 +- dev/SocketsBase/event.cpp | 55 +++++++++++++++++++ dev/SocketsBase/event.hpp | 29 ++++++++++ dev/SocketsBase/network_manager.cpp | 8 +-- dev/SocketsBase/protocol_manager.cpp | 12 ++-- .../protocols/connect_to_server.cpp | 3 + dev/SocketsBase/stk_host.cpp | 1 + dev/SocketsBase/stk_peer.cpp | 6 +- dev/SocketsBase/stk_peer.hpp | 3 +- 10 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 dev/SocketsBase/event.cpp create mode 100644 dev/SocketsBase/event.hpp diff --git a/dev/SocketsBase/SocketsBase.cbp b/dev/SocketsBase/SocketsBase.cbp index 78874c4d2..c607eca17 100644 --- a/dev/SocketsBase/SocketsBase.cbp +++ b/dev/SocketsBase/SocketsBase.cbp @@ -40,6 +40,8 @@ + + @@ -47,6 +49,8 @@ + + diff --git a/dev/SocketsBase/client_network_manager.cpp b/dev/SocketsBase/client_network_manager.cpp index b8b6be1e9..784d68afc 100644 --- a/dev/SocketsBase/client_network_manager.cpp +++ b/dev/SocketsBase/client_network_manager.cpp @@ -16,7 +16,7 @@ ClientNetworkManager::~ClientNetworkManager() { } -void ClientNetworkManager::run() +void ClientNetworkManager::run() { if (enet_initialize() != 0) { @@ -78,7 +78,6 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) { printf("_NetworkInterface> We are NOT connected to the server.\n"); } - // step 5 : hide our public address HidePublicAddress* hpa = new HidePublicAddress(NULL); hpa->setPassword(m_playerLogin.password); @@ -88,6 +87,7 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) { } printf("_NetworkInterface> The public address is now hidden online.\n"); + return success; } diff --git a/dev/SocketsBase/event.cpp b/dev/SocketsBase/event.cpp new file mode 100644 index 000000000..e2135c833 --- /dev/null +++ b/dev/SocketsBase/event.cpp @@ -0,0 +1,55 @@ +#include "event.hpp" + +#include "network_manager.hpp" + +#include +#include + +Event::Event(ENetEvent* event) +{ + switch (event->type) + { + case ENET_EVENT_TYPE_CONNECT: + type = EVENT_TYPE_CONNECTED; + break; + case ENET_EVENT_TYPE_DISCONNECT: + type = EVENT_TYPE_DISCONNECTED; + break; + case ENET_EVENT_TYPE_RECEIVE: + type = EVENT_TYPE_MESSAGE; + break; + default: + break; + } + if (type == EVENT_TYPE_MESSAGE) + data = std::string((char*)(event->packet->data)); + else if (event->data) + data = std::string((char*)(event->data)); + + if (event->packet) + m_packet = event->packet; + + std::vector peers = NetworkManager::getInstance()->getPeers(); + peer = NULL; + for (unsigned int i = 0; i < peers.size(); i++) + { + if (*peers[i] == event->peer) + { + peer = peers[i]; + return; + } + } + if (peer == NULL) + { + printf("The peer still does not exist in %i peers\n", peers.size()); + STKPeer* newPeer = new STKPeer(); + newPeer->m_peer = event->peer; + peer = newPeer; + } +} + +Event::~Event() +{ + if (m_packet) + enet_packet_destroy(m_packet); +} diff --git a/dev/SocketsBase/event.hpp b/dev/SocketsBase/event.hpp new file mode 100644 index 000000000..c425ca5e6 --- /dev/null +++ b/dev/SocketsBase/event.hpp @@ -0,0 +1,29 @@ +#ifndef EVENT_HPP +#define EVENT_HPP + +#include "stk_peer.hpp" +#include +#include + +enum EVENT_TYPE +{ + EVENT_TYPE_CONNECTED, + EVENT_TYPE_DISCONNECTED, + EVENT_TYPE_MESSAGE +}; + +class Event +{ + public: + Event(ENetEvent* event); + ~Event(); + + EVENT_TYPE type; + std::string data; + STKPeer* peer; + + private: + ENetPacket* m_packet; +}; + +#endif // EVENT_HPP diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp index 7d22428e4..0afc99491 100644 --- a/dev/SocketsBase/network_manager.cpp +++ b/dev/SocketsBase/network_manager.cpp @@ -45,10 +45,8 @@ bool NetworkManager::connect(uint32_t ip, uint16_t port) if (peerExists(ip, port)) return isConnectedTo(ip, port); - STKPeer* peer = new STKPeer(); - bool success = peer->connectToHost(m_localhost, ip, port, 2, 0); - if (success) - m_peers.push_back(peer); + bool success = STKPeer::connectToHost(m_localhost, ip, port, 2, 0); + return success; } @@ -74,6 +72,8 @@ void NetworkManager::notifyEvent(Event* event) break; case EVENT_TYPE_CONNECTED: printf("A client has just connected.\n"); + // create the new peer: + m_peers.push_back(event->peer); break; } ProtocolManager::getInstance()->notifyEvent(event); diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp index 45dea4b6f..a05c18256 100644 --- a/dev/SocketsBase/protocol_manager.cpp +++ b/dev/SocketsBase/protocol_manager.cpp @@ -10,8 +10,8 @@ ProtocolManager::ProtocolManager() { - m_messagesMutex = PTHREAD_MUTEX_INITIALIZER; - m_protocolsMutex = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_init(&m_messagesMutex, NULL); + pthread_mutex_init(&m_protocolsMutex, NULL); } ProtocolManager::~ProtocolManager() @@ -36,18 +36,18 @@ void ProtocolManager::sendMessage(std::string message) int ProtocolManager::startProtocol(Protocol* protocol) { - ProtocolInfo protocolInfo; protocolInfo.state = PROTOCOL_STATE_RUNNING; assignProtocolId(protocolInfo); protocolInfo.protocol = protocol; + printf("__ProtocolManager> A new protocol with id=%u has been started. There are %ld protocols running.\n", protocolInfo.id, m_protocols.size()+1); + pthread_mutex_lock(&m_protocolsMutex); m_protocols.push_back(protocolInfo); pthread_mutex_unlock(&m_protocolsMutex); + protocol->setListener(this); protocol->setup(); - printf("__ProtocolManager> A new protocol with id=%ud been started. There are %ld protocols running.\n", protocolInfo.id, m_protocols.size()); - return protocolInfo.id; } @@ -91,8 +91,8 @@ void ProtocolManager::protocolTerminated(Protocol* protocol) pthread_mutex_lock(&m_protocolsMutex); delete m_protocols[i].protocol; m_protocols.erase(m_protocols.begin()+(i-offset), m_protocols.begin()+(i-offset)+1); - offset++; pthread_mutex_unlock(&m_protocolsMutex); + offset++; } } printf("__ProtocolManager> A protocol has been terminated. There are %ld protocols running.\n", m_protocols.size()); diff --git a/dev/SocketsBase/protocols/connect_to_server.cpp b/dev/SocketsBase/protocols/connect_to_server.cpp index 10c1d51e9..97dc2d064 100644 --- a/dev/SocketsBase/protocols/connect_to_server.cpp +++ b/dev/SocketsBase/protocols/connect_to_server.cpp @@ -43,9 +43,11 @@ void ConnectToServer::update() currentTime += 3600; if (currentTime > target) { + printf("DOING AN UPDATE BECAUSE IT IS TIME TO DO IT.\n"); NetworkManager::getInstance()->connect(m_serverIp, m_serverPort); if (NetworkManager::getInstance()->isConnectedTo(m_serverIp, m_serverPort)) { + printf("NetworkManager sayon THAT YOU ARE CONNECTED, BIATCHHH\n"); m_state = DONE; return; } @@ -55,6 +57,7 @@ void ConnectToServer::update() } else if (m_state == DONE) { + printf("STATE IS KNOWN AS DONE\n"); m_listener->protocolTerminated(this); } } diff --git a/dev/SocketsBase/stk_host.cpp b/dev/SocketsBase/stk_host.cpp index b8a553f82..4f966b711 100644 --- a/dev/SocketsBase/stk_host.cpp +++ b/dev/SocketsBase/stk_host.cpp @@ -16,6 +16,7 @@ void* STKHost::receive_data(void* self) while (enet_host_service(host, &event, 0) != 0) { Event* evt = new Event(&event); NetworkManager::getInstance()->notifyEvent(evt); + //NetworkManager::getInstance()->packetReceived((char*)(event.data)); } } return NULL; diff --git a/dev/SocketsBase/stk_peer.cpp b/dev/SocketsBase/stk_peer.cpp index bc923d2c9..94671feee 100644 --- a/dev/SocketsBase/stk_peer.cpp +++ b/dev/SocketsBase/stk_peer.cpp @@ -18,13 +18,13 @@ bool STKPeer::connectToHost(STKHost* host, uint32_t ip, uint16_t port, uint32_t address.host = ip; address.port = port; - m_peer = enet_host_connect(host->m_host, &address, 2, 0); - if (m_peer == NULL) + ENetPeer* peer = enet_host_connect(host->m_host, &address, 2, 0); + if (peer == NULL) { printf("Could not try to connect to server.\n"); return false; } - printf("Connecting to %i.%i.%i.%i:%i.\n", (m_peer->address.host>>0)&0xff,(m_peer->address.host>>8)&0xff,(m_peer->address.host>>16)&0xff,(m_peer->address.host>>24)&0xff,m_peer->address.port); + printf("Connecting to %i.%i.%i.%i:%i.\n", (peer->address.host>>0)&0xff,(peer->address.host>>8)&0xff,(peer->address.host>>16)&0xff,(peer->address.host>>24)&0xff,peer->address.port); return true; } diff --git a/dev/SocketsBase/stk_peer.hpp b/dev/SocketsBase/stk_peer.hpp index c36a8b7a6..3f8236869 100644 --- a/dev/SocketsBase/stk_peer.hpp +++ b/dev/SocketsBase/stk_peer.hpp @@ -6,6 +6,7 @@ class STKPeer { + friend class Event; public: STKPeer(); virtual ~STKPeer(); @@ -15,7 +16,7 @@ class STKPeer virtual void sendPacket(char* data); - bool connectToHost(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data); + static bool connectToHost(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data); bool isConnected(); From f52469159bc0d127a8fdd5400b4943216d8b4efe Mon Sep 17 00:00:00 2001 From: hilnius Date: Sun, 23 Jun 2013 00:53:26 +0000 Subject: [PATCH 17/22] the way events pass trough the protocol system is now clean. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12918 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/client_network_manager.cpp | 2 +- dev/SocketsBase/event.cpp | 2 +- dev/SocketsBase/network_manager.cpp | 22 +++++++---- dev/SocketsBase/protocol.hpp | 10 ++--- dev/SocketsBase/protocol_manager.cpp | 37 +++++++++---------- dev/SocketsBase/protocol_manager.hpp | 4 +- .../protocols/connect_to_server.cpp | 18 ++++----- .../protocols/connect_to_server.hpp | 2 +- .../protocols/get_peer_address.cpp | 4 +- .../protocols/get_peer_address.hpp | 2 +- .../protocols/get_public_address.cpp | 4 +- .../protocols/get_public_address.hpp | 2 +- .../protocols/hide_public_address.cpp | 4 +- .../protocols/hide_public_address.hpp | 2 +- .../protocols/show_public_address.cpp | 4 +- .../protocols/show_public_address.hpp | 2 +- dev/SocketsBase/stk_host.cpp | 1 - dev/SocketsBase/stk_peer.cpp | 2 + 18 files changed, 66 insertions(+), 58 deletions(-) diff --git a/dev/SocketsBase/client_network_manager.cpp b/dev/SocketsBase/client_network_manager.cpp index 784d68afc..aa9f42346 100644 --- a/dev/SocketsBase/client_network_manager.cpp +++ b/dev/SocketsBase/client_network_manager.cpp @@ -71,7 +71,7 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) bool success = false; if (m_localhost->isConnectedTo(addr.ip, addr.port)) { - success = true; + success = true; printf("_NetworkInterface> CONNECTION SUCCES : YOU ARE NOW CONNECTED TO A SERVER.\n"); } else diff --git a/dev/SocketsBase/event.cpp b/dev/SocketsBase/event.cpp index e2135c833..5974dca1f 100644 --- a/dev/SocketsBase/event.cpp +++ b/dev/SocketsBase/event.cpp @@ -41,7 +41,7 @@ Event::Event(ENetEvent* event) } if (peer == NULL) { - printf("The peer still does not exist in %i peers\n", peers.size()); + printf("The peer still does not exist in %u peers\n", peers.size()); STKPeer* newPeer = new STKPeer(); newPeer->m_peer = event->peer; peer = newPeer; diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp index 0afc99491..ef28e4b18 100644 --- a/dev/SocketsBase/network_manager.cpp +++ b/dev/SocketsBase/network_manager.cpp @@ -44,10 +44,8 @@ bool NetworkManager::connect(uint32_t ip, uint16_t port) { if (peerExists(ip, port)) return isConnectedTo(ip, port); - - bool success = STKPeer::connectToHost(m_localhost, ip, port, 2, 0); - return success; + return STKPeer::connectToHost(m_localhost, ip, port, 2, 0); } void NetworkManager::setManualSocketsMode(bool manual) @@ -64,20 +62,30 @@ void NetworkManager::notifyEvent(Event* event) switch (event->type) { case EVENT_TYPE_MESSAGE: - printf("Message, Sender : %ld\n", event->peer->getAddress()); + printf("Message, Sender : %u, message = \"%s\"\n", event->peer->getAddress(), event->data.c_str()); break; case EVENT_TYPE_DISCONNECTED: - printf("Somebody is now disconnected.\n"); + printf("Somebody is now disconnected. There are now %lu peers.\n", m_peers.size()); printf("Disconnected host: %i.%i.%i.%i:%i\n", event->peer->getAddress()>>24&0xff, event->peer->getAddress()>>16&0xff, event->peer->getAddress()>>8&0xff, event->peer->getAddress()&0xff,event->peer->getPort()); + // remove the peer: + for (unsigned int i = 0; i < m_peers.size(); i++) + { + if (m_peers[i] == event->peer) + { + delete m_peers[i]; + m_peers.erase(m_peers.begin()+i, m_peers.begin()+i+1); + break; + } + } + printf("ERROR : the peer that has been disconnected was not registered by the Network Manager.\n"); break; case EVENT_TYPE_CONNECTED: - printf("A client has just connected.\n"); + printf("A client has just connected. There are now %lu peers.\n", m_peers.size() + 1); // create the new peer: m_peers.push_back(event->peer); break; } ProtocolManager::getInstance()->notifyEvent(event); - delete event; // event won't be use again } void NetworkManager::setLogin(std::string username, std::string password) diff --git a/dev/SocketsBase/protocol.hpp b/dev/SocketsBase/protocol.hpp index e3b4414ef..20e8652a2 100644 --- a/dev/SocketsBase/protocol.hpp +++ b/dev/SocketsBase/protocol.hpp @@ -8,18 +8,18 @@ enum PROTOCOL_TYPE { - PROTOCOL_NOT_CONCERNED = 0, - PROTOCOL_CONNECTION = 1 - + PROTOCOL_NONE = 0, + PROTOCOL_CONNECTION = 1, + PROTOCOL_SILENT = 0xffff // used for protocols that do not subscribe to any network event. }; class Protocol { public: - Protocol(CallbackObject* callbackObject, PROTOCOL_TYPE type = PROTOCOL_NOT_CONCERNED); + Protocol(CallbackObject* callbackObject, PROTOCOL_TYPE type); virtual ~Protocol(); - virtual void messageReceived(uint8_t* data) = 0; + virtual void notifyEvent(Event* event) = 0; void setListener(ProtocolManager* listener); diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp index a05c18256..c26cc6ec8 100644 --- a/dev/SocketsBase/protocol_manager.cpp +++ b/dev/SocketsBase/protocol_manager.cpp @@ -10,7 +10,7 @@ ProtocolManager::ProtocolManager() { - pthread_mutex_init(&m_messagesMutex, NULL); + pthread_mutex_init(&m_eventsMutex, NULL); pthread_mutex_init(&m_protocolsMutex, NULL); } @@ -20,12 +20,9 @@ ProtocolManager::~ProtocolManager() void ProtocolManager::notifyEvent(Event* event) { - if (event->type == EVENT_TYPE_MESSAGE) - { - pthread_mutex_lock(&m_messagesMutex); - m_messagesToProcess.push_back(event->data); - pthread_mutex_unlock(&m_messagesMutex); - } + pthread_mutex_lock(&m_eventsMutex); + m_eventsToProcess.push_back(event); + pthread_mutex_unlock(&m_eventsMutex); } void ProtocolManager::sendMessage(std::string message) @@ -83,38 +80,42 @@ void ProtocolManager::unpauseProtocol(Protocol* protocol) } void ProtocolManager::protocolTerminated(Protocol* protocol) { + pthread_mutex_lock(&m_protocolsMutex); int offset = 0; for (unsigned int i = 0; i < m_protocols.size(); i++) { if (m_protocols[i-offset].protocol == protocol) { - pthread_mutex_lock(&m_protocolsMutex); delete m_protocols[i].protocol; m_protocols.erase(m_protocols.begin()+(i-offset), m_protocols.begin()+(i-offset)+1); - pthread_mutex_unlock(&m_protocolsMutex); offset++; } } printf("__ProtocolManager> A protocol has been terminated. There are %ld protocols running.\n", m_protocols.size()); + pthread_mutex_unlock(&m_protocolsMutex); } void ProtocolManager::update() { // before updating, notice protocols that they have received information - pthread_mutex_lock(&m_messagesMutex); // secure threads - int size = m_messagesToProcess.size(); + pthread_mutex_lock(&m_eventsMutex); // secure threads + int size = m_eventsToProcess.size(); for (int i = 0; i < size; i++) { - std::string data = m_messagesToProcess.back(); - PROTOCOL_TYPE searchedProtocol = (PROTOCOL_TYPE)(data[0]); + Event* event = m_eventsToProcess.back(); + + PROTOCOL_TYPE searchedProtocol = PROTOCOL_NONE; + if (event->data.size() > 0) + searchedProtocol = (PROTOCOL_TYPE)(event->data[0]); for (unsigned int i = 0; i < m_protocols.size() ; i++) { - if (m_protocols[i].protocol->getProtocolType() == searchedProtocol) // pass data to them even when paused - m_protocols[i].protocol->messageReceived((uint8_t*)(&data[1])); + if (m_protocols[i].protocol->getProtocolType() == searchedProtocol || event->type != EVENT_TYPE_MESSAGE) // pass data to protocols even when paused + m_protocols[i].protocol->notifyEvent(event); } - m_messagesToProcess.pop_back(); + delete event; + m_eventsToProcess.pop_back(); } - pthread_mutex_unlock(&m_messagesMutex); // release the mutex + pthread_mutex_unlock(&m_eventsMutex); // release the mutex // now update all protocols for (unsigned int i = 0; i < m_protocols.size(); i++) { @@ -151,10 +152,8 @@ void ProtocolManager::assignProtocolId(ProtocolInfo& protocolInfo) exists = false; for (unsigned int i = 0; i < m_protocols.size(); i++) { - pthread_mutex_lock(&m_protocolsMutex); if (m_protocols[i].id == newId) exists = true; - pthread_mutex_unlock(&m_protocolsMutex); } } while (exists); protocolInfo.id = newId; diff --git a/dev/SocketsBase/protocol_manager.hpp b/dev/SocketsBase/protocol_manager.hpp index 7a3b7ef5f..699e2b347 100644 --- a/dev/SocketsBase/protocol_manager.hpp +++ b/dev/SocketsBase/protocol_manager.hpp @@ -49,10 +49,10 @@ class ProtocolManager : public Singleton // protected members std::vector m_protocols; - std::vector m_messagesToProcess; + std::vector m_eventsToProcess; // mutexes: - pthread_mutex_t m_messagesMutex; + pthread_mutex_t m_eventsMutex; pthread_mutex_t m_protocolsMutex; }; diff --git a/dev/SocketsBase/protocols/connect_to_server.cpp b/dev/SocketsBase/protocols/connect_to_server.cpp index 97dc2d064..8bae3c8de 100644 --- a/dev/SocketsBase/protocols/connect_to_server.cpp +++ b/dev/SocketsBase/protocols/connect_to_server.cpp @@ -6,31 +6,34 @@ #include #include -ConnectToServer::ConnectToServer(CallbackObject* callbackObject) : Protocol(callbackObject) +ConnectToServer::ConnectToServer(CallbackObject* callbackObject) : Protocol(callbackObject, PROTOCOL_CONNECTION) { m_serverIp = 0; m_serverPort = 0; + m_state = NONE; } ConnectToServer::~ConnectToServer() { } -void ConnectToServer::messageReceived(uint8_t* data) +void ConnectToServer::notifyEvent(Event* event) { - printf("data received\n"); - printf("%s", data); - m_state = NONE; // we received a message, we are connected + if (event->type == EVENT_TYPE_CONNECTED && event->peer->getAddress() == m_serverIp && event->peer->getPort() == m_serverPort) + { + printf("The Connect To Server protocol has received an event notifying that he's connected to the peer. The peer sent \"%s\"\n", event->data.c_str()); + m_state = DONE; // we received a message, we are connected + } } void ConnectToServer::setup() { + m_state = NONE; if (m_serverIp == 0 || m_serverPort == 0 ) { printf("You have to set the server's public ip:port of the server.\n"); m_listener->protocolTerminated(this); } - m_state = NONE; } void ConnectToServer::update() @@ -43,11 +46,9 @@ void ConnectToServer::update() currentTime += 3600; if (currentTime > target) { - printf("DOING AN UPDATE BECAUSE IT IS TIME TO DO IT.\n"); NetworkManager::getInstance()->connect(m_serverIp, m_serverPort); if (NetworkManager::getInstance()->isConnectedTo(m_serverIp, m_serverPort)) { - printf("NetworkManager sayon THAT YOU ARE CONNECTED, BIATCHHH\n"); m_state = DONE; return; } @@ -57,7 +58,6 @@ void ConnectToServer::update() } else if (m_state == DONE) { - printf("STATE IS KNOWN AS DONE\n"); m_listener->protocolTerminated(this); } } diff --git a/dev/SocketsBase/protocols/connect_to_server.hpp b/dev/SocketsBase/protocols/connect_to_server.hpp index d5d4c6c97..3b6706bb3 100644 --- a/dev/SocketsBase/protocols/connect_to_server.hpp +++ b/dev/SocketsBase/protocols/connect_to_server.hpp @@ -10,7 +10,7 @@ class ConnectToServer : public Protocol, public CallbackObject ConnectToServer(CallbackObject* callbackObject); virtual ~ConnectToServer(); - virtual void messageReceived(uint8_t* data); + virtual void notifyEvent(Event* event); virtual void setup(); virtual void update(); diff --git a/dev/SocketsBase/protocols/get_peer_address.cpp b/dev/SocketsBase/protocols/get_peer_address.cpp index 36dc8ac75..495576271 100644 --- a/dev/SocketsBase/protocols/get_peer_address.cpp +++ b/dev/SocketsBase/protocols/get_peer_address.cpp @@ -6,7 +6,7 @@ #include #include -GetPeerAddress::GetPeerAddress(CallbackObject* callbackObject) : Protocol(callbackObject) +GetPeerAddress::GetPeerAddress(CallbackObject* callbackObject) : Protocol(callbackObject, PROTOCOL_SILENT) { } @@ -14,7 +14,7 @@ GetPeerAddress::~GetPeerAddress() { } -void GetPeerAddress::messageReceived(uint8_t* data) +void GetPeerAddress::notifyEvent(Event* event) { } diff --git a/dev/SocketsBase/protocols/get_peer_address.hpp b/dev/SocketsBase/protocols/get_peer_address.hpp index 418a38271..28b007d4e 100644 --- a/dev/SocketsBase/protocols/get_peer_address.hpp +++ b/dev/SocketsBase/protocols/get_peer_address.hpp @@ -9,7 +9,7 @@ class GetPeerAddress : public Protocol GetPeerAddress(CallbackObject* callbackObject); virtual ~GetPeerAddress(); - virtual void messageReceived(uint8_t* data); + virtual void notifyEvent(Event* event); virtual void setup(); virtual void update(); diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index 3777b1102..a64f5b37b 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -21,7 +21,7 @@ int stunRand() return rand(); } -GetPublicAddress::GetPublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject) +GetPublicAddress::GetPublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject, PROTOCOL_SILENT) { } @@ -29,7 +29,7 @@ GetPublicAddress::~GetPublicAddress() { } -void GetPublicAddress::messageReceived(uint8_t* data) +void GetPublicAddress::notifyEvent(Event* event) { } diff --git a/dev/SocketsBase/protocols/get_public_address.hpp b/dev/SocketsBase/protocols/get_public_address.hpp index 5382c3a25..acafd8d02 100644 --- a/dev/SocketsBase/protocols/get_public_address.hpp +++ b/dev/SocketsBase/protocols/get_public_address.hpp @@ -9,7 +9,7 @@ class GetPublicAddress : public Protocol GetPublicAddress(CallbackObject* callbackObject); virtual ~GetPublicAddress(); - virtual void messageReceived(uint8_t* data); + virtual void notifyEvent(Event* event); virtual void setup(); virtual void update(); diff --git a/dev/SocketsBase/protocols/hide_public_address.cpp b/dev/SocketsBase/protocols/hide_public_address.cpp index 204a24bfe..2ad17417c 100644 --- a/dev/SocketsBase/protocols/hide_public_address.cpp +++ b/dev/SocketsBase/protocols/hide_public_address.cpp @@ -4,7 +4,7 @@ #include -HidePublicAddress::HidePublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject) +HidePublicAddress::HidePublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject, PROTOCOL_SILENT) { } @@ -12,7 +12,7 @@ HidePublicAddress::~HidePublicAddress() { } -void HidePublicAddress::messageReceived(uint8_t* data) +void HidePublicAddress::notifyEvent(Event* event) { } diff --git a/dev/SocketsBase/protocols/hide_public_address.hpp b/dev/SocketsBase/protocols/hide_public_address.hpp index 0ad887030..9848e794d 100644 --- a/dev/SocketsBase/protocols/hide_public_address.hpp +++ b/dev/SocketsBase/protocols/hide_public_address.hpp @@ -10,7 +10,7 @@ class HidePublicAddress : public Protocol HidePublicAddress(CallbackObject* callbackObject); virtual ~HidePublicAddress(); - virtual void messageReceived(uint8_t* data); + virtual void notifyEvent(Event* event); virtual void setup(); virtual void update(); diff --git a/dev/SocketsBase/protocols/show_public_address.cpp b/dev/SocketsBase/protocols/show_public_address.cpp index 27fcf4273..a44f0f065 100644 --- a/dev/SocketsBase/protocols/show_public_address.cpp +++ b/dev/SocketsBase/protocols/show_public_address.cpp @@ -4,7 +4,7 @@ #include -ShowPublicAddress::ShowPublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject) +ShowPublicAddress::ShowPublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject, PROTOCOL_SILENT) { } @@ -12,7 +12,7 @@ ShowPublicAddress::~ShowPublicAddress() { } -void ShowPublicAddress::messageReceived(uint8_t* data) +void ShowPublicAddress::notifyEvent(Event* event) { } diff --git a/dev/SocketsBase/protocols/show_public_address.hpp b/dev/SocketsBase/protocols/show_public_address.hpp index 2ea727fa1..4a4c3381d 100644 --- a/dev/SocketsBase/protocols/show_public_address.hpp +++ b/dev/SocketsBase/protocols/show_public_address.hpp @@ -10,7 +10,7 @@ class ShowPublicAddress : public Protocol ShowPublicAddress(CallbackObject* callbackObject); virtual ~ShowPublicAddress(); - virtual void messageReceived(uint8_t* data); + virtual void notifyEvent(Event* event); virtual void setup(); virtual void update(); diff --git a/dev/SocketsBase/stk_host.cpp b/dev/SocketsBase/stk_host.cpp index 4f966b711..b8a553f82 100644 --- a/dev/SocketsBase/stk_host.cpp +++ b/dev/SocketsBase/stk_host.cpp @@ -16,7 +16,6 @@ void* STKHost::receive_data(void* self) while (enet_host_service(host, &event, 0) != 0) { Event* evt = new Event(&event); NetworkManager::getInstance()->notifyEvent(evt); - //NetworkManager::getInstance()->packetReceived((char*)(event.data)); } } return NULL; diff --git a/dev/SocketsBase/stk_peer.cpp b/dev/SocketsBase/stk_peer.cpp index 94671feee..2e7c86cca 100644 --- a/dev/SocketsBase/stk_peer.cpp +++ b/dev/SocketsBase/stk_peer.cpp @@ -10,6 +10,8 @@ STKPeer::STKPeer() STKPeer::~STKPeer() { + if (m_peer) + delete m_peer; } bool STKPeer::connectToHost(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data) From 36673a2112b8955018dc7c381ebd1124c99b57e3 Mon Sep 17 00:00:00 2001 From: hilnius Date: Wed, 26 Jun 2013 00:05:17 +0000 Subject: [PATCH 18/22] doxygen commenting a lot of code, improved thread-safety, improved coding standards git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@12979 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- dev/SocketsBase/SocketsBase.cbp | 2 + dev/SocketsBase/client_network_manager.cpp | 51 ++-- dev/SocketsBase/client_network_manager.hpp | 19 +- dev/SocketsBase/event.cpp | 20 +- dev/SocketsBase/event.hpp | 50 +++- dev/SocketsBase/http_functions.cpp | 19 +- dev/SocketsBase/http_functions.hpp | 18 ++ dev/SocketsBase/main.cpp | 20 +- dev/SocketsBase/network_interface.cpp | 18 ++ dev/SocketsBase/network_interface.hpp | 18 ++ dev/SocketsBase/network_manager.cpp | 54 ++-- dev/SocketsBase/network_manager.hpp | 33 ++- dev/SocketsBase/protocol.cpp | 26 +- dev/SocketsBase/protocol.hpp | 81 +++++- dev/SocketsBase/protocol_manager.cpp | 243 +++++++++++++---- dev/SocketsBase/protocol_manager.hpp | 257 ++++++++++++++++-- .../protocols/connect_to_server.cpp | 72 +++-- .../protocols/connect_to_server.hpp | 26 +- .../protocols/get_peer_address.cpp | 76 ++++-- .../protocols/get_peer_address.hpp | 26 +- .../protocols/get_public_address.cpp | 120 ++++---- .../protocols/get_public_address.hpp | 26 +- .../protocols/hide_public_address.cpp | 26 +- .../protocols/hide_public_address.hpp | 22 +- .../protocols/lobby_room_protocol.cpp | 50 ++++ .../protocols/lobby_room_protocol.hpp | 47 ++++ .../protocols/show_public_address.cpp | 32 ++- .../protocols/show_public_address.hpp | 26 +- dev/SocketsBase/server_network_manager.cpp | 47 +++- dev/SocketsBase/server_network_manager.hpp | 20 +- dev/SocketsBase/singleton.hpp | 18 ++ dev/SocketsBase/stk_host.cpp | 121 +++++++-- dev/SocketsBase/stk_host.hpp | 135 +++++++-- dev/SocketsBase/stk_peer.cpp | 26 +- dev/SocketsBase/stk_peer.hpp | 20 +- dev/SocketsBase/time.cpp | 18 ++ dev/SocketsBase/time.hpp | 18 ++ dev/SocketsBase/types.hpp | 42 ++- 38 files changed, 1596 insertions(+), 347 deletions(-) create mode 100644 dev/SocketsBase/protocols/lobby_room_protocol.cpp create mode 100644 dev/SocketsBase/protocols/lobby_room_protocol.hpp diff --git a/dev/SocketsBase/SocketsBase.cbp b/dev/SocketsBase/SocketsBase.cbp index c607eca17..a74fa1b6e 100644 --- a/dev/SocketsBase/SocketsBase.cbp +++ b/dev/SocketsBase/SocketsBase.cbp @@ -44,6 +44,8 @@ + + diff --git a/dev/SocketsBase/client_network_manager.cpp b/dev/SocketsBase/client_network_manager.cpp index aa9f42346..f706d31d8 100644 --- a/dev/SocketsBase/client_network_manager.cpp +++ b/dev/SocketsBase/client_network_manager.cpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "client_network_manager.hpp" #include "protocols/get_public_address.hpp" @@ -34,19 +52,20 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) { printf("_NetworkInterface>Starting the connection to host protocol\n"); // step 1 : retreive public address - int id = ProtocolManager::getInstance()->startProtocol(new GetPublicAddress(&m_publicAddress)); - while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + Protocol* protocol = new GetPublicAddress(&m_public_address); + ProtocolManager::getInstance()->requestStart(protocol); + while (ProtocolManager::getInstance()->getProtocolState(protocol) != PROTOCOL_STATE_TERMINATED ) { } printf("_NetworkInterface> The public address is known.\n"); // step 2 : show the public address for others (here, the server) ShowPublicAddress* spa = new ShowPublicAddress(NULL); - spa->setPassword(m_playerLogin.password); - spa->setUsername(m_playerLogin.username); - spa->setPublicAddress(m_publicAddress.ip, m_publicAddress.port); - id = ProtocolManager::getInstance()->startProtocol(spa); - while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + spa->setPassword(m_player_login.password); + spa->setUsername(m_player_login.username); + spa->setPublicAddress(m_public_address.ip, m_public_address.port); + ProtocolManager::getInstance()->requestStart(spa); + while (ProtocolManager::getInstance()->getProtocolState(spa) != PROTOCOL_STATE_TERMINATED ) { } printf("_NetworkInterface> The public address is being shown online.\n"); @@ -55,8 +74,8 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) TransportAddress addr; GetPeerAddress* gpa = new GetPeerAddress(&addr); gpa->setPeerName(serverNickname); - id = ProtocolManager::getInstance()->startProtocol(gpa); - while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + ProtocolManager::getInstance()->requestStart(gpa); + while (ProtocolManager::getInstance()->getProtocolState(gpa) != PROTOCOL_STATE_TERMINATED ) { } printf("_NetworkInterface> The public address of the server is known.\n"); @@ -64,12 +83,12 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) // step 4 : connect to the server ConnectToServer* cts = new ConnectToServer(NULL); cts->setServerAddress(addr.ip, addr.port); - id = ProtocolManager::getInstance()->startProtocol(cts); - while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + ProtocolManager::getInstance()->requestStart(cts); + while (ProtocolManager::getInstance()->getProtocolState(cts) != PROTOCOL_STATE_TERMINATED ) { } bool success = false; - if (m_localhost->isConnectedTo(addr.ip, addr.port)) + if (m_localhost->isConnectedTo(TransportAddress(addr.ip, addr.port))) { success = true; printf("_NetworkInterface> CONNECTION SUCCES : YOU ARE NOW CONNECTED TO A SERVER.\n"); @@ -80,10 +99,10 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) } // step 5 : hide our public address HidePublicAddress* hpa = new HidePublicAddress(NULL); - hpa->setPassword(m_playerLogin.password); - hpa->setUsername(m_playerLogin.username); - id = ProtocolManager::getInstance()->startProtocol(hpa); - while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + hpa->setPassword(m_player_login.password); + hpa->setUsername(m_player_login.username); + ProtocolManager::getInstance()->requestStart(hpa); + while (ProtocolManager::getInstance()->getProtocolState(hpa) != PROTOCOL_STATE_TERMINATED ) { } printf("_NetworkInterface> The public address is now hidden online.\n"); diff --git a/dev/SocketsBase/client_network_manager.hpp b/dev/SocketsBase/client_network_manager.hpp index c9160a6d8..57c4ad62c 100644 --- a/dev/SocketsBase/client_network_manager.hpp +++ b/dev/SocketsBase/client_network_manager.hpp @@ -1,9 +1,26 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef CLIENT_NETWORK_MANAGER_HPP #define CLIENT_NETWORK_MANAGER_HPP #include "network_manager.hpp" - class ClientNetworkManager : public NetworkManager { friend class Singleton; diff --git a/dev/SocketsBase/event.cpp b/dev/SocketsBase/event.cpp index 5974dca1f..2f9565694 100644 --- a/dev/SocketsBase/event.cpp +++ b/dev/SocketsBase/event.cpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "event.hpp" #include "network_manager.hpp" @@ -41,7 +59,7 @@ Event::Event(ENetEvent* event) } if (peer == NULL) { - printf("The peer still does not exist in %u peers\n", peers.size()); + printf("The peer still does not exist in %lu peers\n", peers.size()); STKPeer* newPeer = new STKPeer(); newPeer->m_peer = event->peer; peer = newPeer; diff --git a/dev/SocketsBase/event.hpp b/dev/SocketsBase/event.hpp index c425ca5e6..f0f6005c4 100644 --- a/dev/SocketsBase/event.hpp +++ b/dev/SocketsBase/event.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef EVENT_HPP #define EVENT_HPP @@ -5,25 +23,43 @@ #include #include +/*! + * \enum EVENT_TYPE + * \brief Represents a network event type. + */ enum EVENT_TYPE { - EVENT_TYPE_CONNECTED, - EVENT_TYPE_DISCONNECTED, - EVENT_TYPE_MESSAGE + EVENT_TYPE_CONNECTED, //!< A peer is connected + EVENT_TYPE_DISCONNECTED,//!< A peer is disconnected + EVENT_TYPE_MESSAGE //!< A message between server and client protocols }; +/*! + * \class Event + * \brief Class representing an event that need to pass trough the system. + * This is used to remove ENet dependency in the network. + * It interfaces the ENetEvent structure. + */ class Event { public: + /*! + * \brief Constructor + * \param event : The event that needs to be translated. + */ Event(ENetEvent* event); + /*! + * \brief Destructor + * frees the memory of the ENetPacket. + */ ~Event(); - EVENT_TYPE type; - std::string data; - STKPeer* peer; + EVENT_TYPE type; //!< Type of the event + std::string data; //!< Copy of the data passed by the event + STKPeer* peer; //!< Pointer to the peer that triggered that event private: - ENetPacket* m_packet; + ENetPacket* m_packet; //!< A pointer on the ENetPacket to be deleted. }; #endif // EVENT_HPP diff --git a/dev/SocketsBase/http_functions.cpp b/dev/SocketsBase/http_functions.cpp index 28f65f996..240178816 100644 --- a/dev/SocketsBase/http_functions.cpp +++ b/dev/SocketsBase/http_functions.cpp @@ -1,5 +1,22 @@ -#include "http_functions.hpp" +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include "http_functions.hpp" #include #include diff --git a/dev/SocketsBase/http_functions.hpp b/dev/SocketsBase/http_functions.hpp index 927725c40..970b9c1dd 100644 --- a/dev/SocketsBase/http_functions.hpp +++ b/dev/SocketsBase/http_functions.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef HTTP_FUNCTIONS_HPP #define HTTP_FUNCTIONS_HPP diff --git a/dev/SocketsBase/main.cpp b/dev/SocketsBase/main.cpp index 4e93a0a21..7a538b3f5 100644 --- a/dev/SocketsBase/main.cpp +++ b/dev/SocketsBase/main.cpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include #include @@ -106,7 +124,7 @@ int main() std::cin >> port; ServerNetworkManager::getInstance()->setManualSocketsMode(true); char msg[] = "test"; - ServerNetworkManager::getInstance()->getHost()->sendRawPacket((uint8_t*)(msg), sizeof(msg), peer, port); + ServerNetworkManager::getInstance()->getHost()->sendRawPacket((uint8_t*)(msg), sizeof(msg), TransportAddress(peer, port)); ServerNetworkManager::getInstance()->setManualSocketsMode(false); continue; } diff --git a/dev/SocketsBase/network_interface.cpp b/dev/SocketsBase/network_interface.cpp index 7af2d5975..c3222c866 100644 --- a/dev/SocketsBase/network_interface.cpp +++ b/dev/SocketsBase/network_interface.cpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "network_interface.hpp" diff --git a/dev/SocketsBase/network_interface.hpp b/dev/SocketsBase/network_interface.hpp index 5551caee7..8221a83d2 100644 --- a/dev/SocketsBase/network_interface.hpp +++ b/dev/SocketsBase/network_interface.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef NETWORK_INTERFACE_H #define NETWORK_INTERFACE_H diff --git a/dev/SocketsBase/network_manager.cpp b/dev/SocketsBase/network_manager.cpp index ef28e4b18..cdf4530c1 100644 --- a/dev/SocketsBase/network_manager.cpp +++ b/dev/SocketsBase/network_manager.cpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "network_manager.hpp" #include "protocols/hide_public_address.hpp" @@ -23,10 +41,9 @@ void* protocolManagerUpdate(void* data) NetworkManager::NetworkManager() { - m_publicAddress.ip = 0; - m_publicAddress.port = 0; - m_networkManager = NULL; - m_protocolManagerUpdateThread = NULL; + m_public_address.ip = 0; + m_public_address.port = 0; + m_protocol_manager_update_thread = NULL; } NetworkManager::~NetworkManager() @@ -36,16 +53,16 @@ NetworkManager::~NetworkManager() void NetworkManager::run() { ProtocolManager::getInstance(); - m_protocolManagerUpdateThread = (pthread_t*)(malloc(sizeof(pthread_t))); - pthread_create(m_protocolManagerUpdateThread, NULL, protocolManagerUpdate, ProtocolManager::getInstance()); + m_protocol_manager_update_thread = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_protocol_manager_update_thread, NULL, protocolManagerUpdate, ProtocolManager::getInstance()); } -bool NetworkManager::connect(uint32_t ip, uint16_t port) +bool NetworkManager::connect(TransportAddress peer) { - if (peerExists(ip, port)) - return isConnectedTo(ip, port); + if (peerExists(peer)) + return isConnectedTo(peer); - return STKPeer::connectToHost(m_localhost, ip, port, 2, 0); + return STKPeer::connectToHost(m_localhost, peer, 2, 0); } void NetworkManager::setManualSocketsMode(bool manual) @@ -90,24 +107,23 @@ void NetworkManager::notifyEvent(Event* event) void NetworkManager::setLogin(std::string username, std::string password) { - m_playerLogin.username = username; - m_playerLogin.password = password; + m_player_login.username = username; + m_player_login.password = password; } -void NetworkManager::setPublicAddress(uint32_t ip, uint16_t port) +void NetworkManager::setPublicAddress(TransportAddress addr) { - m_publicAddress.ip = ip; - m_publicAddress.port = port; + m_public_address = addr; } -bool NetworkManager::peerExists(uint32_t ip, uint16_t port) +bool NetworkManager::peerExists(TransportAddress peer) { - return m_localhost->peerExists(ip, port); + return m_localhost->peerExists(peer); } -bool NetworkManager::isConnectedTo(uint32_t ip, uint16_t port) +bool NetworkManager::isConnectedTo(TransportAddress peer) { - return m_localhost->isConnectedTo(ip, port); + return m_localhost->isConnectedTo(peer); } STKHost* NetworkManager::getHost() diff --git a/dev/SocketsBase/network_manager.hpp b/dev/SocketsBase/network_manager.hpp index 426b7ddc2..2fcbeb28e 100644 --- a/dev/SocketsBase/network_manager.hpp +++ b/dev/SocketsBase/network_manager.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef NETWORKMANAGER_HPP #define NETWORKMANAGER_HPP @@ -17,18 +35,18 @@ class NetworkManager : public Singleton virtual void run(); // network management functions - virtual bool connect(uint32_t ip, uint16_t port); + virtual bool connect(TransportAddress peer); virtual void setManualSocketsMode(bool manual); virtual void notifyEvent(Event* event); virtual void packetReceived(char* data) = 0; // raw data management void setLogin(std::string username, std::string password); - void setPublicAddress(uint32_t ip, uint16_t port); + void setPublicAddress(TransportAddress addr); // getters - virtual bool peerExists(uint32_t ip, uint16_t port); - virtual bool isConnectedTo(uint32_t ip, uint16_t port); + virtual bool peerExists(TransportAddress peer); + virtual bool isConnectedTo(TransportAddress peer); STKHost* getHost(); std::vector getPeers(); protected: @@ -39,11 +57,10 @@ class NetworkManager : public Singleton std::vector m_peers; STKHost* m_localhost; - TransportAddress m_publicAddress; - PlayerLogin m_playerLogin; + TransportAddress m_public_address; + PlayerLogin m_player_login; - NetworkManager* m_networkManager; - pthread_t* m_protocolManagerUpdateThread; + pthread_t* m_protocol_manager_update_thread; }; #endif // NETWORKMANAGER_HPP diff --git a/dev/SocketsBase/protocol.cpp b/dev/SocketsBase/protocol.cpp index 9f2933e87..37abf4d52 100644 --- a/dev/SocketsBase/protocol.cpp +++ b/dev/SocketsBase/protocol.cpp @@ -1,8 +1,26 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "protocol.hpp" -Protocol::Protocol(CallbackObject* callbackObject, PROTOCOL_TYPE type) +Protocol::Protocol(CallbackObject* callback_object, PROTOCOL_TYPE type) { - m_callbackObject = callbackObject; + m_callback_object = callback_object; m_type = type; } @@ -12,11 +30,11 @@ Protocol::~Protocol() void Protocol::pause() { - m_listener->pauseProtocol(this); + m_listener->requestPause(this); } void Protocol::unpause() { - m_listener->unpauseProtocol(this); + m_listener->requestUnpause(this); } diff --git a/dev/SocketsBase/protocol.hpp b/dev/SocketsBase/protocol.hpp index 20e8652a2..eda60ad78 100644 --- a/dev/SocketsBase/protocol.hpp +++ b/dev/SocketsBase/protocol.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef PROTOCOL_HPP #define PROTOCOL_HPP @@ -6,33 +24,82 @@ #include +/** \enum PROTOCOL_TYPE + * \brief The types that protocols can have. This is used to select which protocol receives which event. + * \ingroup network + */ enum PROTOCOL_TYPE { - PROTOCOL_NONE = 0, - PROTOCOL_CONNECTION = 1, - PROTOCOL_SILENT = 0xffff // used for protocols that do not subscribe to any network event. + PROTOCOL_NONE = 0, //!< No protocol type assigned. + PROTOCOL_CONNECTION = 1, //!< Protocol that deals with client-server connection. + PROTOCOL_LOBBY_ROOM = 2, //!< Protocol that is used during the lobby room phase. + PROTOCOL_SILENT = 0xffff //!< Used for protocols that do not subscribe to any network event. }; +/** \class Protocol + * \brief Abstract class used to define the global protocol functions. + * A protocol is an entity that is started at a point, and that is updated by a thread. + * A protocol can be terminated by an other class, or it can terminate itself if has fulfilled its role. + * This class must be inherited to make any network job. + * \ingroup network + */ class Protocol { public: - Protocol(CallbackObject* callbackObject, PROTOCOL_TYPE type); + /*! + * \brief Constructor + * + * Sets the basic protocol parameters, as the callback object and the protocol type. + * + * \param callback_object The callback object that will be used by the protocol. Protocols that do not use callback objects must set it to NULL. + * \param type The type of the protocol. + */ + Protocol(CallbackObject* callback_object, PROTOCOL_TYPE type); + /*! + * \brief Destructor + */ virtual ~Protocol(); + /*! + * \brief Notify a protocol matching the Event type of that event. + * \param event : Pointer to the event. + */ virtual void notifyEvent(Event* event) = 0; + /*! + * \brief Set the protocol listener. + * \param listener : Pointer to the listener. + */ void setListener(ProtocolManager* listener); + /*! + * \brief Called when the protocol is going to start. Must be re-defined by subclasses. + */ virtual void setup() = 0; + /*! + * \brief Called when the protocol is paused (by an other entity or by itself). + * This function must be called by the subclasse's pause function if re-defined. + */ virtual void pause(); + /*! + * \brief Called when the protocol is unpaused. + * This function must be called by the subclasse's unpause function if re-defined. + */ virtual void unpause(); + /*! + * \brief Called by the protocol listener as often as possible. Must be re-defined. + */ virtual void update() = 0; + /*! + * \brief Method to get a protocol's type. + * \return The protocol type. + */ PROTOCOL_TYPE getProtocolType(); protected: - ProtocolManager* m_listener; - PROTOCOL_TYPE m_type; - CallbackObject* m_callbackObject; + ProtocolManager* m_listener; //!< The protocol listener + PROTOCOL_TYPE m_type; //!< The type of the protocol + CallbackObject* m_callback_object; //!< The callback object, if needed }; #endif // PROTOCOL_HPP diff --git a/dev/SocketsBase/protocol_manager.cpp b/dev/SocketsBase/protocol_manager.cpp index c26cc6ec8..036a44886 100644 --- a/dev/SocketsBase/protocol_manager.cpp +++ b/dev/SocketsBase/protocol_manager.cpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "protocol_manager.hpp" #include "protocol.hpp" @@ -10,8 +28,11 @@ ProtocolManager::ProtocolManager() { - pthread_mutex_init(&m_eventsMutex, NULL); - pthread_mutex_init(&m_protocolsMutex, NULL); + pthread_mutex_init(&m_events_mutex, NULL); + pthread_mutex_init(&m_protocols_mutex, NULL); + pthread_mutex_init(&m_requests_mutex, NULL); + pthread_mutex_init(&m_id_mutex, NULL); + m_next_protocol_id = 0; } ProtocolManager::~ProtocolManager() @@ -20,9 +41,9 @@ ProtocolManager::~ProtocolManager() void ProtocolManager::notifyEvent(Event* event) { - pthread_mutex_lock(&m_eventsMutex); - m_eventsToProcess.push_back(event); - pthread_mutex_unlock(&m_eventsMutex); + pthread_mutex_lock(&m_events_mutex); + m_events_to_process.push_back(event); // add the event to the queue + pthread_mutex_unlock(&m_events_mutex); } void ProtocolManager::sendMessage(std::string message) @@ -31,60 +52,116 @@ void ProtocolManager::sendMessage(std::string message) newMessage[0] = (char)(0); } -int ProtocolManager::startProtocol(Protocol* protocol) +int ProtocolManager::requestStart(Protocol* protocol) { - ProtocolInfo protocolInfo; - protocolInfo.state = PROTOCOL_STATE_RUNNING; - assignProtocolId(protocolInfo); - protocolInfo.protocol = protocol; - printf("__ProtocolManager> A new protocol with id=%u has been started. There are %ld protocols running.\n", protocolInfo.id, m_protocols.size()+1); + // create the request + ProtocolRequest req; + ProtocolInfo info; + info.protocol = protocol; + info.state = PROTOCOL_STATE_RUNNING; + assignProtocolId(&info); // assign a unique id to the protocol. + req.protocol_info = info; + req.type = PROTOCOL_REQUEST_START; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); - pthread_mutex_lock(&m_protocolsMutex); - m_protocols.push_back(protocolInfo); - pthread_mutex_unlock(&m_protocolsMutex); - - protocol->setListener(this); - protocol->setup(); - - return protocolInfo.id; + return info.id; } -void ProtocolManager::stopProtocol(Protocol* protocol) + +void ProtocolManager::requestStop(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_STOP; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestPause(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_PAUSE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestUnpause(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_UNPAUSE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestTerminate(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_TERMINATE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::startProtocol(ProtocolInfo protocol) +{ + printf("__ProtocolManager> A new protocol with id=%u has been started. There are %ld protocols running.\n", protocol.id, m_protocols.size()+1); + // add the protocol to the protocol vector so that it's updated + pthread_mutex_lock(&m_protocols_mutex); + m_protocols.push_back(protocol); + pthread_mutex_unlock(&m_protocols_mutex); + // setup the protocol and notify it that it's started + protocol.protocol->setListener(this); + protocol.protocol->setup(); +} +void ProtocolManager::stopProtocol(ProtocolInfo protocol) { } -void ProtocolManager::pauseProtocol(Protocol* protocol) +void ProtocolManager::pauseProtocol(ProtocolInfo protocol) { for (unsigned int i = 0; i < m_protocols.size(); i++) { - if (m_protocols[i].protocol == protocol && m_protocols[i].state == PROTOCOL_STATE_RUNNING) + if (m_protocols[i].protocol == protocol.protocol && m_protocols[i].state == PROTOCOL_STATE_RUNNING) { - pthread_mutex_lock(&m_protocolsMutex); m_protocols[i].state = PROTOCOL_STATE_PAUSED; m_protocols[i].protocol->pause(); - pthread_mutex_unlock(&m_protocolsMutex); } } } -void ProtocolManager::unpauseProtocol(Protocol* protocol) +void ProtocolManager::unpauseProtocol(ProtocolInfo protocol) { for (unsigned int i = 0; i < m_protocols.size(); i++) { - if (m_protocols[i].protocol == protocol && m_protocols[i].state == PROTOCOL_STATE_PAUSED) + if (m_protocols[i].protocol == protocol.protocol && m_protocols[i].state == PROTOCOL_STATE_PAUSED) { - pthread_mutex_lock(&m_protocolsMutex); m_protocols[i].state = PROTOCOL_STATE_RUNNING; m_protocols[i].protocol->unpause(); - pthread_mutex_unlock(&m_protocolsMutex); } } } -void ProtocolManager::protocolTerminated(Protocol* protocol) +void ProtocolManager::protocolTerminated(ProtocolInfo protocol) { - pthread_mutex_lock(&m_protocolsMutex); + pthread_mutex_lock(&m_protocols_mutex); // be sure that noone accesses the protocols vector while we erase a protocol int offset = 0; for (unsigned int i = 0; i < m_protocols.size(); i++) { - if (m_protocols[i-offset].protocol == protocol) + if (m_protocols[i-offset].protocol == protocol.protocol) { delete m_protocols[i].protocol; m_protocols.erase(m_protocols.begin()+(i-offset), m_protocols.begin()+(i-offset)+1); @@ -92,17 +169,17 @@ void ProtocolManager::protocolTerminated(Protocol* protocol) } } printf("__ProtocolManager> A protocol has been terminated. There are %ld protocols running.\n", m_protocols.size()); - pthread_mutex_unlock(&m_protocolsMutex); + pthread_mutex_unlock(&m_protocols_mutex); } void ProtocolManager::update() { // before updating, notice protocols that they have received information - pthread_mutex_lock(&m_eventsMutex); // secure threads - int size = m_eventsToProcess.size(); + pthread_mutex_lock(&m_events_mutex); // secure threads + int size = m_events_to_process.size(); for (int i = 0; i < size; i++) { - Event* event = m_eventsToProcess.back(); + Event* event = m_events_to_process.back(); PROTOCOL_TYPE searchedProtocol = PROTOCOL_NONE; if (event->data.size() > 0) @@ -113,15 +190,44 @@ void ProtocolManager::update() m_protocols[i].protocol->notifyEvent(event); } delete event; - m_eventsToProcess.pop_back(); + m_events_to_process.pop_back(); } - pthread_mutex_unlock(&m_eventsMutex); // release the mutex + pthread_mutex_unlock(&m_events_mutex); // release the mutex + // now update all protocols + pthread_mutex_lock(&m_protocols_mutex); for (unsigned int i = 0; i < m_protocols.size(); i++) { if (m_protocols[i].state == PROTOCOL_STATE_RUNNING) m_protocols[i].protocol->update(); } + pthread_mutex_unlock(&m_protocols_mutex); + + // process queued events for protocols + pthread_mutex_lock(&m_requests_mutex); + for (unsigned int i = 0; i < m_requests.size(); i++) + { + switch (m_requests[i].type) + { + case PROTOCOL_REQUEST_START: + startProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_STOP: + stopProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_PAUSE: + pauseProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_UNPAUSE: + unpauseProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_TERMINATE: + protocolTerminated(m_requests[i].protocol_info); + break; + } + } + m_requests.clear(); + pthread_mutex_unlock(&m_requests_mutex); } int ProtocolManager::runningProtocolsCount() @@ -133,30 +239,49 @@ PROTOCOL_STATE ProtocolManager::getProtocolState(uint32_t id) { for (unsigned int i = 0; i < m_protocols.size(); i++) { - if (m_protocols[i].id == id) - { - PROTOCOL_STATE state = m_protocols[i].state; - return state; - } + if (m_protocols[i].id == id) // we know a protocol with that id + return m_protocols[i].state; // return its state } - return PROTOCOL_STATE_TERMINATED; -} - -void ProtocolManager::assignProtocolId(ProtocolInfo& protocolInfo) -{ - uint32_t newId; - bool exists; - do + // the protocol isn't running right now + for (unsigned int i = 0; i < m_requests.size(); i++) { - newId = (rand()<<16)+rand(); - exists = false; - for (unsigned int i = 0; i < m_protocols.size(); i++) - { - if (m_protocols[i].id == newId) - exists = true; - } - } while (exists); - protocolInfo.id = newId; + if (m_requests[i].protocol_info.id == id) // the protocol is going to be started + return PROTOCOL_STATE_RUNNING; // we can say it's running + } + return PROTOCOL_STATE_TERMINATED; // else, it's already finished +} + +PROTOCOL_STATE ProtocolManager::getProtocolState(Protocol* protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol) // the protocol is known + return m_protocols[i].state; // return its state + } + for (unsigned int i = 0; i < m_requests.size(); i++) + { + if (m_requests[i].protocol_info.protocol == protocol) // the protocol is going to be started + return PROTOCOL_STATE_RUNNING; // we can say it's running + } + return PROTOCOL_STATE_TERMINATED; // we don't know this protocol at all, it's finished +} + +int ProtocolManager::getProtocolID(Protocol* protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol) + return m_protocols[i].id; + } + return 0; +} + +void ProtocolManager::assignProtocolId(ProtocolInfo* protocol_info) +{ + pthread_mutex_lock(&m_id_mutex); + protocol_info->id = m_next_protocol_id; + m_next_protocol_id++; + pthread_mutex_unlock(&m_id_mutex); } diff --git a/dev/SocketsBase/protocol_manager.hpp b/dev/SocketsBase/protocol_manager.hpp index 699e2b347..a8abdf078 100644 --- a/dev/SocketsBase/protocol_manager.hpp +++ b/dev/SocketsBase/protocol_manager.hpp @@ -1,3 +1,25 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/*! \file protocol_manager.hpp + * \brief Contains structures and enumerations related to protocol management. + */ + #ifndef PROTOCOL_MANAGER_HPP #define PROTOCOL_MANAGER_HPP @@ -9,51 +31,236 @@ class Protocol; +/*! + * \enum PROTOCOL_STATE + * \brief Defines the three states that a protocol can have. + */ enum PROTOCOL_STATE { - PROTOCOL_STATE_RUNNING, - PROTOCOL_STATE_PAUSED, - PROTOCOL_STATE_TERMINATED + PROTOCOL_STATE_RUNNING, //!< The protocol is being updated everytime. + PROTOCOL_STATE_PAUSED, //!< The protocol is paused. + PROTOCOL_STATE_TERMINATED //!< The protocol is terminated/does not exist. }; +/*! + * \enum PROTOCOL_REQUEST_TYPE + * \brief Defines actions that can be done about protocols. + * This enum is used essentially to keep the manager thread-safe and + * to avoid protocols modifying directly their state. + */ +enum PROTOCOL_REQUEST_TYPE +{ + PROTOCOL_REQUEST_START, //!< Start a protocol + PROTOCOL_REQUEST_STOP, //!< Stop a protocol + PROTOCOL_REQUEST_PAUSE, //!< Pause a protocol + PROTOCOL_REQUEST_UNPAUSE, //!< Unpause a protocol + PROTOCOL_REQUEST_TERMINATE //!< Terminate a protocol +}; + +/*! +* \struct ProtocolInfo +* \brief Stores the information needed to manage protocols +*/ +typedef struct ProtocolInfo +{ + PROTOCOL_STATE state; //!< The state of the protocol + Protocol* protocol; //!< A pointer to the protocol + uint32_t id; //!< The unique id of the protocol +} ProtocolInfo; + +/*! +* \struct ProtocolRequest +* \brief Represents a request to do an action about a protocol. +*/ +typedef struct ProtocolRequest +{ + PROTOCOL_REQUEST_TYPE type; //!< The type of request + ProtocolInfo protocol_info; //!< The concerned protocol information +} ProtocolRequest; + +/*! + * \class ProtocolManager + * \brief Manages the protocols at runtime. + * + * This class is in charge of storing and managing protocols. + * It is a singleton as there can be only one protocol manager per game + * instance. Any game object that wants to start a protocol must create a + * protocol and give it to this singleton. The protocols are updated in a + * special thread, to ensure that they are processed independently from the + * frames per second. Then, the management of protocols is thread-safe: any + * object can start/pause/stop protocols whithout problems. + */ class ProtocolManager : public Singleton { friend class Singleton; - typedef struct - { - PROTOCOL_STATE state; - Protocol* protocol; - uint32_t id; - } ProtocolInfo; + public: + /*! + * \brief Function that processes incoming events. + * This function is called by the network manager each time there is an + * incoming packet. + */ + virtual void notifyEvent(Event* event); + /*! + * \brief WILL BE COMMENTED LATER + */ + virtual void sendMessage(std::string message); - virtual void notifyEvent(Event* event); - virtual void sendMessage(std::string message); - - virtual int startProtocol(Protocol* protocol); - virtual void stopProtocol(Protocol* protocol); - virtual void pauseProtocol(Protocol* protocol); - virtual void unpauseProtocol(Protocol* protocol); - virtual void protocolTerminated(Protocol* protocol); + /*! + * \brief Asks the manager to start a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to start + * \return The unique id of the protocol that is being started. + */ + virtual int requestStart(Protocol* protocol); + /*! + * \brief Asks the manager to stop a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to stop + */ + virtual void requestStop(Protocol* protocol); + /*! + * \brief Asks the manager to pause a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to pause + */ + virtual void requestPause(Protocol* protocol); + /*! + * \brief Asks the manager to unpause a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to unpause + */ + virtual void requestUnpause(Protocol* protocol); + /*! + * \brief Notifies the manager that a protocol is terminated. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol that is finished + */ + virtual void requestTerminate(Protocol* protocol); - virtual void update(); + /*! + * \brief Updates the manager. + * + * This function processes the events queue, notifies the concerned + * protocols that they have events to process. Then ask all protocols + * to update themselves. Finally processes stored requests about + * starting, stoping, pausing etc... protocols. + * This function is called by a thread as often as possible. + * This function is not FPS-dependant. + */ + virtual void update(); - virtual int runningProtocolsCount(); - virtual PROTOCOL_STATE getProtocolState(uint32_t id); + /*! + * \brief Get the number of protocols running. + * \return The number of protocols that are actually running. + */ + virtual int runningProtocolsCount(); + /*! + * \brief Get the state of a protocol using its id. + * \param id : The id of the protocol you seek the state. + * \return The state of the protocol. + */ + virtual PROTOCOL_STATE getProtocolState(uint32_t id); + /*! + * \brief Get the state of a protocol using a pointer on it. + * \param protocol : A pointer to the protocol you seek the state. + * \return The state of the protocol. + */ + virtual PROTOCOL_STATE getProtocolState(Protocol* protocol); + /*! + * \brief Get the id of a protocol. + * \param protocol : A pointer to the protocol you seek the id. + * \return The id of the protocol pointed by the protocol parameter. + */ + virtual int getProtocolID(Protocol* protocol); protected: // protected functions + /*! + * \brief Constructor + */ ProtocolManager(); + /*! + * \brief Destructor + */ virtual ~ProtocolManager(); - void assignProtocolId(ProtocolInfo& protocolInfo); + /*! + * \brief Assign an id to a protocol. + * This function will assign m_next_protocol_id as the protocol id. + * This id starts at 0 at the beginning and is increased by 1 each time + * a protocol starts. + * \param protocol_info : The protocol info that needs an id. + */ + void assignProtocolId(ProtocolInfo* protocol_info); + + /*! + * \brief Starts a protocol. + * Add the protocol info to the m_protocols vector. + * \param protocol : ProtocolInfo to start. + */ + virtual void startProtocol(ProtocolInfo protocol); + /*! + * \brief Stops a protocol. + * Coes nothing. Noone can stop running protocols for now. + * \param protocol : ProtocolInfo to stop. + */ + virtual void stopProtocol(ProtocolInfo protocol); + /*! + * \brief Pauses a protocol. + * Pauses a protocol and tells it that it's being paused. + * \param protocol : ProtocolInfo to pause. + */ + virtual void pauseProtocol(ProtocolInfo protocol); + /*! + * \brief Unpauses a protocol. + * Unpauses a protocol and notifies it. + * \param protocol : ProtocolInfo to unpause. + */ + virtual void unpauseProtocol(ProtocolInfo protocol); + /*! + * \brief Notes that a protocol is terminated. + * Remove a protocol from the protocols vector. + * \param protocol : ProtocolInfo concerned. + */ + virtual void protocolTerminated(ProtocolInfo protocol); // protected members - std::vector m_protocols; - std::vector m_eventsToProcess; + /*! + * \brief Contains the running protocols. + * This stores the protocols that are either running or paused, their + * state and their unique id. + */ + std::vector m_protocols; + /*! + * \brief Contains the network events to pass to protocols. + */ + std::vector m_events_to_process; + /*! + * \brief Contains the requests to start/stop etc... protocols. + */ + std::vector m_requests; + /*! \brief The next id to assign to a protocol. + * This value is incremented by 1 each time a protocol is started. + * If a protocol has an id lower than this value, it means that it have + * been formerly started. + */ + unsigned int m_next_protocol_id; // mutexes: - pthread_mutex_t m_eventsMutex; - pthread_mutex_t m_protocolsMutex; + /*! Used to ensure that the event queue is used thread-safely. */ + pthread_mutex_t m_events_mutex; + /*! Used to ensure that the protocol vector is used thread-safely. */ + pthread_mutex_t m_protocols_mutex; + /*! Used to ensure that the request vector is used thread-safely. */ + pthread_mutex_t m_requests_mutex; + /*! Used to ensure that the protocol id is used in a thread-safe way.*/ + pthread_mutex_t m_id_mutex; + }; #endif // PROTOCOL_MANAGER_HPP diff --git a/dev/SocketsBase/protocols/connect_to_server.cpp b/dev/SocketsBase/protocols/connect_to_server.cpp index 8bae3c8de..7d2529ad7 100644 --- a/dev/SocketsBase/protocols/connect_to_server.cpp +++ b/dev/SocketsBase/protocols/connect_to_server.cpp @@ -1,53 +1,89 @@ -#include "connect_to_server.hpp" +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "../client_network_manager.hpp" -#include "../time.hpp" +#include "protocols/connect_to_server.hpp" + +#include "client_network_manager.hpp" +#include "time.hpp" #include #include -ConnectToServer::ConnectToServer(CallbackObject* callbackObject) : Protocol(callbackObject, PROTOCOL_CONNECTION) +// ---------------------------------------------------------------------------- + +ConnectToServer::ConnectToServer(CallbackObject* callback_object) : + Protocol(callback_object, PROTOCOL_CONNECTION) { - m_serverIp = 0; - m_serverPort = 0; + m_server_ip = 0; + m_server_port = 0; m_state = NONE; } +// ---------------------------------------------------------------------------- + ConnectToServer::~ConnectToServer() { } +// ---------------------------------------------------------------------------- + void ConnectToServer::notifyEvent(Event* event) { - if (event->type == EVENT_TYPE_CONNECTED && event->peer->getAddress() == m_serverIp && event->peer->getPort() == m_serverPort) + if (event->type == EVENT_TYPE_CONNECTED && + event->peer->getAddress() == m_server_ip && + event->peer->getPort() == m_server_port) { - printf("The Connect To Server protocol has received an event notifying that he's connected to the peer. The peer sent \"%s\"\n", event->data.c_str()); + printf("The Connect To Server protocol has received an event notifying \ + that he's connected to the peer. The peer sent \"%s\"\n", + event->data.c_str()); m_state = DONE; // we received a message, we are connected } } +// ---------------------------------------------------------------------------- + void ConnectToServer::setup() { m_state = NONE; - if (m_serverIp == 0 || m_serverPort == 0 ) + if (m_server_ip == 0 || m_server_port == 0 ) { printf("You have to set the server's public ip:port of the server.\n"); - m_listener->protocolTerminated(this); + m_listener->requestTerminate(this); } } +// ---------------------------------------------------------------------------- + void ConnectToServer::update() { if (m_state == NONE) { static double target = 0; double currentTime = Time::getSeconds(); - while (currentTime < target-1800) // sometimes the getSeconds method forgets 3600 seconds. + // sometimes the getSeconds method forgets 3600 seconds. + while (currentTime < target-1800) currentTime += 3600; if (currentTime > target) { - NetworkManager::getInstance()->connect(m_serverIp, m_serverPort); - if (NetworkManager::getInstance()->isConnectedTo(m_serverIp, m_serverPort)) + NetworkManager::getInstance()->connect( + TransportAddress(m_server_ip, m_server_port)); + if (NetworkManager::getInstance()->isConnectedTo( + TransportAddress(m_server_ip, m_server_port))) { m_state = DONE; return; @@ -58,13 +94,17 @@ void ConnectToServer::update() } else if (m_state == DONE) { - m_listener->protocolTerminated(this); + m_listener->requestTerminate(this); } } +// ---------------------------------------------------------------------------- + void ConnectToServer::setServerAddress(uint32_t ip, uint16_t port) { - m_serverIp = ip; - m_serverPort = port; + m_server_ip = ip; + m_server_port = port; } +// ---------------------------------------------------------------------------- + diff --git a/dev/SocketsBase/protocols/connect_to_server.hpp b/dev/SocketsBase/protocols/connect_to_server.hpp index 3b6706bb3..e3908ded9 100644 --- a/dev/SocketsBase/protocols/connect_to_server.hpp +++ b/dev/SocketsBase/protocols/connect_to_server.hpp @@ -1,13 +1,31 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef CONNECT_TO_SERVER_HPP #define CONNECT_TO_SERVER_HPP -#include "../protocol.hpp" +#include "protocol.hpp" #include class ConnectToServer : public Protocol, public CallbackObject { public: - ConnectToServer(CallbackObject* callbackObject); + ConnectToServer(CallbackObject* callback_object); virtual ~ConnectToServer(); virtual void notifyEvent(Event* event); @@ -17,8 +35,8 @@ class ConnectToServer : public Protocol, public CallbackObject void setServerAddress(uint32_t ip, uint16_t port); protected: - uint32_t m_serverIp; - uint16_t m_serverPort; + uint32_t m_server_ip; + uint16_t m_server_port; enum STATE { diff --git a/dev/SocketsBase/protocols/get_peer_address.cpp b/dev/SocketsBase/protocols/get_peer_address.cpp index 495576271..3dc8d265e 100644 --- a/dev/SocketsBase/protocols/get_peer_address.cpp +++ b/dev/SocketsBase/protocols/get_peer_address.cpp @@ -1,12 +1,30 @@ -#include "get_peer_address.hpp" +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "../time.hpp" -#include "../http_functions.hpp" +#include "protocols/get_peer_address.hpp" + +#include "time.hpp" +#include "http_functions.hpp" #include #include -GetPeerAddress::GetPeerAddress(CallbackObject* callbackObject) : Protocol(callbackObject, PROTOCOL_SILENT) +GetPeerAddress::GetPeerAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) { } @@ -28,13 +46,13 @@ void GetPeerAddress::update() if (m_state == NONE) { static double target = 0; - double currentTime = Time::getSeconds(); - while (currentTime < target-1800) // sometimes the getSeconds method forgets 3600 seconds. - currentTime += 3600; - if (currentTime > target) + double current_time = Time::getSeconds(); + while (current_time < target-1800) // sometimes the getSeconds method forgets 3600 seconds. + current_time += 3600; + if (current_time > target) { char url[512]; - sprintf(url, "http://stkconnect.freeserver.me/log.php?get&nick=%s", m_peerName.c_str()); + sprintf(url, "http://stkconnect.freeserver.me/log.php?get&nick=%s", m_peer_name.c_str()); std::string result = HTTP::getPage(url); if (result == "") { @@ -42,39 +60,39 @@ void GetPeerAddress::update() pause(); return; } - std::string ipAddr = result; - ipAddr.erase(ipAddr.find_first_of(':')); - std::string portNb = result; - portNb.erase(0, portNb.find_first_of(':')+1); - uint32_t dstIp = (uint32_t)(atoi(ipAddr.c_str())); - uint16_t dstPort = (uint32_t)(atoi(portNb.c_str())); - if (dstIp == 0 || dstPort == 0) + std::string ip_addr = result; + ip_addr.erase(ip_addr.find_first_of(':')); + std::string port_nb = result; + port_nb.erase(0, port_nb.find_first_of(':')+1); + uint32_t dst_ip = (uint32_t)(atoi(ip_addr.c_str())); + uint16_t dst_port = (uint32_t)(atoi(port_nb.c_str())); + if (dst_ip == 0 || dst_port == 0) { printf("__GetPeerAddress> The host you try to reach is not online. There will be a new try in 10 seconds.\n"); - target = currentTime+10; + target = current_time+10; } else { - printf("__GetPeerAddress> Public ip of target is %i.%i.%i.%i:%i\n", (dstIp>>24)&0xff, (dstIp>>16)&0xff, (dstIp>>8)&0xff, dstIp&0xff, dstPort); - uint32_t serverIp = ((dstIp&0x000000ff)<<24) // change the server IP to have a network-byte order - + ((dstIp&0x0000ff00)<<8) - + ((dstIp&0x00ff0000)>>8) - + ((dstIp&0xff000000)>>24); - uint16_t serverPort = dstPort; - TransportAddress* addr = static_cast(m_callbackObject); - addr->ip = serverIp; - addr->port = serverPort; + printf("__GetPeerAddress> Public ip of target is %i.%i.%i.%i:%i\n", (dst_ip>>24)&0xff, (dst_ip>>16)&0xff, (dst_ip>>8)&0xff, dst_ip&0xff, dst_port); + uint32_t server_ip = ((dst_ip&0x000000ff)<<24) // change the server IP to have a network-byte order + + ((dst_ip&0x0000ff00)<<8) + + ((dst_ip&0x00ff0000)>>8) + + ((dst_ip&0xff000000)>>24); + uint16_t server_port = dst_port; + TransportAddress* addr = static_cast(m_callback_object); + addr->ip = server_ip; + addr->port = server_port; m_state = DONE; } } } else if (m_state == DONE) { - m_listener->protocolTerminated(this); + m_listener->requestTerminate(this); } } -void GetPeerAddress::setPeerName(std::string peerName) +void GetPeerAddress::setPeerName(std::string peer_name) { - m_peerName = peerName; + m_peer_name = peer_name; } diff --git a/dev/SocketsBase/protocols/get_peer_address.hpp b/dev/SocketsBase/protocols/get_peer_address.hpp index 28b007d4e..29bd53430 100644 --- a/dev/SocketsBase/protocols/get_peer_address.hpp +++ b/dev/SocketsBase/protocols/get_peer_address.hpp @@ -1,21 +1,39 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef GET_PEER_ADDRESS_HPP #define GET_PEER_ADDRESS_HPP -#include "../protocol.hpp" +#include "protocol.hpp" class GetPeerAddress : public Protocol { public: - GetPeerAddress(CallbackObject* callbackObject); + GetPeerAddress(CallbackObject* callback_object); virtual ~GetPeerAddress(); virtual void notifyEvent(Event* event); virtual void setup(); virtual void update(); - void setPeerName(std::string peerName); + void setPeerName(std::string peer_name); protected: - std::string m_peerName; + std::string m_peer_name; enum STATE { diff --git a/dev/SocketsBase/protocols/get_public_address.cpp b/dev/SocketsBase/protocols/get_public_address.cpp index a64f5b37b..f79ad0c15 100644 --- a/dev/SocketsBase/protocols/get_public_address.cpp +++ b/dev/SocketsBase/protocols/get_public_address.cpp @@ -1,9 +1,27 @@ -#include "get_public_address.hpp" +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "../network_manager.hpp" -#include "../client_network_manager.hpp" -#include "connect_to_server.hpp" -#include "../network_interface.hpp" +#include "protocols/get_public_address.hpp" + +#include "network_manager.hpp" +#include "client_network_manager.hpp" +#include "protocols/connect_to_server.hpp" +#include "network_interface.hpp" #include #include @@ -21,7 +39,7 @@ int stunRand() return rand(); } -GetPublicAddress::GetPublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject, PROTOCOL_SILENT) +GetPublicAddress::GetPublicAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) { } @@ -45,9 +63,9 @@ void GetPublicAddress::update() { // format : 00MMMMMCMMMCMMMM (cf rfc 5389) uint16_t message_type = 0b0000000000000001; // binding request - m_stunTransactionID[0] = stunRand(); - m_stunTransactionID[1] = stunRand(); - m_stunTransactionID[2] = stunRand(); + m_stun_tansaction_id[0] = stunRand(); + m_stun_tansaction_id[1] = stunRand(); + m_stun_tansaction_id[2] = stunRand(); uint16_t message_length = 0x0000; uint8_t bytes[21]; // the message to be sent @@ -60,72 +78,72 @@ void GetPublicAddress::update() bytes[3] = (uint8_t)(message_length); // bytes 4-7 : magic cookie to recognize the stun protocol - bytes[4] = (uint8_t)(m_stunMagicCookie>>24); - bytes[5] = (uint8_t)(m_stunMagicCookie>>16); - bytes[6] = (uint8_t)(m_stunMagicCookie>>8); - bytes[7] = (uint8_t)(m_stunMagicCookie); + bytes[4] = (uint8_t)(m_stun_magic_cookie>>24); + bytes[5] = (uint8_t)(m_stun_magic_cookie>>16); + bytes[6] = (uint8_t)(m_stun_magic_cookie>>8); + bytes[7] = (uint8_t)(m_stun_magic_cookie); // bytes 8-19 : the transaction id - bytes[8] = (uint8_t)(m_stunTransactionID[0]>>24); - bytes[9] = (uint8_t)(m_stunTransactionID[0]>>16); - bytes[10] = (uint8_t)(m_stunTransactionID[0]>>8); - bytes[11] = (uint8_t)(m_stunTransactionID[0]); - bytes[12] = (uint8_t)(m_stunTransactionID[1]>>24); - bytes[13] = (uint8_t)(m_stunTransactionID[1]>>16); - bytes[14] = (uint8_t)(m_stunTransactionID[1]>>8); - bytes[15] = (uint8_t)(m_stunTransactionID[1]); - bytes[16] = (uint8_t)(m_stunTransactionID[2]>>24); - bytes[17] = (uint8_t)(m_stunTransactionID[2]>>16); - bytes[18] = (uint8_t)(m_stunTransactionID[2]>>8); - bytes[19] = (uint8_t)(m_stunTransactionID[2]); + bytes[8] = (uint8_t)(m_stun_tansaction_id[0]>>24); + bytes[9] = (uint8_t)(m_stun_tansaction_id[0]>>16); + bytes[10] = (uint8_t)(m_stun_tansaction_id[0]>>8); + bytes[11] = (uint8_t)(m_stun_tansaction_id[0]); + bytes[12] = (uint8_t)(m_stun_tansaction_id[1]>>24); + bytes[13] = (uint8_t)(m_stun_tansaction_id[1]>>16); + bytes[14] = (uint8_t)(m_stun_tansaction_id[1]>>8); + bytes[15] = (uint8_t)(m_stun_tansaction_id[1]); + bytes[16] = (uint8_t)(m_stun_tansaction_id[2]>>24); + bytes[17] = (uint8_t)(m_stun_tansaction_id[2]>>16); + bytes[18] = (uint8_t)(m_stun_tansaction_id[2]>>8); + bytes[19] = (uint8_t)(m_stun_tansaction_id[2]); bytes[20] = '\0'; printf("__GetPublicAddress> Querrying STUN server 132.177.123.6\n"); unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; NetworkManager::getInstance()->setManualSocketsMode(true); - NetworkManager::getInstance()->getHost()->sendRawPacket(bytes, 20, dst, 3478); + NetworkManager::getInstance()->getHost()->sendRawPacket(bytes, 20, TransportAddress(dst, 3478)); m_state = TEST_SENT; } if (m_state == TEST_SENT) { unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; - uint8_t* data = NetworkManager::getInstance()->getHost()->receiveRawPacket(dst, 3478); + uint8_t* data = NetworkManager::getInstance()->getHost()->receiveRawPacket(TransportAddress(dst, 3478)); assert(data); // check that the stun response is a response, contains the magic cookie and the transaction ID if ( data[0] == 0b01 && data[1] == 0b01 && - data[4] == (uint8_t)(m_stunMagicCookie>>24) && - data[5] == (uint8_t)(m_stunMagicCookie>>16) && - data[6] == (uint8_t)(m_stunMagicCookie>>8) && - data[7] == (uint8_t)(m_stunMagicCookie)) + data[4] == (uint8_t)(m_stun_magic_cookie>>24) && + data[5] == (uint8_t)(m_stun_magic_cookie>>16) && + data[6] == (uint8_t)(m_stun_magic_cookie>>8) && + data[7] == (uint8_t)(m_stun_magic_cookie)) { if( - data[8] == (uint8_t)(m_stunTransactionID[0]>>24) && - data[9] == (uint8_t)(m_stunTransactionID[0]>>16) && - data[10] == (uint8_t)(m_stunTransactionID[0]>>8 ) && - data[11] == (uint8_t)(m_stunTransactionID[0] ) && - data[12] == (uint8_t)(m_stunTransactionID[1]>>24) && - data[13] == (uint8_t)(m_stunTransactionID[1]>>16) && - data[14] == (uint8_t)(m_stunTransactionID[1]>>8 ) && - data[15] == (uint8_t)(m_stunTransactionID[1] ) && - data[16] == (uint8_t)(m_stunTransactionID[2]>>24) && - data[17] == (uint8_t)(m_stunTransactionID[2]>>16) && - data[18] == (uint8_t)(m_stunTransactionID[2]>>8 ) && - data[19] == (uint8_t)(m_stunTransactionID[2] )) + data[8] == (uint8_t)(m_stun_tansaction_id[0]>>24) && + data[9] == (uint8_t)(m_stun_tansaction_id[0]>>16) && + data[10] == (uint8_t)(m_stun_tansaction_id[0]>>8 ) && + data[11] == (uint8_t)(m_stun_tansaction_id[0] ) && + data[12] == (uint8_t)(m_stun_tansaction_id[1]>>24) && + data[13] == (uint8_t)(m_stun_tansaction_id[1]>>16) && + data[14] == (uint8_t)(m_stun_tansaction_id[1]>>8 ) && + data[15] == (uint8_t)(m_stun_tansaction_id[1] ) && + data[16] == (uint8_t)(m_stun_tansaction_id[2]>>24) && + data[17] == (uint8_t)(m_stun_tansaction_id[2]>>16) && + data[18] == (uint8_t)(m_stun_tansaction_id[2]>>8 ) && + data[19] == (uint8_t)(m_stun_tansaction_id[2] )) { printf("__GetPublicAddress> The STUN server responded with a valid answer\n"); - int messageSize = data[2]*256+data[3]; + int message_size = data[2]*256+data[3]; // parse the stun message now: bool finish = false; uint8_t* attributes = data+20; - if (messageSize == 0) + if (message_size == 0) { printf("__GetPublicAddress> STUN answer does not contain any information.\n"); finish = true; } - if (messageSize < 4) // cannot even read the size + if (message_size < 4) // cannot even read the size { printf("__GetPublicAddress> STUN message is not valid.\n"); finish = true; @@ -153,10 +171,10 @@ void GetPublicAddress::update() break; } attributes = attributes + 4 + size; - messageSize -= 4 + size; - if (messageSize == 0) + message_size -= 4 + size; + if (message_size == 0) finish = true; - if (messageSize < 4) // cannot even read the size + if (message_size < 4) // cannot even read the size { printf("__GetPublicAddress> STUN message is not valid.\n"); finish = true; @@ -168,7 +186,7 @@ void GetPublicAddress::update() printf("__The public address has been found : %i.%i.%i.%i:%i\n", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); m_state = ADDRESS_KNOWN; NetworkManager::getInstance()->setManualSocketsMode(false); - TransportAddress* addr = static_cast(m_callbackObject); + TransportAddress* addr = static_cast(m_callback_object); addr->ip = address; addr->port = port; } @@ -184,6 +202,6 @@ void GetPublicAddress::update() if (m_state == ADDRESS_KNOWN) { // terminate the protocol - m_listener->protocolTerminated(this); + m_listener->requestTerminate(this); } } diff --git a/dev/SocketsBase/protocols/get_public_address.hpp b/dev/SocketsBase/protocols/get_public_address.hpp index acafd8d02..42992dabb 100644 --- a/dev/SocketsBase/protocols/get_public_address.hpp +++ b/dev/SocketsBase/protocols/get_public_address.hpp @@ -1,12 +1,30 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef GET_PUBLIC_ADDRESS_HPP #define GET_PUBLIC_ADDRESS_HPP -#include "../protocol.hpp" +#include "protocol.hpp" class GetPublicAddress : public Protocol { public: - GetPublicAddress(CallbackObject* callbackObject); + GetPublicAddress(CallbackObject* callback_object); virtual ~GetPublicAddress(); virtual void notifyEvent(Event* event); @@ -22,8 +40,8 @@ class GetPublicAddress : public Protocol ADDRESS_KNOWN }; STATE m_state; - uint32_t m_stunTransactionID[3]; - static const uint32_t m_stunMagicCookie = 0x2112A442; + uint32_t m_stun_tansaction_id[3]; + static const uint32_t m_stun_magic_cookie = 0x2112A442; }; #endif // GET_PUBLIC_ADDRESS_HPP diff --git a/dev/SocketsBase/protocols/hide_public_address.cpp b/dev/SocketsBase/protocols/hide_public_address.cpp index 2ad17417c..0e9f431ff 100644 --- a/dev/SocketsBase/protocols/hide_public_address.cpp +++ b/dev/SocketsBase/protocols/hide_public_address.cpp @@ -1,10 +1,28 @@ -#include "hide_public_address.hpp" +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "../http_functions.hpp" +#include "protocols/hide_public_address.hpp" + +#include "http_functions.hpp" #include -HidePublicAddress::HidePublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject, PROTOCOL_SILENT) +HidePublicAddress::HidePublicAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) { } @@ -42,7 +60,7 @@ void HidePublicAddress::update() } else if (m_state == DONE) { - m_listener->protocolTerminated(this); + m_listener->requestTerminate(this); } } diff --git a/dev/SocketsBase/protocols/hide_public_address.hpp b/dev/SocketsBase/protocols/hide_public_address.hpp index 9848e794d..54c8ce9a0 100644 --- a/dev/SocketsBase/protocols/hide_public_address.hpp +++ b/dev/SocketsBase/protocols/hide_public_address.hpp @@ -1,13 +1,31 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef HIDE_PUBLIC_ADDRESS_HPP #define HIDE_PUBLIC_ADDRESS_HPP -#include "../protocol.hpp" +#include "protocol.hpp" #include class HidePublicAddress : public Protocol { public: - HidePublicAddress(CallbackObject* callbackObject); + HidePublicAddress(CallbackObject* callback_object); virtual ~HidePublicAddress(); virtual void notifyEvent(Event* event); diff --git a/dev/SocketsBase/protocols/lobby_room_protocol.cpp b/dev/SocketsBase/protocols/lobby_room_protocol.cpp new file mode 100644 index 000000000..74b6981e6 --- /dev/null +++ b/dev/SocketsBase/protocols/lobby_room_protocol.cpp @@ -0,0 +1,50 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "lobby_room_protocol.hpp" + +#include + +LobbyRoomProtocol::LobbyRoomProtocol(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_LOBBY_ROOM) +{ +} + +LobbyRoomProtocol::~LobbyRoomProtocol() +{ +} + +void LobbyRoomProtocol::notifyEvent(Event* event) +{ + if (event->type == EVENT_TYPE_MESSAGE) + { + printf("Message from %u : \"%s\"\n", event->peer->getAddress(), event->data.c_str()); + } +} + +void LobbyRoomProtocol::setup() +{ +} + +void LobbyRoomProtocol::update() +{ +} + +void LobbyRoomProtocol::sendMessage(std::string message) +{ + +} diff --git a/dev/SocketsBase/protocols/lobby_room_protocol.hpp b/dev/SocketsBase/protocols/lobby_room_protocol.hpp new file mode 100644 index 000000000..2d6a3a169 --- /dev/null +++ b/dev/SocketsBase/protocols/lobby_room_protocol.hpp @@ -0,0 +1,47 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef LOBBY_ROOM_PROTOCOL_HPP +#define LOBBY_ROOM_PROTOCOL_HPP + +#include "protocol.hpp" + +/*! + * \class LobbyRoomProtocol + * \brief Class used while the game is being prepared. + * This protocol starts when a server opens a game, or when a client joins a game. + * It is used to exchange data about the race settings, like kart selection. + */ +class LobbyRoomProtocol : public Protocol +{ + public: + LobbyRoomProtocol(CallbackObject* callback_object); + virtual ~LobbyRoomProtocol(); + + virtual void notifyEvent(Event* event); + + virtual void setup(); + + virtual void update(); + + void sendMessage(std::string message); + + protected: +}; + +#endif // LOBBY_ROOM_PROTOCOL_HPP diff --git a/dev/SocketsBase/protocols/show_public_address.cpp b/dev/SocketsBase/protocols/show_public_address.cpp index a44f0f065..b58cd61aa 100644 --- a/dev/SocketsBase/protocols/show_public_address.cpp +++ b/dev/SocketsBase/protocols/show_public_address.cpp @@ -1,10 +1,28 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "show_public_address.hpp" #include "../http_functions.hpp" #include -ShowPublicAddress::ShowPublicAddress(CallbackObject* callbackObject) : Protocol(callbackObject, PROTOCOL_SILENT) +ShowPublicAddress::ShowPublicAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) { } @@ -19,10 +37,10 @@ void ShowPublicAddress::notifyEvent(Event* event) void ShowPublicAddress::setup() { m_state = NONE; - if (m_publicIp == 0 || m_publicPort == 0 || m_username == "" || m_password == "") + if (m_public_ip == 0 || m_public_port == 0 || m_username == "" || m_password == "") { printf("__ShowPublicAddress> You have to set the public ip:port, username:password and the host nickname before starting this protocol.\n"); - m_listener->protocolTerminated(this); + m_listener->requestTerminate(this); } } @@ -31,7 +49,7 @@ void ShowPublicAddress::update() if (m_state == NONE) { char url[512]; - sprintf(url, "http://stkconnect.freeserver.me/log.php?set&nick=%s&ip=%u&port=%u&pwd=%s", m_username.c_str(), m_publicIp, m_publicPort, m_password.c_str()); + sprintf(url, "http://stkconnect.freeserver.me/log.php?set&nick=%s&ip=%u&port=%u&pwd=%s", m_username.c_str(), m_public_ip, m_public_port, m_password.c_str()); std::string result = HTTP::getPage(url); if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') { @@ -47,7 +65,7 @@ void ShowPublicAddress::update() } else if (m_state == DONE) { - m_listener->protocolTerminated(this); + m_listener->requestTerminate(this); } } @@ -61,6 +79,6 @@ void ShowPublicAddress::setPassword(std::string password) } void ShowPublicAddress::setPublicAddress(uint32_t ip, uint16_t port) { - m_publicIp = ip; - m_publicPort = port; + m_public_ip = ip; + m_public_port = port; } diff --git a/dev/SocketsBase/protocols/show_public_address.hpp b/dev/SocketsBase/protocols/show_public_address.hpp index 4a4c3381d..0c45e1ac9 100644 --- a/dev/SocketsBase/protocols/show_public_address.hpp +++ b/dev/SocketsBase/protocols/show_public_address.hpp @@ -1,13 +1,31 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef SHOW_PUBLIC_ADDRESS_HPP #define SHOW_PUBLIC_ADDRESS_HPP -#include "../protocol.hpp" +#include "protocol.hpp" #include class ShowPublicAddress : public Protocol { public: - ShowPublicAddress(CallbackObject* callbackObject); + ShowPublicAddress(CallbackObject* callback_object); virtual ~ShowPublicAddress(); virtual void notifyEvent(Event* event); @@ -21,8 +39,8 @@ class ShowPublicAddress : public Protocol protected: std::string m_username; std::string m_password; - uint32_t m_publicIp; - uint16_t m_publicPort; + uint32_t m_public_ip; + uint16_t m_public_port; enum STATE { diff --git a/dev/SocketsBase/server_network_manager.cpp b/dev/SocketsBase/server_network_manager.cpp index 1dffa739d..06b5cd1f4 100644 --- a/dev/SocketsBase/server_network_manager.cpp +++ b/dev/SocketsBase/server_network_manager.cpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "server_network_manager.hpp" #include "protocols/get_public_address.hpp" @@ -39,34 +57,35 @@ void ServerNetworkManager::start() printf("_NetworkInterface>Starting the global protocol\n"); // step 1 : retreive public address - int id = ProtocolManager::getInstance()->startProtocol(new GetPublicAddress(&m_publicAddress)); - while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + Protocol* protocol = new GetPublicAddress(&m_public_address); + ProtocolManager::getInstance()->requestStart(protocol); + while (ProtocolManager::getInstance()->getProtocolState(protocol) != PROTOCOL_STATE_TERMINATED ) { } printf("_NetworkInterface> The public address is known.\n"); // step 2 : show the public address for others (here, the server) ShowPublicAddress* spa = new ShowPublicAddress(NULL); - spa->setPassword(m_playerLogin.password); - spa->setUsername(m_playerLogin.username); - spa->setPublicAddress(m_publicAddress.ip, m_publicAddress.port); - id = ProtocolManager::getInstance()->startProtocol(spa); - while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + spa->setPassword(m_player_login.password); + spa->setUsername(m_player_login.username); + spa->setPublicAddress(m_public_address.ip, m_public_address.port); + ProtocolManager::getInstance()->requestStart(spa); + while (ProtocolManager::getInstance()->getProtocolState(spa) != PROTOCOL_STATE_TERMINATED ) { } printf("_NetworkInterface> The public address is being shown online.\n"); } -bool ServerNetworkManager::connectToPeer(std::string peerUsername) +bool ServerNetworkManager::connectToPeer(std::string peer_username) { printf("_NetworkInterface>Starting the connection to host protocol\n"); // step 3 : get the peer's addres. TransportAddress addr; GetPeerAddress* gpa = new GetPeerAddress(&addr); - gpa->setPeerName(peerUsername); - uint32_t id = ProtocolManager::getInstance()->startProtocol(gpa); - while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + gpa->setPeerName(peer_username); + ProtocolManager::getInstance()->requestStart(gpa); + while (ProtocolManager::getInstance()->getProtocolState(gpa) != PROTOCOL_STATE_TERMINATED ) { } printf("_NetworkInterface> The public address of the peer is known.\n"); @@ -74,12 +93,12 @@ bool ServerNetworkManager::connectToPeer(std::string peerUsername) // step 2 : connect to the peer ConnectToServer* cts = new ConnectToServer(NULL); cts->setServerAddress(addr.ip, addr.port); - id = ProtocolManager::getInstance()->startProtocol(cts); - while (ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED ) + ProtocolManager::getInstance()->requestStart(cts); + while (ProtocolManager::getInstance()->getProtocolState(cts) != PROTOCOL_STATE_TERMINATED ) { } bool success = false; - if (isConnectedTo(addr.ip, addr.port)) + if (isConnectedTo(addr)) { success = true; printf("_NetworkInterface> CONNECTION SUCCES : YOU ARE NOW CONNECTED TO A PEER.\n"); diff --git a/dev/SocketsBase/server_network_manager.hpp b/dev/SocketsBase/server_network_manager.hpp index cd0d88be0..b02f7fb08 100644 --- a/dev/SocketsBase/server_network_manager.hpp +++ b/dev/SocketsBase/server_network_manager.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef SERVER_NETWORK_MANAGER_HPP #define SERVER_NETWORK_MANAGER_HPP @@ -16,7 +34,7 @@ class ServerNetworkManager : public NetworkManager virtual void run(); void start(); - bool connectToPeer(std::string peerUsername); + bool connectToPeer(std::string peer_username); virtual void packetReceived(char* data); virtual void sendPacket(char* data); diff --git a/dev/SocketsBase/singleton.hpp b/dev/SocketsBase/singleton.hpp index c6fc7e530..de0c7b3ee 100644 --- a/dev/SocketsBase/singleton.hpp +++ b/dev/SocketsBase/singleton.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef SINGLETON_HPP #define SINGLETON_HPP diff --git a/dev/SocketsBase/stk_host.cpp b/dev/SocketsBase/stk_host.cpp index b8a553f82..b5c0d4fb2 100644 --- a/dev/SocketsBase/stk_host.cpp +++ b/dev/SocketsBase/stk_host.cpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "stk_host.hpp" #include "network_manager.hpp" @@ -7,6 +25,8 @@ #include #include +// ---------------------------------------------------------------------------- + void* STKHost::receive_data(void* self) { ENetEvent event; @@ -21,62 +41,87 @@ void* STKHost::receive_data(void* self) return NULL; } +// ---------------------------------------------------------------------------- + STKHost::STKHost() { m_host = NULL; - m_listeningThread = (pthread_t*)(malloc(sizeof(pthread_t))); + m_listening_thread = (pthread_t*)(malloc(sizeof(pthread_t))); } +// ---------------------------------------------------------------------------- + STKHost::~STKHost() { } -void STKHost::setupServer(uint32_t address, uint16_t port, int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth) +// ---------------------------------------------------------------------------- + +void STKHost::setupServer(uint32_t address, uint16_t port, int peer_count, + int channel_limit, uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth) { ENetAddress* addr = (ENetAddress*)(malloc(sizeof(ENetAddress))); addr->host = address; addr->port = port; - m_host = enet_host_create(addr, peerCount, channelLimit, maxIncomingBandwidth, maxOutgoingBandwidth); + m_host = enet_host_create(addr, peer_count, channel_limit, + max_incoming_bandwidth, max_outgoing_bandwidth); if (m_host == NULL) { - fprintf (stderr, "An error occurred while trying to create an ENet server host.\n"); + fprintf (stderr, "An error occurred while trying to create an ENet \ + server host.\n"); exit (EXIT_FAILURE); } } -void STKHost::setupClient(int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth) +// ---------------------------------------------------------------------------- + +void STKHost::setupClient(int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth) { - m_host = enet_host_create(NULL, peerCount, channelLimit, maxIncomingBandwidth, maxOutgoingBandwidth); + m_host = enet_host_create(NULL, peer_count, channel_limit, + max_incoming_bandwidth, max_outgoing_bandwidth); if (m_host == NULL) { - fprintf (stderr, "An error occurred while trying to create an ENet client host.\n"); + fprintf (stderr, "An error occurred while trying to create an ENet \ + client host.\n"); exit (EXIT_FAILURE); } } +// ---------------------------------------------------------------------------- + void STKHost::startListening() { - pthread_create(m_listeningThread, NULL, &STKHost::receive_data, this); + pthread_create(m_listening_thread, NULL, &STKHost::receive_data, this); } + +// ---------------------------------------------------------------------------- + void STKHost::stopListening() { - pthread_cancel(*m_listeningThread); + pthread_cancel(*m_listening_thread); } -void STKHost::sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort) +// ---------------------------------------------------------------------------- + +void STKHost::sendRawPacket(uint8_t* data, int length, TransportAddress dst) { struct sockaddr_in to; - int toLen = sizeof(to); - memset(&to,0,toLen); + int to_len = sizeof(to); + memset(&to,0,to_len); to.sin_family = AF_INET; - to.sin_port = htons(dstPort); - to.sin_addr.s_addr = htonl(dstIp); + to.sin_port = htons(dst.port); + to.sin_addr.s_addr = htonl(dst.ip); - sendto(m_host->socket, data, length, 0,(sockaddr*)&to, toLen); + sendto(m_host->socket, data, length, 0,(sockaddr*)&to, to_len); } +// ---------------------------------------------------------------------------- + uint8_t* STKHost::receiveRawPacket() { uint8_t* buffer; // max size needed normally (only used for stun) @@ -85,7 +130,8 @@ uint8_t* STKHost::receiveRawPacket() int len = recv(m_host->socket,buffer,2048, 0); int i = 0; - while(len < 0) // wait to receive the message because enet sockets are non-blocking + // wait to receive the message because enet sockets are non-blocking + while(len < 0) { i++; len = recv(m_host->socket,buffer,2048, 0); @@ -94,27 +140,31 @@ uint8_t* STKHost::receiveRawPacket() printf("Packet received after %i milliseconds\n", i); return buffer; } -uint8_t* STKHost::receiveRawPacket(unsigned int dstIp, unsigned short dstPort) + +// ---------------------------------------------------------------------------- + +uint8_t* STKHost::receiveRawPacket(TransportAddress sender) { uint8_t* buffer; // max size needed normally (only used for stun) buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); memset(buffer, 0, 2048); - socklen_t fromlen; + socklen_t from_len; struct sockaddr addr; - fromlen = sizeof(addr); - int len = recvfrom(m_host->socket, buffer, 2048, 0, &addr, &fromlen); + from_len = sizeof(addr); + int len = recvfrom(m_host->socket, buffer, 2048, 0, &addr, &from_len); int i = 0; + // wait to receive the message because enet sockets are non-blocking while(len < 0 || ( - (uint8_t)(addr.sa_data[2]) != (dstIp>>24&0xff) - && (uint8_t)(addr.sa_data[3]) != (dstIp>>16&0xff) - && (uint8_t)(addr.sa_data[4]) != (dstIp>>8&0xff) - && (uint8_t)(addr.sa_data[5]) != (dstIp&0xff))) // wait to receive the message because enet sockets are non-blocking + (uint8_t)(addr.sa_data[2]) != (sender.ip>>24&0xff) + && (uint8_t)(addr.sa_data[3]) != (sender.ip>>16&0xff) + && (uint8_t)(addr.sa_data[4]) != (sender.ip>>8&0xff) + && (uint8_t)(addr.sa_data[5]) != (sender.ip&0xff))) { i++; - len = recvfrom(m_host->socket, buffer, 2048, 0, &addr, &fromlen); + len = recvfrom(m_host->socket, buffer, 2048, 0, &addr, &from_len); usleep(1000); // wait 1 millisecond between two checks } if (addr.sa_family == AF_INET) @@ -127,28 +177,39 @@ uint8_t* STKHost::receiveRawPacket(unsigned int dstIp, unsigned short dstPort) return buffer; } +// ---------------------------------------------------------------------------- + void STKHost::broadcastPacket(char* data) { - ENetPacket* packet = enet_packet_create(data, strlen(data)+1,ENET_PACKET_FLAG_RELIABLE); + ENetPacket* packet = enet_packet_create(data, strlen(data)+1, + ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(m_host, 0, packet); } -bool STKHost::peerExists(uint32_t ip, uint16_t port) +// ---------------------------------------------------------------------------- + +bool STKHost::peerExists(TransportAddress peer) { for (unsigned int i = 0; i < m_host->peerCount; i++) { - if (m_host->peers[i].address.host == ip && m_host->peers[i].address.port == port) + if (m_host->peers[i].address.host == peer.ip && + m_host->peers[i].address.port == peer.port) { return true; } } return false; } -bool STKHost::isConnectedTo(uint32_t ip, uint16_t port) + +// ---------------------------------------------------------------------------- + +bool STKHost::isConnectedTo(TransportAddress peer) { for (unsigned int i = 0; i < m_host->peerCount; i++) { - if (m_host->peers[i].address.host == ip && m_host->peers[i].address.port == port && m_host->peers[i].state == ENET_PEER_STATE_CONNECTED) + if (m_host->peers[i].address.host == peer.ip && + m_host->peers[i].address.port == peer.port && + m_host->peers[i].state == ENET_PEER_STATE_CONNECTED) { return true; } diff --git a/dev/SocketsBase/stk_host.hpp b/dev/SocketsBase/stk_host.hpp index 14378f4bf..460fc3a14 100644 --- a/dev/SocketsBase/stk_host.hpp +++ b/dev/SocketsBase/stk_host.hpp @@ -1,41 +1,140 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/*! \file stk_host.hpp + * \brief Defines an interface to use network low-level functions easily. + */ #ifndef STK_HOST_HPP #define STK_HOST_HPP #include +#include "types.hpp" - +/*! \class STKHost + * \brief Represents the local host. + * This host is either a server host or a client host. A client host is in + * charge of connecting to a server. A server opens a socket for incoming + * connections. + * By default, this host will use ENet to exchange packets. It also defines an + * interface for ENet use. Nevertheless, this class can be used to send and/or + * receive packets whithout ENet adding its headers. + * This class is used by the Network Manager to send packets. + */ class STKHost { - friend class STKPeer; + friend class STKPeer; // allow direct enet modifications in implementations public: + /*! \enum HOST_TYPE + * \brief Defines three host types for the server. + * These values tells the host where he will accept connections from. + */ enum HOST_TYPE { - HOST_ANY = 0, - HOST_BROADCAST = 0xFFFFFFFF, - PORT_ANY = 0 + HOST_ANY = 0, //!< Any host. + HOST_BROADCAST = 0xFFFFFFFF, //!< Defines the broadcast address. + PORT_ANY = 0 //!< Any port. }; - + + /*! \brief Constructor */ STKHost(); + /*! \brief Destructor */ virtual ~STKHost(); + /*! \brief Thread function checking if data is received. + * This function tries to get data from network low-level functions as + * often as possible. When something is received, it generates an + * event and passes it to the Network Manager. + * \param self : used to pass the ENet host to the function. + */ static void* receive_data(void* self); - void setupServer(uint32_t address, uint16_t port, int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth); - void setupClient(int peerCount, int channelLimit, uint32_t maxIncomingBandwidth, uint32_t maxOutgoingBandwidth); + /*! \brief Setups the host as a server. + * \param address : The IPv4 address of incoming connections. + * \param port : The port on which the server listens. + * \param peer_count : The maximum number of peers. + * \param channel_limit : The maximum number of channels per peer. + * \param max_incoming_bandwidth : The maximum incoming bandwidth. + * \param max_outgoing_bandwidth : The maximum outgoing bandwidth. + */ + void setupServer(uint32_t address, uint16_t port, + int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth); + /*! \brief Setups the host as a client. + * In fact there is only one peer connected to this host. + * \param peer_count : The maximum number of peers. + * \param channel_limit : The maximum number of channels per peer. + * \param max_incoming_bandwidth : The maximum incoming bandwidth. + * \param max_outgoing_bandwidth : The maximum outgoing bandwidth. + */ + void setupClient(int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth); - void startListening(); - void stopListening(); + /*! \brief Starts the listening of events from ENet. + * Starts a thread that updates it as often as possible. + */ + void startListening(); + /*! \brief Stops the listening of events from ENet. + * Stops the thread that was receiving events. + */ + void stopListening(); - void sendRawPacket(uint8_t* data, int length, unsigned int dstIp, unsigned short dstPort); - uint8_t* receiveRawPacket(); - uint8_t* receiveRawPacket(unsigned int dstIp, unsigned short dstPort); - void broadcastPacket(char* data); + /*! \brief Sends a packet whithout ENet adding its headers. + * This function is used in particular to achieve the STUN protocol. + * \param data : Data to send. + * \param length : Length of the sent data. + * \param dst : Destination of the packet. + */ + void sendRawPacket(uint8_t* data, int length, + TransportAddress dst); + /*! \brief Receives a packet directly from the network interface. + * Receive a packet whithout ENet processing it. + * \return A string containing the data of the received packet. + */ + uint8_t* receiveRawPacket(); + /*! \brief Receives a packet directly from the network interface and + * filter its address. + * Receive a packet whithout ENet processing it. Checks that the + * sender of the packet is the one that corresponds to the sender + * parameter. Does not check the port right now. + * \param sender : Transport address of the original sender of the + * wanted packet. + * \return A string containing the data of the received packet + * matching the sender's ip address. + */ + uint8_t* receiveRawPacket(TransportAddress sender); + /*! \brief Broadcasts a packet to all peers. + * \param data : Data to send. + */ + void broadcastPacket(char* data); - bool peerExists(uint32_t ip, uint16_t port); - bool isConnectedTo(uint32_t ip, uint16_t port); + /*! \brief Tells if a peer is known. + * \return True if the peer is known, false elseway. + */ + bool peerExists(TransportAddress peer_address); + /*! \brief Tells if a peer is known and connected. + * \return True if the peer is known and connected, false elseway. + */ + bool isConnectedTo(TransportAddress peer_address); protected: - ENetHost* m_host; - pthread_t* m_listeningThread; + ENetHost* m_host; //!< ENet host interfacing sockets. + pthread_t* m_listening_thread; //!< Thread listening network events. }; diff --git a/dev/SocketsBase/stk_peer.cpp b/dev/SocketsBase/stk_peer.cpp index 2e7c86cca..d2990361c 100644 --- a/dev/SocketsBase/stk_peer.cpp +++ b/dev/SocketsBase/stk_peer.cpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "stk_peer.hpp" #include @@ -14,13 +32,13 @@ STKPeer::~STKPeer() delete m_peer; } -bool STKPeer::connectToHost(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data) +bool STKPeer::connectToHost(STKHost* localhost, TransportAddress host, uint32_t channel_count, uint32_t data) { ENetAddress address; - address.host = ip; - address.port = port; + address.host = host.ip; + address.port = host.port; - ENetPeer* peer = enet_host_connect(host->m_host, &address, 2, 0); + ENetPeer* peer = enet_host_connect(localhost->m_host, &address, 2, 0); if (peer == NULL) { printf("Could not try to connect to server.\n"); diff --git a/dev/SocketsBase/stk_peer.hpp b/dev/SocketsBase/stk_peer.hpp index 3f8236869..30b7cad57 100644 --- a/dev/SocketsBase/stk_peer.hpp +++ b/dev/SocketsBase/stk_peer.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef STK_PEER_HPP #define STK_PEER_HPP @@ -16,7 +34,7 @@ class STKPeer virtual void sendPacket(char* data); - static bool connectToHost(STKHost* host, uint32_t ip, uint16_t port, uint32_t channelCount, uint32_t data); + static bool connectToHost(STKHost* localhost, TransportAddress host, uint32_t channel_count, uint32_t data); bool isConnected(); diff --git a/dev/SocketsBase/time.cpp b/dev/SocketsBase/time.cpp index f482bbc16..8623430d4 100644 --- a/dev/SocketsBase/time.cpp +++ b/dev/SocketsBase/time.cpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include "time.hpp" namespace Time diff --git a/dev/SocketsBase/time.hpp b/dev/SocketsBase/time.hpp index 7d5ac32d8..fc9d7835b 100644 --- a/dev/SocketsBase/time.hpp +++ b/dev/SocketsBase/time.hpp @@ -1,3 +1,21 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #ifndef TIME_HPP #define TIME_HPP diff --git a/dev/SocketsBase/types.hpp b/dev/SocketsBase/types.hpp index 8c4f08321..b95380f69 100644 --- a/dev/SocketsBase/types.hpp +++ b/dev/SocketsBase/types.hpp @@ -1,9 +1,33 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/*! \file types.hpp + * \brief Declares the general types that are used by the network. + */ #ifndef TYPES_HPP #define TYPES_HPP #include #include +/*! \class CallbackObject + * \brief Class that must be inherited to pass objects to protocols. + */ class CallbackObject { public: @@ -11,22 +35,30 @@ class CallbackObject }; +/*! \class TransportAddress + * \brief Describes a transport-layer address. + * For IP networks, a transport address is the couple ip:port. + */ class TransportAddress : public CallbackObject { public: - TransportAddress() {} + TransportAddress(uint32_t p_ip = 0, uint16_t p_port = 0) + { ip = p_ip; port = p_port; } - uint32_t ip; - uint16_t port; + uint32_t ip; //!< The IPv4 address + uint16_t port; //!< The port number }; +/*! \class PlayerLogin + * \brief Contains the information needed to authenticate a user. + */ class PlayerLogin : public CallbackObject { public: PlayerLogin() {} - std::string username; - std::string password; + std::string username; //!< Username of the player + std::string password; //!< Password of the player }; From 613e97d0d6bb477d51412dbf09709df810a0e6a5 Mon Sep 17 00:00:00 2001 From: hilnius Date: Sat, 29 Jun 2013 22:57:18 +0000 Subject: [PATCH 19/22] breaking everything that was working. cannot even start a race now, need to rewrite the network manager and part of the race startup sequence git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@13032 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/io/xml_node.cpp | 4 +- src/items/attachment.cpp | 7 +- src/items/flyable.cpp | 5 +- src/items/flyable.hpp | 1 + src/items/item_manager.cpp | 2 +- src/items/powerup.cpp | 3 +- src/items/projectile_manager.cpp | 22 +- src/karts/controller/kart_control.hpp | 24 +- src/karts/controller/skidding_ai.cpp | 2 +- src/karts/kart.cpp | 17 +- src/main.cpp | 36 +- src/main_loop.cpp | 14 +- src/modes/demo_world.cpp | 2 +- src/modes/linear_world.cpp | 4 +- src/modes/overworld.cpp | 2 +- src/modes/world.cpp | 16 +- src/modes/world_status.cpp | 4 +- src/network/character_confirm_message.hpp | 71 -- src/network/character_info_message.hpp | 50 -- src/network/character_selected_message.hpp | 105 --- src/network/client_network_manager.cpp | 128 +++ src/network/client_network_manager.hpp | 48 ++ src/network/connect_message.cpp | 78 -- src/network/event.cpp | 73 ++ src/network/event.hpp | 69 ++ src/network/flyable_info.hpp | 70 -- src/network/http_functions.cpp | 67 ++ ..._update_message.hpp => http_functions.hpp} | 25 +- src/network/item_info.hpp | 66 -- src/network/kart_control_message.cpp | 61 -- src/network/kart_update_message.cpp | 62 -- src/network/message.cpp | 225 ----- src/network/message.hpp | 143 ---- ...trol_message.hpp => network_interface.cpp} | 19 +- ...tart_message.hpp => network_interface.hpp} | 43 +- src/network/network_kart.cpp | 43 - src/network/network_manager.cpp | 801 ++---------------- src/network/network_manager.hpp | 144 +--- .../{connect_message.hpp => protocol.cpp} | 48 +- src/network/protocol.hpp | 105 +++ src/network/protocol_manager.cpp | 287 +++++++ src/network/protocol_manager.hpp | 266 ++++++ src/network/protocols/connect_to_server.cpp | 110 +++ .../connect_to_server.hpp} | 49 +- src/network/protocols/get_peer_address.cpp | 98 +++ src/network/protocols/get_peer_address.hpp | 47 + src/network/protocols/get_public_address.cpp | 207 +++++ src/network/protocols/get_public_address.hpp | 47 + src/network/protocols/hide_public_address.cpp | 74 ++ src/network/protocols/hide_public_address.hpp | 49 ++ src/network/protocols/lobby_room_protocol.cpp | 50 ++ .../lobby_room_protocol.hpp} | 50 +- src/network/protocols/show_public_address.cpp | 84 ++ src/network/protocols/show_public_address.hpp | 53 ++ src/network/race_info_message.cpp | 116 --- src/network/race_result_ack_message.hpp | 58 -- src/network/race_result_message.cpp | 60 -- src/network/race_state.cpp | 195 ----- src/network/race_state.hpp | 109 --- src/network/server_network_manager.cpp | 123 +++ src/network/server_network_manager.hpp | 50 ++ src/network/singleton.hpp | 63 ++ src/network/stk_host.cpp | 218 +++++ src/network/stk_host.hpp | 141 +++ src/network/stk_peer.cpp | 77 ++ .../{network_kart.hpp => stk_peer.hpp} | 42 +- .../{world_loaded_message.hpp => time.cpp} | 25 +- .../{race_info_message.hpp => time.hpp} | 22 +- src/network/types.hpp | 65 ++ src/physics/physics.cpp | 13 +- src/race/history.hpp | 1 + src/race/race_manager.cpp | 30 +- .../dialogs/race_paused_dialog.cpp | 2 +- .../dialogs/select_challenge.cpp | 4 +- src/states_screens/help_screen_1.cpp | 2 +- src/states_screens/main_menu_screen.cpp | 4 +- 76 files changed, 3015 insertions(+), 2585 deletions(-) delete mode 100644 src/network/character_confirm_message.hpp delete mode 100644 src/network/character_info_message.hpp delete mode 100644 src/network/character_selected_message.hpp create mode 100644 src/network/client_network_manager.cpp create mode 100644 src/network/client_network_manager.hpp delete mode 100644 src/network/connect_message.cpp create mode 100644 src/network/event.cpp create mode 100644 src/network/event.hpp delete mode 100644 src/network/flyable_info.hpp create mode 100644 src/network/http_functions.cpp rename src/network/{kart_update_message.hpp => http_functions.hpp} (72%) delete mode 100644 src/network/item_info.hpp delete mode 100644 src/network/kart_control_message.cpp delete mode 100644 src/network/kart_update_message.cpp delete mode 100644 src/network/message.cpp delete mode 100644 src/network/message.hpp rename src/network/{kart_control_message.hpp => network_interface.cpp} (68%) rename src/network/{race_start_message.hpp => network_interface.hpp} (58%) delete mode 100644 src/network/network_kart.cpp rename src/network/{connect_message.hpp => protocol.cpp} (60%) create mode 100644 src/network/protocol.hpp create mode 100644 src/network/protocol_manager.cpp create mode 100644 src/network/protocol_manager.hpp create mode 100644 src/network/protocols/connect_to_server.cpp rename src/network/{num_players_message.hpp => protocols/connect_to_server.hpp} (52%) create mode 100644 src/network/protocols/get_peer_address.cpp create mode 100644 src/network/protocols/get_peer_address.hpp create mode 100644 src/network/protocols/get_public_address.cpp create mode 100644 src/network/protocols/get_public_address.hpp create mode 100644 src/network/protocols/hide_public_address.cpp create mode 100644 src/network/protocols/hide_public_address.hpp create mode 100644 src/network/protocols/lobby_room_protocol.cpp rename src/network/{race_result_message.hpp => protocols/lobby_room_protocol.hpp} (50%) create mode 100644 src/network/protocols/show_public_address.cpp create mode 100644 src/network/protocols/show_public_address.hpp delete mode 100644 src/network/race_info_message.cpp delete mode 100644 src/network/race_result_ack_message.hpp delete mode 100644 src/network/race_result_message.cpp delete mode 100644 src/network/race_state.cpp delete mode 100644 src/network/race_state.hpp create mode 100644 src/network/server_network_manager.cpp create mode 100644 src/network/server_network_manager.hpp create mode 100644 src/network/singleton.hpp create mode 100644 src/network/stk_host.cpp create mode 100644 src/network/stk_host.hpp create mode 100644 src/network/stk_peer.cpp rename src/network/{network_kart.hpp => stk_peer.hpp} (55%) rename src/network/{world_loaded_message.hpp => time.cpp} (65%) rename src/network/{race_info_message.hpp => time.hpp} (67%) create mode 100644 src/network/types.hpp diff --git a/src/io/xml_node.cpp b/src/io/xml_node.cpp index d04d9ec19..4c56d94b3 100644 --- a/src/io/xml_node.cpp +++ b/src/io/xml_node.cpp @@ -41,7 +41,7 @@ XMLNode::XMLNode(const std::string &filename) m_file_name = filename; io::IXMLReader *xml = file_manager->createXMLReader(filename); - + if (xml == NULL) { throw std::runtime_error("Cannot find file "+filename); @@ -597,4 +597,4 @@ bool XMLNode::hasChildNamed(const char* name) const if (m_nodes[i]->getName() == name) return true; } return false; -} \ No newline at end of file +} diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index e974de71f..f7daef227 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -33,7 +33,6 @@ #include "karts/kart_properties.hpp" #include "modes/three_strikes_battle.hpp" #include "modes/world.hpp" -#include "network/race_state.hpp" #include "network/network_manager.hpp" #include "utils/constants.hpp" @@ -260,11 +259,13 @@ void Attachment::hitBanana(Item *item, int new_attachment) // Save the information about the attachment in the race state // so that the clients can be updated. - if(network_manager->getMode()==NetworkManager::NW_SERVER) + if(NetworkManager::getInstance()->isPlayingOnline()) // if we're online { + /* race_state->itemCollected(m_kart->getWorldKartId(), item->getItemId(), - new_attachment); + new_attachment);*/ +//NETWORK_UPDATE_PLZ } if (add_a_new_item) diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index c46496c16..9a10e0cd6 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -38,7 +38,6 @@ #include "karts/abstract_kart.hpp" #include "karts/explosion_animation.hpp" #include "modes/world.hpp" -#include "network/flyable_info.hpp" #include "physics/physics.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -416,11 +415,13 @@ bool Flyable::updateAndDelete(float dt) */ void Flyable::updateFromServer(const FlyableInfo &f, float dt) { +/* setXYZ(f.m_xyz); setRotation(f.m_rotation); - +*/ // Update the graphical position Moveable::update(dt); + } // updateFromServer // ---------------------------------------------------------------------------- diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index bfd46835f..f15bd7cb4 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -19,6 +19,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +//NETWORK_UPDATE_PLZ #ifndef HEADER_FLYABLE_HPP #define HEADER_FLYABLE_HPP diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 9432b9f01..44d4caf6b 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -291,7 +291,7 @@ void ItemManager::collectedItem(Item *item, AbstractKart *kart, int add_info) void ItemManager::checkItemHit(AbstractKart* kart) { // Only do this on the server - if(network_manager->getMode()==NetworkManager::NW_CLIENT) return; + if(NetworkManager::getInstance()->isServer()) return; // We could use m_items_in_quads to to check for item hits: take the quad // of the graph node of the kart, and only check items in that quad. But diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index b566ac0c0..483d9feb5 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -16,6 +16,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +//NETWORK_UPDATE_PLZ #include "items/powerup.hpp" #include "audio/sfx_base.hpp" @@ -30,8 +31,6 @@ #include "karts/controller/controller.hpp" #include "karts/kart_properties.hpp" #include "modes/world.hpp" -#include "network/network_manager.hpp" -#include "network/race_state.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" diff --git a/src/items/projectile_manager.cpp b/src/items/projectile_manager.cpp index f7846b684..6ea6b8069 100644 --- a/src/items/projectile_manager.cpp +++ b/src/items/projectile_manager.cpp @@ -16,6 +16,8 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +//NETWORK_UPDATE_PLZ + #include "items/projectile_manager.hpp" #include "graphics/explosion.hpp" @@ -27,7 +29,6 @@ #include "items/powerup.hpp" #include "items/rubber_ball.hpp" #include "network/network_manager.hpp" -#include "network/race_state.hpp" ProjectileManager *projectile_manager=0; @@ -66,7 +67,8 @@ void ProjectileManager::cleanup() /** General projectile update call. */ void ProjectileManager::update(float dt) { - if(network_manager->getMode()==NetworkManager::NW_CLIENT) + + if(NetworkManager::getInstance()->isClient()) { updateClient(dt); } @@ -101,21 +103,23 @@ void ProjectileManager::update(float dt) void ProjectileManager::updateServer(float dt) { // First update all projectiles on the track - if(network_manager->getMode()!=NetworkManager::NW_NONE) + + if(NetworkManager::getInstance()->isPlayingOnline()) //network_manager->getMode()!=NetworkManager::NW_NONE) { - race_state->setNumFlyables(m_active_projectiles.size()); + //race_state->setNumFlyables(m_active_projectiles.size()); } + Projectiles::iterator p = m_active_projectiles.begin(); while(p!=m_active_projectiles.end()) { bool can_be_deleted = (*p)->updateAndDelete(dt); - if(network_manager->getMode()!=NetworkManager::NW_NONE) + if(NetworkManager::getInstance()->isPlayingOnline()) //network_manager->getMode()!=NetworkManager::NW_NONE) { - race_state->setFlyableInfo(p-m_active_projectiles.begin(), + /*race_state->setFlyableInfo(p-m_active_projectiles.begin(), FlyableInfo((*p)->getXYZ(), (*p)->getRotation(), - can_be_deleted) ); + can_be_deleted) );*/ } if(can_be_deleted) { @@ -130,6 +134,7 @@ void ProjectileManager::updateServer(float dt) else p++; } // while p!=m_active_projectiles.end() + } // updateServer // ----------------------------------------------------------------------------- @@ -138,6 +143,7 @@ void ProjectileManager::updateServer(float dt) * (i.e. position, hit effects etc) */ void ProjectileManager::updateClient(float dt) { + /* unsigned int num_projectiles = race_state->getNumFlyables(); if(num_projectiles != m_active_projectiles.size()) fprintf(stderr, "Warning: num_projectiles %d active %d\n", @@ -154,7 +160,7 @@ void ProjectileManager::updateClient(float dt) (*i)->hit(NULL); } } // for i in m_active_projectiles - + */ } // updateClient // ----------------------------------------------------------------------------- Flyable *ProjectileManager::newProjectile(AbstractKart *kart, Track* track, diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index 78f789e7c..01619c4d5 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -19,9 +19,9 @@ #ifndef HEADER_KART_CONTROL_HPP #define HEADER_KART_CONTROL_HPP -#include "network/message.hpp" +//NETWORK_UPDATE_PLZ -/** +/** * \ingroup controller */ class KartControl @@ -52,15 +52,6 @@ public: reset(); } // ------------------------------------------------------------------------ - /** Construct kart control from a Message (i.e. unserialise) */ - KartControl(Message *m) - { - m_steer = m->getFloat(); - m_accel = m->getFloat(); - char c = m->getChar(); - setButtonsCompressed(c); - } // KartControl(Message*) - // ------------------------------------------------------------------------ /** Resets all controls. */ void reset() { @@ -74,17 +65,6 @@ public: m_look_back = false; } // reset // ------------------------------------------------------------------------ - /** Return the serialised size in bytes. */ - static int getLength() { return 9; } - // ------------------------------------------------------------------------ - /** Serialises the kart control into a message. */ - void serialise(Message *m) const - { - m->addFloat(m_steer); - m->addFloat(m_accel); - m->addChar(getButtonsCompressed()); - } // compress - // ------------------------------------------------------------------------ void uncompress(char *c) { m_steer = ((float*)c)[0]; diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index ef3158c93..029fb8a17 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -298,7 +298,7 @@ void SkiddingAI::update(float dt) #endif // The client does not do any AI computations. - if(network_manager->getMode()==NetworkManager::NW_CLIENT) + if(NetworkManager::getInstance()->isClient()) { AIBaseController::update(dt); return; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 1c0e80007..e90bd7a74 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -56,7 +56,6 @@ #include "karts/max_speed.hpp" #include "karts/skidding.hpp" #include "modes/linear_world.hpp" -#include "network/race_state.hpp" #include "network/network_manager.hpp" #include "physics/btKart.hpp" #include "physics/btKartRaycast.hpp" @@ -78,6 +77,8 @@ # include #endif +//NETWORK_UPDATE_PLZ + /** The kart constructor. * \param ident The identifier for the kart model to use. * \param position The position (or rank) for this kart (between 1 and @@ -867,11 +868,11 @@ void Kart::collectedItem(Item *item, int add_info) // Attachments and powerups are stored in the corresponding // functions (hit{Red,Green}Item), so only coins need to be // stored here. - if(network_manager->getMode()==NetworkManager::NW_SERVER && + /*if(NetworkManager::getInstance()->isServer() && (type==Item::ITEM_NITRO_BIG || type==Item::ITEM_NITRO_SMALL) ) { race_state->itemCollected(getWorldKartId(), item->getItemId()); - } + }*/ if ( m_collected_energy > m_kart_properties->getNitroMax()) m_collected_energy = m_kart_properties->getNitroMax(); @@ -1012,9 +1013,9 @@ void Kart::update(float dt) // Store the actual kart controls at the start of update in the server // state. This makes it easier to reset some fields when they are not used // anymore (e.g. controls.fire). - if(network_manager->getMode()==NetworkManager::NW_SERVER) + if(NetworkManager::getInstance()->isServer()) { - race_state->storeKartControls(*this); + //race_state->storeKartControls(*this); } if (!m_flying) @@ -1815,10 +1816,8 @@ void Kart::updatePhysics(float dt) updateSliding(); - // Only compute the current speed if this is not the client. On a client the - // speed is actually received from the server. - if(network_manager->getMode()!=NetworkManager::NW_CLIENT) - m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); + // Compute the speed of the kart. + m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); // calculate direction of m_speed const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform(); diff --git a/src/main.cpp b/src/main.cpp index 71c083beb..cbe0f15d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -169,6 +169,8 @@ #include "modes/demo_world.hpp" #include "modes/profile_world.hpp" #include "network/network_manager.hpp" +#include "network/client_network_manager.hpp" +#include "network/server_network_manager.hpp" #include "race/grand_prix_manager.hpp" #include "race/highscore_manager.hpp" #include "race/history.hpp" @@ -683,22 +685,17 @@ int handleCmdLine(int argc, char **argv) } else if(sscanf(argv[i], "--server=%d",&n)==1) { - network_manager->setMode(NetworkManager::NW_SERVER); + NetworkManager::getInstance(); //create the server UserConfigParams::m_server_port = n; } else if( !strcmp(argv[i], "--server") ) { - network_manager->setMode(NetworkManager::NW_SERVER); + NetworkManager::getInstance(); } else if( sscanf(argv[i], "--port=%d", &n) ) { UserConfigParams::m_server_port=n; } - else if( sscanf(argv[i], "--client=%1023s", s) ) - { - network_manager->setMode(NetworkManager::NW_CLIENT); - UserConfigParams::m_server_address=s; - } else if ( sscanf(argv[i], "--gfx=%d", &n) ) { if (n) @@ -1142,9 +1139,10 @@ void initRest() kart_properties_manager = new KartPropertiesManager(); projectile_manager = new ProjectileManager (); powerup_manager = new PowerupManager (); + // If the server has been created (--server option), this will do nothing: + NetworkManager::getInstance(); attachment_manager = new AttachmentManager (); highscore_manager = new HighscoreManager (); - network_manager = new NetworkManager (); KartPropertiesManager::addKartSearchDir( file_manager->getAddonsFile("karts/")); track_manager->addTrackSearchDir( @@ -1191,7 +1189,7 @@ void cleanSuperTuxKart() INetworkHttp::destroy(); if(news_manager) delete news_manager; if(addons_manager) delete addons_manager; - if(network_manager) delete network_manager; + NetworkManager::kill(); if(grand_prix_manager) delete grand_prix_manager; if(highscore_manager) delete highscore_manager; if(attachment_manager) delete attachment_manager; @@ -1449,7 +1447,7 @@ int main(int argc, char *argv[] ) { // This will setup the race manager etc. history->Load(); - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); race_manager->startNew(false); main_loop->run(); // well, actually run() will never return, since @@ -1458,20 +1456,6 @@ int main(int argc, char *argv[] ) exit(-3); } - // Initialise connection in case that a command line option was set - // configuring a client or server. Otherwise this function does nothing - // here (and will be called again from the network gui). - if(!network_manager->initialiseConnections()) - { - Log::error("main", "Problems initialising network connections,\n" - "Running in non-network mode."); - } - // On the server start with the network information page for now - if(network_manager->getMode()==NetworkManager::NW_SERVER) - { - // TODO - network menu - //menu_manager->pushMenu(MENUID_NETWORK_GUI); - } // Not replaying // ============= if(!ProfileWorld::isProfileMode()) @@ -1481,7 +1465,7 @@ int main(int argc, char *argv[] ) // Quickstart (-N) // =============== // all defaults are set in InitTuxkart() - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); race_manager->startNew(false); } } @@ -1491,7 +1475,7 @@ int main(int argc, char *argv[] ) // ========= race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE); race_manager->setDifficulty(RaceManager::DIFFICULTY_HARD); - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); race_manager->startNew(false); } main_loop->run(); diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 6fc66e13d..321626bad 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -98,17 +98,17 @@ void MainLoop::updateRace(float dt) // Client: send current controls to server // But don't do this if the race is in finish phase (otherwise // messages can be mixed up in the race manager) - if(!World::getWorld()->isFinishPhase()) - network_manager->sendUpdates(); + /*if(!World::getWorld()->isFinishPhase()) + network_manager->sendUpdates();*/ if(ProfileWorld::isProfileMode()) dt=1.0f/60.0f; // Again, only receive updates if the race isn't over - once the // race results are displayed (i.e. game is in finish phase) // messages must be handled by the normal update of the network // manager - if(!World::getWorld()->isFinishPhase()) - network_manager->receiveUpdates(); - + /*if(!World::getWorld()->isFinishPhase()) + network_manager->receiveUpdates();*/ + World::getWorld()->updateWorld(dt); } // updateRace @@ -127,13 +127,13 @@ void MainLoop::run() m_prev_time = m_curr_time; float dt = getLimitedDt(); - network_manager->update(dt); +// network_manager->update(dt); if (World::getWorld()) // race is active if world exists { // Busy wait if race_manager is active (i.e. creating of world is done) // till all clients have reached this state. - if (network_manager->getState()==NetworkManager::NS_READY_SET_GO_BARRIER) continue; + //if (network_manager->getState()==NetworkManager::NS_READY_SET_GO_BARRIER) continue; updateRace(dt); } // if race is active diff --git a/src/modes/demo_world.cpp b/src/modes/demo_world.cpp index 681382e7e..4801ab4cd 100644 --- a/src/modes/demo_world.cpp +++ b/src/modes/demo_world.cpp @@ -149,7 +149,7 @@ bool DemoWorld::updateIdleTimeAndStartDemo(float dt) m_do_demo = true; race_manager->setNumKarts(m_num_karts); race_manager->setLocalKartInfo(0, "tux"); - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); race_manager->startSingleRace(m_demo_tracks[0], m_num_laps, false); m_demo_tracks.push_back(m_demo_tracks[0]); m_demo_tracks.erase(m_demo_tracks.begin()); diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 10ba446a8..a0ba1992f 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -322,10 +322,10 @@ void LinearWorld::newLap(unsigned int kart_index) // A client does not detect race finished by itself, it will // receive a message from the server. So a client does not do // anything here. - if(network_manager->getMode()!=NetworkManager::NW_CLIENT) + /*if(network_manager->getMode()!=NetworkManager::NW_CLIENT) { kart->finishedRace(getTime()); - } + }*/ } float time_per_lap; if (kart_info.m_race_lap == 1) // just completed first lap diff --git a/src/modes/overworld.cpp b/src/modes/overworld.cpp index 14a498367..01fdd215d 100644 --- a/src/modes/overworld.cpp +++ b/src/modes/overworld.cpp @@ -83,7 +83,7 @@ void OverWorld::enterOverWorld() ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); race_manager->startNew(false); if(race_manager->haveKartLastPositionOnOverworld()){ OverWorld *ow = (OverWorld*)World::getWorld(); diff --git a/src/modes/world.cpp b/src/modes/world.cpp index a1735b602..650b5b9cf 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -42,8 +42,6 @@ #include "karts/kart_properties_manager.hpp" #include "modes/overworld.hpp" #include "modes/profile_world.hpp" -#include "network/network_manager.hpp" -#include "network/race_state.hpp" #include "physics/btKart.hpp" #include "physics/physics.hpp" #include "physics/triangle_mesh.hpp" @@ -119,7 +117,7 @@ World::World() : WorldStatus(), m_clear_color(255,100,101,140) */ void World::init() { - race_state = new RaceState(); +// race_state = new RaceState(); m_faster_music_active = false; m_fastest_kart = 0; m_eliminated_karts = 0; @@ -175,7 +173,7 @@ void World::init() if(ReplayPlay::get()) ReplayPlay::get()->Load(); - network_manager->worldLoaded(); +// network_manager->worldLoaded(); powerup_manager->updateWeightsForRace(num_karts); } // init @@ -361,7 +359,7 @@ World::~World() // gui and this must be deleted. delete m_race_gui; } - delete race_state; +// delete race_state; for ( unsigned int i = 0 ; i < m_karts.size() ; i++ ) delete m_karts[i]; @@ -747,7 +745,7 @@ void World::updateWorld(float dt) ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); race_manager->startNew(false); } else @@ -801,10 +799,10 @@ void World::update(float dt) if(history->replayHistory()) dt=history->getNextDelta(); WorldStatus::update(dt); // Clear race state so that new information can be stored - race_state->clear(); +// race_state->clear(); - if(network_manager->getMode()!=NetworkManager::NW_CLIENT && - !history->dontDoPhysics()) +// if(network_manager->getMode()!=NetworkManager::NW_CLIENT && +// !history->dontDoPhysics()) { m_physics->update(dt); } diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 7ed28c4ac..d9e9656ab 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -112,8 +112,8 @@ void WorldStatus::enterRaceOverState() */ void WorldStatus::terminateRace() { - if(network_manager->getMode()==NetworkManager::NW_SERVER) - network_manager->sendRaceResults(); +// if(network_manager->getMode()==NetworkManager::NW_SERVER) +// network_manager->sendRaceResults(); } // terminateRace //----------------------------------------------------------------------------- diff --git a/src/network/character_confirm_message.hpp b/src/network/character_confirm_message.hpp deleted file mode 100644 index 527794928..000000000 --- a/src/network/character_confirm_message.hpp +++ /dev/null @@ -1,71 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_CHARACTER_CONFIRM_MESSAGE_HPP -#define HEADER_CHARACTER_CONFIRM_MESSAGE_HPP - -#include - -#include "network/message.hpp" - - -/** This message is from the server to all clients to inform them about a - * newly selected character. This means that this character is not available - * anymore. The message contains the hostid of the client who selected this - * character (0 in case of server), so that this message acts as a - * confirmation for the corresponding client (or a reject if the message has - * a different hostid, meaning that another client selected the character - * earlier). - */ -class CharacterConfirmMessage : public Message -{ -private: - /** The host id. */ - int m_host_id; - /** Name of the selected kart. */ - std::string m_kart_name; -public: - /** Constructor, takes the name of the kart name and the host id. - * \param kart_name Name of the kart. - * \param host_id Id of the host who selected this character. - */ - CharacterConfirmMessage(const std::string &kart_name, int host_id) - : Message(Message::MT_CHARACTER_CONFIRM) - { - allocate(getStringLength(kart_name) + getCharLength()); - addString(kart_name); - addChar(host_id); - } // CharacterConfirmMessage - - // ------------------------------------------------------------------------ - /** Unpacks a character confirm message. - * \param pkt Received enet packet. - */ - CharacterConfirmMessage(ENetPacket* pkt):Message(pkt, MT_CHARACTER_CONFIRM) - { - m_kart_name = getString(); - m_host_id = getChar(); - } // CharacterConfirmMessage(EnetPacket) - // ------------------------------------------------------------------------ - /** Returns the kart name contained in a received message. */ - const std::string &getKartName() const { return m_kart_name; } - /** Returns the host id contained in a received message. */ - int getHostId() const { return m_host_id; } - -}; // CharacterConfirmMessage -#endif diff --git a/src/network/character_info_message.hpp b/src/network/character_info_message.hpp deleted file mode 100644 index 443a2d670..000000000 --- a/src/network/character_info_message.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_CHARACTER_INFO_MESSAGE_HPP -#define HEADER_CHARACTER_INFO_MESSAGE_HPP - -#include "karts/kart_properties_manager.hpp" -#include "network/message.hpp" - -/** This message is sent from the server to the clients and contains the list - * of available characters. Additionally, it contains the clients id. - */ -class CharacterInfoMessage : public Message -{ -// Add the remote host id to this message (to avoid sending this separately) -public: - CharacterInfoMessage(int hostid) : Message(Message::MT_CHARACTER_INFO) - { - std::vector all_karts = - kart_properties_manager->getAllAvailableKarts(); - allocate(getCharLength()+getStringVectorLength(all_karts)); - addChar(hostid); - addStringVector(all_karts); - } - // ------------------------------------------------------------------------ - CharacterInfoMessage(ENetPacket* pkt):Message(pkt, MT_CHARACTER_INFO) - { - int hostid=getChar(); - network_manager->setHostId(hostid); - std::vector all_karts; - all_karts = getStringVector(); - kart_properties_manager->setUnavailableKarts(all_karts); - } -}; // CharacterInfoMessage -#endif diff --git a/src/network/character_selected_message.hpp b/src/network/character_selected_message.hpp deleted file mode 100644 index 9e8a61961..000000000 --- a/src/network/character_selected_message.hpp +++ /dev/null @@ -1,105 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_CHARACTER_SELECTED_MESSAGE_HPP -#define HEADER_CHARACTER_SELECTED_MESSAGE_HPP - -#include "network/message.hpp" -#include "network/remote_kart_info.hpp" -#include "race/race_manager.hpp" - -/** This message is send contains information about selected karts. It is send - * from the client to the server to indicate a selected kart, and from the - * server to the clients to indicate that a kart was selected. In the latter - * case it contains the hostid of the successful selecter. This way a client - * selecting a kart can check if its selection was successful or not, and - * other clients are informed that a certain kart is not available anymore. - */ -class CharacterSelectedMessage : public Message -{ -private: - /** Number of local players on a host. If the message is send from the - * server to the clients, this field instead contains the host id of - * the host which selected the kart - */ - int m_num_local_players; - /** Stores information about the selected kart. */ - RemoteKartInfo m_kart_info; - -public: - /** Contains information about a selected kart. When send from the client - * to the server, it contains the number of local players (which - * technically needs only to be sent once); when send from from the server - * to the clients this field instead contains the host id of the host - * selected the character. This allows the client to detect if a selected - * kart was not confirmed by the server (i.e. another client or the server - * has selected the kart first - * \param player_id The local player id. - * \param host_id If this value is specified (>-1), then this value is - * used in the message instead of the number of local - * players. - */ - CharacterSelectedMessage(int player_id, int host_id=-1) - : Message(Message::MT_CHARACTER_INFO) - { - m_kart_info = race_manager->getLocalKartInfo(player_id); - m_num_local_players = race_manager->getNumLocalPlayers(); - - allocate(getCharLength() // m_kart_info.getLocalPlayerId()) - +getStringLength(m_kart_info.getKartName()) - +m_kart_info.getPlayerName().size() + 1 // FIXME: encoding issues - +getCharLength()); // m_num_local_players) - addChar(m_kart_info.getLocalPlayerId()); - addString(m_kart_info.getKartName()); - addString(core::stringc(m_kart_info.getPlayerName().c_str()).c_str()); // FIXME: encoding issues - // Piggy backing this information saves sending it as a separate - // message. It is actually only required in the first message - if(host_id>-1) - addChar(host_id); - else - addChar(race_manager->getNumLocalPlayers()); - } // CharacterSelectedMessage - - // ------------------------------------------------------------------------ - /** Unpacks a character selected message. The additional field is either - * the number of local players (when send from client to server), or the - * hostid of the host selected the character. - * \param pkt Received enet packet. - */ - CharacterSelectedMessage(ENetPacket* pkt):Message(pkt, MT_CHARACTER_INFO) - { - m_kart_info.setLocalPlayerId(getChar()); - m_kart_info.setKartName(getString()); - m_kart_info.setPlayerName(core::stringw(getString().c_str())); // FIXME: encoding issues - m_num_local_players = getChar(); - } // CharacterSelectedMessage(EnetPacket) - - // ------------------------------------------------------------------------ - /** Returns the remote kart info structure of the selected kart. */ - const RemoteKartInfo& getKartInfo () const { return m_kart_info; } - - /** Returns the number of local players. */ - int getNumPlayers() const { return m_num_local_players; } - - /** Returns the host id of the host who selected the kart successfully. - * This information is actually stored in m_num_local_players field, which - * is used when a client receives this message. - */ - int getHostId () const { return m_num_local_players; } -}; // CharacterSelectedMessage -#endif diff --git a/src/network/client_network_manager.cpp b/src/network/client_network_manager.cpp new file mode 100644 index 000000000..1aa96249c --- /dev/null +++ b/src/network/client_network_manager.cpp @@ -0,0 +1,128 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/client_network_manager.hpp" + +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/get_peer_address.hpp" +#include "network/protocols/connect_to_server.hpp" + +#include + +ClientNetworkManager::ClientNetworkManager() +{ +} + +ClientNetworkManager::~ClientNetworkManager() +{ +} + +void ClientNetworkManager::run() +{ + if (enet_initialize() != 0) + { + printf("Could not initialize enet.\n"); + return; + } + m_localhost = new STKHost(); + m_localhost->setupClient(1, 2, 0, 0); + m_localhost->startListening(); + + NetworkManager::run(); +} + +bool ClientNetworkManager::connectToHost(std::string serverNickname) +{ + printf("_NetworkInterface>Starting the connection to host protocol\n"); + // step 1 : retreive public address + Protocol* protocol = new GetPublicAddress(&m_public_address); + ProtocolManager::getInstance()->requestStart(protocol); + while (ProtocolManager::getInstance()->getProtocolState(protocol) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address is known.\n"); + + // step 2 : show the public address for others (here, the server) + ShowPublicAddress* spa = new ShowPublicAddress(NULL); + spa->setPassword(m_player_login.password); + spa->setUsername(m_player_login.username); + spa->setPublicAddress(m_public_address.ip, m_public_address.port); + ProtocolManager::getInstance()->requestStart(spa); + while (ProtocolManager::getInstance()->getProtocolState(spa) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address is being shown online.\n"); + + // step 3 : get the server's addres. + TransportAddress addr; + GetPeerAddress* gpa = new GetPeerAddress(&addr); + gpa->setPeerName(serverNickname); + ProtocolManager::getInstance()->requestStart(gpa); + while (ProtocolManager::getInstance()->getProtocolState(gpa) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address of the server is known.\n"); + + // step 4 : connect to the server + ConnectToServer* cts = new ConnectToServer(NULL); + cts->setServerAddress(addr.ip, addr.port); + ProtocolManager::getInstance()->requestStart(cts); + while (ProtocolManager::getInstance()->getProtocolState(cts) != PROTOCOL_STATE_TERMINATED ) + { + } + bool success = false; + if (m_localhost->isConnectedTo(TransportAddress(addr.ip, addr.port))) + { + success = true; + printf("_NetworkInterface> CONNECTION SUCCES : YOU ARE NOW CONNECTED TO A SERVER.\n"); + } + else + { + printf("_NetworkInterface> We are NOT connected to the server.\n"); + } + // step 5 : hide our public address + HidePublicAddress* hpa = new HidePublicAddress(NULL); + hpa->setPassword(m_player_login.password); + hpa->setUsername(m_player_login.username); + ProtocolManager::getInstance()->requestStart(hpa); + while (ProtocolManager::getInstance()->getProtocolState(hpa) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address is now hidden online.\n"); + + return success; +} + +void ClientNetworkManager::packetReceived(char* data) +{ + printf("ClientNetworkManager::packetReceived()\n"); + puts(data); +} +void ClientNetworkManager::sendPacket(char* data) +{ + if (m_peers.size() > 1) + printf("Ambiguous send of data\n"); + m_peers[0]->sendPacket(data); +} + +STKPeer* ClientNetworkManager::getPeer() +{ + return m_peers[0]; +} diff --git a/src/network/client_network_manager.hpp b/src/network/client_network_manager.hpp new file mode 100644 index 000000000..c2518a9d3 --- /dev/null +++ b/src/network/client_network_manager.hpp @@ -0,0 +1,48 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef CLIENT_NETWORK_MANAGER_HPP +#define CLIENT_NETWORK_MANAGER_HPP + +#include "network_manager.hpp" + +class ClientNetworkManager : public NetworkManager +{ + friend class Singleton; + public: + static ClientNetworkManager* getInstance() + { + return Singleton::getInstance(); + } + + virtual void run(); + + bool connectToHost(std::string serverNickname); + + virtual void packetReceived(char* data); + virtual void sendPacket(char* data); + + STKPeer* getPeer(); + virtual bool isServer() { return false; } + + protected: + ClientNetworkManager(); + virtual ~ClientNetworkManager(); +}; + +#endif // CLIENT_NETWORK_MANAGER_HPP diff --git a/src/network/connect_message.cpp b/src/network/connect_message.cpp deleted file mode 100644 index 4bbc3005c..000000000 --- a/src/network/connect_message.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/connect_message.hpp" - -#include -#include -#include -#ifndef WIN32 -# include -#endif - -#include "config/user_config.hpp" -#include "config/player.hpp" -#include "karts/kart_properties_manager.hpp" -#include "states_screens/state_manager.hpp" -#include "tracks/track_manager.hpp" - -// ---------------------------------------------------------------------------- -/** Creates the connect message. It includes the id of the client (currently - * player name @ hostname), and the list of available tracks. - */ -ConnectMessage::ConnectMessage() : Message(MT_CONNECT) -{ - setId(); - const std::vector &all_tracks = - track_manager->getAllTrackIdentifiers(); - std::vector all_karts = - kart_properties_manager->getAllAvailableKarts(); - allocate(getStringLength(m_id) + getStringVectorLength(all_tracks) - + getStringVectorLength(all_karts)); - addString(m_id); - addStringVector(all_tracks); - addStringVector(all_karts); -} // ConnectMessage - -// ---------------------------------------------------------------------------- -/** Unpacks a connect message. The id of the client is stored in this object, - * and the list of tracks is used to set tracks that are not available on - * the client to be 'unavailable' on the server. - * \param pkt Enet packet. - */ -ConnectMessage::ConnectMessage(ENetPacket* pkt):Message(pkt, MT_CONNECT) -{ - m_id = getString(); - std::vector all_tracks = getStringVector(); - std::vector all_karts = getStringVector(); - track_manager->setUnavailableTracks(all_tracks); - kart_properties_manager->setUnavailableKarts(all_karts); -} // ConnectMessage - -// ---------------------------------------------------------------------------- -/** Sets the id, i.e. player name @ hostname, of this client. - */ -void ConnectMessage::setId() -{ - char hostname[256]; - gethostname(hostname, 255); - const std::string& id = core::stringc(StateManager::get()->getActivePlayerProfile(0)->getName()).c_str(); - std::ostringstream o; - o << id << '@' << hostname; - m_id = o.str(); -} // ConnectMessage diff --git a/src/network/event.cpp b/src/network/event.cpp new file mode 100644 index 000000000..f0a21bf2e --- /dev/null +++ b/src/network/event.cpp @@ -0,0 +1,73 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "event.hpp" + +#include "network_manager.hpp" + +#include +#include + +Event::Event(ENetEvent* event) +{ + switch (event->type) + { + case ENET_EVENT_TYPE_CONNECT: + type = EVENT_TYPE_CONNECTED; + break; + case ENET_EVENT_TYPE_DISCONNECT: + type = EVENT_TYPE_DISCONNECTED; + break; + case ENET_EVENT_TYPE_RECEIVE: + type = EVENT_TYPE_MESSAGE; + break; + default: + break; + } + if (type == EVENT_TYPE_MESSAGE) + data = std::string((char*)(event->packet->data)); + else if (event->data) + data = std::string((char*)(event->data)); + + if (event->packet) + m_packet = event->packet; + + std::vector peers = NetworkManager::getInstance()->getPeers(); + peer = NULL; + for (unsigned int i = 0; i < peers.size(); i++) + { + if (*peers[i] == event->peer) + { + peer = peers[i]; + return; + } + } + if (peer == NULL) + { + printf("The peer still does not exist in %lu peers\n", peers.size()); + STKPeer* new_peer = new STKPeer(); + new_peer->m_peer = event->peer; + peer = new_peer; + } +} + +Event::~Event() +{ + if (m_packet) + enet_packet_destroy(m_packet); +} diff --git a/src/network/event.hpp b/src/network/event.hpp new file mode 100644 index 000000000..18c7d3a34 --- /dev/null +++ b/src/network/event.hpp @@ -0,0 +1,69 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef EVENT_HPP +#define EVENT_HPP + +#include "stk_peer.hpp" +#include +#include + +/*! + * \enum EVENT_TYPE + * \brief Represents a network event type. + */ +enum EVENT_TYPE +{ + EVENT_TYPE_CONNECTED, //!< A peer is connected + EVENT_TYPE_DISCONNECTED,//!< A peer is disconnected + EVENT_TYPE_MESSAGE //!< A message between server and client protocols +}; + +/*! + * \class Event + * \brief Class representing an event that need to pass trough the system. + * This is used to remove ENet dependency in the network. + * It interfaces the ENetEvent structure. + * The user has to be extremely careful about the peer. + * Indeed, when packets are logged, the state of the peer cannot be stored at + * all times, and then the user of this class can rely only on the address/port + * of the peer, and not on values that might change over time. + */ +class Event +{ + public: + /*! + * \brief Constructor + * \param event : The event that needs to be translated. + */ + Event(ENetEvent* event); + /*! + * \brief Destructor + * frees the memory of the ENetPacket. + */ + ~Event(); + + EVENT_TYPE type; //!< Type of the event. + std::string data; //!< Copy of the data passed by the event. + STKPeer* peer; //!< Pointer to the peer that triggered that event. + + private: + ENetPacket* m_packet; //!< A pointer on the ENetPacket to be deleted. +}; + +#endif // EVENT_HPP diff --git a/src/network/flyable_info.hpp b/src/network/flyable_info.hpp deleted file mode 100644 index eedc33585..000000000 --- a/src/network/flyable_info.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_FLYABLE_INFO_HPP -#define HEADER_FLYABLE_INFO_HPP - -#include "network/message.hpp" - -/** Class used to transfer information about projectiles from server to client. - * It contains only the coordinates, rotation, and explosion state. - */ -class FlyableInfo -{ -public: - Vec3 m_xyz; /** Position of object. */ - btQuaternion m_rotation; /** Orientation of object */ - bool m_exploded; /** If the object exploded in the current frame. */ - - /** Constructor to initialise all fields. - */ - FlyableInfo(const Vec3& xyz, const btQuaternion &rotation, bool exploded) : - m_xyz(xyz), m_rotation(rotation), m_exploded(exploded) - {}; - // ------------------------------------------------------------------------ - /** Allow this object to be stored in std::vector fields. - */ - FlyableInfo() {}; - // ------------------------------------------------------------------------ - /** Construct a FlyableInfo from a message (which is unpacked). - */ - FlyableInfo(Message *m) - { - m_xyz = m->getVec3(); - m_rotation = m->getQuaternion(); - m_exploded = m->getBool(); - } // FlyableInfo(Message) - // ------------------------------------------------------------------------ - /** Returns the length of the serialised message. */ - static int getLength() - { - return Message::getVec3Length() - + Message::getQuaternionLength() - + Message::getBoolLength(); - } // getLength - // ------------------------------------------------------------------------ - void serialise(Message *m) - { - m->addVec3(m_xyz); - m->addQuaternion(m_rotation); - m->addBool(m_exploded); - } // serialise -}; - -#endif - diff --git a/src/network/http_functions.cpp b/src/network/http_functions.cpp new file mode 100644 index 000000000..240178816 --- /dev/null +++ b/src/network/http_functions.cpp @@ -0,0 +1,67 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "http_functions.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace HTTP +{ +CURL *curl; +CURLcode res; + +static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; +} + +void init() +{ + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); + if(!curl) + printf("Error while loading cURL library.\n"); +} + +std::string getPage(std::string url) +{ + std::string readBuffer; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + res = curl_easy_perform(curl); + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + return readBuffer; +} + +void shutdown() +{ + curl_easy_cleanup(curl); + curl_global_cleanup(); +} + +} diff --git a/src/network/kart_update_message.hpp b/src/network/http_functions.hpp similarity index 72% rename from src/network/kart_update_message.hpp rename to src/network/http_functions.hpp index 7d3a38fc4..970b9c1dd 100644 --- a/src/network/kart_update_message.hpp +++ b/src/network/http_functions.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,15 +16,20 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_KART_UPDATE_MESSAGE_HPP -#define HEADER_KART_UPDATE_MESSAGE_HPP +#ifndef HTTP_FUNCTIONS_HPP +#define HTTP_FUNCTIONS_HPP -#include "network/message.hpp" +#include -class KartUpdateMessage : public Message +namespace HTTP { -public: - KartUpdateMessage(); - KartUpdateMessage(ENetPacket* pkt); -}; // KartUpdateMessage -#endif + +void init(); +void shutdown(); + +std::string getPage(std::string url); + + +} + +#endif // HTTP_FUNCTIONS_HPP diff --git a/src/network/item_info.hpp b/src/network/item_info.hpp deleted file mode 100644 index 0334e2ee0..000000000 --- a/src/network/item_info.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_ITEM_INFO_HPP -#define HEADER_ITEM_INFO_HPP -/** Class used to transfer information about collected items from -* server to client. -*/ -class ItemInfo -{ -public: - /** Kart id (in world) of the kart collecting the item. */ - unsigned char m_kart_id; - /** Index of the collected item. This is set to -1 if a kart - * triggers a rescue (i.e. attaches the butterfly).] - */ - short m_item_id; - /** Additional info used, depending on item type. This is usually - * the type of the collected item. - */ - char m_add_info; - - /** Constructor to initialise all fields. */ - ItemInfo(int kart, int item, char add_info) : - m_kart_id(kart), m_item_id(item), - m_add_info(add_info) - {} - // ------------------------------------------------------------- - /** Construct ItemInfo from a message (which is unpacked). */ - ItemInfo(Message *m) - { - m_kart_id = m->getChar(); - m_item_id = m->getShort(); - m_add_info = m->getChar(); - } - // ------------------------------------------------------------- - /*** Returns size in bytes necessary to store ItemInfo. */ - static int getLength() {return 2*Message::getCharLength() - + Message::getShortLength();} - // ------------------------------------------------------------- - /** Serialises this object into the message object */ - void serialise(Message *m) - { - m->addChar(m_kart_id); - m->addShort(m_item_id); - m->addChar(m_add_info); - } // serialise -}; // ItemInfo - -#endif - diff --git a/src/network/kart_control_message.cpp b/src/network/kart_control_message.cpp deleted file mode 100644 index db6cea020..000000000 --- a/src/network/kart_control_message.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/kart_control_message.hpp" - -#include "karts/controller/controller.hpp" -#include "modes/world.hpp" -#include "network/network_kart.hpp" - -KartControlMessage::KartControlMessage() - : Message(Message::MT_KART_CONTROL) -{ - World *world=World::getWorld(); - unsigned int num_local_players = race_manager->getNumLocalPlayers(); - unsigned int control_size = KartControl::getLength(); - allocate(control_size*num_local_players); - for(unsigned int i=0; igetLocalPlayerKart(i); - const KartControl& controls = kart->getControls(); - controls.serialise(this); - } -} // KartControlMessage -// ---------------------------------------------------------------------------- -/** Receives a kart control message. - * \param kart_id_offset is the global id of the first kart on the host from - * which this packet was received. - */ -KartControlMessage::KartControlMessage(ENetPacket* pkt, int kart_id_offset, - int num_local_players) - : Message(pkt, MT_KART_CONTROL) -{ - // FIXME: This probably does not work anymore - it assume that - // num_local_Players is the number of all local karts, while it might - // only be the number of all network karts. - for(int i=kart_id_offset; igetKart(i); - if(kart->getController()->isNetworkController()) - { - ((NetworkKart*)kart)->setControl(kc); - } - } -}; // KartControlMessage - diff --git a/src/network/kart_update_message.cpp b/src/network/kart_update_message.cpp deleted file mode 100644 index 2b16cc0e9..000000000 --- a/src/network/kart_update_message.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/kart_update_message.hpp" - -#include "karts/abstract_kart.hpp" -#include "modes/world.hpp" - -KartUpdateMessage::KartUpdateMessage() - : Message(Message::MT_KART_INFO) -{ - World *world = World::getWorld(); - unsigned int num_karts = world->getNumKarts(); - - // Send the number of karts and for each kart the compressed - // control structure (3 ints) and xyz,hpr (4 floats: quaternion: - allocate(getCharLength()+ - num_karts*(KartControl::getLength() + getVec3Length() - +getQuaternionLength()) ); - addChar(num_karts); - for(unsigned int i=0; igetKart(i); - const KartControl& kc=kart->getControls(); - kc.serialise(this); - addVec3(kart->getXYZ()); - addQuaternion(kart->getRotation()); - } // for i -} // KartUpdateMessage -// ---------------------------------------------------------------------------- -KartUpdateMessage::KartUpdateMessage(ENetPacket* pkt) - : Message(pkt, MT_KART_INFO) -{ - World *world = World::getWorld(); - unsigned int num_karts = getInt(); - for(unsigned int i=0; igetKart(i); - kart->setXYZ(xyz); - kart->setRotation(q); - } // for i -}; // KartUpdateMessage - diff --git a/src/network/message.cpp b/src/network/message.cpp deleted file mode 100644 index b1f754c02..000000000 --- a/src/network/message.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs, Stephen Leak -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/message.hpp" - -#include -#include -#include -#include - -/** Creates a message to be sent. - * This only initialised the data structures, it does not reserve any memory. - * A call to allocate() is therefore necessary. - * \param type The type of the message - */ -Message::Message(MessageType type) -{ - assert(sizeof(int)==4); - m_type = type; - m_pos = 0; - m_data_size = -1; - m_data = NULL; - m_needs_destroy = 0; // enet destroys message after send -} // Message - -// ---------------------------------------------------------------------------- -/** Handles a received message. - * The message in pkt is received, and the message type is checked. - * \param pkt The ENetPacket - * \param m The type of the message. The type is checked via an assert! - */ - -Message::Message(ENetPacket* pkt, MessageType m) -{ - receive(pkt, m); -} - -// ---------------------------------------------------------------------------- -/** Loads the message in pkt, and checks for the correct message type. - * Paramaters: - * \param pkt The ENetPacket - * \param m The type of the message. The type is checked via an assert! - */ -void Message::receive(ENetPacket* pkt, MessageType m) -{ - assert(sizeof(int)==4); - m_pkt = pkt; - m_data_size = pkt->dataLength; - m_data = (char*)pkt->data; - m_type = (MessageType)m_data[0]; - if(m_type!=m) - printf("type %d %d\n",m_type,m); - assert(m_type==m); - m_pos = 1; - m_needs_destroy = true; -} // Message - -// ---------------------------------------------------------------------------- -/** Frees the memory allocated for this message. */ -Message::~Message() -{ - clear(); -} // ~Message - -// ---------------------------------------------------------------------------- -/** Frees the memory for a received message. - * Calls enet_packet_destroy if necessary (i.e. if the message was received). - * The memory for a message created to be sent does not need to be freed, it - * is handled by enet. */ -void Message::clear() -{ - if(m_needs_destroy) - enet_packet_destroy(m_pkt); -} // clear - -// ---------------------------------------------------------------------------- -/** Reserves the memory for a message. - * \param size Number of bytes to reserve. - */ -void Message::allocate(int size) -{ - m_data_size = size+1; - m_pkt = enet_packet_create (NULL, m_data_size, ENET_PACKET_FLAG_RELIABLE); - m_data = (char*)m_pkt->data; - m_data[0] = m_type; - m_pos = 1; -} // allocate - -// ---------------------------------------------------------------------------- -/** Adds an integer value to the message. - * \param data The integer value to add. - */ -void Message::addInt(int data) -{ - assert((int)(m_pos + sizeof(int)) <= m_data_size); - int l=htonl(data); - memcpy(m_data+m_pos, &l, sizeof(int)); - m_pos+=sizeof(int); -} // addInt - -// ---------------------------------------------------------------------------- -/** Extracts an integer value from a message. - * \return The extracted integer. - */ -int Message::getInt() -{ - m_pos+=sizeof(int); - return ntohl(*(int*)(&m_data[m_pos-sizeof(int)])); -} // getInt - -// ---------------------------------------------------------------------------- -/** Adds a short value to the message. - * \param data The integer value to add. - */ -void Message::addShort(short data) -{ - assert((int)(m_pos + sizeof(short)) <= m_data_size); - int l=htons(data); - memcpy(m_data+m_pos, &l, sizeof(short)); - m_pos+=sizeof(short); -} // addShort - -// ---------------------------------------------------------------------------- -/** Extracts a short value from a message. - * \return The short value. - */ -short Message::getShort() -{ - m_pos+=sizeof(short); - return ntohs(*(short*)(&m_data[m_pos-sizeof(short)])); -} // getShort - -// ---------------------------------------------------------------------------- -/** Adds a floating point value to the message. - * \param data Floating point value to add. - */ -void Message::addFloat(const float data) -{ - // The simple approach of using addInt(*(int*)&data) - // does not work (at least with optimisation on certain g++ versions, - // see getFloat for more details) - int n; - memcpy(&n, &data, sizeof(float)); - addInt(n); -} // addFloat -// ---------------------------------------------------------------------------- -float Message::getFloat() -{ - int i = getInt(); - float f; - memcpy(&f, &i, sizeof(int)); - return f; - // The 'obvious' way: - // float *f = (float*) &i; - // return *f; - // does NOT work, see http://www.velocityreviews.com/forums/showthread.php?t=537336 - // for details -} // getFloat - -// ---------------------------------------------------------------------------- -void Message::addString(const std::string &data) -{ - int len = data.size()+1; // copy 0 end byte - assert((int)(m_pos+len) <=m_data_size); - memcpy (&(m_data[m_pos]), data.c_str(), len); - m_pos += len; -} // addString - -// ---------------------------------------------------------------------------- -std::string Message::getString() -{ - char *str = (char*) &(m_data[m_pos]); - int len = strlen(str)+1; - m_pos += len; - return std::string(str); -} // getString - -// ---------------------------------------------------------------------------- -/** Returns the number of bytes necessary to store a string vector. - * \param vs std::vector - */ -int Message::getStringVectorLength(const std::vector& vs) -{ - int len=getShortLength(); - for(unsigned int i=0; i to the message. - */ -void Message::addStringVector(const std::vector& vs) -{ - assert(vs.size()<32767); - addShort(vs.size()); - for(unsigned short i=0; i Message::getStringVector() -{ - std::vector vs; - vs.resize(getShort()); - for(unsigned int i=0; i -#include -#include -#include -#include "btBulletDynamicsCommon.h" - -using std::memcpy; - -#include "enet/enet.h" - -#include "utils/vec3.hpp" - -// sjl: when a message is received, need to work out what kind of message it -// is and therefore what to do with it - -/** Base class to serialises/deserialises messages. - * This is the base class for all messages being exchange between client - * and server. It handles the interface to enet, and adds a message type - * (which is checked via an assert to help finding bugs by receiving a - * message of an incorrect type). It also takes care of endianess (though - * floats are converted via a byte swap, too - so it must be guaranteed - * that the float representation between all machines is identical). - */ -class Message -{ -public: - /** Contains all tags used in identifying a message. */ - enum MessageType {MT_CONNECT=1, MT_CHARACTER_INFO, MT_CHARACTER_CONFIRM, - MT_RACE_INFO, MT_RACE_START, MT_WORLD_LOADED, - MT_KART_INFO, MT_KART_CONTROL, MT_RACE_STATE, - MT_RACE_RESULT, MT_RACE_RESULT_ACK - }; -private: - ENetPacket *m_pkt; - char *m_data; - MessageType m_type; - int m_data_size; - unsigned int m_pos; // simple stack counter for constructing packet data - bool m_needs_destroy; // only received messages need to be destroyed - -public: - void addInt(int data); - void addShort(short data); - void addString(const std::string &data); - void addStringVector(const std::vector& vs); - void addUInt(unsigned int data) { addInt(*(int*)&data); } - void addFloat(const float data); - void addBool(bool data) { addChar(data?1:0); } - void addChar(char data) { addCharArray((char*)&data,1);} - void addCharArray(char *c, unsigned int n=1) - { assert((int)(m_pos+n)<=m_data_size); - memcpy(m_data+m_pos,c,n); - m_pos+=n; } -#ifndef WIN32 // on windows size_t is unsigned int - void addSizeT(size_t data) { addInt((int)data); } -#endif - void addIntArray(int *d, unsigned int n) - { for(unsigned int i=0; - i - getStringVector(); - char getChar() {char c;getCharArray(&c,1); - return c; } - void getCharArray(char *c, int n=1) {memcpy(c,m_data+m_pos,n); - m_pos+=n; - return; } - Vec3 getVec3() { Vec3 v; - v.setX(getFloat()); - v.setY(getFloat()); - v.setZ(getFloat()); - return v; } - btQuaternion getQuaternion() { btQuaternion q; - q.setX(getFloat()); - q.setY(getFloat()); - q.setZ(getFloat()); - q.setW(getFloat()); - return q; } - static int getIntLength() { return sizeof(int); } - static int getUIntLength() { return sizeof(int); } - static int getShortLength() { return sizeof(short); } - static int getCharLength() { return sizeof(char); } - static int getBoolLength() { return sizeof(char); } - static int getFloatLength() { return sizeof(float); } - static int getStringLength(const std::string& s) { return s.size()+1;} - static int getVec3Length() { return 3*sizeof(float); } - static int getQuaternionLength() { return 4*sizeof(float); } - static int getStringVectorLength(const std::vector& vs); -#ifndef WIN32 - static int getSizeTLength(size_t n) { return sizeof(int); } -#endif - -public: - Message(MessageType m); - Message(ENetPacket *pkt, MessageType m); - void receive(ENetPacket *pkt, MessageType m); - ~Message(); - void clear(); - void allocate(int size); - MessageType getType() const { return m_type; } - ENetPacket* getPacket() const { assert(m_data_size>-1); return m_pkt; } - /** Return the type of a message without unserialising the message */ - static MessageType peekType(ENetPacket *pkt) - { return (MessageType)pkt->data[0];} - -}; // Message - - -#endif - diff --git a/src/network/kart_control_message.hpp b/src/network/network_interface.cpp similarity index 68% rename from src/network/kart_control_message.hpp rename to src/network/network_interface.cpp index 6127f62b9..c3222c866 100644 --- a/src/network/kart_control_message.hpp +++ b/src/network/network_interface.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,16 +16,13 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_KART_CONTROL_MESSAGE_HPP -#define HEADER_KART_CONTROL_MESSAGE_HPP +#include "network_interface.hpp" -#include "network/message.hpp" -class KartControlMessage : public Message +NetworkInterface::NetworkInterface() { -public: - KartControlMessage(); - KartControlMessage(ENetPacket* pkt, int kart_id_offset, - int num_local_players); -}; // KartUpdateMessage -#endif +} + +NetworkInterface::~NetworkInterface() +{ +} diff --git a/src/network/race_start_message.hpp b/src/network/network_interface.hpp similarity index 58% rename from src/network/race_start_message.hpp rename to src/network/network_interface.hpp index eac2d7594..8221a83d2 100644 --- a/src/network/race_start_message.hpp +++ b/src/network/network_interface.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,25 +16,30 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_RACE_START_MESSAGE_HPP -#define HEADER_RACE_START_MESSAGE_HPP +#ifndef NETWORK_INTERFACE_H +#define NETWORK_INTERFACE_H -#include "network/message.hpp" -#include "network/remote_kart_info.hpp" -#include "race/race_manager.hpp" +#include "singleton.hpp" +#include "types.hpp" +#include "network_manager.hpp" -class RaceStartMessage : public Message +#include +#include +#include + + +class NetworkInterface : public Singleton { -private: -// For now this is an empty message -public: - RaceStartMessage() : Message(Message::MT_RACE_START) - { - allocate(0); - } // RaceStartMessage + friend class Singleton; + public: + + void initNetwork(bool server); + + protected: + // protected functions + NetworkInterface(); + virtual ~NetworkInterface(); + +}; - RaceStartMessage(ENetPacket* pkt):Message(pkt, MT_RACE_START) - { - } -}; // RaceStartMessage -#endif +#endif // NETWORK_INTERFACE_H diff --git a/src/network/network_kart.cpp b/src/network/network_kart.cpp deleted file mode 100644 index 42cf8a3cc..000000000 --- a/src/network/network_kart.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 SuperTuxKart-Team, Joerg Henrichs, Steve Baker -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/network_manager.hpp" -#include "network/network_kart.hpp" - -/** A network kart. On the server, it receives its control information (steering etc) - from the network manager. - */ -NetworkKart::NetworkKart(const std::string &kart_name, - unsigned int world_kart_id, int position, - const btTransform &init_transform, - int global_player_id, - RaceManager::KartType type) - : Kart(kart_name, world_kart_id, position, - init_transform) -{ - m_global_player_id = global_player_id; -} // NetworkKart - -// ---------------------------------------------------------------------------- -void NetworkKart::setControl(const KartControl& kc) -{ - assert(network_manager->getMode()==NetworkManager::NW_SERVER); - m_controls = kc; -} // setControl - diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index 2554e070d..6c4eebf85 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs, Stephen Leak +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,725 +16,112 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "network/network_manager.hpp" +#include "network_manager.hpp" -#include "config/stk_config.hpp" -#include "config/user_config.hpp" -#include "karts/kart_properties_manager.hpp" -#include "modes/world.hpp" -#include "network/connect_message.hpp" -#include "network/character_info_message.hpp" -#include "network/character_selected_message.hpp" -#include "network/race_info_message.hpp" -#include "network/race_start_message.hpp" -#include "network/world_loaded_message.hpp" -#include "network/race_state.hpp" -#include "network/kart_control_message.hpp" -#include "network/character_confirm_message.hpp" -#include "network/race_result_message.hpp" -#include "network/race_result_ack_message.hpp" -#include "race/race_manager.hpp" +#include "protocols/hide_public_address.hpp" +#include "protocols/show_public_address.hpp" +#include "protocols/get_public_address.hpp" + +#include "protocol_manager.hpp" +#include "client_network_manager.hpp" +#include "server_network_manager.hpp" + +#include + +void* protocolManagerUpdate(void* data) +{ + ProtocolManager* manager = static_cast(data); + while(1) + { + manager->update(); + } + return NULL; +} -NetworkManager* network_manager = 0; NetworkManager::NetworkManager() { - m_mode = NW_NONE; - m_state = NS_ACCEPT_CONNECTIONS; - m_host = NULL; + m_public_address.ip = 0; + m_public_address.port = 0; + m_protocol_manager_update_thread = NULL; +} - m_num_clients = 0; - m_host_id = 0; - - if (enet_initialize () != 0) - { - fprintf (stderr, "An error occurred while initializing ENet.\n"); - exit(-1); - } -} // NetworkManager - -// ----------------------------------------------------------------------------- -bool NetworkManager::initialiseConnections() +NetworkManager::~NetworkManager() { - switch(m_mode) - { - case NW_NONE: return true; - case NW_CLIENT: return initClient(); - case NW_SERVER: return initServer(); - } - return true; -} // NetworkManager +} -// ----------------------------------------------------------------------------- -NetworkManager::~NetworkManager() +void NetworkManager::run() { - if(m_mode==NW_SERVER || m_mode==NW_CLIENT) enet_host_destroy(m_host); - enet_deinitialize(); -} // ~NetworkManager + ProtocolManager::getInstance(); + m_protocol_manager_update_thread = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_protocol_manager_update_thread, NULL, protocolManagerUpdate, ProtocolManager::getInstance()); +} -// ----------------------------------------------------------------------------- -bool NetworkManager::initServer() +bool NetworkManager::connect(TransportAddress peer) { - ENetAddress address; - address.host = ENET_HOST_ANY; - address.port = UserConfigParams::m_server_port; + if (peerExists(peer)) + return isConnectedTo(peer); + + return STKPeer::connectToHost(m_localhost, peer, 2, 0); +} - m_host = enet_host_create (& address /* the address to bind the server host to */, - stk_config->m_max_karts /* number of connections */, - 0 /* channel limit */, - 0 /* incoming bandwidth */, - 0 /* outgoing bandwidth */ ); - if (m_host == NULL) - { - fprintf (stderr, - "An error occurred while trying to create an ENet server host.\n" - "Progressing in non-network mode\n"); - m_mode = NW_NONE; - return false; - } - - m_server = NULL; - m_clients.push_back(NULL); // server has host_id=0, so put a dummy entry at 0 in client array - - m_client_names.push_back("server"); - return true; -} // initServer - -// ----------------------------------------------------------------------------- -/** Initialises the client. This function tries to connect to the server. - */ -bool NetworkManager::initClient() +void NetworkManager::setManualSocketsMode(bool manual) { - m_host = enet_host_create (NULL /* create a client host */, - 1 /* only allow 1 outgoing connection */, - 0 /* channel limit */, - 0 /* downstream bandwidth unlimited */, - 0 /* upstream bandwidth unlimited */ ); - - if (m_host == NULL) - { - fprintf (stderr, - "An error occurred while trying to create an ENet client host.\n"); - return false; - } - - ENetAddress address; - ENetEvent event; - ENetPeer *peer; - - enet_address_set_host (& address, UserConfigParams::m_server_address.c_str()); - address.port = UserConfigParams::m_server_port; - - /* Initiate the connection, allocating the two channels 0 and 1. */ - peer = enet_host_connect (m_host, &address, 2, 0); - - if (peer == NULL) - { - fprintf(stderr, - "No available peers for initiating an ENet connection.\n"); - return false; - } - - /* Wait up to 5 seconds for the connection attempt to succeed. */ - if (enet_host_service (m_host, & event, 5000) <= 0 || - event.type != ENET_EVENT_TYPE_CONNECT) - { - /* Either the 5 seconds are up or a disconnect event was */ - /* received. Reset the peer in the event the 5 seconds */ - /* had run out without any significant event. */ - enet_peer_reset (peer); - - fprintf(stderr, "Connection to '%s:%d' failed.\n", - UserConfigParams::m_server_address.c_str(), (int)UserConfigParams::m_server_port); - return false; - } - m_server = peer; - return true; -} // initClient - -// ---------------------------------------------------------------------------- -/** Switches the network manager to client mode. This function sets the state - * to waiting_for_chars (so that the message from the server containing all - * available characters can be received). - */ -void NetworkManager::becomeClient() -{ - m_mode = NW_CLIENT; - m_state = NS_WAIT_FOR_AVAILABLE_CHARACTERS; -} // becomeClient - -// ---------------------------------------------------------------------------- -/** Switches the network manager to server mode. This function sets the state - * to accepting connections. - */ -void NetworkManager::becomeServer() -{ - m_mode = NW_SERVER; - m_state = NS_ACCEPT_CONNECTIONS; -} // becomeServer - -// ---------------------------------------------------------------------------- -/** Called in case of an error, to switch back to non-networking mode. -*/ -void NetworkManager::disableNetworking() -{ - m_mode=NW_NONE; - if (m_host != NULL) - { - enet_host_destroy(m_host); - m_host = NULL; - } - // FIXME: what other enet data structures do we have to free/reset??? - -} // disableNetworking - -// ---------------------------------------------------------------------------- -void NetworkManager::handleNewConnection(ENetEvent *event) -{ - // Only accept while waiting for connections - if(m_state!=NS_ACCEPT_CONNECTIONS) return; - - // The logical connection (from STK point of view) happens when - // the connection message is received. But for now reserve the - // space in the data structures (e.g. in case that two connects - // happen before a connect message is received - m_client_names.push_back("NOT SET YET"); - m_clients.push_back(event->peer); - event->peer->data = (void*)int(m_clients.size()-1); // save hostid in peer data - -} // handleNewConnection - -// ---------------------------------------------------------------------------- -void NetworkManager::handleDisconnection(ENetEvent *event) -{ - if(m_state!=NS_ACCEPT_CONNECTIONS) - { - fprintf(stderr, "Disconnect while in race - close your eyes and hope for the best.\n"); - return; - } - fprintf(stderr, "%x:%d disconnected (host id %d).\n", event->peer->address.host, - event->peer->address.port, (int)(long)event->peer->data ); - m_num_clients--; -} // handleDisconnection - -// ---------------------------------------------------------------------------- -void NetworkManager::handleMessageAtServer(ENetEvent *event) -{ // handle message at server (from client) - - switch(m_state) - { - case NS_ACCEPT_CONNECTIONS: - { - ConnectMessage m(event->packet); - m_client_names[(int)(long)event->peer->data] = m.getId(); - m_num_clients++; - return; - } - case NS_KART_CONFIRMED: // Fall through - case NS_CHARACTER_SELECT: - { - CharacterSelectedMessage m(event->packet); - unsigned int hostid=(unsigned int)(long)event->peer->data; - assert(hostid>=1 && hostid<=m_num_clients); - if(m_num_local_players[hostid]==-1) // first package from that host - { - m_num_local_players[hostid] = m.getNumPlayers(); - m_num_all_players += m.getNumPlayers(); - // count how many hosts have sent (at least) one message - m_barrier_count ++; - } - RemoteKartInfo ki=m.getKartInfo(); - ki.setHostId(hostid); - m_kart_info.push_back(ki); - - int kart_id = kart_properties_manager->getKartId(ki.getKartName()); - kart_properties_manager->testAndSetKart(kart_id); - // TODO - character selection screen in networking - /* - CharSel *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->updateAvailableCharacters(); - */ - - // Broadcast the information about a selected kart to all clients - CharacterConfirmMessage ccm(ki.getKartName(), hostid); - broadcastToClients(ccm); - // See if this was the last message, i.e. we have received at least - // one message from each client, and the size of the kart_info - // array is the same as the number of all players (which does not - // yet include the number of players on the host). - if(m_barrier_count == (int)m_num_clients && - m_num_all_players==(int)m_kart_info.size()) - { - // we can't send the race info yet, since the server might - // not yet have selected all characters! - m_state = NS_ALL_REMOTE_CHARACTERS_DONE; - } - break; - } - case NS_READY_SET_GO_BARRIER: - { - m_barrier_count ++; - if(m_barrier_count==(int)m_num_clients) - { - m_state = NS_RACING; - RaceStartMessage m; - broadcastToClients(m); - } - break; - } - case NS_RACE_RESULT_BARRIER: - { - // Other message, esp. kart control, are silently ignored. - // FIXME: we might want to make sure that no such message actually arrives - if(Message::peekType(event->packet)!=Message::MT_RACE_RESULT_ACK) - { - enet_packet_destroy(event->packet); - return; - } - m_barrier_count++; - if(m_barrier_count==(int)m_num_clients) - { - m_state = NS_MAIN_MENU; - } - break; - } - default: assert(0); // should not happen - } // switch m_state -} // handleMessageAtServer - -// ---------------------------------------------------------------------------- -void NetworkManager::handleMessageAtClient(ENetEvent *event) -{ // handle message at client (from server) - switch(m_state) - { - case NS_WAIT_FOR_AVAILABLE_CHARACTERS: - { - CharacterInfoMessage m(event->packet); - // FIXME: handle list of available characters - m_state = NS_CHARACTER_SELECT; - break; - } - case NS_CHARACTER_SELECT: - { - CharacterConfirmMessage m(event->packet); - kart_properties_manager->selectKartName(m.getKartName()); - // TODO - karts selection screen in networking - /* - CharSel *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->updateAvailableCharacters(); - */ - break; - } - case NS_WAIT_FOR_KART_CONFIRMATION: - { - CharacterConfirmMessage m(event->packet); - kart_properties_manager->selectKartName(m.getKartName()); - - // If the current menu is the character selection menu, - // update the menu so that the newly taken character is removed. - // TODO - kart selection screen and networking - /* - CharSel *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->updateAvailableCharacters();*/ - // Check if we received a message about the kart we just selected. - // If so, the menu needs to progress, otherwise a different kart - // must be selected by the current player. - if(m.getKartName()==m_kart_to_confirm) - { - int host_id = m.getHostId(); - m_state = (host_id == getMyHostId()) ? NS_KART_CONFIRMED - : NS_CHARACTER_SELECT; - } // m.getkartName()==m_kart_to_confirm - break; - } // wait for kart confirmation - case NS_WAIT_FOR_RACE_DATA: - { - // It is possible that character confirm messages arrive at the - // client when it has already left the character selection screen. - // In this case the messages can simply be ignored. - if(Message::peekType(event->packet)==Message::MT_CHARACTER_CONFIRM) - { - // Receiving it will automatically free the memory. - CharacterConfirmMessage m(event->packet); - return; - } - RaceInfoMessage m(event->packet); - // The constructor actually sets the information in the race manager - m_state = NS_LOADING_WORLD; - break; - } - case NS_READY_SET_GO_BARRIER: - { - // Not actually needed, but the destructor of RaceStartMessage - // will free the memory of the event. - RaceStartMessage m(event->packet); - m_state = NS_RACING; - break; - } - case NS_RACING: - { - assert(false); // should never be here while racing - break; - } - case NS_RACE_RESULT_BARRIER: - { - RaceResultAckMessage message(event->packet); - // TODO - race results menu in networking - /* - RaceResultsGUI *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->setSelectedWidget(message.getSelectedMenu());*/ - m_state = NS_RACE_RESULT_BARRIER_OVER; - break; - } - default: - { - printf("received unknown message: type %d\n", - Message::peekType(event->packet)); - // assert(0); // should not happen - } - } // switch m_state -} // handleMessageAtClient - -// ---------------------------------------------------------------------------- -void NetworkManager::update(float dt) -{ - if(m_mode==NW_NONE) return; - // Messages during racing are handled in the sendUpdates/receiveUpdate - // calls, so don't do anything in this case. - if(m_state==NS_RACING) return; - - ENetEvent event; - int result = enet_host_service (m_host, &event, 0); - if(result==0) return; - if(result<0) - { - fprintf(stderr, "Error while receiving messages -> ignored.\n"); - return; - } - switch (event.type) - { - case ENET_EVENT_TYPE_CONNECT: handleNewConnection(&event); break; - case ENET_EVENT_TYPE_RECEIVE: - if(m_mode==NW_SERVER) - handleMessageAtServer(&event); - else - handleMessageAtClient(&event); - break; - case ENET_EVENT_TYPE_DISCONNECT: handleDisconnection(&event); break; - case ENET_EVENT_TYPE_NONE: break; - } -} // update - -// ---------------------------------------------------------------------------- -void NetworkManager::broadcastToClients(Message &m) -{ - enet_host_broadcast(m_host, 0, m.getPacket()); - enet_host_flush(m_host); -} // broadcastToClients - -// ---------------------------------------------------------------------------- -void NetworkManager::sendToServer(Message &m) -{ - enet_peer_send(m_server, 0, m.getPacket()); - enet_host_flush(m_host); -} // sendToServer - -// ---------------------------------------------------------------------------- -/** Cleans up character related data structures. Must be called before any - * character related data is set. - */ -void NetworkManager::initCharacterDataStructures() -{ - // This is called the first time the character selection menu is displayed - - if(m_mode==NW_CLIENT) - { - // Change state to wait for list of characters from server - m_state = NS_WAIT_FOR_AVAILABLE_CHARACTERS; - } - else // Server or no network - { - if(m_mode==NW_SERVER) - { - // server: create message with all valid characters - // ================================================ - for(unsigned int i=1; i<=m_num_clients; i++) - { - CharacterInfoMessage m(i); - enet_peer_send(m_clients[i], 0, m.getPacket()); - } - enet_host_flush(m_host); - } - // For server and no network: - // ========================== - // Prepare the data structures to receive and - // store information from all clients. - m_num_local_players.clear(); - // Server (hostid 0) is not included in the num_clients count. So to - // be able to use the hostid as index, we have to allocate one - // additional element. - m_num_local_players.resize(m_num_clients+1, -1); - m_num_local_players[0] = race_manager->getNumLocalPlayers(); - m_kart_info.clear(); - m_num_all_players = 0; - // use barrier count to see if we had at least one message from each host - m_barrier_count = 0; - m_state = NS_CHARACTER_SELECT; - } - -} // switchTocharacterSelection - -// ---------------------------------------------------------------------------- -/** Called on the client to send the data about the selected kart to the - * server and wait for confirmation. - * \param player_id Local id of the player which selected the kart. - * \param kart_id Identifier of the selected kart. this is used to wait till - * a message about this kart is received back from the server. - */ -void NetworkManager::sendCharacterSelected(int player_id, - const std::string &kart_id) -{ - if(m_mode==NW_SERVER) - { - CharacterConfirmMessage ccm(kart_id, getMyHostId()); - broadcastToClients(ccm); - } - else if(m_mode==NW_CLIENT) - { - CharacterSelectedMessage m(player_id); - sendToServer(m); - // Wait till we receive confirmation about the selected character. - m_state = NS_WAIT_FOR_KART_CONFIRMATION; - m_kart_to_confirm = kart_id; - } -} // sendCharacterSelected - -// ---------------------------------------------------------------------------- -void NetworkManager::waitForRaceInformation() -{ - m_state = NS_WAIT_FOR_RACE_DATA; -} // waitForRaceInformation - -// ---------------------------------------------------------------------------- -void NetworkManager::worldLoaded() -{ - if(m_mode==NW_CLIENT) - { - WorldLoadedMessage m; - sendToServer(m); - m_state = NS_READY_SET_GO_BARRIER; - } -} // worldLoaded - -// ---------------------------------------------------------------------------- -/** Receive and store the information from sendKartsInformation() -*/ -void NetworkManager::setupPlayerKartInfo() -{ - // Not sure if this should be here, but without it extra uncontrolled - // human players accumulate after each race. - m_kart_info.clear(); - - // Get the local kart info - for(unsigned int i=0; igetNumLocalPlayers(); i++) - m_kart_info.push_back(race_manager->getLocalKartInfo(i)); - - // Now sort by (hostid, playerid) - std::sort(m_kart_info.begin(), m_kart_info.end()); - - // Set the player kart information - race_manager->setNumPlayers(m_kart_info.size()); - - // Set the global player ID for each player - for(unsigned int i=0; isetPlayerKart(i, m_kart_info[i]); - } - - // Compute the id of the first kart from each host - m_kart_id_offset.resize(m_num_clients+1); - m_kart_id_offset[0]=0; - for(unsigned int i=1; i<=m_num_clients; i++) - m_kart_id_offset[i]=m_kart_id_offset[i-1]+m_num_local_players[i-1]; - - race_manager->computeRandomKartList(); -} // setupPlayerKartInfo - -// ---------------------------------------------------------------------------- -/** Sends the information from the race_manager to all clients. -*/ -void NetworkManager::sendRaceInformationToClients() -{ - if(m_mode==NW_SERVER) - { - setupPlayerKartInfo(); - RaceInfoMessage m(m_kart_info); - broadcastToClients(m); - } - beginReadySetGoBarrier(); -} // sendRaceInformationToClients - -// ---------------------------------------------------------------------------- -void NetworkManager::beginReadySetGoBarrier() -{ - m_state = NS_READY_SET_GO_BARRIER; - m_barrier_count = 0; - if(m_num_clients==0) m_state = NS_RACING; -} // beginReadySetGoBarrier -// ---------------------------------------------------------------------------- -void NetworkManager::sendConnectMessage() -{ - ConnectMessage msg; - sendToServer(msg); -} // sendConnectMessage -// ---------------------------------------------------------------------------- -/*** Send all kart controls and kart positions to all clients -*/ -void NetworkManager::sendUpdates() -{ - if(m_mode==NW_SERVER) - { - race_state->serialise(); - broadcastToClients(*race_state); - } - else if(m_mode==NW_CLIENT) - { - KartControlMessage m; - sendToServer(m); - } -} // sendUpdates - -// ---------------------------------------------------------------------------- -void NetworkManager::receiveUpdates() -{ - if(m_mode==NW_NONE) return; // do nothing if not networking - // The server receives m_num_clients messages, each client one message - int num_messages = m_mode==NW_SERVER ? m_num_clients : 1; - ENetEvent event; - bool correct=true; - - for(int i=0; ienterRaceOverState(); - return; - } - race_state->receive(event.packet); - } - } // for istopListening(); else + m_localhost->startListening(); +} + +void NetworkManager::notifyEvent(Event* event) +{ + printf("EVENT received\n"); + switch (event->type) { - broadcastToClients(m); + case EVENT_TYPE_MESSAGE: + printf("Message, Sender : %u, message = \"%s\"\n", event->peer->getAddress(), event->data.c_str()); + break; + case EVENT_TYPE_DISCONNECTED: + printf("Somebody is now disconnected. There are now %lu peers.\n", m_peers.size()); + printf("Disconnected host: %i.%i.%i.%i:%i\n", event->peer->getAddress()>>24&0xff, event->peer->getAddress()>>16&0xff, event->peer->getAddress()>>8&0xff, event->peer->getAddress()&0xff,event->peer->getPort()); + // remove the peer: + for (unsigned int i = 0; i < m_peers.size(); i++) + { + if (m_peers[i] == event->peer) + { + delete m_peers[i]; + m_peers.erase(m_peers.begin()+i, m_peers.begin()+i+1); + break; + } + } + printf("ERROR : the peer that has been disconnected was not registered by the Network Manager.\n"); + break; + case EVENT_TYPE_CONNECTED: + printf("A client has just connected. There are now %lu peers.\n", m_peers.size() + 1); + // create the new peer: + m_peers.push_back(event->peer); + break; } -} // sendRaceResultAck + ProtocolManager::getInstance()->notifyEvent(event); +} + +void NetworkManager::setLogin(std::string username, std::string password) +{ + m_player_login.username = username; + m_player_login.password = password; +} + +void NetworkManager::setPublicAddress(TransportAddress addr) +{ + m_public_address = addr; +} + +bool NetworkManager::peerExists(TransportAddress peer) +{ + return m_localhost->peerExists(peer); +} + +bool NetworkManager::isConnectedTo(TransportAddress peer) +{ + return m_localhost->isConnectedTo(peer); +} diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp index 83d55f556..b77832e60 100644 --- a/src/network/network_manager.hpp +++ b/src/network/network_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs, Stephen Leak +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,107 +16,57 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NETWORK_MANAGER_HPP -#define HEADER_NETWORK_MANAGER_HPP +#ifndef NETWORKMANAGER_HPP +#define NETWORKMANAGER_HPP -#include +#include "stk_peer.hpp" +#include "stk_host.hpp" #include -#include "enet/enet.h" +#include "protocol_manager.hpp" +#include "singleton.hpp" +#include "types.hpp" +#include "event.hpp" -#include "network/remote_kart_info.hpp" - - -class Message; - -class NetworkManager +class NetworkManager : public Singleton { -public: - // The mode the network manager is operating in - enum NetworkMode {NW_SERVER, NW_CLIENT, NW_NONE}; + friend class Singleton; + public: + virtual void run(); + + // network management functions + virtual bool connect(TransportAddress peer); + virtual void setManualSocketsMode(bool manual); + virtual void notifyEvent(Event* event); + virtual void packetReceived(char* data) = 0; - // States for the finite state machine. First for server: - enum NetworkState {NS_MAIN_MENU, // before char sel gui - NS_ACCEPT_CONNECTIONS, // server: accept connections - NS_WAIT_FOR_AVAILABLE_CHARACTERS, // client: wait for list - NS_ALL_REMOTE_CHARACTERS_DONE, // server: all client data received - NS_WAIT_FOR_KART_CONFIRMATION, // client: wait for confirmation - // if character selection was ok - NS_KART_CONFIRMED, // Character was confirmed - NS_WAIT_FOR_RACE_DATA, // client: wait for race info - NS_READY_SET_GO_BARRIER, // c&s: barrier before r.s.g. - NS_CHARACTER_SELECT, // c&s: character select in progress - NS_LOADING_WORLD, // client: loading world - NS_RACING, - NS_WAIT_FOR_RACE_RESULT, // clients: waiting for race results - NS_RACE_RESULT_BARRIER , // Wait till all ack results - NS_RACE_RESULT_BARRIER_OVER // Barrier is over, goto next state - }; -private: - - NetworkMode m_mode; - NetworkState m_state; - unsigned int m_num_clients; - std::vector m_kart_info; - int m_host_id; - std::vector m_client_names; - std::vector m_num_local_players; - std::vector m_kart_id_offset; // kart id of first kart on host i - int m_num_all_players; - int m_barrier_count; - - ENetHost *m_host; // me - ENetPeer *m_server; // (clients only) - std::vector m_clients; // (server only) pos in vector is client host_id - /** Name of the kart that a client is waiting for confirmation for. */ - std::string m_kart_to_confirm; - - bool initServer(); - bool initClient(); - void handleNewConnection(ENetEvent *event); - void handleMessageAtServer(ENetEvent *event); - void handleMessageAtClient(ENetEvent *event); - void handleDisconnection(ENetEvent *event); - // the first cast to long avoid compiler errors on 64 bit systems - // about lost precision, then cast long to int to get the right type - unsigned int getHostId(ENetPeer *p) const {return (int)(long)p->data; } - - void sendToServer(Message &m); - void broadcastToClients(Message &m); -public: - NetworkManager(); - ~NetworkManager(); - void setMode(NetworkMode m) {m_mode = m; } - NetworkMode getMode() const {return m_mode; } - void becomeServer(); - void becomeClient(); - void setState(NetworkState s) {m_state = s; } - NetworkState getState() const {return m_state; } - int getMyHostId() const {return m_host_id; } - void setHostId(int host_id) {m_host_id = host_id; } - unsigned int getNumClients() const {return m_num_clients; } - const std::string& - getClientName(int i) const {return m_client_names[i];} - bool initialiseConnections(); - void update(float dt); - - void disableNetworking(); - void sendConnectMessage(); // client send initial info to server - void initCharacterDataStructures(); - void sendCharacterSelected(int player_id, const std::string &kartid); - void waitForRaceInformation(); - void worldLoaded(); - void setupPlayerKartInfo(); - void beginReadySetGoBarrier(); - void sendRaceInformationToClients(); - void sendUpdates(); - void receiveUpdates(); - void waitForClientData(); - void sendRaceResults(); - void beginRaceResultBarrier(); - void sendRaceResultAck(char menu_selection=-1); + // raw data management + void setLogin(std::string username, std::string password); + void setPublicAddress(TransportAddress addr); + + // getters + virtual bool peerExists(TransportAddress peer); + virtual bool isConnectedTo(TransportAddress peer); + + virtual bool isServer() = 0; + inline bool isClient() { return !isServer(); } + bool isPlayingOnline() { return m_playing_online; } + STKHost* getHost() { return m_localhost; } + std::vector getPeers() { return m_peers; } + + protected: + NetworkManager(); + virtual ~NetworkManager(); + + // protected members + std::vector m_peers; + STKHost* m_localhost; + bool m_playing_online; + + TransportAddress m_public_address; + PlayerLogin m_player_login; + + pthread_t* m_protocol_manager_update_thread; }; -extern NetworkManager *network_manager; - -#endif +#endif // NETWORKMANAGER_HPP diff --git a/src/network/connect_message.hpp b/src/network/protocol.cpp similarity index 60% rename from src/network/connect_message.hpp rename to src/network/protocol.cpp index 1875b5718..37abf4d52 100644 --- a/src/network/connect_message.hpp +++ b/src/network/protocol.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,22 +16,34 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_CONNECT_MESSAGE_HPP -#define HEADER_CONNECT_MESSAGE_HPP +#include "protocol.hpp" -#include - -#include "network/message.hpp" - -class ConnectMessage : public Message +Protocol::Protocol(CallbackObject* callback_object, PROTOCOL_TYPE type) { -private: - std::string m_id; - void setId(); -public: - ConnectMessage(); - ConnectMessage(ENetPacket* pkt); - const std::string& - getId() { return m_id; } -}; // ConnectMessage -#endif + m_callback_object = callback_object; + m_type = type; +} + +Protocol::~Protocol() +{ +} + +void Protocol::pause() +{ + m_listener->requestPause(this); +} +void Protocol::unpause() +{ + m_listener->requestUnpause(this); +} + + +void Protocol::setListener(ProtocolManager* listener) +{ + m_listener = listener; +} + +PROTOCOL_TYPE Protocol::getProtocolType() +{ + return m_type; +} diff --git a/src/network/protocol.hpp b/src/network/protocol.hpp new file mode 100644 index 000000000..eda60ad78 --- /dev/null +++ b/src/network/protocol.hpp @@ -0,0 +1,105 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef PROTOCOL_HPP +#define PROTOCOL_HPP + +#include "protocol_manager.hpp" +#include "types.hpp" + +#include + +/** \enum PROTOCOL_TYPE + * \brief The types that protocols can have. This is used to select which protocol receives which event. + * \ingroup network + */ +enum PROTOCOL_TYPE +{ + PROTOCOL_NONE = 0, //!< No protocol type assigned. + PROTOCOL_CONNECTION = 1, //!< Protocol that deals with client-server connection. + PROTOCOL_LOBBY_ROOM = 2, //!< Protocol that is used during the lobby room phase. + PROTOCOL_SILENT = 0xffff //!< Used for protocols that do not subscribe to any network event. +}; + +/** \class Protocol + * \brief Abstract class used to define the global protocol functions. + * A protocol is an entity that is started at a point, and that is updated by a thread. + * A protocol can be terminated by an other class, or it can terminate itself if has fulfilled its role. + * This class must be inherited to make any network job. + * \ingroup network + */ +class Protocol +{ + public: + /*! + * \brief Constructor + * + * Sets the basic protocol parameters, as the callback object and the protocol type. + * + * \param callback_object The callback object that will be used by the protocol. Protocols that do not use callback objects must set it to NULL. + * \param type The type of the protocol. + */ + Protocol(CallbackObject* callback_object, PROTOCOL_TYPE type); + /*! + * \brief Destructor + */ + virtual ~Protocol(); + + /*! + * \brief Notify a protocol matching the Event type of that event. + * \param event : Pointer to the event. + */ + virtual void notifyEvent(Event* event) = 0; + + /*! + * \brief Set the protocol listener. + * \param listener : Pointer to the listener. + */ + void setListener(ProtocolManager* listener); + + /*! + * \brief Called when the protocol is going to start. Must be re-defined by subclasses. + */ + virtual void setup() = 0; + /*! + * \brief Called when the protocol is paused (by an other entity or by itself). + * This function must be called by the subclasse's pause function if re-defined. + */ + virtual void pause(); + /*! + * \brief Called when the protocol is unpaused. + * This function must be called by the subclasse's unpause function if re-defined. + */ + virtual void unpause(); + /*! + * \brief Called by the protocol listener as often as possible. Must be re-defined. + */ + virtual void update() = 0; + + /*! + * \brief Method to get a protocol's type. + * \return The protocol type. + */ + PROTOCOL_TYPE getProtocolType(); + protected: + ProtocolManager* m_listener; //!< The protocol listener + PROTOCOL_TYPE m_type; //!< The type of the protocol + CallbackObject* m_callback_object; //!< The callback object, if needed +}; + +#endif // PROTOCOL_HPP diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp new file mode 100644 index 000000000..036a44886 --- /dev/null +++ b/src/network/protocol_manager.cpp @@ -0,0 +1,287 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "protocol_manager.hpp" + +#include "protocol.hpp" + +#include +#include +#include + +#define RAND_MAX 65536 + +ProtocolManager::ProtocolManager() +{ + pthread_mutex_init(&m_events_mutex, NULL); + pthread_mutex_init(&m_protocols_mutex, NULL); + pthread_mutex_init(&m_requests_mutex, NULL); + pthread_mutex_init(&m_id_mutex, NULL); + m_next_protocol_id = 0; +} + +ProtocolManager::~ProtocolManager() +{ +} + +void ProtocolManager::notifyEvent(Event* event) +{ + pthread_mutex_lock(&m_events_mutex); + m_events_to_process.push_back(event); // add the event to the queue + pthread_mutex_unlock(&m_events_mutex); +} + +void ProtocolManager::sendMessage(std::string message) +{ + std::string newMessage = " " + message; // add one byte + newMessage[0] = (char)(0); +} + +int ProtocolManager::requestStart(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + ProtocolInfo info; + info.protocol = protocol; + info.state = PROTOCOL_STATE_RUNNING; + assignProtocolId(&info); // assign a unique id to the protocol. + req.protocol_info = info; + req.type = PROTOCOL_REQUEST_START; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); + + return info.id; +} + +void ProtocolManager::requestStop(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_STOP; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestPause(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_PAUSE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestUnpause(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_UNPAUSE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestTerminate(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_TERMINATE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::startProtocol(ProtocolInfo protocol) +{ + printf("__ProtocolManager> A new protocol with id=%u has been started. There are %ld protocols running.\n", protocol.id, m_protocols.size()+1); + // add the protocol to the protocol vector so that it's updated + pthread_mutex_lock(&m_protocols_mutex); + m_protocols.push_back(protocol); + pthread_mutex_unlock(&m_protocols_mutex); + // setup the protocol and notify it that it's started + protocol.protocol->setListener(this); + protocol.protocol->setup(); +} +void ProtocolManager::stopProtocol(ProtocolInfo protocol) +{ + +} +void ProtocolManager::pauseProtocol(ProtocolInfo protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol.protocol && m_protocols[i].state == PROTOCOL_STATE_RUNNING) + { + m_protocols[i].state = PROTOCOL_STATE_PAUSED; + m_protocols[i].protocol->pause(); + } + } +} +void ProtocolManager::unpauseProtocol(ProtocolInfo protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol.protocol && m_protocols[i].state == PROTOCOL_STATE_PAUSED) + { + m_protocols[i].state = PROTOCOL_STATE_RUNNING; + m_protocols[i].protocol->unpause(); + } + } +} +void ProtocolManager::protocolTerminated(ProtocolInfo protocol) +{ + pthread_mutex_lock(&m_protocols_mutex); // be sure that noone accesses the protocols vector while we erase a protocol + int offset = 0; + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i-offset].protocol == protocol.protocol) + { + delete m_protocols[i].protocol; + m_protocols.erase(m_protocols.begin()+(i-offset), m_protocols.begin()+(i-offset)+1); + offset++; + } + } + printf("__ProtocolManager> A protocol has been terminated. There are %ld protocols running.\n", m_protocols.size()); + pthread_mutex_unlock(&m_protocols_mutex); +} + +void ProtocolManager::update() +{ + // before updating, notice protocols that they have received information + pthread_mutex_lock(&m_events_mutex); // secure threads + int size = m_events_to_process.size(); + for (int i = 0; i < size; i++) + { + Event* event = m_events_to_process.back(); + + PROTOCOL_TYPE searchedProtocol = PROTOCOL_NONE; + if (event->data.size() > 0) + searchedProtocol = (PROTOCOL_TYPE)(event->data[0]); + for (unsigned int i = 0; i < m_protocols.size() ; i++) + { + if (m_protocols[i].protocol->getProtocolType() == searchedProtocol || event->type != EVENT_TYPE_MESSAGE) // pass data to protocols even when paused + m_protocols[i].protocol->notifyEvent(event); + } + delete event; + m_events_to_process.pop_back(); + } + pthread_mutex_unlock(&m_events_mutex); // release the mutex + + // now update all protocols + pthread_mutex_lock(&m_protocols_mutex); + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].state == PROTOCOL_STATE_RUNNING) + m_protocols[i].protocol->update(); + } + pthread_mutex_unlock(&m_protocols_mutex); + + // process queued events for protocols + pthread_mutex_lock(&m_requests_mutex); + for (unsigned int i = 0; i < m_requests.size(); i++) + { + switch (m_requests[i].type) + { + case PROTOCOL_REQUEST_START: + startProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_STOP: + stopProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_PAUSE: + pauseProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_UNPAUSE: + unpauseProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_TERMINATE: + protocolTerminated(m_requests[i].protocol_info); + break; + } + } + m_requests.clear(); + pthread_mutex_unlock(&m_requests_mutex); +} + +int ProtocolManager::runningProtocolsCount() +{ + return m_protocols.size(); +} + +PROTOCOL_STATE ProtocolManager::getProtocolState(uint32_t id) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].id == id) // we know a protocol with that id + return m_protocols[i].state; // return its state + } + // the protocol isn't running right now + for (unsigned int i = 0; i < m_requests.size(); i++) + { + if (m_requests[i].protocol_info.id == id) // the protocol is going to be started + return PROTOCOL_STATE_RUNNING; // we can say it's running + } + return PROTOCOL_STATE_TERMINATED; // else, it's already finished +} + +PROTOCOL_STATE ProtocolManager::getProtocolState(Protocol* protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol) // the protocol is known + return m_protocols[i].state; // return its state + } + for (unsigned int i = 0; i < m_requests.size(); i++) + { + if (m_requests[i].protocol_info.protocol == protocol) // the protocol is going to be started + return PROTOCOL_STATE_RUNNING; // we can say it's running + } + return PROTOCOL_STATE_TERMINATED; // we don't know this protocol at all, it's finished +} + +int ProtocolManager::getProtocolID(Protocol* protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol) + return m_protocols[i].id; + } + return 0; +} + +void ProtocolManager::assignProtocolId(ProtocolInfo* protocol_info) +{ + pthread_mutex_lock(&m_id_mutex); + protocol_info->id = m_next_protocol_id; + m_next_protocol_id++; + pthread_mutex_unlock(&m_id_mutex); +} + + diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp new file mode 100644 index 000000000..a8abdf078 --- /dev/null +++ b/src/network/protocol_manager.hpp @@ -0,0 +1,266 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/*! \file protocol_manager.hpp + * \brief Contains structures and enumerations related to protocol management. + */ + +#ifndef PROTOCOL_MANAGER_HPP +#define PROTOCOL_MANAGER_HPP + +#include "singleton.hpp" +#include "event.hpp" + +#include +#include + +class Protocol; + +/*! + * \enum PROTOCOL_STATE + * \brief Defines the three states that a protocol can have. + */ +enum PROTOCOL_STATE +{ + PROTOCOL_STATE_RUNNING, //!< The protocol is being updated everytime. + PROTOCOL_STATE_PAUSED, //!< The protocol is paused. + PROTOCOL_STATE_TERMINATED //!< The protocol is terminated/does not exist. +}; + +/*! + * \enum PROTOCOL_REQUEST_TYPE + * \brief Defines actions that can be done about protocols. + * This enum is used essentially to keep the manager thread-safe and + * to avoid protocols modifying directly their state. + */ +enum PROTOCOL_REQUEST_TYPE +{ + PROTOCOL_REQUEST_START, //!< Start a protocol + PROTOCOL_REQUEST_STOP, //!< Stop a protocol + PROTOCOL_REQUEST_PAUSE, //!< Pause a protocol + PROTOCOL_REQUEST_UNPAUSE, //!< Unpause a protocol + PROTOCOL_REQUEST_TERMINATE //!< Terminate a protocol +}; + +/*! +* \struct ProtocolInfo +* \brief Stores the information needed to manage protocols +*/ +typedef struct ProtocolInfo +{ + PROTOCOL_STATE state; //!< The state of the protocol + Protocol* protocol; //!< A pointer to the protocol + uint32_t id; //!< The unique id of the protocol +} ProtocolInfo; + +/*! +* \struct ProtocolRequest +* \brief Represents a request to do an action about a protocol. +*/ +typedef struct ProtocolRequest +{ + PROTOCOL_REQUEST_TYPE type; //!< The type of request + ProtocolInfo protocol_info; //!< The concerned protocol information +} ProtocolRequest; + +/*! + * \class ProtocolManager + * \brief Manages the protocols at runtime. + * + * This class is in charge of storing and managing protocols. + * It is a singleton as there can be only one protocol manager per game + * instance. Any game object that wants to start a protocol must create a + * protocol and give it to this singleton. The protocols are updated in a + * special thread, to ensure that they are processed independently from the + * frames per second. Then, the management of protocols is thread-safe: any + * object can start/pause/stop protocols whithout problems. + */ +class ProtocolManager : public Singleton +{ + friend class Singleton; + + public: + /*! + * \brief Function that processes incoming events. + * This function is called by the network manager each time there is an + * incoming packet. + */ + virtual void notifyEvent(Event* event); + /*! + * \brief WILL BE COMMENTED LATER + */ + virtual void sendMessage(std::string message); + + /*! + * \brief Asks the manager to start a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to start + * \return The unique id of the protocol that is being started. + */ + virtual int requestStart(Protocol* protocol); + /*! + * \brief Asks the manager to stop a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to stop + */ + virtual void requestStop(Protocol* protocol); + /*! + * \brief Asks the manager to pause a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to pause + */ + virtual void requestPause(Protocol* protocol); + /*! + * \brief Asks the manager to unpause a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to unpause + */ + virtual void requestUnpause(Protocol* protocol); + /*! + * \brief Notifies the manager that a protocol is terminated. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol that is finished + */ + virtual void requestTerminate(Protocol* protocol); + + /*! + * \brief Updates the manager. + * + * This function processes the events queue, notifies the concerned + * protocols that they have events to process. Then ask all protocols + * to update themselves. Finally processes stored requests about + * starting, stoping, pausing etc... protocols. + * This function is called by a thread as often as possible. + * This function is not FPS-dependant. + */ + virtual void update(); + + /*! + * \brief Get the number of protocols running. + * \return The number of protocols that are actually running. + */ + virtual int runningProtocolsCount(); + /*! + * \brief Get the state of a protocol using its id. + * \param id : The id of the protocol you seek the state. + * \return The state of the protocol. + */ + virtual PROTOCOL_STATE getProtocolState(uint32_t id); + /*! + * \brief Get the state of a protocol using a pointer on it. + * \param protocol : A pointer to the protocol you seek the state. + * \return The state of the protocol. + */ + virtual PROTOCOL_STATE getProtocolState(Protocol* protocol); + /*! + * \brief Get the id of a protocol. + * \param protocol : A pointer to the protocol you seek the id. + * \return The id of the protocol pointed by the protocol parameter. + */ + virtual int getProtocolID(Protocol* protocol); + + protected: + // protected functions + /*! + * \brief Constructor + */ + ProtocolManager(); + /*! + * \brief Destructor + */ + virtual ~ProtocolManager(); + /*! + * \brief Assign an id to a protocol. + * This function will assign m_next_protocol_id as the protocol id. + * This id starts at 0 at the beginning and is increased by 1 each time + * a protocol starts. + * \param protocol_info : The protocol info that needs an id. + */ + void assignProtocolId(ProtocolInfo* protocol_info); + + /*! + * \brief Starts a protocol. + * Add the protocol info to the m_protocols vector. + * \param protocol : ProtocolInfo to start. + */ + virtual void startProtocol(ProtocolInfo protocol); + /*! + * \brief Stops a protocol. + * Coes nothing. Noone can stop running protocols for now. + * \param protocol : ProtocolInfo to stop. + */ + virtual void stopProtocol(ProtocolInfo protocol); + /*! + * \brief Pauses a protocol. + * Pauses a protocol and tells it that it's being paused. + * \param protocol : ProtocolInfo to pause. + */ + virtual void pauseProtocol(ProtocolInfo protocol); + /*! + * \brief Unpauses a protocol. + * Unpauses a protocol and notifies it. + * \param protocol : ProtocolInfo to unpause. + */ + virtual void unpauseProtocol(ProtocolInfo protocol); + /*! + * \brief Notes that a protocol is terminated. + * Remove a protocol from the protocols vector. + * \param protocol : ProtocolInfo concerned. + */ + virtual void protocolTerminated(ProtocolInfo protocol); + + // protected members + /*! + * \brief Contains the running protocols. + * This stores the protocols that are either running or paused, their + * state and their unique id. + */ + std::vector m_protocols; + /*! + * \brief Contains the network events to pass to protocols. + */ + std::vector m_events_to_process; + /*! + * \brief Contains the requests to start/stop etc... protocols. + */ + std::vector m_requests; + /*! \brief The next id to assign to a protocol. + * This value is incremented by 1 each time a protocol is started. + * If a protocol has an id lower than this value, it means that it have + * been formerly started. + */ + unsigned int m_next_protocol_id; + + // mutexes: + /*! Used to ensure that the event queue is used thread-safely. */ + pthread_mutex_t m_events_mutex; + /*! Used to ensure that the protocol vector is used thread-safely. */ + pthread_mutex_t m_protocols_mutex; + /*! Used to ensure that the request vector is used thread-safely. */ + pthread_mutex_t m_requests_mutex; + /*! Used to ensure that the protocol id is used in a thread-safe way.*/ + pthread_mutex_t m_id_mutex; + +}; + +#endif // PROTOCOL_MANAGER_HPP diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp new file mode 100644 index 000000000..8d086cecb --- /dev/null +++ b/src/network/protocols/connect_to_server.cpp @@ -0,0 +1,110 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/connect_to_server.hpp" + +#include "network/client_network_manager.hpp" +#include "network/time.hpp" + +#include +#include + +// ---------------------------------------------------------------------------- + +ConnectToServer::ConnectToServer(CallbackObject* callback_object) : + Protocol(callback_object, PROTOCOL_CONNECTION) +{ + m_server_ip = 0; + m_server_port = 0; + m_state = NONE; +} + +// ---------------------------------------------------------------------------- + +ConnectToServer::~ConnectToServer() +{ +} + +// ---------------------------------------------------------------------------- + +void ConnectToServer::notifyEvent(Event* event) +{ + if (event->type == EVENT_TYPE_CONNECTED && + event->peer->getAddress() == m_server_ip && + event->peer->getPort() == m_server_port) + { + printf("The Connect To Server protocol has received an event notifying \ + that he's connected to the peer. The peer sent \"%s\"\n", + event->data.c_str()); + m_state = DONE; // we received a message, we are connected + } +} + +// ---------------------------------------------------------------------------- + +void ConnectToServer::setup() +{ + m_state = NONE; + if (m_server_ip == 0 || m_server_port == 0 ) + { + printf("You have to set the server's public ip:port of the server.\n"); + m_listener->requestTerminate(this); + } +} + +// ---------------------------------------------------------------------------- + +void ConnectToServer::update() +{ + if (m_state == NONE) + { + static double target = 0; + double currentTime = Time::getSeconds(); + // sometimes the getSeconds method forgets 3600 seconds. + while (currentTime < target-1800) + currentTime += 3600; + if (currentTime > target) + { + NetworkManager::getInstance()->connect( + TransportAddress(m_server_ip, m_server_port)); + if (NetworkManager::getInstance()->isConnectedTo( + TransportAddress(m_server_ip, m_server_port))) + { + m_state = DONE; + return; + } + target = currentTime+5; + printf("Retrying to connect in 5 seconds.\n"); + } + } + else if (m_state == DONE) + { + m_listener->requestTerminate(this); + } +} + +// ---------------------------------------------------------------------------- + +void ConnectToServer::setServerAddress(uint32_t ip, uint16_t port) +{ + m_server_ip = ip; + m_server_port = port; +} + +// ---------------------------------------------------------------------------- + diff --git a/src/network/num_players_message.hpp b/src/network/protocols/connect_to_server.hpp similarity index 52% rename from src/network/num_players_message.hpp rename to src/network/protocols/connect_to_server.hpp index 4279f9eb9..ad67412f9 100644 --- a/src/network/num_players_message.hpp +++ b/src/network/protocols/connect_to_server.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,27 +16,34 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NUM_PLAYERS_MESSAGE_HPP -#define HEADER_NUM_PLAYERS_MESSAGE_HPP +#ifndef CONNECT_TO_SERVER_HPP +#define CONNECT_TO_SERVER_HPP +#include "network/protocol.hpp" #include -#include -#ifndef WIN32 -# include -#endif -#include "network/message.hpp" -#include "race/race_manager.hpp" - -class NumPlayersMessage : public Message +class ConnectToServer : public Protocol, public CallbackObject { -private: - int m_num_players -public: - NumPlayersMessage():Message(Message::MT_CONNECT) { m_num_players=race } - NumPlayersMessage(ENetPacket* pkt):Message(pkt) - { m_id=getString(); } - const std::string& - getNumPlayers() { return m_num_players; } -}; // ConnectMessage -#endif + public: + ConnectToServer(CallbackObject* callback_object); + virtual ~ConnectToServer(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update(); + + void setServerAddress(uint32_t ip, uint16_t port); + + protected: + uint32_t m_server_ip; + uint16_t m_server_port; + + enum STATE + { + NONE, + DONE + }; + STATE m_state; +}; + +#endif // CONNECT_TO_SERVER_HPP diff --git a/src/network/protocols/get_peer_address.cpp b/src/network/protocols/get_peer_address.cpp new file mode 100644 index 000000000..238cf7790 --- /dev/null +++ b/src/network/protocols/get_peer_address.cpp @@ -0,0 +1,98 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/get_peer_address.hpp" + +#include "network/time.hpp" +#include "network/http_functions.hpp" + +#include +#include + +GetPeerAddress::GetPeerAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) +{ +} + +GetPeerAddress::~GetPeerAddress() +{ +} + +void GetPeerAddress::notifyEvent(Event* event) +{ +} + +void GetPeerAddress::setup() +{ + m_state = NONE; +} + +void GetPeerAddress::update() +{ + if (m_state == NONE) + { + static double target = 0; + double current_time = Time::getSeconds(); + while (current_time < target-1800) // sometimes the getSeconds method forgets 3600 seconds. + current_time += 3600; + if (current_time > target) + { + char url[512]; + sprintf(url, "http://stkconnect.freeserver.me/log.php?get&nick=%s", m_peer_name.c_str()); + std::string result = HTTP::getPage(url); + if (result == "") + { + printf("__GetPeerAddress> The host you try to reach does not exist. Change the host name please.\n"); + pause(); + return; + } + std::string ip_addr = result; + ip_addr.erase(ip_addr.find_first_of(':')); + std::string port_nb = result; + port_nb.erase(0, port_nb.find_first_of(':')+1); + uint32_t dst_ip = (uint32_t)(atoi(ip_addr.c_str())); + uint16_t dst_port = (uint32_t)(atoi(port_nb.c_str())); + if (dst_ip == 0 || dst_port == 0) + { + printf("__GetPeerAddress> The host you try to reach is not online. There will be a new try in 10 seconds.\n"); + target = current_time+10; + } + else + { + printf("__GetPeerAddress> Public ip of target is %i.%i.%i.%i:%i\n", (dst_ip>>24)&0xff, (dst_ip>>16)&0xff, (dst_ip>>8)&0xff, dst_ip&0xff, dst_port); + uint32_t server_ip = ((dst_ip&0x000000ff)<<24) // change the server IP to have a network-byte order + + ((dst_ip&0x0000ff00)<<8) + + ((dst_ip&0x00ff0000)>>8) + + ((dst_ip&0xff000000)>>24); + uint16_t server_port = dst_port; + TransportAddress* addr = static_cast(m_callback_object); + addr->ip = server_ip; + addr->port = server_port; + m_state = DONE; + } + } + } + else if (m_state == DONE) + { + m_listener->requestTerminate(this); + } +} + +void GetPeerAddress::setPeerName(std::string peer_name) +{ + m_peer_name = peer_name; +} diff --git a/src/network/protocols/get_peer_address.hpp b/src/network/protocols/get_peer_address.hpp new file mode 100644 index 000000000..5359d00f3 --- /dev/null +++ b/src/network/protocols/get_peer_address.hpp @@ -0,0 +1,47 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef GET_PEER_ADDRESS_HPP +#define GET_PEER_ADDRESS_HPP + +#include "network/protocol.hpp" + +class GetPeerAddress : public Protocol +{ + public: + GetPeerAddress(CallbackObject* callback_object); + virtual ~GetPeerAddress(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update(); + + void setPeerName(std::string peer_name); + protected: + std::string m_peer_name; + + enum STATE + { + NONE, + DONE + }; + STATE m_state; + +}; + +#endif // GET_PEER_ADDRESS_HPP diff --git a/src/network/protocols/get_public_address.cpp b/src/network/protocols/get_public_address.cpp new file mode 100644 index 000000000..1b977058a --- /dev/null +++ b/src/network/protocols/get_public_address.cpp @@ -0,0 +1,207 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/get_public_address.hpp" + +#include "network/network_manager.hpp" +#include "network/client_network_manager.hpp" +#include "network/protocols/connect_to_server.hpp" +#include "network/network_interface.hpp" + +#include +#include +#include +#include + +int stunRand() +{ + static bool init = false; + if (!init) + { + srand(time(NULL)); + init = true; + } + return rand(); +} + +GetPublicAddress::GetPublicAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) +{ +} + +GetPublicAddress::~GetPublicAddress() +{ +} + +void GetPublicAddress::notifyEvent(Event* event) +{ + +} + +void GetPublicAddress::setup() +{ + m_state = NOTHING_DONE; +} + +void GetPublicAddress::update() +{ + if (m_state == NOTHING_DONE) + { + // format : 00MMMMMCMMMCMMMM (cf rfc 5389) + uint16_t message_type = 0b0000000000000001; // binding request + m_stun_tansaction_id[0] = stunRand(); + m_stun_tansaction_id[1] = stunRand(); + m_stun_tansaction_id[2] = stunRand(); + uint16_t message_length = 0x0000; + + uint8_t bytes[21]; // the message to be sent + // bytes 0-1 : the type of the message, + bytes[0] = (uint8_t)(message_type>>8); + bytes[1] = (uint8_t)(message_type); + + // bytes 2-3 : message length added to header (attributes) + bytes[2] = (uint8_t)(message_length>>8); + bytes[3] = (uint8_t)(message_length); + + // bytes 4-7 : magic cookie to recognize the stun protocol + bytes[4] = (uint8_t)(m_stun_magic_cookie>>24); + bytes[5] = (uint8_t)(m_stun_magic_cookie>>16); + bytes[6] = (uint8_t)(m_stun_magic_cookie>>8); + bytes[7] = (uint8_t)(m_stun_magic_cookie); + + // bytes 8-19 : the transaction id + bytes[8] = (uint8_t)(m_stun_tansaction_id[0]>>24); + bytes[9] = (uint8_t)(m_stun_tansaction_id[0]>>16); + bytes[10] = (uint8_t)(m_stun_tansaction_id[0]>>8); + bytes[11] = (uint8_t)(m_stun_tansaction_id[0]); + bytes[12] = (uint8_t)(m_stun_tansaction_id[1]>>24); + bytes[13] = (uint8_t)(m_stun_tansaction_id[1]>>16); + bytes[14] = (uint8_t)(m_stun_tansaction_id[1]>>8); + bytes[15] = (uint8_t)(m_stun_tansaction_id[1]); + bytes[16] = (uint8_t)(m_stun_tansaction_id[2]>>24); + bytes[17] = (uint8_t)(m_stun_tansaction_id[2]>>16); + bytes[18] = (uint8_t)(m_stun_tansaction_id[2]>>8); + bytes[19] = (uint8_t)(m_stun_tansaction_id[2]); + bytes[20] = '\0'; + + printf("__GetPublicAddress> Querrying STUN server 132.177.123.6\n"); + unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; + NetworkManager::getInstance()->setManualSocketsMode(true); + NetworkManager::getInstance()->getHost()->sendRawPacket(bytes, 20, TransportAddress(dst, 3478)); + m_state = TEST_SENT; + } + if (m_state == TEST_SENT) + { + unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; + uint8_t* data = NetworkManager::getInstance()->getHost()->receiveRawPacket(TransportAddress(dst, 3478)); + assert(data); + + // check that the stun response is a response, contains the magic cookie and the transaction ID + if ( data[0] == 0b01 && + data[1] == 0b01 && + data[4] == (uint8_t)(m_stun_magic_cookie>>24) && + data[5] == (uint8_t)(m_stun_magic_cookie>>16) && + data[6] == (uint8_t)(m_stun_magic_cookie>>8) && + data[7] == (uint8_t)(m_stun_magic_cookie)) + { + if( + data[8] == (uint8_t)(m_stun_tansaction_id[0]>>24) && + data[9] == (uint8_t)(m_stun_tansaction_id[0]>>16) && + data[10] == (uint8_t)(m_stun_tansaction_id[0]>>8 ) && + data[11] == (uint8_t)(m_stun_tansaction_id[0] ) && + data[12] == (uint8_t)(m_stun_tansaction_id[1]>>24) && + data[13] == (uint8_t)(m_stun_tansaction_id[1]>>16) && + data[14] == (uint8_t)(m_stun_tansaction_id[1]>>8 ) && + data[15] == (uint8_t)(m_stun_tansaction_id[1] ) && + data[16] == (uint8_t)(m_stun_tansaction_id[2]>>24) && + data[17] == (uint8_t)(m_stun_tansaction_id[2]>>16) && + data[18] == (uint8_t)(m_stun_tansaction_id[2]>>8 ) && + data[19] == (uint8_t)(m_stun_tansaction_id[2] )) + { + printf("__GetPublicAddress> The STUN server responded with a valid answer\n"); + int message_size = data[2]*256+data[3]; + + // parse the stun message now: + bool finish = false; + uint8_t* attributes = data+20; + if (message_size == 0) + { + printf("__GetPublicAddress> STUN answer does not contain any information.\n"); + finish = true; + } + if (message_size < 4) // cannot even read the size + { + printf("__GetPublicAddress> STUN message is not valid.\n"); + finish = true; + } + uint16_t port; + uint32_t address; + bool valid = false; + while(!finish) + { + int type = attributes[0]*256+attributes[1]; + int size = attributes[2]*256+attributes[3]; + switch(type) + { + case 0: + case 1: + assert(size == 8); + assert(attributes[5] = 0x01); // IPv4 only + port = attributes[6]*256+attributes[7]; + address = (attributes[8]<<24 & 0xFF000000)+(attributes[9]<<16 & 0x00FF0000)+(attributes[10]<<8 & 0x0000FF00)+(attributes[11] & 0x000000FF); + finish = true; + valid = true; + continue; + break; + default: + break; + } + attributes = attributes + 4 + size; + message_size -= 4 + size; + if (message_size == 0) + finish = true; + if (message_size < 4) // cannot even read the size + { + printf("__GetPublicAddress> STUN message is not valid.\n"); + finish = true; + } + } + // finished parsing, we know our public transport address + if (valid) + { + printf("__The public address has been found : %i.%i.%i.%i:%i\n", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); + m_state = ADDRESS_KNOWN; + NetworkManager::getInstance()->setManualSocketsMode(false); + TransportAddress* addr = static_cast(m_callback_object); + addr->ip = address; + addr->port = port; + } + else + m_state = NOTHING_DONE; // need to re-send the stun request + } + else + { + m_state = NOTHING_DONE; // need to re-send the stun request + } + } + } + if (m_state == ADDRESS_KNOWN) + { + // terminate the protocol + m_listener->requestTerminate(this); + } +} diff --git a/src/network/protocols/get_public_address.hpp b/src/network/protocols/get_public_address.hpp new file mode 100644 index 000000000..596ab2c1b --- /dev/null +++ b/src/network/protocols/get_public_address.hpp @@ -0,0 +1,47 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef GET_PUBLIC_ADDRESS_HPP +#define GET_PUBLIC_ADDRESS_HPP + +#include "network/protocol.hpp" + +class GetPublicAddress : public Protocol +{ + public: + GetPublicAddress(CallbackObject* callback_object); + virtual ~GetPublicAddress(); + + virtual void notifyEvent(Event* event); + + virtual void setup(); + virtual void update(); + + protected: + enum STATE + { + NOTHING_DONE, + TEST_SENT, + ADDRESS_KNOWN + }; + STATE m_state; + uint32_t m_stun_tansaction_id[3]; + static const uint32_t m_stun_magic_cookie = 0x2112A442; +}; + +#endif // GET_PUBLIC_ADDRESS_HPP diff --git a/src/network/protocols/hide_public_address.cpp b/src/network/protocols/hide_public_address.cpp new file mode 100644 index 000000000..9af6be7fa --- /dev/null +++ b/src/network/protocols/hide_public_address.cpp @@ -0,0 +1,74 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/hide_public_address.hpp" + +#include "network/http_functions.hpp" + +#include + +HidePublicAddress::HidePublicAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) +{ +} + +HidePublicAddress::~HidePublicAddress() +{ +} + +void HidePublicAddress::notifyEvent(Event* event) +{ +} + +void HidePublicAddress::setup() +{ + m_state = NONE; +} + +void HidePublicAddress::update() +{ + if (m_state == NONE) + { + char url[512]; + sprintf(url, "http://stkconnect.freeserver.me/log.php?logout&nick=%s&pwd=%s", m_username.c_str(), m_password.c_str()); + std::string result = HTTP::getPage(url); + if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') + { + printf("__HidePublicAddress> Public address hidden successfully.\n"); + m_state = DONE; + } + if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') + { + printf("__HidePublicAddress> Public address still visible. Re-set nick:password and retry.\n"); + m_state = NONE; + pause(); + } + } + else if (m_state == DONE) + { + m_listener->requestTerminate(this); + } +} + +void HidePublicAddress::setUsername(std::string username) +{ + m_username = username; +} +void HidePublicAddress::setPassword(std::string password) +{ + m_password = password; +} diff --git a/src/network/protocols/hide_public_address.hpp b/src/network/protocols/hide_public_address.hpp new file mode 100644 index 000000000..625fc8c35 --- /dev/null +++ b/src/network/protocols/hide_public_address.hpp @@ -0,0 +1,49 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HIDE_PUBLIC_ADDRESS_HPP +#define HIDE_PUBLIC_ADDRESS_HPP + +#include "network/protocol.hpp" +#include + +class HidePublicAddress : public Protocol +{ + public: + HidePublicAddress(CallbackObject* callback_object); + virtual ~HidePublicAddress(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update(); + + virtual void setUsername(std::string username); + virtual void setPassword(std::string password); + protected: + std::string m_username; + std::string m_password; + + enum STATE + { + NONE, + DONE + }; + STATE m_state; +}; + +#endif // HIDE_PUBLIC_ADDRESS_HPP diff --git a/src/network/protocols/lobby_room_protocol.cpp b/src/network/protocols/lobby_room_protocol.cpp new file mode 100644 index 000000000..74b6981e6 --- /dev/null +++ b/src/network/protocols/lobby_room_protocol.cpp @@ -0,0 +1,50 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "lobby_room_protocol.hpp" + +#include + +LobbyRoomProtocol::LobbyRoomProtocol(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_LOBBY_ROOM) +{ +} + +LobbyRoomProtocol::~LobbyRoomProtocol() +{ +} + +void LobbyRoomProtocol::notifyEvent(Event* event) +{ + if (event->type == EVENT_TYPE_MESSAGE) + { + printf("Message from %u : \"%s\"\n", event->peer->getAddress(), event->data.c_str()); + } +} + +void LobbyRoomProtocol::setup() +{ +} + +void LobbyRoomProtocol::update() +{ +} + +void LobbyRoomProtocol::sendMessage(std::string message) +{ + +} diff --git a/src/network/race_result_message.hpp b/src/network/protocols/lobby_room_protocol.hpp similarity index 50% rename from src/network/race_result_message.hpp rename to src/network/protocols/lobby_room_protocol.hpp index 95d3a6152..59e28b450 100644 --- a/src/network/race_result_message.hpp +++ b/src/network/protocols/lobby_room_protocol.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,30 +16,32 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_RACE_RESULT_MESSAGE_HPP -#define HEADER_RACE_RESULT_MESSAGE_HPP +#ifndef LOBBY_ROOM_PROTOCOL_HPP +#define LOBBY_ROOM_PROTOCOL_HPP -#include +#include "network/protocol.hpp" -#include "network/message.hpp" - - -/** This message is from the server to all clients to inform them about the - * result of a race. The clients wait for this message before they finish - * a race. +/*! + * \class LobbyRoomProtocol + * \brief Class used while the game is being prepared. + * This protocol starts when a server opens a game, or when a client joins a game. + * It is used to exchange data about the race settings, like kart selection. */ -class RaceResultMessage : public Message +class LobbyRoomProtocol : public Protocol { - struct RaceResult { - float m_time; - int m_score; - }; // RaceResult -private: - std::vector m_all_results; -public: - RaceResultMessage(); - RaceResultMessage(ENetPacket* pkt); - void addRaceResult(int kart_id, float time, int points); - void getRaceResult(int kart_id, float &time, int &points); -}; // RaceResultMessage -#endif + public: + LobbyRoomProtocol(CallbackObject* callback_object); + virtual ~LobbyRoomProtocol(); + + virtual void notifyEvent(Event* event); + + virtual void setup(); + + virtual void update(); + + void sendMessage(std::string message); + + protected: +}; + +#endif // LOBBY_ROOM_PROTOCOL_HPP diff --git a/src/network/protocols/show_public_address.cpp b/src/network/protocols/show_public_address.cpp new file mode 100644 index 000000000..b58cd61aa --- /dev/null +++ b/src/network/protocols/show_public_address.cpp @@ -0,0 +1,84 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "show_public_address.hpp" + +#include "../http_functions.hpp" + +#include + +ShowPublicAddress::ShowPublicAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) +{ +} + +ShowPublicAddress::~ShowPublicAddress() +{ +} + +void ShowPublicAddress::notifyEvent(Event* event) +{ +} + +void ShowPublicAddress::setup() +{ + m_state = NONE; + if (m_public_ip == 0 || m_public_port == 0 || m_username == "" || m_password == "") + { + printf("__ShowPublicAddress> You have to set the public ip:port, username:password and the host nickname before starting this protocol.\n"); + m_listener->requestTerminate(this); + } +} + +void ShowPublicAddress::update() +{ + if (m_state == NONE) + { + char url[512]; + sprintf(url, "http://stkconnect.freeserver.me/log.php?set&nick=%s&ip=%u&port=%u&pwd=%s", m_username.c_str(), m_public_ip, m_public_port, m_password.c_str()); + std::string result = HTTP::getPage(url); + if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') + { + printf("__ShowPublicAddress> Address set.\n"); + m_state = DONE; + } + if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') + { + printf("__ShowPublicAddress> Login fail. Please re-set username:password and unpause the protocol.\n"); + m_state = NONE; + pause(); + } + } + else if (m_state == DONE) + { + m_listener->requestTerminate(this); + } +} + +void ShowPublicAddress::setUsername(std::string username) +{ + m_username = username; +} +void ShowPublicAddress::setPassword(std::string password) +{ + m_password = password; +} +void ShowPublicAddress::setPublicAddress(uint32_t ip, uint16_t port) +{ + m_public_ip = ip; + m_public_port = port; +} diff --git a/src/network/protocols/show_public_address.hpp b/src/network/protocols/show_public_address.hpp new file mode 100644 index 000000000..fde549e11 --- /dev/null +++ b/src/network/protocols/show_public_address.hpp @@ -0,0 +1,53 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SHOW_PUBLIC_ADDRESS_HPP +#define SHOW_PUBLIC_ADDRESS_HPP + +#include "network/protocol.hpp" +#include + +class ShowPublicAddress : public Protocol +{ + public: + ShowPublicAddress(CallbackObject* callback_object); + virtual ~ShowPublicAddress(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update(); + + virtual void setUsername(std::string username); + virtual void setPassword(std::string password); + virtual void setPublicAddress(uint32_t ip, uint16_t port); + + protected: + std::string m_username; + std::string m_password; + uint32_t m_public_ip; + uint16_t m_public_port; + + enum STATE + { + NONE, + DONE + }; + STATE m_state; +}; + +#endif // HIDE_PUBLIC_ADDRESS_HPP diff --git a/src/network/race_info_message.cpp b/src/network/race_info_message.cpp deleted file mode 100644 index 4d2b6aa0b..000000000 --- a/src/network/race_info_message.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/race_info_message.hpp" - -#include "race/grand_prix_manager.hpp" -#include "race/race_manager.hpp" - -RaceInfoMessage::RaceInfoMessage(const std::vector& kart_info) - : Message(Message::MT_RACE_INFO) -{ - const GrandPrixData *cup=NULL; - int len = 2*getCharLength() // major, difficulty - + getIntLength() // minor - which is too big for a char/short! - + getCharLength(); // num karts - if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX) - { - cup = race_manager->getGrandPrix(); - len += getStringLength(cup->getId()); - } - else - { - len += getStringLength(race_manager->getTrackName()); - len += getCharLength(); // num laps - } - len += getCharLength(); // kart_info.size() - for(unsigned int i=0; i& rkl=race_manager->getAIKartList(); - len += getStringVectorLength(rkl); - - allocate(len); - addChar(race_manager->getMajorMode() ); - addInt (race_manager->getMinorMode() ); - addChar(race_manager->getDifficulty() ); - addChar(race_manager->getNumberOfKarts()); - if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX) - addString(cup->getId()); - else - { - addString(race_manager->getTrackName()); - addChar(race_manager->getNumLaps()); - } - - addChar(kart_info.size()); - for(unsigned int i=0; isetMajorMode ( RaceManager::MajorRaceModeType(getChar()) ); - race_manager->setMinorMode ( RaceManager::MinorRaceModeType(getInt()) ); - race_manager->setDifficulty( RaceManager::Difficulty (getChar()) ); - race_manager->setNumKarts ( getChar() ); - if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX) - { - const GrandPrixData *cup = grand_prix_manager->getGrandPrix(getString()); - race_manager->setGrandPrix(*cup); - } - else - { - race_manager->setTrack(getString()); - race_manager->setNumLaps(getChar()); - } - - std::vector kart_info; - kart_info.resize(getChar()); - - for(unsigned int i=0; isetNumPlayers(kart_info.size()); - for(unsigned int i=0; isetPlayerKart(i, kart_info[i]); - } - std::vector rkl=getStringVector(); - race_manager->setAIKartList(rkl); -} // RaceInfoMessage diff --git a/src/network/race_result_ack_message.hpp b/src/network/race_result_ack_message.hpp deleted file mode 100644 index eda990208..000000000 --- a/src/network/race_result_ack_message.hpp +++ /dev/null @@ -1,58 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_RACE_RESULT_ACK_MESSAGE_HPP -#define HEADER_RACE_RESULT_ACK_MESSAGE_HPP - -#include - -#include "network/message.hpp" - - -/** This message is sent from the clients to the server when the race result - * screen was acknowledged, and then from the server to all clients to - * finish synchronisation. - */ -class RaceResultAckMessage : public Message -{ -private: - char m_menu_selected; -public: - /** Constructor, creates an empty message - */ - RaceResultAckMessage(char menu_choice) : Message(Message::MT_RACE_RESULT_ACK) - { - allocate(getCharLength()); - addChar(menu_choice); - } // RaceResultAckMessage - - // ------------------------------------------------------------------------ - /** Receives the ack message. - * \param pkt Received enet packet. - */ - RaceResultAckMessage(ENetPacket* pkt):Message(pkt, MT_RACE_RESULT_ACK) - { - m_menu_selected = getChar(); - } // RaceResultAckMessage(EnetPacket) - // ------------------------------------------------------------------------ - /** Returns the menu selected on the server after this message is received - * on a client. */ - char getSelectedMenu() const {return m_menu_selected; } - -}; // RaceResultAckMessageMessage -#endif diff --git a/src/network/race_result_message.cpp b/src/network/race_result_message.cpp deleted file mode 100644 index eeaacd656..000000000 --- a/src/network/race_result_message.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/race_result_message.hpp" - -#include "karts/abstract_kart.hpp" -#include "modes/world.hpp" -#include "race/race_manager.hpp" - -/** Creates a message containing the finishing time and rank of each kart. - * This message is serialised so that it can be sent. - */ -RaceResultMessage::RaceResultMessage() : Message(MT_RACE_RESULT) -{ - World *world = World::getWorld(); - const unsigned int num_karts = world->getNumKarts(); - allocate(num_karts * (getFloatLength()+getCharLength())); - for(unsigned int i=0; igetKart(i); - addFloat(kart->getFinishTime()); - addChar(kart->getPosition()); - } // for i in karts -} // RaceResultMessage - -// ---------------------------------------------------------------------------- -/** De-serialises a race result message and sets the appropriate results in - * the kart and the race manager. - * \param pkt The enet message paket. - */ -RaceResultMessage::RaceResultMessage(ENetPacket* pkt) - : Message(pkt, MT_RACE_RESULT) -{ - World *world = World::getWorld(); - const unsigned int num_karts = world->getNumKarts(); - for(unsigned int i=0; igetKart(i); - float time = getFloat(); - char position = getChar(); - kart->setPosition(position); - kart->finishedRace(time); - } -} // RaceResultMessage - diff --git a/src/network/race_state.cpp b/src/network/race_state.cpp deleted file mode 100644 index db5c66c83..000000000 --- a/src/network/race_state.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/race_state.hpp" - -#include "items/item_manager.hpp" -#include "items/powerup.hpp" -#include "items/projectile_manager.hpp" -#include "karts/rescue_animation.hpp" -#include "modes/world.hpp" -#include "network/network_manager.hpp" -#include "physics/physics.hpp" - -RaceState *race_state=NULL; - -// ---------------------------------------------------------------------------- -void RaceState::serialise() -{ - // First compute the overall size needed - // ===================================== - int len = 0; - - // 1. Add all kart information - // --------------------------- - unsigned int num_karts = World::getWorld()->getCurrentNumKarts(); - KartControl c; - // Send the number of karts and for each kart the compressed - // control structure, xyz,hpr, and speed (which is necessary to - // display the speed, and e.g. to determine when a parachute is detached) - len += 1 + num_karts*(KartControl::getLength() - + getVec3Length()+getQuaternionLength() - + getFloatLength()) ; - - // 2. Add information about collected items - // --------------------------------------- - len += 1 + m_item_info.size()* ItemInfo::getLength(); - - // 3. Add rocket positions - // ----------------------- - len += 2 + m_flyable_info.size()*FlyableInfo::getLength(); - - // 4. Add collisions - // ================= - len += 1 + m_collision_info.size()*getCharLength(); - - // Now add the data - // ================ - allocate(len); - - // 1. Kart positions - // ----------------- - addChar(num_karts); - World *world = World::getWorld(); - for(unsigned int i=0; igetKart(i); - m_kart_controls[i].serialise(this); - addVec3(kart->getXYZ()); - addQuaternion(kart->getRotation()); - addFloat(kart->getSpeed()); - } // for i - - // 2. Collected items - // ----------------- - addChar(m_item_info.size()); - for(unsigned int i=0; igetKart(i); - // Firing needs to be done from here to guarantee that any potential - // new rockets are created before the update for the rockets is handled - if(kc.m_fire) - kart->getPowerup()->use(); - kart->setXYZ(xyz); - kart->setRotation(q); - kart->setSpeed(getFloat()); - } // for i - - // 2. Collected Items - // ----------------- - unsigned short num_items=getChar(); - for(unsigned int i=0; igetKart(hi.m_kart_id)); - else - { - Item *item = ItemManager::get()->getItem(hi.m_item_id); - ItemManager::get()->collectedItem(item, - world->getKart(hi.m_kart_id), - hi.m_add_info); - } - } - - // 3. Projectiles - // -------------- - unsigned short num_flyables = getShort(); - m_flyable_info.clear(); - m_flyable_info.resize(num_flyables); - for(unsigned short i=0; igetKart(kart_id1)->crashed(NULL, normal); - } - else - { - // FIXME: KartKartCollision now takes information about the - // collision points. This either needs to be added as the third - // parameter, or perhaps the outcome of the collision (the - // impulse) could be added. - world->getPhysics()->KartKartCollision( - world->getKart(kart_id1), Vec3(0,0,0), - world->getKart(kart_id2), Vec3(0,0,0)); - } - } // for(i=0; i - -#include "items/flyable.hpp" -#include "items/item.hpp" -#include "karts/abstract_kart.hpp" -#include "karts/controller/kart_control.hpp" -#include "modes/world.hpp" -#include "network/flyable_info.hpp" -#include "network/item_info.hpp" -#include "network/message.hpp" -#include "utils/aligned_array.hpp" - -/** This class stores the state information of a (single) race, e.g. the - position and orientation of karts, collisions that have happened etc. - It is used for the network version to update the clients with the - 'official' state information from the server. - */ -class RaceState : public Message -{ -private: - - /** Updates about collected items. */ - std::vector m_item_info; - /** Updates about existing flyables. */ - AlignedArray m_flyable_info; - /** Stores the controls of each kart at the beginning of its update(). */ - std::vector m_kart_controls; - /** Collision information. This vector stores information about which - * kart collided with which kart or track (kartid=-1) */ - std::vector m_collision_info; - - public: - /** Initialise the global race state. */ - RaceState() : Message(MT_RACE_STATE) - { - m_kart_controls.resize(World::getWorld()->getNumKarts()); - } // RaceState() - // -------------------------------------------------------------------- - void itemCollected(int kartid, int item_id, char add_info=-1) - { - m_item_info.push_back(ItemInfo(kartid, item_id, add_info)); - } // itemCollected - // -------------------------------------------------------------------- - /** Collects information about collision in which at least one kart was - * involved. Other collision (e.g. projectiles, moving physics) are - * not needed on the client, so it's not stored at all. If a kart - * track collision happens, the second kart id is -1 (necessary to - * play back sound effects). A simple int vector is used to store the - * pair of collision, so the first collision is using the index 0 and - * 1; the second one 2 and 3 etc. - * \param kartId1 World id of the kart involved in the collision. - * \param kartId2 World id of the 2nd kart involved in the collision, - * or -1 if it's the track (which is the default). - */ - void addCollision(signed char kartId1, signed char kartId2=-1) - { - m_collision_info.push_back(kartId1); - m_collision_info.push_back(kartId2); - } // addCollision - // -------------------------------------------------------------------- - void setNumFlyables(int n) { m_flyable_info.resize(n); } - // -------------------------------------------------------------------- - void setFlyableInfo(int n, const FlyableInfo& fi) - { - m_flyable_info[n] = fi; - } - // -------------------------------------------------------------------- - /** Stores the current kart control (at the time kart->update() is - * called. This allows modifications of kart->m_control during the - * update (e.g. see in kart::update() how firing is handled). - */ - void storeKartControls(const AbstractKart& kart) - { - m_kart_controls[kart.getWorldKartId()] = kart.getControls(); - } // storeKartControls - // -------------------------------------------------------------------- - void serialise(); - void receive(ENetPacket *pkt); - void clear(); // Removes all currently stored information - unsigned int getNumFlyables() const {return m_flyable_info.size(); } - const FlyableInfo - &getFlyable(unsigned int i) const {return m_flyable_info[i];} - }; // RaceState - -extern RaceState *race_state; - -#endif - diff --git a/src/network/server_network_manager.cpp b/src/network/server_network_manager.cpp new file mode 100644 index 000000000..06b5cd1f4 --- /dev/null +++ b/src/network/server_network_manager.cpp @@ -0,0 +1,123 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "server_network_manager.hpp" + +#include "protocols/get_public_address.hpp" +#include "protocols/hide_public_address.hpp" +#include "protocols/show_public_address.hpp" +#include "protocols/get_peer_address.hpp" +#include "protocols/connect_to_server.hpp" + +#include +#include +#include +#include + +ServerNetworkManager::ServerNetworkManager() +{ + m_localhost = NULL; +} + +ServerNetworkManager::~ServerNetworkManager() +{ +} + +void ServerNetworkManager::run() +{ + if (enet_initialize() != 0) + { + printf("Could not initialize enet.\n"); + return; + } + NetworkManager::run(); +} + +void ServerNetworkManager::start() +{ + m_localhost = new STKHost(); + m_localhost->setupServer(STKHost::HOST_ANY, 7321, 32, 2, 0, 0); + m_localhost->startListening(); + printf("Server now setup, listening on port 7321.\n"); + + printf("_NetworkInterface>Starting the global protocol\n"); + // step 1 : retreive public address + Protocol* protocol = new GetPublicAddress(&m_public_address); + ProtocolManager::getInstance()->requestStart(protocol); + while (ProtocolManager::getInstance()->getProtocolState(protocol) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address is known.\n"); + + // step 2 : show the public address for others (here, the server) + ShowPublicAddress* spa = new ShowPublicAddress(NULL); + spa->setPassword(m_player_login.password); + spa->setUsername(m_player_login.username); + spa->setPublicAddress(m_public_address.ip, m_public_address.port); + ProtocolManager::getInstance()->requestStart(spa); + while (ProtocolManager::getInstance()->getProtocolState(spa) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address is being shown online.\n"); +} + +bool ServerNetworkManager::connectToPeer(std::string peer_username) +{ + printf("_NetworkInterface>Starting the connection to host protocol\n"); + + // step 3 : get the peer's addres. + TransportAddress addr; + GetPeerAddress* gpa = new GetPeerAddress(&addr); + gpa->setPeerName(peer_username); + ProtocolManager::getInstance()->requestStart(gpa); + while (ProtocolManager::getInstance()->getProtocolState(gpa) != PROTOCOL_STATE_TERMINATED ) + { + } + printf("_NetworkInterface> The public address of the peer is known.\n"); + + // step 2 : connect to the peer + ConnectToServer* cts = new ConnectToServer(NULL); + cts->setServerAddress(addr.ip, addr.port); + ProtocolManager::getInstance()->requestStart(cts); + while (ProtocolManager::getInstance()->getProtocolState(cts) != PROTOCOL_STATE_TERMINATED ) + { + } + bool success = false; + if (isConnectedTo(addr)) + { + success = true; + printf("_NetworkInterface> CONNECTION SUCCES : YOU ARE NOW CONNECTED TO A PEER.\n"); + } + else + { + printf("_NetworkInterface> We are NOT connected to the server.\n"); + } + + return success; +} + +void ServerNetworkManager::packetReceived(char* data) +{ + printf("ServerNetworkManager::packetReceived()\n"); + puts(data); + sendPacket(data); +} +void ServerNetworkManager::sendPacket(char* data) +{ + m_localhost->broadcastPacket(data); +} diff --git a/src/network/server_network_manager.hpp b/src/network/server_network_manager.hpp new file mode 100644 index 000000000..630aae26a --- /dev/null +++ b/src/network/server_network_manager.hpp @@ -0,0 +1,50 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SERVER_NETWORK_MANAGER_HPP +#define SERVER_NETWORK_MANAGER_HPP + +#include "network_manager.hpp" + + +class ServerNetworkManager : public NetworkManager +{ + friend class Singleton; + public: + static ServerNetworkManager* getInstance() + { + return Singleton::getInstance(); + } + + virtual void run(); + + void start(); + bool connectToPeer(std::string peer_username); + + virtual void packetReceived(char* data); + virtual void sendPacket(char* data); + + virtual bool isServer() { return false; } + + protected: + ServerNetworkManager(); + virtual ~ServerNetworkManager(); + +}; + +#endif // SERVER_NETWORK_MANAGER_HPP diff --git a/src/network/singleton.hpp b/src/network/singleton.hpp new file mode 100644 index 000000000..1a4ea59a5 --- /dev/null +++ b/src/network/singleton.hpp @@ -0,0 +1,63 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SINGLETON_HPP +#define SINGLETON_HPP + +#include + +template +class Singleton +{ + protected: + Singleton () { } + virtual ~Singleton () { std::cout << "destroying singleton." << std::endl; delete m_singleton; } + + public: + template + static S *getInstance () + { + if (m_singleton == NULL) + m_singleton = new S; + + S* result = (dynamic_cast (m_singleton)); + if (result == NULL) + std::cout << "THE SINGLETON HAS NOT BEEN REALOCATED, BUT IS NOT OF THE REQUESTED TYPE." << std::endl; + return result; + } + static T *getInstance() + { + return (dynamic_cast (m_singleton)); + } + + static void kill () + { + if (NULL != m_singleton) + { + delete m_singleton; + m_singleton = NULL; + } + } + + private: + static T *m_singleton; +}; + +template T *Singleton::m_singleton = NULL; + +#endif // SINGLETON_HPP diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp new file mode 100644 index 000000000..b5c0d4fb2 --- /dev/null +++ b/src/network/stk_host.cpp @@ -0,0 +1,218 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stk_host.hpp" + +#include "network_manager.hpp" + +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- + +void* STKHost::receive_data(void* self) +{ + ENetEvent event; + ENetHost* host = (((STKHost*)(self))->m_host); + while (1) + { + while (enet_host_service(host, &event, 0) != 0) { + Event* evt = new Event(&event); + NetworkManager::getInstance()->notifyEvent(evt); + } + } + return NULL; +} + +// ---------------------------------------------------------------------------- + +STKHost::STKHost() +{ + m_host = NULL; + m_listening_thread = (pthread_t*)(malloc(sizeof(pthread_t))); +} + +// ---------------------------------------------------------------------------- + +STKHost::~STKHost() +{ +} + +// ---------------------------------------------------------------------------- + +void STKHost::setupServer(uint32_t address, uint16_t port, int peer_count, + int channel_limit, uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth) +{ + ENetAddress* addr = (ENetAddress*)(malloc(sizeof(ENetAddress))); + addr->host = address; + addr->port = port; + + m_host = enet_host_create(addr, peer_count, channel_limit, + max_incoming_bandwidth, max_outgoing_bandwidth); + if (m_host == NULL) + { + fprintf (stderr, "An error occurred while trying to create an ENet \ + server host.\n"); + exit (EXIT_FAILURE); + } +} + +// ---------------------------------------------------------------------------- + +void STKHost::setupClient(int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth) +{ + m_host = enet_host_create(NULL, peer_count, channel_limit, + max_incoming_bandwidth, max_outgoing_bandwidth); + if (m_host == NULL) + { + fprintf (stderr, "An error occurred while trying to create an ENet \ + client host.\n"); + exit (EXIT_FAILURE); + } +} + +// ---------------------------------------------------------------------------- + +void STKHost::startListening() +{ + pthread_create(m_listening_thread, NULL, &STKHost::receive_data, this); +} + +// ---------------------------------------------------------------------------- + +void STKHost::stopListening() +{ + pthread_cancel(*m_listening_thread); +} + +// ---------------------------------------------------------------------------- + +void STKHost::sendRawPacket(uint8_t* data, int length, TransportAddress dst) +{ + struct sockaddr_in to; + int to_len = sizeof(to); + memset(&to,0,to_len); + + to.sin_family = AF_INET; + to.sin_port = htons(dst.port); + to.sin_addr.s_addr = htonl(dst.ip); + + sendto(m_host->socket, data, length, 0,(sockaddr*)&to, to_len); +} + +// ---------------------------------------------------------------------------- + +uint8_t* STKHost::receiveRawPacket() +{ + uint8_t* buffer; // max size needed normally (only used for stun) + buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); + memset(buffer, 0, 2048); + + int len = recv(m_host->socket,buffer,2048, 0); + int i = 0; + // wait to receive the message because enet sockets are non-blocking + while(len < 0) + { + i++; + len = recv(m_host->socket,buffer,2048, 0); + usleep(1000); // wait 1 millisecond between two checks + } + printf("Packet received after %i milliseconds\n", i); + return buffer; +} + +// ---------------------------------------------------------------------------- + +uint8_t* STKHost::receiveRawPacket(TransportAddress sender) +{ + uint8_t* buffer; // max size needed normally (only used for stun) + buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); + memset(buffer, 0, 2048); + + socklen_t from_len; + struct sockaddr addr; + + from_len = sizeof(addr); + int len = recvfrom(m_host->socket, buffer, 2048, 0, &addr, &from_len); + + int i = 0; + // wait to receive the message because enet sockets are non-blocking + while(len < 0 || ( + (uint8_t)(addr.sa_data[2]) != (sender.ip>>24&0xff) + && (uint8_t)(addr.sa_data[3]) != (sender.ip>>16&0xff) + && (uint8_t)(addr.sa_data[4]) != (sender.ip>>8&0xff) + && (uint8_t)(addr.sa_data[5]) != (sender.ip&0xff))) + { + i++; + len = recvfrom(m_host->socket, buffer, 2048, 0, &addr, &from_len); + usleep(1000); // wait 1 millisecond between two checks + } + if (addr.sa_family == AF_INET) + { + char s[20]; + inet_ntop(AF_INET, &(((struct sockaddr_in *)&addr)->sin_addr), s, 20); + printf("IPv4 Address %s\n", s); + } + printf("Packet received after %i milliseconds\n", i); + return buffer; +} + +// ---------------------------------------------------------------------------- + +void STKHost::broadcastPacket(char* data) +{ + ENetPacket* packet = enet_packet_create(data, strlen(data)+1, + ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(m_host, 0, packet); +} + +// ---------------------------------------------------------------------------- + +bool STKHost::peerExists(TransportAddress peer) +{ + for (unsigned int i = 0; i < m_host->peerCount; i++) + { + if (m_host->peers[i].address.host == peer.ip && + m_host->peers[i].address.port == peer.port) + { + return true; + } + } + return false; +} + +// ---------------------------------------------------------------------------- + +bool STKHost::isConnectedTo(TransportAddress peer) +{ + for (unsigned int i = 0; i < m_host->peerCount; i++) + { + if (m_host->peers[i].address.host == peer.ip && + m_host->peers[i].address.port == peer.port && + m_host->peers[i].state == ENET_PEER_STATE_CONNECTED) + { + return true; + } + } + return false; +} diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp new file mode 100644 index 000000000..460fc3a14 --- /dev/null +++ b/src/network/stk_host.hpp @@ -0,0 +1,141 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/*! \file stk_host.hpp + * \brief Defines an interface to use network low-level functions easily. + */ +#ifndef STK_HOST_HPP +#define STK_HOST_HPP + +#include +#include "types.hpp" + +/*! \class STKHost + * \brief Represents the local host. + * This host is either a server host or a client host. A client host is in + * charge of connecting to a server. A server opens a socket for incoming + * connections. + * By default, this host will use ENet to exchange packets. It also defines an + * interface for ENet use. Nevertheless, this class can be used to send and/or + * receive packets whithout ENet adding its headers. + * This class is used by the Network Manager to send packets. + */ +class STKHost +{ + friend class STKPeer; // allow direct enet modifications in implementations + public: + /*! \enum HOST_TYPE + * \brief Defines three host types for the server. + * These values tells the host where he will accept connections from. + */ + enum HOST_TYPE + { + HOST_ANY = 0, //!< Any host. + HOST_BROADCAST = 0xFFFFFFFF, //!< Defines the broadcast address. + PORT_ANY = 0 //!< Any port. + }; + + /*! \brief Constructor */ + STKHost(); + /*! \brief Destructor */ + virtual ~STKHost(); + + /*! \brief Thread function checking if data is received. + * This function tries to get data from network low-level functions as + * often as possible. When something is received, it generates an + * event and passes it to the Network Manager. + * \param self : used to pass the ENet host to the function. + */ + static void* receive_data(void* self); + + /*! \brief Setups the host as a server. + * \param address : The IPv4 address of incoming connections. + * \param port : The port on which the server listens. + * \param peer_count : The maximum number of peers. + * \param channel_limit : The maximum number of channels per peer. + * \param max_incoming_bandwidth : The maximum incoming bandwidth. + * \param max_outgoing_bandwidth : The maximum outgoing bandwidth. + */ + void setupServer(uint32_t address, uint16_t port, + int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth); + /*! \brief Setups the host as a client. + * In fact there is only one peer connected to this host. + * \param peer_count : The maximum number of peers. + * \param channel_limit : The maximum number of channels per peer. + * \param max_incoming_bandwidth : The maximum incoming bandwidth. + * \param max_outgoing_bandwidth : The maximum outgoing bandwidth. + */ + void setupClient(int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth); + + /*! \brief Starts the listening of events from ENet. + * Starts a thread that updates it as often as possible. + */ + void startListening(); + /*! \brief Stops the listening of events from ENet. + * Stops the thread that was receiving events. + */ + void stopListening(); + + /*! \brief Sends a packet whithout ENet adding its headers. + * This function is used in particular to achieve the STUN protocol. + * \param data : Data to send. + * \param length : Length of the sent data. + * \param dst : Destination of the packet. + */ + void sendRawPacket(uint8_t* data, int length, + TransportAddress dst); + /*! \brief Receives a packet directly from the network interface. + * Receive a packet whithout ENet processing it. + * \return A string containing the data of the received packet. + */ + uint8_t* receiveRawPacket(); + /*! \brief Receives a packet directly from the network interface and + * filter its address. + * Receive a packet whithout ENet processing it. Checks that the + * sender of the packet is the one that corresponds to the sender + * parameter. Does not check the port right now. + * \param sender : Transport address of the original sender of the + * wanted packet. + * \return A string containing the data of the received packet + * matching the sender's ip address. + */ + uint8_t* receiveRawPacket(TransportAddress sender); + /*! \brief Broadcasts a packet to all peers. + * \param data : Data to send. + */ + void broadcastPacket(char* data); + + /*! \brief Tells if a peer is known. + * \return True if the peer is known, false elseway. + */ + bool peerExists(TransportAddress peer_address); + /*! \brief Tells if a peer is known and connected. + * \return True if the peer is known and connected, false elseway. + */ + bool isConnectedTo(TransportAddress peer_address); + protected: + ENetHost* m_host; //!< ENet host interfacing sockets. + pthread_t* m_listening_thread; //!< Thread listening network events. + +}; + +#endif // STK_HOST_HPP diff --git a/src/network/stk_peer.cpp b/src/network/stk_peer.cpp new file mode 100644 index 000000000..d2990361c --- /dev/null +++ b/src/network/stk_peer.cpp @@ -0,0 +1,77 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stk_peer.hpp" + +#include +#include + +STKPeer::STKPeer() +{ + m_peer = NULL; +} + +STKPeer::~STKPeer() +{ + if (m_peer) + delete m_peer; +} + +bool STKPeer::connectToHost(STKHost* localhost, TransportAddress host, uint32_t channel_count, uint32_t data) +{ + ENetAddress address; + address.host = host.ip; + address.port = host.port; + + ENetPeer* peer = enet_host_connect(localhost->m_host, &address, 2, 0); + if (peer == NULL) + { + printf("Could not try to connect to server.\n"); + return false; + } + printf("Connecting to %i.%i.%i.%i:%i.\n", (peer->address.host>>0)&0xff,(peer->address.host>>8)&0xff,(peer->address.host>>16)&0xff,(peer->address.host>>24)&0xff,peer->address.port); + return true; +} + +void STKPeer::sendPacket(char* data) +{ + //printf("sending packet to %i.%i.%i.%i:%i", (m_peer->address.host>>24)&0xff,(m_peer->address.host>>16)&0xff,(m_peer->address.host>>8)&0xff,(m_peer->address.host>>0)&0xff,m_peer->address.port); + + ENetPacket* packet = enet_packet_create(data, strlen(data)+1,ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(m_peer, 0, packet); +} + +uint32_t STKPeer::getAddress() +{ + return m_peer->address.host; +} + +uint16_t STKPeer::getPort() +{ + return m_peer->address.port; +} + +bool STKPeer::isConnected() +{ + printf("PEER STATE %i\n", m_peer->state); + return (m_peer->state == ENET_PEER_STATE_CONNECTED); +} +bool STKPeer::operator==(ENetPeer* peer) +{ + return peer==m_peer; +} diff --git a/src/network/network_kart.hpp b/src/network/stk_peer.hpp similarity index 55% rename from src/network/network_kart.hpp rename to src/network/stk_peer.hpp index 892439a24..61151e615 100644 --- a/src/network/network_kart.hpp +++ b/src/network/stk_peer.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,22 +16,30 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NETWORK_KART_HPP -#define HEADER_NETWORK_KART_HPP +#ifndef STK_PEER_HPP +#define STK_PEER_HPP -#include "karts/kart.hpp" +#include "stk_host.hpp" +#include -class Track; - -class NetworkKart : public Kart +class STKPeer { -private: - int m_global_player_id; // to identify this kart to the network manager -public: - NetworkKart(const std::string& kart_name, unsigned int world_kart_id, - int position, const btTransform& init_transform, - int global_player_id, RaceManager::KartType type); - void setControl(const KartControl& kc); - virtual bool isNetworkKart() const { return true; } -}; // NetworkKart -#endif + friend class Event; + public: + STKPeer(); + virtual ~STKPeer(); + + virtual void sendPacket(char* data); + + static bool connectToHost(STKHost* localhost, TransportAddress host, uint32_t channel_count, uint32_t data); + + bool isConnected(); + + uint32_t getAddress(); + uint16_t getPort(); + bool operator==(ENetPeer* peer); + protected: + ENetPeer* m_peer; +}; + +#endif // STK_PEER_HPP diff --git a/src/network/world_loaded_message.hpp b/src/network/time.cpp similarity index 65% rename from src/network/world_loaded_message.hpp rename to src/network/time.cpp index 682e0fa05..8623430d4 100644 --- a/src/network/world_loaded_message.hpp +++ b/src/network/time.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,16 +16,17 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_WORLD_LOADED_HPP -#define HEADER_WORLD_LOADED_HPP +#include "time.hpp" -#include "network/message.hpp" - -class WorldLoadedMessage : public Message +namespace Time { -// For now this is an empty message -public: - WorldLoadedMessage() :Message(MT_WORLD_LOADED) {allocate(0);} - WorldLoadedMessage(ENetPacket* pkt):Message(pkt, MT_WORLD_LOADED) {} -}; // WorldLoadedMessage -#endif +double getSeconds() +{ + time_t timer; + time(&timer); + struct tm y2k; + y2k.tm_hour = 0; y2k.tm_min = 0; y2k.tm_sec = 0; + y2k.tm_year = 100; y2k.tm_mon = 0; y2k.tm_mday = 1; + return difftime(timer,mktime(&y2k)); // get the seconds elapsed since january 2000 +} +} diff --git a/src/network/race_info_message.hpp b/src/network/time.hpp similarity index 67% rename from src/network/race_info_message.hpp rename to src/network/time.hpp index 52404b3f5..fc9d7835b 100644 --- a/src/network/race_info_message.hpp +++ b/src/network/time.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,18 +16,14 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_RACE_INFO_MESSAGE_HPP -#define HEADER_RACE_INFO_MESSAGE_HPP +#ifndef TIME_HPP +#define TIME_HPP -#include +#include -#include "network/message.hpp" -#include "network/remote_kart_info.hpp" - -class RaceInfoMessage : public Message +namespace Time { -public: - RaceInfoMessage(const std::vector& kart_info); - RaceInfoMessage(ENetPacket* pkt); -}; // RaceInfoMessage -#endif +double getSeconds(); +} + +#endif // TIME_HPP_INCLUDED diff --git a/src/network/types.hpp b/src/network/types.hpp new file mode 100644 index 000000000..b95380f69 --- /dev/null +++ b/src/network/types.hpp @@ -0,0 +1,65 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/*! \file types.hpp + * \brief Declares the general types that are used by the network. + */ +#ifndef TYPES_HPP +#define TYPES_HPP + +#include +#include + +/*! \class CallbackObject + * \brief Class that must be inherited to pass objects to protocols. + */ +class CallbackObject +{ + public: + CallbackObject() {} + +}; + +/*! \class TransportAddress + * \brief Describes a transport-layer address. + * For IP networks, a transport address is the couple ip:port. + */ +class TransportAddress : public CallbackObject +{ + public: + TransportAddress(uint32_t p_ip = 0, uint16_t p_port = 0) + { ip = p_ip; port = p_port; } + + uint32_t ip; //!< The IPv4 address + uint16_t port; //!< The port number +}; + +/*! \class PlayerLogin + * \brief Contains the information needed to authenticate a user. + */ +class PlayerLogin : public CallbackObject +{ + public: + PlayerLogin() {} + + std::string username; //!< Username of the player + std::string password; //!< Password of the player +}; + + +#endif // TYPES_HPP diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 18104a039..36427d332 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -20,9 +20,12 @@ #include "animations/three_d_animation.hpp" #include "config/user_config.hpp" +#include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" #include "karts/rescue_animation.hpp" -#include "network/race_state.hpp" +#include "items/flyable.hpp" +#include "items/item.hpp" +#include "modes/world.hpp" #include "graphics/stars.hpp" #include "karts/explosion_animation.hpp" #include "physics/btKart.hpp" @@ -157,8 +160,8 @@ void Physics::update(float dt) { AbstractKart *a=p->getUserPointer(0)->getPointerKart(); AbstractKart *b=p->getUserPointer(1)->getPointerKart(); - race_state->addCollision(a->getWorldKartId(), - b->getWorldKartId()); +// race_state->addCollision(a->getWorldKartId(), +// b->getWorldKartId()); KartKartCollision(p->getUserPointer(0)->getPointerKart(), p->getContactPointCS(0), p->getUserPointer(1)->getPointerKart(), @@ -457,7 +460,7 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies, else if(upB->is(UserPointer::UP_KART)) { AbstractKart *kart=upB->getPointerKart(); - race_state->addCollision(kart->getWorldKartId()); +// race_state->addCollision(kart->getWorldKartId()); int n = contact_manifold->getContactPoint(0).m_index0; const Material *m = n>=0 ? upA->getPointerTriangleMesh()->getMaterial(n) @@ -477,7 +480,7 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies, if(upB->is(UserPointer::UP_TRACK)) { AbstractKart *kart = upA->getPointerKart(); - race_state->addCollision(kart->getWorldKartId()); +// race_state->addCollision(kart->getWorldKartId()); int n = contact_manifold->getContactPoint(0).m_index1; const Material *m = n>=0 ? upB->getPointerTriangleMesh()->getMaterial(n) diff --git a/src/race/history.hpp b/src/race/history.hpp index 22aa430ed..71d38a034 100644 --- a/src/race/history.hpp +++ b/src/race/history.hpp @@ -20,6 +20,7 @@ #define HEADER_HISTORY_HPP #include +#include #include "LinearMath/btQuaternion.h" diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index b7247bedf..c07de4790 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -143,9 +143,9 @@ void RaceManager::setLocalKartInfo(unsigned int player_id, assert(0<=player_id && player_id getKart(kart) != NULL); - m_local_player_karts[player_id] = RemoteKartInfo(player_id, kart, +/* m_local_player_karts[player_id] = RemoteKartInfo(player_id, kart, StateManager::get()->getActivePlayerProfile(player_id)->getName(), - network_manager->getMyHostId()); + network_manager->getMyHostId());*/ } // setLocalKartInfo //----------------------------------------------------------------------------- @@ -293,7 +293,7 @@ void RaceManager::startNew(bool from_overworld) // Create the kart status data structure to keep track of scores, times, ... // ========================================================================== m_kart_status.clear(); - + printf("%d %d %d\n", (unsigned int)m_num_karts, m_ai_kart_list.size(), m_player_karts.size()); assert((unsigned int)m_num_karts == m_ai_kart_list.size()+m_player_karts.size()); // First add the AI karts (randomly chosen) @@ -321,8 +321,8 @@ void RaceManager::startNew(bool from_overworld) // ------------------------------------------------- for(int i=m_player_karts.size()-1; i>=0; i--) { - KartType kt=(m_player_karts[i].getHostId()==network_manager->getMyHostId()) - ? KT_PLAYER : KT_NETWORK_PLAYER; + KartType kt= KT_PLAYER; //(m_player_karts[i].getHostId()==network_manager->getMyHostId()) + //? KT_PLAYER : KT_NETWORK_PLAYER; m_kart_status.push_back(KartStatus(m_player_karts[i].getKartName(), i, m_player_karts[i].getLocalPlayerId(), m_player_karts[i].getGlobalPlayerId(), @@ -510,20 +510,20 @@ void RaceManager::next() user_config->saveConfig(); } - if(network_manager->getMode()==NetworkManager::NW_SERVER) - network_manager->beginReadySetGoBarrier(); - else - network_manager->setState(NetworkManager::NS_WAIT_FOR_RACE_DATA); +// if(network_manager->getMode()==NetworkManager::NW_SERVER) +// network_manager->beginReadySetGoBarrier(); +// else +// network_manager->setState(NetworkManager::NS_WAIT_FOR_RACE_DATA); startNextRace(); } else { // Back to main menu. Change the state of the state of the // network manager. - if(network_manager->getMode()==NetworkManager::NW_SERVER) - network_manager->setState(NetworkManager::NS_MAIN_MENU); - else - network_manager->setState(NetworkManager::NS_WAIT_FOR_AVAILABLE_CHARACTERS); +// if(network_manager->getMode()==NetworkManager::NW_SERVER) +// network_manager->setState(NetworkManager::NS_MAIN_MENU); +// else +// network_manager->setState(NetworkManager::NS_WAIT_FOR_AVAILABLE_CHARACTERS); exitRace(); } } // next @@ -753,7 +753,7 @@ void RaceManager::startGP(const GrandPrixData* gp, bool from_overworld) StateManager::get()->enterGameState(); setGrandPrix(*gp); setCoinTarget( 0 ); // Might still be set from a previous challenge - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); setMajorMode(RaceManager::MAJOR_MODE_GRAND_PRIX); startNew(from_overworld); @@ -778,7 +778,7 @@ void RaceManager::startSingleRace(const std::string &track_ident, setMajorMode(RaceManager::MAJOR_MODE_SINGLE); setCoinTarget( 0 ); // Might still be set from a previous challenge - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); startNew(from_overworld); } diff --git a/src/states_screens/dialogs/race_paused_dialog.cpp b/src/states_screens/dialogs/race_paused_dialog.cpp index 3e46dea4b..3e98e0caa 100644 --- a/src/states_screens/dialogs/race_paused_dialog.cpp +++ b/src/states_screens/dialogs/race_paused_dialog.cpp @@ -146,7 +146,7 @@ GUIEngine::EventPropagation else if (selection == "restart") { ModalDialog::dismiss(); - network_manager->setState(NetworkManager::NS_MAIN_MENU); +// network_manager->setState(NetworkManager::NS_MAIN_MENU); World::getWorld()->scheduleUnpause(); race_manager->rerunRace(); return GUIEngine::EVENT_BLOCK; diff --git a/src/states_screens/dialogs/select_challenge.cpp b/src/states_screens/dialogs/select_challenge.cpp index 2b4f885cf..fedfbb6be 100644 --- a/src/states_screens/dialogs/select_challenge.cpp +++ b/src/states_screens/dialogs/select_challenge.cpp @@ -199,7 +199,7 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin // Initialise global data - necessary even in local games to avoid // many if tests in other places (e.g. if network_game call // network_manager else call race_manager). - network_manager->initCharacterDataStructures(); +// network_manager->initCharacterDataStructures(); // Launch challenge if (eventSource == "novice") @@ -226,7 +226,7 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin } // Sets up kart info, including random list of kart for AI - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); race_manager->startNew(true); irr_driver->hidePointer(); diff --git a/src/states_screens/help_screen_1.cpp b/src/states_screens/help_screen_1.cpp index 20ac321d9..a11e208e1 100644 --- a/src/states_screens/help_screen_1.cpp +++ b/src/states_screens/help_screen_1.cpp @@ -82,7 +82,7 @@ void HelpScreen1::eventCallback(Widget* widget, const std::string& name, const i ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); race_manager->startNew(false); } else if (name == "category") diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index 2bbd7e606..e0e465bff 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -331,7 +331,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); +// network_manager->setupPlayerKartInfo(); race_manager->startNew(false); } else if (selection == "story") @@ -402,4 +402,4 @@ void MainMenuScreen::onDisabledItemClicked(const std::string& item) new MessageDialog( _("Please wait while the add-ons are loading")); } } -} // onDisabledItemClicked \ No newline at end of file +} // onDisabledItemClicked From 29697049ed98ddec8c7150892864d326b6e26248 Mon Sep 17 00:00:00 2001 From: hilnius Date: Mon, 1 Jul 2013 22:46:23 +0000 Subject: [PATCH 20/22] network rework git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@13053 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- sources.cmake | 84 ++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/sources.cmake b/sources.cmake index b975bccfd..e1e1692c0 100644 --- a/sources.cmake +++ b/sources.cmake @@ -70,6 +70,7 @@ src/guiengine/widgets/label_widget.cpp src/guiengine/widgets/list_widget.cpp src/guiengine/widgets/model_view_widget.cpp src/guiengine/widgets/progress_bar_widget.cpp +src/guiengine/widgets/rating_bar_widget.cpp src/guiengine/widgets/ribbon_widget.cpp src/guiengine/widgets/spinner_widget.cpp src/guiengine/widgets/text_box_widget.cpp @@ -96,8 +97,8 @@ src/items/projectile_manager.cpp src/items/rubber_ball.cpp src/items/rubber_band.cpp src/items/swatter.cpp -src/karts/abstract_kart.cpp src/karts/abstract_kart_animation.cpp +src/karts/abstract_kart.cpp src/karts/cannon_animation.cpp src/karts/controller/ai_base_controller.cpp src/karts/controller/ai_properties.cpp @@ -134,15 +135,23 @@ src/modes/tutorial_world.cpp src/modes/world.cpp src/modes/world_status.cpp src/modes/world_with_rank.cpp -src/network/connect_message.cpp -src/network/kart_control_message.cpp -src/network/kart_update_message.cpp -src/network/message.cpp -src/network/network_kart.cpp +src/network/client_network_manager.cpp +src/network/event.cpp +src/network/http_functions.cpp +src/network/network_interface.cpp src/network/network_manager.cpp -src/network/race_info_message.cpp -src/network/race_result_message.cpp -src/network/race_state.cpp +src/network/protocol.cpp +src/network/protocol_manager.cpp +src/network/protocols/connect_to_server.cpp +src/network/protocols/get_peer_address.cpp +src/network/protocols/get_public_address.cpp +src/network/protocols/hide_public_address.cpp +src/network/protocols/lobby_room_protocol.cpp +src/network/protocols/show_public_address.cpp +src/network/server_network_manager.cpp +src/network/singleton.cpp +src/network/stk_host.cpp +src/network/stk_peer.cpp src/physics/btKart.cpp src/physics/btKartRaycast.cpp src/physics/btUprightConstraint.cpp @@ -187,13 +196,13 @@ src/states_screens/kart_selection.cpp src/states_screens/main_menu_screen.cpp src/states_screens/minimal_race_gui.cpp src/states_screens/options_screen_audio.cpp -src/states_screens/options_screen_input.cpp src/states_screens/options_screen_input2.cpp +src/states_screens/options_screen_input.cpp src/states_screens/options_screen_players.cpp src/states_screens/options_screen_ui.cpp src/states_screens/options_screen_video.cpp -src/states_screens/race_gui.cpp src/states_screens/race_gui_base.cpp +src/states_screens/race_gui.cpp src/states_screens/race_gui_overworld.cpp src/states_screens/race_result_gui.cpp src/states_screens/race_setup_screen.cpp @@ -253,8 +262,8 @@ src/animations/animation_base.hpp src/animations/ipo.hpp src/animations/three_d_animation.hpp src/audio/dummy_sfx.hpp -src/audio/music.hpp src/audio/music_dummy.hpp +src/audio/music.hpp src/audio/music_information.hpp src/audio/music_manager.hpp src/audio/music_ogg.hpp @@ -262,8 +271,8 @@ src/audio/sfx_base.hpp src/audio/sfx_buffer.hpp src/audio/sfx_manager.hpp src/audio/sfx_openal.hpp -src/challenges/challenge.hpp src/challenges/challenge_data.hpp +src/challenges/challenge.hpp src/challenges/game_slot.hpp src/challenges/unlock_manager.hpp src/config/device_config.hpp @@ -305,23 +314,24 @@ src/guiengine/scalable_font.hpp src/guiengine/screen.hpp src/guiengine/skin.hpp src/guiengine/widget.hpp -src/guiengine/widgets.hpp src/guiengine/widgets/bubble_widget.hpp src/guiengine/widgets/button_widget.hpp src/guiengine/widgets/check_box_widget.hpp src/guiengine/widgets/dynamic_ribbon_widget.hpp +src/guiengine/widgets.hpp src/guiengine/widgets/icon_button_widget.hpp src/guiengine/widgets/label_widget.hpp src/guiengine/widgets/list_widget.hpp src/guiengine/widgets/model_view_widget.hpp src/guiengine/widgets/progress_bar_widget.hpp +src/guiengine/widgets/rating_bar_widget.hpp src/guiengine/widgets/ribbon_widget.hpp src/guiengine/widgets/spinner_widget.hpp src/guiengine/widgets/text_box_widget.hpp src/input/binding.hpp src/input/device_manager.hpp -src/input/input.hpp src/input/input_device.hpp +src/input/input.hpp src/input/input_manager.hpp src/input/wiimote.hpp src/input/wiimote_manager.hpp @@ -343,8 +353,8 @@ src/items/projectile_manager.hpp src/items/rubber_ball.hpp src/items/rubber_band.hpp src/items/swatter.hpp -src/karts/abstract_kart.hpp src/karts/abstract_kart_animation.hpp +src/karts/abstract_kart.hpp src/karts/cannon_animation.hpp src/karts/controller/ai_base_controller.hpp src/karts/controller/ai_properties.hpp @@ -355,8 +365,8 @@ src/karts/controller/player_controller.hpp src/karts/controller/skidding_ai.hpp src/karts/explosion_animation.hpp src/karts/ghost_kart.hpp -src/karts/kart.hpp src/karts/kart_gfx.hpp +src/karts/kart.hpp src/karts/kart_model.hpp src/karts/kart_properties.hpp src/karts/kart_properties_manager.hpp @@ -381,25 +391,25 @@ src/modes/tutorial_world.hpp src/modes/world.hpp src/modes/world_status.hpp src/modes/world_with_rank.hpp -src/network/character_confirm_message.hpp -src/network/character_info_message.hpp -src/network/character_selected_message.hpp -src/network/connect_message.hpp -src/network/flyable_info.hpp -src/network/item_info.hpp -src/network/kart_control_message.hpp -src/network/kart_update_message.hpp -src/network/message.hpp -src/network/network_kart.hpp +src/network/client_network_manager.hpp +src/network/event.hpp +src/network/http_functions.hpp +src/network/network_interface.hpp src/network/network_manager.hpp -src/network/num_players_message.hpp -src/network/race_info_message.hpp -src/network/race_result_ack_message.hpp -src/network/race_result_message.hpp -src/network/race_start_message.hpp -src/network/race_state.hpp +src/network/protocol.hpp +src/network/protocol_manager.hpp +src/network/protocols/connect_to_server.hpp +src/network/protocols/get_peer_address.hpp +src/network/protocols/get_public_address.hpp +src/network/protocols/hide_public_address.hpp +src/network/protocols/lobby_room_protocol.hpp +src/network/protocols/show_public_address.hpp src/network/remote_kart_info.hpp -src/network/world_loaded_message.hpp +src/network/server_network_manager.hpp +src/network/singleton.hpp +src/network/stk_host.hpp +src/network/stk_peer.hpp +src/network/types.hpp src/physics/btKart.hpp src/physics/btKartRaycast.hpp src/physics/btUprightConstraint.hpp @@ -447,13 +457,13 @@ src/states_screens/kart_selection.hpp src/states_screens/main_menu_screen.hpp src/states_screens/minimal_race_gui.hpp src/states_screens/options_screen_audio.hpp -src/states_screens/options_screen_input.hpp src/states_screens/options_screen_input2.hpp +src/states_screens/options_screen_input.hpp src/states_screens/options_screen_players.hpp src/states_screens/options_screen_ui.hpp src/states_screens/options_screen_video.hpp -src/states_screens/race_gui.hpp src/states_screens/race_gui_base.hpp +src/states_screens/race_gui.hpp src/states_screens/race_gui_overworld.hpp src/states_screens/race_result_gui.hpp src/states_screens/race_setup_screen.hpp @@ -483,8 +493,8 @@ src/tracks/check_sphere.hpp src/tracks/check_structure.hpp src/tracks/graph_node.hpp src/tracks/lod_node_loader.hpp -src/tracks/quad.hpp src/tracks/quad_graph.hpp +src/tracks/quad.hpp src/tracks/quad_set.hpp src/tracks/terrain_info.hpp src/tracks/track.hpp From 7a174fd1229b016136cd2e5cf0679c6c07cfbf7a Mon Sep 17 00:00:00 2001 From: hilnius Date: Thu, 4 Jul 2013 13:19:32 +0000 Subject: [PATCH 21/22] cleaning the includes to meet stk's standard git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@13075 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/network/client_network_manager.cpp | 2 +- src/network/client_network_manager.hpp | 4 ++-- src/network/event.cpp | 10 ++++++++-- src/network/event.hpp | 17 ++++++++++------- src/network/http_functions.cpp | 2 +- src/network/network_interface.cpp | 2 +- src/network/network_interface.hpp | 6 +++--- src/network/network_manager.cpp | 14 +++++++------- src/network/network_manager.hpp | 16 +++++++++------- src/network/protocol.cpp | 2 +- src/network/protocol.hpp | 4 ++-- src/network/protocol_manager.cpp | 13 ++++++++----- src/network/protocol_manager.hpp | 6 +++--- src/network/protocols/lobby_room_protocol.cpp | 2 +- src/network/protocols/show_public_address.cpp | 4 ++-- src/network/server_network_manager.cpp | 14 +++++++------- src/network/server_network_manager.hpp | 4 ++-- src/network/stk_host.cpp | 6 +++--- src/network/stk_host.hpp | 4 ++-- src/network/stk_peer.cpp | 4 ++-- src/network/stk_peer.hpp | 4 ++-- src/network/time.cpp | 2 +- 22 files changed, 78 insertions(+), 64 deletions(-) diff --git a/src/network/client_network_manager.cpp b/src/network/client_network_manager.cpp index 1aa96249c..16ef480f9 100644 --- a/src/network/client_network_manager.cpp +++ b/src/network/client_network_manager.cpp @@ -115,7 +115,7 @@ void ClientNetworkManager::packetReceived(char* data) printf("ClientNetworkManager::packetReceived()\n"); puts(data); } -void ClientNetworkManager::sendPacket(char* data) +void ClientNetworkManager::sendPacket(const char* data) { if (m_peers.size() > 1) printf("Ambiguous send of data\n"); diff --git a/src/network/client_network_manager.hpp b/src/network/client_network_manager.hpp index c2518a9d3..c8cb9b28b 100644 --- a/src/network/client_network_manager.hpp +++ b/src/network/client_network_manager.hpp @@ -19,7 +19,7 @@ #ifndef CLIENT_NETWORK_MANAGER_HPP #define CLIENT_NETWORK_MANAGER_HPP -#include "network_manager.hpp" +#include "network/network_manager.hpp" class ClientNetworkManager : public NetworkManager { @@ -35,7 +35,7 @@ class ClientNetworkManager : public NetworkManager bool connectToHost(std::string serverNickname); virtual void packetReceived(char* data); - virtual void sendPacket(char* data); + virtual void sendPacket(const char* data); STKPeer* getPeer(); virtual bool isServer() { return false; } diff --git a/src/network/event.cpp b/src/network/event.cpp index f0a21bf2e..5e6def323 100644 --- a/src/network/event.cpp +++ b/src/network/event.cpp @@ -16,9 +16,9 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "event.hpp" +#include "network/event.hpp" -#include "network_manager.hpp" +#include "network/network_manager.hpp" #include #include @@ -71,3 +71,9 @@ Event::~Event() if (m_packet) enet_packet_destroy(m_packet); } + +void Event::removeFront(int size) +{ + data.erase(0, size); +} + diff --git a/src/network/event.hpp b/src/network/event.hpp index 18c7d3a34..8aa1939b1 100644 --- a/src/network/event.hpp +++ b/src/network/event.hpp @@ -19,7 +19,7 @@ #ifndef EVENT_HPP #define EVENT_HPP -#include "stk_peer.hpp" +#include "network/stk_peer.hpp" #include #include @@ -47,16 +47,19 @@ enum EVENT_TYPE class Event { public: - /*! - * \brief Constructor - * \param event : The event that needs to be translated. + /*! \brief Constructor + * \param event : The event that needs to be translated. */ Event(ENetEvent* event); - /*! - * \brief Destructor - * frees the memory of the ENetPacket. + /*! \brief Destructor + * frees the memory of the ENetPacket. */ ~Event(); + + /*! \brief Remove bytes at the beginning of data. + * \param size : The number of bytes to remove. + */ + void removeFront(int size); EVENT_TYPE type; //!< Type of the event. std::string data; //!< Copy of the data passed by the event. diff --git a/src/network/http_functions.cpp b/src/network/http_functions.cpp index 240178816..83383e863 100644 --- a/src/network/http_functions.cpp +++ b/src/network/http_functions.cpp @@ -16,7 +16,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "http_functions.hpp" +#include "network/http_functions.hpp" #include #include diff --git a/src/network/network_interface.cpp b/src/network/network_interface.cpp index c3222c866..262083330 100644 --- a/src/network/network_interface.cpp +++ b/src/network/network_interface.cpp @@ -16,7 +16,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "network_interface.hpp" +#include "network/network_interface.hpp" NetworkInterface::NetworkInterface() diff --git a/src/network/network_interface.hpp b/src/network/network_interface.hpp index 8221a83d2..c2b7f287c 100644 --- a/src/network/network_interface.hpp +++ b/src/network/network_interface.hpp @@ -19,9 +19,9 @@ #ifndef NETWORK_INTERFACE_H #define NETWORK_INTERFACE_H -#include "singleton.hpp" -#include "types.hpp" -#include "network_manager.hpp" +#include "network/singleton.hpp" +#include "network/types.hpp" +#include "network/network_manager.hpp" #include #include diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index 6c4eebf85..d94a37b77 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -16,15 +16,15 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "network_manager.hpp" +#include "network/network_manager.hpp" -#include "protocols/hide_public_address.hpp" -#include "protocols/show_public_address.hpp" -#include "protocols/get_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/get_public_address.hpp" -#include "protocol_manager.hpp" -#include "client_network_manager.hpp" -#include "server_network_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/client_network_manager.hpp" +#include "network/server_network_manager.hpp" #include diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp index b77832e60..2558c80d2 100644 --- a/src/network/network_manager.hpp +++ b/src/network/network_manager.hpp @@ -19,14 +19,15 @@ #ifndef NETWORKMANAGER_HPP #define NETWORKMANAGER_HPP -#include "stk_peer.hpp" -#include "stk_host.hpp" -#include +#include "network/stk_peer.hpp" +#include "network/stk_host.hpp" -#include "protocol_manager.hpp" -#include "singleton.hpp" -#include "types.hpp" -#include "event.hpp" +#include "network/protocol_manager.hpp" +#include "network/singleton.hpp" +#include "network/types.hpp" +#include "network/event.hpp" + +#include class NetworkManager : public Singleton { @@ -39,6 +40,7 @@ class NetworkManager : public Singleton virtual void setManualSocketsMode(bool manual); virtual void notifyEvent(Event* event); virtual void packetReceived(char* data) = 0; + virtual void sendPacket(const char* data) = 0; // raw data management void setLogin(std::string username, std::string password); diff --git a/src/network/protocol.cpp b/src/network/protocol.cpp index 37abf4d52..366ac7b6d 100644 --- a/src/network/protocol.cpp +++ b/src/network/protocol.cpp @@ -16,7 +16,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "protocol.hpp" +#include "network/protocol.hpp" Protocol::Protocol(CallbackObject* callback_object, PROTOCOL_TYPE type) { diff --git a/src/network/protocol.hpp b/src/network/protocol.hpp index eda60ad78..84dfdf6eb 100644 --- a/src/network/protocol.hpp +++ b/src/network/protocol.hpp @@ -19,8 +19,8 @@ #ifndef PROTOCOL_HPP #define PROTOCOL_HPP -#include "protocol_manager.hpp" -#include "types.hpp" +#include "network/protocol_manager.hpp" +#include "network/types.hpp" #include diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index 036a44886..36e8177be 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -16,9 +16,10 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "protocol_manager.hpp" +#include "network/protocol_manager.hpp" -#include "protocol.hpp" +#include "network/protocol.hpp" +#include "network/network_manager.hpp" #include #include @@ -46,10 +47,11 @@ void ProtocolManager::notifyEvent(Event* event) pthread_mutex_unlock(&m_events_mutex); } -void ProtocolManager::sendMessage(std::string message) +void ProtocolManager::sendMessage(Protocol* sender, std::string message) { - std::string newMessage = " " + message; // add one byte - newMessage[0] = (char)(0); + std::string newMessage = " " + message; // add one byte to add protocol type + newMessage[0] = (char)(sender->getProtocolType()); + NetworkManager::getInstance()->sendPacket(newMessage.c_str()); } int ProtocolManager::requestStart(Protocol* protocol) @@ -184,6 +186,7 @@ void ProtocolManager::update() PROTOCOL_TYPE searchedProtocol = PROTOCOL_NONE; if (event->data.size() > 0) searchedProtocol = (PROTOCOL_TYPE)(event->data[0]); + event->removeFront(1); // remove the first byte which indicates the protocol for (unsigned int i = 0; i < m_protocols.size() ; i++) { if (m_protocols[i].protocol->getProtocolType() == searchedProtocol || event->type != EVENT_TYPE_MESSAGE) // pass data to protocols even when paused diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp index a8abdf078..1193eab14 100644 --- a/src/network/protocol_manager.hpp +++ b/src/network/protocol_manager.hpp @@ -23,8 +23,8 @@ #ifndef PROTOCOL_MANAGER_HPP #define PROTOCOL_MANAGER_HPP -#include "singleton.hpp" -#include "event.hpp" +#include "network/singleton.hpp" +#include "network/event.hpp" #include #include @@ -104,7 +104,7 @@ class ProtocolManager : public Singleton /*! * \brief WILL BE COMMENTED LATER */ - virtual void sendMessage(std::string message); + virtual void sendMessage(Protocol* sender, std::string message); /*! * \brief Asks the manager to start a protocol. diff --git a/src/network/protocols/lobby_room_protocol.cpp b/src/network/protocols/lobby_room_protocol.cpp index 74b6981e6..c578b25d8 100644 --- a/src/network/protocols/lobby_room_protocol.cpp +++ b/src/network/protocols/lobby_room_protocol.cpp @@ -16,7 +16,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "lobby_room_protocol.hpp" +#include "network/protocols/lobby_room_protocol.hpp" #include diff --git a/src/network/protocols/show_public_address.cpp b/src/network/protocols/show_public_address.cpp index b58cd61aa..95601f40b 100644 --- a/src/network/protocols/show_public_address.cpp +++ b/src/network/protocols/show_public_address.cpp @@ -16,9 +16,9 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "show_public_address.hpp" +#include "network/protocols/show_public_address.hpp" -#include "../http_functions.hpp" +#include "network/http_functions.hpp" #include diff --git a/src/network/server_network_manager.cpp b/src/network/server_network_manager.cpp index 06b5cd1f4..1cfffa1a7 100644 --- a/src/network/server_network_manager.cpp +++ b/src/network/server_network_manager.cpp @@ -16,13 +16,13 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "server_network_manager.hpp" +#include "network/server_network_manager.hpp" -#include "protocols/get_public_address.hpp" -#include "protocols/hide_public_address.hpp" -#include "protocols/show_public_address.hpp" -#include "protocols/get_peer_address.hpp" -#include "protocols/connect_to_server.hpp" +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/get_peer_address.hpp" +#include "network/protocols/connect_to_server.hpp" #include #include @@ -117,7 +117,7 @@ void ServerNetworkManager::packetReceived(char* data) puts(data); sendPacket(data); } -void ServerNetworkManager::sendPacket(char* data) +void ServerNetworkManager::sendPacket(const char* data) { m_localhost->broadcastPacket(data); } diff --git a/src/network/server_network_manager.hpp b/src/network/server_network_manager.hpp index 630aae26a..b288208a2 100644 --- a/src/network/server_network_manager.hpp +++ b/src/network/server_network_manager.hpp @@ -19,7 +19,7 @@ #ifndef SERVER_NETWORK_MANAGER_HPP #define SERVER_NETWORK_MANAGER_HPP -#include "network_manager.hpp" +#include "network/network_manager.hpp" class ServerNetworkManager : public NetworkManager @@ -37,7 +37,7 @@ class ServerNetworkManager : public NetworkManager bool connectToPeer(std::string peer_username); virtual void packetReceived(char* data); - virtual void sendPacket(char* data); + virtual void sendPacket(const char* data); virtual bool isServer() { return false; } diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index b5c0d4fb2..3b2969cfc 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -16,9 +16,9 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "stk_host.hpp" +#include "network/stk_host.hpp" -#include "network_manager.hpp" +#include "network/network_manager.hpp" #include #include @@ -179,7 +179,7 @@ uint8_t* STKHost::receiveRawPacket(TransportAddress sender) // ---------------------------------------------------------------------------- -void STKHost::broadcastPacket(char* data) +void STKHost::broadcastPacket(const char* data) { ENetPacket* packet = enet_packet_create(data, strlen(data)+1, ENET_PACKET_FLAG_RELIABLE); diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index 460fc3a14..8bae95e08 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -23,7 +23,7 @@ #define STK_HOST_HPP #include -#include "types.hpp" +#include "network/types.hpp" /*! \class STKHost * \brief Represents the local host. @@ -122,7 +122,7 @@ class STKHost /*! \brief Broadcasts a packet to all peers. * \param data : Data to send. */ - void broadcastPacket(char* data); + void broadcastPacket(const char* data); /*! \brief Tells if a peer is known. * \return True if the peer is known, false elseway. diff --git a/src/network/stk_peer.cpp b/src/network/stk_peer.cpp index d2990361c..0e4e41196 100644 --- a/src/network/stk_peer.cpp +++ b/src/network/stk_peer.cpp @@ -16,7 +16,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "stk_peer.hpp" +#include "network/stk_peer.hpp" #include #include @@ -48,7 +48,7 @@ bool STKPeer::connectToHost(STKHost* localhost, TransportAddress host, uint32_t return true; } -void STKPeer::sendPacket(char* data) +void STKPeer::sendPacket(const char* data) { //printf("sending packet to %i.%i.%i.%i:%i", (m_peer->address.host>>24)&0xff,(m_peer->address.host>>16)&0xff,(m_peer->address.host>>8)&0xff,(m_peer->address.host>>0)&0xff,m_peer->address.port); diff --git a/src/network/stk_peer.hpp b/src/network/stk_peer.hpp index 61151e615..cc3709b93 100644 --- a/src/network/stk_peer.hpp +++ b/src/network/stk_peer.hpp @@ -19,7 +19,7 @@ #ifndef STK_PEER_HPP #define STK_PEER_HPP -#include "stk_host.hpp" +#include "network/stk_host.hpp" #include class STKPeer @@ -29,7 +29,7 @@ class STKPeer STKPeer(); virtual ~STKPeer(); - virtual void sendPacket(char* data); + virtual void sendPacket(const char* data); static bool connectToHost(STKHost* localhost, TransportAddress host, uint32_t channel_count, uint32_t data); diff --git a/src/network/time.cpp b/src/network/time.cpp index 8623430d4..bc90498b9 100644 --- a/src/network/time.cpp +++ b/src/network/time.cpp @@ -16,7 +16,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "time.hpp" +#include "network/time.hpp" namespace Time { From 253a3ec1e886168701f3c1953ac7c2afd4aa4af2 Mon Sep 17 00:00:00 2001 From: hilnius Date: Thu, 4 Jul 2013 16:59:30 +0000 Subject: [PATCH 22/22] cleaning destructors, using log:: instead of printf or std::cout, cleaning more paths, removed useless fnctions git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/hilnius@13076 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/main.cpp | 1 + src/network/client_network_manager.cpp | 25 ++++++-------- src/network/client_network_manager.hpp | 1 - src/network/event.cpp | 7 +--- src/network/http_functions.cpp | 8 ++--- src/network/network_manager.cpp | 30 ++++++++++++---- src/network/network_manager.hpp | 1 - src/network/protocol_manager.cpp | 28 ++++++++++++--- src/network/protocols/connect_to_server.cpp | 22 +++++------- src/network/protocols/get_peer_address.cpp | 17 ++++------ src/network/protocols/get_public_address.cpp | 17 +++++----- src/network/protocols/hide_public_address.cpp | 7 ++-- src/network/protocols/lobby_room_protocol.cpp | 4 +-- src/network/protocols/show_public_address.cpp | 9 +++-- src/network/server_network_manager.cpp | 28 ++++++--------- src/network/server_network_manager.hpp | 1 - src/network/singleton.hpp | 14 ++++---- src/network/stk_host.cpp | 34 +++++++++++++------ src/network/stk_peer.cpp | 16 +++++---- src/network/time.cpp | 32 ----------------- src/network/time.hpp | 29 ---------------- src/network/types.hpp | 3 ++ src/race/race_manager.cpp | 2 +- 23 files changed, 152 insertions(+), 184 deletions(-) delete mode 100644 src/network/time.cpp delete mode 100644 src/network/time.hpp diff --git a/src/main.cpp b/src/main.cpp index cbe0f15d3..8fac93de5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1190,6 +1190,7 @@ void cleanSuperTuxKart() if(news_manager) delete news_manager; if(addons_manager) delete addons_manager; NetworkManager::kill(); + if(grand_prix_manager) delete grand_prix_manager; if(highscore_manager) delete highscore_manager; if(attachment_manager) delete attachment_manager; diff --git a/src/network/client_network_manager.cpp b/src/network/client_network_manager.cpp index 16ef480f9..fd98e9ca5 100644 --- a/src/network/client_network_manager.cpp +++ b/src/network/client_network_manager.cpp @@ -24,7 +24,7 @@ #include "network/protocols/get_peer_address.hpp" #include "network/protocols/connect_to_server.hpp" -#include +#include "utils/log.hpp" ClientNetworkManager::ClientNetworkManager() { @@ -38,7 +38,7 @@ void ClientNetworkManager::run() { if (enet_initialize() != 0) { - printf("Could not initialize enet.\n"); + Log::error("ClientNetworkManager", "Could not initialize enet.\n"); return; } m_localhost = new STKHost(); @@ -50,14 +50,14 @@ void ClientNetworkManager::run() bool ClientNetworkManager::connectToHost(std::string serverNickname) { - printf("_NetworkInterface>Starting the connection to host protocol\n"); + Log::info("ClientNetworkManager", "Starting the connection to host protocol\n"); // step 1 : retreive public address Protocol* protocol = new GetPublicAddress(&m_public_address); ProtocolManager::getInstance()->requestStart(protocol); while (ProtocolManager::getInstance()->getProtocolState(protocol) != PROTOCOL_STATE_TERMINATED ) { } - printf("_NetworkInterface> The public address is known.\n"); + Log::info("ClientNetworkManager", "The public address is known.\n"); // step 2 : show the public address for others (here, the server) ShowPublicAddress* spa = new ShowPublicAddress(NULL); @@ -68,7 +68,7 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) while (ProtocolManager::getInstance()->getProtocolState(spa) != PROTOCOL_STATE_TERMINATED ) { } - printf("_NetworkInterface> The public address is being shown online.\n"); + Log::info("ClientNetworkManager", "The public address is being shown online.\n"); // step 3 : get the server's addres. TransportAddress addr; @@ -78,7 +78,7 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) while (ProtocolManager::getInstance()->getProtocolState(gpa) != PROTOCOL_STATE_TERMINATED ) { } - printf("_NetworkInterface> The public address of the server is known.\n"); + Log::info("ClientNetworkManager", "The public address of the server is known.\n"); // step 4 : connect to the server ConnectToServer* cts = new ConnectToServer(NULL); @@ -91,11 +91,11 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) if (m_localhost->isConnectedTo(TransportAddress(addr.ip, addr.port))) { success = true; - printf("_NetworkInterface> CONNECTION SUCCES : YOU ARE NOW CONNECTED TO A SERVER.\n"); + Log::info("ClientNetworkManager", "Connection success. You are now connected to a server.\n"); } else { - printf("_NetworkInterface> We are NOT connected to the server.\n"); + Log::error("ClientNetworkManager", "We are NOT connected to the server.\n"); } // step 5 : hide our public address HidePublicAddress* hpa = new HidePublicAddress(NULL); @@ -105,20 +105,15 @@ bool ClientNetworkManager::connectToHost(std::string serverNickname) while (ProtocolManager::getInstance()->getProtocolState(hpa) != PROTOCOL_STATE_TERMINATED ) { } - printf("_NetworkInterface> The public address is now hidden online.\n"); + Log::info("ClientNetworkManager", "The public address is now hidden online.\n"); return success; } -void ClientNetworkManager::packetReceived(char* data) -{ - printf("ClientNetworkManager::packetReceived()\n"); - puts(data); -} void ClientNetworkManager::sendPacket(const char* data) { if (m_peers.size() > 1) - printf("Ambiguous send of data\n"); + Log::warn("ClientNetworkManager", "Ambiguous send of data.\n"); m_peers[0]->sendPacket(data); } diff --git a/src/network/client_network_manager.hpp b/src/network/client_network_manager.hpp index c8cb9b28b..33704187a 100644 --- a/src/network/client_network_manager.hpp +++ b/src/network/client_network_manager.hpp @@ -34,7 +34,6 @@ class ClientNetworkManager : public NetworkManager bool connectToHost(std::string serverNickname); - virtual void packetReceived(char* data); virtual void sendPacket(const char* data); STKPeer* getPeer(); diff --git a/src/network/event.cpp b/src/network/event.cpp index 5e6def323..e6a34aae5 100644 --- a/src/network/event.cpp +++ b/src/network/event.cpp @@ -17,12 +17,8 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "network/event.hpp" - #include "network/network_manager.hpp" -#include -#include - Event::Event(ENetEvent* event) { switch (event->type) @@ -57,9 +53,8 @@ Event::Event(ENetEvent* event) return; } } - if (peer == NULL) + if (peer == NULL) // peer does not exist, create him { - printf("The peer still does not exist in %lu peers\n", peers.size()); STKPeer* new_peer = new STKPeer(); new_peer->m_peer = event->peer; peer = new_peer; diff --git a/src/network/http_functions.cpp b/src/network/http_functions.cpp index 83383e863..8ff861a05 100644 --- a/src/network/http_functions.cpp +++ b/src/network/http_functions.cpp @@ -18,10 +18,10 @@ #include "network/http_functions.hpp" -#include +#include "utils/log.hpp" + #include #include -#include #include #include #include @@ -42,7 +42,7 @@ void init() curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if(!curl) - printf("Error while loading cURL library.\n"); + Log::error("HTTP", "Error while loading cURL library.\n"); } std::string getPage(std::string url) @@ -53,7 +53,7 @@ std::string getPage(std::string url) curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); res = curl_easy_perform(curl); if(res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + Log::error("HTTP", "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); return readBuffer; } diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index d94a37b77..308168257 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -26,7 +26,10 @@ #include "network/client_network_manager.hpp" #include "network/server_network_manager.hpp" -#include +#include "utils/log.hpp" + +#include +#include void* protocolManagerUpdate(void* data) { @@ -44,10 +47,23 @@ NetworkManager::NetworkManager() m_public_address.ip = 0; m_public_address.port = 0; m_protocol_manager_update_thread = NULL; + m_localhost = NULL; } NetworkManager::~NetworkManager() { + if (m_protocol_manager_update_thread) + pthread_cancel(*m_protocol_manager_update_thread);//, SIGKILL); + + ProtocolManager::kill(); + + if (m_localhost) + delete m_localhost; + while(!m_peers.empty()) + { + delete m_peers.back(); + m_peers.pop_back(); + } } void NetworkManager::run() @@ -75,15 +91,15 @@ void NetworkManager::setManualSocketsMode(bool manual) void NetworkManager::notifyEvent(Event* event) { - printf("EVENT received\n"); + Log::info("NetworkManager", "EVENT received\n"); switch (event->type) { case EVENT_TYPE_MESSAGE: - printf("Message, Sender : %u, message = \"%s\"\n", event->peer->getAddress(), event->data.c_str()); + Log::info("NetworkManager", "Message, Sender : %u, message = \"%s\"\n", event->peer->getAddress(), event->data.c_str()); break; case EVENT_TYPE_DISCONNECTED: - printf("Somebody is now disconnected. There are now %lu peers.\n", m_peers.size()); - printf("Disconnected host: %i.%i.%i.%i:%i\n", event->peer->getAddress()>>24&0xff, event->peer->getAddress()>>16&0xff, event->peer->getAddress()>>8&0xff, event->peer->getAddress()&0xff,event->peer->getPort()); + Log::info("NetworkManager", "Somebody is now disconnected. There are now %lu peers.\n", m_peers.size()); + Log::info("NetworkManager", "Disconnected host: %i.%i.%i.%i:%i\n", event->peer->getAddress()>>24&0xff, event->peer->getAddress()>>16&0xff, event->peer->getAddress()>>8&0xff, event->peer->getAddress()&0xff,event->peer->getPort()); // remove the peer: for (unsigned int i = 0; i < m_peers.size(); i++) { @@ -94,10 +110,10 @@ void NetworkManager::notifyEvent(Event* event) break; } } - printf("ERROR : the peer that has been disconnected was not registered by the Network Manager.\n"); + Log::fatal("NetworkManager", "The peer that has been disconnected was not registered by the Network Manager.\n"); break; case EVENT_TYPE_CONNECTED: - printf("A client has just connected. There are now %lu peers.\n", m_peers.size() + 1); + Log::info("NetworkManager", "A client has just connected. There are now %lu peers.\n", m_peers.size() + 1); // create the new peer: m_peers.push_back(event->peer); break; diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp index 2558c80d2..a8931862b 100644 --- a/src/network/network_manager.hpp +++ b/src/network/network_manager.hpp @@ -39,7 +39,6 @@ class NetworkManager : public Singleton virtual bool connect(TransportAddress peer); virtual void setManualSocketsMode(bool manual); virtual void notifyEvent(Event* event); - virtual void packetReceived(char* data) = 0; virtual void sendPacket(const char* data) = 0; // raw data management diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index 36e8177be..4b6190814 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -20,13 +20,11 @@ #include "network/protocol.hpp" #include "network/network_manager.hpp" +#include "utils/log.hpp" #include -#include #include -#define RAND_MAX 65536 - ProtocolManager::ProtocolManager() { pthread_mutex_init(&m_events_mutex, NULL); @@ -38,6 +36,26 @@ ProtocolManager::ProtocolManager() ProtocolManager::~ProtocolManager() { + pthread_mutex_lock(&m_events_mutex); + pthread_mutex_lock(&m_protocols_mutex); + pthread_mutex_lock(&m_requests_mutex); + pthread_mutex_lock(&m_id_mutex); + for (unsigned int i = 0; i < m_protocols.size() ; i++) + delete m_protocols[i].protocol; + for (unsigned int i = 0; i < m_events_to_process.size() ; i++) + delete m_events_to_process[i]; + m_protocols.clear(); + m_requests.clear(); + m_events_to_process.clear(); + pthread_mutex_unlock(&m_events_mutex); + pthread_mutex_unlock(&m_protocols_mutex); + pthread_mutex_unlock(&m_requests_mutex); + pthread_mutex_unlock(&m_id_mutex); + + pthread_mutex_destroy(&m_events_mutex); + pthread_mutex_destroy(&m_protocols_mutex); + pthread_mutex_destroy(&m_requests_mutex); + pthread_mutex_destroy(&m_id_mutex); } void ProtocolManager::notifyEvent(Event* event) @@ -122,7 +140,7 @@ void ProtocolManager::requestTerminate(Protocol* protocol) void ProtocolManager::startProtocol(ProtocolInfo protocol) { - printf("__ProtocolManager> A new protocol with id=%u has been started. There are %ld protocols running.\n", protocol.id, m_protocols.size()+1); + Log::info("ProtocolManager", "A new protocol with id=%u has been started. There are %ld protocols running.\n", protocol.id, m_protocols.size()+1); // add the protocol to the protocol vector so that it's updated pthread_mutex_lock(&m_protocols_mutex); m_protocols.push_back(protocol); @@ -170,7 +188,7 @@ void ProtocolManager::protocolTerminated(ProtocolInfo protocol) offset++; } } - printf("__ProtocolManager> A protocol has been terminated. There are %ld protocols running.\n", m_protocols.size()); + Log::info("ProtocolManager", "A protocol has been terminated. There are %ld protocols running.\n", m_protocols.size()); pthread_mutex_unlock(&m_protocols_mutex); } diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 8d086cecb..6034d7588 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -19,10 +19,8 @@ #include "network/protocols/connect_to_server.hpp" #include "network/client_network_manager.hpp" -#include "network/time.hpp" - -#include -#include +#include "utils/time.hpp" +#include "utils/log.hpp" // ---------------------------------------------------------------------------- @@ -48,9 +46,9 @@ void ConnectToServer::notifyEvent(Event* event) event->peer->getAddress() == m_server_ip && event->peer->getPort() == m_server_port) { - printf("The Connect To Server protocol has received an event notifying \ - that he's connected to the peer. The peer sent \"%s\"\n", - event->data.c_str()); + Log::info("ConnectToServer", "The Connect To Server protocol has \ + received an event notifying that he's connected to the peer. \ + The peer sent \"%s\"\n", event->data.c_str()); m_state = DONE; // we received a message, we are connected } } @@ -62,7 +60,8 @@ void ConnectToServer::setup() m_state = NONE; if (m_server_ip == 0 || m_server_port == 0 ) { - printf("You have to set the server's public ip:port of the server.\n"); + Log::error("ConnectToServer", "You have to set the server's public \ + ip:port of the server.\n"); m_listener->requestTerminate(this); } } @@ -74,10 +73,7 @@ void ConnectToServer::update() if (m_state == NONE) { static double target = 0; - double currentTime = Time::getSeconds(); - // sometimes the getSeconds method forgets 3600 seconds. - while (currentTime < target-1800) - currentTime += 3600; + double currentTime = Time::getRealTime(); if (currentTime > target) { NetworkManager::getInstance()->connect( @@ -89,7 +85,7 @@ void ConnectToServer::update() return; } target = currentTime+5; - printf("Retrying to connect in 5 seconds.\n"); + Log::info("ConnectToServer", "Retrying to connect in 5 seconds.\n"); } } else if (m_state == DONE) diff --git a/src/network/protocols/get_peer_address.cpp b/src/network/protocols/get_peer_address.cpp index 238cf7790..ffa2a0b1c 100644 --- a/src/network/protocols/get_peer_address.cpp +++ b/src/network/protocols/get_peer_address.cpp @@ -18,11 +18,9 @@ #include "network/protocols/get_peer_address.hpp" -#include "network/time.hpp" #include "network/http_functions.hpp" - -#include -#include +#include "utils/time.hpp" +#include "utils/log.hpp" GetPeerAddress::GetPeerAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) { @@ -34,6 +32,7 @@ GetPeerAddress::~GetPeerAddress() void GetPeerAddress::notifyEvent(Event* event) { + // nothing there. If we receive events, they must be ignored } void GetPeerAddress::setup() @@ -46,9 +45,7 @@ void GetPeerAddress::update() if (m_state == NONE) { static double target = 0; - double current_time = Time::getSeconds(); - while (current_time < target-1800) // sometimes the getSeconds method forgets 3600 seconds. - current_time += 3600; + double current_time = Time::getRealTime(); if (current_time > target) { char url[512]; @@ -56,7 +53,7 @@ void GetPeerAddress::update() std::string result = HTTP::getPage(url); if (result == "") { - printf("__GetPeerAddress> The host you try to reach does not exist. Change the host name please.\n"); + Log::error("GetPeerAddress", "The host you try to reach does not exist. Change the host name please.\n"); pause(); return; } @@ -68,12 +65,12 @@ void GetPeerAddress::update() uint16_t dst_port = (uint32_t)(atoi(port_nb.c_str())); if (dst_ip == 0 || dst_port == 0) { - printf("__GetPeerAddress> The host you try to reach is not online. There will be a new try in 10 seconds.\n"); + Log::info("GetPeerAddress", "The host you try to reach is not online. There will be a new try in 10 seconds.\n"); target = current_time+10; } else { - printf("__GetPeerAddress> Public ip of target is %i.%i.%i.%i:%i\n", (dst_ip>>24)&0xff, (dst_ip>>16)&0xff, (dst_ip>>8)&0xff, dst_ip&0xff, dst_port); + Log::info("GetPeerAddress", "Public ip of target is %i.%i.%i.%i:%i\n", (dst_ip>>24)&0xff, (dst_ip>>16)&0xff, (dst_ip>>8)&0xff, dst_ip&0xff, dst_port); uint32_t server_ip = ((dst_ip&0x000000ff)<<24) // change the server IP to have a network-byte order + ((dst_ip&0x0000ff00)<<8) + ((dst_ip&0x00ff0000)>>8) diff --git a/src/network/protocols/get_public_address.cpp b/src/network/protocols/get_public_address.cpp index 1b977058a..218645b2a 100644 --- a/src/network/protocols/get_public_address.cpp +++ b/src/network/protocols/get_public_address.cpp @@ -23,10 +23,9 @@ #include "network/protocols/connect_to_server.hpp" #include "network/network_interface.hpp" -#include -#include +#include "utils/log.hpp" + #include -#include int stunRand() { @@ -98,7 +97,7 @@ void GetPublicAddress::update() bytes[19] = (uint8_t)(m_stun_tansaction_id[2]); bytes[20] = '\0'; - printf("__GetPublicAddress> Querrying STUN server 132.177.123.6\n"); + Log::info("GetPublicAddress", "Querrying STUN server 132.177.123.6\n"); unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; NetworkManager::getInstance()->setManualSocketsMode(true); NetworkManager::getInstance()->getHost()->sendRawPacket(bytes, 20, TransportAddress(dst, 3478)); @@ -132,7 +131,7 @@ void GetPublicAddress::update() data[18] == (uint8_t)(m_stun_tansaction_id[2]>>8 ) && data[19] == (uint8_t)(m_stun_tansaction_id[2] )) { - printf("__GetPublicAddress> The STUN server responded with a valid answer\n"); + Log::error("GetPublicAddress", "The STUN server responded with a valid answer\n"); int message_size = data[2]*256+data[3]; // parse the stun message now: @@ -140,12 +139,12 @@ void GetPublicAddress::update() uint8_t* attributes = data+20; if (message_size == 0) { - printf("__GetPublicAddress> STUN answer does not contain any information.\n"); + Log::error("GetPublicAddress", "STUN answer does not contain any information.\n"); finish = true; } if (message_size < 4) // cannot even read the size { - printf("__GetPublicAddress> STUN message is not valid.\n"); + Log::error("GetPublicAddress", "STUN message is not valid.\n"); finish = true; } uint16_t port; @@ -176,14 +175,14 @@ void GetPublicAddress::update() finish = true; if (message_size < 4) // cannot even read the size { - printf("__GetPublicAddress> STUN message is not valid.\n"); + Log::error("GetPublicAddress", "STUN message is not valid.\n"); finish = true; } } // finished parsing, we know our public transport address if (valid) { - printf("__The public address has been found : %i.%i.%i.%i:%i\n", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); + Log::info("GetPublicAddress", "The public address has been found : %i.%i.%i.%i:%i\n", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); m_state = ADDRESS_KNOWN; NetworkManager::getInstance()->setManualSocketsMode(false); TransportAddress* addr = static_cast(m_callback_object); diff --git a/src/network/protocols/hide_public_address.cpp b/src/network/protocols/hide_public_address.cpp index 9af6be7fa..5394e8dd9 100644 --- a/src/network/protocols/hide_public_address.cpp +++ b/src/network/protocols/hide_public_address.cpp @@ -19,8 +19,7 @@ #include "network/protocols/hide_public_address.hpp" #include "network/http_functions.hpp" - -#include +#include "utils/log.hpp" HidePublicAddress::HidePublicAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) { @@ -48,12 +47,12 @@ void HidePublicAddress::update() std::string result = HTTP::getPage(url); if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') { - printf("__HidePublicAddress> Public address hidden successfully.\n"); + Log::info("HidePublicAddress", "Public address hidden successfully.\n"); m_state = DONE; } if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') { - printf("__HidePublicAddress> Public address still visible. Re-set nick:password and retry.\n"); + Log::warn("HidePublicAddress", "Public address still visible. Re-set nick:password and retry.\n"); m_state = NONE; pause(); } diff --git a/src/network/protocols/lobby_room_protocol.cpp b/src/network/protocols/lobby_room_protocol.cpp index c578b25d8..cc39b340e 100644 --- a/src/network/protocols/lobby_room_protocol.cpp +++ b/src/network/protocols/lobby_room_protocol.cpp @@ -18,7 +18,7 @@ #include "network/protocols/lobby_room_protocol.hpp" -#include +#include "utils/log.hpp" LobbyRoomProtocol::LobbyRoomProtocol(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_LOBBY_ROOM) { @@ -32,7 +32,7 @@ void LobbyRoomProtocol::notifyEvent(Event* event) { if (event->type == EVENT_TYPE_MESSAGE) { - printf("Message from %u : \"%s\"\n", event->peer->getAddress(), event->data.c_str()); + Log::info("LobbyRoomProtocol", "Message from %u : \"%s\"\n", event->peer->getAddress(), event->data.c_str()); } } diff --git a/src/network/protocols/show_public_address.cpp b/src/network/protocols/show_public_address.cpp index 95601f40b..bc4248c04 100644 --- a/src/network/protocols/show_public_address.cpp +++ b/src/network/protocols/show_public_address.cpp @@ -19,8 +19,7 @@ #include "network/protocols/show_public_address.hpp" #include "network/http_functions.hpp" - -#include +#include "utils/log.hpp" ShowPublicAddress::ShowPublicAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) { @@ -39,7 +38,7 @@ void ShowPublicAddress::setup() m_state = NONE; if (m_public_ip == 0 || m_public_port == 0 || m_username == "" || m_password == "") { - printf("__ShowPublicAddress> You have to set the public ip:port, username:password and the host nickname before starting this protocol.\n"); + Log::error("ShowPublicAddress", "You have to set the public ip:port, username:password and the host nickname before starting this protocol.\n"); m_listener->requestTerminate(this); } } @@ -53,12 +52,12 @@ void ShowPublicAddress::update() std::string result = HTTP::getPage(url); if (result[0] == 's' && result[1] == 'u' && result[2] == 'c' && result[3] == 'c' && result[4] == 'e' && result[5] == 's' && result[6] == 's') { - printf("__ShowPublicAddress> Address set.\n"); + Log::info("ShowPublicAddress", "Address set.\n"); m_state = DONE; } if (result[0] == 'f' && result[1] == 'a' && result[2] == 'i' && result[3] == 'l') { - printf("__ShowPublicAddress> Login fail. Please re-set username:password and unpause the protocol.\n"); + Log::warn("ShowPublicAddress", "Login fail. Please re-set username:password and unpause the protocol.\n"); m_state = NONE; pause(); } diff --git a/src/network/server_network_manager.cpp b/src/network/server_network_manager.cpp index 1cfffa1a7..8ebe083fa 100644 --- a/src/network/server_network_manager.cpp +++ b/src/network/server_network_manager.cpp @@ -24,9 +24,9 @@ #include "network/protocols/get_peer_address.hpp" #include "network/protocols/connect_to_server.hpp" +#include "utils/log.hpp" + #include -#include -#include #include ServerNetworkManager::ServerNetworkManager() @@ -42,7 +42,7 @@ void ServerNetworkManager::run() { if (enet_initialize() != 0) { - printf("Could not initialize enet.\n"); + Log::error("ServerNetworkManager", "Could not initialize enet.\n"); return; } NetworkManager::run(); @@ -53,16 +53,16 @@ void ServerNetworkManager::start() m_localhost = new STKHost(); m_localhost->setupServer(STKHost::HOST_ANY, 7321, 32, 2, 0, 0); m_localhost->startListening(); - printf("Server now setup, listening on port 7321.\n"); + Log::info("ServerNetworkManager", "Server now setup, listening on port 7321.\n"); - printf("_NetworkInterface>Starting the global protocol\n"); + Log::info("ServerNetworkManager", "Starting the global protocol\n"); // step 1 : retreive public address Protocol* protocol = new GetPublicAddress(&m_public_address); ProtocolManager::getInstance()->requestStart(protocol); while (ProtocolManager::getInstance()->getProtocolState(protocol) != PROTOCOL_STATE_TERMINATED ) { } - printf("_NetworkInterface> The public address is known.\n"); + Log::info("ServerNetworkManager", "The public address is known.\n"); // step 2 : show the public address for others (here, the server) ShowPublicAddress* spa = new ShowPublicAddress(NULL); @@ -73,12 +73,12 @@ void ServerNetworkManager::start() while (ProtocolManager::getInstance()->getProtocolState(spa) != PROTOCOL_STATE_TERMINATED ) { } - printf("_NetworkInterface> The public address is being shown online.\n"); + Log::info("ServerNetworkManager", "The public address is being shown online.\n"); } bool ServerNetworkManager::connectToPeer(std::string peer_username) { - printf("_NetworkInterface>Starting the connection to host protocol\n"); + Log::info("ServerNetworkManager", "Starting the connection to host protocol\n"); // step 3 : get the peer's addres. TransportAddress addr; @@ -88,7 +88,7 @@ bool ServerNetworkManager::connectToPeer(std::string peer_username) while (ProtocolManager::getInstance()->getProtocolState(gpa) != PROTOCOL_STATE_TERMINATED ) { } - printf("_NetworkInterface> The public address of the peer is known.\n"); + Log::info("ServerNetworkManager", "The public address of the peer is known.\n"); // step 2 : connect to the peer ConnectToServer* cts = new ConnectToServer(NULL); @@ -101,22 +101,16 @@ bool ServerNetworkManager::connectToPeer(std::string peer_username) if (isConnectedTo(addr)) { success = true; - printf("_NetworkInterface> CONNECTION SUCCES : YOU ARE NOW CONNECTED TO A PEER.\n"); + Log::info("ServerNetworkManager", "Connection success : you are now connected to the peer.\n"); } else { - printf("_NetworkInterface> We are NOT connected to the server.\n"); + Log::warn("ServerNetworkManager", "We are NOT connected to the peer.\n"); } return success; } -void ServerNetworkManager::packetReceived(char* data) -{ - printf("ServerNetworkManager::packetReceived()\n"); - puts(data); - sendPacket(data); -} void ServerNetworkManager::sendPacket(const char* data) { m_localhost->broadcastPacket(data); diff --git a/src/network/server_network_manager.hpp b/src/network/server_network_manager.hpp index b288208a2..1895a70f9 100644 --- a/src/network/server_network_manager.hpp +++ b/src/network/server_network_manager.hpp @@ -36,7 +36,6 @@ class ServerNetworkManager : public NetworkManager void start(); bool connectToPeer(std::string peer_username); - virtual void packetReceived(char* data); virtual void sendPacket(const char* data); virtual bool isServer() { return false; } diff --git a/src/network/singleton.hpp b/src/network/singleton.hpp index 1a4ea59a5..168d8d720 100644 --- a/src/network/singleton.hpp +++ b/src/network/singleton.hpp @@ -19,14 +19,17 @@ #ifndef SINGLETON_HPP #define SINGLETON_HPP -#include +#include "utils/log.hpp" template class Singleton { protected: - Singleton () { } - virtual ~Singleton () { std::cout << "destroying singleton." << std::endl; delete m_singleton; } + Singleton () { m_singleton = NULL; } + virtual ~Singleton () + { + Log::info("Singleton", "Destroyed singleton."); + } public: template @@ -37,7 +40,7 @@ class Singleton S* result = (dynamic_cast (m_singleton)); if (result == NULL) - std::cout << "THE SINGLETON HAS NOT BEEN REALOCATED, BUT IS NOT OF THE REQUESTED TYPE." << std::endl; + Log::fatal("Singleton", "THE SINGLETON HAS NOT BEEN REALOCATED, BUT IS NOT OF THE REQUESTED TYPE."); return result; } static T *getInstance() @@ -47,10 +50,9 @@ class Singleton static void kill () { - if (NULL != m_singleton) + if (m_singleton) { delete m_singleton; - m_singleton = NULL; } } diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 3b2969cfc..6adfe761a 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -19,11 +19,12 @@ #include "network/stk_host.hpp" #include "network/network_manager.hpp" +#include "utils/log.hpp" -#include #include -#include #include +#include +#include // ---------------------------------------------------------------------------- @@ -46,13 +47,23 @@ void* STKHost::receive_data(void* self) STKHost::STKHost() { m_host = NULL; - m_listening_thread = (pthread_t*)(malloc(sizeof(pthread_t))); + m_listening_thread = NULL; } // ---------------------------------------------------------------------------- STKHost::~STKHost() { + if (m_listening_thread) + { + pthread_cancel(*m_listening_thread);//, SIGKILL); + delete m_listening_thread; + m_listening_thread = NULL; + } + if (m_host) + { + enet_host_destroy(m_host); + } } // ---------------------------------------------------------------------------- @@ -69,7 +80,7 @@ void STKHost::setupServer(uint32_t address, uint16_t port, int peer_count, max_incoming_bandwidth, max_outgoing_bandwidth); if (m_host == NULL) { - fprintf (stderr, "An error occurred while trying to create an ENet \ + Log::info("STKHost", "An error occurred while trying to create an ENet \ server host.\n"); exit (EXIT_FAILURE); } @@ -85,7 +96,7 @@ void STKHost::setupClient(int peer_count, int channel_limit, max_incoming_bandwidth, max_outgoing_bandwidth); if (m_host == NULL) { - fprintf (stderr, "An error occurred while trying to create an ENet \ + Log::info("STKHost", "An error occurred while trying to create an ENet \ client host.\n"); exit (EXIT_FAILURE); } @@ -95,6 +106,7 @@ void STKHost::setupClient(int peer_count, int channel_limit, void STKHost::startListening() { + m_listening_thread = (pthread_t*)(malloc(sizeof(pthread_t))); pthread_create(m_listening_thread, NULL, &STKHost::receive_data, this); } @@ -102,7 +114,11 @@ void STKHost::startListening() void STKHost::stopListening() { - pthread_cancel(*m_listening_thread); + if(m_listening_thread) + { + pthread_cancel(*m_listening_thread); + m_listening_thread = NULL; + } } // ---------------------------------------------------------------------------- @@ -135,9 +151,8 @@ uint8_t* STKHost::receiveRawPacket() { i++; len = recv(m_host->socket,buffer,2048, 0); - usleep(1000); // wait 1 millisecond between two checks + usleep(1000); } - printf("Packet received after %i milliseconds\n", i); return buffer; } @@ -171,9 +186,8 @@ uint8_t* STKHost::receiveRawPacket(TransportAddress sender) { char s[20]; inet_ntop(AF_INET, &(((struct sockaddr_in *)&addr)->sin_addr), s, 20); - printf("IPv4 Address %s\n", s); + Log::info("STKHost", "IPv4 Address of the sender was %s\n", s); } - printf("Packet received after %i milliseconds\n", i); return buffer; } diff --git a/src/network/stk_peer.cpp b/src/network/stk_peer.cpp index 0e4e41196..3ea5a0006 100644 --- a/src/network/stk_peer.cpp +++ b/src/network/stk_peer.cpp @@ -18,7 +18,8 @@ #include "network/stk_peer.hpp" -#include +#include "utils/log.hpp" + #include STKPeer::STKPeer() @@ -29,7 +30,10 @@ STKPeer::STKPeer() STKPeer::~STKPeer() { if (m_peer) - delete m_peer; + { + //free(m_peer); + m_peer = NULL; + } } bool STKPeer::connectToHost(STKHost* localhost, TransportAddress host, uint32_t channel_count, uint32_t data) @@ -41,16 +45,16 @@ bool STKPeer::connectToHost(STKHost* localhost, TransportAddress host, uint32_t ENetPeer* peer = enet_host_connect(localhost->m_host, &address, 2, 0); if (peer == NULL) { - printf("Could not try to connect to server.\n"); + Log::error("STKPeer", "Could not try to connect to server.\n"); return false; } - printf("Connecting to %i.%i.%i.%i:%i.\n", (peer->address.host>>0)&0xff,(peer->address.host>>8)&0xff,(peer->address.host>>16)&0xff,(peer->address.host>>24)&0xff,peer->address.port); + Log::info("STKPeer", "Connecting to %i.%i.%i.%i:%i.\n", (peer->address.host>>0)&0xff,(peer->address.host>>8)&0xff,(peer->address.host>>16)&0xff,(peer->address.host>>24)&0xff,peer->address.port); return true; } void STKPeer::sendPacket(const char* data) { - //printf("sending packet to %i.%i.%i.%i:%i", (m_peer->address.host>>24)&0xff,(m_peer->address.host>>16)&0xff,(m_peer->address.host>>8)&0xff,(m_peer->address.host>>0)&0xff,m_peer->address.port); + //Log::info("STKPeer", "sending packet to %i.%i.%i.%i:%i", (m_peer->address.host>>24)&0xff,(m_peer->address.host>>16)&0xff,(m_peer->address.host>>8)&0xff,(m_peer->address.host>>0)&0xff,m_peer->address.port); ENetPacket* packet = enet_packet_create(data, strlen(data)+1,ENET_PACKET_FLAG_RELIABLE); enet_peer_send(m_peer, 0, packet); @@ -68,7 +72,7 @@ uint16_t STKPeer::getPort() bool STKPeer::isConnected() { - printf("PEER STATE %i\n", m_peer->state); + Log::info("STKPeer", "The peer state is %i\n", m_peer->state); return (m_peer->state == ENET_PEER_STATE_CONNECTED); } bool STKPeer::operator==(ENetPeer* peer) diff --git a/src/network/time.cpp b/src/network/time.cpp deleted file mode 100644 index bc90498b9..000000000 --- a/src/network/time.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/time.hpp" - -namespace Time -{ -double getSeconds() -{ - time_t timer; - time(&timer); - struct tm y2k; - y2k.tm_hour = 0; y2k.tm_min = 0; y2k.tm_sec = 0; - y2k.tm_year = 100; y2k.tm_mon = 0; y2k.tm_mday = 1; - return difftime(timer,mktime(&y2k)); // get the seconds elapsed since january 2000 -} -} diff --git a/src/network/time.hpp b/src/network/time.hpp deleted file mode 100644 index fc9d7835b..000000000 --- a/src/network/time.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef TIME_HPP -#define TIME_HPP - -#include - -namespace Time -{ -double getSeconds(); -} - -#endif // TIME_HPP_INCLUDED diff --git a/src/network/types.hpp b/src/network/types.hpp index b95380f69..dd5b62709 100644 --- a/src/network/types.hpp +++ b/src/network/types.hpp @@ -32,6 +32,7 @@ class CallbackObject { public: CallbackObject() {} + ~CallbackObject() {} }; @@ -44,6 +45,7 @@ class TransportAddress : public CallbackObject public: TransportAddress(uint32_t p_ip = 0, uint16_t p_port = 0) { ip = p_ip; port = p_port; } + ~TransportAddress() {} uint32_t ip; //!< The IPv4 address uint16_t port; //!< The port number @@ -56,6 +58,7 @@ class PlayerLogin : public CallbackObject { public: PlayerLogin() {} + ~PlayerLogin() { username.clear(); password.clear(); } std::string username; //!< Username of the player std::string password; //!< Password of the player diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index c07de4790..d68334a2a 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -293,7 +293,7 @@ void RaceManager::startNew(bool from_overworld) // Create the kart status data structure to keep track of scores, times, ... // ========================================================================== m_kart_status.clear(); - printf("%d %d %d\n", (unsigned int)m_num_karts, m_ai_kart_list.size(), m_player_karts.size()); + printf("%u %lu %lu\n", (unsigned int)m_num_karts, m_ai_kart_list.size(), m_player_karts.size()); assert((unsigned int)m_num_karts == m_ai_kart_list.size()+m_player_karts.size()); // First add the AI karts (randomly chosen)