转自:http://www.david0446.com/?p=23
1. 简介
Welcome to the Project Darkstar Server (PDS) application tutorial. This document is designed to teach you everything you need to know to start writing game servers that run on top of the Project Darkstar Server. We call such programs PDS applications, and you will see that term used in this and other PDS documents.
This tutorial begins with an overview of how to code a PDS application, then steps through the development of a very simple application.
欢迎来到Project Darkstar Server (PDS)服务器端应用指南。这篇文档让你了解在你开始编写运行在Project Darkstar Server上的游戏服务端程序时需要知道的一切。我们称那些程序为“PDS应用程序”,你会在这篇文档和其他PDS文档中看到这个称呼。
这篇指南以一个编写PDS应用程序的概述开始,然后一步步进行一个非常简单的应用程序的开发。
2.编写Project Darkstar 服务端应用程序
This chapter presents the fundamental concepts of how to code game server applications in the Project Darkstar Server (PDS) environment. Understanding these concepts is the first step on the path to building massively scalable, reliable, fault-tolerant, and persistent network game
这个章节呈现了一些基本概念,关于如何在PDS环境下编写游戏的服务端应用程序。理解这些概念是构建大型可扩展的、可靠的、可容错的并且是持久化的网络游戏的第一步。
1. 目标和理念
In order to understand the Project Darkstar Server (PDS) coding model, it is useful to understand the system’s goals. The fundamental goals are as follows:
● Make server-side game code reliable, scalable, persistent, and fault-tolerant in a manner that is transparent to the game developer.
● Present a simple single-threaded event-driven programming model to the developer. The developer should never have his or her code fail due to interactions between code handling different events.
Applications coded to run in the PDS environment are called PDS applications.
为了理解PDS的编码规范,了解一下系统目标是很有用的。基本目标为以下内容:
● 使得服务器端代码以一个可靠的,可扩展的,持久化的和可容错的方式,透明的呈现给游戏开发者。
● 提供一个简单的单线程、事件驱动的运行方式给开发者。开发者的代码不会由于那些处理不同事件的代码之间的相互影响而失败。
那些被编写运行在PDS环境下的应用程序被称作PDS应用程序。
2. 接近实现
1. 任务(Tasks)和管理器(Managers)
From the point of view of the PDS application programmer, PDS applications execute in an apparently monothreaded, event-driven model. The code handling the event appears to the coder to have sole ownership of any data it modifies. Thus, execution is both race-proof and deadlock-proof. Under most conditions there is no need to synchronize application code and, in fact, attempting to use the synchronized keyword in Managed Objects(1) can cause subtle bugs.
从PDS应用程序员的角度来说,PDS应用程序在一个显式的单线程事件驱动模式下执行。处理事件的代码似乎让编码者对任何自己修改的数据都有唯一的掌控权。从而,程序的执行既是平稳竞争的,又是弱死锁的。在绝大多数情况下,不需要对代码进行同步操作,事实上,尝试着在管理对象(1)上做同步操作,会导致一些隐性的错误。
In actuality, the system has many threads of control all simultaneously processing their own events. These threads of control are called tasks. The system keeps track of what data each task accesses. Should a conflict arise, one task is aborted and scheduled for retry at a later date so that the other task can complete and get out of the way.
事实上,系统包含了很多线程,这些线程控制着所有并发处理他们各自事件的操作。这些控制线程称作tasks。系统监视着每一个task访问的数据。一旦有冲突发生,一个task将被终止,并安排在晚些时候重新执行,以便使得另一个task可以执行完成并跳出冲突的局面。
Tasks are created by PDS managers. a PDS application uses these managers to effect actions in the PDS environment and the outside world.
这些task被PDS管理器创建。一个PDS应用程序通过这些管理器来影响在PDS环境里和外部世界的行为。
There are three standard managers in the system. In addition, arbitrary managers may be coded and added to the PDS environment. The standard managers are:
● Task Manager
A PDS application can use the Task Manager to queue tasks of its own. Tasks can be queued for immediate execution, delayed execution, or periodic execution. Tasks created from within other tasks are called child tasks. The task that queued the child task is called the parent task. Multiple child tasks queued by the same parent task are called sibling tasks.
● Data Manager
A PDS application can use the Data Manager to create and access persistent, distributed Java objects called Managed Objects. The PDS application is itself composed of Managed Objects.
● Channel Manager
A PDS application can use the Channel Manager to create and control publish/subscribe data channels. These data channels are used to communicate between groups of clients and the server.
系统有三种基本的管理器。另外,这些管理器都可能被编写并添加到PDS环境中。基本的管理器是:
● 任务管理器(Task Manager)
一个PDS应用程序可以用任务管理器来对所拥有的任务(tasks)进行调度。这些任务可以被安排为立即执行、延迟执行或者周期性执行。被其他任务创建的任务称为子任务(child tasks)。可以调度子任务的任务称为父任务(parent tasks)。被同一个父任务调度的多个子任务称为兄弟任务(sibling tasks)。
● 数据管理器(Data Manager)
一个PDS应用程序可以通过数据管理器(Data Manager)来创建和访问持久化的,分布式的,被称为管理对象(Managed Objects)的Java对象。PDS应用程序由它自己的管理对象组成。
● 通道管理器(Channel Manager)
一个PDS应用程序可以通过通道管理器(Channel Manager)来创建和控制发布/订阅模式的数据通道。这些通道用来建立一群客户端与服务器的连接。
A PDS application gets access to core PDS functionality through the AppContext class, which provides methods to obtain references to the various managers.
一个PDS应用程序通过AppContext类来获取PDS的核心功能。该类提供获取各种管理器引用的方法。
2. Task的执行顺序
Tasks that handle events created by a client are guaranteed to execute in order. A task to handle a client event that occurred later in time will not start executing until the tasks to handle all earlier events generated by the same client have finished. A child task is ordered with regard to its parent task, but not with regard to its siblings. This means that execution of a child task will not begin until execution of the parent has completed. There are, however, no guarantees among sibling tasks as to order of execution.
处理事件的任务被一个客户端创建,并保证按一定的顺序执行。一个较晚产生的处理客户端事件的任务(task)要等到那些由同一个客户端较早产生的任务执行结束后,才能开始执行。一个子任务(child task)由它的父任务(parent task)进行排序,与它的兄弟任务(siblings)没有关系。这就意味着,只有父任务执行完成后,其子任务才会开始执行。然而,在兄弟任务之间,并没有规定的执行顺序。
Important: There are no other order guarantees in the system. In particular, execution of tasks to handle events of different users are not guaranteed to start executing relative to each other in the order they arrived at the server.(2)
重点:系统中没有其他的顺序担保。尤其是那些处理不同用户事件的任务的执行顺序,并不是由它们到达服务器的顺序决定的。(2)
3. Task的生命周期
Tasks must be short-lived so that they do not block access for an inordinate amount of time to resources that might be needed by other tasks. The PDS is configured by its operator with a maximum task execution time (the default is 100 milliseconds). Any task that does not finish within that time will be forcibly terminated by the system.
任务(Tasks)必须是短命的,这样他们就不会过度的用大量的时间去阻塞访问那些其他任务可能需要使用的资源。PDS系统会被操作者配置任务的最大执行时间(默认为100毫秒)。任何在这个时间内没有执行完成的任务,都会被系统强制终止。
If you have a task that runs too long, there are two approaches to reducing its execution time:
● Split it up into a chain of child tasks, each of which handles one discrete portion of the problem, and then queues the next task in sequence. This is known as continuation-passing style.
● Move the time-consuming calculations into a custom manager that queues a result-task when the calculations are complete.
如果你有一个任务运行太久,有两个办法可以减少它的执行时间:
● 把它分解为一连串子任务,每个子任务处理单独的一部分问题,然后依次排列到下一个任务。这被称为连续通过(continuation-passing)方式。
● 将耗时的计算过程搬到一个自定义的管理器中,当计算结束时,它会列出一个包含结果的任务。
Each approach has its advantages and disadvantages. The first is easier for simple problems that lend themselves to serial decomposition. Care must be taken that each task ends with the data in a sensible and usable state, because there is no guarantee as to exactly when the next step will be executed.
每个方法都有它的优缺点。第一个方法对于那些将自身串型分解的简单问题来说更加容易。必须注意每个(分解后的)任务要以一个合理的可用的状态结束,因为没有一个明确的保证下一步操作什么时候会执行。
A special case of this approach is where parts of the problem are separable and handleable in parallel. In this case, the time to complete may be reduced by launching parallel chains of tasks. These parallel chains, however, have no guaranteed ordering in relation to each other, so the work they perform must really be independent of each other.
这个方法的一个特殊情况是,问题中某些可分离并且可控的部分是并行的。在这种情况下,完成的时间可能会由于并行任务链的出现而减少。然而,这些并行的任务链彼此间没有顺序保证,所以,它们的执行工作必定是彼此独立的。
The second approach is easier for problems that don’t decompose well into small, discrete components; however, it requires the writing and installation of a custom PDS manager. (Writing custom PDS managers will be covered by a separate document explaining how to extend the PDS environment.)
第二种方法对于那些不能很好的分解成离散的小组件的问题来说比较容易;然而,这要求一个自定义PDS管理器的编写和安装。(关于编写自定义的PDS管理器,将在一篇专门解释如何扩展PDS环境的文档中介绍。)
A particularly important case is code that has to go into system calls that can block for more then the task execution lifetime. These must be implemented through a custom manager in order to produce a robust PDS application.
特别重要的一点,进入系统调用的代码能够阻塞比任务执行的生命周期更多的时间。这些(代码或任务)必须通过一个自定义的管理器来实现,以建造一个健壮的PDS应用程序。
3. 管理对象(Managed Objects)和管理引用(Managed References)
The Data Manager maintains a persistent set of Managed Objects stored in a pool of objects called the Object Store. Like a normal Java object, each Managed Object contains both data and the methods to act upon that data. In order to be a Managed Object, the object must implement both the ManagedObject and Serializable interfaces. A Managed Object does not become part of the Object Store’s pool until the pool is made aware of the object. This is done by using the Data Manager either to request a Managed Reference to the object or to bind a name to the object. (3)
数据管理器(Data Manager)包含一个管理对象(Manager Objects)的集合,这个集合存储在一个称为对象存储器(Object Store)的对象池中。就像一个平常的Java对象,每一个管理对象包含数据和操作数据的方法。要成为一个管理对象,这个对象必须实现ManagerObject接口和Serializable接口。一个对象只有当它被对象池意识到了,才能成为对象存储器的一部分。这个操作(被对象池意识到)既可以使用数据管理器,也可以为这个对象请求一个管理引用(Manager Reference),或者为这个对象绑定一个名字。(3)
A Managed Reference is a reference object that looks much like the J2SE(tm) reference objects (for example, SoftReference, WeakReference). Managed Objects must refer to other Managed Objects through Managed References. This is how the Data Manager can tell the difference between a reference to a component object of the Managed Object (for instance, a list) that is part of that Managed Object’s state, and a reference to a separate Managed Object with a state of its own.
一个管理引用就是一个引用对象,类似于J2SE(tm)的引用对象(比如,软引用,弱引用)。管理对象必须通过管理引用与其他管理对象进行关联。这就是数据管理器如何区分管理对象(如list)中一个元素对象的引用(它是管理对象状态的一部分)和一个独立的管理对象的引用(它就是管理对象自身的状态)。(PS:这句翻译的不好,仅供参考)
A name binding associates a string with the Managed Object such that the object may be retrieved from the Object Store by other tasks using the getBinding call on the Data Manager.
一个命名绑定将一个字符串与管理对象关联起来,使得那个对象可以被其他的任务(tasks)用数据管理器中的getBinding()方法重新从对象存储器中取回。
1. 通过管理引用访问管理对象
The Managed Reference has two access methods: get and getForUpdate. Both methods return a task-local copy of the object. The difference between the two methods is:
● getForUpdate informs the system that you intend to modify the state of the Managed Object.
● get says you intend only to read the state but not write it.
管理引用有两种访问方法:get和getForUpdate。两种方法都返回一个对象的拷贝。两种方法的区别在于:
● getForUpdate 告诉系统你想要修改这个管理对象的状态。
● get 说明你只是想读取它的状态,并不想修改它。
Although all changes to any Managed Object are persistent (even those accessed via get), it is more efficient to use getForUpdate if you know at that time that you are going to want to modify the Managed Object’s state. This allows the system to detect conflicts between tasks and handle them earlier and with greater efficiency.
虽然所有对管理对象的改变都是持久化的(甚至是那些用get方法访问的),但是如果你知道你将要修改管理对象的状态时,用getForUpdate方法会更有效率。这样系统可以监测任务间的冲突,并且更早、更高效的处理冲突。
Conversely, it is better to use get if the state of the Managed Object may not be modified. The get call can allow for more parallel access to the Managed Object from multiple tasks. If you reach a point later in the execution where you know you are going to modify the object’s state, you can upgrade your access from get to getForUpdate by calling the markForUpdate method on the Data Manager. (Multiple calls to mark the same Managed Object for update are harmless.)
相反的,如果管理对象的状态不会被修改,那get方法会更好。调用get方法允许多样任务中更多的对管理对象进行并行的访问。如果你知道在后面的执行中,需要修改对象的状态,你可以通过调用数据管理器中的markForUpdate方法,从get升级到getForUpdate。(多个任务为修改同一个管理对象而进行标记是无害的。)
Subsequent calls to get or getForUpdate on equivalent Managed References in the same task will return the same task-local copy.
同一个任务(task),同一个管理引用,后续调用get或getForUpdate方法,将会返回相同的本地对象拷贝。
You can also retrieve an object with a bound name by calling getBinding. This is equivalent to a get call on a Managed Reference to the object, so, if you intend to modify the object’s state, you should call markForUpdate after retrieving the object.
你也可以用一个绑定过的名字,通过调用getBinding方法来重新获得一个对象。这等同于用管理引用的get方法获得对象,所以,如果你想要修改对象的状态,你可以在重获对象之后,调用markForUpdate方法。
Managed Objects in the Object Store are not garbage-collected. Once the store is made aware of a Managed Object, it keeps the state of that object until it is explicitly removed from the object store with a call to the removeObject call on the Data Manager. It is up to the application to manage the life cycle of Managed Objects and to remove them from the Object Store when they are no longer needed. Failure to do so may result in garbage building up in your Object Store and impacting its performance. Likewise, name bindings are stored until explicitly destroyed with removeBinding. A name binding is not removed when the object it refers to is removed.
对象存储器中的管理对象是不能垃圾回收的。当存储器意识到了一个管理对象,它会保持这个对象的状态,直到通过调用数据管理器中的removeObject方法,明确的把这个对象中从存储器中移除。由应用程序来决定对管理对象的生命周期进行管理和当它们不再需要时,从对象存储器中移除它们。管理操作的失败会导致你的对象存储器里产生垃圾,影响它的性能。同样的,命名绑定也会被存储,直到调用removeBinding方法明确的销毁掉。一个命名绑定并不会随着与它关联的对象的移除而销毁。
2. 设计你的管理对象
Managed Objects typically fall into three general types of entity:
● Actual objects in your game’s simulated environment, such as a sword, a monster, or a play-space (such as a room).
● Purely logical or data constructs such as a quad-tree for determining player-proximity or a walk-mesh to determine movement paths.
● Proxies for human players in the world of Managed Objects.
管理对象典型的分为三种一般的实体类型:
● 在你游戏的模拟环境中真实的物品,像是一把剑,一个怪兽,或是一个游戏空间(比如一件屋子)。
● 纯粹逻辑上的或者一些数据结构,像是一个方块树,用来决定玩家周围的环境,或者一个行动网格,用来决定移动的路线。
● 玩家在管理对象世界中的代理对象。(也就是人物对象)
Figure 1 below illustrates a very basic world consisting of a single room that contains two players and a sword.
下图1说明了一个非常基本的世界,它包含一个拥有两个玩家和一把剑的房间。
When deciding where to break data up into multiple Managed Objects, consider these questions:
● How big is the data? The more data a single Managed Object encompasses in its state, the more time it takes to load and save.
● How closely coupled is the data? Data that are generally accessed together are more efficiently stored in the same Managed Object. Data that are accessed independently are candidates for separation onto different Managed Objects. Data that have to be modified atomically are best stored in the same Managed Object.
● How many simultaneous tasks are going to need access to this data? As explained above, the PDS does its best to execute as many tasks in parallel as it can. Resolving the conflicts that arise when multiple parallel tasks want to change the state of the same ManagedObject can be expensive.
It is best to split up data that has to be locked for update from data that can be shared with a get. Data that is going to be updated has to be owned by the updating task, whereas data that is just read can be shared by multiple reading tasks. When multiple tasks have to access fields on a Managed Object that is being updated by at least one of them, that Managed Object becomes a potential bottleneck. For best performance, you want as few bottleneck Managed Objects as possible.
当在决定从哪里把数据分解为多个管理对象时,思考一下这些问题:
● 这个数据有多大?一个管理对象状态里包含的数据越多,加载和保存它的时间就越长。
● 这个数据的耦合有多紧密?那些通常会被一起访问的数据存储在相同的管理对象中会更有效率。那些被单独访问的数据就分离到不同的管理对象中。那些必须被自动修改的数据,最好存储在同一个管理对象中。
● 有多少同步(作并行讲)的任务(tasks)会访问这个数据?就像上面所解释的,PDS会尽可能多的执行并行任务。当多种并行的任务想要修改同一个管理对象的状态时会产生冲突,解决这个冲突需要很大的代价。
最好将那些必须因修改而锁住的数据与那些可以调用get方法进行共享的数据分离开来。那些将要被修改的数据必须被那个修改任务(task)所拥有,而那些只读数据可以被多种读取任务(tasks)所共享。当多种任务都必须访问一个管理对象,而这个管理对象又要被它们中至少一个来修改,这个管理对象就会变成一个潜在的瓶颈。为了最好的性能,你尽可能不要有瓶颈管理对象。
Of all these considerations, the third is the most critical to a well-running PDS application.
所有这些要考虑的地方,第三点对一个运行良好的PDS应用程序来说是决定性因素。
3. 玩家管理对象(The Player Managed Object)
Managed Objects register themselves as event handlers with a manager in order to get called when outside events occur. One very important type of Managed Object is the Player Managed Object. A Player ManagedObject implements the ClientSessionListener interface and is returned to the system as the return value from the loggedIn callback on the AppListener. From then on, it will get called for any incoming data packets and disconnect events from that player.
管理对象将它们自己注册为事件句柄(事件操作类),以至于在外部事件发生时,它们能够被调用。一个非常重要的管理对象类型就是玩家管理对象。一个玩家管理对象实现了ClientSessionListener接口,它作为AppListener中loggedIn方法的返回值被返回到系统中。至此,从玩家那里来的任何数据包和断开连接的事件发生时,它就会被调用。
The Player Managed Object acts as a proxy for the player in the world of Managed Objects. The player sends data packets to the server using the PDS Client API. This causes a userDataReceived event in the system, which results in a task that calls the Player Managed Object’s userDataReceived method. The Player Managed Object should parse the packet to find out what it is supposed to do, and then act on itself and other Managed Objects in order to accomplish the requested task.
玩家管理对象在管理对象的世界里,扮演真实玩家的代理者的角色。玩家通过PDS客户端API向服务器发送数据包。这在系统里产生一个userDataReceived事件,其结果是一个任务(task)调用玩家管理对象中的userDataReceived方法。玩家管理对象需要分析数据包,找出它想要做什么,然后作用于自己和其他的管理对象,来完成任务的请求。
Figure 2 shows our simple Managed Object world, with two players connected to the PDS as clients. The Player Managed Objects have a “current room” field, which is a Managed Reference that points to the Room Managed Object. The Room Managed Object has an inventory list, which is a list of Managed References. Currently, there are three items in the list: the two players and a sword. Each is represented by a Managed Object (Player 1 Managed Object, Player 2 Managed Object, and Sword Managed Object).
图2展示了我们的简单的管理对象世界,有两个作为客户端的玩家被连接。玩家管理对象有个“当前房间(current room)”的作用域,它就是一个指向房间管理对象(Room Managed Object)的管理引用(Managed Reference)。房间管理对象有个目录列表,它是一个管理引用的列表。当前,列表里有三个元素:两个玩家和一把剑。每一个都是通过一个管理对象表现出来(Player1管理对象,Player2管理对象和Sword管理对象)。
4. AppListener 接口
Above we had a world of Managed Objects consisting of a Room Managed Object, a Sword Managed Object and a couple of Player Managed Objects. However, when we start the game in the PDS for the first time, the world of Managed Objects doesn’t look like that. In fact it looks like this:
上面我们有了一个管理对象世界,包含一个房间管理对象,一个剑管理对象,和一对玩家管理对象。然而,当我们在PDS里第一次启动游戏时,这个管理对象世界并不是像那个样子。事实上,它是看起来是这样的:
Which is to say, it is empty.
How then do the Managed Objects get into the Object Store in the first place?
这是空的。
管理对象第一次怎样进入对象存储器的呢?
The answer is a special interface called the AppListener. There are two special things about the class that defines the AppListener:
● It implements the AppListener interface. This interface defines two methods:
● initialize
● loggedIn
● It has been specified as the AppListener class for this application.
答案是一个特殊的接口AppListener。关于定义AppListener接口的类,有两点需要说明:
● 它实现了AppListener接口。这个接口定义了两个方法:
● Initialize
● loggedIn
● 它作为AppListener类,是被应用程序指定的。
These two properties combine in the following way:
● Upon the boot of the PDS (or the installation of a new application into the PDS), the PDS attempts to locate the AppListener for that application in the Object Store.
● If the application has never been booted before, then its Object Store in the PDS is blank (as in Figure 3), and the PDS will fail to find the AppListener. In that case, it creates the AppListener itself, and then starts a task that calls initialize.
● If, on the other hand, the application has been booted at least once, the Object Store will contain the AppListener already. In this case, execution just resumes from where it left off when the system came down, listening for new connections and executing any periodic tasks that were running before.
下面有两个关联的属性:
● 在PDS启动时(或者在一个新应用程序安装进PDS时),PDS试图为那个应用程序在对象存储器里定位一个AppListener。
● 如果应用程序之前从来没有启动过,它的对象存储器就是空的(就像图3所示),PDS就找不到AppListener。这种情况下,它会自己创建一个AppListener,然后启动一个叫initialize的任务。
● 另一方面,如果这个应用程序至少启动过一次了,对象存储器就已经包含AppListener。这种情况下,执行操作会从上次系统停止时的地方继续进行,监听新的连接,执行任何之前运行的周期性的任务。
In the case of our little demo application, the boot method will have a block in it that, in pseudo-code, looks something like this: (4)
像我们的小例子的那种情况,启动方法会有一个阻塞在里面,就像下面的伪代码:(4)
initialize {
CREATE ROOM MANAGED OBJECT
CREATE SWORD MANAGED OBJECT
ADD REF TO SWORD MANAGED OBJECT TO ROOM’S INVENTORY
SAVE A MANAGED OBJECT REF TO ROOM FOR LATER
}
In general, it is the responsibility of the AppListener to create the initial world of Managed Objects during the first startup.
通常,在第一次启动时,创建初始化的管理对象世界是AppListener的职责
Now we have something that is beginning to look like our game. We still don’t have Player Managed Objects, however. We will create the Player Managed Objects as users join, in much the same way the AppListener was created. The first time we see a user log in, we create a new Player Managed Object for that user. After that, every time that user logs in, we just reconnect him to his existing Player Managed Object. Thus, the system creates Player Managed Objects as needed, and remembers user information between logins.
现在,我们有了一些像我们游戏的东西了。然而,我们仍然没有玩家管理对象(Player Managed Objects)。我们将用大多数创建AppListener相同的方式来创建玩家管理对象,作为用户的参与。我们看到一个用户首次登录进来,我们为这个用户创建一个新的玩家管理对象。在那之后,那个用户每次登录,我们只要将他重新连接到他已存在的玩家管理对象就行了。从而,系统只在需要时创建管理对象,记住用户每次登录之间的信息。
So how do we find out when a user has logged in?
The answer is the second callback on our AppListener: loggedIn. Every time a user logs into a PDS application, a task is started that calls the loggedIn method on the application’s AppListener.
When the loggedIn callback is called on our AppListener, it executes the following code, presented as pseudocode.
那么,我们怎么知道一个用户已经登录了呢?
答案就是AppListener的第二个方法:loggedIn。用户每次登录进一个PDS应用程序,一个任务就被启动,调用应用的AppListener中loggedIn方法。
当loggedIn方法在我们的AppListener中被调用,它执行以下的代码,以伪代码表示。
loggedIn {
managedObject_name = “player_”+ SESSION.PLAYER_NAME;
IF MANAGED OBJECT EXISTS (managedObject_name) {
FIND MANAGED OBJECT(managedObject_name);
} ELSE {
CREATE NAMED PLAYER MANAGED OBJECT (managedObject_name);
}
SET currentRoom on PLAYER MANAGED OBJECT
TO SAVED MANAGED OBJECT REF TO ROOM
GET ROOM MANAGED OBJECT
ADD PLAYER REF TO ROOM MANAGED OBJECT’S PLAYERS LIST
REGISTER PLAYER MANAGED OBJECT AS SessionListener (SESSION);
}
ClientSessionListener is another event interface. It defines methods that get called on tasks to respond to actions the client takes with the client API, such as the client sending data to the server for processing and the client logging out.
ClientSessionListener是另外一个事件接口。它定义了一些方法,这些方法在任务中被调用来回应客户端用客户端API产生的动作,比如客户端向服务器发送数据要求处理,或者客户端登出
Figure 5 illustrates that our Managed Object world is starting to look the way we want it to.
图5所示的管理对象世界已经开始像我们想要的形式了。
When a second user logs in, we will be back to our original world. Figure 6 illustrates our world after restarting the game with our previous players:
当第二个用户进入时,我们将返回到初始的管理对象世界。图6展示了先前的用户重新启动游戏后的世界。
So far, the logic has been laid out in pseudo-code. The actual code to implement this application is included in Appendix A as the SwordWorld application. The actual application code goes a bit further in that it also implements a look command, to show you how the Player Managed Object actually handles commands being sent from the client.
至此,伪代码已经将逻辑展示出来。实现这个应用的真实代码包含在附录A里,名为SwordWorld应用程序。真实的程序代码功能要更多,它还实现了一个look命令,向你展示了玩家管理对象如何处理从客户端发送来的命令。
4.服务器API类
All the Project Darkstar Server API classes are in the com.sun.sgs.app.* package.
These are the Project Darkstar Server API classes with brief descriptions:
全部的Project Darkstar服务器端API类都在com.sun.sgs.app.*包里。这里是一个简单描述。
1. 系统类和接口
Class | Description |
AppContext |
Provides access to facilities available in the current application. Primarily used to find references to managers. This is the starting point for the application code to talk to the system. 提供存取当前应用中可用的设备。主要用来找到管理器的引用。这是程序代码与系统会话的起点。 |
AppListener |
Interface representing a listener for application-level events. This listener is called when the application is started for the first time, and when client sessions log in. 一个接口,监听应用级事件。这个监听器当应用第一次启动和客户端会话登录时被调用。 |
ManagerNotFoundException | Thrown when a requested manager is not found.当一个请求的管理器没有找到时,抛出异常。 |
ClientSession |
Interface representing a single, connected login session between a client and the server. 接口描述了一个客户端与服务器之间独立的,连接登录上的会话。 |
Class |
Description |
ClientSessionListener |
Listener for messages sent from an associated client session to the server. 监听从一个已连接的客户端向服务器发送的信息。 |
2. 任务管理类和接口
Class | Description |
TaskManager | Provides facilities for scheduling tasks.提供安排任务计划的工具 |
Task |
Defines an application operation that will be run by the Task Manager. 定义一个将要被任务管理器(Task Manager)运行的操作。 |
PeriodicTaskHandle |
Provides facilities for managing a task scheduled with the Task Manager to run periodically. 提供了管理那些由任务管理器安排的,周期性任务的工具。 |
TaskRejectedException |
Thrown when an attempt to schedule a task fails because the Task Manager refuses to accept the task due to resource limitations. 当安排一个任务的企图因为任务管理器拒绝接受访问受限资源的任务而失败时,抛出异常。 |
ExceptionRetryStatus |
Implemented by exception classes that want to control whether an operation that throws an exception of that exception should be retried. 被异常类实现,那些异常类想要控制一个操作是否抛出了一个需要被重试的异常。 |
3. 数据管理类和接口
Class | Description |
DataManager |
Provides facilities for managing access to shared, persistent objects. 提供了存取共享的、持久对象的工具。 |
ManagedObject |
A marker interface implemented by shared, persistent objects managed by the Data Manager. 一个接口,被数据管理器管理的共享、持久性对象实现。 |
ManagedReference | Represents a reference to a managed object.描述一个管理对象的引用。 |
ObjectIOException |
Thrown when an operation fails because of an I/O failure when attempting to access a Managed Object. 当一个操作因为在存取管理对象时I/O操作失败而没有成功时,抛出异常。 |
ObjectNotFoundException |
Thrown when an operation fails because it attempted to refer to a Managed Object that was not found. 当一个操作由于它想关联的管理对象不存在而失败时,抛出异常。 |
TransactionAbortedException |
Thrown when an operation fails because the system aborted the current transaction during the operation. 当一个操作由于系统将操作间的处理终止而失败时,抛出异常。 |
TransactionConflictException |
Thrown when an operation fails because the system aborted the current transaction when it detected a conflict with another transaction. 当一个操作由于系统检测到当前操作与另一个操作有冲突并终止当前操作而失败时,抛出异常。 |
TransactionException |
Thrown when an operation fails because of a problem with the current transaction. 当一个操作由于当前处理有问题而失败时,抛出异常。 |
TransactionNotActiveException |
Thrown when an operation fails because there is no current, active transaction. 当一个操作由于没有当前主动的处理而失败时,抛出异常。 |
Class |
Description |
TransactionTimeoutException |
Thrown when an operation fails because the system aborted the current transaction when it exceeded the maximum permitted duration. 当一个操作由于超过了最大处理时间,被系统终止而失败时,抛出异常。 |
NameNotBoundException |
Thrown when an operation fails because it referred to a name that was not bound to an object. 当一个操作由于它使用一个没有被绑定到对象的名字而失败时,抛出异常。 |
4. 通道管理类和接口
Class | Description |
ChannelManager | Manager for creating and obtaining channels.管理通道的创建和获取。 |
Channel |
Interface representing a communication group, a channel consisting of multiple client sessions and the server. 接口描述一个通信组,一个通道由许多客户端会话和服务器组成。 |
ChannelListener |
A channel can be created with a ChannelListener, which is notified when any client session sends a message on that channel. 一个通道可以由一个ChannelListener创建,当任何客户端在那个通道上发送消息时,它会被意识到。 |
Delivery |
Representation for message delivery requirements. A channel is created with a delivery requirement. 描述消息传递请求。一个通道被一个传递请求创建。 |
NameExistsException |
Thrown when an operation fails because it referred to a name that is currently bound to an object. 当一个操作由于它要关联的名字已经绑定到一个对象而失败时,抛出异常。 |
Class |
Description |
NameNotBoundException |
Thrown if a channel is not bound to a name specified to ChannelManager.getChannel(). 如果一个通道没有绑定到一个名字,使用ChannelManager.getChannel()会抛出异常。 |
注释:
(1) PDS应用程序由管理对象组成,这些管理对象被解释为“管理对象和管理引用。”
(2)这也意味着,由两个不同客户端产生的任务,并不一定按他们在客户端创建的时间顺序来得到执行。实际上,这(以到达顺序决定执行顺序)通常在在线游戏中是不正确的,除非做特别的努力去实现它,因为在两台客户端对服务器进行网络连接时,延迟方面的简单变化就可以引起它们到达顺序的重新排序。
(3) 要知道这个可能在库文件或PDS本身的API中发生。可以看一下本文最后的实践章节,得到更多的信息。
(4) 在这篇文档中的所有伪代码里,变量和方法的引用以小写字母开头,类和接口的引用以大写字母开头。