Networked Custom Controllers:Full Source

From C4 Engine Wiki

Jump to: navigation, search

Contents

Tutorial

This is the full source code to the application without any comments or explanations.

You can read the full tutorial here: Networked_Custom_Controllers.

EXGame.h

#pragma once
 
#include <C4Application.h>
#include <C4Engine.h>
#include <C4World.h>
#include <C4Input.h>
 
#include "EXInterface.h"
#include "EXMultiplayer.h"
#include "EXController.h"
 
using namespace C4;
 
extern "C"
{
	C4MODULEEXPORT C4::Application *ConstructApplication(void);
}
 
namespace C4
{
 
	class Game : public Application, public Singleton<Game>
	{
	public:
		Game();
		~Game();
 
		EngineResult LoadWorld(const char *name);
		void UnloadWorld();
 
		Message *ConstructMessage(MessageType type, Decompressor &data) const;
		void ReceiveMessage(Player *sender, const NetworkAddress &address, const Message *message);
		void HandlePlayerEvent(PlayerEvent event, Player *player, const void *param);
		void HandleConnectionEvent(ConnectionEvent event, const NetworkAddress& address, const void *param);
 
		void SpawnBox(Player *player, Point3D location, long controllerIndex);
 
		void HostGame();
		void JoinGame(String<> ipAddress);
		void QuitGame();
 
		static Player *ConstructPlayer(PlayerKey key, void *data);
		static void EscapeProc(void *data);
	private:
		InputMgr::KeyProc				*prevEscapeProc;
		void							*prevEscapeData;
 
		ModelRegistration				boxModelReg;
 
		BoxSnapshot						*boxSnapshot;
	};
 
	extern Game *TheGame;
}

EXGame.cpp

#include "EXGame.h"
 
Game *C4::TheGame = nullptr;
 
C4::Application *ConstructApplication()
{
	return (new Game);
}
 
Game::Game() : Singleton<Game>(TheGame),
		boxModelReg(C4::kModelWoodBox, nullptr, "model/wood_box", C4::kModelPrecache, C4::kControllerBox)
{
	prevEscapeProc = TheInputMgr->GetEscapeProc();
	prevEscapeData = TheInputMgr->GetEscapeData();
	TheInputMgr->SetEscapeProc(&EscapeProc, this);
 
	TheInterfaceMgr->SetInputManagementMode(C4::kInputManagementAutomatic);
 
	TheNetworkMgr->SetProtocol(C4::kGameProtocol);
	TheNetworkMgr->SetPortNumber(C4::kGamePort);
 
	TheMessageMgr->SetPlayerConstructor(&ConstructPlayer, this);
 
	boxSnapshot = nullptr;
 
	MainWindow::Open();
}
 
Game::~Game()
{
	if(boxSnapshot) delete boxSnapshot;
 
	this->UnloadWorld();
	if(TheMainWindow) delete TheMainWindow;
 
	TheInputMgr->SetEscapeProc(prevEscapeProc, prevEscapeData);
}
 
EngineResult Game::LoadWorld(const char *name)
{
	WorldResult result = TheWorldMgr->LoadWorld(name);
 
	if(result == C4::kWorldOkay)
	{
		World *world = TheWorldMgr->GetWorld();
		if(world)
		{
			FrustumCamera *camera = new FrustumCamera(2.0F, 1.0F);
 
			camera->SetNodePosition(Point3D(20.0F, 20.0F, 10.0F));
			camera->LookAtPoint(Point3D(0.0F, 0.0F, 0.0F));
			camera->Invalidate();
 
			world->SetCamera(camera);
		}
 
		TheEngine->Report("Game has begun!");
	}
 
	return result;
}
 
void Game::UnloadWorld()
{
	TheWorldMgr->UnloadWorld();
	TheMessageMgr->EndGame();
}
 
void Game::ReceiveMessage(Player *sender, const NetworkAddress &address, const Message *message)
{
	switch(message->GetMessageType())
	{
		case C4::kMessageSpawn: break;
		case C4::kMessageRequest: 
		{
			if(TheMessageMgr->Server())
			{
				Point3D loc;
				loc.x = -10.0F + Math::Random(20.0F);
				loc.y = -10.0F + Math::Random(20.0F);
				loc.z = 1.0F;
 
				long contIndex = TheWorldMgr->GetWorld()->NewControllerIndex();
				TheMessageMgr->SendMessageAll(SpawnMessage(sender->GetPlayerKey(), contIndex, loc));
			}
			break;
		}
		case C4::kMessageSpin:
		{
			if(TheMessageMgr->Server())
			{
				GamePlayer *gPlayer = static_cast<GamePlayer *>(sender);
				BoxController *cont = gPlayer->GetController();
				if(cont)
				{
					const SpinMessage *msg = static_cast<const SpinMessage *>(message);
					TheMessageMgr->SendMessageAll(BoxStateMessage(cont->GetControllerIndex(), cont->GetSpinRate() + msg->GetRate(), cont->GetCurrentAngle()));
				}
			}
			break;
		}
	}
}
 
