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.
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 Mode | Description |
---|---|
Client | Run as a Game Client which will not run Server game logic. |
Listen Server | Run 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 Server | Run 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 Server | Contain Member Server features and additionally carry an Entry Server to provide a secure access solution for massively multiplayer online games. |
Standalone | Running 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 Feature | Description |
---|---|
Create and Destroy | When 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 Synchronization | If the GameObject enables Movement Synchronization, or calls the SetNetMovement function in C++, the position, rotation and speed will be automatically synchronized. |
Property Synchronization | When 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 Call | RPC 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.
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 Type | Description |
---|---|
NONE | GameObject has no character in the online game and will not be copied. |
SERVER_CONTROLLED | GameObject is controlled by the Server and will replicate its information to network proxies on other machines. |
CLIENT_NON_PLAYER_CONTROLLED | GameObject 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_CONTROLLED | GameObject 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 Type | Description |
---|---|
Server | The Client sends an RPC request to the Server using the function LScene::ServerRemoteFunction. |
Client | The 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-Broadcast | The Server sends an RPC broadcast request to all connected Clients of the GameObject using the function LScene::ClientRemoteFunction. |
C++ Code Examples
Use the FX_METHOD specifier to specify the function as an entity method.
Use LScene::ServerRemoteFunction to initiate an RPC to the Server, which will call the specified entity method on the Server's GameObject;
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
}