// ----------------------------------------------------------------------
// Filename Compression.cpp
// Created by Rakkar Software (rakkar@rakkarsoftware.com) September 25, 2003
// Demonstrates how to use compression with the standard chat example.
// The first time you run this you won't have frequency tables, so you must run it twice.
// The first time to generate a table, the second time to use the last table generated
// ----------------------------------------------------------------------
#include "PacketEnumerations.h"
#include "RakNetworkFactory.h"
#include "RakClientInterface.h"
#include "RakServerInterface.h"
#include "Multiplayer.h"
#include "NetworkTypes.h"
#include "BitStream.h"
#include <assert.h>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#ifdef WIN32
#include <conio.h>
#else
#include "../Unix/kbhit.h"
#endif
// Holds enumeration data
const int MOTD_MAXIMUM_LENGTH=50; // Characters allocated for the message of the day, used for enumerations
const int SERVER_NAME_MAXIMUM_LENGTH=40; // Characters allocated for the server name
struct EnumerationDataStruct
{
char MOTD[MOTD_MAXIMUM_LENGTH]; // (Message of the day) Optional, replace with whatever and/or add more fields
char serverName[SERVER_NAME_MAXIMUM_LENGTH];
};
const int SCDS_MAXIMUM_NAME_LENGTH=40;
struct StaticClientDataStruct
{
unsigned char typeId; // ID_SET_CLIENT_DATA
char name[SCDS_MAXIMUM_NAME_LENGTH];
};
template <class InterfaceType>
class ClientMultiplayer : public Multiplayer<InterfaceType>
{
void ProcessUnhandledPacket(Packet *packet, unsigned char packetIdentifier, InterfaceType *interfaceType)
{
// It's a client, so just show the message
printf("%s\n", packet->data);
}
};
template <class InterfaceType>
class ServerMultiplayer : public Multiplayer<InterfaceType>
{
void ProcessUnhandledPacket(Packet *packet, unsigned char packetIdentifier, InterfaceType *interfaceType)
{
char message[200];
// The server knows the static data of all clients, so we can prefix the message
// With the name data
printf("%s: %s\n", ((StaticClientDataStruct*)(interfaceType->GetStaticClientData(packet->playerId)->GetData()))->name, packet->data);
// Relay the message. We prefix the name for other clients. This demonstrates
// That messages can be changed on the server before being broadcast
// Sending is the same as before
sprintf(message, "%s: %s", ((StaticClientDataStruct*)(interfaceType->GetStaticClientData(packet->playerId)->GetData()))->name, packet->data);
interfaceType->Send(message, (const int)strlen(message)+1, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->playerId, true);
}
};
int main(void)
{
printf("This sample demonstrates RakNet's global compression feature.\n");
printf("When activated, outgoing and incoming data is tracked and saved to a frequency\n");
printf("table. This frequency table is then saved to disk and later loaded to generate\n");
printf("a Huffman encoding tree.\n");
printf("Difficulty: Intermediate\n\n");
// Pointers to the interfaces of our server and client.
// Note we can easily have both in the same program
FILE *serverToClientFrequencyTableFilePointer, *clientToServerFrequencyTableFilePointer;
char serverToClientFrequencyTableFilename[100], clientToServerFrequencyTableFilename[100];
char buff[256];
RakClientInterface *rakClient=RakNetworkFactory::GetRakClientInterface();
RakServerInterface *rakServer=RakNetworkFactory::GetRakServerInterface();
ClientMultiplayer<RakClientInterface> rakClientMultiplayer;
ServerMultiplayer<RakServerInterface> rakServerMultiplayer;
// Frequency table tracking takes a bit of CPU power under high loads so is off by default. We need to turn it on
// So RakNet will track data frequencies which we can then use to create the compression tables
rakServer->SetTrackFrequencyTable(true);
rakClient->SetTrackFrequencyTable(true);
int i = rakServer->GetNumberOfAddresses();
// Just so we can remember where the packet came from
bool isServer;
// We are generating compression layers for both the client and the server just to demonstrate it
// in practice you would only do one or the other depending on whether you wanted to run as a client or as a server
// Note that both the client and the server both need both frequency tables and they must be the same
puts("Enter the filename holding the server to client frequency table:");
gets(serverToClientFrequencyTableFilename);
if (serverToClientFrequencyTableFilename[0]==0)
strcpy(serverToClientFrequencyTableFilename, "s2c");
serverToClientFrequencyTableFilePointer = fopen(serverToClientFrequencyTableFilename, "rb");
if (serverToClientFrequencyTableFilePointer==0)
{
printf("Can't open %s\n", serverToClientFrequencyTableFilename);
}
else
{
unsigned int frequencyData[256];
int numRead;
numRead=(int)fread(frequencyData, sizeof(unsigned int), 256, serverToClientFrequencyTableFilePointer);
if (numRead != 256)
puts("Error reading data");
else
{
rakClient->GenerateCompressionLayer(frequencyData, true); // server to client is input for the client so the last parameter is true
rakServer->GenerateCompressionLayer(frequencyData, false); // server to client is output for the server so the last parameter is false
puts("Compression layer generated for server to client data");
}
fclose(serverToClientFrequencyTableFilePointer);
}
puts("Enter the filename holding the client to server frequency table:");
gets(clientToServerFrequencyTableFilename);
if (clientToServerFrequencyTableFilename[0]==0)
strcpy(clientToServerFrequencyTableFilename, "c2s");
clientToServerFrequencyTableFilePointer = fopen(clientToServerFrequencyTableFilename, "rb");
if (clientToServerFrequencyTableFilePointer==0)
{
printf("Can't open %s\n", clientToServerFrequencyTableFilename);
}
else
{
unsigned int frequencyData[256];
int numRead;
numRead=(int)fread(frequencyData, sizeof(unsigned int), 256, clientToServerFrequencyTableFilePointer);
if (numRead != 256)
puts("Error reading data");
else
{
rakClient->GenerateCompressionLayer(frequencyData, false); // client to server is output for the client so the last parameter is false
rakServer->GenerateCompressionLayer(frequencyData, true); // client to server is input for the server so the last parameter is true
puts("Compression layer generated for server to client data");
}
fclose(clientToServerFrequencyTableFilePointer);
}
// Crude interface
puts("Run as (1) Client or (2) Server ?");
if ( gets(buff)&&(buff[0]=='1') )
{
// Holds user data
char ip[30], serverPort[30], clientPort[30];
StaticClientDataStruct staticClientData;
// A client
isServer=false;
// Get our input
puts("Enter the client port to listen on.");
gets(clientPort);
if (clientPort[0]==0)
strcpy(clientPort, "60001");
puts("Enter your name (up to 40 characters)");
gets(staticClientData.name);
// Note this passes by value, because otherwise people could
// get access to and damage our internal data
// UNASSIGNED_PLAYER_ID is to specify changing our own data, rather than our copy of another clients data
rakClient->SetStaticClientData(UNASSIGNED_PLAYER_ID, (char*)&staticClientData, sizeof(StaticClientDataStruct));
puts("Enter IP to connect to");
gets(ip);
if (ip[0]==0)
strcpy(ip, "127.0.0.1");
puts("Enter the port to connect to");
gets(serverPort);
if (serverPort[0]==0)
strcpy(serverPort, "60000");
// Connecting the client is very simple. 0 means we don't care about
// a connectionValidationInteger, and false for low priority threads
bool b = rakClient->Connect(ip, atoi(serverPort), atoi(clientPort), 0, 30);
if (b)
puts("Attempting connection");
else
{
puts("Bad connection attempt. Terminating.");
exit(1);
}
}
else
{
// Holds user data
char portstring[30];
EnumerationDataStruct enumerationDataStruct;
// A server
isServer=true;
puts("Enter the server port to listen on");
gets(portstring);
if (portstring[0]==0)
strcpy(portstring, "60000");
puts("Enter the server name (up to 40 characters)");
gets(enumerationDataStruct.serverName);
puts("Enter the server message of the day (up to 50 characters)");
gets(enumerationDataStruct.MOTD);
// Note this passes by value, because otherwise people could get access
// to and damage our internal data
rakServer->SetStaticServerData((char*)&enumerationDataStruct, sizeof(EnumerationDataStruct));
puts("Starting server.");
// Starting the server is very simple. 2 players allowed.
// 0 means we don't care about a connectionValidationInteger, and false
// for low priority threads
bool b = rakServer->Start(2, 0, 30, atoi(portstring));
if (b)
puts("Server started, waiting for connections.");
else
{
puts("Server failed to start. Terminating.");
exit(1);
}
}
puts("Type 'quit' to quit or type to talk.");
char message[400];
// Loop for input
while (1)
{
if (kbhit())
{
// Notice what is not here: something to keep our network running. It's
// fine to block on gets or anything we want
// Because the network engine was painstakingly written using threads.
gets(message);
if (strcmp(message, "quit")==0)
{
puts("Quitting.");
break;
}
// Message now holds what we want to broadcast
if (isServer)
{
char message2[420];
// Append Server: to the message so clients know that it ORIGINATED from the server
// All messages to all clients come from the server either directly or by being
// relayed from other cilents
strcpy(message2, "Server: ");
strcat(message2, message);
// message2 is the data to send
// strlen(message2)+1 is to send the null terminator
// HIGH_PRIORITY doesn't actually matter here because we don't use any other priority
// RELIABLE_ORDERED means make sure the message arrives in the right order
// We arbitrarily pick 0 for the ordering stream
// UNASSIGNED_PLAYER_ID means don't exclude anyone from the broadcast
// true means broadcast the message to everyone connected
rakServer->Send(message2, (const int)strlen(message2)+1, HIGH_PRIORITY, RELIABLE_ORDERED, 0, UNASSIGNED_PLAYER_ID, true);
}
else
{
// message is the data to send
// strlen(message)+1 is to send the null terminator
// HIGH_PRIORITY doesn't actually matter here because we don't use any other priority
// RELIABLE_ORDERED means make sure the message arrives in the right order
rakClient->Send(message, (const int)strlen(message)+1, HIGH_PRIORITY, RELIABLE_ORDERED, 0);
}
}
rakClientMultiplayer.ProcessPackets(rakClient);
rakServerMultiplayer.ProcessPackets(rakServer);
}
// Output statistics
printf("Server: Compression ratio=%f. Decompression ratio=%f\n", rakServer->GetCompressionRatio(), rakServer->GetDecompressionRatio());
printf("Client: Compression ratio=%f. Decompression ratio=%f\n", rakClient->GetCompressionRatio(), rakClient->GetDecompressionRatio());
rakServer->Disconnect(100);
rakClient->Disconnect(100);
// Output the frequency table generated by this run
if (isServer)
puts("Enter the filename to use to save the server to client frequency table:");
else
puts("Enter the filename to use to save the client to server frequency table:");
scanf("%s", serverToClientFrequencyTableFilename);
if (serverToClientFrequencyTableFilename[0]==0)
{
if (isServer)
strcpy(serverToClientFrequencyTableFilename, "s2c");
else
strcpy(serverToClientFrequencyTableFilename, "c2s");
}
serverToClientFrequencyTableFilePointer = fopen(serverToClientFrequencyTableFilename, "wb");
if (serverToClientFrequencyTableFilePointer==0)
{
printf("Can't open %s\n", serverToClientFrequencyTableFilename);
}
else
{
unsigned int frequencyData[256];
// Get the frequency table generated during the run
if (isServer)
rakServer->GetSendFrequencyTable(frequencyData);
else
rakClient->GetSendFrequencyTable(frequencyData);
fwrite(frequencyData, sizeof(unsigned int), 256, serverToClientFrequencyTableFilePointer);
fclose(serverToClientFrequencyTableFilePointer);
}
// We're done with the network
RakNetworkFactory::DestroyRakServerInterface(rakServer);
RakNetworkFactory::DestroyRakClientInterface(rakClient);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1