论坛首页 Java企业应用论坛

基于Java的2D mmorpg开源引擎Threerings系列之一(概述篇)

浏览 6769 次
该帖已经被评为精华帖
作者 正文
   发表时间:2010-02-10  

Threerings是Three Rings Design公司旗下的一款基于Java并完全具备开发出商业品质的2D mmorpg游戏的开源引擎。Three Rings Design是一家位于美国总部在加州旧金山的网游开发商,但又不同于一般网游开发商的是,该公司旗下的所有游戏都抛弃了砍怪升级,打装备pk的老套路,而是着重于游戏性本身,从非常与众不同的视角来开发它的游戏,让人有完全耳目一新的感觉,大家有兴趣可以去该公司主页http://www.threerings.net下载下来玩一下,不过都是E文哦。旗下的一款Puzzle Pirates更是从2003年12月开始一直稳定运营到现在,这款游戏就是在此引擎基础上搭建的(或者说此引擎脱胎于该款游戏)。但是由于文档资料相当缺乏,再加上该公司又比较低调,所以即使开源,了解的人也并不多,国内对之了解的更是少之又少,笔者到目前为止还没有发现有关这款引擎的中文介绍资料。所以,笔者在此愿抛砖引玉,把它介绍给大家,希望能有更多的人能了解它。

 

 OK,闲话少叙,下面笔者就带大家来具体了解一番。首先,我们去http://www.threerings.net/code/下载它的源代码。所谓three rings,主要就是分别指narya, nenya, vilya这三个核心框架,在字面上应该是指在魔戒中除了The Ring之外最强大的三个ring了,呵呵,美国佬还挺有意思的,给取了这么个名。我们可以看到,在http://www.threerings.net/code/上除了三个核心框架外,还有很多其他的工具类库,我们即可以在此页面上单独下载,也可以从subversion源码库中签出完整的开发包,当然还可以点此下载编译好的开发包,不过笔者还是决定从svn://code.threerings.net/gardens/trunk 签出完整的源码开发包,为了论述的方便起见,笔者假设你把源码开发包签出到threerings/gardens目录下,整个源码开发包下载下来大概有100多M,需要花不少时间,所以,在此期间,你可以先去泡杯茶什么的,呵呵。

 

总算全部下载下来了,不过先别急,在正式开始尝试前,我们还要配置好我们的运行环境,首先,请确保JDK版本在1.5之上,其次,请确保你的ant版本在1.6以上,如果没有安装,可以去这里http://ant.apache.org/下载。好了,在环境配置好之后,我们就可以正式开始了,如果你用的是Linux操作系统,那恭喜你,你只需要进入threerings/gardens目录,在命令行下运行ant distall。如果成功的话,会在threerings/gardens目录下生成一个dist目录,在这个目录下包含了所有运行你自己的应用的所有jar包。

 

如果你用的是windows,跟笔者一样,我想大多数人都是,如果直接运行ant distall会发现很多无法打开压缩包的错误,这是因为在threerings内部,他们用的都是Linux作为开发环境,并且使用了symbolic link作为管理jar包的方式(其实他们应该尝试下maven作为jar包管理工具),而symbolic link对于windows来说是不支持的,所以我们需要用真正的jar包来覆盖这些symbolic link。打开threerings/gardens/lib以及threerings/gardens/build/lib/java目录,可以发现在这两个目录下有许多长度只有1k的jar包,如果你用文本编辑器打开的话就会发现它只是一个链接而已,那我们只需要有点耐心,使用带版本号的jar包来一一覆盖不带版本号的1k的jar文件。

 

另外,在运行ant distall命令前,还需要在threerings/gardens目录下把gardens.conf.dist文件复制成相同目下的gardens.conf文件。

 

好了,现在在成功运行ant distall之后,进入threerings/gardens/projects/narya/tests目录,在这个目录下有一个server跟client的例子,但是在运行他们之前,为了方便起见,我们编辑一下threerings/gardens/projects/narya/tests/build.xml这个文件,在此文件的末尾,加入下面的代码。

 

<target name="runserver">     
	<java classname="com.threerings.presents.server.TestServer" fork="true">          
		<classpath refid="classpath"/>     
	</java>
