Hello all,
As I was trying to recreate a Client/Server from the DBPro Codebase, I found that DGDK only supports Serial Connection and Modem Connection from DirectPlay.
So, I grabbed RakNet and built a little appy thing with DGDK built within. It's not completely finished, but I have college work to do all this week and want to give people the opportunity to go nuts with it. I built it in to a class, so hopefully it might come in handy.
Instructions:
Download RakNet from here:
http://www.jenkinssoftware.com/raknet/downloads/RakNet-3.3.zip
Unzip somewhere (C:\?)
Go to http://www.boostpro.com/products/free
and Get Boost 1.3.5. On the install menu, select your version of VS (probably 9.0), and tick all of the boxes on
the right with the exceptions of the Single Threaded ones.
Go for a coffee. Seriously, this download takes forever.
When you are back and it is installed:
Right click on your Project in VC++. Select properties.
Click On C\C++. In Additional Include Directories, click on the little Browse square thing at the right hand
side(you need to click the field before it will appear)
Browse to Include the following folder:
<WhereEverYouPutRakNet>\source
<WhereEverYouPutBoost>\boost_1_3_5
Similarly, click on the Linker option and put this in Additional Library Directories:
<WhereEverYouPutRakNet>\lib
While you are in this general vicinity, click on Input and add in the following dependencies:
wsock32.lib
RakNetDLL.lib
Include the following headers wherever you plan to use RakNet:
#include <MessageIdentifiers.h>
#include <RakNetworkFactory.h>
#include <RakPeerInterface.h>
#include "RPC3.h"
As you may notice from the "", you need to go and grab RPC3.h, RPC3_Boost.h and RPC3.cpp from
<Raknet>\DependentExtensions\RPC3 (if you havent used the ones included)
You need to build RakNet now. Go to the RakNet/source directory and open the .sln file. See the project called DLL
?
This is broken ><
You need to right click, then click on Add Existing Item and select the following files:
RSACrypt.h
RSACrypt.cpp
WSAStartupSingleton.h
WSAStartupSingleton.cpp
This should now build correctly.
Copy the newly built RakNet.dll from <WhereEverYouPutRakNet>\Lib to your project folder (where the EXE goes).
Get coding!
dbServer.h
#pragma once
/**
* Name: dbServer
* Usage: Console Client/Server Class
* Author: Paul Hollingworth
* Date: 15/11/08
*/
#include "DarkGDK.h"
#include <string> // Defines std::string
#include <MessageIdentifiers.h>
#include <RakNetworkFactory.h>
#include <RakPeerInterface.h>
#include "RPC3.h"
class dbServer
{
public:
dbServer();
~dbServer(void);
//Will start a new server
void startasHost(char* cpServerName, int iMaxPlayers, int iServerPort);
//Will start a new client
void connectAsClient(char* cIP, int iPort);
void updateServer();
void updateClient();
int getServerPort();
char* getServerName();
//Will be the method used to send chat messages between server/client needs _cdecl call
void PrintMessage(std::string message, RakNet::RPC3* rpc3);
private:
int iServerPort;
char* cpServerName;
char* cpServerIP;
//A new RakNet peer object
RakPeerInterface* peer;
//server checker
bool isServer;
//Basic Packet
Packet* packet;
//Our Remote Procedure Call object
RakNet::RPC3 rpc3;
};
dbServer.cpp
#include "dbServer.h"
dbServer::dbServer()
{
//init a few pointers;
cpServerName = "";
cpServerIP = "";
//Creates the new peer
peer = RakNetworkFactory::GetRakPeerInterface();
//Attach the RPC3 to the server
//RPC3 is brand new, AutoRPC was a pain with classes
peer->AttachPlugin(&rpc3);
//Register PrintMessage as a function that can be called by server and client
RPC3_REGISTER_FUNCTION(&rpc3, &dbServer::PrintMessage);
}
dbServer::~dbServer(void)
{
//general cleanup
//I'm a bit of a nazi about deleting pointers, just the way I was taught really
RakNetworkFactory::DestroyRakPeerInterface(peer);
delete cpServerName;
delete cpServerIP;
delete packet;
}
void dbServer::startasHost(char* cpNameOfServer, int iMaxPlayers, int iPort)
{
//assign name and port to class members
dbPrint(cpNameOfServer);
iServerPort = iPort;
cpServerName = cpNameOfServer;
isServer = true;
dbPrint(cpServerName);
dbPrint("Starting Host Server....");
dbSync();
if(peer->Startup(iMaxPlayers, 30, &SocketDescriptor(iServerPort, 0), 1))
{
//set max players
peer->SetMaximumIncomingConnections(iMaxPlayers);
dbWait(2000); //wait 2 secs, y'know, just for drama
dbPrint("Server Started!!");
dbSync();
}
else
throw("Server failed to start"); //caught by catch in main.cpp - if we are ghere, server failed to start
}
void dbServer::connectAsClient(char* cpIP, int iPort)
{
dbPrint("Starting Client.....");
dbSync();
isServer = false;
iServerPort = iPort;
cpServerIP = cpIP;
//startup new peer
if(peer->Startup(1, 30, &SocketDescriptor(), 1))
{
//connect to server and port passed as argument to connectAsClient
if(peer->Connect(cpServerIP, iServerPort, 0, 0))
{
//need to RPC getServerName()
dbPrint("You are now connected");
dbSync();
}
else
{
throw("Server connection failed (Did you enter the correct IP and Port?"); //caught by catch in main.cpp
}
}
}
//Still working on this
void dbServer::updateClient()
{
/* Input some chat message.
dbPrint("Enter a chat message: ");
char* chatMessage = new char[801];
chatMessage = dbInput();
*
* TODO: Implement Chat - Client Side
*
*
// RPC call.
char buffer[1024];
sprintf(buffer, "%s", chatMessage);
dbPrint(buffer);
cout << "RPC call PrintMessage(\"" << chatMessage << "\")" << endl; ??
rpc3.SetRecipientAddress(UNASSIGNED_SYSTEM_ADDRESS, true);
rpc3.CallC("PrintMessage", chatMessage);
delete chatMessage;*/
}
void dbServer::updateServer()
{
//get the packet from client or server
packet = peer->Receive();
if(packet) //if its not a NULL poinnter
{
switch(packet->data[0]) //read the packet and determine the message
{
case ID_REMOTE_DISCONNECTION_NOTIFICATION:
dbPrint("Another client has disconnected.");
dbSync();
break;
case ID_REMOTE_CONNECTION_LOST:
dbPrint("Another client has lost the connection.");
dbSync();
break;
case ID_REMOTE_NEW_INCOMING_CONNECTION:
dbPrint("Another client has connected.\n");
dbSync();
break;
case ID_CONNECTION_REQUEST_ACCEPTED:
dbPrint("Our connection request has been accepted.");
dbSync();
break;
case ID_NEW_INCOMING_CONNECTION:
dbPrint("A connection is incoming.");
dbSync();
break;
case ID_NO_FREE_INCOMING_CONNECTIONS:
dbPrint("The server is full.");
dbSync();
break;
case ID_DISCONNECTION_NOTIFICATION:
if (isServer)
{
dbPrint("A client has disconnected.");
dbSync();
}
else
{
dbPrint("We have been disconnected.");
dbSync();
}
break;
case ID_CONNECTION_LOST:
if (isServer)
{
dbPrint("A client lost the connection.");
dbSync();
}
else
{
dbPrint("Connection lost.");
dbSync();
}
break;
default:
char tempBuffer [256];
sprintf(tempBuffer, "Message with identifier %s has arrived", packet->data[0]);
dbPrint(tempBuffer);
dbSync();
break;
}
}
peer->DeallocatePacket(packet); //clean up
}
char* dbServer::getServerName()
{
return cpServerName;
}
int dbServer::getServerPort()
{
return iServerPort;
}
//not fully implemented yet
void dbServer::PrintMessage(std::string message, RakNet::RPC3* rpc3)
{
//print the message locally
char tempBuffer [256];
sprintf(tempBuffer, "Message: %s ", message);
dbPrint(tempBuffer);
dbSync();
if (isServer)
{
// Broadcast this chat message to everyone except the sender
rpc3->SetRecipientAddress(rpc3->GetLastSenderAddress(), true);
rpc3->CallC("PrintMessage", message);
}
}
main.cpp
/***
*
* Networking Example - RakNet
* by Paul Hollingworth
* Date: 15/11/2008
* Usage : Client/Server system built upon RakNet.
* Suggested Additions : MANY!
* Completed while listening to : Quiet Riot - Cum On Feel The Noize
*/
// whenever using Dark GDK you must ensure you include the header file
#include "DarkGDK.h"
#include "dbServer.h"
//helper function
void cleanMe(char*);
// the main entry point for the application is this function
void DarkGDK ( void )
{
int iConnectionMode = 0;
int iPort = 0;
dbSyncOn();
dbSyncRate(120);
//Makes cursor go away :)
dbHideMouse();
//sets the 640x480 window up if not defaulted
dbSetWindowSize(640, 480);
//print out menu and set title
dbSetWindowTitle("Networking Example");
dbPrint("*************Network Example*******************");
dbPrint();
dbServer *myServer = new dbServer();
dbPrint("Press 1 for Host Mode and 2 for Client Mode: ");
dbSync();
char* temp = "";
temp = dbInput();
iConnectionMode = atoi(temp); //convert from char* to int
cleanMe(temp); //clean up
if(iConnectionMode != 1 && iConnectionMode != 2)
{
//if they didn't enter 1 or 2
dbPrint("Connection type not recognised");
iConnectionMode = 0;
return;
}
else if(iConnectionMode == 1) //server
{
char* cpServerName = "";
int iMaxPlayers = 0;
dbPrint("Please enter a server name: ");
cpServerName = dbInput();
dbSync();
dbPrint("Please enter the max number of players: ");
dbSync();
char* cpTempConverter = "";
cpTempConverter = dbInput();
iMaxPlayers = atoi(cpTempConverter); //convert to int
dbPrint("Please enter the server port you wish to use (Recommended: 60000) : ");
dbSync();
cpTempConverter = "";
cpTempConverter = dbInput();
iPort = atoi(cpTempConverter);
cleanMe(cpTempConverter);
try
{
myServer->startasHost(cpServerName, iMaxPlayers, iPort);
cleanMe(cpServerName);
}
catch(char* message)//thrown by startasHost
{
dbPrint("Could not start host server, Error: ");
dbPrint(message);
dbSync();
dbWait(5000); //give them time to read message
cleanMe(message);
return;
}
}
else
{
dbPrint("Please enter the IP of the server (yours should be 127.0.0.1) : ");
char* cpIP = "";
cpIP = dbInput();
dbPrint("Please enter the port of the server (yours should be 60000) : ");
char* cpTempPortHolder = dbInput();
iPort = atoi(cpTempPortHolder);
cleanMe(cpTempPortHolder);
try
{
myServer->connectAsClient(cpIP, iPort);
cleanMe(cpIP);
}
catch(char* message)//thrown by connectAsClient
{
dbPrint(message);
dbSync();
dbWait(5000);
cleanMe(message);
cleanMe(cpIP);
return;
}
}
// our main loop
while ( LoopGDK ( ) )
{
myServer->updateServer(); //update every frame
dbSync(); //draw it all :)
if(dbEscapeKey())
return;
}
dbPrint("Exiting in 10 seconds, you can exit now, this is a status message");
dbSync();
dbWait(10000);
// return back to windows
return;
}
void cleanMe(char* thingtoDelete)
{
if(thingtoDelete)
{
delete thingtoDelete;
}
}
You can tell its working btw if you start 2 instances of it on a single local machine. This works through TCP/IP so you could do it by IP, whatever.
To come:
Proper RPC support - I just haven't gotten time to do this, might be able to Thursday night/Friday morning, but then again maybe not. This is how you would send info from client to server, the client calls a function on the server itself.
Voice Support - Easy Peasy to do, next weekend.
SQL Hookup - A few weeks time. The ability for the server to check in to MySQL, MSSQL, PostSQL databases and read information.
Admin stuff - user banning, muting, kicking, etc.