附录A:SwordWorld例子代码
Sword World
/*
* Copyright 2007-2009 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package
com.sun.sgs.tutorial.server.swordworld;
import
java.io.Serializable;
import
java.util.Properties;
import
java.util.logging.Level;
import
java.util.logging.Logger;
import
com.sun.sgs.app.AppContext;
import
com.sun.sgs.app.AppListener;
import
com.sun.sgs.app.ClientSession;
import
com.sun.sgs.app.ClientSessionListener;
import
com.sun.sgs.app.DataManager;
import
com.sun.sgs.app.ManagedReference;
/**
* A tiny sample MUD application for the Project Darkstar Server.
*
*
* There is a Room. In the Room there is a Sword
*/
public
class
SwordWorld
implements
Serializable, AppListener {
/**
The version of the serialized form of this class.
*/
private
static
final
long
serialVersionUID
=
1L
;
/**
The {
@link
Logger} for this class.
*/
private
static
final
Logger logger
=
Logger.getLogger(SwordWorld.
class
.getName());
/**
A reference to the one-and-only {
@linkplain
SwordWorldRoom room}.
*/
private
ManagedReference roomRef
=
null
;
/**
* {
@inheritDoc
}
*
*
* Creates the world within the MUD.
*/
public
void
initialize(Properties props) {
logger.info(
"
Initializing SwordWorld
"
);
//
Create the Room
SwordWorldRoom room
=
new
SwordWorldRoom(
"
Plain Room
"
,
"
a nondescript room
"
);
//
Create the Sword
SwordWorldObject sword
=
new
SwordWorldObject(
"
Shiny Sword
"
,
"
a shiny sword.
"
);
//
Put the Sword to the Room
room.addItem(sword);
//
Keep a reference to the Room
setRoom(room);
logger.info(
"
SwordWorld Initialized
"
);
}
/**
* Gets the SwordWorld's One True Room.
*
*
*
@return
the room for this {
@code
SwordWorld}
*/
public
SwordWorldRoom getRoom() {
if
(roomRef
==
null
)
return
null
;
return
roomRef.get();
}
/**
* Sets the SwordWorld's One True Room to the given room.
*
*
*
@param
room
* the room to set
*/
public
void
setRoom(SwordWorldRoom room) {
DataManager dataManager
=
AppContext.getDataManager();
if
(room
==
null
) {
roomRef
=
null
;
return
;
}
roomRef
=
dataManager.createReference(room);
}
/**
* {
@inheritDoc
}
*
*
* Obtains the {
@linkplain
SwordWorldPlayer player} for this
* {
@linkplain
ClientSession session}'s user, and puts the player into the
* One True Room for this {
@code
SwordWorld}.
*/
public
ClientSessionListener loggedIn(ClientSession session) {
logger.log(Level.INFO,
"
SwordWorld Client login: {0}
"
, session
.getName());
//
Delegate to a factory method on SwordWorldPlayer,
//
since player management really belongs in that class.
SwordWorldPlayer player
=
SwordWorldPlayer.loggedIn(session);
//
Put player in room
player.enter(getRoom());
//
return player object as listener to this client session
return
player;
}
}
SwordWorldObject
/*
* Copyright 2007-2009 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package
com.sun.sgs.tutorial.server.swordworld;
import
java.io.Serializable;
import
com.sun.sgs.app.AppContext;
import
com.sun.sgs.app.ManagedObject;
/**
* A {
@code
ManagedObject} that has a name and a description.
*/
public
class
SwordWorldObject
implements
Serializable, ManagedObject {
/**
The version of the serialized form of this class.
*/
private
static
final
long
serialVersionUID
=
1L
;
/**
The name of this object.
*/
private
String name;
/**
The description of this object.
*/
private
String description;
/**
* Creates a new {
@code
SwordWorldObject} with the given {
@code
name} and
* {
@code
description}.
*
*
@param
name
* the name of this object
*
@param
description
* the description of this object
*/
public
SwordWorldObject(String name, String description) {
this
.name
=
name;
this
.description
=
description;
}
/**
* Sets the name of this object.
*
*
@param
name
* the name of this object
*/
public
void
setName(String name) {
AppContext.getDataManager().markForUpdate(
this
);
this
.name
=
name;
}
/**
* Returns the name of this object.
*
*
@return
the name of this object
*/
public
String getName() {
return
name;
}
/**
* Sets the description of this object.
*
*
@param
description
* the description of this object
*/
public
void
setDescription(String description) {
AppContext.getDataManager().markForUpdate(
this
);
this
.description
=
description;
}
/**
* Returns the description of this object.
*
*
@return
the description of this object
*/
public
String getDescription() {
return
description;
}
/**
{
@inheritDoc
}
*/
@Override
public
String toString() {
return
getName();
}
}
SwordWorldRoom
/*
* Copyright 2007-2009 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package
com.sun.sgs.tutorial.server.swordworld;
import
java.util.ArrayList;
import
java.util.Collections;
import
java.util.HashSet;
import
java.util.Iterator;
import
java.util.List;
import
java.util.Set;
import
java.util.logging.Level;
import
java.util.logging.Logger;
import
com.sun.sgs.app.AppContext;
import
com.sun.sgs.app.DataManager;
import
com.sun.sgs.app.ManagedReference;
/**
* Represents a room in the {
@link
SwordWorld} example MUD.
*/
public
class
SwordWorldRoom
extends
SwordWorldObject
{
/**
The version of the serialized form of this class.
*/
private
static
final
long
serialVersionUID
=
1L
;
/**
The {
@link
Logger} for this class.
*/
private
static
final
Logger logger
=
Logger.getLogger(SwordWorldRoom.
class
.getName());
/**
The set of items in this room.
*/
private
final
Set
&
gt; items
=
new
HashSet
&
gt;();
/**
The set of players in this room.
*/
private
final
Set
&
gt; players
=
new
HashSet
&
gt;();
/**
* Creates a new room with the given name and description, initially empty of
* items and players.
*
*
@param
name
* the name of this room
*
@param
description
* a description of this room
*
*/
public
SwordWorldRoom(String name, String description) {
super
(name, description);
}
/**
* Adds an item to this room.
*
*
@param
item
* the item to add to this room.
*
@return
{
@code
true} if the item was added to the room
*/
public
boolean
addItem(SwordWorldObject item) {
logger.log(Level.INFO,
"
{0} placed in {1}
"
,
new
Object[] { item,
this
});
//
NOTE: we can't directly save the item in the list, or
//
we'll end up with a local copy of the item. Instead, we
//
must save a ManagedReference to the item.
DataManager dataManager
=
AppContext.getDataManager();
dataManager.markForUpdate(
this
);
return
items.add(dataManager.createReference(item));
}
/**
* Adds a player to this room.
*
*
@param
player
* the player to add
*
@return
{
@code
true} if the player was added to the room
*/
public
boolean
addPlayer(SwordWorldPlayer player) {
logger.log(Level.INFO,
"
{0} enters {1}
"
,
new
Object[] { player,
this
});
DataManager dataManager
=
AppContext.getDataManager();
dataManager.markForUpdate(
this
);
return
players.add(dataManager.createReference(player));
}
/**
* Removes a player from this room.
*
*
@param
player
* the player to remove
*
@return
{
@code
true} if the player was in the room
*/
public
boolean
removePlayer(SwordWorldPlayer player) {
logger.log(Level.INFO,
"
{0} leaves {1}
"
,
new
Object[] { player,
this
});
DataManager dataManager
=
AppContext.getDataManager();
dataManager.markForUpdate(
this
);
return
players.remove(dataManager.createReference(player));
}
/**
* Returns a description of what the given player sees in this room.
*
*
@param
looker
* the player looking in this room
*
@return
a description of what the given player sees in this room
*/
public
String look(SwordWorldPlayer looker) {
logger.log(Level.INFO,
"
{0} looks at {1}
"
,
new
Object[] { looker,
this
});
StringBuilder output
=
new
StringBuilder();
output.append(
"
You are in
"
).append(getDescription()).append(
"
.\n
"
);
List otherPlayers
=
getPlayersExcluding(looker);
if
(
!
otherPlayers.isEmpty()) {
output.append(
"
Also in here are
"
);
appendPrettyList(output, otherPlayers);
output.append(
"
.\n
"
);
}
if
(
!
items.isEmpty()) {
output.append(
"
On the floor you see:\n
"
);
for
(ManagedReference itemRef : items) {
SwordWorldObject item
=
itemRef.get();
output.append(item.getDescription()).append(
'
\n
'
);
}
}
return
output.toString();
}
/**
* Appends the names of the {
@code
SwordWorldObject}s in the list to the
* builder, separated by commas, with an "and" before the final item.
*
*
@param
builder
* the {
@code
StringBuilder} to append to
*
@param
list
* the list of items to format
*/
private
void
appendPrettyList(StringBuilder builder,
List
<!--
xtends SwordWorldObje
-->
list)
{
if
(list.isEmpty()) {
return
;
}
int
lastIndex
=
list.size()
-
1
;
SwordWorldObject last
=
list.get(lastIndex);
Iterator
<!--
xtends SwordWorldObje
-->
it
=
list.subList(
0
, lastIndex).iterator();
if
(it.hasNext()) {
SwordWorldObject other
=
it.next();
builder.append(other.getName());
while
(it.hasNext()) {
other
=
it.next();
builder.append(
"
,
"
);
builder.append(other.getName());
}
builder.append(
"
and
"
);
}
builder.append(last.getName());
}
/**
* Returns a list of players in this room excluding the given player.
*
*
@param
player
* the player to exclude
*
@return
the list of players
*/
private
ListgetPlayersExcluding(SwordWorldPlayer player)
{
if
(players.isEmpty()) {
return
Collections.emptyList();
}
ArrayList otherPlayers
=
new
ArrayList(players.size());
for
(ManagedReference playerRef : players) {
SwordWorldPlayer other
=
playerRef.get();
if
(
!
player.equals(other)) {
otherPlayers.add(other);
}
}
return
Collections.unmodifiableList(otherPlayers);
}
}
SwordWorldPlayer
/*
* Copyright 2007-2009 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package
com.sun.sgs.tutorial.server.swordworld;
import
java.io.UnsupportedEncodingException;
import
java.nio.ByteBuffer;
import
java.util.logging.Level;
import
java.util.logging.Logger;
import
com.sun.sgs.app.AppContext;
import
com.sun.sgs.app.ClientSession;
import
com.sun.sgs.app.ClientSessionListener;
import
com.sun.sgs.app.DataManager;
import
com.sun.sgs.app.ManagedReference;
import
com.sun.sgs.app.NameNotBoundException;
/**
* Represents a player in the {
@link
SwordWorld} example MUD.
*/
public
class
SwordWorldPlayer
extends
SwordWorldObject
implements
ClientSessionListener {
/**
The version of the serialized form of this class.
*/
private
static
final
long
serialVersionUID
=
1L
;
/**
The {
@link
Logger} for this class.
*/
private
static
final
Logger logger
=
Logger
.getLogger(SwordWorldPlayer.
class
.getName());
/**
The message encoding.
*/
public
static
final
String MESSAGE_CHARSET
=
"
UTF-8
"
;
/**
The prefix for player bindings in the {
@code
DataManager}.
*/
protected
static
final
String PLAYER_BIND_PREFIX
=
"
Player.
"
;
/**
The {
@code
ClientSession} for this player, or null if logged out.
*/
private
ManagedReference currentSessionRef
=
null
;
/**
The {
@link
SwordWorldRoom} this player is in, or null if none.
*/
private
ManagedReference currentRoomRef
=
null
;
/**
* Find or create the player object for the given session, and mark the
* player as logged in on that session.
*
*
@param
session
* which session to find or create a player for
*
@return
a player for the given session
*/
public
static
SwordWorldPlayer loggedIn(ClientSession session) {
String playerBinding
=
PLAYER_BIND_PREFIX
+
session.getName();
//
try to find player object, if non existent then create
DataManager dataMgr
=
AppContext.getDataManager();
SwordWorldPlayer player;
try
{
player
=
(SwordWorldPlayer) dataMgr.getBinding(playerBinding);
}
catch
(NameNotBoundException ex) {
//
this is a new player
player
=
new
SwordWorldPlayer(playerBinding);
logger.log(Level.INFO,
"
New player created: {0}
"
, player);
dataMgr.setBinding(playerBinding, player);
}
player.setSession(session);
return
player;
}
/**
* Creates a new {
@code
SwordWorldPlayer} with the given name.
*
*
@param
name
* the name of this player
*/
protected
SwordWorldPlayer(String name) {
super
(name,
"
Seeker of the Sword
"
);
}
/**
* Returns the session for this listener.
*
*
@return
the session for this listener
*/
protected
ClientSession getSession() {
if
(currentSessionRef
==
null
) {
return
null
;
}
return
currentSessionRef.get();
}
/**
* Mark this player as logged in on the given session.
*
*
@param
session
* the session this player is logged in on
*/
protected
void
setSession(ClientSession session) {
DataManager dataMgr
=
AppContext.getDataManager();
dataMgr.markForUpdate(
this
);
currentSessionRef
=
dataMgr.createReference(session);
logger.log(Level.INFO,
"
Set session for {0} to {1}
"
,
new
Object[] {
this
, session });
}
/**
* Handles a player entering a room.
*
*
@param
room
* the room for this player to enter
*/
public
void
enter(SwordWorldRoom room) {
logger.log(Level.INFO,
"
{0} enters {1}
"
,
new
Object[] {
this
, room });
room.addPlayer(
this
);
setRoom(room);
}
/**
{
@inheritDoc
}
*/
public
void
receivedMessage(ByteBuffer message) {
String command
=
decodeString(message);
logger.log(Level.INFO,
"
{0} received command: {1}
"
,
new
Object[] {
this
, command });
if
(command.equalsIgnoreCase(
"
look
"
)) {
String reply
=
getRoom().look(
this
);
getSession().send(encodeString(reply));
}
else
{
logger.log(Level.WARNING,
"
{0} unknown command: {1}
"
,
new
Object[] {
this
, command });
//
We could disconnect the rogue player at this point.
//
currentSession.disconnect();
}
}
/**
{
@inheritDoc
}
*/
public
void
disconnected(
boolean
graceful) {
setSession(
null
);
logger.log(Level.INFO,
"
Disconnected: {0}
"
,
this
);
getRoom().removePlayer(
this
);
setRoom(
null
);
}
/**
* Returns the room this player is currently in, or {
@code
null} if this
* player is not in a room.
*
*
*
@return
the room this player is currently in, or {
@code
null}
*/
protected
SwordWorldRoom getRoom() {
if
(currentRoomRef
==
null
) {
return
null
;
}
return
currentRoomRef.get();
}
/**
* Sets the room this player is currently in. If the room given is null,
* marks the player as not in any room.
*
*
*
@param
room
* the room this player should be in, or {
@code
null}
*/
protected
void
setRoom(SwordWorldRoom room) {
DataManager dataManager
=
AppContext.getDataManager();
dataManager.markForUpdate(
this
);
if
(room
==
null
) {
currentRoomRef
=
null
;
return
;
}
currentRoomRef
=
dataManager.createReference(room);
}
/**
{
@inheritDoc
}
*/
@Override
public
String toString() {
StringBuilder buf
=
new
StringBuilder(getName());
buf.append(
'
@
'
);
if
(getSession()
==
null
) {
buf.append(
"
null
"
);
}
else
{
buf.append(currentSessionRef.getId());
}
return
buf.toString();
}
/**
* Encodes a {
@code
String} into a {
@link
ByteBuffer}.
*
*
@param
s
* the string to encode
*
@return
the {
@code
ByteBuffer} which encodes the given string
*/
protected
static
ByteBuffer encodeString(String s) {
try
{
return
ByteBuffer.wrap(s.getBytes(MESSAGE_CHARSET));
}
catch
(UnsupportedEncodingException e) {
throw
new
Error(
"
Required character set
"
+
MESSAGE_CHARSET
+
"
not found
"
, e);
}
}
/**
* Decodes a message into a {
@code
String}.
*
*
@param
message
* the message to decode
*
@return
the decoded string
*/
protected
static
String decodeString(ByteBuffer message) {
try
{
byte
[] bytes
=
new
byte
[message.remaining()];
message.get(bytes);
return
new
String(bytes, MESSAGE_CHARSET);
}
catch
(UnsupportedEncodingException e) {
throw
new
Error(
"
Required character set
"
+
MESSAGE_CHARSET
+
"
not found
"
, e);
}
}
}