Message *Game::ConstructMessage(MessageType type, Decompressor &data) const
{
	switch(type)
	{
		case C4::kMessageSpawn: return (new SpawnMessage);
		case C4::kMessageRequest: return (new RequestMessage);
		case C4::kMessageSpin: return (new SpinMessage);
	}
 
	return nullptr;
}
 
void Game::HandlePlayerEvent(PlayerEvent event, Player *player, const void *param)
{
	switch(event)
	{
		case C4::kPlayerInitialized:
		{
			TheEngine->Report("Player initialized.");
 
			GamePlayer *gp = nullptr;
			BoxController *cont = nullptr;
			Node *node = nullptr;
			PlayerKey key = -1;
			long id = -1;
			Point3D loc;
 
			Player *p = TheMessageMgr->GetFirstPlayer();
			while(p)
			{
				gp = static_cast<GamePlayer *>(p);
 
				cont = gp->GetController();
				if(cont)
				{
					node = cont->GetTargetNode();
 
					key = gp->GetPlayerKey();
					id = cont->GetControllerIndex();
					loc = node->GetWorldPosition();
 
					TheMessageMgr->SendMessage(player->GetPlayerKey(), SpawnMessage(key, id, loc));
				}
 
				p = p->Next();
			}
 
			break;
		}
 
		case C4::kPlayerDisconnected:
		{
			GamePlayer *gp = static_cast<GamePlayer *>(player);
			TheMessageMgr->SendMessageAll(BoxDestroyMessage(gp->GetController()->GetControllerIndex()));
			break;
		}
	}
 
	Application::HandlePlayerEvent(event, player, param);
}
 
void Game::HandleConnectionEvent(ConnectionEvent event, const NetworkAddress& address, const void *param)
{
	switch(event)
	{
		case C4::kConnectionClientOpened:
		{
			Engine::Report("Client Connected.");
			break;
		}
		case C4::kConnectionClientClosed:
		case C4::kConnectionClientTimedOut:
		{
			Engine::Report("Client Connection Closed.");
			break;
		}
		case C4::kConnectionServerAccepted:
		{
			Engine::Report("We are connected.");
			TheGame->LoadWorld("world/mult");
			break;
		}
		case C4::kConnectionServerClosed:
		case C4::kConnectionServerTimedOut:
		{
			Engine::Report("Server Connection Closed.");
			UnloadWorld();
			break;
		}
	}
 
	Application::HandleConnectionEvent(event, address, param);
}
 
void Game::SpawnBox(Player *player, Point3D location, long controllerIndex)
{
	World *world = TheWorldMgr->GetWorld();
	if(world)
	{
		GamePlayer *gPlayer = static_cast<GamePlayer *>(player);
		BoxController *cont = gPlayer->GetController();
		if(!cont)
		{
			cont = new BoxController();
			gPlayer->SetController(cont);
			cont->SetControllerIndex(controllerIndex);
 
			Model *box = Model::Get(C4::kModelWoodBox);
			box->SetController(cont);
 
			Zone *zone = world->FindZone(location);
			box->SetNodePosition(zone->GetInverseWorldTransform() * location);
			zone->AddNewSubnode(box);
		}
	}
}
 
void Game::HostGame()
{
	TheMessageMgr->BeginMultiplayerGame(true);
	TheEngine->Report(String<>("Initialized. Hosting on: ") + MessageMgr::AddressToString(TheNetworkMgr->GetLocalAddress(), true));
 
	TheGame->LoadWorld("world/mult");
 
	boxSnapshot = new BoxSnapshot();
	TheMessageMgr->AddSnapshotSender(boxSnapshot);
}
 
