Main Page | Modules | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | Related Pages | Examples

9. Presending chains (multiple pack/unpack)

In this test we will create a chain of plugins on the server, and on the client the nested wrapping should be unpacked correctly.

Aggregation Plugin

The first plugin is the same as in the previous excample, it aggregates packets into one large packet.

/***************************************************************************
 * The contents of this file are subject to the Mozilla Public             *
 * License Version 1.1 (the "License"); you may not use this file          *
 * except in compliance with the License. You may obtain a copy of         *
 * the License at http://www.mozilla.org/MPL/                              *
 *                                                                         *
 * Software distributed under the License is distributed on an "AS         *
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or              *
 * implied. See the License for the specific language governing            *
 * rights and limitations under the License.                               *
 *                                                                         *
 * The Original Code is Game Network Framework (GaNeF).                    *
 *                                                                         *
 * The Initial Developers of the Original Code are                         *
 * Lars Langer and Emanuel Greisen                                         *
 * Copyright (C) 2005. Lars Langer & Emanuel Greisen                       *
 * All Rights Reserved.                                                    *
 *                                                                         *
 * Contributor(s):                                                         *
 *   none yet....                                                          *
 *                                                                         *
 ***************************************************************************/
#ifndef AGGR_PLUGIN_H
#define AGGR_PLUGIN_H

#include <map>

#include "../Ganef/common/plugin.h"

template<class PacketType>
class AggregationPlugin : public Plugin<PacketType>
{
   private:
      std::map<unsigned int, PacketType *> aggr_packs;
      typedef typename std::map<unsigned int, PacketType *>::iterator MapIterator;

   public:
      /// This method must return a unique number identifying the plugin.
      virtual const unsigned int getUniqueId() const
      {
         return 0x12345;
      }
      /// Will be called when ever a packet must be packed.
      virtual void pack(PacketType * packet)
      {
         //std::cout << "AggregationPlugin::pack(" << packet << ")\n";
         if(aggr_packs.find(packet->getUserID()) == aggr_packs.end())
         {
            std::cout << "Creating new Aggregation packet for client: " << packet->getUserID() << std::endl;
            aggr_packs[packet->getUserID()] = PacketType::createPluginPacket(this, packet);
         }

         // Add a new packet
         aggr_packs[packet->getUserID()]->writeUInt32(packet->getSize());
         aggr_packs[packet->getUserID()]->writePacket(packet);
         delete packet;
      }
      /// Will be called when ever a packet must be unpacked.
      virtual void unpack(PacketType * packet)
      {
         std::cout << "AggregationPlugin::unpack(" << packet << ")\n";
         unsigned int psize;
         while((psize = packet->readUInt32()) != 0)
         {
            //std::cout << "There is a packet of size:" << psize << std::endl;
            PacketType * p = packet->readPacket(psize);
            forwardUnpack(p);
         }

         // This is of no more use
         delete packet;
      }

      /// Will be called when ever we must purge our packets
      virtual void onPurge()
      {
         MapIterator it;
         for(it = aggr_packs.begin(); it != aggr_packs.end(); ++it)
         {
            PacketType * pack = it->second;
            pack->writeUInt32(0); // This ends the packet
            std::cout << "Purging a packet of size: " << pack->getSize() << std::endl;
            forwardPack(pack);
         }
         // Clean up our map
         aggr_packs.clear();
      }
};

#endif

ZLIB compression Plugin

The second plugin is a compression plugin using Zlib. This plugin does not need the onPurge(...) method, because it just forwards the compressed data-packet. The combination of these two plugins is smart, since packets are first collected into one large packet and then compressed to minimize bandwidth usage.

/***************************************************************************
 * The contents of this file are subject to the Mozilla Public             *
 * License Version 1.1 (the "License"); you may not use this file          *
 * except in compliance with the License. You may obtain a copy of         *
 * the License at http://www.mozilla.org/MPL/                              *
 *                                                                         *
 * Software distributed under the License is distributed on an "AS         *
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or              *
 * implied. See the License for the specific language governing            *
 * rights and limitations under the License.                               *
 *                                                                         *
 * The Original Code is Game Network Framework (GaNeF).                    *
 *                                                                         *
 * The Initial Developers of the Original Code are                         *
 * Lars Langer and Emanuel Greisen                                         *
 * Copyright (C) 2005. Lars Langer & Emanuel Greisen                       *
 * All Rights Reserved.                                                    *
 *                                                                         *
 * Contributor(s):                                                         *
 *   none yet....                                                          *
 *                                                                         *
 ***************************************************************************/