</target>
<target name="runclient">     
	<java classname="com.threerings.presents.client.TestClient" fork="true">          
		<classpath refid="classpath"/>     
	</java>
</target>

 

在此,我们就加入了两个ant target,这样就不需要手工来运行java命令了。下面我们就来运行一下这个server和client的例子。打开一个命令行,在threerings/gardens/projects/narya/tests下,运行ant runserver;再打开一个命令行,在同样的目录下运行ant runclient。如果运行成功,在众多的输出里面,你在server端可以看到其中一行有test request...字样,在client端可以看到其中一行有test response...字样,这样就基本能说明服务端和客户端能够正常通讯了。

 

 那么我们现在就来看看在client端的具体的代码。打开threerings/gardens/projects/narya/tests/src/java/com/threerings/presents/client/TestClient.java,在main函数中我们创建了一个UsernamePasswordCreds对象,这个是在client登录的时候发送给服务端的验证信息,我们把这个对象传递给client对象,而client对象则包含了所有的连接信息。最后我们调用client.logon(), 在这个时候,client对象把登录的验证信息发送给服务器,服务器验证通过后发回一些客户端用于自举的数据,然后客户端会再次连接到服务端。

 

    public static void main (String[] args)
    {
        TestClient tclient = new TestClient();
        UsernamePasswordCreds creds =
            new UsernamePasswordCreds(new Name("test"), "test");
...
        Client client = new Client(creds, rqueue);
        tclient.setClient(client);
...
        client.logon();
...
    }

 

当客户端登录成功以后,在客户端的侦听器回调函数clientDidLogon会被框架自动调用。这是通过实现一个叫做SessionObserver的特殊的观察者接口来完成的。在实现这个接口的同时还需要调用client.addClientObserver(tclient)方法使我们的TestClient注册成为一个logon事件的侦听者。很多用到这种介于服务端和客户端通讯的功能,包括后面会谈到的DObjects,都非常多的使用了观察者模式,即Observer Pattern,这是GOF23种设计模式中的一种,具体可以参看笔者的另一篇博文深度探索观察者模式。回过头来,我们再来看一下clientDidLogon这个方法,在这个方法中包含有许多代码,但在这里,我们只关注其中的两样,即请求和调用一个服务。服务是在服务端的一个功能,它可以被客户端直接调用,就好像远程方法调用(RPC)一样。这种基于服务的调用就提供了服务端和客户端通讯的一种基本方法。代码client.requireService执行后返回一个service对象,通过这个对象,客户端就可以用它来调用服务端上的服务了。代码service.getTestOid就是对服务的一个调用,这个服务用来返回分布式对象DObject的object id。

 

    // from interface SessionObserver
    public void clientDidLogon (Client client)
    {
...
        TestService service = client.requireService(TestService.class);
...
         // get the test object id
        service.getTestOid(client, this);
    }

 

 下面我们来简单介绍下这个框架中的分布式对象概念(笔者会在下一篇中详细介绍),一个分布式对象即一个distributed object或者我们叫DObject,它是专门用来在服务端和客户端共享数据的。当服务端更新了一个DObject对象的状态时,更新后的状态会被自动复制到client端。不过当client端需要调用这个DObject时,它首先必须订阅(subscribe)它,打个比方,就象订阅我们的杂志一样。每次一个DObject在服务端被更新时,更新后的新版本会被发送到订阅者手里。通过这种机制,客户端能保持手里的DObject更新状态。

 

 

那我们又如何来订阅DObject呢?这就需要先找到它的oid(Object ID),比如你定杂志的时候需要填写一个唯一可识别的订刊号。我们通过调用service.getTestOid方法向服务器(杂志发行商)请求这个id(杂志的订刊号)。当我们调用这个方法的时候服务器(杂志发行商)处理完我们的请求后会回调我们的侦听方法gotTestOid,在调用的同时DObject(杂志)的oid(订刊号)作为参数被传入。

  

    // documentation inherited from interface
    public void gotTestOid (int testOid)
    {
        // subscribe to the test object
        _client.getDObjectManager().subscribeToObject(testOid, this);
    }

 

 当我们得到oid后(杂志订刊号)后我们便可以订阅它了。在gotTestOid方法被调用后,这时我们通过oid(杂志订刊号)来调用subscribeToObject方法。服务器(杂志发行商)在得到我们的订阅DObject(杂志)请求后便会返回真正的DObject对象(杂志)到我们的侦听方法objectAvailable

 

    // from interface Subscriber
    public void objectAvailable (final TestObject object)
    {
        object.addListener(this);
        log.info("Object available: " + object);
        object.postMessage("lawl!");
...
        }

 