void Game::JoinGame(String<> ipAddress)
{
	TheMessageMgr->BeginMultiplayerGame(false);
	NetworkAddress addr = MessageMgr::StringToAddress(ipAddress);
	addr.SetPort(C4::kGamePort);
	NetworkResult result = TheMessageMgr->Connect(addr);
	TheEngine->Report(String<>("Attempting connection with: ") + MessageMgr::AddressToString(addr, true));
 
	if(result == C4::kNetworkOkay)
	{
		TheEngine->Report("Network initialized. Waiting on response...");
	}
	else
	{
		TheEngine->Report(String<>("Issues arose when trying to initialize the connection. Code: ") += result);
	}
}
 
void Game::QuitGame()
{
	TheMessageMgr->DisconnectAll();
	TheEngine->Quit();
}
 
Player *Game::ConstructPlayer(PlayerKey key, void *data)
{
	return (new GamePlayer(key));
}
 
void Game::EscapeProc(void *data)
{
	MainWindow::Open();
}

EXInterface.h

#pragma once
 
#include <C4Interface.h>
 
using namespace C4;
 
namespace C4
{
	class MainWindow : public Window, public Singleton<MainWindow>
	{
	public:
		MainWindow();
		~MainWindow();
 
		static void Open();
 
		void Preprocess();
		void HandleWidgetEvent(Widget *widget, const WidgetEventData *eventData);
 
	private:
		PushButtonWidget	*hostGame;
		PushButtonWidget	*joinGame;
		PushButtonWidget	*quitGame;
 
		PushButtonWidget	*spawnBox;
		PushButtonWidget	*spinLeft;
		PushButtonWidget	*spinRight;
 
		EditTextWidget		*ipAddress;
	};
 
	extern MainWindow *TheMainWindow;
}

EXInterface.cpp

#include "EXInterface.h"
#include "EXGame.h"
 
MainWindow *C4::TheMainWindow = nullptr;
 
MainWindow::MainWindow() : Window("panel/Main"), Singleton<MainWindow>(TheMainWindow)
{
}
 
MainWindow::~MainWindow()
{
}
 
void MainWindow::Preprocess() 
{
	Window::Preprocess();
 
	hostGame = static_cast<PushButtonWidget *>(this->FindWidget("hostGame"));
	joinGame = static_cast<PushButtonWidget *>(this->FindWidget("joinGame"));
	quitGame = static_cast<PushButtonWidget *>(this->FindWidget("quitGame"));
 
	spawnBox = static_cast<PushButtonWidget *>(this->FindWidget("spawnBox"));
	spinLeft = static_cast<PushButtonWidget *>(this->FindWidget("spinLeft"));
	spinRight = static_cast<PushButtonWidget *>(this->FindWidget("spinRight"));
 
	ipAddress = static_cast<EditTextWidget *>(this->FindWidget("ipAddress"));
}
 
void MainWindow::HandleWidgetEvent(Widget *widget, const WidgetEventData *eventData)
{
	if(widget == hostGame)
	{
		TheGame->HostGame();
		this->Close();
	}
	else if(widget == joinGame)
	{
		TheGame->JoinGame(ipAddress->GetText());
		this->Close();
	}
	else if(widget == spawnBox)
	{
		TheMessageMgr->SendMessageAll(RequestMessage());
		this->Close();
	}
	else if(widget == spinLeft)
	{
		TheMessageMgr->SendMessage(C4::kPlayerServer, SpinMessage(-1.0F));
		this->Close();
	}
	else if(widget == spinRight)
	{
		TheMessageMgr->SendMessage(C4::kPlayerServer, SpinMessage(1.0F));
		this->Close();
	}
	else if(widget == quitGame)
	{
		TheGame->QuitGame();
	}
}
 
void MainWindow::Open()
{
	if(TheMainWindow)
	{
		TheInterfaceMgr->SetActiveWindow(TheMainWindow);
	}
	else
	{
		TheInterfaceMgr->AddWidget(new MainWindow);
	}
}


EXMultiplayer.h

#pragma once
 
#include <C4Messages.h>
#include "EXController.h"
 
using namespace C4;
 
namespace C4
{
	const unsigned long kGameProtocol = 0x00000012;
	const unsigned short kGamePort = 3003;
 
	enum MESSAGES
	{
		kMessageServerInfo = kMessageBaseCount,
		kMessageSpawn,
		kMessageRequest,
		kMessageSpin,
		kMessageBoxState,
		kMessageBoxDestroy
	};
 
	class GamePlayer : public Player
	{
	public:
		GamePlayer(PlayerKey key);
		~GamePlayer();
 
		BoxController *GetController() const
		{
			return cont;
		}
 
		void SetController(BoxController *controller)
		{
			cont = controller;
		}
	private:
		BoxController *cont;
	};
 
