KeyFC欢迎致辞,点击播放
资源、介绍、历史、Q群等新人必读
KeyFC 社区总索引
如果你找到这个笔记本,请把它邮寄给我们的回忆
KeyFC 漂流瓶传递活动 Since 2011
 

[M] Prelude to K.O. (3)

[ 9698 查看 / 8 回复 ]

K.O.(KeyFC Open Translation Toolset) 是目前正在开发的“开放版本”汉化工具的代号。
其中的汉化部分工具试验性的采用了代号为Chobits Application Server(CAS)的架构。
这个结构的特点为,程序全部由基于消息传递的模块动态组合而成。

上回谈了一下CAS的内部结构,今天谈谈CAS如何工作——CAS消息、传递和应用。

--- CAS消息 ---

前面讲到了,因为CAS消息传递是处于进程内的,因此可以将“消息”抽象为指针。

那么一个指针能传递什么样的信息呢?
我的回答是:“给我一个指针,我就能传递整个世界” (听起来有点耳熟 :D)
想想,在程序的世界里面,有什么是指针不能表达的呢?

当然,要做有用的事情,自然需要按照一定的方法去做,不然就和自我崩溃没有区别了...
CAS的消息同样需要遵循一定的格式,但是为了能够兼容各种不同的应用模块和编程习惯,又不能对消息的格式作过多的限制。
软件工程学帮助CAS实现了这一目的:可扩展格式而又不限制实现的结构——接口(Interface)
(其实,我已经不是第一次提到接口了。上回谈到将CAS队列管理和消息传递模块化的时候,就提到模块和消息总线之间的交互就是接口提供的。)

将一个CAS消息定义为一个指向“CAS基本消息接口"(BaseMSG Interface)的指针,这样总线传递的消息和消息的实例也就分离开来。
消息总线接受任何实现了这个“基本消息接口”的消息,因此一切疑难杂症也就迎刃而解。

“CAS基本消息接口"(BaseMSG Interface) 规定了一些传递消息必须的方法,比如:给出消息的接受方的编号,消息传递时的一些参数,以及一个可以自定义意义的整数(通常用来区分消息的种类)。
在编写应用模块的时候,编程者可以在“基本消息接口”的基础上任意定义自己想要传递的消息的接口,以及用任何方法实现这些接口,而不用担心与总线的兼容性问题。
作为参考,在CAS SDK(开发套件)中提供了一个基本的实现。通过继承这个对象,就不用自己实现“CAS基本消息接口"的功能了。


(图1) 注: 消息实例中蓝色的部分表示实现“基本消息接口"的方法。

--- 消息的传递 ---

发送一条消息的基本流程非常简单,同时也具有很强的可调控性:
0. 首先需要知道接受方的标识(消息接口编号)。
    这很简单,只需要通过消息接口提交给总线对方的名称和GUID就行了。
1. 生成一个消息实例,填充好自定义的信息,然后填上接受方的标识,以及一些参数(下面详细解释)。
2. 调用消息接口的发送方法,完事。

根据选择的发送方法(上回里介绍过),这条消息可能会先存放在总线的某个消息队列,然后被转发到接受方的消息队列里;或者直接放入接受方的消息队列里。
编程人员不仅可以选择发送方法,而且可以通过消息的参数详细的控制消息传输的过程:

一般情况下,消息会一路顺畅的到达接受方的队列,不论什么参数都一样,没什么有趣的事情发生。
但是当总线发生拥堵时,配置的参数就会产生效果了:

* 默认的配置下,当消息在一个队列阻塞一段(预设定的)时间还不能被送达,这个消息将被队列抛弃;
(如果模块选择亲自送达,那么将会得知这一事件;如果是总线转交,当总线队列阻塞,发送方会得知,但如果接受方队列阻塞,消息则会被"安静"的抛弃)

* 如果消息的适时性很高,发送方可以配置消息完全不等待,一旦阻塞,立刻抛弃。(处理结果的通知情况同前)

* 如果消息是具有时间性的,超过一定时间就没有意义了(比如音/视频帧),发送方可以为消息增加一个(高精度)作废时间戳。
发送的过程会一直尝试投递消息直到截止时间之后,如果消息还未送达就会被抛弃;同时如果截止时间前消息没有被接受方提取,此消息也会被抛弃。
(这种配置的消息不能交给总线转发,只能亲自送达,因为过长的等待时间将会影响转发效率。如果消息在发送过程里被抛弃,发送方会得知;如果在提取的时候被抛弃,那么是"安静"的)

* 如果消息无论如何都要被送达,发送方也可以选择相应配置,这样在消息送进对方的队列之前,传送的过程不能返回(除非异常发生: 比如接受方的队列被释放)。
(同前一种配置一样,这种配置的消息不能交给总线转发,只能亲自送达;如果异常发生,消息被抛弃,发送方会得知)



