mirror of
https://github.com/wyb10a10/cocos_creator_framework.git
synced 2026-05-21 09:45:54 +08:00
286 lines
10 KiB
TypeScript
286 lines
10 KiB
TypeScript
import ReplicateMark from "./ReplicateMark";
|
||
import { createReplicator } from "./ReplicatorFactory";
|
||
import { Consturctor, IReplicator } from "./SyncUtil";
|
||
|
||
export type SimpleType = number | string | boolean | bigint;
|
||
|
||
/**
|
||
* 数组对象某个版本的数据
|
||
*/
|
||
interface ArraySimpleVersionInfo {
|
||
version: number;
|
||
data: SimpleType;
|
||
}
|
||
|
||
/**
|
||
* SimpleArrayReplicator 高效的数组对象同步器
|
||
* 用于同步number、string、boolean、bigint等基础类型的数组对象
|
||
*/
|
||
export class SimpleArrayReplicator implements IReplicator {
|
||
/** 最后一个有数据变化的版本号 */
|
||
private lastVersion: number = 0;
|
||
/** 最后一次检测的版本号 */
|
||
private lastCheckVersion: number = 0;
|
||
/** 数组长度发生变化的最后一个版本 */
|
||
private lastLengthVersion: number = 0;
|
||
private data: ArraySimpleVersionInfo[];
|
||
private target: SimpleType[];
|
||
|
||
constructor(target: SimpleType[], mark?: ReplicateMark) {
|
||
this.target = target;
|
||
this.data = [];
|
||
this.makeUpDataArray(target, mark);
|
||
}
|
||
|
||
makeUpDataArray(target: SimpleType[], mark?: ReplicateMark) {
|
||
for (let i = 0; i < target.length; i++) {
|
||
this.data.push({ version: 0, data: target[i] });
|
||
}
|
||
}
|
||
|
||
getTarget() {
|
||
return this.target;
|
||
}
|
||
|
||
setTarget(target: any): void {
|
||
this.target = target;
|
||
}
|
||
|
||
genDiff(fromVersion: number, toVersion: number): any {
|
||
if (toVersion < fromVersion) {
|
||
return false;
|
||
}
|
||
let needScan = this.lastCheckVersion < toVersion;
|
||
// 如果不需要扫描,且最终版本小于fromVersion,则直接返回
|
||
if (!needScan && fromVersion > this.lastVersion) {
|
||
return false;
|
||
}
|
||
// 如果需要扫描,先判断长度是否相等
|
||
if (needScan) {
|
||
let diff: SimpleType[] = [this.target.length];
|
||
let lengthChanged = this.data.length != this.target.length;
|
||
if (lengthChanged) {
|
||
this.lastLengthVersion = toVersion;
|
||
}
|
||
if (this.data.length > this.target.length) {
|
||
// 删除多余的data
|
||
this.data.splice(this.target.length, this.data.length - this.target.length);
|
||
}
|
||
for (let i = this.target.length; i < this.target.length; i++) {
|
||
if (this.data.length <= i) {
|
||
this.data.push({ version: toVersion, data: this.target[i] });
|
||
diff.push(i, this.target[i]);
|
||
} else if (this.data[i].data != this.target[i]) {
|
||
this.data[i].version = toVersion;
|
||
this.data[i].data = this.target[i];
|
||
diff.push(i, this.target[i]);
|
||
}
|
||
}
|
||
this.lastCheckVersion = toVersion;
|
||
// 没有任何变化
|
||
if (!lengthChanged && diff.length == 1) {
|
||
return false;
|
||
}
|
||
this.lastVersion = toVersion;
|
||
return diff;
|
||
} else {
|
||
// 遍历data,过滤出版本范围内的数据
|
||
let diff: SimpleType[] = [this.target.length];
|
||
for (let i = 0; i < this.data.length; i++) {
|
||
if (this.data[i].version >= fromVersion && this.data[i].version <= toVersion) {
|
||
diff.push(i, this.data[i].data);
|
||
}
|
||
}
|
||
// 没有任何变化
|
||
if (this.lastLengthVersion < fromVersion && diff.length == 1) {
|
||
return false;
|
||
}
|
||
return diff;
|
||
}
|
||
}
|
||
|
||
applyDiff(diff: any): void {
|
||
if (diff instanceof Array) {
|
||
// 如果长度减少,删除多余的对象
|
||
let length = diff[0];
|
||
if (length < this.target.length) {
|
||
this.target.splice(length, this.target.length - length);
|
||
}
|
||
// 遍历修改或push
|
||
for (let i = 1; i < diff.length; i += 2) {
|
||
let index = diff[i];
|
||
let value = diff[i + 1];
|
||
if (index >= this.target.length) {
|
||
this.target.push(value);
|
||
} else {
|
||
this.target[index] = value;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
getVersion(): number {
|
||
return this.lastVersion;
|
||
}
|
||
}
|
||
|
||
interface ArrayObjectVersionInfo {
|
||
version: number;
|
||
index: number;
|
||
data: IReplicator;
|
||
}
|
||
|
||
/**
|
||
* ArrayReplicator 数组对象同步器
|
||
* 用于同步对象类型的数组,例如自定义的ReplicateClass、cc.Vec2、cc.Color、cc.Rect等
|
||
*/
|
||
export class ArrayReplicator<T extends Consturctor> implements IReplicator {
|
||
private data: ArrayObjectVersionInfo[];
|
||
private target: Array<T>;
|
||
private lastVersion: number = 0;
|
||
private lastCheckVersion: number = 0;
|
||
private ctor: Consturctor;
|
||
|
||
constructor(target: Array<T>, mark?: ReplicateMark) {
|
||
let objMark = mark?.getObjMark();
|
||
if (objMark?.Constructor) {
|
||
this.ctor = objMark?.Constructor;
|
||
} else {
|
||
// 如果没有指定Constructor,则target数组不得为空
|
||
this.ctor = target[0];
|
||
}
|
||
this.target = target;
|
||
this.data = [];
|
||
this.makeUpDataArray(target, mark);
|
||
}
|
||
|
||
getTarget() {
|
||
return this.target;
|
||
}
|
||
|
||
setTarget(target: any): void {
|
||
this.target = target;
|
||
}
|
||
|
||
pushData(data: T, version: number, mark?: ReplicateMark) {
|
||
let replicator = createReplicator(data, mark);
|
||
if (replicator) {
|
||
this.data.push({
|
||
version,
|
||
index: this.data.length + 1,
|
||
data: replicator
|
||
});
|
||
} else {
|
||
console.error("ArrayReplicator.pushData createReplicator error:", data);
|
||
}
|
||
}
|
||
|
||
makeUpDataArray(target: Array<T>, mark?: ReplicateMark) {
|
||
for (let i = 0; i < target.length; ++i) {
|
||
this.pushData(target[i], this.lastVersion, mark);
|
||
}
|
||
}
|
||
|
||
genDiff(fromVersion: number, toVersion: number): any {
|
||
if (toVersion < fromVersion) {
|
||
return false;
|
||
}
|
||
|
||
// 长度都为0时,不发生变化
|
||
if (this.target.length == 0 && this.data.length == 0) {
|
||
return false;
|
||
}
|
||
|
||
let needScan = this.lastCheckVersion < toVersion;
|
||
// 如果不需要扫描,且最终版本小于fromVersion,则直接返回
|
||
if (!needScan && fromVersion > this.lastVersion) {
|
||
return false;
|
||
}
|
||
|
||
let ret: Array<any> = [];
|
||
if (needScan) {
|
||
ret.push(this.target.length);
|
||
for (let i = 0; i < this.target.length; i++) {
|
||
// 如果数组长度小于当前索引,则直接添加
|
||
if (this.data.length <= i) {
|
||
this.pushData(this.target[i], toVersion);
|
||
ret.push(i, this.data[i].data.genDiff(-1, toVersion));
|
||
} else {
|
||
let data: IReplicator = this.data[i].data;
|
||
// 如果由于数组的插入与删除,导致对象下标变化,则需要重新绑定
|
||
if (this.data[i].index != i) {
|
||
data.setTarget(this.target[i]);
|
||
this.data[i].index = i;
|
||
}
|
||
let diff = data.genDiff(fromVersion, toVersion);
|
||
// 如果不是新插入的,则需要有diff才进入ret
|
||
if (diff) {
|
||
ret.push(i, diff);
|
||
}
|
||
}
|
||
}
|
||
this.lastCheckVersion = toVersion;
|
||
} else {
|
||
// 先记录长度,再比较数据,这里不再扫描target,直接使用data
|
||
ret.push(this.data.length);
|
||
for (let i = 0; i < this.data.length; i++) {
|
||
let data: IReplicator = this.data[i].data;
|
||
// 如果version大于fromVersion,则表示为新插入的,必须添加到ret
|
||
if (this.data[i].version > fromVersion) {
|
||
ret.push(i, data.genDiff(-1, toVersion));
|
||
} else {
|
||
// 元素有变化则更新
|
||
let diff = data.genDiff(fromVersion, toVersion);
|
||
if (diff) {
|
||
ret.push(i, diff);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果没有差异(ret的长度为1),且长度相同,则返回false
|
||
if (ret.length == 1 && ret[0] == this.data.length) {
|
||
return false;
|
||
}
|
||
|
||
this.lastVersion = toVersion;
|
||
// 如果data的长度大于target的长度,则删除data的多余部分
|
||
if (this.data.length > this.target.length) {
|
||
this.data.splice(this.target.length, this.data.length - this.target.length);
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
applyDiff(diff: any): void {
|
||
if (diff instanceof Array) {
|
||
// 如果长度减少,删除多余的对象
|
||
let length = diff[0];
|
||
if (length < this.target.length) {
|
||
this.target.splice(length, this.target.length - length);
|
||
}
|
||
// 遍历修改或push
|
||
for (let i = 1; i < diff.length; i += 2) {
|
||
let index = diff[i];
|
||
let value = diff[i + 1];
|
||
// 如果需要创建新的对象
|
||
if (index >= this.target.length) {
|
||
// TODO: 如果有构造函数参数,如何传递?
|
||
// 暂时只能使用默认构造函数,数值的变化可以使用applyDiff更新
|
||
this.target.push(new this.ctor());
|
||
let replicator = createReplicator(this.target[index]);
|
||
if (replicator) {
|
||
this.data.push({
|
||
version: this.lastVersion,
|
||
data: replicator,
|
||
index: index
|
||
});
|
||
}
|
||
}
|
||
this.data[index].data.applyDiff(value);
|
||
}
|
||
}
|
||
}
|
||
|
||
getVersion(): number {
|
||
return this.lastVersion;
|
||
}
|
||
} |