	class RequestMessage : public Message
	{
	public:
		RequestMessage();
		~RequestMessage();
 
		void Compress(Compressor &data) const;
		bool Decompress(Decompressor &data);
	};
 
	class SpinMessage : public Message
	{
	public:
		SpinMessage();
		SpinMessage(float rate);
		~SpinMessage();
 
		void Compress(Compressor &data) const;
		bool Decompress(Decompressor &data);
 
		float GetRate() const
		{
			return rate;
		}
	private:
		float rate;
	};
 
	class SpawnMessage : public Message
	{
	public:
		SpawnMessage();
		SpawnMessage(PlayerKey playerkey, long controllerIndex, Point3D location);
		~SpawnMessage();
 
		bool HandleMessage(Player *sender) const;
 
		void Compress(Compressor &data) const;
		bool Decompress(Decompressor &data);
 
		Point3D GetLocation() const
		{
			return loc;
		}
 
		PlayerKey GetPlayerKey() const
		{
			return key;
		}
 
		long GetControllerIndex() const
		{
			return contIndex;
		}
	private:
		Point3D loc;
		long contIndex;
		PlayerKey key;
	};
}

EXMultiplayer.cpp

#include "EXMultiplayer.h"
#include "EXGame.h"
 
GamePlayer::GamePlayer(PlayerKey key) : Player(key)
{
	cont = nullptr;
}
 
GamePlayer::~GamePlayer()
{
}
 
RequestMessage::RequestMessage() : Message(C4::kMessageRequest)
{
}
 
RequestMessage::~RequestMessage() 
{
}
 
void RequestMessage::Compress(Compressor &data) const
{
}
 
bool RequestMessage::Decompress(Decompressor &data)
{
	return true;
}
 
SpawnMessage::SpawnMessage() : Message(C4::kMessageSpawn)
{
	this->contIndex = 0;
	this->key = -1;
}
 
SpawnMessage::SpawnMessage(PlayerKey playerkey, long controllerIndex, Point3D location) : Message(C4::kMessageSpawn)
{
	this->contIndex = controllerIndex;
	this->key = playerkey;
	this->loc = location;
}
 
SpawnMessage::~SpawnMessage()
{
}
 
bool SpawnMessage::HandleMessage(Player *sender) const
{
	Player *player = TheMessageMgr->GetPlayer(this->GetPlayerKey());
	TheGame->SpawnBox(player, this->GetLocation(), this->GetControllerIndex());
	return (true);
}
 
void SpawnMessage::Compress(Compressor &data) const
{
	data << loc;
	data << contIndex;
	data << key;
}
 
bool SpawnMessage::Decompress(Decompressor &data)
{
	data >> loc;
	data >> contIndex;
	data >> key;
	return (true);
}
 
SpinMessage::SpinMessage() : Message(C4::kMessageSpin)
{
	rate = 0.0F;
}
 
SpinMessage::SpinMessage(float r) : Message(C4::kMessageSpin)
{
	rate = r;
}
 
SpinMessage::~SpinMessage()
{
}
 
void SpinMessage::Compress(Compressor &data) const
{
	data << rate;
}
 
bool SpinMessage::Decompress(Decompressor &data)
{
	data >> rate;
	return (true);
}

EXController.h

#pragma once
 
#include <C4Controller.h>
 
using namespace C4;
 
namespace C4
{
	enum
	{
		kModelWoodBox = 'wbox',
	};
 
	enum
	{
		kControllerBox = 'cbox'
	};
 
	class BoxController : public Controller
	{
	public:
		BoxController();
		BoxController(float rate);
		~BoxController();
 
		float GetSpinRate() const
		{
			return (spinRate);
		}
 
		void SetSpinRate(float rate)
		{
			spinRate = rate;
		}
 
		float GetCurrentAngle() const
		{
			return (currentAngle);
		}
 
		void SetCurrentAngle(float angle)
		{
			currentAngle = angle;
		}
 
		void Preprocess(void);
		void Move(void);
 
		void ReceiveMessage(const ControllerMessage *message);
		ControllerMessage *ConstructMessage(ControllerMessageType type) const;
	private:
		float	spinRate;
		float	currentAngle;
 
		BoxController(const BoxController& boxController);
		Controller *Replicate(void) const;
 
		void Destroy();
	};
 
	class BoxStateMessage : public ControllerMessage
	{
	public:
		BoxStateMessage(long controllerIndex);
		BoxStateMessage(long controllerIndex, float rate, float angle);
		~BoxStateMessage();
 
