From 291ad04d5b5c4ab08d85eadde116f968cd579b77 Mon Sep 17 00:00:00 2001
From: Guillaume Melquiond <guillaume.melquiond@gmail.com>
Date: Fri, 29 Dec 2006 13:43:24 +0000
Subject: Physically split the server into one tmwserv-acount program (account
 + chat + database) and multiple tmwserv-game programs (selected with respect
 to the maps). Cleaned the repository by moving server-specific source files
 into dedicated directories.

---
 src/net/connection.cpp        | 110 ++++++++++++++++++++++++++++
 src/net/connection.hpp        |  80 ++++++++++++++++++++
 src/net/connectionhandler.cpp | 166 ++++++++++++++++++++++++++++++++++++++++++
 src/net/connectionhandler.hpp | 123 +++++++++++++++++++++++++++++++
 src/net/messagein.cpp         | 100 +++++++++++++++++++++++++
 src/net/messagein.hpp         |  77 ++++++++++++++++++++
 src/net/messageout.cpp        | 135 ++++++++++++++++++++++++++++++++++
 src/net/messageout.hpp        |  96 ++++++++++++++++++++++++
 src/net/netcomputer.cpp       |  94 ++++++++++++++++++++++++
 src/net/netcomputer.hpp       |  97 ++++++++++++++++++++++++
 10 files changed, 1078 insertions(+)
 create mode 100644 src/net/connection.cpp
 create mode 100644 src/net/connection.hpp
 create mode 100644 src/net/connectionhandler.cpp
 create mode 100644 src/net/connectionhandler.hpp
 create mode 100644 src/net/messagein.cpp
 create mode 100644 src/net/messagein.hpp
 create mode 100644 src/net/messageout.cpp
 create mode 100644 src/net/messageout.hpp
 create mode 100644 src/net/netcomputer.cpp
 create mode 100644 src/net/netcomputer.hpp

(limited to 'src/net')