另外,消息的接口化带来了另外一个(可以算)有用的特性,就是消息是被引用计数(Reference Counted)的,因此,发送的消息将一直存在到其没有被引用为止。
当消息经过接口进入总线内部后,总线就"获取"一份此消息的引用;当消息被提取时这一份引用也就传递到了接受方;
当消息被总线"抛弃"时,总线的操作仅仅是将其引用计数减去一份;同理,接受模块处理完消息之后,也仅仅需要减去一份引用。
这也就意味着,如果发送方在发送消息之前就保留了一份或者更多的引用的话,消息将会一直存在,而且发送方也一直可以访问(不过需要注意同步问题)。
这带来了一些值得注意的应用方法:
1. 发送方如果希望在投递失败的时候对企图发送的消息进行其它处理,则应该预先保留一份引用;否则等发送失败的结果返回的时候,消息已经被自动释放(不过有时这种处理方法更方便,根据个人的编程风格和应用目的而定);
2. 通过保留引用,发送模块也可以达到与接受模块的线程直接交互的效果。一些问题通过纯粹消息传递模式实现效率低下,这种方法则可以越过CAS消息传递,回到多线程交替操作的经典模式(当然,就需要自己解决同步问题了);
3. 通过保留引用,还可以高效的实现"流水线"处理。例如,加密一块数据时,一个挂接了数据的消息可以依次被各个模块处理,其间不用频繁的被释放、复制(当然,需要统一每个模块接受的消息的接口)。

--- 消息的应用 ---

基于接口的CAS消息具有开放、灵活的特征,因此,可以被方便的扩展,赋予多种多样的应用。
仅支持"基本消息接口"的消息只能达到有限的目的,但是CAS 开发套件中提供了丰富的扩展消息接口和参考实现:

1. 可重定向消息。
这个是很基本的扩展。默认的消息接口仅对传递的信息提供"读取"的方法。
这对于防止编程者误操作(意外的向接受到的消息里面写一些东西,然后又忘记更改了消息,导致接下来处理过程出错)有用,但也意味着消息是单向的,处理后只能抛弃。
但是有一些的时候消息需要被回复。因此可重定向消息提供了更改消息接受方、参数、标志数的方法。

2. 跨模块调用(Inter-Module Call, IMC)消息。
通过使用这个消息,可以实现类似于"远过程调用"(RPC)的功能,只不过RPC是跨进程的,IMC只是跨线程的。
具体实现很简单,将一个"事件"(Event)和一系列辅助数据(如返回值)、方法(如"等待"、"通知")捆绑到一个基本消息上。
消息发送方发送消息,然后调用"等待"方法;接受方收到消息并处理完成后,调用"通知"方法;这时发送方将会从"等待"方法返回,并获得一个返回值,就像刚调用了一个函数一样。
IMC消息受到总线的支持,当消息不能送达,被抛弃的时候,等待中的发送方将会返回并获得一个错误返回值。(目前这种支持是内部特定的,但是我会通过事件抽象化将其改写为通用的)

事实上,除了直接继承,接口还提供了另一种很实用的扩展方法——代理(delegation)。
CAS 开发套件中,IMC的实现是独立于消息的;因此任何消息实例,包括自定义的,都可以通过代理的方法将"绑定"IMC接口,成为一个支持IMC操作的消息,仅需要写几行代码。

3. 可复制消息
这种消息接口定义了一个“复制”方法,只要调用,就可获得一份"几乎"一样的复制品(每份消息都有一个"复制编号"表明其是第几份复制品)。
这种消息是为了实现消息“广播”(Boradcast)和“组播”(Unicast)而定制的。(目前尚未实现)
在我的设计中,“广播”和“组播”不是总线原生功能,而是以服务模块的方式实现的(遵循最大模块化宗旨 :P):
该模块中可动态定义多个"组",每个组接受各个模块的"订阅";发送给一个组的消息(当然,必须提供"可复制消息"接口),订阅的所有模块都会收到该消息的副本。

4. 可序列化消息
这种消息接口定义了"序列化"和"反序列化"方法。
顾名思义,序列化方法将一个消息的实例转换为一串数据,可以被记录在任何媒体上;反序列化则将数据实例化。
这种消息为将来CAS消息总线的跨进程、跨平台通讯奠定了基础。
(当然,这个功能的实现应该是另外N年以后的事情了。)
(不过想象一下,向远程机器的一个CAS模块发送一个请求,就像同本地模块通讯一样,是一件多么美好的事情啊... =v=)

这些仅仅是抛砖引玉了,在实际应用中相信有更多种类复杂的功能要求;而且我相信CAS的开放、灵活的消息接口应该能够胜任这些要求。

------

