智慧 + 毅力 = 无所不能

正确性、健壮性、可靠性、效率、易用性、可读性、可复用性、兼容性、可移植性...

导航

ProjectDarkStar服务器端开发文档(九)

Posted on 2009-11-24 23:34  Bill Yuan  阅读(668)  评论(0编辑  收藏  举报

附录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> items =
        
new HashSet>();
     
    
/** The set of players in this room. */
    
private final Set> players =
        
new HashSet>();
    
/**
     * 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);
        }
    }
}