diff options
author | Björn Steinbrink <B.Steinbrink@gmx.de> | 2006-01-22 13:31:13 +0000 |
---|---|---|
committer | Björn Steinbrink <B.Steinbrink@gmx.de> | 2006-01-22 13:31:13 +0000 |
commit | bd56bf8afdab16383ed8ad08412a8c807f84af85 (patch) | |
tree | 0e963ada63bcbe3c50dd77986aaa15b9ba49816a /src/net/network.cpp | |
parent | 5359640b6f271af31f6423df9d661433eff89a3e (diff) | |
download | mana-bd56bf8afdab16383ed8ad08412a8c807f84af85.tar.gz mana-bd56bf8afdab16383ed8ad08412a8c807f84af85.tar.bz2 mana-bd56bf8afdab16383ed8ad08412a8c807f84af85.tar.xz mana-bd56bf8afdab16383ed8ad08412a8c807f84af85.zip |
Merged NETWORK branch (includes BEING_OVERHAUL).
Diffstat (limited to 'src/net/network.cpp')
-rw-r--r-- | src/net/network.cpp | 505 |
1 files changed, 285 insertions, 220 deletions
diff --git a/src/net/network.cpp b/src/net/network.cpp index fbcf199b..3b0652e2 100644 --- a/src/net/network.cpp +++ b/src/net/network.cpp @@ -23,15 +23,10 @@ #include "network.h" -#include <cassert> -#include <sstream> -#include <SDL_net.h> -#include <SDL_thread.h> - +#include "messagehandler.h" #include "messagein.h" #include "../log.h" -#include "../main.h" /** Warning: buffers and other variables are shared, so there can be only one connection active at a time */ @@ -81,309 +76,379 @@ short packet_lengths[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -unsigned int buffer_size = 65536; -char *in = NULL; -char *out = NULL; -unsigned int in_size = 0; -unsigned int out_size = 0; -int connectionOpen = NET_IDLE; +const unsigned int BUFFER_SIZE = 65536; -TCPsocket sock; -SDLNet_SocketSet set; -SDL_Thread *mThread = NULL; -SDL_mutex *mMutex = NULL; -IPaddress *ip = NULL; +int networkThread(void *data) +{ + Network *network = static_cast<Network*>(data); -char *iptostring(int address) + if (!network->realConnect()) + return -1; + + network->receive(); + + return 0; +} + +Network::Network(): + mAddress(0), mPort(0), + mInBuffer(new char[BUFFER_SIZE]), + mOutBuffer(new char[BUFFER_SIZE]), + mInSize(0), mOutSize(0), + mToSkip(0), + mState(IDLE), + mWorkerThread(0) { - static char asciiIP[16]; + mMutex = SDL_CreateMutex(); +} - sprintf(asciiIP, "%i.%i.%i.%i", - (unsigned char)(address), - (unsigned char)(address >> 8), - (unsigned char)(address >> 16), - (unsigned char)(address >> 24)); +Network::~Network() +{ + clearHandlers(); - return asciiIP; + if (mAddress) + free(mAddress); + + if (mState != IDLE && mState != ERROR) + disconnect(); + + SDL_DestroyMutex(mMutex); + + delete mInBuffer; + delete mOutBuffer; } -int connectionThread(void *ptr) +bool Network::connect(const char *address, short port) { - // Create the socket for the current session - sock = SDLNet_TCP_Open((IPaddress *)ptr); - if (!sock) + if (mState != IDLE && mState != ERROR) { - logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; - return NET_ERROR; + logger->log("Tried to connect an already connected socket!"); + return false; } - // Create a socket set to listen to socket - set = SDLNet_AllocSocketSet(1); - if (!set) + if (!address) { - logger->log("Error in SDLNet_AllocSocketSet(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; - return NET_ERROR; + logger->log("Empty address given to Network::connect()!"); + mState = ERROR; + return false; } - // Add the socket to the set - int ret = SDLNet_TCP_AddSocket(set, sock); - if (ret == -1) + if (mAddress) + free(mAddress); + + mAddress = strdup(address); + mPort = port; + + // Reset to sane values + mOutSize = 0; + mInSize = 0; + mToSkip = 0; + + mState = CONNECTING; + mWorkerThread = SDL_CreateThread(networkThread, this); + if (!mWorkerThread) { - logger->log("Error in SDLNet_AddSocket(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; - return NET_ERROR; + logger->log("Unable to create network worker thread"); + mState = ERROR; + return false; } - // Init buffers - in = (char*)malloc(buffer_size); - out = (char*)malloc(buffer_size); - memset(in, '\0', buffer_size); - memset(out, '\0', buffer_size); - in_size = 0; - out_size = 0; - - SDL_mutexP(mMutex); - logger->log("Network::Started session with %s:%i", - iptostring(((IPaddress *)ptr)->host), - ((IPaddress *)ptr)->port); - connectionOpen = NET_CONNECTED; - SDL_mutexV(mMutex); - return NET_CONNECTED; + return true; } -void openConnection(const char* address, short port) +void Network::disconnect() { - //assert(connectionOpen <= NET_IDLE); + if (mState != CONNECTED && mState != CONNECTING) + return; - // Initialize SDL_net - if (SDLNet_Init() == -1) + mState = IDLE; + + if (mWorkerThread) { - logger->log("Error in SDLNet_Init(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; + SDL_WaitThread(mWorkerThread, NULL); + mWorkerThread = NULL; } + SDLNet_TCP_Close(mSocket); +} - ip = new IPaddress(); +void Network::registerHandler(MessageHandler *handler) +{ + const Uint16 *i = handler->handledMessages; - // Resolve host name - if (SDLNet_ResolveHost(ip, address, port) == -1) + while(*i) { - logger->log("Error in SDLNet_ResolveHost(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; + mMessageHandlers[*i] = handler; + i++; } - connectionOpen = NET_CONNECTING; - // Create the synchronization lock - mMutex = SDL_CreateMutex(); - // Create the connection thread - mThread = SDL_CreateThread(connectionThread, ip); - if (mThread == NULL) { - logger->log("Unable to create connection thread"); - connectionOpen = NET_ERROR; - } + handler->setNetwork(this); } -int pollConnection() +void Network::unregisterHandler(MessageHandler *handler) { - if (mMutex) - { - SDL_mutexP(mMutex); - } + const Uint16 *i = handler->handledMessages; - switch (connectionOpen) + while(*i) { - case NET_IDLE: - case NET_CONNECTING: - break; - case NET_CONNECTED: - case NET_ERROR: - SDL_WaitThread(mThread, NULL); - mThread = NULL; - SDL_DestroyMutex(mMutex); - mMutex = NULL; - break; + std::map<Uint16, MessageHandler*>::iterator iter; + iter = mMessageHandlers.find(*i); + if (iter != mMessageHandlers.end()) + { + mMessageHandlers.erase(iter); + } + i++; } - if (mMutex) + handler->setNetwork(0); +} + +void Network::clearHandlers() +{ + std::map<Uint16, MessageHandler*>::iterator i; + for (i = mMessageHandlers.begin(); i != mMessageHandlers.end(); i++) { - SDL_mutexV(mMutex); + i->second->setNetwork(0); } - return connectionOpen; + mMessageHandlers.clear(); +} + +void Network::dispatchMessages() +{ + if (!messageReady()) + return; + + MessageIn msg = getNextMessage(); + + std::map<Uint16, MessageHandler*>::iterator iter; + iter = mMessageHandlers.find(msg.getId()); + + if (iter != mMessageHandlers.end()) + iter->second->handleMessage(&msg); + else + logger->log("Unhandled packet: %x", msg.getId()); + + skip(msg.getLength()); } -void closeConnection() +void Network::flush() { - //assert(connectionOpen > ); + if (!mOutSize || mState != CONNECTED) + return; + + int ret; - if (connectionOpen == NET_ERROR)return; - if (mThread) + SDL_mutexP(mMutex); + ret = SDLNet_TCP_Send(mSocket, mOutBuffer, mOutSize); + if (ret < (int)mOutSize) { - SDL_WaitThread(mThread, NULL); - mThread = NULL; + logger->log("Error in SDLNet_TCP_Send(): %s", SDLNet_GetError()); + mState = ERROR; } + mOutSize = 0; + SDL_mutexV(mMutex); +} - if (mMutex) +void Network::skip(int len) +{ + SDL_mutexP(mMutex); + mToSkip += len; + if (!mInSize) { - SDL_DestroyMutex(mMutex); - mMutex = NULL; + SDL_mutexV(mMutex); + return; } - if (ip) + if (mInSize >= mToSkip) { - delete ip; - ip = NULL; + mInSize -= mToSkip; + memmove(mInBuffer, mInBuffer + mToSkip, mInSize); + mToSkip = 0; } - - // Remove the socket from the socket set - int ret = SDLNet_TCP_DelSocket(set, sock); - if (ret == -1) + else { - logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError()); + mToSkip -= mInSize; + mInSize = 0; } + SDL_mutexV(mMutex); +} - // Close the TCP connection - SDLNet_TCP_Close(sock); - - // Free the socket set - SDLNet_FreeSocketSet(set); - set = NULL; +bool Network::messageReady() +{ + int len = -1; - // Clear buffers - if (in != NULL) + SDL_mutexP(mMutex); + if (mInSize >= 2) { - free(in); - in = NULL; + len = packet_lengths[readWord(0)]; + + if (len == -1 && mInSize > 4) + len = readWord(2); + } - if (out != NULL) + bool ret = (mInSize >= static_cast<unsigned int>(len)); + SDL_mutexV(mMutex); + + return ret; +} + +MessageIn Network::getNextMessage() +{ + while (!messageReady()) { - free(out); - out = NULL; + if (mState == ERROR) + break; } - in_size = 0; - out_size = 0; + SDL_mutexP(mMutex); + int msgId = readWord(0); + int len = packet_lengths[msgId]; - // Shutdown the network API - SDLNet_Quit(); + if (len == -1) + len = readWord(2); - logger->log("Network::Closed session"); - connectionOpen = NET_IDLE; +#ifdef DEBUG + printf("Received packet 0x%x of length %d\n", msgId, length); +#endif + + MessageIn msg(mInBuffer, len); + SDL_mutexV(mMutex); + + return msg; } -void flush() +bool Network::realConnect() { - // Send all available data, waits if not all data can be sent immediately - if (out_size > 0) + IPaddress ipAddress; + + if (SDLNet_ResolveHost(&ipAddress, mAddress, mPort) == -1) { - int ret = SDLNet_TCP_Send(sock, (char*)out, out_size); - if (ret < (int)out_size) - { - logger->log("Error in SDLNet_TCP_Send(): %s", SDLNet_GetError()); - errorMessage = "You got disconnected from server"; - state = ERROR_STATE; - return; - } - out_size -= ret; + logger->log("Error in SDLNet_ResolveHost(): %s", SDLNet_GetError()); + mState = ERROR; + return false; } - int numReady = SDLNet_CheckSockets(set, 0); - if (numReady == -1) + mState = CONNECTING; + + mSocket = SDLNet_TCP_Open(&ipAddress); + if (!mSocket) { - logger->log("Error: SDLNet_CheckSockets"); - return; + logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError()); + mState = ERROR; + return false; } - else if (numReady == 0) // any socket ready + + logger->log("Network::Started session with %s:%i", + iptostring(ipAddress.host), ipAddress.port); + + mState = CONNECTED; + + return true; +} + +void Network::receive() +{ + SDLNet_SocketSet set; + + if (!(set = SDLNet_AllocSocketSet(1))) { + logger->log("Error in SDLNet_AllocSocketSet(): %s", SDLNet_GetError()); + mState = ERROR; return; } - else if (numReady == 1) // one socket is ready - { - // Receive data from the socket - int ret = SDLNet_TCP_Recv(sock, in + in_size, buffer_size - in_size); - if (ret <= 0) - { - logger->log("Error in SDLNet_TCP_Recv(): %s", SDLNet_GetError()); - errorMessage = "You got disconnected from server"; - state = ERROR_STATE; - return; - } - else { - in_size += ret; - } - } - else // more than one socket is ready.. this should not happen since we only listen once socket. + + if (SDLNet_TCP_AddSocket(set, mSocket) == -1) { - logger->log("Error in SDLNet_TCP_Recv(), %d sockets are ready : %s", numReady, SDLNet_GetError()); - errorMessage = "You got disconnected from server"; - state = ERROR_STATE; - return; + logger->log("Error in SDLNet_AddSocket(): %s", SDLNet_GetError()); + mState = ERROR; } -} -unsigned short readWord(int pos) -{ -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - return SDL_Swap16((*(unsigned short*)(in+(pos)))); -#else - return (*(unsigned short *)(in+(pos))); -#endif -} - -bool packetReady() -{ - bool ret = false; - if (in_size >= 2) + while (mState == CONNECTED) { - int length = packet_lengths[readWord(0)]; - if (length == -1) + // TODO Try to get this to block all the time while still being able + // to escape the loop + int numReady = SDLNet_CheckSockets(set, ((Uint32)500)); + int ret; + switch (numReady) { - if (in_size >= 4) - { - length = readWord(2); - if (in_size >= (unsigned int)length) + case -1: + logger->log("Error: SDLNet_CheckSockets"); + // FALLTHROUGH + case 0: + break; + + case 1: + // Receive data from the socket + SDL_mutexP(mMutex); + ret = SDLNet_TCP_Recv(mSocket, mInBuffer + mInSize, BUFFER_SIZE - mInSize); + + if (!ret) { - ret = true; + // We got disconnected + mState = IDLE; + logger->log("Disconnected."); } - } - } - else if (in_size >= (unsigned int)length) - { - ret = true; + else if (ret < 0) + { + logger->log("Error in SDLNet_TCP_Recv(): %s", SDLNet_GetError()); + mState = ERROR; + } + else { + mInSize += ret; + if (mToSkip) + { + if (mInSize >= mToSkip) + { + mInSize -= mToSkip; + memmove(mInBuffer, mInBuffer + mToSkip, mInSize); + mToSkip = 0; + } + else + { + mToSkip -= mInSize; + mInSize = 0; + } + } + } + SDL_mutexV(mMutex); + break; + + default: + // more than one socket is ready.. + // this should not happen since we only listen once socket. + logger->log("Error in SDLNet_TCP_Recv(), %d sockets are ready : %s", numReady, SDLNet_GetError()); + mState = ERROR; + break; } } - return ret; -} - -MessageIn -get_next_message() -{ - // At least 2 bytes should be received for the message ID - while (in_size < 2 && state != ERROR_STATE) flush(); - - int length = packet_lengths[readWord(0)]; - if (length == -1) + if (SDLNet_TCP_DelSocket(set, mSocket) == -1) { - // Another 2 bytes should be received for the length - while (in_size < 4) flush(); - length = readWord(2); + logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError()); } -#ifdef DEBUG - printf("Received packet 0x%x of length %d\n", readWord(0), length); -#endif + SDLNet_FreeSocketSet(set); +} + +char *iptostring(int address) +{ + static char asciiIP[16]; - // Make sure the whole packet is received - while (in_size < static_cast<unsigned int>(length) && state != ERROR_STATE) flush(); + sprintf(asciiIP, "%i.%i.%i.%i", + (unsigned char)(address), + (unsigned char)(address >> 8), + (unsigned char)(address >> 16), + (unsigned char)(address >> 24)); - return MessageIn(in, length); + return asciiIP; } -void skip(int len) +Uint16 Network::readWord(int pos) { - memcpy(in, in + len, in_size - len); - in_size -= len; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + return SDL_Swap16((*(Uint16*)(mInBuffer+(pos)))); +#else + return (*(Uint16*)(mInBuffer+(pos))); +#endif } |