Replication System - vux0303/typescript-ECS-framework GitHub Wiki
Usage
web-based games, nodejs server, simulation run on both client and server
Idea
ECS allow our game code break into to separate and clean chunk of two chunk of code: data and systems. The idea behind this replication system is synchronize data between clients and server.
A typical multiplayer is where all clients and server share a level, so level's data should be identical on all sides. Major different in data on character states and control on each clients which is not implemented to sync them. Overall, this system is just an experiment on the idea.
Workflow
This system provide:
- Properties decorator: decide which properties of a component will be sync.
- MessageProcessSystem: associate with each decorate, decide what process a data record go though before delivering.
- RepAttachmentComponent component: decide which entity will be sync.
- ReplicationSystem: system to register by your admin.
- repConfig: system configuration.
Decorator
Example using a decorator to have position interpolated when receiving message from server.
class Position extends ecs.Component {
@serverToClient({isBroadcast, processors:[]}, {processors:[Interpolator]})
x: number = 0;
@serverToClient({isBroadcast, processors:[]}, {processors:[Interpolator ]})
y: number = 0;
}
There are two decorator for this system:
- @clientToServer: sent data on change, on client env, with client deliver options and server receive options
- @serverToclient: sent data on change, on server env, with server deliver options and client receive options
For now, deliver options and receive option are just processors. Except for deliverOption of @serverToclient allow you to chose to broadcast the message or not.
Some important notes that these two decorator can not use on a same properties, which will cause an infinite feedback loop. The system will throw error to prevent that happen.
In real cases, client sent input and server return position, so there no reason to use them together. Also you can work around by using additional data.
Decorator work on ecs.Singleton
.
Decorator don't work on reference types or getter/setter. There also no point in using getter and setter in ECS, all properties should be public.
MessageProcessSystem
MessageProcessSystem
allow you to define a processor that you can use in your properties decorator.
At the end of each frame, all changing data will stored into an object called content
. This object then passing through all processors that was assigned.
Each processor receive the content
object by overriding process
method. content
actually contains data of all others processors, so to know which entries of content
will be process by an particular processor, a second parameter keys
provide indexes to data entries that belong to that processor.
class Interpolator extends MessageProcessSystem {
process(content: any, keys: string[]) {
//keys are indexes to data entries that belong to this processor
//you can iterate over all data entries for this processor by this
//content are collect at runtime so Typescript type system can not reach here.
keys.forEach((key) => {
content[key]
})
//if this system is used to processing received data, you can access current value of a property by following method to make comparison and resolve conflict
this.getKeyCurrentValue(key);
}
}
This system are supposed to collect what to be sent, there are no networking implementation provided so you have to do it yourself. After passing through all processors, final payload will be given to a processor to sent over network.
Define this processor just as same as others. But in this one, you will pay attention to other override method.
class MessageShipSystem extends MessageProcessSystem {
shipMessageAtClient(content: any) {
//content are payload to send to server
};
shipMessageAtServer(personalMsg: any, broadcastContent: any) {
//to get payloads for a particular client
personMsg.forEach((clientID) => {
let personalContent = personMsg[clientID];
})
//broacastContent are payload to send to all client
}
}
shipMessageAtClient
run at environment where repConfig.repEnv
are set to repEnv.Client
.
shipMessageAtServer
run at environment where repConfig.repEnv
are set to repEnv.Server
.
You also have to add this processor to the repConfig
object before initiate an admin:
repConfig.messageShipSystem = MessageShipSystem;
RepAttachmentComponent
Decorators only mark properties of a component class. To synchronize a component instance, add a RepAttachmentComponent
to entities that you wish to synchronize their components.
ReplicationSystem
will execute logic on this component so don't forget to register this system as your last systems.