		void Compress(Compressor &data) const override;
		bool Decompress(Decompressor &data) override;
 
		float GetRate() const
		{
			return rate;
		}
 
		float GetAngle() const
		{
			return angle;
		}
	private:
		float rate;
		float angle;
	};
 
	class BoxDestroyMessage : public ControllerMessage
	{
	public:
		BoxDestroyMessage(long controllerIndex);
		~BoxDestroyMessage();
	};
 
	class BoxSnapshot : public SnapshotSender
	{
	public:
		BoxSnapshot();
		~BoxSnapshot();
 
		void SendSnapshot();
	};
}

EXController.cpp

#include "EXController.h"
#include <C4Node.h>
#include <C4World.h>
#include "EXMultiplayer.h"
 
BoxController::BoxController() : Controller(C4::kControllerBox)
{
	spinRate = 0.0F;
	currentAngle = 0.0F;
}
 
BoxController::~BoxController()
{
}
 
BoxController::BoxController(const BoxController& boxController) : Controller(boxController)
{
	spinRate = boxController.spinRate;
}
 
Controller *BoxController::Replicate(void) const
{
    return (new BoxController(*this));
}
 
void BoxController::Preprocess(void)
{
    Controller::Preprocess();
}
 
void BoxController::Move(void)
{
	Node *node = this->GetTargetNode();
	if(node)
	{
		Zone *zone = node->GetOwningZone();
 
		Transform4D trans = node->GetNodeTransform();
 
		currentAngle += ((spinRate / 1000.0F) * TheTimeMgr->GetFloatDeltaTime());
		trans.SetRotationAboutZ(currentAngle);
		trans.SetTranslation(node->GetNodePosition());
 
		node->SetNodeTransform(zone->GetInverseWorldTransform() * trans);
		node->Invalidate();
	}
}
 
void BoxController::ReceiveMessage(const ControllerMessage *message)
{
	switch(message->GetControllerMessageType())
	{
		case C4::kMessageBoxState:
		{
			const BoxStateMessage *msg = static_cast<const BoxStateMessage *>(message);
			this->SetSpinRate(msg->GetRate());
			this->SetCurrentAngle(msg->GetAngle());
			break;
		}
		case C4::kMessageBoxDestroy:
		{
			this->Destroy();
			break;
		}
	}
 
	Controller::ReceiveMessage(message);
}
 
ControllerMessage *BoxController::ConstructMessage(ControllerMessageType type) const
{
	switch(type)
	{
		case C4::kMessageBoxState: return (new BoxStateMessage(this->GetControllerIndex()));
	}
	return (Controller::ConstructMessage(type));
}
 
void BoxController::Destroy()
{
	Node *node = this->GetTargetNode();
	if(node)
	{
		delete node;
	}
}
 
BoxStateMessage::BoxStateMessage(long contIndex) : ControllerMessage(C4::kMessageBoxState, contIndex)
{
	rate = 0.0F;
	angle = 0.0F;
}
 
BoxStateMessage::BoxStateMessage(long contIndex, float r, float a) : ControllerMessage(C4::kMessageBoxState, contIndex)
{
	rate = r;
	angle = a;
}
 
BoxStateMessage::~BoxStateMessage()
{
}
 
void BoxStateMessage::Compress(Compressor &data) const
{
	ControllerMessage::Compress(data);
	data << rate;
	data << angle;
}
 
bool BoxStateMessage::Decompress(Decompressor &data)
{
	if(ControllerMessage::Decompress(data))
	{
		data >> rate;
		data >> angle;
		return true;
	}
	return false;
}
 
BoxDestroyMessage::BoxDestroyMessage(long contIndex) : ControllerMessage(C4::kMessageBoxDestroy, contIndex)
{
}
 
BoxDestroyMessage::~BoxDestroyMessage()
{
}
 
BoxSnapshot::BoxSnapshot()
{
}
 
BoxSnapshot::~BoxSnapshot()
{
}
 
void BoxSnapshot::SendSnapshot()
{
	BoxController *cont;
	GamePlayer *p = static_cast<GamePlayer *>(TheMessageMgr->GetFirstPlayer());
	while(p)
	{
		cont = p->GetController();
		if(cont)
		{
			TheMessageMgr->SendMessageClients(BoxStateMessage(cont->GetControllerIndex(), cont->GetSpinRate(), cont->GetCurrentAngle()));
		}
 
		p = static_cast<GamePlayer *>(p->Next());
	}
}
Personal tools