summaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
authorGuillaume Melquiond <guillaume.melquiond@gmail.com>2006-12-29 13:43:24 +0000
committerGuillaume Melquiond <guillaume.melquiond@gmail.com>2006-12-29 13:43:24 +0000
commit291ad04d5b5c4ab08d85eadde116f968cd579b77 (patch)
treee4dced5715a5d9792cfdc0455a6b3ee6d3116079 /src/net
parent3d404e743105bb9168c89e3451cf35d7d59120b1 (diff)
downloadmanaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.tar.gz
manaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.tar.bz2
manaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.tar.xz
manaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.zip
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.
Diffstat (limited to 'src/net')
-rw-r--r--src/net/connection.cpp110
-rw-r--r--src/net/connection.hpp80
-rw-r--r--src/net/connectionhandler.cpp166
-rw-r--r--src/net/connectionhandler.hpp123
-rw-r--r--src/net/messagein.cpp100
-rw-r--r--src/net/messagein.hpp77
-rw-r--r--src/net/messageout.cpp135
-rw-r--r--src/net/messageout.hpp96
-rw-r--r--src/net/netcomputer.cpp94
-rw-r--r--src/net/netcomputer.hpp97
10 files changed, 1078 insertions, 0 deletions
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_