diff --git a/assets/Script/example/SyncExmaple.ts b/assets/Script/example/SyncExmaple.ts index 4684f83..5fa8a5d 100644 --- a/assets/Script/example/SyncExmaple.ts +++ b/assets/Script/example/SyncExmaple.ts @@ -1,6 +1,6 @@ import { Component, Label, _decorator, view, director, Node, RichText, tween, Tween, math, randomRange, Vec3, Quat, ModelComponent, Color } from "cc"; -import { ReplicatedOption } from "../sync/ReplicateMark"; -import { applyDiff, genDiff, getReplicateObject, makeObjectReplicated, replicated } from "../sync/SyncUtil"; +import { getReplicateMark, ReplicatedOption } from "../sync/ReplicateMark"; +import { applyDiff, genDiff, getReplicateObject, getReplicator, makeObjectReplicated, replicated } from "../sync/SyncUtil"; const { ccclass, property } = _decorator; @@ -42,7 +42,11 @@ export default class SyncExample extends Component { {Name : '_lscale', Setter: 'setScale'}, {Name : '_lpos', Setter: 'setPosition'}, {Name : '_euler', Setter: 'eulerAngles'}]; - makeObjectReplicated(this.leftNode, { SyncProperty : syncProperty}); + //makeObjectReplicated(this.leftNode, { SyncProperty : syncProperty}); + + let mark = getReplicateMark(this.leftNode); + mark.setObjMark({SyncProperty : syncProperty}); + getReplicator(this.leftNode, true, mark); } onSyncClick() { @@ -50,7 +54,12 @@ export default class SyncExample extends Component { let diffPos = getReplicateObject(this.leftNode.position).genDiff(this.lastVersion, this.lastVersion + 1); let diffRot = getReplicateObject(this.leftNode.eulerAngles).genDiff(this.lastVersion, this.lastVersion + 1); let diff = {scale : diffScale, position: diffPos, eulerAngles: diffRot};*/ - let diff = getReplicateObject(this.leftNode).genDiff(this.lastVersion, this.lastVersion + 1); + /*let diff = getReplicateObject(this.leftNode).genDiff(this.lastVersion, this.lastVersion + 1); + if (diff) { + applyDiff(diff, this.rightNode); + this.lastVersion += 1; + }*/ + let diff = getReplicator(this.leftNode).genDiff(this.lastVersion, this.lastVersion + 1); if (diff) { applyDiff(diff, this.rightNode); this.lastVersion += 1; diff --git a/assets/Script/sync/CocosReplicator.ts b/assets/Script/sync/CocosReplicator.ts new file mode 100644 index 0000000..f16760a --- /dev/null +++ b/assets/Script/sync/CocosReplicator.ts @@ -0,0 +1,40 @@ +/** + * 实现Cocos一些内置类型的Replicator + */ + +import { Vec3 } from "cc"; +import { IReplicator } from "./SyncUtil"; + +export class CCVec3Replicator implements IReplicator { + private data: Vec3; + private target: Vec3; + private version: number; + + constructor(target: Vec3) { + this.target = target; + this.data = target.clone(); + this.version = 0; + } + + genDiff(fromVersion: number, toVersion: number): any { + if (toVersion < fromVersion) { + return false; + } + if(this.target.equals(this.data)) { + return false; + } + this.data.set(this.target); + this.version = toVersion; + return [this.target.x, this.target.y, this.target.z]; + } + + applyDiff(diff: any): void { + if (diff) { + this.target.set(diff[0], diff[1], diff[2]); + } + } + + getVersion(): number { + return this.version; + } +} diff --git a/assets/Script/sync/CocosReplicator.ts.meta b/assets/Script/sync/CocosReplicator.ts.meta new file mode 100644 index 0000000..e0ece60 --- /dev/null +++ b/assets/Script/sync/CocosReplicator.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "6ffb06ed-a5b5-4393-9b7e-d86e36f318ef", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/Script/sync/DiffScaner.ts b/assets/Script/sync/DiffScaner.ts index 26bc66f..888fe3f 100644 --- a/assets/Script/sync/DiffScaner.ts +++ b/assets/Script/sync/DiffScaner.ts @@ -3,7 +3,8 @@ * 2022-01-16 by 宝爷 */ -import { getReplicateMark } from "./ReplicateMark"; +import ReplicateMark, { getReplicateMark } from "./ReplicateMark"; +import { createReplicator } from "./ReplicatorFactory"; import { IReplicator, ReplicateProperty } from "./SyncUtil"; export class ReplicateScanner implements IReplicator { @@ -15,32 +16,43 @@ export class ReplicateScanner implements IReplicator { private target: any = null; /** 所有发生过变化的数据,属性名 : 变化参数 */ private dataMap: Map = new Map(); - + /** 构造函数 */ - constructor(target: any) { + constructor(target: any, mark?: ReplicateMark) { this.target = target; - this.makeUpDataMap(target); + this.makeUpDataMap(target, mark); } /** * 生成dataMap * @param target 要扫描的目标Object */ - makeUpDataMap(target: any) { - // 获取类的同步标记 - let mark = getReplicateMark(target.prototype, false); - if(mark) { + makeUpDataMap(target: any, mark?: ReplicateMark) { + if (mark === undefined) { + // 获取类的同步标记 + // 当我们不希望这个类的所有实例都支持同步,只是希望某些实例支持同步时,mark由外部传入 + mark = getReplicateMark(target.__proto__, false); + } + if (mark) { let marks = mark.getMarks(); for (let [name, info] of marks) { let data = info.def || target[name]; // 如果def是函数,则执行函数返回一个新的对象(比如嵌套的对象) - if(typeof info.def === "function") { + if (typeof info.def === "function") { data = info.def.call(target[name], target, info); + } else if (data instanceof Object) { + if (info.option?.ObjectOption) { + let subMark = getReplicateMark(data, true); + subMark.setObjMark(info.option.ObjectOption); + data = createReplicator(target[name], subMark); + } else { + data = createReplicator(target[name]); + } } - let rp : ReplicateProperty = { data, version: 0}; - if(info.option) { + let rp: ReplicateProperty = { data, version: 0 }; + if (info.option) { // 这里还可能做点其他事情 - if(info.option.Setter) { + if (info.option.Setter) { rp.setter = info.option.Setter; } } @@ -67,7 +79,8 @@ export class ReplicateScanner implements IReplicator { let ret: any = {}; // 遍历生成Diff for (let [name, property] of this.dataMap) { - let setter = property.setter || name; + // let setter = property.setter || name; + let setter = name; // 判断是否实现了genDiff接口 if ("genDiff" in property.data) { let diff = property.data.genDiff(fromVersion, toVersion); @@ -76,7 +89,7 @@ export class ReplicateScanner implements IReplicator { } } else { if (needScan) { - if(property.data != this.target[name]) { + if (property.data != this.target[name]) { property.data = this.target[name]; ret[setter] = property.data; property.version = toVersion; @@ -97,13 +110,15 @@ export class ReplicateScanner implements IReplicator { applyDiff(diff: any) { let keys = Object.keys(diff); for (let key of keys) { - // 如果是setter函数,则执行函数 - if (typeof this.target[key] === "function") { - this.target[key](diff[key]); - } else { - let property = this.dataMap.get(key); - // 判断是否实现了applyDiff接口 - if (property instanceof Object && "applyDiff" in property.data) { + let property = this.dataMap.get(key); + if (property) { + // 如果指定了setter,则优先执行setter方法来应用Diff + if (property.setter && typeof this.target[property.setter] === "function") { + // diff[key]可能是数组,当它是数组的时候,可以得到下面这样的效果 + // 如setPosition,this.target.setPosition(diff[key][0], diff[key][1], diff[key][2]) + this.target[property.setter].call(this.target[key], diff[key]); + } else if (property.data instanceof Object && "applyDiff" in property.data) { + // 判断是否实现了applyDiff接口 property.data.applyDiff(diff[key]); } else { this.target[key] = diff[key]; @@ -111,7 +126,7 @@ export class ReplicateScanner implements IReplicator { } } } - + /** * 获取当前版本号 * @returns 最后一个有数据变化的版本号 diff --git a/assets/Script/sync/DiffTrigger.ts b/assets/Script/sync/DiffTrigger.ts index 6b31347..0833781 100644 --- a/assets/Script/sync/DiffTrigger.ts +++ b/assets/Script/sync/DiffTrigger.ts @@ -3,6 +3,7 @@ * 2022-01-16 by 宝爷 */ +import ReplicateMark from "./ReplicateMark"; import { IReplicator, ReplicateProperty } from "./SyncUtil"; export class ReplicateTrigger implements IReplicator { @@ -19,6 +20,10 @@ export class ReplicateTrigger implements IReplicator { /** 在outter中的属性名 */ private outterKey: string = ""; + /** 构造函数 */ + constructor(target: any, mark?: ReplicateMark) { + this.target = target; + } /** * 当一个属性被重新赋值时回调,即 target.key = v时 * 1. 对比数值是否有发生变化,有则更新dataMap diff --git a/assets/Script/sync/ReplicateMark.ts b/assets/Script/sync/ReplicateMark.ts index c9838c5..30b2b78 100644 --- a/assets/Script/sync/ReplicateMark.ts +++ b/assets/Script/sync/ReplicateMark.ts @@ -1,5 +1,6 @@ /** * 属性复制标记,描述一个类的哪些属性需要被同步,如何同步等 + * 支持多级属性嵌套 * 2022-01-16 by 宝爷 */ @@ -7,6 +8,14 @@ import { ReplicateNotify } from "./SyncUtil"; export const REPLICATE_MARK_INDEX = "__repMrk__"; +/** + * 对象的属性同步类型 + */ +export enum ReplicateType { + REPLICATE_SCAN = 0, + REPLICATE_TRIGGER = 1, +} + /** * 属性同步选项 */ @@ -17,8 +26,11 @@ export interface ReplicatedOption { Setter?: string; /** 属性同步条件 */ Condiction?: number; - /** 同步回调 */ + /** 同步回调,在属性被Apply的时候调用 */ Notify?: ReplicateNotify; + /** 如果该属性是一个Object类型的,可以指定该Object的ObjectReplicatedOption + * 对于未指定的Object,则默认会为其创建不带参数的IReplicator,默认为ScannerReplicator */ + ObjectOption?: ObjectReplicatedOption; } /** @@ -29,8 +41,13 @@ export interface ObjectReplicatedOption { SyncProperty?: ReplicatedOption[]; /** 指定跳过哪些属性的同步 */ SkipProperty?: string[]; + /** 同步类型 */ + Type?: ReplicateType; } +/** + * 属性同步信息 + */ export interface ReplicateMarkInfo { /** 默认值 */ def?: any, @@ -95,6 +112,20 @@ export default class ReplicateMark { public setObjMark(objMark: ObjectReplicatedOption) { this.objMark = objMark; + if (objMark) { + // 遍历markMap,如果在objMark.SkipProperty中,则删除 + this.markMap.forEach((value, key) => { + if (objMark.SkipProperty && objMark.SkipProperty.indexOf(key) >= 0) { + this.markMap.delete(key); + } + }); + // 遍历objMark的SyncProperty,添加到markMap + if (objMark.SyncProperty) { + objMark.SyncProperty.forEach((item) => { + this.addMark(item.Name, this.cls[item.Name], item); + }); + } + } } public getObjMark(): ObjectReplicatedOption | undefined { diff --git a/assets/Script/sync/ReplicatorFactory.ts b/assets/Script/sync/ReplicatorFactory.ts new file mode 100644 index 0000000..e1e0f2d --- /dev/null +++ b/assets/Script/sync/ReplicatorFactory.ts @@ -0,0 +1,22 @@ +import { ReplicateScanner } from "./DiffScaner"; +import { ReplicateTrigger } from "./DiffTrigger"; +import ReplicateMark, { ReplicateType } from "./ReplicateMark"; +import { IReplicator } from "./SyncUtil"; + +export function createReplicator(target: any, mark?: ReplicateMark) : IReplicator | null{ + // 根据target的类型和mark参数决定创建哪种类型的Replicator + if (target instanceof Array) { + // TODO: 数组类型 + return null; + } else if (target instanceof Object) { + if (mark) { + let objMark = mark.getObjMark(); + if (objMark && objMark.Type == ReplicateType.REPLICATE_TRIGGER) { + return new ReplicateTrigger(target, mark); + } + } + return new ReplicateScanner(target, mark); + } else { + return null; + } +} \ No newline at end of file diff --git a/assets/Script/sync/ReplicatorFactory.ts.meta b/assets/Script/sync/ReplicatorFactory.ts.meta new file mode 100644 index 0000000..0879067 --- /dev/null +++ b/assets/Script/sync/ReplicatorFactory.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "7da7f748-536b-4156-b909-83a6f522b2c2", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/Script/sync/SyncUtil.ts b/assets/Script/sync/SyncUtil.ts index cb6797a..acd2acb 100644 --- a/assets/Script/sync/SyncUtil.ts +++ b/assets/Script/sync/SyncUtil.ts @@ -12,7 +12,7 @@ */ import { ReplicateScanner } from "./DiffScaner"; -import { getReplicateMark, ObjectReplicatedOption, ReplicatedOption } from "./ReplicateMark"; +import ReplicateMark, { getReplicateMark, ObjectReplicatedOption, ReplicatedOption } from "./ReplicateMark"; /** 属性变化回调 */ export type ReplicateNotify = (target: any, key: string, value: any) => boolean; @@ -50,13 +50,14 @@ export function getReplicateObject(target: any, autoCreator: boolean = false): R * 每个实例会有一个自己的ReplicateObject * @param target 要指定的对象 * @param autoCreator 找不到是否自动创建一个? + * @param mark 同步标记 * @returns */ -export function getReplicator(target: any, autoCreator: boolean = false): IReplicator { +export function getReplicator(target: any, autoCreator: boolean = false, mark?: ReplicateMark): IReplicator { let ret: IReplicator = target[REPLICATOR_INDEX]; if (!ret && autoCreator) { // TODO: 这里应该使用IReplicator的工厂 - ret = new ReplicateScanner(target); + ret = new ReplicateScanner(target, mark); Object.defineProperty(target, REPLICATOR_INDEX, { value: ret, enumerable: false,