当然任何东西都不可能完美,修订和改错是必定的。
当CAS总线 / 消息接口被更改后,有可能会和旧的模块不再兼容,需要它们被重新编译(或者,更坏的情况下,修改)。
这就涉及到下一次将要谈的问题: CAS模块的结构、封装和应用程序的组装。
最后编辑Prz 最后编辑于 2007-05-13 13:23:24
本主题由 管理员 深海蓝空 于 2007/5/13 13:28:46 执行 设置精华/取消 操作
分享 转发
飛べない翼に、意味はあるんでしょうか?
TOP

回复:[M] Prelude to K.O. (3)

知识果然是人类最能“晒”的东西啊......
很期待这个工具的开发完成,虽然看不大懂........
潜水一年,注册一年的论坛浮云类
TOP

回复:[M] Prelude to K.O. (3)

- -||||

好复杂...好可怕....这个是汉化工具?偶不懂程序的.....纯支持了
我们的双手 就是为了  紧紧抓住什么 而存在的 .....
TOP

回复:[M] Prelude to K.O. (3)

果然开始利用COM技术了...不多说了,期待下一讲.
TOP

回复:[M] Prelude to K.O. (3)

Chobits Application Server……真是相当有爱的人型电脑架构啊……

“这就涉及到下一次将要谈的问题: CAS模块的结构、封装和应用程序的组装。”
原来还是连载……

除了“给我一个指针,我就能传递整个世界”,其他一概不懂……
TOP

回复:[M] Prelude to K.O. (3)

介绍产品可不能这样
We are the Borg.You will be http://tinypic.com/xylfl.Resistance is futile
TOP

回复:[M] Prelude to K.O. (3)

现在才看到这东西=。=

先感谢汉化组的辛劳,以前以为楼主只是开玩笑说说罢了,因为后来帖都不知删哪去了……

还是说点自己的看法吧

很怀疑CAS的必要,想必KO只是个修改剧本和配置之类的工具,改完后游戏时是不用祭出来当外挂的

既然不需要在游戏运行时同时运行,即时响应消息有什么用

如果是KO自身修改剧本时需要传递消息,就像楼主说的IMC,那么传递什么消息需要比总线消息更多的功能呢

看了下KO的汉化剧本的功能,看不出有需要同步的部分,各个模块间都是必须严格串行的(暂不考虑提取剧本同时写入更改的剧本,因为一般人只会在第一次运行时提取剧本),简单地传递消息就好了,而且不是要等待消息传到,而是一定要传到,不然修改不就无效了=。=

另外clannad剧本只有4.6M,没必要用到数据库吧,即使为了以后的需要,那里也可以设计为一个接口,需要用数据库或保持成文件什么再调用相应模块(即Facade模式)

还有那个消息的“投递”,感觉这些模块只要告诉目标模块做什么,动作完成没,准备好没就行了。楼主说的媒体文件的播放、动画特效的渲染应该集中在1、2个模块内(假如需要分别处理音、视频)完成,其中也没必要传递大量消息

啰嗦了一堆,其实只想说,不必要的复杂只会导致项目的失败。如果不是需要这个构架开发一系列东西,完全可以舍弃的。软件构架是为了降低复杂度、提高重用性设计的,不是为了提高复杂度=。=

个人观点是除了调试外,这个构架基本没用(不过VS也能调试这种东西,只是偶没写过这种东西;另外TDD的方式感觉对调试帮助更大),而且只会是KO的牵绊。如果只是为了试验的话,我就不说了

『智代after PS2汉化移植完毕』www.keakon.cn
TOP

回复:[M] Prelude to K.O. (3)

Prz的架构本身是很好的,更靠近人类思维,在未来的硬件支持的多线程中会有更好的发展.有许多看似很简单的道理,实现起来会遇到很多细节问题,有编程经验的人现在就能看出一些了.
目前这种架构还很难取代普通的编程思维,而且双核也尚未普及,运行效率也不会超过一般架构.所以现在大多数程序都不是这样设计的.看看任务管理器中每个进程的线程数就知道.看起来只有svchost和System进程的线程数比较多,说明WinNT内核有可能类似CAS架构,每个服务看起来必须至少常驻一个线程,调用服务的某个功能可能是发送一个消息来实现的吧.
当然汉化工具是很没必要用这种架构的,数据库看起来也是大材小用.不过做个AVG引擎可以用这个架构尝试一番,编程思维才会渐渐转移.目前应该还没有一个商业AVG引擎用这种架构吧.
最后期望能开放CAS的SDK,当然开源CAS内核最好.
最后编辑dwing 最后编辑于 2007-05-14 09:28:30
TOP

回复:[M] Prelude to K.O. (3)

全部看不明白...
Misha酱果然是晒命级别接近MAX的人- -|
只能理解"接口" "指针"等无谓关键词的某人路过...
新心情。新世界。
TOP