对于有些喜欢修改源代码的人,这里是一个指南的开端。我想到什么说什么,第一篇说说如何修改源代码让你用TM登录。
你不想使用QQ登录,想使用TM登录么?通过修改源代码,是很简单的。
其实TM和QQ的协议是一样的,虽然有些功能TM有QQ没有,但是基本上都不是服务器的限制。举个例子说,TM里面有个“隐身对某人可见”功能,命令是0x0024,虽然QQ里面没有这个功能,但是你如果真的通过QQ发送这个命令,服务器目前是不会拒绝的。
PS: 所以我一直觉得TM不过是界面改改罢了,里面的东西,照旧。
我们看一下QQ.java, 有这么一个常量
public static final char QQ_CLIENT = QQ_CLIENT_0E1B;
你把他改成
public static final char QQ_CLIENT = 0x0F0A;
然后你再运行,LumaQQ就成了LumaTM了
基本上啥也没干,是不是。QQ_CLIENT是版本标识,0x0F0A表示的是TM 2006新春版。TM和QQ基本上就这点差别。也许夸张了点。
你怎么知道修改了这个常量就是TM了?假设你有两个号码A和B,你用腾讯TM把A设成隐身对B可见。然后尝试用LumaQQ登录A,看看修改前后在B的那边有没有区别,就知道了。
但是LumaTM还是QQ界面?这...,这不指南才开始吗?哪有空把界面怎么改都一次说全了。
59.25.9.* 于 2007-07-19 06:02:46发表:
http://www.ringtones-dir.net/get/ ringtones site free. Free nokia ringtones here, Download ringtones FREE, Best free samsung ringtones. From website .
59.25.9.* 于 2007-07-19 06:02:41发表:
http://www.ringtones-dir.net/get/ ringtones site free. ringtones download: Free nokia ringtones here, Download ringtones FREE, Best free samsung ringtones. samsung ringtones From website .
59.25.9.* 于 2007-07-19 06:02:40发表:
http://www.ringtones-dir.net/download/ download ringtones. nokia ringtones: Free nokia ringtones here, Download ringtones FREE, Best free samsung ringtones. http://www.ringtones-dir.net/free/ [link=http://www.ringtones-dir.net]ring tones[/link] From site .
yorktown 于 2006-10-03 21:00:35发表:
有点难!
romeobh 于 2006-08-23 15:36:31发表:
CVS中,JQL再次承受了大量的重构,这次的目标是彻底分离协议层和网络层。重构已经完成,jql_net中的大量包都被转移到了 jql_protocol中,在jql_protocol中增加了一个edu.tsinghua.lumaqq.qq.net包,这里面基本都是一些接口,没有太多具体实现。而jql_net充当了这些接口的实现。所以jql_net现在是完全可选的了(如果有其他网络实现的话)。不像以前, jql_net里面混杂着一些和网络没有太多关系的代码。
现在你可以去实现你自己的网络层了,比如你不想用NIO,想用个传统IO。或者,你甚至可以实现一个"伪网络层",假装建立了连接,但是实际上回复包都是从什么数据库里面读出来的,这也可以做为调试的一种方法。
romeobh 于 2006-08-23 15:36:17发表:
开源项目大多数都缺乏文档,LumaQQ也如此。大部分原因是我比较懒,不想写这些玩意。但是注释里面有很多内容可以用来做为文档,如果能利用起来,可以弥补文档不够的问题。
要做文档,就有两个问题
1. 怎么样把源代码的注释整理成文档
2. 文档整理出来了之后,怎么保持更新,怎么维护,谁有这么多闲功夫来管它?
我没有这么多闲功夫,你们也不会有,也不指望你们有了。以前有些人自告奋勇的来帮我整理文档,确实整理了一些,精神是可嘉的,成果是看的见的。但是只解决了第一个问题,无法解决第二个问题。所以文档的真正问题是:你也许有精力把文档写出来,却没精力更新维护它。
这几天无聊,重新考虑了文档的问题,觉得JDK 1.5的Annotation特性,很适合来做这样的事情,而且可以比较好的解决问题1和问题2。同时,我可以顺便学习学习这方面的内容,值得一做。
所以我CVS中增加了一个LumaQQ_tools_2006项目。按照名字所示,是LumaQQ附加工具的项目。目前就只有文档自动生成工具在里面。我简单介绍一下这个工具如何工作。
为了方便自动生成文档,我在jql_protocol_2006中添加了一个包: edu.tsinghua.lumaqq.qq.annotation。这个包包含了一些自定义的Annotation,用来标注各个包和QQ事件。我不一一介绍了,这些内容比较简单。
有了Annotation,现在的问题就是怎么处理了。JDK 1.5中的apt工具是个还不错的玩意,它就是用来干这个事的,当然我们需要做一些编程的工作。基本上,就是需要实现一个 AnnotationProcessorFactory接口,实现一个AnnotationProcessor接口,实现一个 DeclarationVisitor接口。具体怎么做我不说了,在sun的网站上有介绍的文档,JDK 1.5的doc里面也有,或者google吧。
首先我自己写了个比较简单的HTML模板,就几个网页,比如一个包一个网页,一个QQ事件一个网页,这些模板都是用JET来做的(对于JET,参见以前的指南)。生成实际的网页,需要数据,那么我再把这些数据包装到不同的Bean中。比如为了生成某个包的网页,需要传递一个PacketBean对象。那么接下来的问题就是如何生成这些Bean了。这个就是DeclarationVisitor的工作了。这是一个两步的过程,首先我用一个 CollectParameterVisitor来收集生成文档所需的所有参数。然后再用一个GeneratorVisitor来执行实际的生成工作。就这样,ok了。
还没说怎么用这个工具,嗯,看看LumaQQ_tools_2006的build.xml文件,其缺省目标是build-manual,还用我说什么呢,直接ant一把就可以了。每次生成的时间会记录在文档中,就做为文档版本的标志了。
非常好,我没花太多时间,就改善了文档不足的问题,有不少好处。
1. 要改变文档的样式,或者添加一些什么内容,只要改改模板,修改一下相关代码,有必要的话加几个Annotation,再ant一把,就ok了
2. 代码中的注释得到了最大程度的重用,我不用额外写什么,文档就出来了。
3. 通过Annotation和apt工具,实现了文档内容和文档样式的分离,或者说,Model和View的分离。Annotation是Model,JET模板是View,那些代码是Controller.
当然了,用javadoc也可以做,只不过我赶时髦,用Annotation来玩。嗯,边玩边学,自娱自乐。
也许目前我写的网页模板还比较幼稚,不过既然框架已经搭出来了,以后想改就方便了。《LumaQQ开发者手册》,想下载的就去文档页看看吧。
romeobh 于 2006-08-23 15:36:02发表:
我们来了解一下自2006 M2之后,核心层有了什么样的变化
JQL里面有很多关键部件,先介绍一些概念
1. Porter,启动一个异步I/O循环,直接监听网络事件
2. Port, 代表了一个端口,或者说代表了一个连接,Port需要注册到Porter中,才能获得网络事件
3. Parser,代表了一个解析器,用来解析某个协议族的包
4. PacketHistory, 是包的缓冲,用来检测重复包
5. Processor,代表了一个处理器,用来处理某个协议族的包,继而触发QQ事件
6. QQClient,代表了一个QQ客户端
在2006 M2之前
1. 一个QQClient只有一个Porter
2. 一个Porter可以有多个Port
3. 在一个QQClient中,一个协议族只有一个Parser
4. 在一个QQClient中,一个协议族一个Processor
5. 一个QQClient只有一个PacketHistory
那么在2006 M2之后
1. 不变
2. 不变
3. 在一个Port中,一个协议族一个Parser,一个Port支持哪些协议族,通过PortPolicy指定
4. 不变
5. 一个Parser一个PacketHistory
所以关键的改变在3,5。通过3,使Port更加灵活。通过5,使包重复检测的机制更加灵活。其实,4也应该变一下,但是目前没需求,所以暂时不变。
曾经提过,这些改变是为了实现网络硬盘功能的关系。网络硬盘的协议和其他协议族有一个很大的不同就是:
网络硬盘协议包可以非常长,带来了解析上和处理上的新需求。在代码里面,每个Port都有一个发送和一个接收缓冲,其最大是65535,由于网络硬盘的包可以非常长,所以增加了DiskInPacketFragment类,用来表示一个接收包的片断,相应的Parser也需要做些修改。所以网络硬盘协议族的Parser和其他协议族的Parser在实现上有些差别。
romeobh 于 2006-08-23 15:35:45发表:
在对网络硬盘协议的分析中,发现有必要对JQL进行重构,以实现更灵活的协议建模和调试
主要改变在
1. 增加了PortPolicy类,隔离网络收发和包解析功能,简化了Port和协议之间的绑定操作
2. 简化了IParser接口,删除了很多不必要的方法,更加简洁
3. 细分调试包对象,使其对应于特定协议族
4. 由于PortPolicy的引入,导致了其他一些类的相应修改,比较琐碎,不提了
5. 细分超时事件,使其对应于特定协议族
6. 在Packet中增加getFamily方法,明确标识包所属协议族,抛弃以往使用header标识包协议族的方式
7. 不再允许Port直接触发QQ事件,降低了他们之间的耦合
重构无止境,但是目前这样已经能满足我增加网络硬盘功能的需要,所以暂时就这样。
romeobh 于 2006-08-23 15:35:30发表:
QQ的协议非常庞大,怎么统一的描述包的结构,是一个很麻烦的问题。腾讯的包是不是有个统一的准则,不清楚,当然从设计上来说,统一的当然好。
以前以为QQ的包就那么多,设计的简单化了。后来功能越加越多,就接触了一些没见过的包,发现类结构描述不了,所以只好改写,目前的结构是一个4层的模型,真的够用吗?目前来说还凑合
首先我们得了解一下QQ那么多包的共同点和不同点,才好了解JQL中的类结构。
1. 目前看来,QQ的包有包头,包体,包尾之分,但是包尾在某些协议族里面是个可选的玩意儿
2. 在某些协议族里面,包格式又有TCP和UDP之分,有些没有。抽象一点来说呢,UDP是简单的,随便定义什么格式都可以满足要求,TCP是连续的数据流,在连续的数据流里面解析包,就需要有包长度的描述。
3. 包体有全加密,不加密和部分加密的区别
4. 包有输入和输出包之分,不过在有些协议族里面这个区别不明显,甚至可以说输入和输出包是一样的格式
为了能够描述现有的包,并且能够为未知的新包提供兼容,目前类的层次有这样4层,相关源代码请查看edu.tsinghua.lumaqq.qq.packets包和子包
1. 一个顶层的基类Packet。它提供最基本的描述和很多抽象的方法。比如一些通用的字段,还有一些通用的方法,但是这个基类的大部分内容还是抽象方法,留待子类实现。Packet最重要的地方在于提供了一个基本的模型,就是把包的构造和解析过程分成了头,体,尾三部分。你可以找到putHead, putBody, putTail方法用于构造一个包,可以找到parseHead, parseBody, parseTail方法用于解析一个包。
2. 两个Packet的子类InPacket和OutPacket提供对输入和输出包的最基本封装。Packet类是通用的模型,其不涉及输入和输出包有什么不同,所以我们需要这两个子类来提供更具体的描述。InPacket很简单,没什么太多内容,因为解析包的过程是通用的,封装在了Packet中,所以 InPacket没有多少事做。主要是OutPacket多了不少专用于输出包的方法,比如重发次数,超时时间等等,还封装了输出包的填充过程。查看 OutPacket的fill方法,可以看到输出包的生成过程。
3. 每个协议族,分别包含了一个InPacket和OutPacket的子类,对于一个特定的协议族来说,包头包体包尾里面的很多共同点已经可以确定了,所以父类中的很多方法可以来实现了。
4. 最下面的一层自然就是具体的包了,继承各自协议族的输入输出基类,基本上他们需要实现的方法只有一两个,比如putBody和parseBody等。
实际上呢,目前是有5层的,在基本协议族里面,群是很大的一部分功能,群包有一些共同点,所有群包有一个基类的。不过呢,说它是4层设计更准确一些。
romeobh 于 2006-08-23 15:35:12发表:
LumaQQ里面用到了一些Wizard,比如在搜索的时候,在创建群的时候。JFace是带了一个Wizard框架的,用的就是它的,只不过稍微改了一下。
查看edu.tsinghua.lumaqq.ui.wizard,看到IModelBasedWizard.java,这里对IWizard做了两个扩展,第一个是采用一个model对象来保存wizard中的信息,所以,你看到了,MVC模式是无处不在的。
第二个扩展是加了个preNext,用来在点Next按钮之前做一些事情,这个需求源自对QQ的搜索功能的调查。QQ搜索的时候,有时候下一步并不是到下一个page,而是打开一个浏览器去网页上找了。JFace的wizard框架似乎是不能这样做,所以我加了一个preNext方法。为了让这个方法能够被触发,使用了WizardWindow类继承WizardDialog以便能插入preNext方法,WizardWindow还有一个目的:使 Wizard窗口成为独立窗口,而不是对话框(如果是对话框的话,主窗口最小化,它也会不见,所以要改成独立窗口的),这个主要是重载 getParentShell方法,让它返回null,虽然这个方法不是很推荐,但是似乎简单又方便。
其他的内容就没有什么奇怪了,按照JFace的标准框架走下来就可以了。可能有人觉得wizard那些按钮都是英文的不爽,这个可以通过修改jface的jar包里面的properties文件来改,你可以试试。
romeobh 于 2006-08-23 15:34:56发表:
对于写界面来说,很大的功夫都花在了写组件上。为了界面尽量好看点,我写了一些自绘的组件。之所以没去实现皮肤,那是因为太繁琐了,没必要。
在edu.tsinghua.lumaqq.widgets下面,都是LumaQQ用到的一些组件。这些组件你可以直接拿去用。
edu.tsinghua.lumaqq.widgets: 一些尚未归类的组件,主要是表情头像选择的那个窗口: ImageSelector.java。通过实现IImageSelectorAdvisor接口,可以指定窗口的一些样式和可选择的图片内容。
edu.tsinghua.lumaqq.widgets.mac: 这下面目前只有一个组件,Ring.java。从包名来看,这下面的组件是一些Mac风格的组件,如果你用过Mac,也许你对Mac的那个转来转去的 Busy指示器有点印象,Ring就是这个玩意。Ring.java负责处理通用的逻辑,转圈的方式是可以扩展的。缺省的实现是圆形的,如果你想要个其他形状的,比如说,正方形;那么扩展IBorderPainter和ISignPainter就可以了。具体可以参考缺省的圆形实现。
edu.tsinghua.lumaqq.widgets.menu: 这里面是一个自定义的菜单实现,没办法的产物。在Linux下面窗口置顶时,菜单出不来,只好自己写了一个简单菜单的实现。优点是想怎么画就怎么画,美观有所提高。缺点是带来了一个bug,不点一下窗口其他地方菜单不会消息,很难处理,现在也没解决,不过想来想去,这个bug不是太严重,所以也就忍了。
edu.tsinghua.lumaqq.widgets.qstyle: 看名字可知,这是QQ样式的组件。QQ什么样式?比如QQ那个好友列表,QQ的button,等等。最主要的是QQ的好友列表,为了获得和QQ一样的视觉效果,这个好友列表是最重要的部分。首先windows不提供这样的控件,SWT也没有,只好自己写。在2004之前,用的叫Shutter,由于速度上不行,后来改写了,叫做Blind,这两个单词都是百叶窗的意思。Shutter已经抛弃了,就不提了。稍微说一下Blind。Blind实际上是一个组合组件,并不是自绘的。Blind上面的按钮是Slat,实际的内容区域是Composite子类。在LumaQQ里面,Blind里面的内容是 QTree组件,QTree是自绘的,它由QItem组成,QTreeViewer呢,是QTree的MVC封装。主要的重绘工作在QItem里面,一个 Item(形象一点说,一个好友)分成了很多部分,好友的头像是主图标,好友的昵称是文本,好友离开的时候,头像的右下有个小图标,这叫装饰,好友如果是绑定手机用户,头像旁边会有手机图标,这叫附件,在群里面,还可以看到群下面有组织,可以收起展开,图标的前面会有加号减号标明是否展开,这叫前缀。是不是有点晕了。QItem从QTree哪里得到一个重绘的起始位置,它自己负责计算出装饰,附件,前缀,文本的位置,然后重画。为了支持动画,提供了 IEffect接口让用户可以自定义重画的行为,可以参考IEffect的实现类查看具体的实现。Slat呢,就是QQ样式的button,也是自绘的,比较简单。LevelBar是一个等级条,用来显示好友的等级,缺省使用太阳月亮星星来表示,图标是可以换的,提供了API设置。Bubble是一个 MSN那样的冒泡窗口的简单实现,还没有在LumaQQ里面用到,DieAway呢,是一个淡入淡出的窗口,QQ上线提示那样的,不过也没有用到。
edu.tsinghua.lumaqq.widgets.record: 聊天记录的显示控件,以前用的是table,后来改成QQ样式的了,随便给那个控件起了个名字叫Waterfall,因为是从上到下显示下来的。以前用 table的时候,碰到多行的显示不好,现在使用Waterfall就可以了。
edu.tsinghua.lumaqq.widgets.rich: 这个就不多说了,是自己写的编辑器,以便能支持图片和动画。这个要被淘汰掉的。
edu.tsinghua.lumaqq.widgets.rich2: 这是一个临时的包,是下一个版本的编辑器,基于SWT 3.2的StyledText,由于Eclipse 3.2现在只到M4,bug还很多,所以这下面的代码是一些试验性质的代码,不过其实要不要M4有些bug,现在的实现其实就已经差不多了。
romeobh 于 2006-08-23 15:34:40发表:
LumaQQ有个Debugger,还是很好玩的吧?你有没有想自己写一个debugger UI,或者让LumaQQ的debugger功能更强?
Debug的支持是嵌入在JQL协议层的,而界面怎么实现是留给你去完成的。所以这部分代码分成两个部分:
1. jql_protocol: edu.tsinghua.lumaqq.qq.debug包: Debug的核心支持
2. LumaQQ: edu.tsinghua.lumaqq.ui.debug包: LumaQQ自带的debugger UI实现
看起来很牛B其实debug功能很有限,也就是让你看看收发了些什么包而已吧。如果你还想要监控核心层其他的活动,那这个就要你去扩展了,不过我觉得能看看包也就行了,其他的功能,因为我自己没这个需求,自然就没做。
Debug在核心层的支持是可以通过DebugSwitch.java来切换的,唯一增加的开销是一个if检查,所以这点我还是比较满意的。核心层在开通了Debug功能后,会把所有的收到和发送的包都传给你,当然你必须要添加个IDebugListener才能收到这些包。现在找到 edu.tsinghua.lumaqq.qq包,查看Packet.java和OutPacket.java,你可以发现一些Debug功能的相关代码,这是为什么所有的包都可以被监视的原因。
至于UI的实现,那可以很灵活,具体的就看主页文档,看看缺省的调试器是怎么做的。
romeobh 于 2006-08-23 15:34:26发表:
Windows下面没有热键,等着你们来实现
其实Windows下面实现热键很简单,比linux简单。有人愿意写就写吧,这个我接受你的contribution,并且会加你到About对话框的,当然你实现的要有质量。
自然了,这一块不得不用JNI,接口是很简单的,就三个方法,具体去看看edu.tsinghua.lumaqq.hotkey.linux.edu_tsinghua_lumaqq_hotkey_KeyBinder.h文件,三个方法的原型在那里。
简单说一下这三个方法:
init: 初始化
bind: 绑定一个热键,热键用字符串的形式描述,比如Z,绑定成功返回true,否则返回false
unbind: 取消绑定,参数和bind一样,没有返回值。
接口很简单,能用就行。怎么实现热键我是指导不了你了,反正Windows就是钩子嘛
romeobh 于 2006-08-23 15:34:05发表:
01-17版本改写了聊天记录导出的架构,稍微灵活了一些,不过实现的也不是太好,意思一下就是了啦
我缺省做了两个简单的聊天记录导出模板,文本的和HTML的,如果你想添加自己的模板,或者修改现有的模板,可以看看LumaQQ_template工程,这是一个刚创建的新工程,为了正确的使用这个工程,你需要装EMF,我用了JET来做模板
template下面就是jet的模板定义,这个工程文件不多,除了RecordExporterFactory是手写的,其他的代码都是从jet模板生成的。
RecordExporterFactory很简单,随便看看也就明白了,如果你想加个自己的模板上去,照着样子加点代码,然后写一下自己的模板文件,然后执行build.xml的jar目标,然后把jar拷贝到LumaQQ_2005的lib里面,然后没有了
实现的不是太强,够用就行。对JET感兴趣的,安装完EMF之后,eclipse的帮助里面会有两篇JET的文章。Eclipse主页也有。
romeobh 于 2006-08-23 15:33:49发表:
想改变整个程序的样式么? 工作有点多
我没有实现什么皮肤,只是简单的做了一些程序边框的修饰,主要的工作都是在BorderStyler.java里面完成的。
BorderStyler的工作就是你传一个shell进去,它给shell加上边框,标题条,还有最大最小之类的按钮,当然还要添加一些事件监听器。你要改也可以,不过我觉得基本上BorderStyler没有太多需要改的。你可以换换颜色,换换那些按钮的图标,这样界面风格就可以变了。
大部分颜色都定义在Colors.java里面,你可以改。
最大化最小化按钮的图片在指南(2)里面有介绍,还有一些背景图,也可以改了以便和按钮的色调配合
光改了边框恐怕还不行,其他控件的颜色方案也得改改吧,比如好友列表的颜色方案。
如果有些控件用到的颜色没有在Colors里面定义,那你得稍微改改控件的代码。
记得我在指南(2)里面说过的,改图片的时候不要乱改图片大小。
romeobh 于 2006-08-23 15:33:31发表:
你嫌LumaQQ的机器人实现太简单么?是简单了点,我只处理了普通消息,你想让机器人也回复群消息,也回复手机短信,等等。
不麻烦,稍微改改QQClient.java吧,找找qqEvent方法,有这么一块
case QQEvent.QQ_RECEIVE_NORMAL_IM:
processNormalIM(e);
break;
processNormalIM很简单:
// 先返回确认
processReceiveIM(e);
// 得到消息包
ReceiveIMPacket packet = (ReceiveIMPacket)e.getSource();
doRobot(packet);
so,把下面加到qqEvent里面
case QQEvent.QQ_RECEIVE_CLUSTER_IM:
processClusterIM(e);
break;
再加一个processClusterIM方法:
// 先返回确认
processReceiveIM(e);
// 得到消息包
ReceiveIMPacket packet = (ReceiveIMPacket)e.getSource();
doRobot(packet);
这样就可以让robot也收到群消息了,剩下的事,是你的Robot实现的事。doRobot方法里面只发送普通消息,所以要改一下,具体就不说了
romeobh 于 2006-08-23 15:33:11发表:
你解析了一个未知的包,不知道怎么添加到JQL么?如果你有这样的疑问,我给你一些指南
一般来说,你要完成以下任务:
1. 添加相应的命令常量到QQ.java
2. 根据协议族不同,继承不同的基类。以基本协议族为例,继承BasicOutPacket,创建一个输出包类。再继承BasicInPacket,创建一个输入包类。你可以看看现有的包,抄一个过来,然后修改putBody或者parseBody就可以了。
3. 根据协议族不同,找到不同的parser,以基本协议族为例,看看BasicParser.java,他负责解析所有基本协议族的包,把你的包加到parseIncoming和parseOutcoming的大switch里面吧
4. 你的包是否触发一些事件?如果是,修改QQEvent.java,添加你自己的事件常量。千万不要和其他的事件常量冲突了,我在里面标明了下一个可用的事件ID。
5. 根据协议族不同,找到不同的包事件处理器。以基本协议族为例,看看BasicFamilyProcessor.java,那个packetArrived的大switch里面。加上你自己的代码,触发你的事件吧。
6. 找到QQClient.java,添加一个方便的方法可以发送你的包,不加其实没事,但是加了好些,上层调用起来方便。
还好步骤不算很多,Just do it!
想把图标都换换?这个简单
edu.tsinghua.lumaqq.resource.icon,程序的图标
edu.tsinghua.lumaqq.resource.head,用户头像
edu.tsinghua.lumaqq.resource.smallhead,用户小头像
edu.tsinghua.lumaqq.resource.clusterhead,群头像
edu.tsinghua.lumaqq.resource.face,缺省表情
edu.tsinghua.lumaqq.resource.image,一些比较大的图片,logo,背景什么的
看哪个不顺眼,换吧。但是文件名和图片尺寸不要乱变。