diff --git a/src/net/connection.cpp b/src/net/connection.cpp
new file mode 100644
index 00000000..8a5c8d0e
--- /dev/null
+++ b/src/net/connection.cpp
@@ -0,0 +1,110 @@
+/*
+ *  The Mana World Server
+ *  Copyright 2006 The Mana World Development Team
+ *
+ *  This file is part of The Mana World.
+ *
+ *  The Mana World 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 2 of the License, or
+ *  any later version.
+ *
+ *  The Mana World 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 The Mana World; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id$
+ */
+
+#include "net/connection.hpp"
+#include "net/messagein.hpp"
+#include "net/messageout.hpp"
+#include "utils/logger.h"
+
+bool Connection::start(std::string const &address, int port)
+{
+    ENetAddress enetAddress;
+    enet_address_set_host(&enetAddress, address.c_str());
+    enetAddress.port = port;
+
+    mLocal = enet_host_create(NULL    /* create a client host */,
+                              1       /* allow one outgoing connection */,
+                              0       /* assume any amount of incoming bandwidth */,
+                              0       /* assume any amount of outgoing bandwidth */);
+
+    if (!mLocal) return false;
+
+    // Initiate the connection, allocating channel 0.
+    mRemote = enet_host_connect(mLocal, &enetAddress, 1);
+
+    ENetEvent event;
+    if (enet_host_service(mLocal, &event, 10000) <= 0 ||
+        event.type != ENET_EVENT_TYPE_CONNECT)
+    {
+        stop();
+        return false;
+    }
+    return mRemote;
+}
+
+void Connection::stop()
+{
+    enet_peer_disconnect(mRemote, 0);
+    enet_host_flush(mLocal);
+    enet_peer_reset(mRemote);
+    enet_host_destroy(mLocal);
+    mRemote = NULL;
+}
+
+bool Connection::isConnected() const
+{
+    return mRemote && mRemote->state == ENET_PEER_STATE_CONNECTED;
+}
+
+void Connection::send(MessageOut const &msg, bool reliable, unsigned channel)
+{
+    ENetPacket *packet;
+    packet = enet_packet_create(msg.getData(),
+                                msg.getLength(),
+                                reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
+
+    if (packet)
+    {
+        enet_peer_send(mRemote, channel, packet);
+    }
+    else
+    {
+        LOG_WARN("Failure to create packet!", 0);
+    }
+}
+
+void Connection::process()
+{
+    ENetEvent event;
+    // Process Enet events and do not block.
+    while (enet_host_service(mLocal, &event, 0) > 0)
+    {
+        switch (event.type)
+        {
+            case ENET_EVENT_TYPE_RECEIVE:
+                if (event.packet->dataLength >= 2) {
+                    MessageIn msg((char *)event.packet->data,
+                                  event.packet->dataLength);
+                    processMessage(msg);
+                } else {
+                    LOG_ERROR("Message too short.", 0);
+                }
+                // Clean up the packet now that we are done using it.
+                enet_packet_destroy(event.packet);
+                break;
+
+            default:
+                break;
+        }
+    }
+}
diff --git a/src/net/connection.hpp b/src/net/connection.hpp
new file mode 100644
index 00000000..0cdb1fc0
--- /dev/null
+++ b/src/net/connection.hpp
@@ -0,0 +1,80 @@
+/*
+ *  The Mana World Server
+ *  Copyright 2006 The Mana World Development Team
+ *
+ *  This file is part of The Mana World.
+ *
+ *  The Mana World 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 2 of the License, or
+ *  any later version.
+ *
+ *  The Mana World 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 The Mana World; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id$
+ */
+
+#ifndef _TMWSERV_CONNECTION_H_
+#define _TMWSERV_CONNECTION_H_
+
+#include <string>
+#include <enet/enet.h>
+
+class MessageIn;
+class MessageOut;
+
+/**
+ * A point-to-point connection to a remote host. The remote host can use a
+ * ConnectionHandler to handle this incoming connection.
+ */
+class Connection
+{
+    public:
+        Connection(): mRemote(NULL) {}
+        virtual ~Connection() {}
+
+        /**
+         * Connects to the given host/port and waits until the connection is
+         * established. Returns false if it fails to connect.
+         */
+        bool start(std::string const &, int);
+
+        /**
+         * Disconnects.
+         */
+        void stop();
+
+        /**
+         * Returns whether the connection is established or not.
+         */
+        bool isConnected() const;
+
+        /**
+         * Sends a message to the remote host.
+         */
+        void send(MessageOut const &msg, bool reliable = true, unsigned channel = 0);
+
+        /**
+         * Dispatches received messages to processMessage.
+         */
+        void process();
+
+    protected:
+        /**
+         * Processes a single message from the remote host.
+         */
+        virtual void processMessage(MessageIn &) = 0;
+
+    private:
+        ENetPeer *mRemote;
+        ENetHost *mLocal;
+};
+
+#endif
diff --git a/src/net/connectionhandler.cpp b/src/net/connectionhandler.cpp
new file mode 100644
index 00000000..7782a74a
--- /dev/null
+++ b/src/net/connectionhandler.cpp
@@ -0,0 +1,166 @@
+/*
+ *  The Mana World Server
+ *  Copyright 2004 The Mana World Development Team
+ *
+ *  This file is part of The Mana World.
+ *
+ *  The Mana World  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 2 of the License, or any later version.
+ *
+ *  The Mana  World 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 The Mana  World; if not, write to the  Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *  $Id$
+ */
+
+#include "defines.h"
+#include "net/connectionhandler.hpp"
+#include "net/messagein.hpp"
+#include "net/messageout.hpp"
+#include "net/netcomputer.hpp"
+#include "utils/logger.h"
+
+#ifdef SCRIPT_SUPPORT
+#include "script.h"
+#endif
+
+
+std::string
+ip4ToString(unsigned int ip4addr)
+{
+    std::stringstream ss;
+    ss << (ip4addr & 0x000000ff) << "."
+       << ((ip4addr & 0x0000ff00) >> 8) << "."
+       << ((ip4addr & 0x00ff0000) >> 16) << "."
+       << ((ip4addr & 0xff000000) >> 24);
+    return ss.str();
+}
+
+bool ConnectionHandler::startListen(enet_uint16 port)
+{
+    // Bind the server to the default localhost.
+    address.host = ENET_HOST_ANY;
+    address.port = port;
+
+    LOG_INFO("Listening on port " << port << "...", 0);
+    host = enet_host_create(&address    /* the address to bind the server host to */,
+                            MAX_CLIENTS /* allow up to MAX_CLIENTS clients and/or outgoing connections */,
+                            0           /* assume any amount of incoming bandwidth */,
+                            0           /* assume any amount of outgoing bandwidth */);
+
+    return host;
+}
+
+void ConnectionHandler::stopListen()
+{
+    // - Disconnect all clients (close sockets)
+
+    // TODO: probably there's a better way.
+    ENetPeer *currentPeer;
+
+    for (currentPeer = host->peers;
+         currentPeer < &host->peers[host->peerCount];
+         ++currentPeer)
+    {
+       if (currentPeer->state == ENET_PEER_STATE_CONNECTED)
+       {
+            enet_peer_disconnect(currentPeer, 0);
+            enet_host_flush(host);
+            enet_peer_reset(currentPeer);
+       }
+    }
+    enet_host_destroy(host);
+    // FIXME: memory leak on NetComputers
+}
+
+void ConnectionHandler::flush()
+{
+    enet_host_flush(host);
+}
+
+void ConnectionHandler::process()
+{
+    ENetEvent event;
+    // Process Enet events and do not block.
+    while (enet_host_service(host, &event, 0) > 0) {
+        switch (event.type) {
+            case ENET_EVENT_TYPE_CONNECT:
+            {
+                LOG_INFO("A new client connected from " <<
+                         ip4ToString(event.peer->address.host) << ":" <<
+                         event.peer->address.port << " to port " <<
+                         host->address.port, 0);
+                NetComputer *comp = computerConnected(event.peer);
+                clients.push_back(comp);
+
+                // Store any relevant client information here.
+                event.peer->data = (void *)comp;
+            } break;
+
+            case ENET_EVENT_TYPE_RECEIVE:
+            {
+                NetComputer *comp = (NetComputer*) event.peer->data;
+
+#ifdef SCRIPT_SUPPORT
+                // This could be good if you wanted to extend the
+                // server protocol using a scripting language. This
+                // could be attained by using allowing scripts to
+                // "hook" certain messages.
+
+                //script->message(buffer);
+#endif
+
+                // If the scripting subsystem didn't hook the message
+                // it will be handled by the default message handler.
+
+                // Make sure that the packet is big enough (> short)
+                if (event.packet->dataLength >= 2) {
+                    MessageIn msg((char *)event.packet->data,
+                                  event.packet->dataLength);
+                    LOG_INFO("Received message " << msg.getId() << " ("
+                            << event.packet->dataLength << " B) from "
+                            << *comp, 2);
+
+                    processMessage(comp, msg);
+                } else {
+                    LOG_ERROR("Message too short from " << *comp, 0);
+                }
+
+                /* Clean up the packet now that we're done using it. */
+                enet_packet_destroy(event.packet);
+            } break;
+
+            case ENET_EVENT_TYPE_DISCONNECT:
+            {
+                NetComputer *comp = (NetComputer *)event.peer->data;
+                LOG_INFO(ip4ToString(event.peer->address.host) << " disconnected.", 0);
+                // Reset the peer's client information.
+                computerDisconnected(comp);
+                clients.erase(std::find(clients.begin(), clients.end(), comp));
+                event.peer->data = NULL;
+            } break;
+
+            default: break;
+        }
+    }
+}
+
+void ConnectionHandler::sendToEveryone(const MessageOut &msg)
+{
+    for (NetComputers::iterator i = clients.begin(), i_end = clients.end();
+         i != i_end; ++i) {
+        (*i)->send(msg);
+    }
+}
+
+unsigned int ConnectionHandler::getClientNumber()
+{
+    return clients.size();
+}
diff --git a/src/net/connectionhandler.hpp b/src/net/connectionhandler.hpp
new file mode 100644
index 00000000..df22aacc
--- /dev/null
+++ b/src/net/connectionhandler.hpp
@@ -0,0 +1,123 @@
+/*
+ *  The Mana World Server
+ *  Copyright 2004 The Mana World Development Team
+ *
+ *  This file is part of The Mana World.
+ *
+ *  The Mana World 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 2 of the License, or
+ *  any later version.
+ *
+ *  The Mana World 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 The Mana World; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id$
+ */
+
+#ifndef _TMWSERV_CONNECTIONHANDLER_H_
+#define _TMWSERV_CONNECTIONHANDLER_H_
+
+#include <list>
+#include <enet/enet.h>
+
+class MessageIn;
+class MessageOut;
+class NetComputer;
+
+/**
+ * Convert a IP4 address into its string representation
+ */
+std::string
+ip4ToString(unsigned int ip4addr);
+
+/**
+ * This class represents the connection handler interface. The connection
+ * handler will respond to connect/reconnect/disconnect events and handle
+ * incoming messages, passing them on to registered message handlers.
+ */
+class ConnectionHandler
+{
+    public:
+        virtual ~ConnectionHandler() {}
+
+        /**
+         * Open the server socket.
+         */
+        bool startListen(enet_uint16 port);
+
+        /**
+         * Disconnect all the clients and close the server socket.
+         */
+        void stopListen();
+
+        /**
+         * Process outgoing messages and listen to the server socket for
+         * incoming messages and new connections.
+         */
+        virtual void process();
+
+        /**
+         * Process outgoing messages.
+         */
+        void flush();
+
+        /**
+         * Called when a computer sends a packet to the network session.
+         */
+        //void receivePacket(NetComputer *computer, Packet *packet);
+
+        /**
+         * Send packet to every client, used for announcements.
+         */
+        void sendToEveryone(const MessageOut &msg);
+
+        /**
+         * Return the number of connected clients.
+         */
+        unsigned int getClientNumber();
+
+    private:
+        ENetAddress address;      /**< Includes the port to listen to. */
+        ENetHost *host;           /**< The host that listen for connections. */
+
+    protected:
+        /**
+         * Called when a computer connects to the server. Initialize
+         * an object derived of NetComputer.
+         */
+        virtual NetComputer *computerConnected(ENetPeer *peer) = 0;
+
+        /**
+         * Called when a computer reconnects to the server.
+         */
+        //virtual NetComputer *computerReconnected(ENetPeer *) = 0;
+
+        /**
+         * Called when a computer disconnects from the server.
+         *
+         * <b>Note:</b> After returning from this method the NetComputer
+         *              reference is no longer guaranteed to be valid.
+         */
+        virtual void computerDisconnected(NetComputer *) = 0;
+
+        /**
+         * Called when a message is received.
+         */
+        virtual void processMessage(NetComputer *, MessageIn &) = 0;
+
+        typedef std::list<NetComputer*> NetComputers;
+        /**
+         * A list of pointers to the client structures created by
+         * computerConnected.
+         */
+        NetComputers clients;
+};
+
+#endif
diff --git a/src/net/messagein.cpp b/src/net/messagein.cpp
new file mode 100644
index 00000000..8da02550
--- /dev/null
+++ b/src/net/messagein.cpp
@@ -0,0 +1,100 @@
+/*
+ *  The Mana World Server
+ *  Copyright 2004 The Mana World Development Team
+ *
+ *  This file is part of The Mana World.
+ *
+ *  The Mana World 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 2 of the License, or
+ *  any later version.
+ *
+ *  The Mana World 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 The Mana World; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id$
+ */
+
+#include <string>
+#include <enet/enet.h>
+
+#include "net/messagein.hpp"
+
+MessageIn::MessageIn(const char *data, int length):
+    mData(data),
+    mLength(length),
+    mPos(0)
+{
+    // Read the message ID
+    mId = readShort();
+}
+
+MessageIn::~MessageIn()
+{
+}
+
+char MessageIn::readByte()
+{
+    char value = -1;
+    if (mPos < mLength)
+    {
+        value = mData[mPos];
+    }
+    mPos += 1;
+    return value;
+}
+
+short MessageIn::readShort()
+{
+    short value = -1;
+    if (mPos + 2 <= mLength)
+    {
+        uint16_t t;
+        memcpy(&t, mData + mPos, 2);
+        value = ENET_NET_TO_HOST_16(t);
+    }
+    mPos += 2;
+    return value;
+}
+
+long MessageIn::readLong()
+{
+    long value = -1;
+    if (mPos + 4 <= mLength)
+    {
+        uint32_t t;
+        memcpy(&t, mData + mPos, 4);
+        value = ENET_NET_TO_HOST_32(t);
+    }
+    mPos += 4;
+    return value;
+}
+
+std::string MessageIn::readString(int length)
+{
+    // Get string length
+    if (length < 0) {
+        length = readShort();
+    }
+
+    // Make sure the string isn't erroneous
+    if (length < 0 || mPos + length > mLength) {
+        mPos = mLength + 1;
+        return "";
+    }
+
+    // Read the string
+    char const *stringBeg = mData + mPos;
+    char const *stringEnd = (char const *)memchr(stringBeg, '\0', length);
+    std::string readString(stringBeg,
+            stringEnd ? stringEnd - stringBeg : length);
+    mPos += length;
+
+    return readString;
+}
diff --git a/src/net/messagein.hpp b/src/net/messagein.hpp
new file mode 100644
index 00000000..c582d056
--- /dev/null
+++ b/src/net/messagein.hpp
@@ -0,0 +1,77 @@
+/*
+ *  The Mana World Server
+ *  Copyright 2004 The Mana World Development Team
+ *
+ *  This file is part of The Mana World.
+ *
+ *  The Mana World 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 2 of the License, or
+ *  any later version.
+ *
+ *  The Mana World 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 The Mana World; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id$
+ */
+
+#ifndef _TMWSERV_MESSAGEIN_H_
+#define _TMWSERV_MESSAGEIN_H_
+
+#include <iosfwd>
+
+class Packet;
+
+/**
+ * Used for parsing an incoming message.
+ */
+class MessageIn
+{
+    public:
+        /**
+         * Constructor.
+         */
+        MessageIn(const char *data, int length);
+
+        /**
+         * Destructor.
+         */
+        ~MessageIn();
+
+        short getId() { return mId; } /**< Returns the message ID. */
+
+        char readByte();              /**< Reads a byte. */
+        short readShort();            /**< Reads a short. */
+        long readLong();              /**< Reads a long. */
+
+        /**
+         * Reads a string. If a length is not given (-1), it is assumed
+         * that the length of the string is stored in a short at the
+         * start of the string.
+         */
+        std::string readString(int length = -1);
+
+        /**
+         * Returns the length of unread data.
+         */
+        unsigned getUnreadLength() { return mLength - mPos; }
+
+    private:
+        const char *mData;            /**< Packet data */
+        unsigned int mLength;         /**< Length of data in bytes */
+        short mId;                    /**< The message ID. */
+
+        /**
+         * Actual position in the packet. From 0 to packet->length. A value
+         * bigger than packet->length means EOP was reached when reading it.
+         */
+        unsigned int mPos;
+};
+
+#endif
diff --git a/src/net/messageout.cpp b/src/net/messageout.cpp
new file mode 100644
index 00000000..34655e4c
--- /dev/null
+++ b/src/net/messageout.cpp
@@ -0,0 +1,135 @@
+/*
+ *  The Mana World Server
+ *  Copyright 2004 The Mana World Development Team
+ *
+ *  This file is part of The Mana World.
+ *
+ *  The Mana World 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 2 of the License, or
+ *  any later version.
+ *
+ *  The Mana World 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 The Mana World; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id$
+ */
+
+#include <cstdlib>
+#include <iosfwd>
+#include <string>
+#include <enet/enet.h>
+
+#include "net/messageout.hpp"
+
+/** Initial amount of bytes allocated for the messageout data buffer. */
+const unsigned int INITIAL_DATA_CAPACITY = 16;
+
+/** Factor by which the messageout data buffer is increased when too small. */
+const unsigned int CAPACITY_GROW_FACTOR = 2;
+
+MessageOut::MessageOut():
+    mPos(0)
+{
+    mData = (char*) malloc(INITIAL_DATA_CAPACITY);
+    mDataSize = INITIAL_DATA_CAPACITY;
+}
+
+MessageOut::MessageOut(short id):
+    mPos(0)
+{
+    mData = (char*) malloc(INITIAL_DATA_CAPACITY);
+    mDataSize = INITIAL_DATA_CAPACITY;
+
+    writeShort(id);
+}
+
+MessageOut::~MessageOut()
+{
+    free(mData);
+}
+
+void
+MessageOut::expand(size_t bytes)
+{
+    if (bytes > mDataSize)
+    {
+        do
+        {
+            mDataSize *= CAPACITY_GROW_FACTOR;
+        }
+        while (bytes > mDataSize);
+
+        mData = (char*) realloc(mData, mDataSize);
+    }
+}
+
+void
+MessageOut::writeByte(char value)
+{
+    expand(mPos + 1);
+    mData[mPos] = value;
+    mPos += 1;
+}
+
+void
+MessageOut::writeShort(short value)
+{
+    expand(mPos + 2);
+    uint16_t t = ENET_HOST_TO_NET_16(value);
+    memcpy(mData + mPos, &t, 2);
+    mPos += 2;
+}
+
+void
+MessageOut::writeLong(long value)
+{
+    expand(mPos + 4);
+    uint32_t t = ENET_HOST_TO_NET_32(value);
+    memcpy(mData + mPos, &t, 4);
+    mPos += 4;
+}
+
+void MessageOut::writeCoordinates(int x, int y)
+{
+    expand(mPos + 3);
+    char *p = mData + mPos;
+    p[0] = x & 0x00FF;
+    p[1] = ((x & 0x0700) >> 8) | ((y & 0x001F) << 3);
+    p[2] = (y & 0x07E0) >> 5;
+    mPos += 3;
+}
+
+void
+MessageOut::writeString(const std::string &string, int length)
+{
+    int stringLength = string.length();
+    if (length < 0)
+    {
+        // Write the length at the start if not fixed
+        writeShort(stringLength);
+        length = stringLength;
+    }
+    else if (length < stringLength)
+    {
+        // Make sure the length of the string is no longer than specified
+        stringLength = length;
+    }
+    expand(mPos + length);
+
+    // Write the actual string
+    memcpy(mData + mPos, string.c_str(), stringLength);
+
+    if (length > stringLength)
+    {
+        // Pad remaining space with zeros
+        memset(mData + mPos + stringLength, '\0', length - stringLength);
+    }
+    mPos += length;
+}
diff --git a/src/net/messageout.hpp b/src/net/messageout.hpp
new file mode 100644
index 00000000..8ec6b171
--- /dev/null
+++ b/src/net/messageout.hpp
@@ -0,0 +1,96 @@
+/*
+ *  The Mana World Server
+ *  Copyright 2004 The Mana World Development Team
+ *
+ *  This file is part of The Mana World.
+ *
+ *  The Mana World 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 2 of the License, or
+ *  any later version.
+ *
+ *  The Mana World 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 The Mana World; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id$
+ */
+
+#ifndef _TMWSERV_MESSAGEOUT_H_
+#define _TMWSERV_MESSAGEOUT_H_
+
+#include <iosfwd>
+
+/**
+ * Used for building an outgoing message.
+ */
+class MessageOut
+{
+    public:
+        /**
+         * Constructor.
+         */
+        MessageOut();
+
+        /**
+         * Constructor that takes a message ID.
+         */
+        MessageOut(short id);
+
+        /**
+         * Destructor.
+         */
+        ~MessageOut();
+
+        void
+        writeByte(char value);          /**< Writes a byte. */
+
+        void
+        writeShort(short value);        /**< Writes a short. */
+
+        void
+        writeLong(long value);          /**< Writes a long. */
+
+        /**
+         * Writes a 3-byte block containing tile-based coordinates.
+         */
+        void writeCoordinates(int x, int y);
+
+        /**
+         * Writes a string. If a fixed length is not given (-1), it is stored
+         * as a short at the start of the string.
+         */
+        void
+        writeString(const std::string &string, int length = -1);
+
+        /**
+         * Returns the content of the message.
+         */
+        char*
+        getData() const { return mData; }
+
+        /**
+         * Returns the length of the data.
+         */
+        unsigned int
+        getLength() const { return mPos; }
+
+    private:
+        /**
+         * Ensures the capacity of the data buffer is large enough to hold the
+         * given amount of bytes.
+         */
+        void
+        expand(size_t size);
+
+        char *mData;                         /**< Data building up. */
+        unsigned int mPos;                   /**< Position in the data. */
+        unsigned int mDataSize;              /**< Allocated datasize. */
+};
+
+#endif //_TMWSERV_MESSAGEOUT_H_
diff --git a/src/net/netcomputer.cpp b/src/net/netcomputer.cpp
new file mode 100644
index 00000000..f963db55
--- /dev/null
+++ b/src/net/netcomputer.cpp
@@ -0,0 +1,94 @@
+/*
+ *  The Mana World Server
+ *  Copyright 2004 The Mana World Development Team
+ *
+ *  This file is part of The Mana World.
+ *
+ *  The Mana World 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 2 of the License, or
+ *  any later version.
+ *
+ *  The Mana World 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 The Mana World; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id$
+ */
+
+#include <iosfwd>
+#include <queue>
+#include <enet/enet.h>
+
+#include "defines.h"
+#include "net/messageout.hpp"
+#include "net/netcomputer.hpp"
+#include "utils/logger.h"
+
+NetComputer::NetComputer(ENetPeer *peer):
+    mPeer(peer)
+{
+}
+
+bool
+NetComputer::isConnected()
+{
+    return (mPeer->state == ENET_PEER_STATE_CONNECTED);
+}
+
+void
+NetComputer::disconnect(const MessageOut &msg)
+{
+    if (isConnected())
+    {
+        /* ChannelID 0xFF is the channel used by enet_peer_disconnect.
+         * If a reliable packet is send over this channel ENet guaranties
+         * that the message is recieved before the disconnect request.
+         */
+        send(msg, ENET_PACKET_FLAG_RELIABLE, 0xFF);
+
+        /* ENet generates a disconnect event
+         * (notifying the connection handler).
+         */
+        enet_peer_disconnect(mPeer, 0);
+    }
+}
+
+void
+NetComputer::send(const MessageOut &msg, bool reliable,
+                  unsigned int channel)
+{
+    LOG_INFO("Sending packet of length " << msg.getLength() << " to "
+             << *this, 2);
+
+    ENetPacket *packet;
+    packet = enet_packet_create(msg.getData(),
+                                msg.getLength(),
+                                reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
+
+    if (packet)
+    {
+        enet_peer_send(mPeer, channel, packet);
+    }
+    else
+    {
+        LOG_WARN("Failure to create packet!", 0);
+    }
+}
+
+std::ostream&
+operator <<(std::ostream &os, const NetComputer &comp)
+{
+    // address.host contains the ip-address in network-byte-order
+
+    os << ((comp.mPeer->address.host & 0xff000000) >> 24) << "."
+       << ((comp.mPeer->address.host & 0x00ff0000) >> 16) << "."
+       << ((comp.mPeer->address.host & 0x0000ff00) >> 8) << "."
+       << ((comp.mPeer->address.host & 0x000000ff) >> 0);
+    return os;
+}
diff --git a/src/net/netcomputer.hpp b/src/net/netcomputer.hpp
new file mode 100644
index 00000000..0b8dab3d
--- /dev/null
+++ b/src/net/netcomputer.hpp
@@ -0,0 +1,97 @@
+/*
+ *  The Mana World Server
+ *  Copyright 2004 The Mana World Development Team
+ *
+ *  This file is part of The Mana World.
+ *
+ *  The Mana World 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 2 of the License, or
+ *  any later version.
+ *
+ *  The Mana World 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 The Mana World; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id$
+ */
+
+#ifndef _TMWSERV_NETCOMPUTER_H_
+#define _TMWSERV_NETCOMPUTER_H_
+
+#include <iostream>
+#include <enet/enet.h>
+
+class MessageOut;
+
+/**
+ * This class represents a known computer on the network. For example a
+ * connected client or a server we're connected to.
+ */
+class NetComputer
+{
+    public:
+        /**
+         * Constructor.
+         */
+        NetComputer(ENetPeer *peer);
+
+        /**
+         * Destructor.
+         */
+        virtual ~NetComputer() {}
+
+        /**
+         * Returns <code>true</code> if this computer is connected.
+         */
+        bool
+        isConnected();
+
+        /**
+         * Disconnects the computer from the server, after sending a message.
+         *
+         * The caller of this method should prepare the message, because
+         * NetComputer does not know which handler is sending it
+         * (could have been chat/game/account)
+         */
+        void
+        disconnect(const MessageOut &msg);
+
+        /**
+         * Queues a message for sending to a client.
+         *
+         * Reliable packets always arrive, if the client stays connected.
+         * Unreliable packets may not arrive, and may not even be sent.
+         *
+         * Channels are used to ensure that unrelated reliable packets do not
+         * hold each other up. In essence, each channel represents a different
+         * queue.
+         *
+         * @param msg      The message to be sent.
+         * @param reliable Defines if a reliable or an unreliable packet
+         *                 should be sent.
+         * @param channel  The channel number of which the packet should
+         *                 be sent.
+         */
+        void
+        send(const MessageOut &msg, bool reliable = true,
+             unsigned int channel = 0);
+
+    private:
+        ENetPeer *mPeer;              /**< Client peer */
+
+        /**
+         * Converts the ip-address of the peer to a stringstream.
+         * Example:
+         * <code> std::cout << comp </code>
+         */
+        friend std::ostream& operator <<(std::ostream &os,
+                                         const NetComputer &comp);
+};
+
+#endif // _TMWSERV_NETCOMPUTER_H_
-- 
cgit v1.2.3-70-g09d2