Running Syncker for a Custom Project
To use Syncker most efficiently from the point of view of performance, selective synchronization of nodes and materials is needed. This is possible if Syncker is run from a script (and it does not matter if it will be a world, editor or a custom system one) for full control over synchronization.
Script Implementation
To run Syncker from the world script of a custom project, to synchronize nodes, render parameters and the player over the network, implement your script according to instructions below. Notice that similar logic is used to synchronize materials.
- First of all, initialize Syncker in the script that is run on the master computer. Also you should initialize a console to enable the ability of running master command-line options.
To synchronize creation of a node on all connected slaves, use the Unigine::Syncker::Master::createNode() function.
// my_project.cpp on a master computer #include <core/unigine.h> #include <core/systems/syncker/syncker_master.h> #include <core/systems/syncker/syncker_master_console.h> // It is more efficient to serialize nodes stored in an array and pass them as // one packet instead of sending them one by one. ObjectMesh meshes[0]; int init() { // Set a fixed FPS value to make simulations consistent across all computers. engine.game.setFTime(1.0f / 60.0f); // Initialize master console Unigine::Syncker::Master::Console::init(); // Initialize Syncker using a given broadcast address. Unigine::Syncker::Master::init(master_address); PlayerSpectator camera = new PlayerSpectator(); camera.setPosition(Vec3(2.0f,0.0f,1.5f)); camera.setDirection(Vec3(-1.0f,0.0f,-0.5f)); engine.game.setPlayer(camera); // Fill the world with meshes that will be synchronized. for(int j = 0; j < 2; j++) { for(int i = 0; i < 2 - j; i++) { // you can specify the path to any other mesh ObjectMesh mesh = node_remove(new ObjectMesh("my_project/meshes/box.mesh")); engine.editor.addNode(mesh); mesh.setMaterial("mesh_base","*"); mesh.setProperty("surface_base","*"); mesh.setPosition(Vec3(i,j,j)); // Synchronize creation of the mesh on all slaves Unigine::Syncker::Master::createNode(mesh); meshes.append(mesh); } } return 1; }
- Implement logic, which allows loading the world for all of the slaves on the master side, and call it in the init() function after the Syncker initialization.
You can skip this step and load the world on each slave individually.
void sync_world() { // load slave world Unigine::Syncker::Master::systemCall(~0,"engine.console.run",("world_load " + engine.world.getName())); // waiting for the slave world int ret[0]; float begin = clock(); while(clock() - begin < 2.0f) { int is_loaded = 1; Unigine::Syncker::Master::systemCall(~0,"engine.world.isLoaded",(),ret); foreach(int i = 0; ret) is_loaded &= i; if(is_loaded) break; } }
- Then, specify the shutdown logic for the console and Syncker:
// my_project.cpp on a master computer int shutdown() { // Run a console command on all slave applications, if necessary. // The command being called is run from a system script of slaves. Unigine::Syncker::Master::systemCall(~0,"engine.console.run",("world_quit")); // Shut down the console Unigine::Syncker::Master::Console::shutdown(); // Shut down Syncker. Unigine::Syncker::Master::shutdown(); return 1; }
- And synchronization logic in the script render() function. Such logic allows you to specify all nodes (and materials, if required), which have to be synchronized.
Detailed function descriptions can be found here.
// my_project.cpp on a master computer int render() { // Synchronize frame information Unigine::Syncker::Master::syncFrame(); // Synchronize player position Unigine::Syncker::Master::syncPlayer(); // Synchronize render parameters Unigine::Syncker::Master::syncRender(); // Synchronize nodes Unigine::Syncker::Master::syncNodes(meshes); // End of the frame Unigine::Syncker::Master::syncEnd(); return 1; }
If you run the same world script on the master and slave sides, you need to wrap your code around with #ifndef DEFINE_NAME ... #else ... #endif as follows:
#include <core/unigine.h>
// logic to run on the master
#ifndef DEFINE_NAME
#include <core/systems/syncker/syncker_master.h>
#include <core/systems/syncker/syncker_master_console.h>
ObjectMesh meshes[0];
// init() function defined above
// shutdown() functions defined above
// render() function defined above
#else
// logic to run on a slave
int init() {
PlayerSpectator camera = new PlayerSpectator();
camera.setPosition(Vec3(2.0f,0.0f,1.5f));
camera.setDirection(Vec3(-1.0f,0.0f,-0.5f));
engine.game.setPlayer(camera);
return 1;
}
#endif
Running Syncker
After the script is implemented, you need to run it on the slaves and master computers as follows:
- Run all slave applications that will be synchronized with the master.
For that, specify the following option on the slave application start-up to override a default editor script with the Syncker script for slaves. In case you use the same world script on the master and slave sides, specify also the required definition:
Do not forget to add other required start-up arguments.main_x86 -data_path "../" -editor_script "core/systems/syncker/syncker_slave.cpp" -extern_define "DEFINE_NAME"
-
Load the world for each slave if you didn't implement the loading logic in the script:
You can also perform this command on the application start-up (see the previous step):
world_load my_project/my_project
-console_command "world_load my_project/my_project"
- If the editor on slave applications is not loaded, load it via the following console command:
If you open the console, you will see that a slave application listens on two ports for packets from a master (this default values can be left unchanged):
editor_load
---- Syncker::Slave ---- Name: unknown UDP: 8890 TCP: 8891
- Run the my_project script on the main computer to be used as the master.
After that, in the console of the connected slaves you will see the following:
---- Syncker::Master ---- Address: 192.168.0.255 UDP: 8890 TCP: 8891
- Change the broadcasting network address of the master:
And reload the world by using the world_reload. After that, in the master console you will see messages that slaves have connected:
master_address xxx.xxx.xxx.xxx
Connection to "xxx.xxx.xxx.xxx" accepted in 0.00 seconds
Configuring Displays
After launching your application on the master and slave computers, you need to configure a viewport of each slave depending on the screen configuration:
- If your screen configuration is represented by a grid of monitors, you can simply follow the instructions specified in the Changing Screen Configuration chapter of the main article on Syncker.
-
In case your screen configuration is represented as a mesh, set it for all of the connected slaves via the master_mesh console command.
Make sure that the name of each mesh surface matches the name of the corresponding slave.To set the slave name, run the slave_name console command:
master_mesh <mesh_name>
slave_name <name>