Skip to main content

Component Network Programming Overview

The Flexi Engine provides a network programming framework based on component objects, which can simplify the complexity of multiplayer game programming to compile both the Client and the Server with only a single copy of code. This document outlines the concepts used in multiplayer game programming.

Client-Server Network Model

Flexi Engine uses a Client-Server model, for short C/S mode, to implement online game development.

The C/S mode is a network architecture, rather than an actual computer device, which distinguishes the Client from the Server. For online games, each Game Client can send a request to a Game Server located on the network. After receiving the request, the Server performs a certain feature or other tasks. The Game Client and Server share state information in the game world through the network.

The Game Server, where multiplayer games actually happen, is authoritative in online games for maintaining the most correct and realistic game state. The Client will remotely control the character on the Server, requesting it to perform game operations through the network. The Server will synchronize the game state information to each Client, and notify the corresponding GameObject, GameObject behavior, and the values that different variables should have. Using this information enables the Client to simulate what is happening in the game.

Note: The Client and the Server are connected through network devices to form a system that interacts with each other in online games.

client-server

Basic Network Programming Concepts

The following will introduce the concept of driving network Gameplay in the Flexi Engine in detail to help understand multiplayer game development and build games.

Network Modes and Server Types

There are various network modes and server types in the Flexi Engine for online game development and operation in a variety of situations.

Network ModeDescription
ClientRun as a Game Client which will not run Server game logic.
Listen ServerRun as a Server for online multiplayer games. It accepts remote Client connections and has local players directly on the Server. This mode is typically used for temporary cooperative and competitive multiplayer games.
Member ServerRun as a Server for online multiplayer games. It accepts remote Client connections, but has no local players. Graphics, sound, input, and other features available for players are abandoned. This mode is typically used in massively multiplayer online games that require more persistence and security.
Entry Server-Member ServerContain Member Server features and additionally carry an Entry Server to provide a secure access solution for massively multiplayer online games.
StandaloneRunning in Standalone mode, which can be considered as running both Server and Client logic. Connections from remote Clients are not accepted.

GameObject Network Synchronization

GameObject Network Synchronization refers to the replication process of GameObject state information between different machines. Most GameObjects will not enable replication by default and will perform all features locally. Call the SetAddNetScene function of the GameObject class to enable the GameObject network synchronization.

Synchronization FeatureDescription
Create and DestroyWhen the Server creates a GameObject with network synchronization, it will automatically generate the same GameObject network proxy on all connected Clients. While the game is running, information will also be synchronized to these remote proxies. Destroying the GameObject on the Server will automatically destroy the network proxies on all connected Clients.
Movement SynchronizationIf the GameObject enables Movement Synchronization, or calls the SetNetMovement function in C++, the position, rotation and speed will be automatically synchronized.
Property SynchronizationWhen the values of property variables marked with NetProperty change, it will automatically synchronize these values from the corresponding GameObject to the network proxy.
Remote Procedure CallRPC is a special function transmitted to a specific machine in an online game. No matter which machine originally called the RPC, its implementation runs only on the target machine. Such RPCs can be specified as Server (run only on the Server), Client (run only on the GameObject owning Client), or run on all machines connected to the session.

Note: Some commonly used features of characters are usually not synchronized by the network.

  • Skeletal Mesh and Static Mesh Components
  • Material
  • Animation Tree
  • Particle System
  • Sound
  • Physics Object

Such features run independently on all Clients. However, synchronizing the variables of above visual elements ensures that all Clients have the same information to simulate network characters and control types in roughly the same way.

Property Variable Network Synchronization

The NetProperty specifier inside the corresponding FX_PROPERTY macro can be used in C++ to specify them as network synchronization. When the value of a variable on the GameObject changes, its information will be automatically sent from the GameObject on the Server to the network proxy on the Client.

Network Synchronization Callback Functions

The NetPropertyNotify specifier of the FX_PROPERTY macro for a variable can be used in C++ to specify synchronization callback functions and the functions to be called when the GameObject receives a network synchronization change for a property variable. NetPropertyNotify will be triggered locally when a variable is updated. Using NetPropertyNotify to implement callbacks when variables are updated can reduce network overhead compared to using RPCs for that property synchronization is a fixed overhead and no additional network overhead will be added.

Network Synchronization Code Descriptions