在下一篇中具体介绍DObject之前,这儿简单说下关于mmorpg网络通讯的安全性问题。众所周知,有网游的地方就会有人zuobi,有外挂,那么作为一款专用于mmorpg开发的引擎的底层通讯框架必须提供一种机制来尽可能的遏制这种现象。那么,在这里对于DObject来说,在游戏中DObject经常被用于存储众多客户端与服务端共享的敏感数据,对于这些数据,客户端是没有权利直接去修改的,客户端只能发送请求(通过我们前面介绍的服务)给服务端,服务端在接收到来自客户端的请求后只有在验证通过之后才会对DObject作出修改,而只要订阅了这个DObject的客户端都会收到更新后的状态。游戏引擎通过使用这种机制来确保玩家无法通过修改客户端运行数据来zuobi。如果你在运行你的服务器的时候发现这样的警告信息WARNING: Event failed permission check,这是因为客户端试图在本地修改DObject,而服务器拒绝了客户端的请求。如果仍旧用杂志订阅来做比喻(在这里不一定恰当),如果一个订户想要在杂志中包含一篇新的专题或其他文章,它首先要发送这个请求到杂志发行商,杂志发行商更新他们的杂志包含此新专题并再次发放到订户手里。

 

 

public static void main (String[] args)
    {
        TestClient tclient = new TestClient();
        UsernamePasswordCreds creds =
            new UsernamePasswordCreds(new Name("test"), "test");
        BasicRunQueue rqueue = new BasicRunQueue();
        Client client = new Client(creds, rqueue);
        tclient.setClient(client);
        client.addClientObserver(tclient);
        client.setServer("localhost", Client.DEFAULT_SERVER_PORTS);
        client.logon();
        // start up our event processing loop
        rqueue.run();
    }

 

下一篇我们会详细介绍Threerings引擎中的DObject部分。

 

  • 大小: 98.8 KB
  • 大小: 71.5 KB
   发表时间:2010-02-10  
我想问一下..这个引擎的图形绘制部分是怎么做的?调用DX?
0 请登录后投票
   发表时间:2010-02-10  
图形部分主要包括在nenya框架中,是对java 2d的封装。不过这个引擎的各个子框架都是模块化设计的,既可以一起使用,也可以单独使用,所以你可以比较方便的替换各个组成部分,比如如果需要的话,你可以把图形绘制部分替换成slick 2d等。
0 请登录后投票
   发表时间:2010-02-11  
恩,前两年大概看过一眼.LZ要是有研究可以分享一下.我还是比较关注性能情况.
0 请登录后投票
   发表时间:2010-02-11  
貌似这个网站的新闻最新的还是2008年的。
是不是项目已经死掉了?
我觉得从设计上来说,这个订阅发布的路子可能是走不通的。
0 请登录后投票
   发表时间:2010-02-12  
项目没有死掉,只不过相对来说比较稳定罢了。
0 请登录后投票
   发表时间:2010-04-22  
linkerlin 写道
貌似这个网站的新闻最新的还是2008年的。
是不是项目已经死掉了?
我觉得从设计上来说,这个订阅发布的路子可能是走不通的。


说这话太害怕了~ 我今年4月进的一家公司,打算用这个~ 现在让我研究源代码。但是人家的项目没停,还在不断更新。
0 请登录后投票
   发表时间:2010-04-23  
你的那个深度探索观察者模式不错。  就是有点错误。
不理解那个narya的源码研究看不懂。
希望继续多写点Threerings的东西。  貌似搞这个的很少。
0 请登录后投票
   发表时间:2010-04-23  
搞Threerings的确实很少,你们公司研究这个准备做什么项目呢?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics