logo
Video Call
On this page

Real-time Multi-user State Synchronization


Feature Overview

Starting from version 3.0.0, ZEGO Express SDK has added a real-time multi-user state synchronization feature, providing an ordered, high-frequency, low-latency, and large-scale state synchronization service to help developers quickly achieve real-time information synchronization capabilities such as player positions, actions, and avatars in virtual gameplay, while supporting 10,000 users online simultaneously in a single scene.

In large virtual worlds, users generally do not need to perceive distant scenes or remote users. ZEGO provides AOI (Area Of Interest) capability to reduce the acquisition of information outside the user's visible range, greatly reducing customer traffic costs, user-side traffic, and performance consumption.

Concept Explanation

  • Scene: Users need to log in to a scene first, and only users who enter the same scene can synchronize state information.
  • AOI (Area of interest) range: The square size of the area of interest to the user, generally the user's visible range in a virtual scene. This range follows the user's position in real-time and only synchronizes remote user information within the AOI range.

Application Scenarios

  • Virtual office, virtual exhibitions, virtual social networking, virtual KTV, and other metaverse scenarios.
  • General scenarios that require ultra-high frequency, low latency, and large-scale synchronization of information or control commands.

Prerequisites

Warning

Using this service will incur corresponding fees. Please contact ZEGO business personnel for specific fee information.

Before implementing state synchronization, please ensure:

  • You have contacted ZEGO Technical Support for special packaging and enabled the state synchronization service.
  • You have integrated ZEGO Express SDK in your project.
  • You have created a project in the ZEGOCLOUD Console and applied for a valid AppID and AppSign. For details, please refer to Console - Project Management.

Implementation Flow

1 Create Engine

Call the createEngine interface, pass the obtained AppID and AppSign into the parameters "appID" and "appSign", and create an engine singleton object. The engine currently only supports creating one instance at a time. Beyond that, it will return null.

/** Define SDK engine object */
ZegoExpressEngine engine;

ZegoEngineProfile profile = new ZegoEngineProfile();
/** Please obtain through official website registration, format is 123456789L */
profile.appID = appID;
/** 64 characters, please obtain through official website registration, format is "0123456789012345678901234567890123456789012345678901234567890123" */
profile.appSign = appSign;
/** General scenario access */
profile.scenario = ZegoScenario.DEFAULT;
/** Set app's application object */
profile.application = getApplication();
/** Create engine */
engine = ZegoExpressEngine.createEngine(profile, null);

2 Create Range Scene Module

Call the createRangeScene interface to create a range scene instance. Currently only supports creating one instance at a time. Beyond that, it will return null.

/** Define range scene object */
ZegoRangeScene rangeScene;

/** Create range scene */
rangeScene = engine.createRangeScene();

3 Listen to range scene event callbacks

According to needs, call the ZegoRangeScene.setEventHandler interface to set the range scene event handler, used to listen to range scene state, login state, enter/leave AOI notifications, etc. Call the ZegoRangeSceneItem.setEventHandler interface to set the range scene item management class event handler, used to listen to events such as items entering/leaving the AOI range, item binding state changes, and item status and command updates.

/** Set range scene event callback */
rangeScene.setEventHandler(new IZegoRangeSceneEventHandler() {
    @Override
    public void onSceneStateUpdate(ZegoRangeScene rangeScene, ZegoSceneState state, int errorCode) {
        super.onSceneStateUpdate(rangeScene, state, errorCode);
    }

    @Override
    public void onEnterView(ZegoRangeScene rangeScene, ZegoUser user, ZegoPosition position) {
        super.onEnterView(rangeScene, user, position);
    }

    @Override
    public void onLeaveView(ZegoRangeScene rangeScene, String userID) {
        super.onLeaveView(rangeScene, userID);
    }

    @Override
    public void onUserStatusUpdate(ZegoRangeScene rangeScene, String userID, ZegoPosition position, int channel, byte[] status) {
        super.onUserStatusUpdate(rangeScene, userID, position, channel, status);
    }

    @Override
    public void onUserCommandUpdate(ZegoRangeScene rangeScene, String userID, ZegoPosition position, int channel, byte[] command) {
        super.onUserCommandUpdate(rangeScene, userID, position, channel, command);
    }

    @Override
    public void onCustomCommandUpdate(ZegoRangeScene rangeScene, byte[] command) {
        super.onCustomCommandUpdate(rangeScene, command);
    }
});

/** Set range scene item management class event callback */
rangeScene.getRangeSceneItem().setEventHandler(new IZegoRangeSceneItemEventHandler() {
    @Override
    public void onItemEnterView(ZegoRangeScene rangeScene, long itemID, int capacity, ZegoPosition position, ArrayList<String> userList) {
        super.onItemEnterView(rangeScene, itemID, capacity, position, userList);
    }

    @Override
    public void onItemLeaveView(ZegoRangeScene rangeScene, long itemID) {
        super.onItemLeaveView(rangeScene, itemID);
    }

    @Override
    public void onItemBindUpdate(ZegoRangeScene rangeScene, long itemID, String userID) {
        super.onItemBindUpdate(rangeScene, itemID, userID);
    }

    @Override
    public void onItemUnbindUpdate(ZegoRangeScene rangeScene, long itemID, ArrayList<String> userList) {
        super.onItemUnbindUpdate(rangeScene, itemID, userList);
    }

    @Override
    public void onItemStatusUpdate(ZegoRangeScene rangeScene, long itemID, ZegoPosition position, int channel, byte[] status) {
        super.onItemStatusUpdate(rangeScene, itemID, position, channel, status);
    }

    @Override
    public void onItemCommandUpdate(ZegoRangeScene rangeScene, long itemID, ZegoPosition position, int channel, byte[] command) {
        super.onItemCommandUpdate(rangeScene, itemID, position, channel, command);
    }
});

4 Login to Scene

Call the loginScene interface and pass in scene parameters: sceneID, user, position, broadcastMode to log in to the scene.

Warning
  • Within the same AppID, ensure that userID is globally unique. It is recommended that developers set it to a meaningful value and associate userID with their own business account system.
  • userID cannot be empty, otherwise it will cause login scene failure.
/** Login scene parameters */
ZegoSceneParam param = new ZegoSceneParam();
/** Create user */
ZegoUser user = new ZegoUser(userID, userName);
/** Set user's scene coordinates, motion orientation, camera orientation */
ZegoPosition position = new ZegoPosition()
for (int i = 0; i < 3; ++i) {
    position.coordinate[i] = coordinate[i];
    position.motionOrientation.axisForward[i] = rotateMatrixFront[i];
    position.motionOrientation.axisRight[i] = rotateMatrixRight[i];
    position.motionOrientation.axisUp[i] = rotateMatrixUp[i];
    position.cameraOrientation.axisForward[i] = rotateMatrixFront[i];
    position.cameraOrientation.axisRight[i] = rotateMatrixRight[i];
    position.cameraOrientation.axisUp[i] = rotateMatrixUp[i];
}
/** Set scene ID */
param.sceneID = sceneID;
/** (Optional) Configure template ID */
param.templateID = template_id;
param.user = user;
param.position = position;
/** Set user login scene broadcast mode */
param.broadcastMode = ZegoBroadcastMode.ALL;
rangeScene.loginScene(param, new IZegoRangeSceneLoginSceneCallback() {
    @Override
    public void onLoginSceneCallback(int errorCode, ZegoSceneConfig config) {
    }
});
Warning

If you need to customize templates, please refer to Server API - Scene Template Configuration.

5 Synchronize State

Update user state and user commands through updateUserStatus and updateUserCommand interfaces. Receive state information such as remote user positions and commands within the AOI range through onUserStatusUpdate and onUserCommandUpdate callbacks.

Note
  • User state (status, S) is a full update, user command is an incremental update (command, C). User state can be calculated from a previous user state through a series of user commands.
  • The model for user state updates should be Si->Ci->Ci+1->Ci+2->...Ci+j->S(i+1)->C(i+1)+1->C(i+1)+2->...C(i+1)+k->S(i+2)->C(i+2)->...
  • The state synchronization service will ensure that incremental updates (commands) between two full states (status) Si and Si+1 arrive in order.
/** Set user's scene coordinates, motion orientation, camera orientation */
ZegoPosition position = new ZegoPosition()
for (int i = 0; i < 3; ++i) {
    position.coordinate[i] = coordinate[i];
    position.motionOrientation.axisForward[i] = rotateMatrixFront[i];
    position.motionOrientation.axisRight[i] = rotateMatrixRight[i];
    position.motionOrientation.axisUp[i] = rotateMatrixUp[i];
    position.cameraOrientation.axisForward[i] = rotateMatrixFront[i];
    position.cameraOrientation.axisRight[i] = rotateMatrixRight[i];
    position.cameraOrientation.axisUp[i] = rotateMatrixUp[i];
}
/** Update user state */
byte[] status = new byte[10];
int errorCode = rangeScene.updateUserStatus(position, 0, status);

/** Update user command */
byte[] command = new byte[10];
int errorCode = rangeScene.updateUserCommand(position, 0, command);

6 (Optional) Get user count in scene, get user list within AOI range

According to needs, you can call getUserCount and getUserListInView interfaces to get the user count in the scene and the user list within the AOI range.

/** Get user count in scene */
rangeScene.getUserCount(new IZegoRangeSceneGetUserCountCallback() {
    @Override
    public void onGetUserCountCallback(int errorCode, int count) {
    }
});

