有关影子实例系统的基本设计可以参考 影子实例(Shadow Instance) 章节。 接下来的章节将会默认你知道影子实例的基本状况,进而对如何实现的进行讲解。
为了实现对象的同步,我们需要有一个机构(Module)对这些对象(Object)进行管理。这个机构就叫做 对象库。在沙箱内(Worker端)和沙箱外(Host端)都需要有一个对象库,而且这两个库需要时刻保持同步, 这才能保障有效的影子实例实现。
由于我们需要区分不同的Object,一个便利的方法是通过给对象颁发“身份证”(Unique Identification) 而这个办法ID的机构就是对象库。
对象库会根据:对象类型,生成时间,库序列和随机数生成一个唯一的ID,如果这个ID与库内其余对象没有冲突, 那么ID就会输出。 由于Object一旦注册,库序列就会发生变化,ID就不会产生冲撞。每一毫秒可以产出4096 个非注册ID和无限多个注册ID。生成时间是为了调试便利和增加唯一性。
具体算法请参考 Runtime.generateId()
有了ID就需要一个可以入库管理关系的机制。在本质上,对象库就是一个大哈希表(Hash Map)对应ID于对象 以方便迅速查找和派发event。
Runtime定义了基础的可以被管理的对象,必须提供以下三个接口:
getId()
: 返回该对象的唯一IDserialize()
: 返回一个可以表示这个对象的对象,不得超过JSON允许的种类子集。dispatchEvent(event)
: 接口用于派发Event。
提供了如下接口的Object就都可以注册入对象库,前提是它们的ID不与前者冲撞。由于生成ID的方式,我们可以
保证新的ID不会与库中既在的ID冲突,所以只要使用generateId()
即可保障这个约束条件。
同时,同一个Object不能注册两次,也就是说同一个ID只能注册一次。
特殊对象包括播放器,舞台对象等。它们不是由ID派发而出的,因为它们总存在,所以由手写的ID产生。在
Kagerou Engine里面,分别使用了__player
,__root
和__self
。
在Runtime里面,我们的定义是,对于任何ID开头是两个下划线的,视其为特殊对象。这些特殊对象在
clear()
和 reset()
时不会被去掉,依然存在于对象库中。所以非常适合表示一些超脱于正常对象的常在
的对应关系。
对于特殊对象,也有特殊的派发方式,我们暂且不讨论。
对于普通的对象,Runtime会打开一个新的信道,监听有关此Object的信息。在信道上传输的信息则会被派发到 Object上。这个信道用于派发监听器,同时也用于接受消息,如外部摧毁Object的消息。