Networked Custom Controllers:Full Source
From C4 Engine Wiki
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()); } }
