Files
cocos_creator_framework/assets/Script/sync/ArrayReplicator.ts
2022-08-14 22:20:14 +08:00

286 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}