Implementing CIGI Client Logic for a Custom Project
C++ Implementation
To enable the CigiClient in your application to interact with a CIGI Host over the network, follow the instructions below.
Initializing CIGI Client for a Single-IG Setup
First of all, you should initialize CigiClient. To do so you can simply add the following code to the AppSystemLogic class.
In the AppSystemLogic.h file include CigiClientInterface.h library and declare a CigiClient manager interface pointer in the private section of the AppSystemLogic
// AppSystemLogic.h
/* .. */
// including the CigiClient interface library
#include <CigiClientInterface.h>
/* .. */
class AppSystemLogic : public Unigine::SystemLogic
{
public:
AppSystemLogic() {}
virtual ~AppSystemLogic() {}
virtual int init() override;
virtual int render() override;
private:
// declaring a CigiClient manager interface pointer
Cigi::CigiClientInterface* cigi = nullptr;
public:
/* .. */
};
/* .. */
Insert the following CigiClient initialization code into the AppSystemLogic::init() method:
// AppSystemLogic.cpp
#include <UnigineApp.h>
using namespace Unigine;
using namespace Cigi;
/* .. */
int AppSystemLogic::init(
{
// update even if the window is not focused
App::get()->setUpdate(1);
// finding CigiClient plugin index
int index = Engine::get()->findPlugin("CigiClient");
if (index == -1)
{
Log::error("AppSystemLogic::init_cigi(): Plugin \"CigiClient\" is not loaded!\n");
return;
}
// geting Cigi manager interface
cigi = (Cigi::CigiClientInterface*)Engine::get()->getPluginData(index);
// initializing the CIGI Client (IG)
cigi->init(Cigi::CIGI_VERSION_33, "127.0.0.1", 8889, 8888);
// loading entity types from the "entity_types.xml" definition file
cigi->loadEntityTypes("entity_types.xml");
return 1;
}
You should also perform a part of initialization in the AppWorldLogic class. For this purpose include the CigiClientInterface.h library in the AppWorldLogic.h file:
// AppWorldLogic.h
/* .. */
// including the CigiClient interface library
#include <CigiClientInterface.h>
/* .. */
In the AppWorldLogic.h file add the code to get the pointer to the manager interface, create the main camera (player) and do some framerate adjustment in the AppWorldLogic::init() method and insert a couple of lines in the AppWorldLogic::render() method to display debug information:
// AppWorldLogic.cpp
#include <UnigineEditor.h>
#include <UnigineGame.h>
#include <UnigineApp.h>
/* .. */
using namespace Unigine;
using namespace Cigi;
using namespace Math;
int AppWorldLogic::init()
{
// find CigiClient plugin index
int index = Engine::get()->findPlugin("CigiClient");
if (index == -1)
{
Log::error("AppWorldLogic::init(): Plugin \"CigiClient\" is not loaded!\n");
return 0;
}
// getting Cigi interface
Cigi::CigiClientInterface* cigi = (Cigi::CigiClientInterface*)Engine::get()->getPluginData(index);
cigi->init(Cigi::CIGI_VERSION_33, "127.0.0.1", 8889, 8888);
// creating a View (camera) with id=0 and setting it as the main player
int view_id = 0;
PlayerDummyPtr player = cigi->getView(view_id)->getPlayer();
Game::get()->setPlayer(player->getPlayer());
// clamping framerate to 60Hz
// (set a fixed FPS value to make simulations consistent across all computers)
if ((App::get()->getFlags() & App::VSYNC) == 0 ||
(App::get()->getFlags() & App::FULLSCREEN) == 0)
Game::get()->setFTime(1.0f / 60.0f);
return 1;
}
int AppSystemLogic::render()
{
// showing the CigiClient debug information
if (cigi)
cigi->showDebug();
return 1;
}
Initializing CIGI Client for a Multi-IG Setup
First of all, you should initialize Syncker and then CigiClient plugin. To do so you can simply add the following code to the AppSystemLogic class.
In the AppSystemLogic.h file include SynckerInterface.h library and declare a CigiClient manager interface pointer and initialization methods for both Syncker and CigiClient plugins in the private section of the AppSystemLogic
// AppSystemLogic.h
/* .. */
// including the CigiClient and Syncker interface libraries
#include <CigiClientInterface.h>
#include <SynckerInterface.h>
/* .. */
class AppSystemLogic : public Unigine::SystemLogic
{
public:
AppSystemLogic() {}
virtual ~AppSystemLogic() {}
virtual int init() override;
virtual int render() override;
private:
// declaring a CigiClient manager interface pointer
Cigi::CigiClientInterface* cigi = nullptr;
void init_syncker();
void init_cigi();
public:
/* .. */
};
/* .. */
Implement bot initialization methods for Syncker and CigiClient plugins as shown below and insert them into the AppSystemLogic::init() method:
// AppSystemLogic.cpp
#include <UnigineApp.h>
using namespace Unigine;
using namespace Cigi;
/* .. */
int AppSystemLogic::init(
{
// updating even if the window is not focused
App::get()->setUpdate(1);
// initializing Syncker
init_syncker();
// initializing CigiClient
init_cigi();
return 1;
}
/// method performing the Syncker plugin initialization
void AppSystemLogic::init_syncker()
{
// finding Syncker plugin index
int index = Engine::get()->findPlugin("Syncker");
if (index == -1)
{
Log::error("AppSystemLogic::init_syncker(): Plugin \"Syncker\" is not loaded!\n");
return;
}
// getting Syncker manager interface
Syncker::ManagerInterface* syncker_manager = (Syncker::ManagerInterface*)Engine::get()->getPluginData(index);
// initializing Syncker Master or Syncker Slave (depending on the command line arguments)
int udp_port = syncker_manager->getArgUdpPort();
int tcp_port = syncker_manager->getArgTcpPort();
int tcp_ping_port = syncker_manager->getArgTcpPingPort();
if (syncker_manager->getArgIsMaster())
syncker_manager->initMaster(syncker_manager->getArgMasterBroadcast(), udp_port, tcp_port, tcp_ping_port);
else
syncker_manager->initSlave(syncker_manager->getArgSlaveName(), udp_port, tcp_port, tcp_ping_port);
// enabling Syncker debug information
syncker_manager->getSyncker()->setDebug(1);
}
/// method performing the CigiClient plugin initialization
void AppSystemLogic::init_cigi()
{
// finding CigiClient plugin index
int index = Engine::get()->findPlugin("CigiClient");
if (index == -1)
{
Log::error("AppSystemLogic::init_cigi(): Plugin \"CigiClient\" is not loaded!\n");
return;
}
// getting CigiClient manager interface
cigi = (Cigi::CigiClientInterface*)Engine::get()->getPluginData(index);
// initializing the CIGI Client (IG)
cigi->init(Cigi::CIGI_VERSION_33, "127.0.0.1", 8889, 8888);
// loading entity types from the "entity_types.xml" definition file
cigi->loadEntityTypes("entity_types.xml");
}
You should also perform a part of initialization in the AppWorldLogic class. For this purpose include the CigiClientInterface.h library in the AppWorldLogic.h file:
// AppWorldLogic.h
/* .. */
// including the CigiClient interface library
#include <CigiClientInterface.h>
/* .. */
In the AppWorldLogic.h file add the code to get the pointer to the manager interface, create the main camera (player) and do some framerate adjustment in the AppWorldLogic::init() method and insert a couple of lines in the AppWorldLogic::render() method to display debug information:
// AppWorldLogic.cpp
#include <UnigineEditor.h>
#include <UnigineGame.h>
#include <UnigineApp.h>
/* .. */
using namespace Unigine;
using namespace Cigi;
using namespace Math;
int AppWorldLogic::init()
{
// find CigiClient plugin index
int index = Engine::get()->findPlugin("CigiClient");
if (index == -1)
{
Log::error("AppWorldLogic::init(): Plugin \"CigiClient\" is not loaded!\n");
return 0;
}
// getting Cigi interface
Cigi::CigiClientInterface* cigi = (Cigi::CigiClientInterface*)Engine::get()->getPluginData(index);
cigi->init(Cigi::CIGI_VERSION_33, "127.0.0.1", 8889, 8888);
// creating a View (camera) with id=0 and setting it as the main player
int view_id = 0;
PlayerDummyPtr player = cigi->getView(view_id)->getPlayer();
Game::get()->setPlayer(player->getPlayer());
// clamping framerate to 60Hz
// (set a fixed FPS value to make simulations consistent across all computers)
if ((App::get()->getFlags() & App::VSYNC) == 0 ||
(App::get()->getFlags() & App::FULLSCREEN) == 0)
Game::get()->setFTime(1.0f / 60.0f);
return 1;
}
int AppSystemLogic::render()
{
// showing the CigiClient debug information
if (cigi)
cigi->showDebug();
return 1;
}
Adding Callbacks for Host Packets
CIGI Host sends various control, definition and request packets to the IG. You can set a callback function for any type of packets.
The list of the Host packets includes the following:
Each callback function receives the control variable, which represents an interface for the packet of the corresponding type. To process such a packet first, you should get all the parameters contained in it via get*() methods. A typical callback function for a certain type of Host Control packet should look like this (pseudocode):
/// function processing a <control_packet_type> Control packet
void <control_packet_type>_control(Cigi::<control_packet_type_interface_class>* control)
{
// getting values from the incoming Host packet
control->getValue_1();
...
control->getValue_n();
/* Processing values */
}
CIGI Host Request packets (e.g., ICigiPositionRequest) require a response from the IG, so you have to create a response variable of the corresponding type (e.g., ICigiPositionResponse), set all necessary parameters and send it via the addIGPacket() function. A typical callback function for the packets of this type is similar to the one given above, but includes sending a response the the CIGI Host (pseudocode):
Cigi::CigiClientInterface *manager_interface;
/* ... */
/// function processing a <request_packet_type> Request packet (e.g. ICigiPositionRequest)
void <request_packet_type>_request(Cigi::<request_packet_type_interface_class>* control)
{
// getting values from the incoming Host packet
control->getValue_1();
...
control->getValue_n();
/* Processing values */
// creating a response packet (e.g. ICigiPositionResponse)
Cigi::<request_packet_type__response_interface_class>* response;
// setting values for the outgoing IG packet
response->setValue_1();
...
response->setValue_n();
// sending the response to the Host
manager_interface->addIGPacket(response);
}
To add a callback for a specific type of CIGI Host Control packets you should add callback functions to the private section of the AppWorldLogic class in the AppWorldLogic.h file:
// AppWorldLogic.h
/* .. */
class AppWorldLogic : public Unigine::WorldLogic
{
/* .. */
private:
// callbacks for CIGI Host Control packets
void celestial_control(Cigi::ICigiCelestialControl* control);
void weather_control(Cigi::ICigiWeatherControl* control);
};
/* .. */
So, implement your callback functions in the AppWorldLogic.cpp file and insert them into the AppWorldLogic::init() method:
/* .. */
/// method processing a Celestial Control packet
void AppWorldLogic::celestial_control(Cigi::ICigiCelestialControl* control)
{
if (control->getTimeValid())
{
float time = control->getHour() + control->getMinute() / 60.0f;
// call UnigineScript world function for changing time of day
// (time of day changes via Tracker)
// You can use your own code here
Engine::get()->runWorldFunction(Variable("setTime"), Variable(time));
}
}
/// method processing a Weather Control packet
void AppWorldLogic::weather_control(Cigi::ICigiWeatherControl* control)
{
if (control->getLayerID() == 1)
{
float clouds = control->getCoverage() / 100.0f;
// call UnigineScript world function for changing coverage
// (clouds coverage changes via Tracker)
// You can use your own code here
Engine::get()->runWorldFunction(Variable("setWeather"), Variable(clouds));
}
}
/* .. */
int AppWorldLogic::init()
{
/* ... */
// set cigi callbacks
cigi->setReceivePacketCallback(Cigi::CIGI_OPCODE_CELESTIAL_CONTROL, MakeCallback(this, &AppWorldLogic::celestial_control));
cigi->setReceivePacketCallback(Cigi::CIGI_OPCODE_WEATHER_CONTROL, MakeCallback(this, &AppWorldLogic::weather_control));
return 1;
}
Defining Entity Types for Your IG
An IG has a number of models, that are used to represent certain entities in the virtual environment. To define these entities the definition file is used. It is an *.xml file, with the following structure (example):
//entity_types.xml
<entity_types>
<entity_type id="118" name="be200_aircraft">
<path>cigi/be-200/be_200.node</path>
<component id="0" name="gears"/>
<articulated_part id="1">
<path invert_pitch="1">dynamic/pivot_aileron_left/aileron_left</path>
<path>dynamic/pivot_aileron_right/aileron_right</path>
</articulated_part>
<articulated_part id="2">
<path invert_yaw="1">dynamic/pivot_rudder/rudder</path>
</articulated_part>
<articulated_part id="3">
<path invert_pitch="1">dynamic/pivot_flipper_left/flipper_left</path>
<path invert_pitch="1">dynamic/pivot_flipper_right/flipper_right</path>
</articulated_part>
<articulated_part id="4">
<path invert_pitch="1">dynamic/pivot_flap_0_left/flap_0_left</path>
<path invert_pitch="1">dynamic/pivot_flap_0_right/flap_0_right</path>
<path invert_pitch="1">dynamic/pivot_flap_1_left/flap_1_left</path>
<path invert_pitch="1">dynamic/pivot_flap_1_right/flap_1_right</path>
</articulated_part>
<articulated_part id="5">
<path>dynamic/engine_left</path>
<path>dynamic/engine_right</path>
</articulated_part>
<articulated_part id="6">
<path>dynamic/chassis_front001/wheels_front</path>
<path>dynamic/chassis_element_left/chassis_left/wheels_left</path>
<path>dynamic/chassis_element_right/chassis_right/wheels_right</path>
</articulated_part>
</entity_type>
</entity_types>