#ifndef ZLIB_PLUGIN_H
#define ZLIB_PLUGIN_H

#include <zlib.h>

#include "../Ganef/common/plugin.h"

template<class PacketType>
class ZlibPlugin : public Plugin<PacketType>
{
   public:
      /// This method must return a unique number identifying the plugin.
      virtual const unsigned int getUniqueId() const
      {
         return 0x711b;
      }
      /// Will be called when ever a packet must be packed.
      virtual void pack(PacketType * packet)
      {
         // First make the packet ready for transmission
         packet->makeSendReady();

         unsigned long have;
         // According to ZLIB the outbuffer must be 0.1% bigger, plus 12 bytes.
         // We just make sure nothing happen an take 10% + 12 bytes :)
         unsigned int outbuffsize = (packet->getSize() * 110) / 100 + 12;
         unsigned char out[outbuffsize];

         int ret = compress(out, &have, (unsigned char *)packet->getData(), packet->getSize());

         PacketType * compressedpacket = PacketType::createPluginPacket(this, packet);
         compressedpacket->writeUInt16(static_cast<unsigned short>(packet->getSize()));
         compressedpacket->writeData(out, have);

         delete packet;

         std::cout << "ZLIB::pack(" << packet << ") ["<<packet->getSize()<<"->"<<have<<"]\n";

         // And forward the packet
         forwardPack(compressedpacket);
      }
      /// Will be called when ever a packet must be unpacked.
      virtual void unpack(PacketType * packet)
      {
         unsigned long insize =  packet->getDataSize() - sizeof(unsigned short);
         unsigned long outsize = static_cast<unsigned long>(packet->readUInt16());
         std::cout << "ZLIB::unpack(" << packet << ") ["<<insize<<"->"<<outsize<<"]\n";
         unsigned char in[insize];
         packet->readData(in, insize);
         unsigned char dest[outsize];

         int ret = uncompress(dest, &outsize, in, insize);
         std::cout << "Actual size: " << outsize << ",ret:" << ret << std::endl;
         // OAK: create the packet
         PacketType * uncompressedpacket = new PacketType((char *)dest, outsize);

         // This is of no more use
         delete packet;

         // Finally forward the packet
         forwardUnpack(uncompressedpacket);
      }
};

#endif

Client Application

The client application is the same as the example before, except this one registeres both plugins.

/// @cond EXCLUDEDTESTSOURCES
/***************************************************************************
 * The contents of this file are subject to the Mozilla Public             *
 * License Version 1.1 (the "License"); you may not use this file          *
 * except in compliance with the License. You may obtain a copy of         *
 * the License at http://www.mozilla.org/MPL/                              *
 *                                                                         *
 * Software distributed under the License is distributed on an "AS         *
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or              *
 * implied. See the License for the specific language governing            *
 * rights and limitations under the License.                               *
 *                                                                         *
 * The Original Code is Game Network Framework (GaNeF).                    *
 *                                                                         *
 * The Initial Developers of the Original Code are                         *
 * Lars Langer and Emanuel Greisen                                         *
 * Copyright (C) 2005. Lars Langer & Emanuel Greisen                       *
 * All Rights Reserved.                                                    *
 *                                                                         *
 * Contributor(s):                                                         *
 *   none yet....                                                          *
 *                                                                         *
 ***************************************************************************/

#include <iostream>
#include <cstdlib>

#include "../Ganef/client/clientframework.h"
#include "../Ganef/client/clientdata.h"

#include "aggr_plugin.h"
#include "zlib_plugin.h"

class MyClientData : public ClientData
{
   private:
      int x,y;

   public:
      MyClientData(ClientFramework * f, ClientPacket * p) : ClientData(f,p),x(0),y(0)
      {
         x = p->readInt32();
         y = p->readInt32();
         std::cout << "Constructed a new object of class: MyClientData(";
         std::cout << "ID:" << getFrameworkId() << ",";
         std::cout << "x:"<<x<<",y:"<<y<<")" << std::endl;
      }
      virtual void updatePacket(ClientFramework * f, unsigned char updatetype, ClientPacket * packet)
      {
         // Here we receive updates (but no updates are comming in this test)
      }