/** Get user list within AOI range */
rangeScene.getUserListInView(new IZegoRangeSceneGetUserListInViewCallback() {
    @Override
    public void onGetUserListInViewCallback(int errorCode, ArrayList<String> userList) {
    }
});

7 (Optional) Item state and competitive lock

Through item state synchronization, you can implement item movement and placement gameplay in virtual scenes, such as placement systems, ball kicking, etc. Through item locks, you can implement item grabbing gameplay, such as equipment picking, chair grabbing, etc., which can be set according to needs.

  1. Create item

According to needs, call the createItem interface to create items in the scene.

/** Create item parameters */
ZegoItemParam param = new ZegoItemParam();
/** Set item's scene coordinates, motion orientation */
ZegoPosition position = new ZegoPosition();
for (int i = 0; i < 3; ++i) {
    position.coordinate[i] = coordinate[i];
    position.motionOrientation.axisForward[i] = rotateMatrixFront[i];
    position.motionOrientation.axisRight[i] = rotateMatrixRight[i];
    position.motionOrientation.axisUp[i] = rotateMatrixUp[i];
}
/** Set item ID */
param.itemID = itemID;
/** Set item maximum binding number limit */
param.capacity = capacity;
param.position = position;
/** Set create item mode */
param.createMode = ZegoCreateItemMode.NO_BIND;
rangeScene.getRangeSceneItem().createItem(param, new IZegoRangeSceneCreateItemCallback() {
    @Override
    public void onCreateItemCallback(int errorCode, long itemID) {
    }
});
  1. Bind item

If you need to operate on an item (i.e., update item state commands), you first need to call bindItem to bind the item.

rangeScene.getRangeSceneItem().bindItem(itemID, new IZegoRangeSceneBindItemCallback() {
    @Override
    public void onBindItemCallback(int errorCode, long itemID) {
    }
});
  1. Synchronize item state

Update item state and item commands through updateItemStatus and updateItemCommand interfaces. Receive state information such as remote item positions and commands within the AOI range through onItemStatusUpdate and onItemCommandUpdate callbacks.

Note
  • Before updating item state and commands, you need to bind the item first.
  • Item state (status, S) is a full update, item command is an incremental update (command, C). Item state can be calculated from a previous item state through a series of item commands.
  • The model for item state updates should be Si->Ci->Ci+1->Ci+2->...Ci+j->S(i+1)->C(i+1)+1->C(i+1)+2->...C(i+1)+k->S(i+2)->C(i+2)->...
  • The state synchronization service will ensure that incremental updates (commands) between two full states (status) Si and Si+1 arrive in order.
/** Set item's scene coordinates, motion orientation */
ZegoPosition position = new ZegoPosition()
for (int i = 0; i < 3; ++i) {
    position.coordinate[i] = coordinate[i];
    position.motionOrientation.axisForward[i] = rotateMatrixFront[i];
    position.motionOrientation.axisRight[i] = rotateMatrixRight[i];
    position.motionOrientation.axisUp[i] = rotateMatrixUp[i];
}
/** Update item state */
byte[] status = new byte[10];
rangeScene.getRangeSceneItem().updateItemStatus(itemID, position, 0, status, new IZegoRangeSceneUpdateItemStatusCallback() {
    @Override
    public void onUpdateItemStatusCallback(int errorCode, long itemID) {
    }
});

/** Update item command */
byte[] command = new byte[10];
rangeScene.getRangeSceneItem().updateItemCommand(itemID, position, 0, command, new IZegoRangeSceneUpdateItemCommandCallback() {
    @Override
    public void onUpdateItemCommandCallback(int errorCode, long itemID) {
    }
});
  1. Unbind item

If you do not need to operate on an item (i.e., update item state commands), you can call unbindItem to unbind the item.

rangeScene.getRangeSceneItem().unbindItem(itemID, new IZegoRangeSceneUnbindItemCallback() {
    @Override
    public void onUnbindItemCallback(int errorCode, long itemID) {
    }
});
  1. Destroy item

According to needs, call destroyItem to destroy items in the scene.

rangeScene.getRangeSceneItem().destroyItem(itemID, new IZegoRangeSceneDestroyItemCallback() {
    @Override
    public void onDestroyItemCallback(int errorCode, long itemID) {
    }
});

8 Logout of Scene

Call the logoutScene interface to log out of the scene.

/** Logout of scene */
rangeScene.logoutScene(new IZegoRangeSceneLogoutSceneCallback() {
    @Override
    public void onLogoutSceneCallback(int errorCode) {
    }
});

9 Destroy range scene module

When the range scene module is no longer needed, you can call the destroyRangeScene interface to destroy the range scene module.

engine.destroyRangeScene(rangeScene);

10 Destroy Engine

When ZEGO Express SDK is no longer needed, you can call destroyEngine to destroy the engine.

ZegoExpressEngine.destroyEngine(null);

Previous

Mass-Scale Range Audio and Video

Next

Room Connection Status