The multiplayer demo uses network synchronization of property variables to handle the player's HP.

class LFPSDemoPawn : public LRole
{
DECLARE_LCLASS(LFPSDemoPawn, LRole, COMPILED_IN_FLAGS(0))

public:
LFPSDemoPawn();
virtual ~LFPSDemoPawn();

// CurrentHp
FX_PROPERTY(Type = float, GetFunc = GetHp, SetFunc = SetHp, Name = CurrentHp,
NetProperty, NetPropertyNotify = OnHpChangedNotify)
float m_fCurrentHp;

}

void LFPSDemoPawn::OnHpChangedNotify()
{
g_pCore->ExecAsyncProc("share\\ui\\form_state_bar.lua", "refresh_hp",
CVarList() << m_fCurrentHp << m_fMaxHp);
}

Relevance - Vision Management

The relevance between GameObjects determines whether network synchronization is required. Not all GameObjects on the Server will exist in a specific Client at a certain time. The vision management of the Server will eliminate GameObjects that are considered irrelevant, which can save bandwidth and improve efficiency. If the GameObject not owned by the player is far away, it will be considered irrelevant and no network synchronization will be performed. However, irrelevant GameObjects will exist on the Server and affect the game state, but will not send information to the Client until the player approaches. Relevance can be controlled by setting the Visual Range of the GameObject using the SetServerVisualRange function.

Note: Whether to enable network synchronization and Server visual range can be set in the Property panel of the GameObject.

2023-02-17_145714

Network Control Types

NET_CONTROL_TYPE will determine whether the GameObject is controlled by the Server or the Client during the online game. NET_CONTROL_TYPE::SERVER_CONTROLLED is considered to be a Server that controls a GameObject and can replicate its information to other Client machines. It is tracked by the LocalControlType variable, which can take the following values:

Network Control TypeDescription
NONEGameObject has no character in the online game and will not be copied.
SERVER_CONTROLLEDGameObject is controlled by the Server and will replicate its information to network proxies on other machines.
CLIENT_NON_PLAYER_CONTROLLEDGameObject is a network proxy, fully controlled by GameObject on the Server. Most GameObjects, such as interactive objects in online games, will be displayed as non-player-controlled on the Client.
CLIENT_PLAYER_CONTROLLEDGameObject is a network proxy that can perform some features locally, but will receive corrections from the Server. It is usually reserved for GameObjects directly controlled by the player, such as Pawn.

Remote Procedure Calls

Remote Procedure Call, for short RPC, is also referred to as a network message processing process. Based on network objects and object reflection, we can send network messages to GameObject and Component objects and process them on the other end. The specific call of the game logic will happen on the specific end connected to the network according to the RPC type. Here are three types of RPCs as follows.

RPC TypeDescription
ServerThe Client sends an RPC request to the Server using the function LScene::ServerRemoteFunction.
ClientThe Server sends an RPC request to the Client using the function LScene::ClientRemoteFunction. If the GameObject has no connection, this logic will not be executed.
Client-BroadcastThe Server sends an RPC broadcast request to all connected Clients of the GameObject using the function LScene::ClientRemoteFunction.

C++ Code Examples

  1. Use the FX_METHOD specifier to specify the function as an entity method.

  2. Use LScene::ServerRemoteFunction to initiate an RPC to the Server, which will call the specified entity method on the Server's GameObject;

  3. Use LScene::ClientRemoteFunction to initiate an RPC to the Client, which will call the specified entity method on the Client's GameObject.

// LMyGameObject
class LMyGameObject : public LGameObject
{
public:
void CallServerFunction();
void CallClientFunction();
FX_METHOD()
void MyFunction_Server(int n);
FX_METHOD()
void MyFunction_Client(int n);
}

void LMyGameObject::CallServerFunction()
{
auto* pScene = GetScene();
CVarList var;
var.AddInt(10);
pScene->ServerRemoteFunction(this, "MyFunction_Server", var);
}

void LMyGameObject::CallClientFunction()
{
auto* pScene = GetScene();
CVarList var;
var.AddInt(10);
pScene->ClientRemoteFunction(this, "MyFunction_Client", var);
}

void LMyGameObject::MyFunction_Server(int n)
{
// Server gameplay logic
}

void LMyGameObject::MyFunction_Client(int n)
{
// Client gameplay logic
}