      static ClientData * constructMyClientData(ClientFramework * f, ClientPacket * p)
      {
         return new MyClientData(f,p);
      }
};


/**
 * This is just a test-client-framework.
 * @author Lars Langer and Emanuel Greisen
 */
class TestClientFramework : public ClientFramework
{
   private:
      bool is_running;
      std::string server_hostname;

   public:
      TestClientFramework(const std::string & server_hostname) : ClientFramework(int(0)),server_hostname(server_hostname)
      {
         // Notice how the constructor is registered, now all we need is the
         // server sending a packet telling us to construct an object of
         // class with id: 0x424242
         registerConstructor(0x424242, MyClientData::constructMyClientData);
      }
      ~TestClientFramework()
      {
      };

   protected: // These must be implemented in a derived class
      virtual void writeHandshakeInitMessage( ClientPacket * fp ){}
      virtual void onHandshakingDiscontinue( ClientPacket * fp ){}
      virtual bool onHandshakeDescription( ClientPacket * fp, ClientPacket * reply )
      {
         return true;
      }
      virtual void onDisconnect( ) { std::cout << "Disconnected\n"; }
      virtual void onConnect( ){ std::cout << "Connected\n"; }

   public:
      void mainLoop( )
      {
         // First we register the plugin for use when decoding packets.
         //  You might note that the plugin is a template class and that
         //  we use different packet-types on the server and client
         //  This is no big issue, because they are almost the same (they
         //  both implement the same methods used in the plugin).
         registerPlugin(new AggregationPlugin<ClientPacket>());
         registerPlugin(new ZlibPlugin<ClientPacket>());

         // Then we connect to the server
         std::cout << "Connecting to: " << server_hostname <<":"<< 8000<< std::endl;
         initServerConnection(server_hostname, 8000);
         is_running = true;
         while(is_running)
         {
            // Get/handle Packets from the framework
            keepAlive();

            // Then we sleep to let the CPU live
            psleep(50);
         }
      }
};




int main(int argc, char *argv[])
{
   std::string server_host("localhost");
   if(argc > 1)
      server_host = argv[1];
   TestClientFramework app(server_host);

   // Start the Application
   app.mainLoop();
}

/// @endcond

Server Application

The server in this test is almost the same as in the previous test, except the presending chain in this application contains two plugins. We also create a little more objects to better see the impact of the compression.

/// @cond EXCLUDEDTESTSOURCES
/***************************************************************************
 * The contents of this file are subject to the Mozilla Public             *
 * License Version 1.1 (the "License"); you may not use this file          *
 * except in compliance with the License. You may obtain a copy of         *
 * the License at http://www.mozilla.org/MPL/                              *
 *                                                                         *
 * Software distributed under the License is distributed on an "AS         *
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or              *
 * implied. See the License for the specific language governing            *
 * rights and limitations under the License.                               *
 *                                                                         *
 * The Original Code is Game Network Framework (GaNeF).                    *
 *                                                                         *
 * The Initial Developers of the Original Code are                         *
 * Lars Langer and Emanuel Greisen                                         *
 * Copyright (C) 2005. Lars Langer & Emanuel Greisen                       *
 * All Rights Reserved.                                                    *
 *                                                                         *
 * Contributor(s):                                                         *
 *   none yet....                                                          *
 *                                                                         *
 ***************************************************************************/

#include <iostream>
#include <cstdlib>
#include <vector>

#include "../Ganef/server/serverframework.h"

#include "aggr_plugin.h"
#include "zlib_plugin.h"


class MyServerData : public ServerData
{
   private:
      int x,y;

   public:
      // The two last parameters for ServerData is the priority of its updatepackets
      //  and wether they should be sent reliable.
      MyServerData(ServerFramework * fw) : ServerData(fw, 10, false)
      {
         x = 200;
         y = 150;
      };
   public:
      /// This must produce a unique id for this class, it will be used on the client side to construct new objects.
      virtual const unsigned int getClassId() const { return 0x424242; };
      /// Here the data-class should fill in data according to update type `type`.
      virtual void fillUpdatePacket(ServerPacket * packet, unsigned char type) const
      {
         // We do not use updates in this test.
         switch(type)
         {
            case 5:
               packet->writeInt32(x);
               packet->writeInt32(y);
               break;
         }
      }
      /// Here the data-class should fill in all data required by the constructor on the client side.
      virtual void fillCreateObjectPacket(ServerPacket * packet) const
      {
         // Here we supply enough data to create the client-version of this object
         // Remember to make absolutely sure that what you write here is exactly the
         //   same as the client version reads in its constructor.
         packet->writeInt32(x);
         packet->writeInt32(y);
      }
      /// Called when ever we receive a packet for this object from a client.
      virtual void clientPacket(Client * client, unsigned char type, ServerPacket * packet)
      {
         // We do not receive any actions from the client in this test.
      }

};

/**
 * This is just a test server-framework;
 * @author Lars Langer and Emanuel Greisen
 */
class TestServerFramework : public ServerFramework
{
   public:
      TestServerFramework() : ServerFramework(8000)
      {
      }

      ~TestServerFramework()
      {
      }

   protected: // These method should be implemented on the GameServer
      virtual bool onInitialHandshake(ServerPacket * handshake, ServerPacket * reply)
      {
         return true; // Accept all clients.
      }
      virtual void onClientConnected(Client * client)
      {
         std::cout << "Client: " << client->getId() << " connected\n";

         // Here we send the construction packages for all Framework Data
         // But first we change the "chain" of plugins to use our registered one.
         setPreSendingChain("MyPluginChain");
         std::map<unsigned int, ServerData *>::const_iterator it;
         for(it = getServerDataObjects().begin(); it != getServerDataObjects().end(); ++it)
         {
            ServerData * s_data = it->second;
            s_data->sendCreateObject(client);
         }
         // Since our plugin aggregates the packets we need to purge it to make
         // sure everything is sent.
         purgeCurrentPreSendingChain();

         // Once we have sent all our constructor packets to the client we revert to
         // the default send-chain (the empty send chain)
         setDefaultPreSendingChain();
      }
      virtual void onClientDisconnected(Client * client)
      {
         std::cout << "Client: " << client->getId() << " disconnected\n";
      }
      virtual Client * createClient(const ipaddress &addr, int port)
      {
         return new Client(this, addr, port);
      }

   public:
      void mainLoop()
      {
         // First we create a vector of plugins (this one only containing one)
         std::vector< Plugin<ServerPacket> * > chainlist;
         chainlist.push_back(new AggregationPlugin<ServerPacket>());
         chainlist.push_back(new ZlibPlugin<ServerPacket>());
         // Then we register this "chain" under a name
         registerPreSendingChain("MyPluginChain", chainlist);

         std::cout << "TestServerFramework: Running\n";

         // Construct a lot of data
         std::vector<MyServerData *> mydata;
         for(int i = 0; i < 25; i++)
            mydata.push_back(new MyServerData(this));

         int counter = 0;
         while(true)
         {
            // Get all incomming messages
            keepAlive();

            if(counter++ % 100 == 0)
            {
               setPreSendingChain("MyPluginChain");
               std::vector< MyServerData *>::const_iterator it;
               for(it = mydata.begin(); it != mydata.end(); ++it)
               {
                  MyServerData * s_data = *it;
                  s_data->sendUpdateToAll(5, false);
               }
               purgeCurrentPreSendingChain();
               setDefaultPreSendingChain();
            }

            // Sleep a little
            psleep(100);
         }
      }
};




int main(int argc, char *argv[])
{
   try
   {
      // Create a framework
      TestServerFramework framework;

      // Start the framework
      framework.start();

      // GameLoop
      framework.mainLoop();
   }
   catch(FrameworkError *err)
   {
      std::cout << "FrameworkError:" << err->msg << std::endl;
   }
   catch(PacketError *err)
   {
      std::cout << "PacketError:" << err->msg << std::endl;
   }

   return EXIT_SUCCESS;
}

/// @endcond

Generated on Mon Feb 6 12:24:58 2006 for Ganef by  doxygen 1.4.4